added logging to builder trait and implemented for compiler and

assembler crates.
This commit is contained in:
2026-02-23 09:04:30 +00:00
parent a1d7b54479
commit 7ab1ac8842
11 changed files with 137 additions and 60 deletions
+3 -2
View File
@@ -8,7 +8,7 @@ use std::{
use crate::assembler::{AssembleError, Token, expand_pseudo_ops, lexer, quick_hash}; use crate::assembler::{AssembleError, Token, expand_pseudo_ops, lexer, quick_hash};
use crate::assembler::{Node, Parser, resolve_dependencies}; use crate::assembler::{Node, Parser, resolve_dependencies};
use crate::util::logging::Logger; // use crate::util::logging::Logger;
// pub fn new_assemble(path: &Path) { // pub fn new_assemble(path: &Path) {
// let program = Program::new(); // let program = Program::new();
@@ -144,7 +144,8 @@ impl Module {
} }
pub fn build(path: PathBuf, program: ProgramRef) -> Result<Task, AssembleError> { pub fn build(path: PathBuf, program: ProgramRef) -> Result<Task, AssembleError> {
// Spawn a thread that creates the main function and executes the lexer and parser. // Spawn a thread that creates the main function and executes the lexer and
// parser.
let handle = thread::spawn(move || { let handle = thread::spawn(move || {
let mut module = let mut module =
Self::new(path.clone(), quick_hash(&path), Vec::new(), program.clone()); Self::new(path.clone(), quick_hash(&path), Vec::new(), program.clone());
+29 -17
View File
@@ -5,13 +5,16 @@ use std::{
fmt, fs, fmt, fs,
hash::{DefaultHasher, Hash, Hasher}, hash::{DefaultHasher, Hash, Hasher},
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::{Arc, Mutex, mpsc}, sync::{
Arc, Mutex,
mpsc::{self, Receiver, Sender},
},
thread, thread,
}; };
pub use common::logging::log;
use common::{ use common::{
build::{BuildError, Builder}, build::{BuildError, Builder},
logging::{LogReceiver, Logger},
prelude::Instruction, prelude::Instruction,
}; };
@@ -20,7 +23,7 @@ use common::{
pub mod macros; pub mod macros;
#[allow(clippy::module_inception)] #[allow(clippy::module_inception)]
pub mod assembler; // pub mod assembler;
pub mod codegen; pub mod codegen;
pub mod expand; pub mod expand;
pub mod lexer; pub mod lexer;
@@ -38,13 +41,13 @@ pub use self::{
resolver::{create_sections, resolve_dependencies, resolve_symbols}, resolver::{create_sections, resolve_dependencies, resolve_symbols},
}; };
use crate::util::logging::{Entry, Logger};
pub struct Assembler { pub struct Assembler {
src_path: PathBuf, src_path: PathBuf,
result_tx: mpsc::Sender<Result<Vec<u8>, AssembleError>>, result_tx: mpsc::Sender<Result<Vec<u8>, AssembleError>>,
result_rx: Option<mpsc::Receiver<Result<Vec<u8>, AssembleError>>>, result_rx: Option<mpsc::Receiver<Result<Vec<u8>, AssembleError>>>,
is_running: bool, is_running: bool,
logs_rx: LogReceiver,
} }
impl From<AssembleError> for BuildError { impl From<AssembleError> for BuildError {
@@ -56,6 +59,10 @@ impl From<AssembleError> for BuildError {
impl Builder for Assembler { impl Builder for Assembler {
type Output = Vec<u8>; type Output = Vec<u8>;
fn logs(&self) -> Vec<String> {
self.logs_rx.logs()
}
#[must_use] #[must_use]
fn new(src_path: impl Into<PathBuf>) -> Self { fn new(src_path: impl Into<PathBuf>) -> Self {
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
@@ -64,6 +71,9 @@ impl Builder for Assembler {
result_tx: tx, result_tx: tx,
result_rx: Some(rx), result_rx: Some(rx),
is_running: false, is_running: false,
// for logging
logs_rx: LogReceiver::new(true),
} }
} }
@@ -75,9 +85,10 @@ impl Builder for Assembler {
let src = self.src_path.clone(); let src = self.src_path.clone();
let tx = self.result_tx.clone(); let tx = self.result_tx.clone();
let logger = self.logs_rx.logger();
thread::spawn(move || { thread::spawn(move || {
if let Ok(res) = assemble(&src) { if let Ok(res) = assemble(&src, &logger) {
let buffer: Vec<u8> = res let buffer: Vec<u8> = res
.iter() .iter()
.flat_map(|instruction| instruction.encode().to_be_bytes()) .flat_map(|instruction| instruction.encode().to_be_bytes())
@@ -136,7 +147,7 @@ impl Builder for Assembler {
impl Assembler {} impl Assembler {}
fn assemble(src: &Path) -> Result<Vec<Instruction>, AssembleError> { fn assemble(src: &Path, logger: &Logger) -> Result<Vec<Instruction>, AssembleError> {
let mut modules = HashSet::new(); let mut modules = HashSet::new();
let mut program = Program::new(); let mut program = Program::new();
@@ -146,18 +157,18 @@ fn assemble(src: &Path) -> Result<Vec<Instruction>, AssembleError> {
return Ok(vec![]); return Ok(vec![]);
} }
prepare_dependency(src, &mut modules, &mut program)?; prepare_dependency(src, &mut modules, &mut program, &logger)?;
let mut nodes = program.nodes.clone(); let mut nodes = program.nodes.clone();
create_sections(&mut nodes)?; create_sections(&mut nodes)?;
resolve_symbols(&mut nodes)?; resolve_symbols(&mut nodes)?;
log("Generating assembly output..."); logger.info("Generating assembly output...");
let instructions = codegen(nodes)?; let instructions = codegen(nodes)?;
log("Compilation Successful"); logger.info("Compilation Successful");
Ok(instructions) Ok(instructions)
} }
@@ -165,6 +176,7 @@ fn prepare_dependency(
path: &Path, path: &Path,
modules: &mut HashSet<u64>, modules: &mut HashSet<u64>,
program: &mut Program, program: &mut Program,
logger: &Logger,
) -> Result<(), AssembleError> { ) -> Result<(), AssembleError> {
let filename = path let filename = path
.file_name() .file_name()
@@ -172,7 +184,7 @@ fn prepare_dependency(
.expect("Failed to get file name from path"); .expect("Failed to get file name from path");
if let Ok(path) = path.canonicalize() { if let Ok(path) = path.canonicalize() {
log(&format!( logger.info(&format!(
"{:20} {:20} [{}]", "{:20} {:20} [{}]",
"Building", "Building",
filename, filename,
@@ -184,13 +196,13 @@ fn prepare_dependency(
.map_err(|_| AssembleError::InvalidFile(path.to_path_buf()))?; .map_err(|_| AssembleError::InvalidFile(path.to_path_buf()))?;
let file_hash = quick_hash(path); let file_hash = quick_hash(path);
log(&format!("{:20} {:20}", "Tokenising", filename)); logger.info(&format!("{:20} {:20}", "Tokenising", filename));
let tokens = lexer::lexer(src, file_hash)?; let tokens = lexer::lexer(src, file_hash)?;
log(&format!("{:20} {:20}", "Parsing", filename)); logger.info(&format!("{:20} {:20}", "Parsing", filename));
let parsed = Parser::parse_nodes(tokens)?; let parsed = Parser::parse_nodes(tokens)?;
log(&format!("{:20} {:20}", "Resolving Deps", filename)); logger.info(&format!("{:20} {:20}", "Resolving Deps", filename));
// Get the parent directory of the source file to use as the base directory // Get the parent directory of the source file to use as the base directory
let base_dir = path let base_dir = path
.parent() .parent()
@@ -200,7 +212,7 @@ fn prepare_dependency(
let deps = Parser::get_dependencies(&nodes, path)?; let deps = Parser::get_dependencies(&nodes, path)?;
log(&format!("{:20} {:20}", "Expanding Pseudo-ops", filename)); logger.info(&format!("{:20} {:20}", "Expanding Pseudo-ops", filename));
// add a section instruction // add a section instruction
nodes.insert( nodes.insert(
@@ -215,7 +227,7 @@ fn prepare_dependency(
program.add_module(nodes); program.add_module(nodes);
for dep in deps { for dep in deps {
log(&format!( logger.info(&format!(
"{:20} {:20}", "{:20} {:20}",
"Including", "Including",
dep.file_name() dep.file_name()
@@ -225,7 +237,7 @@ fn prepare_dependency(
let dep_hash = quick_hash(&dep); let dep_hash = quick_hash(&dep);
if modules.insert(dep_hash) { if modules.insert(dep_hash) {
prepare_dependency(dep.as_path(), modules, program)?; prepare_dependency(dep.as_path(), modules, program, logger)?;
} }
} }
+1 -4
View File
@@ -6,11 +6,8 @@ use std::{
use common::prelude::Register; use common::prelude::Register;
use crate::assembler::model::{Module, Node, Opcode, Symbol, Token};
use crate::assembler::quick_hash; use crate::assembler::quick_hash;
use crate::assembler::{
log,
model::{Module, Node, Opcode, Symbol, Token},
};
use crate::{assembler::AssembleError, node}; use crate::{assembler::AssembleError, node};
pub fn resolve_symbols(nodes: &mut [Node]) -> Result<(), AssembleError> { pub fn resolve_symbols(nodes: &mut [Node]) -> Result<(), AssembleError> {
+10 -10
View File
@@ -2,18 +2,18 @@
#![allow(unused)] #![allow(unused)]
use std::{fmt, sync::mpsc::Sender}; use std::{fmt, sync::mpsc::Sender};
pub struct Logger {} // pub struct Logger {}
impl Logger { // impl Logger {
pub const fn new() -> Self { // pub const fn new() -> Self {
Self {} // Self {}
} // }
pub fn log(&self, message: &str) { // pub fn log(&self, message: &str) {
_ = self; // _ = self;
println!("\x1b[32mINFO:\x1b[0m {message}"); // println!("\x1b[32mINFO:\x1b[0m {message}");
} // }
} // }
// #[derive(Debug)]= // #[derive(Debug)]=
// pub struct Logger { // pub struct Logger {
+2
View File
@@ -49,4 +49,6 @@ pub trait Builder {
std::fs::write(path.as_ref(), output) std::fs::write(path.as_ref(), output)
.map_err(|e| BuildError::IoError(e.to_string())) .map_err(|e| BuildError::IoError(e.to_string()))
} }
fn logs(&self) -> Vec<String>;
} }
+63 -2
View File
@@ -1,4 +1,65 @@
// TODO: Use an actual logging or tracing library for pretty (scoped) output. use std::sync::{Arc, mpsc};
pub fn log(message: &str) {
pub fn info(message: &str) {
println!("\x1b[32mINFO:\x1b[0m {message}"); println!("\x1b[32mINFO:\x1b[0m {message}");
} }
#[derive(Debug)]
pub struct LogReceiver {
logs_rx: mpsc::Receiver<String>,
sender: Logger,
use_stdio: bool,
}
#[derive(Debug, Clone)]
pub struct Logger {
use_stdio: bool,
logs_tx: Arc<mpsc::Sender<String>>,
}
impl Logger {
#[must_use]
pub fn new(logs_tx: mpsc::Sender<String>, use_stdio: bool) -> Self {
Self {
use_stdio: true,
logs_tx: Arc::new(logs_tx),
}
}
pub fn info(&self, message: &str) {
let res = format!("\x1b[32mINFO:\x1b[0m {message}");
if self.use_stdio {
println!("{res}");
}
self.logs_tx.send(res).expect("Failed to send log message");
}
}
impl LogReceiver {
#[must_use]
pub fn new(use_stdio: bool) -> Self {
let (logs_tx, logs_rx) = mpsc::channel();
Self {
use_stdio,
logs_rx,
sender: Logger::new(logs_tx, use_stdio),
}
}
#[must_use]
pub fn logs(&self) -> Vec<String> {
self.logs_rx.try_iter().collect()
}
#[must_use]
pub fn logger(&self) -> Logger {
self.sender.clone()
}
}
impl Default for LogReceiver {
fn default() -> Self {
Self::new(true)
}
}
+7 -8
View File
@@ -1,6 +1,5 @@
use common::logging::log;
use crate::model::{CompilerError, Program}; use crate::model::{CompilerError, Program};
use common::logging::Logger;
use parser::{ParseResult, Parser}; use parser::{ParseResult, Parser};
// use semantic_analyser::Analyser; // use semantic_analyser::Analyser;
@@ -8,14 +7,14 @@ pub mod lexer;
pub mod parser; pub mod parser;
// pub mod semantic_analyser; // pub mod semantic_analyser;
pub fn generate_ast(input: &str) -> Result<Program, CompilerError> { pub fn generate_ast(input: &str, logger: &Logger) -> Result<Program, CompilerError> {
log("Tokenising Input..."); logger.info("Tokenising Input...");
let lexer = lexer::Lexer::new(&input); let lexer = lexer::Lexer::new(&input);
let tokens = lexer.collect::<Vec<_>>(); let tokens = lexer.collect::<Vec<_>>();
println!("{tokens:#?}"); println!("{tokens:#?}");
log(&format!("Parsing {} Tokens...", tokens.len())); logger.info(&format!("Parsing {} Tokens...", tokens.len()));
let mut parser = Parser::new(tokens); let mut parser = Parser::new(tokens);
let ast = match parser.parse() { let ast = match parser.parse() {
@@ -27,12 +26,12 @@ pub fn generate_ast(input: &str) -> Result<Program, CompilerError> {
}; };
// println!("{ast:#?}"); // println!("{ast:#?}");
log("Analyzing AST..."); logger.info("Analyzing AST...");
log("Checking Type Information..."); logger.info("Checking Type Information...");
// let mut analyser = Analyser::new(); // let mut analyser = Analyser::new();
// analyser.analyse(ast.clone()).unwrap(); // analyser.analyse(ast.clone()).unwrap();
log("Type Checking Complete..."); logger.info("Type Checking Complete...");
Ok(ast) Ok(ast)
} }
+8 -2
View File
@@ -1,11 +1,17 @@
use common::logging::Logger;
use crate::model::{CompilerError, Program}; use crate::model::{CompilerError, Program};
// mod c; // mod c;
mod dsc; mod dsc;
pub fn compiler_frontend(ext: &str, data: &str) -> Result<Program, CompilerError> { pub fn compiler_frontend(
ext: &str,
data: &str,
logger: &Logger,
) -> Result<Program, CompilerError> {
match ext { match ext {
"dsc" => Ok(dsc::generate_ast(&data)?), "dsc" => Ok(dsc::generate_ast(&data, &logger)?),
// "c" => Ok(c::generate_ast(&data)?), // "c" => Ok(c::generate_ast(&data)?),
_ => Err(CompilerError::Generic(format!( _ => Err(CompilerError::Generic(format!(
"File type {} not supported", "File type {} not supported",
+9 -2
View File
@@ -4,7 +4,7 @@ use std::path::{Path, PathBuf};
use common::{ use common::{
build::{BuildError, Builder}, build::{BuildError, Builder},
logging::log, logging::LogReceiver,
}; };
use crate::{model::CompilerError, specialised::build_specialised}; use crate::{model::CompilerError, specialised::build_specialised};
@@ -17,6 +17,7 @@ mod specialised;
pub struct Compiler { pub struct Compiler {
src_path: PathBuf, src_path: PathBuf,
result: Option<Result<String, BuildError>>, result: Option<Result<String, BuildError>>,
logger: LogReceiver,
} }
impl Compiler { impl Compiler {
@@ -36,7 +37,8 @@ impl Compiler {
} }
// Parse the input using the frontend, providing the file extension and data. // Parse the input using the frontend, providing the file extension and data.
let ast = match frontend::compiler_frontend(input_ext, &input) { let ast =
match frontend::compiler_frontend(input_ext, &input, &self.logger.logger()) {
Ok(ast) => ast, Ok(ast) => ast,
Err(err) => return Err(format!("Compilation failed: {err:?}").into()), Err(err) => return Err(format!("Compilation failed: {err:?}").into()),
}; };
@@ -60,6 +62,7 @@ impl Builder for Compiler {
Self { Self {
src_path: src_path.into(), src_path: src_path.into(),
result: None, result: None,
logger: LogReceiver::new(true),
} }
} }
@@ -79,6 +82,10 @@ impl Builder for Compiler {
"Compiler was never started", "Compiler was never started",
)))? )))?
} }
fn logs(&self) -> Vec<String> {
todo!()
}
} }
pub fn error(msg: impl Into<String>) -> CompilerError { pub fn error(msg: impl Into<String>) -> CompilerError {
+2 -2
View File
@@ -1,6 +1,6 @@
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use common::{build::Builder, logging::log}; use common::{build::Builder, logging::info};
use compiler::Compiler; use compiler::Compiler;
fn main() { fn main() {
@@ -25,7 +25,7 @@ fn main() {
std::fs::write(output_file, &result).expect("Failed to write output"); std::fs::write(output_file, &result).expect("Failed to write output");
log(&format!( info(&format!(
"Compilation Successful ✅ \n\tSource: {}\n\tOutput: {}\n", "Compilation Successful ✅ \n\tSource: {}\n\tOutput: {}\n",
input_file, output_file, input_file, output_file,
)); ));
-8
View File
@@ -31,14 +31,6 @@ pub fn build_project(cwd: &Path) -> Result<(), BuildError> {
))); )));
} }
// check is redundant as we're already checking for main files.
// if !has_dsc && !has_dsa {
// return Err(io::Error::new(
// io::ErrorKind::NotFound,
// "No .dsc or .dsa source found in src directory.",
// ));
// }
// detect src. // detect src.
let (has_dsa, has_dsc) = detect_source_language(&src_dir); let (has_dsa, has_dsc) = detect_source_language(&src_dir);