From e2b7ec3e430b37560fcc75637de9eab06bd84d5c Mon Sep 17 00:00:00 2001 From: raphy Date: Mon, 8 May 2023 12:37:27 +0200 Subject: [PATCH] added rudimentary pretty printer. Right now utterly useless. --- src/assembler/AST.rs | 32 ++++++ src/assembler/mod.rs | 2 + src/cli/mod.rs | 36 +++++- src/lib.rs | 1 + src/main.rs | 49 +++++--- src/pretty_printers/asm.rs | 222 +++++++++++++++++++++++++++++++++++++ src/pretty_printers/bin.rs | 0 src/pretty_printers/mod.rs | 49 ++++++++ 8 files changed, 373 insertions(+), 18 deletions(-) create mode 100644 src/pretty_printers/asm.rs create mode 100644 src/pretty_printers/bin.rs create mode 100644 src/pretty_printers/mod.rs diff --git a/src/assembler/AST.rs b/src/assembler/AST.rs index 6e3d9d6..8d1c4fc 100644 --- a/src/assembler/AST.rs +++ b/src/assembler/AST.rs @@ -1,3 +1,4 @@ + /// Type alias to represent a register. type RegisterMem = String; @@ -38,3 +39,34 @@ pub enum Operation { /// J Type JAL(RegisterMem, RegisterMem, Const), } + + +pub fn print_op(op: Operation) -> String { + + let print_const = |x : Const| { + match x { + Const::CS(i) => i, + Const::C(n) => format!("{}", n), + } + }; + + match op { + Operation::NOP => String::from("nop"), + Operation::HALT => String::from("halt"), + Operation::ADD(a, b, c) => format!("add {} {} {}", a, b, c), + Operation::SUB(a, b, c) => format!("sub {} {} {}", a, b, c), + Operation::AND(a, b, c) => format!("and {} {} {}", a, b, c), + Operation::XOR(a, b, c) => format!("xor {} {} {}", a, b, c), + Operation::SLL(a, b, c) => format!("sll {} {} {}", a, b, c), + Operation::BEQ(a, b, c) => format!("beq {} {} {}", a, b, c), + Operation::BGT(a, b, c) => format!("bgq {} {} {}", a, b, c), + Operation::LOAD(a, b, c) => format!("load {} {} {}", a, b, c), + Operation::STORE(a, b, c) => format!("store {} {} {}", a, b, c), + Operation::SLI(a, c) => format!("sli {} {}", a, print_const(c)), + Operation::ADDI(a, c) => format!("addi {} {}", a, print_const(c)), + Operation::CALL(a, c) => format!("call {} {}", a, print_const(c)), + Operation::JAL(a, b, c) => format!("jal {} {} {}", a, b, print_const(c)), + } + +} + diff --git a/src/assembler/mod.rs b/src/assembler/mod.rs index ac53e37..6f69161 100644 --- a/src/assembler/mod.rs +++ b/src/assembler/mod.rs @@ -6,6 +6,8 @@ mod tests; use encoder::CodeFormat; use encoder::SymbolTable; +pub use AST::print_op; + use log::{trace, debug}; use parser::Section; diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 4d39bfa..8971ed8 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,7 +1,9 @@ -use clap::{Parser, Subcommand}; +use std::fmt::Display; + +use clap::{Parser, Subcommand, ValueEnum}; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] @@ -21,6 +23,36 @@ pub enum Subc { Run {filename: String}, Build {filename: String, output: String}, - View {filename: String}, + View { + + filename: String, + + #[arg(short, long, default_value_t = FormatKind::FancyTable)] + format: FormatKind + + }, + Debug {} } + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)] +pub enum FormatKind { + FancyTable, + Raw, + BinSections, + Serializable, +} + + +impl Display for FormatKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + FormatKind::FancyTable => write!(f, "fancy-table"), + FormatKind::Raw => write!(f, "raw"), + FormatKind::BinSections => write!(f, "memory"), + FormatKind::Serializable => write!(f, "rust"), + } + } +} + + diff --git a/src/lib.rs b/src/lib.rs index f4921bd..5f2fa22 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ pub mod jit; pub mod loader; pub mod cli; pub mod assembler; +pub mod pretty_printers; // pub fn interpret_as_signed(x: u16) -> i16 { diff --git a/src/main.rs b/src/main.rs index 41d3dfd..adc9348 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,12 @@ +use dekejit::assembler::parser; use dekejit::cli::{Cli, Subc::*}; use dekejit::cpu::IOBuffer; use dekejit::cpu::CPU; +use dekejit::pretty_printers::*; use clap::Parser; -use dekejit::loader::loader::prepare_memory; +use dekejit::loader::loader::{prepare_memory, self}; use dekejit::loader::loader::read_binary; use dekejit::transmute_to_vecu8_as_is; use log::{info, debug}; @@ -47,7 +49,7 @@ fn main() { let bin = prepare_memory(sections); - info!("{:?}", bin); + // trace!("{:?}", bin); let mut env = IOBuffer::default(); // @@ -101,31 +103,46 @@ fn main() { }; } - Some(View {filename}) => { + Some(View {filename, format}) => { info!("Trying to read {}", filename); - let Ok(content) = std::fs::read(&filename) else { - println!("File {} does not exist or cannot be read.", &filename); + let Ok(content) = std::fs::read_to_string(&filename) else { + println!("File {} does not exist or cannot be read", &filename); return; }; + let p = parser::Parser::new(content); - let bytes = dekejit::transmute_to_vecu16_as_is(content.to_vec()); - info!("{:?}", bytes); + let s = p.parse_sections().unwrap(); + + println!("{}", format_code(dekejit::pretty_printers::CodeWrapper::Asm(s), format).unwrap()); + + // let Ok(content) = std::fs::read(&filename) else { + // println!("File {} does not exist or cannot be read.", &filename); + // return; + // }; + // + // + // let bytes = dekejit::transmute_to_vecu16_as_is(content.to_vec()); + // + // info!("{:?}", bytes); + // + // info!("Begin parsing file {}", &filename); + // let sections = match read_binary(&bytes) { + // Ok(k) => k, + // Err(p) => { + // println!("Parsing error: {:?}", p); + // return; + // } + // }; + // + // println!("{:?}", sections); - info!("Begin parsing file {}", &filename); - let sections = match read_binary(&bytes) { - Ok(k) => k, - Err(p) => { - println!("Parsing error: {:?}", p); - return; - } - }; - println!("{:?}", sections); } + Some(Debug) => {}, None => {} } diff --git a/src/pretty_printers/asm.rs b/src/pretty_printers/asm.rs new file mode 100644 index 0000000..6fbe579 --- /dev/null +++ b/src/pretty_printers/asm.rs @@ -0,0 +1,222 @@ + + +use crate::assembler::{self, print_op}; + +use super::{Section, SectionContent}; + +/// Aaaand we don't know the size of the terminal. +/// We'll just pretend it is 80 characters. +/// It would be wise to be able to change it with +/// a parameter. Later! #TODO + +pub const TERMSIZE : u8 = 80; + + +pub fn to_table(secs: Vec
) -> String { + + let mut res = String::new(); + + let mut header = String::from("┌"); + + for _ in 2..TERMSIZE { + header.push('─'); + } + + header.push('┐'); + header.push('\n'); +// ┏ +// ┐ +// + res.push_str(&header); + + let sections_strings : Vec = secs.into_iter().map(|x| draw_section(x)).collect(); + + let cont = sections_strings.join(&draw_separator(TERMSIZE)); + + res.push_str(&cont); + + let mut footer = String::from("└"); + + for _ in 2..TERMSIZE { + footer.push('─'); + } + + footer.push('┘'); + + res.push_str(&footer); + + + + // for i in -40..40 { + // let p : i32 = (c as u32).try_into().unwrap(); + // res.push_str(&format!("{}\n", char::from_u32((p + i).try_into().unwrap()).unwrap())); + // } + + + return res; + +} + + +fn draw_section(sec: Section) -> String { + + let mut res = String::new(); + + res.push_str(&format_line(&format!("{}", sec.name), 4, TERMSIZE)); + + res.push_str(&draw_separator(TERMSIZE)); + + match sec.content { + SectionContent::Code(ops) => { + // we just print ops again. + // we could make this prettier. + + for op in ops { + res.push_str(&format_line(&print_op(op), 8, TERMSIZE)); + + } + }, + SectionContent::CString(s) => res.push_str(&s), + SectionContent::CVec() => todo!(), + } + + + return res; + + + +} + +/// Formats a line in a box, with said indendation. +fn format_line(s: &str, indentation: u8, len: u8) -> String { + let mut res = String::from("│"); + + // TODO: UTF8 GRAPHEMES???; + + for _ in 1..indentation { + res.push(' '); + } + + res.push_str(s); + + // if ASCII, or even extended ascii are concerned, + // this works. But we need to get the number of graphemes, which can be tricky + // in utf-8. + + for _ in res.len()..(len + 1) as usize { + res.push(' '); + } + + res.push_str("│\n"); + + + return res; +} + + +/// Draws a horizontal line of specified length. +/// +fn draw_separator(len: u8) -> String { + + let mut res = String::from("├"); + + for _ in 2..len { + res.push('─'); + } + + res.push('┤'); + res.push('\n'); + + return res; + +} + + + + +// fn wrap_in_bars(&str, indentation: u8) -> String { +// +// +// +// } + +// ┄ +// ┅ +// ┆ +// ┇ +// ┈ +// ┉ +// ┊ +// ┋ +// ┌ +// ┍ +// ┎ +// ┏ +// ┐ +// ┑ +// ┒ +// ┓ +// └ +// ┕ +// ┖ +// ┗ +// ┘ +// ┙ +// ┚ +// ┛ +// ├ +// ┝ +// ┞ +// ┟ +// ┠ +// ┡ +// ┢ +// ┣ +// ┤ +// ┥ +// ┦ +// ┧ +// ┨ +// ┩ +// ┪ +// ┫ +// ┬ +// ┭ +// ┮ +// ┯ +// ┰ +// ┱ +// ┲ +// ┳ +// ┴ +// ┵ +// ┶ +// ┷ +// ┸ +// ┹ +// ┺ +// ┻ +// ┼ +// ┽ +// ┾ +// ┿ +// ╀ +// ╁ +// ╂ +// ╃ +// ╄ +// ╅ +// ╆ +// ╇ +// ╈ +// ╉ +// ╊ +// ╋ +// ╌ +// ╍ +// ╎ +// ╏ +// ═ +// ║ +// ╒ +// ╓ diff --git a/src/pretty_printers/bin.rs b/src/pretty_printers/bin.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/pretty_printers/mod.rs b/src/pretty_printers/mod.rs new file mode 100644 index 0000000..09f7fa9 --- /dev/null +++ b/src/pretty_printers/mod.rs @@ -0,0 +1,49 @@ + +mod asm; +mod bin; + +use asm::*; +use bin::*; + +use crate::loader::loader::read_binary; + +// We'll use the Cli to dictate which format kinds are available. +use super::cli::FormatKind; + + +use super::assembler::parser::{*}; + + +pub enum CodeWrapper { + Bin(Vec), + Asm(Vec
) +} + + + +pub fn format_code(c: CodeWrapper, k: FormatKind) -> Result { + + + match k { + FormatKind::FancyTable => match c { + CodeWrapper::Bin(_) => todo!(), + CodeWrapper::Asm(sections) => { + return Ok(to_table(sections))}, + }, + FormatKind::Raw => match c { + CodeWrapper::Bin(_) => todo!(), + CodeWrapper::Asm(_) => todo!(), + }, + FormatKind::BinSections => match c { + CodeWrapper::Bin(_) => todo!(), + CodeWrapper::Asm(_) => todo!(), + }, + FormatKind::Serializable => match c { + CodeWrapper::Bin(_) => todo!(), + CodeWrapper::Asm(_) => todo!(), + }, + } + + +} +