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 type Params = ();
45
46 fn as_document(&self, _params: &Self::Params) -> Document<'_> {
47 Document::join(self.items.iter().map(|i| i.as_document(&())), Document::Line)
48 }
49}
50
51#[derive(Clone, Debug, PartialEq, Eq, Hash)]
53#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
54pub enum Item {
55 Assembly(Assembly),
57 Module(String),
59 Class(Class),
61 AssemblyExtern(String),
63}
64
65impl ToSource for Item {
66 fn to_source(&self, buffer: &mut SourceBuffer) {
67 match self {
68 Item::Assembly(a) => a.to_source(buffer),
69 Item::Module(m) => {
70 buffer.push(".module ");
71 buffer.push(m)
72 }
73 Item::Class(c) => c.to_source(buffer),
74 Item::AssemblyExtern(a) => {
75 buffer.push(".assembly extern ");
76 buffer.push(a);
77 buffer.push(" {}")
78 }
79 }
80 }
81}
82
83#[cfg(feature = "oak-pretty-print")]
84impl AsDocument for Item {
85 type Params = ();
86
87 fn as_document(&self, _params: &Self::Params) -> Document<'_> {
88 match self {
89 Item::Assembly(a) => a.as_document(&()),
90 Item::Module(m) => Document::Text(format!(".module {}", m).into()),
91 Item::Class(c) => c.as_document(&()),
92 Item::AssemblyExtern(a) => Document::Text(format!(".assembly extern {} {{}}", a).into()),
93 }
94 }
95}
96
97#[derive(Clone, Debug, PartialEq, Eq, Hash)]
99#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
100pub struct Assembly {
101 pub name: String,
103 #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
105 pub span: Range<usize>,
106}
107
108impl ToSource for Assembly {
109 fn to_source(&self, buffer: &mut SourceBuffer) {
110 buffer.push(".assembly ");
111 buffer.push(&self.name);
112 buffer.push(" {}")
113 }
114}
115
116#[cfg(feature = "oak-pretty-print")]
117impl AsDocument for Assembly {
118 type Params = ();
119
120 fn as_document(&self, _params: &Self::Params) -> Document<'_> {
121 Document::Text(format!(".assembly {} {{}}", self.name).into())
122 }
123}
124
125#[derive(Clone, Debug, PartialEq, Eq, Hash)]
127#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
128pub struct Class {
129 pub name: String,
131 pub methods: Vec<Method>,
133 #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
135 pub span: Range<usize>,
136}
137
138impl ToSource for Class {
139 fn to_source(&self, buffer: &mut SourceBuffer) {
140 buffer.push(".class public auto ansi beforefieldinit ");
141 buffer.push(&self.name);
142 buffer.push("\n{");
143 for method in &self.methods {
144 buffer.push("\n");
145 method.to_source(buffer)
146 }
147 buffer.push("\n}")
148 }
149}
150
151#[cfg(feature = "oak-pretty-print")]
152impl AsDocument for Class {
153 type Params = ();
154
155 fn as_document(&self, _params: &Self::Params) -> Document<'_> {
156 Document::Concat(vec![
157 Document::Text(format!(".class public auto ansi beforefieldinit {}", self.name).into()),
158 Document::Line,
159 Document::Text("{".into()),
160 Document::indent(Document::join(self.methods.iter().map(|m| m.as_document(&())), Document::Line)),
161 Document::Text("}".into()),
162 ])
163 }
164}
165
166#[derive(Clone, Debug, PartialEq, Eq, Hash)]
168#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
169pub struct Method {
170 pub name: String,
172 pub instructions: Vec<Instruction>,
174 #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
176 pub span: Range<usize>,
177}
178
179impl ToSource for Method {
180 fn to_source(&self, buffer: &mut SourceBuffer) {
181 buffer.push(".method public hidebysig static void ");
182 buffer.push(&self.name);
183 buffer.push("() cil managed\n{");
184 if !self.instructions.is_empty() {
185 buffer.push("\n .entrypoint");
186 for inst in &self.instructions {
187 buffer.push("\n ");
188 inst.to_source(buffer)
189 }
190 }
191 buffer.push("\n}")
192 }
193}
194
195#[cfg(feature = "oak-pretty-print")]
196impl AsDocument for Method {
197 type Params = ();
198
199 fn as_document(&self, _params: &Self::Params) -> Document<'_> {
200 let mut body = vec![Document::Text(".entrypoint".into()), Document::Line];
201 body.extend(self.instructions.iter().map(|i| i.as_document(&())));
202
203 Document::Concat(vec![
204 Document::Text(format!(".method public hidebysig static void {}() cil managed", self.name).into()),
205 Document::Line,
206 Document::Text("{".into()),
207 Document::indent(Document::join(body, Document::Line)),
208 Document::Text("}".into()),
209 ])
210 }
211}
212
213#[derive(Clone, Debug, PartialEq, Eq, Hash)]
215#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
216pub enum Instruction {
217 Simple(String),
219 String(String),
221 Call(String),
223}
224
225impl ToSource for Instruction {
226 fn to_source(&self, buffer: &mut SourceBuffer) {
227 match self {
228 Instruction::Simple(s) => buffer.push(s),
229 Instruction::String(s) => {
230 buffer.push("ldstr \"");
231 buffer.push(s);
232 buffer.push("\"")
233 }
234 Instruction::Call(s) => {
235 buffer.push("call ");
236 buffer.push(s)
237 }
238 }
239 }
240}
241
242#[cfg(feature = "oak-pretty-print")]
243impl AsDocument for Instruction {
244 type Params = ();
245
246 fn as_document(&self, _params: &Self::Params) -> Document<'_> {
247 match self {
248 Instruction::Simple(s) => Document::Text(s.clone().into()),
249 Instruction::String(s) => Document::Text(format!("ldstr \"{}\"", s).into()),
250 Instruction::Call(s) => Document::Text(format!("call {}", s).into()),
251 }
252 }
253}