Compare commits

..

No commits in common. "61a3a305b6339baca5c0252e344809cdb1f35ae6" and "03668a71e88e45c4123305283de17cbd4f3f1773" have entirely different histories.

15 changed files with 162 additions and 446 deletions

View File

@ -6,9 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
clap = { version = "4.4.8", features = ["derive"] } clap = { version = "4.2.5", features = ["derive"] }
log = "0.4" log = "0.4.17"
simple_logger = "4.2.0" simple_logger = "4.1.0"
[[bin]]
name = "dekejit"

View File

@ -1,29 +0,0 @@
# DEKEJIT
## EXPERIMENTAL!! NOTHING WORKS AND MAKES SENSE!!!
This is an experimental 16-bit virtual machine.
A lenghty description can be found [here.](spec.md)
If you want to have a great time, you can try experimenting with it:
### Installation
To install the program, run `cargo install --path .` .
This currently builds the virtual machine, which can execute binary files, and an assembler which can turn text files into binary files.
### Usage
`dekejit build <assemby_file> <output_file>` will read the provided `<assembly_file>` and write a binary file `<output_file>` which can be executed using `dekejit run <binary_file>`
A few example files can be found in `tests/assembly/`.
### Future
Right now the only possible way to use the virtual machine is to manually write the assembly.
In the future, small compilers for toy languages will be built that target my toy assembly.

47
spec.md
View File

@ -10,8 +10,8 @@ Since I'm studying riscV, this will be a lot riscv inspired.
The gravejit virtual machine sports 16 16-bit registers (plus the program counter!) and 16 operations. The gravejit virtual machine sports 16 16-bit registers (plus the program counter!) and 16 operations.
Here is the list of registers together with memonics. Here is the list of registers togheter with memonics.
```
0 : zero // register 0 is always 0. 0 : zero // register 0 is always 0.
1 : ra // return address 1 : ra // return address
2 : sp // stack pointer 2 : sp // stack pointer
@ -30,33 +30,32 @@ Here is the list of registers together with memonics.
15: t4 // don't know what to do with this 15: t4 // don't know what to do with this
pc: program counter. pc: program counter.
```
## ISA ## ISA
| opcode | memonic | format | description | opcode | memonic | format | description
| ------ | -------------- | ------- | --------------------------------------- |
| 0000 | NOP | just 0s'| Does nothing. | 0000 | NOP | just 0s'| Does nothing.
| 0001 | ADD s0 s1 s2 | R | s0 = s1 + s2 | 0001 | ADD s0 s1 s2 | R | s0 = s1 + s2
| 0010 | SUB s0 s1 s2 | R | s0 = s1 - s2 | 0010 | SUB s0 s1 s2 | R | s0 = s1 - s2
| 0011 | AND s0 s1 s2 | R | s0 = s1 && s2 | 0011 | AND s0 s1 s2 | R | s0 = s1 && s2
| 0100 | XOR s0 s1 s2 | R | s0 = s1 xor s2 | 0100 | XOR s0 s1 s2 | R | s0 = s1 xor s2
| 0101 | SLL s0 s1 s2 | R | s0 = s1 << s2 | 0101 | SLL s0 s1 s2 | R | s0 = s1 << s2
| 0110 | SLI s0 c | I | s0 = s0 << c | 0110 | SLI s0 c | I | s0 = s0 << c
| 0111 | ADDI s0 c | I | s0 = s0 + c | 0111 | ADDI s0 c | I | s0 = s0 + c
| 1000 | BEQ s0 s1 s2 | R | if (s1 == s2) -> pc = s0 | 1000 | BEQ s0 s1 s2 | R | if (s1 == s2) -> pc = s0
| 1001 | BGT s0 s1 s2 | R | if (s1 > s2) -> pc = s0 | 1001 | BGT s0 s1 s2 | R | if (s1 > s2) -> pc = s0
| 1010 | JAL s0 s1 c | J | s0 = pc+1; pc += s1 + c; | 1010 | JAL s0 s1 c | J | s0 = pc+1; pc += s1 + c;
| 1011 | | | #TODO? | 1011 |
| 1100 | LOAD s0 s1 s2 | R | loads s1 + shift by s2 in s0 | 1100 | LOAD s0 s1 s2 | R | loads s1 + shift by s2 in s0
| 1101 | STORE s0 s1 s2 | R | stores s0 in address s1 + shift by s2 | 1101 | STORE s0 s1 s2| R | stores s0 in address s1 + shift by s2
| 1110 | CALL s0 c | I | performs system call | 1110 | CALL s0 c | I | performs system call
| 1111 | HALT | just 1s'| halt, and possibly catch fire. | 1111 | HALT | just 1s'| halt, and possibly catch fire.
### Operation formats: ### Operation formats:
Each instruction is 16 bits long. Each istruction is 16 bits long.
The first 4 most-significant bits are the opcode. The first 4 most-significant bits are the opcode.
Constants (c in the above table) are always considered signed, and written in Constants (c in the above table) are always considered signed, and written in
two's compliment. Sign extension also takes place whenever needed. two's compliment. Sign extension also takes place whenever needed.
@ -91,7 +90,9 @@ The constant is added to the value of the second register argument.
### JIT's system calls: ### JIT's system calls:
What the `CALL` instruction does is up to implementations. The JIT can decide what to do with the register s0 and the number c. It could provide mechanisms to perform I/O on a true filesystem, on an emulated filesystem, or it could do something else entirely, i.e, something web related. the `CALL` instruction is a bit of a hack because I want to load more functionality into the thing.
The JIT can decide what to do with the register s0 and the number c.
It should be possible to open files, write files, read stdin, write to stdout, etc...
#### io\_vec: first systemcall environment #### io\_vec: first systemcall environment

