1#![doc = include_str!("readme.md")]
2use core::range::Range;
3use oak_core::source::{SourceBuffer, ToSource};
4#[cfg(feature = "oak-pretty-print")]
5use oak_pretty_print::{AsDocument, Document};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct MsilTypedRoot<'a> {
10 red: oak_core::RedNode<'a, crate::language::MsilLanguage>,
11}
12
13impl<'a> oak_core::tree::TypedNode<'a> for MsilTypedRoot<'a> {
14 type Language = crate::language::MsilLanguage;
15
16 fn cast(node: oak_core::RedNode<'a, Self::Language>) -> Option<Self> {
17 if node.kind::<crate::parser::element_type::MsilElementType>() == crate::parser::element_type::MsilElementType::Root { Some(Self { red: node }) } else { None }
18 }
19
20 fn green(&self) -> &oak_core::GreenNode<'a, Self::Language> {
21 self.red.green()
22 }
23}
24
25#[derive(Clone, Debug, PartialEq, Eq, Hash)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28pub struct MsilRoot {
29 pub items: Vec<Item>,
31}
32
33impl ToSource for MsilRoot {
34 fn to_source(&self, buffer: &mut SourceBuffer) {
35 for item in &self.items {
36 item.to_source(buffer);
37 buffer.push("\n")
38 }
39 }
40}
41
42#[cfg(feature = "oak-pretty-print")]
43impl AsDocument for MsilRoot {
44 fn as_document(&self) -> Document<'_> {
45 Document::join(self.items.iter().map(|i| i.as_document()), Document::Line)
46 }
47}
48
49#[derive(Clone, Debug, PartialEq, Eq, Hash)]
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52pub enum Item {
53 Assembly(Assembly),
55 Module(String),
57 Class(Class),
59 AssemblyExtern(String),
61}
62
63impl ToSource for Item {
64 fn to_source(&self, buffer: &mut SourceBuffer) {
65 match self {
66 Item::Assembly(a) => a.to_source(buffer),
67 Item::Module(m) => {
68 buffer.push(".module ");
69 buffer.push(m)
70 }
71 Item::Class(c) => c.to_source(buffer),
72 Item::AssemblyExtern(a) => {
73 buffer.push(".assembly extern ");
74 buffer.push(a);
75 buffer.push(" {}")
76 }
77 }
78 }
79}
80
81#[cfg(feature = "oak-pretty-print")]
82impl AsDocument for Item {
83 fn as_document(&self) -> Document<'_> {
84 match self {
85 Item::Assembly(a) => a.as_document(),
86 Item::Module(m) => Document::Text(format!(".module {}", m).into()),
87 Item::Class(c) => c.as_document(),
88 Item::AssemblyExtern(a) => Document::Text(format!(".assembly extern {} {{}}", a).into()),
89 }
90 }
91}
92
93#[derive(Clone, Debug, PartialEq, Eq, Hash)]
95#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
96pub struct Assembly {
97 pub name: String,
99 #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
101 pub span: Range<usize>,
102}
103
104impl ToSource for Assembly {
105 fn to_source(&self, buffer: &mut SourceBuffer) {
106 buffer.push(".assembly ");
107 buffer.push(&self.name);
108 buffer.push(" {}")
109 }
110}
111
112#[cfg(feature = "oak-pretty-print")]
113impl AsDocument for Assembly {
114 fn as_document(&self) -> Document<'_> {
115 Document::Text(format!(".assembly {} {{}}", self.name).into())
116 }
117}
118
119#[derive(Clone, Debug, PartialEq, Eq, Hash)]
121#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
122pub struct Class {
123 pub name: String,
125 pub methods: Vec<Method>,
127 #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
129 pub span: Range<usize>,
130}
131
132impl ToSource for Class {
133 fn to_source(&self, buffer: &mut SourceBuffer) {
134 buffer.push(".class public auto ansi beforefieldinit ");
135 buffer.push(&self.name);
136 buffer.push("\n{");
137 for method in &self.methods {
138 buffer.push("\n");
139 method.to_source(buffer)
140 }
141 buffer.push("\n}")
142 }
143}
144
145#[cfg(feature = "oak-pretty-print")]
146impl AsDocument for Class {
147 fn as_document(&self) -> Document<'_> {
148 Document::Concat(vec![
149 Document::Text(format!(".class public auto ansi beforefieldinit {}", self.name).into()),
150 Document::Line,
151 Document::Text("{".into()),
152 Document::indent(Document::join(self.methods.iter().map(|m| m.as_document()), Document::Line)),
153 Document::Text("}".into()),
154 ])
155 }
156}
157
158#[derive(Clone, Debug, PartialEq, Eq, Hash)]
160#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
161pub struct Method {
162 pub name: String,
164 pub instructions: Vec<Instruction>,
166 #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
168 pub span: Range<usize>,
169}
170
171impl ToSource for Method {
172 fn to_source(&self, buffer: &mut SourceBuffer) {
173 buffer.push(".method public hidebysig static void ");
174 buffer.push(&self.name);
175 buffer.push("() cil managed\n{");
176 if !self.instructions.is_empty() {
177 buffer.push("\n .entrypoint");
178 for inst in &self.instructions {
179 buffer.push("\n ");
180 inst.to_source(buffer)
181 }
182 }
183 buffer.push("\n}")
184 }
185}
186
187#[cfg(feature = "oak-pretty-print")]
188impl AsDocument for Method {
189 fn as_document(&self) -> Document<'_> {
190 let mut body = vec![Document::Text(".entrypoint".into()), Document::Line];
191 body.extend(self.instructions.iter().map(|i| i.as_document()));
192
193 Document::Concat(vec![
194 Document::Text(format!(".method public hidebysig static void {}() cil managed", self.name).into()),
195 Document::Line,
196 Document::Text("{".into()),
197 Document::indent(Document::join(body, Document::Line)),
198 Document::Text("}".into()),
199 ])
200 }
201}
202
203#[derive(Clone, Debug, PartialEq, Eq, Hash)]
205#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
206pub enum Instruction {
207 Simple(String),
209 String(String),
211 Call(String),
213}
214
215impl ToSource for Instruction {
216 fn to_source(&self, buffer: &mut SourceBuffer) {
217 match self {
218 Instruction::Simple(s) => buffer.push(s),
219 Instruction::String(s) => {
220 buffer.push("ldstr \"");
221 buffer.push(s);
222 buffer.push("\"")
223 }
224 Instruction::Call(s) => {
225 buffer.push("call ");
226 buffer.push(s)
227 }
228 }
229 }
230}
231
232#[cfg(feature = "oak-pretty-print")]
233impl AsDocument for Instruction {
234 fn as_document(&self) -> Document<'_> {
235 match self {
236 Instruction::Simple(s) => Document::Text(s.clone().into()),
237 Instruction::String(s) => Document::Text(format!("ldstr \"{}\"", s).into()),
238 Instruction::Call(s) => Document::Text(format!("call {}", s).into()),
239 }
240 }
241}