1use crate::{
4 ast::*,
5 language::JasmLanguage,
6 lexer::token_type::JasmTokenType,
7 parser::{JasmParser, element_type::JasmElementType},
8};
9use oak_core::{Builder, BuilderCache, GreenNode, OakDiagnostics, OakError, Parser, RedNode, RedTree, SourceText, TextEdit, source::Source};
10
11#[derive(Clone)]
13pub struct JasmBuilder<'config> {
14 pub config: &'config JasmLanguage,
16}
17
18impl<'config> JasmBuilder<'config> {
19 pub fn new(config: &'config JasmLanguage) -> Self {
21 Self { config }
22 }
23}
24
25impl<'config> Builder<JasmLanguage> for JasmBuilder<'config> {
26 fn build<'a, S: Source + ?Sized>(&self, source: &'a S, edits: &[TextEdit], cache: &'a mut impl BuilderCache<JasmLanguage>) -> OakDiagnostics<JasmRoot> {
27 let parser = JasmParser::new(self.config);
28
29 let parse_result = parser.parse(source, edits, cache);
30
31 match parse_result.result {
32 Ok(green_tree) => {
33 let source_text = SourceText::new(source.get_text_in((0..source.length()).into()).into_owned());
34 match self.build_root(green_tree, &source_text) {
35 Ok(ast_root) => OakDiagnostics { result: Ok(ast_root), diagnostics: parse_result.diagnostics },
36 Err(build_error) => {
37 let mut diagnostics = parse_result.diagnostics;
38 diagnostics.push(build_error.clone());
39 OakDiagnostics { result: Err(build_error), diagnostics }
40 }
41 }
42 }
43 Err(parse_error) => OakDiagnostics { result: Err(parse_error), diagnostics: parse_result.diagnostics },
44 }
45 }
46}
47
48impl<'config> JasmBuilder<'config> {
49 fn build_root<'a>(&self, green_tree: &'a GreenNode<'a, JasmLanguage>, source: &SourceText) -> Result<JasmRoot, OakError> {
50 let red_root = RedNode::new(green_tree, 0);
51 let mut class = None;
52
53 for child in red_root.children() {
54 if let RedTree::Node(n) = child {
55 if n.green.kind == JasmElementType::Class {
56 class = Some(self.build_class(n, source)?);
57 }
58 }
59 }
60
61 Ok(JasmRoot { class: class.unwrap_or_else(|| JasmClass { modifiers: vec![], name: String::new(), version: None, methods: vec![], fields: vec![], source_file: None, super_class: None, interfaces: vec![], annotations: vec![], attributes: vec![] }) })
62 }
63
64 fn build_class(&self, node: RedNode<JasmLanguage>, source: &SourceText) -> Result<JasmClass, OakError> {
65 let mut modifiers = vec![];
66 let mut name = String::new();
67 let mut methods = vec![];
68 let mut fields = vec![];
69
70 for child in node.children() {
71 match child {
72 RedTree::Leaf(t) => {
73 let text = source.get_text_in(t.span.clone().into()).to_string();
74 match t.kind {
75 JasmTokenType::Public | JasmTokenType::Private | JasmTokenType::Protected | JasmTokenType::Static | JasmTokenType::Final | JasmTokenType::Abstract | JasmTokenType::Synthetic | JasmTokenType::Deprecated => {
76 modifiers.push(text);
77 }
78 JasmTokenType::Identifier => {
79 name = text;
80 }
81 _ => {}
82 }
83 }
84 RedTree::Node(n) => match n.green.kind {
85 JasmElementType::Method => {
86 methods.push(self.build_method(n, source)?);
87 }
88 JasmElementType::Field => {
89 fields.push(self.build_field(n, source)?);
90 }
91 _ => {}
92 },
93 }
94 }
95
96 Ok(JasmClass { modifiers, name, version: None, methods, fields, source_file: None, super_class: None, interfaces: vec![], annotations: vec![], attributes: vec![] })
97 }
98
99 fn build_method(&self, node: RedNode<JasmLanguage>, source: &SourceText) -> Result<JasmMethod, OakError> {
100 let mut modifiers = vec![];
101 let mut name = String::new();
102 let mut descriptor = String::new();
103 let mut instructions = vec![];
104 let mut stack = None;
105 let mut locals = None;
106
107 for child in node.children() {
108 match child {
109 RedTree::Leaf(t) => {
110 let text = source.get_text_in(t.span.clone().into()).to_string();
111 match t.kind {
112 JasmTokenType::Public
113 | JasmTokenType::Private
114 | JasmTokenType::Protected
115 | JasmTokenType::Static
116 | JasmTokenType::Final
117 | JasmTokenType::Native
118 | JasmTokenType::Synchronized
119 | JasmTokenType::Synthetic
120 | JasmTokenType::Deprecated
121 | JasmTokenType::Varargs => {
122 modifiers.push(text);
123 }
124 JasmTokenType::Identifier => {
125 if name.is_empty() {
126 name = text;
127 }
128 else if descriptor.is_empty() {
129 descriptor = text;
130 }
131 }
132 JasmTokenType::Number => {
133 if let Ok(val) = text.parse::<u32>() {
134 if stack.is_none() {
135 stack = Some(val);
136 }
137 else if locals.is_none() {
138 locals = Some(val);
139 }
140 }
141 }
142 _ => {}
143 }
144 }
145 RedTree::Node(n) => {
146 if n.green.kind == JasmElementType::Instruction {
147 instructions.push(self.build_instruction(n, source)?);
148 }
149 }
150 }
151 }
152
153 let name_and_descriptor = if descriptor.is_empty() { name } else { format!("{}{}", name, descriptor) };
154
155 Ok(JasmMethod { modifiers, name_and_descriptor, stack_size: stack, locals_count: locals, instructions, exception_handlers: vec![], annotations: vec![], attributes: vec![] })
156 }
157
158 fn build_field(&self, node: RedNode<JasmLanguage>, source: &SourceText) -> Result<JasmField, OakError> {
159 let mut modifiers = vec![];
160 let mut name = String::new();
161 let mut descriptor = String::new();
162
163 for child in node.children() {
164 if let RedTree::Leaf(t) = child {
165 let text = source.get_text_in(t.span.clone().into()).to_string();
166 match t.kind {
167 JasmTokenType::Public | JasmTokenType::Private | JasmTokenType::Protected | JasmTokenType::Static | JasmTokenType::Final | JasmTokenType::Synthetic | JasmTokenType::Deprecated => {
168 modifiers.push(text);
169 }
170 JasmTokenType::Identifier => {
171 if name.is_empty() {
172 name = text;
173 }
174 else {
175 descriptor = text;
176 }
177 }
178 _ => {}
179 }
180 }
181 }
182
183 let name_and_descriptor = if descriptor.is_empty() { name } else { format!("{} {}", name, descriptor) };
184
185 Ok(JasmField { modifiers, name_and_descriptor, annotations: vec![], attributes: vec![] })
186 }
187
188 fn build_instruction(&self, node: RedNode<JasmLanguage>, source: &SourceText) -> Result<JasmInstruction, OakError> {
189 let mut opcode = String::new();
190 let mut operands = vec![];
191
192 for child in node.children() {
193 if let RedTree::Leaf(t) = child {
194 let text = source.get_text_in(t.span.clone().into()).to_string();
195 let kind_val = t.kind as u16;
196 if kind_val >= JasmTokenType::ALoad0 as u16 && kind_val <= JasmTokenType::Pop as u16 {
197 opcode = text;
198 }
199 else if matches!(t.kind, JasmTokenType::Identifier | JasmTokenType::Number | JasmTokenType::String) {
200 operands.push(text);
201 }
202 }
203 }
204
205 if operands.is_empty() {
206 Ok(JasmInstruction::Simple(opcode))
207 }
208 else {
209 let argument = operands.join(" ");
210 if opcode.starts_with("invoke") {
211 Ok(JasmInstruction::MethodCall { instruction: opcode, method_ref: argument })
212 }
213 else if opcode.starts_with("get") || opcode.starts_with("put") {
214 Ok(JasmInstruction::FieldAccess { instruction: opcode, field_ref: argument })
215 }
216 else {
217 Ok(JasmInstruction::WithArgument { instruction: opcode, argument })
218 }
219 }
220 }
221}