40
src/assembler/AST.rs Normal file
View File

@ -0,0 +1,40 @@
/// Type alias to represent a register.
type RegisterMem = String;
/// Type alias to represent a label used in place of a const.
pub type ConstId = String;
/// A const can either be a number or a label of some section.
#[derive(Debug)]
pub enum Const {
CS(ConstId),
C(u8),
}
/// Operations as they are parsed, before translating them to binary.
/// This type is used internally by the parser and
/// differs from cpu::Operation.
#[derive(Debug)]
pub enum Operation {
/// No type.
NOP,
HALT,
/// R type
ADD(RegisterMem, RegisterMem, RegisterMem),
SUB(RegisterMem, RegisterMem, RegisterMem),
AND(RegisterMem, RegisterMem, RegisterMem),
XOR(RegisterMem, RegisterMem, RegisterMem),
SLL(RegisterMem, RegisterMem, RegisterMem),
BEQ(RegisterMem, RegisterMem, RegisterMem),
BGT(RegisterMem, RegisterMem, RegisterMem),
LOAD(RegisterMem, RegisterMem, RegisterMem),
STORE(RegisterMem, RegisterMem, RegisterMem),
/// I Type
SLI(RegisterMem, Const),
ADDI(RegisterMem, Const),
CALL(RegisterMem, Const),
/// J Type
JAL(RegisterMem, RegisterMem, Const),
}

View File

@ -1,72 +0,0 @@
/// Type alias to represent a register.
type RegisterMem = String;
/// Type alias to represent a label used in place of a const.
pub type ConstId = String;
/// A const can either be a number or a label of some section.
#[derive(Debug)]
pub enum Const {
CS(ConstId),
C(u8),
}
/// Operations as they are parsed, before translating them to binary.
/// This type is used internally by the parser and
/// differs from cpu::Operation.
#[derive(Debug)]
pub enum Operation {
/// No type.
NOP,
HALT,
/// R type
ADD(RegisterMem, RegisterMem, RegisterMem),
SUB(RegisterMem, RegisterMem, RegisterMem),
AND(RegisterMem, RegisterMem, RegisterMem),
XOR(RegisterMem, RegisterMem, RegisterMem),
SLL(RegisterMem, RegisterMem, RegisterMem),
BEQ(RegisterMem, RegisterMem, RegisterMem),
BGT(RegisterMem, RegisterMem, RegisterMem),
LOAD(RegisterMem, RegisterMem, RegisterMem),
STORE(RegisterMem, RegisterMem, RegisterMem),
/// I Type
SLI(RegisterMem, Const),
ADDI(RegisterMem, Const),
CALL(RegisterMem, Const),
/// 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)),
}
}

