refactor & fixed assembler path handling
This commit is contained in:
Generated
+21
@@ -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"
|
||||
|
||||
@@ -13,4 +13,6 @@ name = "assembler"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
common = { path = "../common" }
|
||||
common = { path = "../common" }
|
||||
num_cpus = "1.17.0"
|
||||
threadpool = "1.8.1"
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
+98
-118
@@ -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<Vec<Instruction>, AssembleError> {
|
||||
let mut modules = HashSet::<u64>::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<u64>,
|
||||
program: Program,
|
||||
logger: Option<Logger>,
|
||||
receiver: Option<mpsc::Receiver<Entry>>,
|
||||
result: Option<Result<Vec<Instruction>, 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::<Entry>();
|
||||
CompilerEngine {
|
||||
program: Program::new(),
|
||||
modules: HashSet::new(),
|
||||
logger: Some(Logger::new(tx)),
|
||||
receiver: Some(rx),
|
||||
result: None,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(instructions)
|
||||
pub fn get_log() -> Option<Entry> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_logs() -> Vec<Entry> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.result.is_some()
|
||||
}
|
||||
|
||||
pub fn result(&self) -> Option<Result<Vec<Instruction>, 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<Vec<Instruction>, 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<Instruction>) -> 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 };
|
||||
}
|
||||
|
||||
@@ -11,29 +11,6 @@ pub struct Node {
|
||||
pub tokens: Vec<Token>,
|
||||
}
|
||||
|
||||
#[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<Symbol>, opcode: Opcode, tokens: Vec<Token>) -> Node {
|
||||
Node {
|
||||
|
||||
@@ -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<Node>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Program {
|
||||
pub nodes: Vec<Node>,
|
||||
}
|
||||
@@ -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<Node>) -> Result<Vec<PathBuf>, AssembleError> {
|
||||
pub fn get_dependencies(
|
||||
nodes: &Vec<Node>,
|
||||
source_path: &Path,
|
||||
) -> Result<Vec<PathBuf>, 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)
|
||||
|
||||
@@ -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<HashMap<Symbol, u32>, Assembl
|
||||
Ok(table)
|
||||
}
|
||||
|
||||
pub fn resolve_dependencies(mut nodes: Vec<Node>) -> Result<Vec<Node>, AssembleError> {
|
||||
pub fn resolve_dependencies(
|
||||
mut nodes: Vec<Node>,
|
||||
base_dir: &Path,
|
||||
) -> Result<Vec<Node>, 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<Node>) -> Result<Vec<Node>, 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));
|
||||
}
|
||||
|
||||
@@ -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}");
|
||||
}
|
||||
|
||||
+38
-11
@@ -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<String> = 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
use std::{fmt, sync::mpsc::Sender};
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub struct Logger {
|
||||
pub sender: Sender<Entry>,
|
||||
}
|
||||
|
||||
impl Logger {
|
||||
pub fn new(sender: Sender<Entry>) -> Self {
|
||||
Self { sender }
|
||||
}
|
||||
|
||||
pub fn debug<T: fmt::Display>(&self, message: T) {
|
||||
self.sender
|
||||
.send(Entry {
|
||||
etype: EntryType::Debug,
|
||||
message: message.to_string(),
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn info<T: fmt::Display>(&self, message: T) {
|
||||
self.sender
|
||||
.send(Entry {
|
||||
etype: EntryType::Info,
|
||||
message: message.to_string(),
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn warn<T: fmt::Display>(&self, message: T) {
|
||||
self.sender
|
||||
.send(Entry {
|
||||
etype: EntryType::Warn,
|
||||
message: message.to_string(),
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn error<T: fmt::Display>(&self, message: T) {
|
||||
self.sender
|
||||
.send(Entry {
|
||||
etype: EntryType::Error,
|
||||
message: message.to_string(),
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn fatal<T: fmt::Display>(&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)
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
pub mod logging;
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
pub fn input(prompt: &str) -> String {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user