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