View File

@ -1,8 +1,9 @@
use super::ast::*; use super::AST::*;
use crate::cpu::get_num; use crate::cpu::get_num;
use log::{trace, warn}; use log::{trace, warn};
/// Trait to represent a format we can translate our assembly to. /// Trait to represent a format we can translate our assembly to.
pub trait CodeFormat { pub trait CodeFormat {
fn encode_op(op: &Operation, sy: &SymbolTable, current_pc: u16) -> Option<Self> fn encode_op(op: &Operation, sy: &SymbolTable, current_pc: u16) -> Option<Self>
@ -35,7 +36,7 @@ impl SymbolTable {
// query, sy // query, sy
// ); // );
warn!("Symbol {} not found in symbol table.", query); warn!("Symbol {} not found in symbol table.", query);
return None; return None
} }
} }

View File

@ -1,13 +1,12 @@
mod ast; mod AST;
pub mod encoder; pub mod encoder;
pub mod parser; pub mod parser;
mod tests;
use encoder::CodeFormat; use encoder::CodeFormat;
use encoder::SymbolTable; use encoder::SymbolTable;
pub use ast::print_op; use log::{trace, debug};
use log::{debug, trace};
use parser::Section; use parser::Section;
@ -62,16 +61,14 @@ impl Section {
return Some(crate::loader::Section::new(self.name.clone(), &res)); return Some(crate::loader::Section::new(self.name.clone(), &res));
} }
SectionContent::CString(s) => { SectionContent::CString(s) => {
return Some(crate::loader::Section::new( return Some(crate::loader::Section::new(self.name.clone(), &make_string(s)));
self.name.clone(),
&make_string(s),
));
} }
SectionContent::CVec() => todo!(), SectionContent::CVec() => todo!(),
} }
} }
} }
/// Sorts a list of sections. /// Sorts a list of sections.
/// All .text sections containing code are /// All .text sections containing code are
/// put at the beginning of the binary file, in the order they /// put at the beginning of the binary file, in the order they

View File

