#![doc = include_str!("readme.md")]
use core::range::Range;
use oak_core::source::{SourceBuffer, ToSource};
#[cfg(feature = "oak-pretty-print")]
use oak_pretty_print::{AsDocument, Document};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MsilTypedRoot<'a> {
red: oak_core::RedNode<'a, crate::language::MsilLanguage>,
}
impl<'a> oak_core::tree::TypedNode<'a> for MsilTypedRoot<'a> {
type Language = crate::language::MsilLanguage;
fn cast(node: oak_core::RedNode<'a, Self::Language>) -> Option<Self> {
if node.kind::<crate::parser::element_type::MsilElementType>() == crate::parser::element_type::MsilElementType::Root { Some(Self { red: node }) } else { None }
}
fn green(&self) -> &oak_core::GreenNode<'a, Self::Language> {
self.red.green()
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MsilRoot {
pub items: Vec<Item>,
}
impl ToSource for MsilRoot {
fn to_source(&self, buffer: &mut SourceBuffer) {
for item in &self.items {
item.to_source(buffer);
buffer.push("\n")
}
}
}
#[cfg(feature = "oak-pretty-print")]
impl AsDocument for MsilRoot {
type Params = ();
fn as_document(&self, _params: &Self::Params) -> Document<'_> {
Document::join(self.items.iter().map(|i| i.as_document(&())), Document::Line)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Item {
Assembly(Assembly),
Module(String),
Class(Class),
AssemblyExtern(String),
}
impl ToSource for Item {
fn to_source(&self, buffer: &mut SourceBuffer) {
match self {
Item::Assembly(a) => a.to_source(buffer),
Item::Module(m) => {
buffer.push(".module ");
buffer.push(m)
}
Item::Class(c) => c.to_source(buffer),
Item::AssemblyExtern(a) => {
buffer.push(".assembly extern ");
buffer.push(a);
buffer.push(" {}")
}
}
}
}
#[cfg(feature = "oak-pretty-print")]
impl AsDocument for Item {
type Params = ();
fn as_document(&self, _params: &Self::Params) -> Document<'_> {
match self {
Item::Assembly(a) => a.as_document(&()),
Item::Module(m) => Document::Text(format!(".module {}", m).into()),
Item::Class(c) => c.as_document(&()),
Item::AssemblyExtern(a) => Document::Text(format!(".assembly extern {} {{}}", a).into()),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Assembly {
pub name: String,
#[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
pub span: Range<usize>,
}
impl ToSource for Assembly {
fn to_source(&self, buffer: &mut SourceBuffer) {
buffer.push(".assembly ");
buffer.push(&self.name);
buffer.push(" {}")
}
}
#[cfg(feature = "oak-pretty-print")]
impl AsDocument for Assembly {
type Params = ();
fn as_document(&self, _params: &Self::Params) -> Document<'_> {
Document::Text(format!(".assembly {} {{}}", self.name).into())
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Class {
pub name: String,
pub methods: Vec<Method>,
#[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
pub span: Range<usize>,
}
impl ToSource for Class {
fn to_source(&self, buffer: &mut SourceBuffer) {
buffer.push(".class public auto ansi beforefieldinit ");
buffer.push(&self.name);
buffer.push("\n{");
for method in &self.methods {
buffer.push("\n");
method.to_source(buffer)
}
buffer.push("\n}")
}
}
#[cfg(feature = "oak-pretty-print")]
impl AsDocument for Class {
type Params = ();
fn as_document(&self, _params: &Self::Params) -> Document<'_> {
Document::Concat(vec![
Document::Text(format!(".class public auto ansi beforefieldinit {}", self.name).into()),
Document::Line,
Document::Text("{".into()),
Document::indent(Document::join(self.methods.iter().map(|m| m.as_document(&())), Document::Line)),
Document::Text("}".into()),
])
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Method {
pub name: String,
pub instructions: Vec<Instruction>,
#[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
pub span: Range<usize>,
}
impl ToSource for Method {
fn to_source(&self, buffer: &mut SourceBuffer) {
buffer.push(".method public hidebysig static void ");
buffer.push(&self.name);
buffer.push("() cil managed\n{");
if !self.instructions.is_empty() {
buffer.push("\n .entrypoint");
for inst in &self.instructions {
buffer.push("\n ");
inst.to_source(buffer)
}
}
buffer.push("\n}")
}
}
#[cfg(feature = "oak-pretty-print")]
impl AsDocument for Method {
type Params = ();
fn as_document(&self, _params: &Self::Params) -> Document<'_> {
let mut body = vec![Document::Text(".entrypoint".into()), Document::Line];
body.extend(self.instructions.iter().map(|i| i.as_document(&())));
Document::Concat(vec![
Document::Text(format!(".method public hidebysig static void {}() cil managed", self.name).into()),
Document::Line,
Document::Text("{".into()),
Document::indent(Document::join(body, Document::Line)),
Document::Text("}".into()),
])
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Instruction {
Simple(String),
String(String),
Call(String),
}
impl ToSource for Instruction {
fn to_source(&self, buffer: &mut SourceBuffer) {
match self {
Instruction::Simple(s) => buffer.push(s),
Instruction::String(s) => {
buffer.push("ldstr \"");
buffer.push(s);
buffer.push("\"")
}
Instruction::Call(s) => {
buffer.push("call ");
buffer.push(s)
}
}
}
}
#[cfg(feature = "oak-pretty-print")]
impl AsDocument for Instruction {
type Params = ();
fn as_document(&self, _params: &Self::Params) -> Document<'_> {
match self {
Instruction::Simple(s) => Document::Text(s.clone().into()),
Instruction::String(s) => Document::Text(format!("ldstr \"{}\"", s).into()),
Instruction::Call(s) => Document::Text(format!("call {}", s).into()),
}
}
}