Skip to main content

oak_r/builder/
mod.rs

1use crate::{
2    RParser,
3    ast::{Expr, Identifier, RRoot, Statement},
4    language::RLanguage,
5    lexer::token_type::RTokenType,
6    parser::element_type::RElementType,
7};
8use oak_core::{Builder, BuilderCache, GreenNode, GreenTree, OakDiagnostics, OakError, Parser, SourceText, TextEdit, builder::BuildOutput, source::Source};
9
10#[derive(Clone)]
11pub struct RBuilder<'config> {
12    config: &'config RLanguage,
13}
14
15impl<'config> RBuilder<'config> {
16    pub fn new(config: &'config RLanguage) -> Self {
17        Self { config }
18    }
19
20    pub fn build_root(&self, green_tree: &GreenNode<RLanguage>, source: &SourceText) -> Result<RRoot, OakError> {
21        let mut statements = Vec::new();
22        let mut current_offset = 0;
23
24        for child in green_tree.children() {
25            let child_len = child.len() as usize;
26            if let GreenTree::Node(node) = child {
27                if !node.kind.is_trivia() {
28                    if let Some(stmt) = self.build_statement(node, current_offset, source)? {
29                        statements.push(stmt);
30                    }
31                }
32            }
33            current_offset += child_len;
34        }
35
36        Ok(RRoot { statements })
37    }
38
39    fn build_statement(&self, node: &GreenNode<RLanguage>, offset: usize, source: &SourceText) -> Result<Option<Statement>, OakError> {
40        match node.kind {
41            RElementType::Assignment => {
42                let mut name = None;
43                let mut expr = None;
44                let mut current_offset = offset;
45
46                for child in node.children() {
47                    let child_len = child.len() as usize;
48                    match child {
49                        GreenTree::Node(sub_node) => {
50                            if sub_node.kind == RElementType::Identifier {
51                                name = Some(Identifier { name: source.get_text_in((current_offset..current_offset + child_len).into()).to_string(), span: (current_offset..current_offset + child_len).into() });
52                            }
53                            else if sub_node.kind == RElementType::LiteralExpression || sub_node.kind == RElementType::IdentifierExpression || sub_node.kind == RElementType::CallExpression {
54                                expr = Some(self.build_expression(sub_node, current_offset, source)?)
55                            }
56                        }
57                        _ => {}
58                    }
59                    current_offset += child_len
60                }
61
62                if let (Some(n), Some(e)) = (name, expr) { Ok(Some(Statement::Assignment { name: n, expr: e, span: (offset..current_offset).into() })) } else { Ok(None) }
63            }
64            RElementType::Function => {
65                let name = Identifier { name: "anonymous".to_string(), span: (offset..offset).into() };
66                let mut params = Vec::new();
67                let mut body = Vec::new();
68                let mut current_offset = offset;
69
70                for child in node.children() {
71                    let child_len = child.len() as usize;
72                    if let GreenTree::Node(sub_node) = child {
73                        match sub_node.kind {
74                            RElementType::Identifier => params.push(Identifier { name: source.get_text_in((current_offset..current_offset + child_len).into()).to_string(), span: (current_offset..current_offset + child_len).into() }),
75                            RElementType::BlockExpression => {
76                                let mut block_offset = current_offset;
77                                for block_child in sub_node.children() {
78                                    let block_child_len = block_child.len() as usize;
79                                    if let GreenTree::Node(stmt_node) = block_child {
80                                        if let Some(s) = self.build_statement(stmt_node, block_offset, source)? {
81                                            body.push(s)
82                                        }
83                                    }
84                                    block_offset += block_child_len
85                                }
86                            }
87                            _ => {}
88                        }
89                    }
90                    current_offset += child_len
91                }
92
93                Ok(Some(Statement::FunctionDef { name, params, body, span: (offset..current_offset).into() }))
94            }
95            _ => {
96                if let Ok(expr) = self.build_expression(node, offset, source) {
97                    Ok(Some(Statement::ExprStmt { expr, span: (offset..offset + node.text_len() as usize).into() }))
98                }
99                else {
100                    Ok(None)
101                }
102            }
103        }
104    }
105
106    fn build_expression(&self, node: &GreenNode<RLanguage>, offset: usize, source: &SourceText) -> Result<Expr, OakError> {
107        match node.kind {
108            RElementType::IdentifierExpression | RElementType::Identifier => Ok(Expr::Ident(Identifier { name: source.get_text_in((offset..offset + node.text_len() as usize).into()).to_string(), span: (offset..offset + node.text_len() as usize).into() })),
109            RElementType::LiteralExpression => {
110                let text = source.get_text_in((offset..offset + node.text_len() as usize).into()).to_string();
111                if text == "TRUE" || text == "FALSE" {
112                    Ok(Expr::Bool { value: text == "TRUE", span: (offset..offset + node.text_len() as usize).into() })
113                }
114                else if text == "NULL" {
115                    Ok(Expr::Null { span: (offset..offset + node.text_len() as usize).into() })
116                }
117                else {
118                    Ok(Expr::Literal { value: text, span: (offset..offset + node.text_len() as usize).into() })
119                }
120            }
121            RElementType::CallExpression => {
122                let mut callee = None;
123                let mut args = Vec::new();
124                let mut current_offset = offset;
125
126                for child in node.children() {
127                    let child_len = child.len() as usize;
128                    if let GreenTree::Node(sub_node) = child {
129                        if callee.is_none() && (sub_node.kind == RElementType::Identifier || sub_node.kind == RElementType::IdentifierExpression) {
130                            callee = Some(Box::new(self.build_expression(sub_node, current_offset, source)?))
131                        }
132                        else if sub_node.kind != RElementType::LeftParen && sub_node.kind != RElementType::RightParen && sub_node.kind != RElementType::Comma && !sub_node.kind.is_trivia() {
133                            args.push(self.build_expression(sub_node, current_offset, source)?)
134                        }
135                    }
136                    current_offset += child_len
137                }
138
139                if let Some(c) = callee { Ok(Expr::Call { callee: c, args, span: (offset..offset + node.text_len() as usize).into() }) } else { Err(OakError::custom_error(format!("Unexpected token at offset {}", offset))) }
140            }
141            _ => Err(OakError::custom_error(format!("Unexpected token at offset {}", offset))),
142        }
143    }
144}
145
146impl<'config> Builder<RLanguage> for RBuilder<'config> {
147    fn build<'a, S: Source + ?Sized>(&self, source: &S, edits: &[TextEdit], _cache: &'a mut impl BuilderCache<RLanguage>) -> BuildOutput<RLanguage> {
148        let parser = RParser::new(self.config);
149        let mut parse_cache = oak_core::parser::session::ParseSession::<RLanguage>::default();
150        let parse_result = parser.parse(source, edits, &mut parse_cache);
151
152        match parse_result.result {
153            Ok(green_tree) => {
154                let source_text = SourceText::new(source.get_text_in((0..source.length()).into()).into_owned());
155                match self.build_root(green_tree, &source_text) {
156                    Ok(ast_root) => OakDiagnostics { result: Ok(ast_root), diagnostics: parse_result.diagnostics },
157                    Err(build_error) => {
158                        let mut diagnostics = parse_result.diagnostics;
159                        diagnostics.push(build_error.clone());
160                        OakDiagnostics { result: Err(build_error), diagnostics }
161                    }
162                }
163            }
164            Err(e) => OakDiagnostics { result: Err(e), diagnostics: parse_result.diagnostics },
165        }
166    }
167}