oak_crystal/builder/
mod.rs1#![doc = include_str!("readme.md")]
2use crate::{
3 ast::*,
4 language::CrystalLanguage,
5 parser::{CrystalParser, element_type::CrystalElementType},
6};
7use oak_core::{Builder, BuilderCache, GreenNode, OakDiagnostics, Parser, RedNode, RedTree, SourceText, TextEdit, source::Source};
8
9#[derive(Clone)]
11pub struct CrystalBuilder<'config> {
12 config: &'config CrystalLanguage,
14}
15
16impl<'config> CrystalBuilder<'config> {
17 pub fn new(config: &'config CrystalLanguage) -> Self {
19 Self { config }
20 }
21
22 pub fn build_root(&self, green: &GreenNode<CrystalLanguage>, source: &SourceText) -> Result<CrystalRoot, oak_core::OakError> {
24 let red = RedNode::new(green, 0);
25 let mut items = Vec::new();
26
27 for child in red.children() {
28 if let RedTree::Node(node) = child {
29 if let Some(item) = self.build_item(&node, source) {
30 items.push(item);
31 }
32 }
33 }
34
35 Ok(CrystalRoot { items })
36 }
37
38 fn build_item(&self, node: &RedNode<CrystalLanguage>, source: &SourceText) -> Option<Item> {
39 match node.green.kind {
40 CrystalElementType::ClassDef => self.build_class(node, source).map(Item::Class),
41 CrystalElementType::ModuleDef => self.build_module(node, source).map(Item::Module),
42 CrystalElementType::MethodDef => self.build_method(node, source).map(Item::Def),
43 _ if node.green.kind as u16 >= CrystalElementType::IfExpr as u16 && node.green.kind as u16 <= CrystalElementType::ParenExpr as u16 => self.build_expression(node, source).map(Item::Expression),
44 _ => None,
45 }
46 }
47
48 fn build_class(&self, node: &RedNode<CrystalLanguage>, source: &SourceText) -> Option<ClassDeclaration> {
49 let mut name = None;
50 let mut body = Vec::new();
51
52 for child in node.children() {
53 if let RedTree::Node(n) = child {
54 match n.green.kind {
55 CrystalElementType::Identifier => {
56 if name.is_none() {
57 name = Some(Identifier { name: source.get_text_in(n.span()).to_string(), span: n.span() });
58 }
59 }
60 CrystalElementType::Block => {
61 for block_child in n.children() {
62 if let RedTree::Node(bn) = block_child {
63 if let Some(item) = self.build_item(&bn, source) {
64 body.push(item);
65 }
66 }
67 }
68 }
69 _ => {
70 if let Some(item) = self.build_item(&n, source) {
71 body.push(item);
72 }
73 }
74 }
75 }
76 }
77
78 name.map(|name| ClassDeclaration { name, body, span: node.span() })
79 }
80
81 fn build_module(&self, node: &RedNode<CrystalLanguage>, source: &SourceText) -> Option<ModuleDeclaration> {
82 let mut name = None;
83 let mut body = Vec::new();
84
85 for child in node.children() {
86 if let RedTree::Node(n) = child {
87 match n.green.kind {
88 CrystalElementType::Identifier => {
89 if name.is_none() {
90 name = Some(Identifier { name: source.get_text_in(n.span()).to_string(), span: n.span() });
91 }
92 }
93 CrystalElementType::Block => {
94 for block_child in n.children() {
95 if let RedTree::Node(bn) = block_child {
96 if let Some(item) = self.build_item(&bn, source) {
97 body.push(item);
98 }
99 }
100 }
101 }
102 _ => {
103 if let Some(item) = self.build_item(&n, source) {
104 body.push(item);
105 }
106 }
107 }
108 }
109 }
110
111 name.map(|name| ModuleDeclaration { name, body, span: node.span() })
112 }
113
114 fn build_method(&self, node: &RedNode<CrystalLanguage>, source: &SourceText) -> Option<MethodDeclaration> {
115 let mut name = None;
116 let mut params = Vec::new();
117 let mut body = Vec::new();
118
119 for child in node.children() {
120 if let RedTree::Node(n) = child {
121 match n.green.kind {
122 CrystalElementType::Identifier => {
123 if name.is_none() {
124 name = Some(Identifier { name: source.get_text_in(n.span()).to_string(), span: n.span() });
125 }
126 }
127 CrystalElementType::ParamList => {
128 params = self.build_params(&n, source);
129 }
130 CrystalElementType::Block => {
131 for block_child in n.children() {
132 if let RedTree::Node(bn) = block_child {
133 if let Some(item) = self.build_item(&bn, source) {
134 body.push(item);
135 }
136 }
137 }
138 }
139 _ => {
140 if let Some(item) = self.build_item(&n, source) {
141 body.push(item);
142 }
143 }
144 }
145 }
146 }
147
148 name.map(|name| MethodDeclaration { name, params, body, span: node.span() })
149 }
150
151 fn build_params(&self, node: &RedNode<CrystalLanguage>, source: &SourceText) -> Vec<Parameter> {
152 let mut params = Vec::new();
153 for child in node.children() {
154 if let RedTree::Node(n) = child {
155 if n.green.kind == CrystalElementType::Param {
156 if let Some(param) = self.build_param(&n, source) {
157 params.push(param);
158 }
159 }
160 }
161 }
162 params
163 }
164
165 fn build_param(&self, node: &RedNode<CrystalLanguage>, source: &SourceText) -> Option<Parameter> {
166 let mut name = None;
167 let mut type_name = None;
168
169 for child in node.children() {
170 if let RedTree::Node(n) = child {
171 match n.green.kind {
172 CrystalElementType::Identifier => {
173 if name.is_none() {
174 name = Some(Identifier { name: source.get_text_in(n.span()).to_string(), span: n.span() });
175 }
176 else if type_name.is_none() {
177 type_name = Some(Identifier { name: source.get_text_in(n.span()).to_string(), span: n.span() });
178 }
179 }
180 _ => {}
181 }
182 }
183 }
184
185 name.map(|name| Parameter { name, type_name })
186 }
187
188 fn build_expression(&self, node: &RedNode<CrystalLanguage>, source: &SourceText) -> Option<Expression> {
189 match node.green.kind {
190 CrystalElementType::LiteralExpr => {
191 for child in node.children() {
192 if let RedTree::Node(n) = child {
193 match n.green.kind {
194 CrystalElementType::Number => return Some(Expression::Literal(Literal::Number(source.get_text_in(n.span()).to_string()))),
195 CrystalElementType::String => return Some(Expression::Literal(Literal::String(source.get_text_in(n.span()).to_string()))),
196 CrystalElementType::TrueKeyword => return Some(Expression::Literal(Literal::Boolean(true))),
197 CrystalElementType::FalseKeyword => return Some(Expression::Literal(Literal::Boolean(false))),
198 CrystalElementType::NilKeyword => return Some(Expression::Literal(Literal::Nil)),
199 _ => {}
200 }
201 }
202 }
203 None
204 }
205 CrystalElementType::CallExpr => {
206 let mut name = None;
207 let mut receiver = None;
208 let mut args = Vec::new();
209
210 for child in node.children() {
211 if let RedTree::Node(n) = child {
212 match n.green.kind {
213 CrystalElementType::Identifier => {
214 if name.is_none() {
215 name = Some(Identifier { name: source.get_text_in(n.span()).to_string(), span: n.span() });
216 }
217 }
218 _ if n.green.kind as u16 >= CrystalElementType::IfExpr as u16 && n.green.kind as u16 <= CrystalElementType::ParenExpr as u16 => {
219 if let Some(expr) = self.build_expression(&n, source) {
220 if receiver.is_none() {
221 receiver = Some(Box::new(expr));
222 }
223 else {
224 args.push(expr);
225 }
226 }
227 }
228 _ => {}
229 }
230 }
231 }
232
233 name.map(|name| Expression::Call(Call { receiver, name, args }))
234 }
235 _ => None,
236 }
237 }
238}
239
240impl<'config> Builder<CrystalLanguage> for CrystalBuilder<'config> {
241 fn build<'a, S: Source + ?Sized>(&self, source: &'a S, edits: &[TextEdit], _cache: &'a mut impl BuilderCache<CrystalLanguage>) -> oak_core::builder::BuildOutput<CrystalLanguage> {
242 let parser = CrystalParser::new(self.config);
243 let mut cache = oak_core::parser::ParseSession::<CrystalLanguage>::default();
244 let parse_result = parser.parse(source, edits, &mut cache);
245
246 match parse_result.result {
247 Ok(green_tree) => {
248 let source_text = SourceText::new(source.get_text_in((0..source.length()).into()).into_owned());
249 match self.build_root(green_tree, &source_text) {
250 Ok(ast_root) => OakDiagnostics { result: Ok(ast_root), diagnostics: parse_result.diagnostics },
251 Err(build_error) => {
252 let mut diagnostics = parse_result.diagnostics;
253 diagnostics.push(build_error.clone());
254 OakDiagnostics { result: Err(build_error), diagnostics }
255 }
256 }
257 }
258 Err(parse_error) => OakDiagnostics { result: Err(parse_error), diagnostics: parse_result.diagnostics },
259 }
260 }
261}