diff --git a/Cargo.lock b/Cargo.lock index 0942501..f8df113 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -236,6 +236,8 @@ name = "assembler" version = "0.2.0" dependencies = [ "common", + "num_cpus", + "threadpool", ] [[package]] @@ -1951,6 +1953,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "num_enum" version = "0.7.3" @@ -3041,6 +3053,15 @@ dependencies = [ "syn", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + [[package]] name = "tiff" version = "0.9.1" diff --git a/assembler/Cargo.toml b/assembler/Cargo.toml index d920624..676d37d 100644 --- a/assembler/Cargo.toml +++ b/assembler/Cargo.toml @@ -13,4 +13,6 @@ name = "assembler" path = "src/lib.rs" [dependencies] -common = { path = "../common" } \ No newline at end of file +common = { path = "../common" } +num_cpus = "1.17.0" +threadpool = "1.8.1" diff --git a/assembler/src/assembler/macros.rs b/assembler/src/assembler/macros.rs new file mode 100644 index 0000000..32e5974 --- /dev/null +++ b/assembler/src/assembler/macros.rs @@ -0,0 +1,131 @@ +//! Macros used throughout the assembler + +use crate::assembler::model::{Node, Opcode, Symbol, Token}; + +/// Parse DSA assembly code with optional formatting +/// +/// # Examples +/// ``` +/// // With formatting: +/// let nodes = dsa!(hash, "mov r1, {}", 42)?; +/// +/// // Without formatting: +/// let nodes = dsa!(hash, "mov r1, 42")?; +/// ``` +#[macro_export] +macro_rules! dsa { + // Version with formatting arguments + ($hash:expr, $input:expr, $($args:expr),+) => {{ + let input = format!($input, $($args),+); + let tokens = $crate::lexer::lexer(input, $hash)?; + let parsed = $crate::parser::Parser::parse_nodes(tokens)?; + parsed + }}; + // Version without formatting + ($hash:expr, $input:expr) => {{ + let input = String::from($input); + let tokens = $crate::lexer::lexer(input, $hash)?; + let parsed = $crate::parser::Parser::parse_nodes(tokens)?; + parsed + }}; +} + +/// Creates a new Node with the given symbol, opcode, and tokens +#[macro_export] +macro_rules! node { + ($symbol: expr, $opcode: expr, args: $tokens: expr) => { + $crate::assembler::model::Node::new($symbol.clone(), $opcode.clone(), $tokens.clone()) + }; + + ($symbol: expr, $opcode: expr, $($tokens: expr),+) => { + $crate::assembler::model::Node::new( + $symbol.clone(), + $opcode.clone(), + vec![$(node!(@convert_token $tokens)),+] + ) + }; + + (@convert_token $token: literal) => { + $crate::assembler::model::Token::Immediate($token) + }; + + (@convert_token $token: expr) => { + $token.clone() + }; +} + +/// Extracts a specific token type from a token +#[macro_export] +macro_rules! expect_token { + ($token:expr, Symbol) => { + match $token { + $crate::assembler::model::Token::Symbol(value) => Ok(value.clone()), + other => Err($crate::assembler::AssembleError::UnexpectedToken( + other.clone(), + $crate::assembler::model::TokenType::Symbol, + )), + } + }; + ($token:expr, Register) => { + match $token { + $crate::assembler::model::Token::Register(value) => Ok(value.clone()), + other => Err($crate::assembler::AssembleError::UnexpectedToken( + other.clone(), + $crate::assembler::model::TokenType::Register, + )), + } + }; + ($token:expr, Immediate) => { + match $token { + $crate::assembler::model::Token::Immediate(value) => Ok(value.clone()), + other => Err($crate::assembler::AssembleError::UnexpectedToken( + other.clone(), + $crate::assembler::model::TokenType::Immediate, + )), + } + }; + ($token:expr, StringLit) => { + match $token { + $crate::assembler::model::Token::StringLit(value) => Ok(value.clone()), + other => Err($crate::assembler::AssembleError::UnexpectedToken( + other.clone(), + $crate::assembler::model::TokenType::StringLit, + )), + } + }; + ($token:expr, Opcode) => { + match $token { + $crate::assembler::model::Token::Opcode(value) => Ok(value.clone()), + other => Err($crate::assembler::AssembleError::UnexpectedToken( + other.clone(), + $crate::assembler::model::TokenType::Opcode, + )), + } + }; +} + +/// Checks if a token matches any of the specified types +#[macro_export] +macro_rules! expect_type { + ($token:expr, $($variant:ident),+) => {{ + let token = $token; + match &token { + $( + $crate::assembler::model::Token::$variant(_) => Ok(token.clone()), + )+ + other => { + let expected_type = expect_type!(@get_first_type $($variant),+); + Err($crate::assembler::AssembleError::UnexpectedToken( + other.clone().clone(), + expected_type, + )) + } + } + }}; + + (@get_first_type Symbol $(, $rest:ident)*) => { $crate::assembler::model::TokenType::Symbol }; + (@get_first_type Register $(, $rest:ident)*) => { $crate::assembler::model::TokenType::Register }; + (@get_first_type Immediate $(, $rest:ident)*) => { $crate::assembler::model::TokenType::Immediate }; + (@get_first_type StringLit $(, $rest:ident)*) => { $crate::assembler::model::TokenType::StringLit }; + (@get_first_type Opcode $(, $rest:ident)*) => { $crate::assembler::model::TokenType::Opcode }; +} diff --git a/assembler/src/assembler/mod.rs b/assembler/src/assembler/mod.rs index fbb38c6..16c934c 100644 --- a/assembler/src/assembler/mod.rs +++ b/assembler/src/assembler/mod.rs @@ -1,21 +1,23 @@ +#![allow(dead_code, unused)] + use std::{ collections::HashSet, fmt, fs, hash::{DefaultHasher, Hash, Hasher}, path::{Path, PathBuf}, + sync::mpsc, }; use common::prelude::Instruction; -use crate::{ - assembler::{ - expand::expand_pseudo_ops, - model::{Node, Opcode, Symbol, Token, TokenType}, - parser::{Parser, Program}, - resolver::{create_sections, resolve_dependencies, resolve_symbols}, - }, - codegen, log, node, -}; +// TODO: Use an actual logging or tracing library for pretty (scoped) output. +fn log(message: &str) { + println!("\x1b[32mINFO:\x1b[0m {message}"); +} + +// Module declarations +#[macro_use] +pub mod macros; pub mod codegen; pub mod expand; @@ -24,25 +26,91 @@ pub mod model; pub mod parser; pub mod resolver; -pub fn assemble(src: &Path) -> Result, AssembleError> { - let mut modules = HashSet::::new(); - let mut program = Program::new(); +// Re-exports +pub use self::{ + codegen::codegen, + expand::expand_pseudo_ops, + lexer::lexer, + model::{Module, Node, Opcode, Symbol, Token, TokenType}, + parser::{Parser, Program}, + resolver::{create_sections, resolve_dependencies, resolve_symbols}, +}; - let hash = quick_hash(src); - modules.insert(hash); +use crate::util::logging::{Entry, Logger}; - prepare_dependency(src, &mut modules, &mut program)?; - let mut nodes = program.nodes; +pub struct CompilerEngine { + modules: HashSet, + program: Program, + logger: Option, + receiver: Option>, + result: Option, AssembleError>>, +} - create_sections(&mut nodes)?; - resolve_symbols(&mut nodes)?; - - let instructions = codegen(nodes)?; - for inst in instructions.iter() { - println!("{inst}"); +impl CompilerEngine { + pub fn new() -> CompilerEngine { + let (tx, rx) = mpsc::channel::(); + CompilerEngine { + program: Program::new(), + modules: HashSet::new(), + logger: Some(Logger::new(tx)), + receiver: Some(rx), + result: None, + } } - Ok(instructions) + pub fn get_log() -> Option { + None + } + + pub fn get_logs() -> Vec { + vec![] + } + + pub fn is_ready(&self) -> bool { + self.result.is_some() + } + + pub fn result(&self) -> Option, AssembleError>> { + self.result.clone() + } + + pub fn assemble(&mut self, src: &Path) -> Result<(), AssembleError> { + let hash = quick_hash(src); + + if self.modules.contains(&hash) { + return Ok(()); + } + + prepare_dependency(src, &mut self.modules, &mut self.program)?; + + self.result = Some(self.build()); + + Ok(()) + } + + fn load_module(&mut self, path: &Path) -> Result<(), AssembleError> { + Ok(()) + } + + fn build(&self) -> Result, AssembleError> { + let mut nodes = self.program.nodes.clone(); + + create_sections(&mut nodes)?; + resolve_symbols(&mut nodes)?; + + let instructions = codegen(nodes)?; + for inst in instructions.iter() { + println!("{inst}"); + } + + Ok(instructions) + } +} + +impl Default for CompilerEngine { + fn default() -> Self { + Self::new() + } } fn prepare_dependency( @@ -71,9 +139,13 @@ fn prepare_dependency( let parsed = Parser::parse_nodes(tokens)?; log(&format!("{:20} {:20}", "Resolving Deps", filename)); - let nodes = resolve_dependencies(parsed)?; + // Get the parent directory of the source file to use as the base directory + let base_dir = path + .parent() + .ok_or_else(|| AssembleError::InvalidFile(path.to_path_buf()))?; + let nodes = resolve_dependencies(parsed, base_dir)?; - let deps = Parser::get_dependencies(&nodes)?; + let deps = Parser::get_dependencies(&nodes, path)?; log(&format!( "{:20} {:20}", @@ -122,7 +194,7 @@ pub fn disassemble(_: Vec) -> String { todo!() } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum AssembleError { Generic, UnexpectedEof, @@ -154,95 +226,3 @@ fn quick_hash(value: &Path) -> u64 { value.canonicalize().unwrap().to_str().hash(&mut hasher); hasher.finish() } - -#[macro_export] -macro_rules! dsa { - // Version with formatting arguments - ($hash:expr, $input:expr, $($args:expr),+) => {{ - let input = format!($input, $($args),+); - let tokens = $crate::lexer::lexer(input, $hash)?; - let parsed = $crate::parser::Parser::parse_nodes(tokens)?; - parsed - }}; - // Version without formatting - ($hash:expr, $input:expr) => {{ - let input = String::from($input); - let tokens = $crate::lexer::lexer(input, $hash)?; - let parsed = $crate::parser::Parser::parse_nodes(tokens)?; - parsed - }}; -} - -#[macro_export] -macro_rules! expect_token { - ($token:expr, Symbol) => { - match $token { - $crate::assembler::model::Token::Symbol(value) => Ok(value.clone()), - other => Err($crate::assembler::AssembleError::UnexpectedToken( - other.clone(), - $crate::assembler::model::TokenType::Symbol, - )), - } - }; - ($token:expr, Register) => { - match $token { - $crate::assembler::model::Token::Register(value) => Ok(value.clone()), - other => Err($crate::assembler::AssembleError::UnexpectedToken( - other.clone(), - $crate::assembler::model::TokenType::Register, - )), - } - }; - ($token:expr, Immediate) => { - match $token { - $crate::assembler::model::Token::Immediate(value) => Ok(value.clone()), - other => Err($crate::assembler::AssembleError::UnexpectedToken( - other.clone(), - $crate::assembler::model::TokenType::Immediate, - )), - } - }; - ($token:expr, StringLit) => { - match $token { - $crate::assembler::model::Token::StringLit(value) => Ok(value.clone()), - other => Err($crate::assembler::AssembleError::UnexpectedToken( - other.clone(), - $crate::assembler::model::TokenType::StringLit, - )), - } - }; - ($token:expr, Opcode) => { - match $token { - $crate::assembler::model::Token::Opcode(value) => Ok(value.clone()), - other => Err($crate::assembler::AssembleError::UnexpectedToken( - other.clone(), - $crate::assembler::model::TokenType::Opcode, - )), - } - }; -} - -#[macro_export] -macro_rules! expect_type { - ($token:expr, $($variant:ident),+) => {{ - let token = $token; - match &token { - $( - $crate::assembler::model::Token::$variant(_) => Ok(token.clone()), - )+ - other => { - let expected_type = expect_type!(@get_first_type $($variant),+); - Err($crate::assembler::AssembleError::UnexpectedToken( - other.clone().clone(), - expected_type, - )) - } - } - }}; - - (@get_first_type Symbol $(, $rest:ident)*) => { $crate::assembler::model::TokenType::Symbol }; - (@get_first_type Register $(, $rest:ident)*) => { $crate::assembler::model::TokenType::Register }; - (@get_first_type Immediate $(, $rest:ident)*) => { $crate::assembler::model::TokenType::Immediate }; - (@get_first_type StringLit $(, $rest:ident)*) => { $crate::assembler::model::TokenType::StringLit }; - (@get_first_type Opcode $(, $rest:ident)*) => { $crate::assembler::model::TokenType::Opcode }; -} diff --git a/assembler/src/assembler/model.rs b/assembler/src/assembler/model.rs index 955c5ac..28c37a2 100644 --- a/assembler/src/assembler/model.rs +++ b/assembler/src/assembler/model.rs @@ -11,29 +11,6 @@ pub struct Node { pub tokens: Vec, } -#[macro_export] -macro_rules! node { - ($symbol: expr, $opcode: expr, args: $tokens: expr) => { - Node::new($symbol.clone(), $opcode.clone(), $tokens.clone()) - }; - - ($symbol: expr, $opcode: expr, $($tokens: expr),+) => { - Node::new( - $symbol.clone(), - $opcode.clone(), - vec![$(node!(@convert_token $tokens)),+] - ) - }; - - (@convert_token $token: literal) => { - Token::Immediate($token) - }; - - (@convert_token $token: expr) => { - $token.clone() - }; -} - impl Node { pub fn new(symbol: Option, opcode: Opcode, tokens: Vec) -> Node { Node { diff --git a/assembler/src/assembler/parser.rs b/assembler/src/assembler/parser.rs index 1861b42..73c23fa 100644 --- a/assembler/src/assembler/parser.rs +++ b/assembler/src/assembler/parser.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use crate::{assembler::AssembleError, expect_token, expect_type, node}; @@ -10,6 +10,7 @@ pub struct Parser { nodes: Vec, } +#[derive(Debug)] pub struct Program { pub nodes: Vec, } @@ -44,8 +45,6 @@ impl Parser { nodes: vec![], }; - println!("{:#?}", tokens); - while !self_.tokens.is_empty() { let ins = self_.parse_instruction()?; self_.nodes.push(ins); @@ -54,12 +53,29 @@ impl Parser { Ok(self_.nodes.clone()) } - pub fn get_dependencies(nodes: &Vec) -> Result, AssembleError> { + pub fn get_dependencies( + nodes: &Vec, + source_path: &Path, + ) -> Result, AssembleError> { let mut dependencies = Vec::new(); + // Get the parent directory of the source file to use as the base directory + let base_dir = source_path + .parent() + .ok_or_else(|| AssembleError::InvalidFile(source_path.to_path_buf()))?; + for node in nodes { if let Opcode::Include = node.opcode() { - let path = expect_token!(node.args().get(1).unwrap(), StringLit)?; - dependencies.push(PathBuf::from(path)); + let path_str = expect_token!(node.args().get(1).unwrap(), StringLit)?; + let path = PathBuf::from(path_str); + + // If the path is not absolute, make it relative to the base directory + let full_path = if path.is_absolute() { + path + } else { + base_dir.join(path) + }; + + dependencies.push(full_path); } } Ok(dependencies) diff --git a/assembler/src/assembler/resolver.rs b/assembler/src/assembler/resolver.rs index 6dea158..cb6d602 100644 --- a/assembler/src/assembler/resolver.rs +++ b/assembler/src/assembler/resolver.rs @@ -1,9 +1,16 @@ -use std::{collections::HashMap, path::PathBuf}; +use std::{ + collections::HashMap, + fs::canonicalize, + path::{Path, PathBuf}, +}; use common::prelude::Register; -use crate::assembler::model::{Module, Node, Opcode, Symbol, Token}; use crate::assembler::quick_hash; +use crate::assembler::{ + log, + model::{Module, Node, Opcode, Symbol, Token}, +}; use crate::{assembler::AssembleError, node}; pub fn resolve_symbols(nodes: &mut [Node]) -> Result<(), AssembleError> { @@ -63,7 +70,10 @@ fn generate_symbol_table(nodes: &[Node]) -> Result, Assembl Ok(table) } -pub fn resolve_dependencies(mut nodes: Vec) -> Result, AssembleError> { +pub fn resolve_dependencies( + mut nodes: Vec, + base_dir: &Path, +) -> Result, AssembleError> { // First we get a list of imports. let mut dependencies = Vec::new(); for node in &nodes { @@ -79,11 +89,13 @@ pub fn resolve_dependencies(mut nodes: Vec) -> Result, AssembleE } else { unreachable!() }; - let hash = quick_hash( - &PathBuf::from(path) - .canonicalize() - .expect("ERROR: Invalid import path."), - ); + + let full_path = base_dir.join(path); + let canonical_path = full_path + .canonicalize() + .map_err(|_| AssembleError::InvalidFile(full_path.to_path_buf()))?; + + let hash = quick_hash(&canonical_path); dependencies.push((name, hash)); } diff --git a/assembler/src/lib.rs b/assembler/src/lib.rs index 8f6ae20..45d7389 100644 --- a/assembler/src/lib.rs +++ b/assembler/src/lib.rs @@ -1,15 +1,8 @@ -use assembler::codegen::codegen; - pub mod assembler; pub mod tooling; mod util; pub mod prelude { - pub use crate::assembler::assemble; + pub use crate::assembler::CompilerEngine; pub use crate::assembler::disassemble; } - -// TODO: Use an actual logging or tracing library for pretty (scoped) output. -fn log(message: &str) { - println!("\x1b[32mINFO:\x1b[0m {message}"); -} diff --git a/assembler/src/main.rs b/assembler/src/main.rs index 3ce7bcc..0d4f523 100644 --- a/assembler/src/main.rs +++ b/assembler/src/main.rs @@ -1,7 +1,8 @@ +use assembler::prelude::*; use std::{fs, io::Write, path::PathBuf}; fn main() { - // parse args: + // Parse command line arguments let args: Vec = std::env::args().collect(); if args.len() == 2 && args[1] == "init" { @@ -10,23 +11,49 @@ fn main() { } if args.len() != 5 || args[1] != "-i" || args[3] != "-o" { - eprintln!("Usage: binary_name -i input_path -o output_path"); + eprintln!("Usage: {} -i input_path -o output_path", args[0]); std::process::exit(1); } + let input_path = &args[2]; let output_path = &args[4]; - let src = PathBuf::from(input_path); - let mut output_file = fs::File::create(output_path).unwrap(); - match assembler::assembler::assemble(&src) { - Ok(res) => { - res.iter().map(|i| i.encode()).for_each(|i| { - output_file.write_all(&i.to_le_bytes()).unwrap(); - }); - } + // Create the output file + let mut output_file = match fs::File::create(output_path) { + Ok(file) => file, Err(e) => { - eprintln!("{e}"); + eprintln!("Failed to create output file: {}", e); + std::process::exit(1); + } + }; + + // Initialize the compiler engine + let mut engine = CompilerEngine::new(); + + // Assemble the source file + if let Err(e) = engine.assemble(&src) { + eprintln!("Assembly error: {}", e); + std::process::exit(1); + } + + // Build and write the output + match engine.result() { + Some(Ok(instructions)) => { + for instruction in instructions { + if let Err(e) = output_file.write_all(&instruction.encode().to_le_bytes()) + { + eprintln!("Failed to write to output file: {}", e); + std::process::exit(1); + } + } + } + Some(Err(e)) => { + eprintln!("Build error: {}", e); + std::process::exit(1); + } + None => { + eprintln!("Build error: No result available"); std::process::exit(1); } } diff --git a/assembler/src/util/logging.rs b/assembler/src/util/logging.rs new file mode 100644 index 0000000..ab6645a --- /dev/null +++ b/assembler/src/util/logging.rs @@ -0,0 +1,94 @@ +use std::{fmt, sync::mpsc::Sender}; + +#[allow(dead_code)] +#[derive(Debug)] +pub struct Logger { + pub sender: Sender, +} + +impl Logger { + pub fn new(sender: Sender) -> Self { + Self { sender } + } + + pub fn debug(&self, message: T) { + self.sender + .send(Entry { + etype: EntryType::Debug, + message: message.to_string(), + }) + .unwrap(); + } + + pub fn info(&self, message: T) { + self.sender + .send(Entry { + etype: EntryType::Info, + message: message.to_string(), + }) + .unwrap(); + } + + pub fn warn(&self, message: T) { + self.sender + .send(Entry { + etype: EntryType::Warn, + message: message.to_string(), + }) + .unwrap(); + } + + pub fn error(&self, message: T) { + self.sender + .send(Entry { + etype: EntryType::Error, + message: message.to_string(), + }) + .unwrap(); + } + + pub fn fatal(&self, message: T) { + self.sender + .send(Entry { + etype: EntryType::Fatal, + message: message.to_string(), + }) + .unwrap(); + } +} + +pub struct Entry { + etype: EntryType, + pub message: String, +} + +#[derive(Copy, Clone, Eq, PartialEq)] +enum EntryType { + Debug, + Info, + Warn, + Error, + Fatal, +} + +impl fmt::Display for EntryType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{:<5}", + match self { + EntryType::Debug => "DEBUG", + EntryType::Info => "INFO", + EntryType::Warn => "WARN", + EntryType::Error => "ERROR", + EntryType::Fatal => "FATAL", + } + ) + } +} + +impl fmt::Display for Entry { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}: {}", self.etype, self.message) + } +} diff --git a/assembler/src/util/mod.rs b/assembler/src/util/mod.rs index 5328c05..ea56139 100644 --- a/assembler/src/util/mod.rs +++ b/assembler/src/util/mod.rs @@ -1,3 +1,5 @@ +pub mod logging; + use std::io::Write; pub fn input(prompt: &str) -> String { diff --git a/emulator/src/emulator/ui/editor.rs b/emulator/src/emulator/ui/editor.rs index b974d59..98cc1c2 100644 --- a/emulator/src/emulator/ui/editor.rs +++ b/emulator/src/emulator/ui/editor.rs @@ -346,12 +346,24 @@ impl Editor { // builds the current file if ui.button("Build").clicked() && !self.unsaved { if let Some(path) = &self.path { - let instructions = match assemble(path) { - Ok(instructions) => instructions, - Err(error) => { + let mut assembler = CompilerEngine::new(); + if let Err(error) = assembler.assemble(path) { + self.error = Some(format!("Failed to assemble: {error:?}")); + return; + } + + let instructions = match assembler.result() { + Some(Ok(instructions)) => instructions, + Some(Err(error)) => { self.error = Some(format!("Failed to assemble: {error:?}")); return; } + None => { + self.error = Some( + "Failed to assemble: No result available".to_string(), + ); + return; + } }; self.output = instructions diff --git a/resources/dsa/fib.dsa b/resources/dsa/lib/fib.dsa similarity index 100% rename from resources/dsa/fib.dsa rename to resources/dsa/lib/fib.dsa diff --git a/resources/dsa/multiply.dsa b/resources/dsa/lib/multiply.dsa similarity index 100% rename from resources/dsa/multiply.dsa rename to resources/dsa/lib/multiply.dsa diff --git a/resources/dsa/print.dsa b/resources/dsa/lib/print.dsa similarity index 100% rename from resources/dsa/print.dsa rename to resources/dsa/lib/print.dsa diff --git a/resources/dsa/test.dsa b/resources/dsa/test.dsa index aa1400a..8faa776 100644 --- a/resources/dsa/test.dsa +++ b/resources/dsa/test.dsa @@ -1,11 +1,7 @@ -include print "./print.dsa" +include print "./lib/print.dsa" dw stack: 0x10000 -db string: "An idiot admires complexity, a genius admires simplicity, -a physicist tries to make it simple, for an idiot anything the more complicated it is, -the more he will admire it, if you make something so clusterfucked he can't understand it he's -gonna think you're a god cause you made it so complicated nobody can understand it. -That's how they write journals in Academics, they try to make it so complicated people think you're a genius" +db string: "Hello world frfrfr" init: // set up a stack. diff --git a/resources/dsb/test.dsb b/resources/dsb/test.dsb index c284fe1..67d1c7f 100644 Binary files a/resources/dsb/test.dsb and b/resources/dsb/test.dsb differ