@ -1,4 +1,4 @@
use super::ast::{Const, Operation}; use super::AST::{Const, Operation};
use Operation::*; use Operation::*;
use log::*; use log::*;
@ -21,7 +21,9 @@ pub struct Parser {
impl Parser { impl Parser {
pub fn new(i: String) -> Self { pub fn new(i: String) -> Self {
Parser { input: sanitize(i) } Parser {
input: sanitize(i),
}
} }
} }
@ -87,20 +89,19 @@ fn take_between(i: &str, start: &str, stop: &str) -> Option<(String, String)> {
return take_alpha_till(&s1, stop); return take_alpha_till(&s1, stop);
} }
/// finds special escaped characters in a string
/// (such as \n) and replaces them with the actual special
/// character
fn escaped_codes() {}
#[test] #[test]
fn take_between_test1() { fn take_between_test() {
assert_eq!( assert_eq!(
take_between("\"wow\" etc", "\"", "\""), take_between("\"wow\" etc", "\"", "\""),
Some(("wow".to_string(), " etc".to_string())) Some(("wow".to_string(), " etc".to_string()))
); );
} }
/// finds special escaped characters in a string
/// (such as \n) and replaces them with the actual special
/// character
/// #TODO: do we need this? I forgot.
fn _escaped_codes() {}
//// SECTION PARSING //// SECTION PARSING
/// Enum to represent possible section content. /// Enum to represent possible section content.
@ -120,6 +121,7 @@ pub struct Section {
pub content: SectionContent, pub content: SectionContent,
} }
impl Parser { impl Parser {
pub fn parse_sections(&self) -> Result<Vec<Section>, ParseError> { pub fn parse_sections(&self) -> Result<Vec<Section>, ParseError> {
let mut res = vec![]; let mut res = vec![];
@ -149,12 +151,8 @@ impl Parser {
}) })
} }
"asciiz" => { "asciiz" => {
let Some(s) = lines.next() else { let Some(s) = lines.next() else {return Err(ParseError::UnexpectedEOF)};
return Err(ParseError::UnexpectedEOF); let Some((s, _)) = take_between(s.trim(), "\"", "\"") else {return Err(ParseError::BadSectionContent)};
};
let Some((s, _)) = take_between(s.trim(), "\"", "\"") else {
return Err(ParseError::BadSectionContent);
};
res.push(Section { res.push(Section {
name: name.trim().to_owned(), name: name.trim().to_owned(),
content: CString(s), content: CString(s),
@ -202,9 +200,7 @@ fn parse_code_line(i: &str) -> Result<Operation, ParseError> {
// every operation has at most 3 arguments // every operation has at most 3 arguments
let mut bits = i.split_whitespace(); let mut bits = i.split_whitespace();
trace!("current parse code line: {}", i); trace!("current parse code line: {}", i);
let Some(op) = bits.next() else { let Some(op) = bits.next() else {return Err(ParseError::BadInstruction)};
return Err(ParseError::BadInstruction);
};
// no type // no type
match op { match op {
@ -218,12 +214,8 @@ fn parse_code_line(i: &str) -> Result<Operation, ParseError> {
}; };
// I-type // I-type
let Some(r1) = bits.next() else { let Some(r1) = bits.next() else {return Err(ParseError::BadInstruction)};
return Err(ParseError::BadInstruction); let Some(r2) = bits.next() else {return Err(ParseError::BadInstruction)};
};
let Some(r2) = bits.next() else {
return Err(ParseError::BadInstruction);
};
match op { match op {
"addi" => { "addi" => {
@ -238,9 +230,7 @@ fn parse_code_line(i: &str) -> Result<Operation, ParseError> {
_ => {} _ => {}
} }
let Some(r3) = bits.next() else { let Some(r3) = bits.next() else {return Err(ParseError::BadInstruction)};
return Err(ParseError::BadInstruction);
};
// R-type // R-type
match op { match op {
@ -287,17 +277,3 @@ fn parse_const(i: &str) -> Result<Const, ParseError> {
}; };
return Ok(Const::C(num)); return Ok(Const::C(num));
} }
/// TESTS
#[test]
fn parser_test() {
let code = std::fs::read_to_string("./tests/assembly/hello_world.grasm").unwrap();
let parser = Parser::new(code);
let _r = parser.parse_sections();
// #TODO: WRITE PARSER TEST SUITE!
//assert_eq!(r, Ok(vec![]));
}

18
src/assembler/tests.rs Normal file
View File

@ -0,0 +1,18 @@
// use super::*;
#[cfg(test)]
mod tests {
use crate::assembler::parser;
#[test]
fn parser_test() {
println!("Parser test begins");
let code = std::fs::read_to_string("./tests/assembly/hello_world.grasm").unwrap();
let parser = parser::Parser::new(code);
let r = parser.parse_sections().unwrap();
println!("Parsed sections: {:?}", r);
}
}

View File

@ -1,10 +1,13 @@
use std::fmt::Display;
use clap::{Parser, Subcommand, ValueEnum};
use clap::{Parser, Subcommand};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(author, version, about, long_about = None, arg_required_else_help = true)] #[command(author, version, about, long_about = None)]
pub struct Cli { pub struct Cli {
#[command(subcommand)] #[command(subcommand)]
pub comm: Option<Subc>, pub comm: Option<Subc>,
@ -15,37 +18,9 @@ pub struct Cli {
#[derive(Subcommand, Debug)] #[derive(Subcommand, Debug)]
pub enum Subc { pub enum Subc {
Run {
filename: String,
},
Build {
filename: String,
output: String,
},
View {
filename: String,
#[arg(short, long, default_value_t = FormatKind::FancyTable)] Run {filename: String},
format: FormatKind, Build {filename: String, output: String},
}, View {filename: String},
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"),
}
}
} }

View File

@ -1,11 +1,11 @@
use std::mem::transmute; use std::mem::transmute;
pub mod assembler;
pub mod cli;
pub mod cpu; pub mod cpu;
pub mod jit; pub mod jit;
pub mod loader; pub mod loader;
pub mod pretty_printers; pub mod cli;
pub mod assembler;
//
pub fn interpret_as_signed(x: u16) -> i16 { pub fn interpret_as_signed(x: u16) -> i16 {
// the two types have the same size. // the two types have the same size.
@ -22,13 +22,14 @@ pub fn interpret_as_unsigned(x: i16) -> u16 {
} }
pub fn transmute_to_vecu16_as_is(x: Vec<u8>) -> Vec<u16> { pub fn transmute_to_vecu16_as_is(x: Vec<u8>) -> Vec<u16> {
let mut rb = x.iter(); let mut rb = x.iter();
// raw_bytes must be converted to u16. // raw_bytes must be converted to u16.
// //
let bytes: Vec<u16> = { let bytes: Vec<u16> = {
let mut res = vec![]; let mut res = vec![];
// Depends on the order in which the arguments of a tuple are // highly cursed: depends on the order in which the arguments of a tuple are
// evaluated. Rust guarantees to be left-to-right. // evaluated. Does its job!
while let (Some(word0), Some(word1)) = (rb.next(), rb.next()) { while let (Some(word0), Some(word1)) = (rb.next(), rb.next()) {
// println!("Pair: {}, {}, word: {:?}", word0, word1, raw_bytes); // println!("Pair: {}, {}, word: {:?}", word0, word1, raw_bytes);
res.push(((*word0 as u16) << 8) + (*word1 as u16)); res.push(((*word0 as u16) << 8) + (*word1 as u16));
@ -54,6 +55,7 @@ pub fn transmute_to_vecu16_as_is(x: Vec<u8>) -> Vec<u16> {
} }
pub fn transmute_to_vecu8_as_is(x: Vec<u16>) -> Vec<u8> { pub fn transmute_to_vecu8_as_is(x: Vec<u16>) -> Vec<u8> {
let mut bytes = vec![]; let mut bytes = vec![];
for b in x.iter() { for b in x.iter() {

View File

@ -1,14 +1,13 @@
use dekejit::assembler::parser;
use dekejit::cli::{Cli, Subc::*}; use dekejit::cli::{Cli, Subc::*};
use dekejit::cpu::IOBuffer; use dekejit::cpu::IOBuffer;
use dekejit::cpu::CPU; use dekejit::cpu::CPU;
use dekejit::pretty_printers::*;
use clap::Parser; use clap::Parser;
use dekejit::loader::loader::prepare_memory;
use dekejit::loader::loader::read_binary; use dekejit::loader::loader::read_binary;
use dekejit::loader::loader::{self, prepare_memory};
use dekejit::transmute_to_vecu8_as_is; use dekejit::transmute_to_vecu8_as_is;
use log::{debug, info}; use log::{info, debug};
fn main() { fn main() {
let cli: Cli = Cli::parse(); let cli: Cli = Cli::parse();
@ -26,6 +25,7 @@ fn main() {
match cli.comm { match cli.comm {
Some(Run { filename }) => { Some(Run { filename }) => {
info!("Trying to read {}", filename); info!("Trying to read {}", filename);
let Ok(content) = std::fs::read(&filename) else { let Ok(content) = std::fs::read(&filename) else {
@ -33,6 +33,7 @@ fn main() {
return; return;
}; };
let bytes = dekejit::transmute_to_vecu16_as_is(content.to_vec()); let bytes = dekejit::transmute_to_vecu16_as_is(content.to_vec());
info!("Begin parsing file {}", &filename); info!("Begin parsing file {}", &filename);
@ -46,7 +47,7 @@ fn main() {
let bin = prepare_memory(sections); let bin = prepare_memory(sections);
// trace!("{:?}", bin); info!("{:?}", bin);
let mut env = IOBuffer::default(); let mut env = IOBuffer::default();
// //
@ -60,6 +61,7 @@ fn main() {
}; };
} }
Some(Build { filename, output }) => { Some(Build { filename, output }) => {
let Ok(inp_content) = std::fs::read_to_string(filename.clone()) else { let Ok(inp_content) = std::fs::read_to_string(filename.clone()) else {
println!("Could not read file {}", &filename); println!("Could not read file {}", &filename);
return; return;
@ -72,9 +74,10 @@ fn main() {
Err(p_err) => { Err(p_err) => {
println!("Parser error: {:?}", p_err); println!("Parser error: {:?}", p_err);
return; return;
} },
}; };
let Some(bin) = dekejit::assembler::to_binary(sections) else { let Some(bin) = dekejit::assembler::to_binary(sections) else {
println!("Unspecified error while converting file to binary. Must fix."); println!("Unspecified error while converting file to binary. Must fix.");
return; return;
@ -88,57 +91,46 @@ fn main() {
let out_bin = transmute_to_vecu8_as_is(out_bin); let out_bin = transmute_to_vecu8_as_is(out_bin);
info!("{:?}", out_bin); info!("{:?}", out_bin);
match std::fs::write(output.clone(), out_bin) { match std::fs::write(output.clone(), out_bin) {
Ok(_) => {} Ok(_) => {},
Err(_) => { Err(_) => {
println!("could not write file {}", output); println!("could not write file {}", output);},
}
}; };
} }
Some(View { filename, format }) => { Some(View {filename}) => {
info!("Trying to read {}", filename); info!("Trying to read {}", filename);
let Ok(content) = std::fs::read_to_string(&filename) else { let Ok(content) = std::fs::read(&filename) else {
println!("File {} does not exist or cannot be read", &filename); println!("File {} does not exist or cannot be read.", &filename);
return; return;
}; };
let p = parser::Parser::new(content);
let s = p.parse_sections().unwrap(); let bytes = dekejit::transmute_to_vecu16_as_is(content.to_vec());
println!( info!("{:?}", bytes);
"{}",
format_code(dekejit::pretty_printers::CodeWrapper::Asm(s), format).unwrap()
);
// let Ok(content) = std::fs::read(&filename) else { info!("Begin parsing file {}", &filename);
// println!("File {} does not exist or cannot be read.", &filename); let sections = match read_binary(&bytes) {
// return; Ok(k) => k,
// }; Err(p) => {
// println!("Parsing error: {:?}", p);
// return;
// let bytes = dekejit::transmute_to_vecu16_as_is(content.to_vec()); }
// };
// info!("{:?}", bytes);
// 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 => {} None => {}
} }
// let parser = parser::Parser::new(code); // let parser = parser::Parser::new(code);
// //
// let r = parser.parse_sections().unwrap(); // let r = parser.parse_sections().unwrap();

View File

@ -1,133 +0,0 @@
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<Section>) -> 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<String> = 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(&format_line(&s, 8, TERMSIZE)),
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;
}

View File

@ -1,49 +0,0 @@
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<u16>),
Asm(Vec<Section>)
}
pub fn format_code(c: CodeWrapper, k: FormatKind) -> Result<String, crate::loader::loader::ParseError> {
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!(),
},
}
}