Skip to main content

oak_msil/builder/
mod.rs

1//! Builder implementation for the MSIL language.
2
3use crate::{
4    ast::*,
5    language::MsilLanguage,
6    parser::{MsilParser, element_type::MsilElementType},
7};
8use oak_core::{Builder, BuilderCache, GreenNode, OakDiagnostics, Source, SourceText, TextEdit};
9
10/// Builder for the MSIL language.
11#[derive(Clone)]
12pub struct MsilBuilder<'config> {
13    #[allow(dead_code)]
14    config: &'config MsilLanguage,
15}
16
17impl<'config> MsilBuilder<'config> {
18    /// Creates a new `MsilBuilder` with the given configuration.
19    pub fn new(config: &'config MsilLanguage) -> Self {
20        Self { config }
21    }
22
23    /// Builds the root AST node from a green tree.
24    pub fn build_root(&self, green_tree: &GreenNode<MsilLanguage>, source: &SourceText) -> Result<MsilRoot, oak_core::OakError> {
25        let red_root = oak_core::RedNode::new(green_tree, 0);
26
27        let mut items = Vec::new();
28        for child in red_root.children() {
29            if let oak_core::RedTree::Node(node) = child {
30                if let Some(item) = self.build_item(&node, source) {
31                    items.push(item)
32                }
33            }
34        }
35
36        Ok(MsilRoot { items })
37    }
38
39    fn build_item(&self, node: &oak_core::RedNode<MsilLanguage>, source: &SourceText) -> Option<Item> {
40        let kind = node.green.kind;
41        match kind {
42            MsilElementType::Assembly => {
43                let mut name = "unknown".to_string();
44                for child in node.children() {
45                    if let oak_core::RedTree::Node(n) = child {
46                        if n.green.kind == MsilElementType::Identifier {
47                            name = source.get_text_in(n.span()).to_string();
48                            break;
49                        }
50                    }
51                }
52                Some(Item::Assembly(crate::ast::Assembly { name, span: node.span() }))
53            }
54            MsilElementType::AssemblyExtern => {
55                let mut name = "unknown".to_string();
56                for child in node.children() {
57                    if let oak_core::RedTree::Node(n) = child {
58                        if n.green.kind == MsilElementType::Identifier {
59                            name = source.get_text_in(n.span()).to_string();
60                            break;
61                        }
62                    }
63                }
64                Some(Item::AssemblyExtern(name))
65            }
66            MsilElementType::Module => {
67                let mut name = "unknown".to_string();
68                for child in node.children() {
69                    if let oak_core::RedTree::Node(n) = child {
70                        if n.green.kind == MsilElementType::Identifier {
71                            name = source.get_text_in(n.span()).to_string();
72                            break;
73                        }
74                    }
75                }
76                Some(Item::Module(name))
77            }
78            MsilElementType::Class => {
79                let mut name = "Unknown".to_string();
80                let mut methods = Vec::new();
81                for child in node.children() {
82                    if let oak_core::RedTree::Node(n) = child {
83                        if n.green.kind == MsilElementType::Identifier {
84                            name = source.get_text_in(n.span()).to_string()
85                        }
86                        else if n.green.kind == MsilElementType::Method {
87                            if let Some(method) = self.build_method(&n, source) {
88                                methods.push(method)
89                            }
90                        }
91                    }
92                }
93                Some(Item::Class(crate::ast::Class { name, methods, span: node.span() }))
94            }
95            _ => None,
96        }
97    }
98
99    fn build_method(&self, node: &oak_core::RedNode<MsilLanguage>, source: &SourceText) -> Option<crate::ast::Method> {
100        let mut name = "Unknown".to_string();
101        let mut instructions = Vec::new();
102        for child in node.children() {
103            if let oak_core::RedTree::Node(n) = child {
104                if n.green.kind == MsilElementType::Identifier {
105                    name = source.get_text_in(n.span()).to_string();
106                }
107                else if n.green.kind == MsilElementType::Instruction {
108                    if let Some(inst) = self.build_instruction(&n, source) {
109                        instructions.push(inst);
110                    }
111                }
112            }
113        }
114
115        Some(crate::ast::Method { name, instructions, span: node.span() })
116    }
117
118    fn build_instruction(&self, node: &oak_core::RedNode<MsilLanguage>, source: &SourceText) -> Option<Instruction> {
119        let mut opcode = String::new();
120        let mut operand = None;
121
122        for child in node.children() {
123            if let oak_core::RedTree::Node(n) = child {
124                match n.green.kind {
125                    MsilElementType::Identifier => {
126                        if opcode.is_empty() {
127                            opcode = source.get_text_in(n.span()).to_string();
128                        }
129                        else {
130                            operand = Some(source.get_text_in(n.span()).to_string());
131                        }
132                    }
133                    MsilElementType::String => {
134                        operand = Some(source.get_text_in(n.span()).to_string());
135                    }
136                    _ => {}
137                }
138            }
139        }
140
141        if opcode == "ldstr" {
142            Some(Instruction::String(operand.unwrap_or_default()))
143        }
144        else if opcode == "call" {
145            Some(Instruction::Call(operand.unwrap_or_default()))
146        }
147        else if !opcode.is_empty() {
148            Some(Instruction::Simple(opcode))
149        }
150        else {
151            None
152        }
153    }
154}
155
156impl<'config> Builder<MsilLanguage> for MsilBuilder<'config> {
157    fn build<'a, S: Source + ?Sized>(&self, source: &S, edits: &[TextEdit], _cache: &'a mut impl BuilderCache<MsilLanguage>) -> OakDiagnostics<MsilRoot> {
158        let parser = MsilParser::new(self.config);
159        let lexer = crate::lexer::MsilLexer::new(&self.config);
160
161        let mut cache = oak_core::parser::session::ParseSession::<MsilLanguage>::default();
162        let parse_result = oak_core::parser::parse(&parser, &lexer, source, edits, &mut cache);
163
164        match parse_result.result {
165            Ok(green_tree) => {
166                let source_text = SourceText::new(source.get_text_in((0..source.length()).into()).into_owned());
167                OakDiagnostics { result: self.build_root(green_tree, &source_text), diagnostics: parse_result.diagnostics }
168            }
169            Err(parse_error) => OakDiagnostics { result: Err(parse_error), diagnostics: parse_result.diagnostics },
170        }
171    }
172}