progress on serial and added editor

This commit is contained in:
2026-03-16 04:31:47 +00:00
parent a4446cdb62
commit 6061e1701e
47 changed files with 1878 additions and 396 deletions
+21
View File
@@ -0,0 +1,21 @@
[package]
name = "assembler"
version.workspace = true
edition.workspace = true
authors.workspace = true
[[bin]]
name = "dsa-a"
path = "src/main.rs"
[lib]
name = "assembler"
path = "src/lib.rs"
[dependencies]
clap = { version = "4.6.0", features = ["derive"] }
common = { path = "../common" }
num_cpus = "1.17.0"
strum = { version = "0.28.0", features = ["derive"] }
threadpool = "1.8.1"
View File
+399
View File
@@ -0,0 +1,399 @@
use common::prelude::*;
use crate::assembler::Token;
use crate::assembler::model::{Node, Opcode};
use crate::{assembler::AssembleError, expect_token};
fn log(message: &str) {
println!("\x1b[32mINFO:\x1b[0m {message}");
}
pub fn codegen(nodes: Vec<Node>) -> Result<Vec<Instruction>, AssembleError> {
let mut instructions = vec![];
for node in nodes {
println!("{:?}", node);
instructions.push(build_instruction(&node)?);
}
log("Assembly Successful ✅");
Ok(instructions)
}
fn build_instruction(node: &Node) -> Result<Instruction, AssembleError> {
let opcode = node.opcode();
let args = node.args();
match opcode {
Opcode::Nop => Ok(Instruction::nop()),
Opcode::Mov => ins_mov(opcode, &args),
Opcode::CMov => ins_cmov(opcode, &args),
Opcode::Ldb
| Opcode::Ldw
| Opcode::Ldh
| Opcode::Ldbs
| Opcode::Ldhs
| Opcode::Stb
| Opcode::Stw
| Opcode::Sth => ins_ldx_stx(opcode, &args),
Opcode::Lli | Opcode::Lui => ins_load_imm(opcode, &args),
Opcode::Push | Opcode::Pop => ins_stack(opcode, &args),
Opcode::Ieq | Opcode::Ine | Opcode::Igt | Opcode::Ige | Opcode::Ile | Opcode::Ilt => {
ins_comparison(opcode, &args)
}
Opcode::Jmp | Opcode::Call | Opcode::Jic | Opcode::Jnc => {
ins_jump_unconditional(opcode, &args)
}
Opcode::Jez | Opcode::Jnz => ins_jump_conditional(opcode, &args),
Opcode::Shl | Opcode::Shr => ins_bitshift(opcode, &args),
Opcode::Add
| Opcode::Sub
| Opcode::And
| Opcode::Or
| Opcode::Xor
| Opcode::Nand
| Opcode::Nor
| Opcode::Xnor => ins_arithmetic(opcode, &args),
Opcode::AddI | Opcode::SubI => ins_imm_arithmetic(opcode, &args),
Opcode::Not => ins_not(&args),
Opcode::Int => ins_interrupt(&args),
Opcode::Ret => Ok(Instruction::ret()),
Opcode::IRet => Ok(Instruction::irt()),
Opcode::Hlt => Ok(Instruction::hlt()),
Opcode::Data => build_data_instruction(&args),
Opcode::Segment => build_segment_instruction(&args),
Opcode::Db
| Opcode::Dh
| Opcode::Dw
| Opcode::Resb
| Opcode::Resh
| Opcode::Resw
| Opcode::Lwi
| Opcode::Include
| Opcode::Func
| Opcode::Return => Err(AssembleError::InvalidArg),
}
}
fn ins_mov(
opcode: Opcode,
args: &[crate::assembler::model::Token],
) -> Result<Instruction, AssembleError> {
let Some(src_token) = args.first() else {
return Err(AssembleError::MissingArgument(0));
};
let Some(dest_token) = args.get(1) else {
return Err(AssembleError::MissingArgument(1));
};
let src = expect_token!(src_token, Register)?;
let dest = expect_token!(dest_token, Register)?;
match opcode {
Opcode::Mov => Ok(Instruction::mov(src, dest)),
_ => unreachable!(),
}
}
fn ins_cmov(
opcode: Opcode,
args: &[crate::assembler::model::Token],
) -> Result<Instruction, AssembleError> {
let Some(src_token) = args.first() else {
return Err(AssembleError::MissingArgument(0));
};
let Some(dest_token) = args.get(1) else {
return Err(AssembleError::MissingArgument(1));
};
let Some(cmp_token) = args.get(2) else {
return Err(AssembleError::MissingArgument(1));
};
let src = expect_token!(src_token, Register)?;
let dest = expect_token!(dest_token, Register)?;
let cmp = expect_token!(cmp_token, Register)?;
match opcode {
Opcode::CMov => Ok(Instruction::cmov(src, dest, cmp)),
_ => unreachable!(),
}
}
fn ins_ldx_stx(
opcode: Opcode,
args: &[crate::assembler::model::Token],
) -> Result<Instruction, AssembleError> {
let Some(src_token) = args.first() else {
return Err(AssembleError::MissingArgument(0));
};
let Some(dest_token) = args.get(1) else {
return Err(AssembleError::MissingArgument(1));
};
let Some(offset_token) = args.get(2) else {
return Err(AssembleError::MissingArgument(2));
};
let src = expect_token!(src_token, Register)?;
let dest = expect_token!(dest_token, Register)?;
let offset = expect_token!(offset_token, Immediate)?;
match opcode {
Opcode::Ldb => Ok(Instruction::ldb(src, dest, offset as u16)),
Opcode::Ldbs => Ok(Instruction::ldbs(src, dest, offset as u16)),
Opcode::Ldh => Ok(Instruction::ldh(src, dest, offset as u16)),
Opcode::Ldhs => Ok(Instruction::ldhs(src, dest, offset as u16)),
Opcode::Ldw => Ok(Instruction::ldw(src, dest, offset as u16)),
Opcode::Stb => Ok(Instruction::stb(src, dest, offset as u16)),
Opcode::Sth => Ok(Instruction::sth(src, dest, offset as u16)),
Opcode::Stw => Ok(Instruction::stw(src, dest, offset as u16)),
_ => unreachable!(),
}
}
fn ins_stack(
opcode: Opcode,
args: &[crate::assembler::model::Token],
) -> Result<Instruction, AssembleError> {
let Some(reg_token) = args.first() else {
return Err(AssembleError::MissingArgument(0));
};
let reg = expect_token!(reg_token, Register)?;
match opcode {
Opcode::Push => Ok(Instruction::push(reg)),
Opcode::Pop => Ok(Instruction::pop(reg)),
_ => unreachable!(),
}
}
fn ins_load_imm(
opcode: Opcode,
args: &[crate::assembler::model::Token],
) -> Result<Instruction, AssembleError> {
let Some(value_token) = args.first() else {
return Err(AssembleError::MissingArgument(0));
};
let Some(dest_token) = args.get(1) else {
return Err(AssembleError::MissingArgument(1));
};
let value = expect_token!(value_token, Immediate)?;
let dest = expect_token!(dest_token, Register)?;
match opcode {
Opcode::Lli => Ok(Instruction::lli(dest, value as u16)),
Opcode::Lui => Ok(Instruction::lui(dest, (value >> 16) as u16)),
_ => unreachable!(),
}
}
fn ins_jump_unconditional(
opcode: Opcode,
args: &[crate::assembler::model::Token],
) -> Result<Instruction, AssembleError> {
let Some(addr_imm_token) = args.first() else {
return Err(AssembleError::MissingArgument(0));
};
// addr_reg is optional; default to Register::Zero if missing
let addr_reg_token = args.get(1).unwrap_or(&Token::Register(Register::Zero));
let addr_imm = expect_token!(addr_imm_token, Immediate)?;
let addr_reg = expect_token!(addr_reg_token, Register)?;
match opcode {
Opcode::Jmp => Ok(Instruction::jmp(addr_reg, addr_imm as u16)),
Opcode::Jic => Ok(Instruction::jic(addr_reg, addr_imm as u16)),
Opcode::Jnc => Ok(Instruction::jnc(addr_reg, addr_imm as u16)),
Opcode::Call => Ok(Instruction::call(addr_reg, addr_imm as u16)),
_ => unreachable!(),
}
}
fn ins_jump_conditional(
opcode: Opcode,
args: &[crate::assembler::model::Token],
) -> Result<Instruction, AssembleError> {
let Some(condition_token) = args.first() else {
return Err(AssembleError::MissingArgument(0));
};
let Some(addr_imm_token) = args.get(1) else {
return Err(AssembleError::MissingArgument(1));
};
let addr_reg_token = if let Some(token) = args.get(2) {
token
} else {
&Token::Register(Register::Zero)
};
let condition = expect_token!(condition_token, Register)?;
let addr_imm = expect_token!(addr_imm_token, Immediate)?;
let addr_reg = expect_token!(addr_reg_token, Register)?;
Ok(match opcode {
Opcode::Jez => Instruction::jez(condition, addr_reg, addr_imm as u16),
Opcode::Jnz => Instruction::jnz(condition, addr_reg, addr_imm as u16),
_ => unreachable!(),
})
}
fn ins_comparison(
opcode: Opcode,
args: &[crate::assembler::model::Token],
) -> Result<Instruction, AssembleError> {
let Some(left_token) = args.first() else {
return Err(AssembleError::MissingArgument(0));
};
let Some(right_token) = args.get(1) else {
return Err(AssembleError::MissingArgument(1));
};
let Some(dest_token) = args.get(2) else {
return Err(AssembleError::MissingArgument(2));
};
let left = expect_token!(left_token, Register)?;
let right = expect_token!(right_token, Register)?;
let dest = expect_token!(dest_token, Register)?;
Ok(match opcode {
Opcode::Ieq => Instruction::ieq(left, right, dest),
Opcode::Ine => Instruction::ine(left, right, dest),
Opcode::Igt => Instruction::igt(left, right, dest),
Opcode::Ige => Instruction::ige(left, right, dest),
Opcode::Ile => Instruction::ile(left, right, dest),
Opcode::Ilt => Instruction::ilt(left, right, dest),
_ => unreachable!(),
})
}
fn ins_bitshift(
opcode: Opcode,
args: &[crate::assembler::model::Token],
) -> Result<Instruction, AssembleError> {
let Some(src_reg) = args.first() else {
return Err(AssembleError::MissingArgument(0));
};
let Some(r_shamt) = args.get(1) else {
return Err(AssembleError::MissingArgument(0));
};
let Some(i_shamt) = args.get(2) else {
return Err(AssembleError::MissingArgument(1));
};
let Some(dest_reg) = args.get(3) else {
return Err(AssembleError::MissingArgument(1));
};
let src = expect_token!(src_reg, Register)?;
let r_shamt = expect_token!(r_shamt, Register)?;
let i_shamt = expect_token!(i_shamt, Immediate)? as u8;
let dest = expect_token!(dest_reg, Register)?;
Ok(match opcode {
Opcode::Shl => Instruction::shl(src, r_shamt, dest, i_shamt),
Opcode::Shr => Instruction::shr(src, r_shamt, dest, i_shamt),
_ => unreachable!(),
})
}
fn ins_arithmetic(
opcode: Opcode,
args: &[crate::assembler::model::Token],
) -> Result<Instruction, AssembleError> {
let Some(left_token) = args.first() else {
return Err(AssembleError::MissingArgument(0));
};
let Some(right_token) = args.get(1) else {
return Err(AssembleError::MissingArgument(1));
};
let Some(dest_token) = args.get(2) else {
return Err(AssembleError::MissingArgument(2));
};
let left = expect_token!(left_token, Register)?;
let right = expect_token!(right_token, Register)?;
let dest = expect_token!(dest_token, Register)?;
Ok(match opcode {
Opcode::Add => Instruction::add(left, right, dest),
Opcode::Sub => Instruction::sub(left, right, dest),
Opcode::And => Instruction::and(left, right, dest),
Opcode::Or => Instruction::or(left, right, dest),
Opcode::Xor => Instruction::xor(left, right, dest),
Opcode::Nand => Instruction::nand(left, right, dest),
Opcode::Nor => Instruction::nor(left, right, dest),
Opcode::Xnor => Instruction::xnor(left, right, dest),
_ => unreachable!(),
})
}
fn ins_imm_arithmetic(
opcode: Opcode,
args: &[crate::assembler::model::Token],
) -> Result<Instruction, AssembleError> {
let Some(reg_token) = args.first() else {
return Err(AssembleError::MissingArgument(0));
};
let Some(immediate_token) = args.get(1) else {
return Err(AssembleError::MissingArgument(1));
};
let Some(dest_token) = args.get(2) else {
return Err(AssembleError::MissingArgument(2));
};
let reg = expect_token!(reg_token, Register)?;
let immediate = expect_token!(immediate_token, Immediate)? as u16;
let dest = expect_token!(dest_token, Register)?;
Ok(match opcode {
Opcode::AddI => Instruction::addi(reg, dest, immediate),
Opcode::SubI => Instruction::subi(reg, dest, immediate),
_ => unreachable!(),
})
}
fn ins_not(args: &[crate::assembler::model::Token]) -> Result<Instruction, AssembleError> {
let Some(reg_token) = args.first() else {
return Err(AssembleError::MissingArgument(0));
};
let Some(dest_token) = args.get(1) else {
return Err(AssembleError::MissingArgument(1));
};
let src = expect_token!(reg_token, Register)?;
let dest = expect_token!(dest_token, Register)?;
Ok(Instruction::not(src, dest))
}
fn ins_interrupt(args: &[crate::assembler::model::Token]) -> Result<Instruction, AssembleError> {
let Some(code_token) = args.first() else {
return Err(AssembleError::MissingArgument(0));
};
let code = expect_token!(code_token, Immediate)? as u8;
Ok(Instruction::int(code))
}
fn build_data_instruction(
args: &[crate::assembler::model::Token],
) -> Result<Instruction, AssembleError> {
let Some(immediate_token) = args.first() else {
return Err(AssembleError::MissingArgument(0));
};
let immediate = expect_token!(immediate_token, Immediate)?;
Ok(Instruction::data(immediate))
}
fn build_segment_instruction(
args: &[crate::assembler::model::Token],
) -> Result<Instruction, AssembleError> {
let Some(immediate_token) = args.first() else {
return Err(AssembleError::MissingArgument(0));
};
let immediate = expect_token!(immediate_token, Immediate)?;
Ok(Instruction::data(
// mask the first 6 bits for the opcode
// this is really deprecated tbh. may remove in future.
Opcode::Segment as u32 | (immediate & 0x05FFFFFF),
))
}
+252
View File
@@ -0,0 +1,252 @@
use common::prelude::Register;
use crate::assembler::model::{Node, Opcode, Token};
use crate::{assembler::AssembleError, expect_token, expect_type, node};
pub fn expand_pseudo_ops(mut nodes: Vec<Node>, module: u64) -> Result<Vec<Node>, AssembleError> {
let mut result = Vec::<Node>::with_capacity(nodes.len());
for node in &mut nodes {
if try_expand(node.clone(), &mut result, module).is_err() {
result.push(node.clone());
}
}
Ok(result)
}
fn try_expand(node: Node, result: &mut Vec<Node>, _module: u64) -> Result<(), AssembleError> {
match node.opcode() {
Opcode::Func => expand_func(&node, result),
Opcode::Return => expand_return(&node, result),
Opcode::Ldb | Opcode::Ldbs | Opcode::Ldh | Opcode::Ldhs | Opcode::Ldw => {
expand_ldx(&node, result)?;
}
Opcode::Stb | Opcode::Sth | Opcode::Stw => expand_stx(&node, result)?,
Opcode::Lwi => expand_lwi(&node, result)?,
Opcode::Resb | Opcode::Resh | Opcode::Resw => expand_resx(&node, result)?,
Opcode::Db | Opcode::Dh | Opcode::Dw => expand_dx(&node, result)?,
_ => result.push(node),
}
Ok(())
}
/// Function stack frame initialisation
fn expand_func(current: &Node, nodes: &mut Vec<Node>) {
let label = current.label();
let spr = Token::Register(Register::Spr);
let bpr = Token::Register(Register::Bpr);
nodes.extend(vec![
node!(label, Opcode::Push, bpr),
node!(None, Opcode::Mov, spr, bpr),
]);
}
/// Return from a function
fn expand_return(current: &Node, nodes: &mut Vec<Node>) {
let label = current.label();
let spr = Token::Register(Register::Spr);
let ret = Token::Register(Register::Ret);
let bpr = Token::Register(Register::Bpr);
nodes.extend(vec![
node!(label, Opcode::Mov, bpr, spr),
node!(None, Opcode::Pop, bpr),
node!(None, Opcode::Ret),
]);
}
fn expand_ldx(current: &Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
let opcode = current.opcode();
let args: Vec<Token> = current.args().into_iter().take(3).collect();
let Some(name) = args.first() else {
return Err(AssembleError::MissingArgument(0));
};
let Some(reg) = args.get(1) else {
return Err(AssembleError::MissingArgument(1));
};
let Some(offset) = args.get(2) else {
return Err(AssembleError::MissingArgument(2));
};
let name = expect_type!(name, Symbol)?;
let reg = expect_type!(reg, Register)?;
let offset = expect_type!(offset, Immediate)?;
nodes.extend(vec![
node!(current.label(), Opcode::Lli, name, reg),
node!(None, Opcode::Lui, name, reg),
node!(None, opcode, reg, reg, offset),
]);
Ok(())
}
fn expand_stx(current: &Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
let opcode = current.opcode();
let args: Vec<Token> = current.args().into_iter().take(3).collect();
let Some(base) = args.first() else {
return Err(AssembleError::MissingArgument(0));
};
let Some(dest) = args.get(1) else {
return Err(AssembleError::MissingArgument(1));
};
let Some(offset) = args.get(2) else {
return Err(AssembleError::MissingArgument(2));
};
let base = expect_type!(base, Register)?;
let dest = expect_type!(dest, Symbol)?;
let offset = expect_type!(offset, Immediate)?;
let temp = Token::Register(Register::Acc);
nodes.extend(vec![
node!(current.label(), Opcode::Lli, dest, temp),
node!(None, Opcode::Lui, dest, temp),
node!(None, opcode, base, temp, offset),
]);
Ok(())
}
fn expand_lwi(current: &Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
let Ok(val) = current.arg(0) else {
return Err(AssembleError::MissingArgument(0));
};
let Ok(reg) = current.arg(1) else {
return Err(AssembleError::MissingArgument(1));
};
let val = expect_type!(val, Symbol, Immediate)?;
let reg = expect_type!(reg, Register)?;
nodes.extend(vec![
node!(current.label(), Opcode::Lli, val, reg),
node!(None, Opcode::Lui, val, reg),
]);
Ok(())
}
fn expand_resx(current: &Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
let Ok(region_label) = current.arg(0) else {
return Err(AssembleError::MissingArgument(0));
};
let Ok(size) = current.arg(1) else {
return Err(AssembleError::MissingArgument(1));
};
let region_label = expect_token!(region_label, Symbol)?;
let size = expect_token!(size, Immediate)?;
let units_per = match current.opcode() {
Opcode::Resb => 4,
Opcode::Resh => 2,
Opcode::Resw => 1,
_ => unreachable!(),
};
let mut buffer = vec![];
// push the inital node with the label
for _ in 0..size.div_ceil(units_per) {
// push the rest of the nodes
buffer.push(node!(None, Opcode::Data, 0));
}
buffer[0].symbol = Some(region_label);
nodes.extend(buffer);
Ok(())
}
fn expand_dx(current: &Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
let Ok(region_label) = current.arg(0) else {
return Err(AssembleError::MissingArgument(0));
};
let region_label = expect_token!(region_label, Symbol)?;
let size = match current.opcode() {
Opcode::Db => 4,
Opcode::Dh => 2,
Opcode::Dw => 1,
_ => unreachable!(),
};
let mut buffer = vec![];
let mut args = current.args();
let _label = args.remove(0);
for word in process_dx_data(args, size)? {
buffer.push(node!(None, Opcode::Data, Token::Immediate(word)));
}
buffer[0].symbol = Some(region_label);
nodes.extend(buffer);
Ok(())
}
fn process_dx_data(args: Vec<Token>, size: usize) -> Result<Vec<u32>, AssembleError> {
assert!(matches!(size, 1 | 2 | 4));
let mut buffer = Vec::<u8>::new();
// Process each token
for token in args {
match token {
Token::StringLit(mut s) => {
s.push('\0');
// Split string into chars and write as bytes
for ch in s.chars() {
// Convert char to bytes (UTF-8 encoding)
let mut char_buf = [0u8; 4];
let char_bytes = ch.encode_utf8(&mut char_buf);
buffer.extend_from_slice(char_bytes.as_bytes());
}
}
Token::Immediate(value) => {
// Split u32 into bytes (little-endian)
buffer.extend_from_slice(&value.to_le_bytes());
}
_ => {
return Err(AssembleError::Generic);
}
}
// Pad buffer to alignment boundary with zeros
let remainder = buffer.len() % size;
if remainder != 0 {
let padding = size - remainder;
buffer.resize(buffer.len() + padding, 0);
}
}
// Convert byte buffer to u32 chunks
// Pad final buffer to u32 boundary if needed
let remainder = buffer.len() % 4;
if remainder != 0 {
let padding = 4 - remainder;
buffer.resize(buffer.len() + padding, 0);
}
// Convert bytes to u32s efficiently using chunks_exact
let result = buffer
.chunks_exact(4)
.map(|chunk| {
// Convert 4 bytes to u32 (little-endian)
u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]])
})
.collect();
Ok(result)
}
+167
View File
@@ -0,0 +1,167 @@
use std::str::FromStr;
use crate::assembler::AssembleError;
use crate::assembler::model::{Module, Opcode, Symbol, Token};
use common::prelude::Register;
pub fn lexer(mut program: String, module: u64) -> Result<Vec<Token>, AssembleError> {
let mut tokens = Vec::new();
let lines = program.lines();
let mut literal = String::new();
for line in lines {
for (i, token) in line.split_whitespace().enumerate() {
if token.starts_with("//") {
break;
}
if let Some(stripped) = token.strip_prefix('"') {
literal.push_str(stripped);
}
if !literal.is_empty() {
if !token.starts_with('"') {
if i > 0 {
literal.push(' ');
}
literal.push_str(token);
}
if token.ends_with('"') {
literal.pop(); // remove the closing quote
tokens.push(Token::StringLit(literal));
literal = String::new();
}
continue;
}
let token = token.trim_end_matches(',');
if token.is_empty() {
continue;
}
if let Some(token) = parse_register(token)? {
tokens.push(token);
} else if let Some(token) = parse_opcode(token)? {
tokens.push(token);
} else if let Some(token) = parse_hex(token)? {
tokens.push(token);
} else if let Some(token) = parse_octal(token)? {
tokens.push(token);
} else if let Some(token) = parse_binary(token)? {
tokens.push(token);
} else if let Some(token) = parse_decimal(token)? {
tokens.push(token);
} else if let Some(token) = parse_label(token, module)? {
tokens.push(token);
} else if let Some(token) = parse_symbol(token, module)? {
tokens.push(token);
} else {
return Err(AssembleError::Generic);
}
}
}
// println!("{:#?}", tokens);
Ok(tokens)
}
pub fn parse_register(token: &str) -> Result<Option<Token>, AssembleError> {
Ok(Register::from_str(token).map(Token::Register).ok())
}
pub fn parse_opcode(token: &str) -> Result<Option<Token>, AssembleError> {
Ok(Opcode::from_str(token).ok().map(Token::Opcode))
}
pub fn parse_hex(token: &str) -> Result<Option<Token>, AssembleError> {
if (token.len() < 3) | !token.starts_with("0x") {
return Ok(None);
}
let Some(lit) = &token.get(2..) else {
return Err(AssembleError::InvalidArg);
};
u32::from_str_radix(lit, 16).map_or(Err(AssembleError::Generic), |value| {
Ok(Some(Token::Immediate(value)))
})
}
pub fn parse_octal(token: &str) -> Result<Option<Token>, AssembleError> {
if (token.len() < 3) | !token.starts_with("0o") {
return Ok(None);
}
let Some(lit) = &token.get(2..) else {
return Err(AssembleError::InvalidArg);
};
u32::from_str_radix(lit, 8).map_or(Err(AssembleError::Generic), |value| {
Ok(Some(Token::Immediate(value)))
})
}
pub fn parse_binary(token: &str) -> Result<Option<Token>, AssembleError> {
if (token.len() < 3) | !token.starts_with("0b") {
return Ok(None);
}
let Some(lit) = &token.get(2..) else {
return Err(AssembleError::InvalidArg);
};
u32::from_str_radix(lit, 2).map_or(Err(AssembleError::Generic), |value| {
Ok(Some(Token::Immediate(value)))
})
}
pub fn parse_decimal(token: &str) -> Result<Option<Token>, AssembleError> {
let Ok(tok) = token.parse::<u32>() else {
return Ok(None);
};
Ok(Some(Token::Immediate(tok)))
}
pub fn parse_label(token: &str, module: u64) -> Result<Option<Token>, AssembleError> {
if token.ends_with(':') {
Ok(Some(Token::Symbol(Symbol {
name: token[0..token.len() - 1].to_string(),
module: Module::Resolved(module),
})))
} else {
Ok(None)
}
}
pub fn parse_symbol(token: &str, module: u64) -> Result<Option<Token>, AssembleError> {
let Some(tokc) = token.chars().next() else {
return Err(AssembleError::Generic); // TODO: What is this error?
};
if tokc.is_numeric() {
return Ok(None);
}
let mut split = token.splitn(2, "::");
let Some(symbol1) = split.next() else {
return Err(AssembleError::InvalidArg);
};
let symbol1 = symbol1.to_string();
if let Some(symbol2) = split.next() {
Ok(Some(Token::Symbol(Symbol {
name: symbol2.to_string(),
module: Module::Unresolved(symbol1),
})))
} else {
Ok(Some(Token::Symbol(Symbol {
name: symbol1,
module: Module::Resolved(module),
})))
}
}
+139
View File
@@ -0,0 +1,139 @@
//! Macros used throughout the assembler
use crate::assembler::model::{Node, Opcode, Symbol, Token};
/// Parse DSA assembly code with optional formatting
///
/// # Examples
/// ```rs
/// use assembler::macros::dsa;
/// // 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)),+]
)
};
($symbol: expr, $opcode: expr) => {
$crate::assembler::model::Node::new(
$symbol.clone(),
$opcode.clone(),
Vec::new()
)
};
(@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 };
}
+266
View File
@@ -0,0 +1,266 @@
#![allow(dead_code, unused)]
use std::{
collections::HashSet,
fmt, fs,
hash::{DefaultHasher, Hash, Hasher},
path::{Path, PathBuf},
sync::{
Arc, Mutex,
mpsc::{self, Receiver, Sender},
},
thread,
};
use common::prelude::Instruction;
// Module declarations
#[macro_use]
pub mod macros;
#[allow(clippy::module_inception)]
pub mod codegen;
pub mod expand;
pub mod lexer;
pub mod model;
pub mod parser;
pub mod resolver;
use crate::assemblerv2::lexer::Lexer;
// 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},
};
pub struct Assembler {
src_path: PathBuf,
result_tx: mpsc::Sender<Result<Vec<u8>, AssembleError>>,
result_rx: Option<mpsc::Receiver<Result<Vec<u8>, AssembleError>>>,
is_running: bool,
}
impl Assembler {
#[must_use]
pub fn new(src_path: impl Into<PathBuf>) -> Self {
let (tx, rx) = mpsc::channel();
Self {
src_path: src_path.into(),
result_tx: tx,
result_rx: Some(rx),
is_running: false,
}
}
/// Start the compilation process in a separate thread
pub fn start(&mut self, args: ()) {
if self.is_running {
return;
}
let src = self.src_path.clone();
let tx = self.result_tx.clone();
thread::spawn(move || match assemble(&src) {
Ok(res) => {
let buffer: Vec<u8> = res
.iter()
.flat_map(|instruction| instruction.to_le_bytes())
.collect();
tx.send(Ok(buffer))
.expect("Failed to send compilation result from worker thread");
}
Err(err) => {
tx.send(Err(err))
.expect("Failed to send compilation error from worker thread");
}
});
self.is_running = true;
}
pub fn poll(&mut self) -> Option<Result<Vec<u8>, String>> {
if !self.is_running {
return None;
}
match self
.result_rx
.as_ref()
.expect("result_rx should be Some while compilation is running")
.try_recv()
{
Ok(result) => {
self.is_running = false;
Some(result.map_err(|e| e.to_string()))
}
Err(mpsc::TryRecvError::Empty) => None,
Err(mpsc::TryRecvError::Disconnected) => {
self.is_running = false;
Some(Err(String::from(
"Compilation terminated before a result was returned",
)))
}
}
}
/// Block until compilation is complete and return the result
pub fn output(&mut self) -> Result<Vec<u8>, String> {
if let Ok(result) = self
.result_rx
.take()
.expect("result_rx should be Some while waiting for compilation result")
.recv()
{
self.is_running = false;
result.map_err(|e| e.to_string())
} else {
self.is_running = false;
Err(String::from(
"Compilation terminated before a result was returned",
))
}
}
}
impl Assembler {}
fn assemble(src: &Path) -> Result<Vec<Instruction>, AssembleError> {
let mut modules = HashSet::new();
let mut program = Program::new();
let hash = quick_hash(src);
if modules.contains(&hash) {
return Ok(vec![]);
}
prepare_dependency(src, &mut modules, &mut program)?;
let mut nodes = program.nodes.clone();
create_sections(&mut nodes)?;
resolve_symbols(&mut nodes)?;
println!("Generating assembly output...");
let instructions = codegen(nodes)?;
println!("Compilation Successful");
Ok(instructions)
}
fn prepare_dependency(
path: &Path,
modules: &mut HashSet<u64>,
program: &mut Program,
) -> Result<(), AssembleError> {
let filename = path
.file_name()
.and_then(|n| n.to_str())
.expect("Failed to get file name from path");
if let Ok(path) = path.canonicalize() {
println!("{:20} {:20} [{}]", "Building", filename, path.display());
}
let src =
fs::read_to_string(path).map_err(|_| AssembleError::InvalidFile(path.to_path_buf()))?;
let file_hash = quick_hash(path);
println!("{:20} {:20}", "Tokenising", filename);
let tokens = lexer::lexer(src, file_hash)?;
// let tokens = Lexer::new(src, file_hash).run()?;
println!("{:20} {:20}", "Parsing", filename);
let parsed = Parser::parse_nodes(tokens)?;
println!("{:20} {:20}", "Resolving Deps", filename);
// 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 mut nodes = expand_pseudo_ops(parsed, file_hash)?;
nodes = resolve_dependencies(nodes, base_dir)?;
let deps = Parser::get_dependencies(&nodes, path)?;
println!("{:20} {:20}", "Expanding Pseudo-ops", filename);
// add a section instruction
nodes.insert(
0,
node!(None, Opcode::Segment, Token::Immediate(file_hash as u32)),
);
// for n in &nodes {
// println!("{n}");
// }
program.add_module(nodes);
for dep in deps {
println!(
"{:20} {:20}",
"Including",
dep.file_name()
.and_then(|f| f.to_str())
.expect("Dependency path has no file name or is not valid UTF-8")
);
let dep_hash = quick_hash(&dep);
if modules.insert(dep_hash) {
prepare_dependency(dep.as_path(), modules, program)?;
}
}
Ok(())
}
#[derive(Debug, Clone)]
pub enum AssembleError {
Generic,
UnexpectedEof,
InvalidFile(PathBuf),
UnexpectedToken(Token, TokenType),
InvalidArg,
UndefinedSymbol(Symbol),
/// Contains the nth element missing from the instruction.
MissingArgument(u8),
}
impl fmt::Display for AssembleError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Generic => write!(f, "Generic error"),
Self::UnexpectedToken(tok, expected) => {
write!(f, "Unexpected token {tok:?}, expected {expected:?}")
}
Self::UnexpectedEof => write!(f, "Unexpected end of file"),
Self::InvalidFile(path) => write!(f, "Invalid file `{}`", path.display()),
Self::InvalidArg => write!(f, "Invalid argument"),
Self::UndefinedSymbol(symbol) => {
write!(f, "Undefined symbol {symbol}")
}
Self::MissingArgument(n) => {
write!(f, "Missing argument #{n} from instruction arguments.")
}
}
}
}
fn quick_hash(value: &Path) -> u64 {
let mut hasher = DefaultHasher::new();
value
.canonicalize()
.expect("Failed to canonicalize path for quick_hash")
.to_str()
.hash(&mut hasher);
hasher.finish()
}
+484
View File
@@ -0,0 +1,484 @@
use std::{fmt, str::FromStr};
use common::prelude::Register;
use crate::assembler::AssembleError;
#[derive(Debug, Clone)]
pub struct Node {
pub symbol: Option<Symbol>,
pub opcode: Opcode,
pub tokens: Vec<Token>,
}
impl Node {
#[must_use]
pub const fn new(symbol: Option<Symbol>, opcode: Opcode, tokens: Vec<Token>) -> Self {
Self {
symbol,
opcode,
tokens,
}
}
#[must_use]
pub fn label(&self) -> Option<Symbol> {
self.symbol.clone()
}
#[must_use]
pub const fn opcode(&self) -> Opcode {
self.opcode
}
#[must_use]
pub fn args(&self) -> Vec<Token> {
self.tokens.clone()
}
pub fn arg(&self, index: usize) -> Result<Token, AssembleError> {
self.args()
.get(index)
.cloned()
.ok_or(AssembleError::InvalidArg)
}
}
impl fmt::Display for Node {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let symbol = self
.label()
.as_ref()
.map_or_else(String::new, |symbol| format!("{symbol}:\n"));
let args = self
.args()
.into_iter()
.map(|arg| arg.to_string())
.collect::<Vec<_>>()
.join(" ");
write!(
f,
"\x1b[93m{} \t\x1b[94m{} \x1b[37m{} \x1b[0m",
symbol,
self.opcode(),
args,
)
}
}
impl fmt::Display for Symbol {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} [ID:{}]", self.name, self.module)
}
}
impl fmt::Display for Module {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Unresolved(name) => write!(f, "{name}"),
Self::Resolved(name) => write!(f, "{name}"),
}
}
}
impl fmt::Display for Opcode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Nop => write!(f, "nop"),
Self::Mov => write!(f, "mov"),
Self::CMov => write!(f, "movs"),
Self::Ldb => write!(f, "ldb"),
Self::Ldbs => write!(f, "ldbs"),
Self::Ldh => write!(f, "ldh"),
Self::Ldhs => write!(f, "ldhs"),
Self::Ldw => write!(f, "ldw"),
Self::Stb => write!(f, "stb"),
Self::Sth => write!(f, "sth"),
Self::Stw => write!(f, "stw"),
Self::Lli => write!(f, "lli"),
Self::Lui => write!(f, "lui"),
Self::Jmp => write!(f, "jmp"),
Self::Jez => write!(f, "jez"),
Self::Jnz => write!(f, "jnz"),
Self::Jic => write!(f, "jic"),
Self::Jnc => write!(f, "jnc"),
Self::Ieq => write!(f, "ieq"),
Self::Ine => write!(f, "ine"),
Self::Igt => write!(f, "igt"),
Self::Ige => write!(f, "ige"),
Self::Ilt => write!(f, "ilt"),
Self::Ile => write!(f, "ile"),
Self::Shl => write!(f, "shl"),
Self::Shr => write!(f, "shr"),
Self::Add => write!(f, "add"),
Self::Sub => write!(f, "sub"),
Self::AddI => write!(f, "addi"),
Self::SubI => write!(f, "subi"),
Self::And => write!(f, "and"),
Self::Or => write!(f, "or"),
Self::Not => write!(f, "not"),
Self::Xor => write!(f, "xor"),
Self::Nand => write!(f, "nand"),
Self::Nor => write!(f, "nor"),
Self::Xnor => write!(f, "xnor"),
Self::Int => write!(f, "int"),
Self::IRet => write!(f, "irt"),
Self::Hlt => write!(f, "hlt"),
Self::Db => write!(f, "db"),
Self::Dh => write!(f, "dh"),
Self::Dw => write!(f, "dw"),
Self::Resb => write!(f, "resb"),
Self::Resh => write!(f, "resh"),
Self::Resw => write!(f, "resw"),
Self::Push => write!(f, "push"),
Self::Pop => write!(f, "pop"),
Self::Lwi => write!(f, "lwi"),
Self::Func => write!(f, "func"),
Self::Call => write!(f, "call"),
Self::Ret => write!(f, "ret"),
Self::Return => write!(f, "return"),
Self::Include => write!(f, "include"),
Self::Data => write!(f, "data"),
Self::Segment => write!(f, "[SEGMENT]"),
}
}
}
#[derive(Debug, Clone, Eq)]
pub struct Symbol {
pub name: String,
pub module: Module,
}
impl std::hash::Hash for Symbol {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
self.module.hash(state);
}
}
impl PartialEq for Symbol {
fn eq(&self, other: &Self) -> bool {
self.name == other.name && self.module == other.module
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Module {
Resolved(u64),
Unresolved(String),
}
#[derive(Debug, Clone)]
pub enum Token {
Symbol(Symbol),
Register(Register),
Immediate(u32),
StringLit(String),
CharLit(char),
Opcode(Opcode),
}
impl fmt::Display for Token {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Symbol(symbol) => write!(f, "{symbol}"),
Self::Register(register) => write!(f, "{register}",),
Self::Immediate(immediate) => write!(f, "{immediate}",),
Self::StringLit(string_lit) => write!(f, "{string_lit}",),
Self::CharLit(char_lit) => write!(f, "{char_lit}",),
Self::Opcode(opcode) => write!(f, "{opcode}",),
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum TokenType {
Symbol,
Register,
Immediate,
StringLit,
CharLit,
Opcode,
}
impl TokenType {
#[must_use]
pub const fn from_token(token: &Token) -> Self {
match token {
Token::Symbol(_) => Self::Symbol,
Token::Register(_) => Self::Register,
Token::Immediate(_) => Self::Immediate,
Token::StringLit(_) => Self::StringLit,
Token::CharLit(_) => Self::CharLit,
Token::Opcode(_) => Self::Opcode,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Opcode {
// Real instructions (0x00-0x26)
Nop,
Mov,
CMov,
Ldb,
Ldbs,
Ldh,
Ldhs,
Ldw,
Stb,
Sth,
Stw,
Lli,
Lui,
Jmp,
Jez,
Jnz,
Jic,
Jnc,
Ieq,
Ine,
Igt,
Ige,
Ilt,
Ile,
Shl,
Shr,
Add,
Sub,
AddI,
SubI,
And,
Or,
Not,
Xor,
Nand,
Nor,
Xnor,
Int,
IRet,
Hlt,
// Function instructions
Call,
Ret,
// Stack ops
Push,
Pop,
// Pseudo-instructions
Db,
Dh,
Dw,
Resb,
Resh,
Resw,
Lwi,
Func,
Return,
// meta instructions (these aren't present in the binary as instructions)
Include,
Data,
Segment,
}
#[derive(Debug)]
pub enum OpcodeFromStrError {
InvalidRegister(&'static str),
InvalidOpcode(String),
}
impl std::fmt::Display for OpcodeFromStrError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidRegister(reg) => write!(f, "register does not exist: {reg}"),
Self::InvalidOpcode(op) => write!(f, "instruction does not exist: {op}"),
}
}
}
impl std::error::Error for OpcodeFromStrError {}
impl FromStr for Opcode {
type Err = OpcodeFromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"nop" => Ok(Self::Nop),
"mov" => Ok(Self::Mov),
"cmov" => Ok(Self::CMov),
"ldb" => Ok(Self::Ldb),
"ldbs" => Ok(Self::Ldbs),
"ldh" => Ok(Self::Ldh),
"ldhs" => Ok(Self::Ldhs),
"ldw" => Ok(Self::Ldw),
"stb" => Ok(Self::Stb),
"sth" => Ok(Self::Sth),
"stw" => Ok(Self::Stw),
"lli" => Ok(Self::Lli),
"lui" => Ok(Self::Lui),
// comparison
"ieq" => Ok(Self::Ieq),
"ine" => Ok(Self::Ine),
"igt" => Ok(Self::Igt),
"ige" => Ok(Self::Ige),
"ilt" => Ok(Self::Ilt),
"ile" => Ok(Self::Ile),
// jumps
"jmp" => Ok(Self::Jmp),
"jez" => Ok(Self::Jez),
"jnz" => Ok(Self::Jnz),
"jic" => Ok(Self::Jic),
"jnc" => Ok(Self::Jnc),
"shl" => Ok(Self::Shl),
"shr" => Ok(Self::Shr),
"add" => Ok(Self::Add),
"sub" => Ok(Self::Sub),
"and" => Ok(Self::And),
"or" => Ok(Self::Or),
"not" => Ok(Self::Not),
"xor" => Ok(Self::Xor),
"nand" => Ok(Self::Nand),
"nor" => Ok(Self::Nor),
"xnor" => Ok(Self::Xnor),
"addi" => Ok(Self::AddI),
"subi" => Ok(Self::SubI),
// stack ops
"push" => Ok(Self::Push),
"pop" => Ok(Self::Pop),
// function instructions
"call" => Ok(Self::Call),
"ret" => Ok(Self::Ret),
"int" => Ok(Self::Int),
"irt" | "iret" => Ok(Self::IRet),
"hlt" => Ok(Self::Hlt),
// pseudoinstructions
"func" => Ok(Self::Func),
"return" => Ok(Self::Return),
"lwi" => Ok(Self::Lwi),
// directives
"include" => Ok(Self::Include),
"db" => Ok(Self::Db),
"dh" => Ok(Self::Dh),
"dw" => Ok(Self::Dw),
"resb" => Ok(Self::Resb),
"resh" => Ok(Self::Resh),
"resw" => Ok(Self::Resw),
// function pseudoinstructions
_ => Err(OpcodeFromStrError::InvalidOpcode(s.to_string())),
}
}
}
impl Opcode {
pub const OPCODES: &[&str] = &[
// Real instructions (0x00-0x26)
"nop", "mov", "movs", "ldb", "ldbs", "ldh", "ldhs", "ldw", "stb", "sth", "stw", "lli",
"lui", "jmp", "jeq", "jne", "jgt", "jge", "jlt", "jle", "cmp", "inc", "dec", "shl", "shr",
"add", "sub", "and", "or", "not", "xor", "nand", "nor", "xnor", "int", "iret", "hlt",
"addi", "subi", "call", "ret", // Pseudo-instructions
"db", "dh", "dw", "resb", "resh", "resw", "lwi", "func", "return",
// meta instructions
"include",
];
#[must_use]
pub const fn to_opcode_value(&self) -> Option<u8> {
match self {
Self::Nop => Some(0x00),
Self::Mov => Some(0x01),
Self::CMov => Some(0x02),
Self::Ldb => Some(0x03),
Self::Ldbs => Some(0x04),
Self::Ldh => Some(0x05),
Self::Ldhs => Some(0x06),
Self::Ldw => Some(0x07),
Self::Stb => Some(0x08),
Self::Sth => Some(0x09),
Self::Stw => Some(0x0A),
Self::Lli => Some(0x0B),
Self::Lui => Some(0x0C),
Self::Ieq => Some(0x0D),
Self::Ine => Some(0x0E),
Self::Ilt => Some(0x0F),
Self::Ile => Some(0x10),
Self::Igt => Some(0x11),
Self::Ige => Some(0x12),
Self::Jmp => Some(0x13),
Self::Jez => Some(0x14),
Self::Jnz => Some(0x15),
Self::Jic => Some(0x16),
Self::Jnc => Some(0x17),
Self::And => Some(0x18),
Self::Nand => Some(0x19),
Self::Or => Some(0x1A),
Self::Nor => Some(0x1B),
Self::Xor => Some(0x1C),
Self::Xnor => Some(0x1D),
Self::Not => Some(0x1E),
Self::Add => Some(0x1F),
Self::Sub => Some(0x20),
Self::Shl => Some(0x21),
Self::Shr => Some(0x22),
Self::AddI => Some(0x23),
Self::SubI => Some(0x24),
Self::Push => Some(0x25),
Self::Pop => Some(0x26),
Self::Call => Some(0x27),
Self::Ret => Some(0x28),
Self::Int => Some(0x29),
Self::IRet => Some(0x2A),
Self::Hlt => Some(0x2B),
Self::Segment => Some(0x2C),
// Pseudo-instructions don't have opcode values
_ => None,
}
}
#[must_use]
pub const fn is_pseudo_instruction(&self) -> bool {
matches!(
self,
Self::Db
| Self::Dh
| Self::Dw
| Self::Resb
| Self::Resh
| Self::Resw
| Self::Lwi
| Self::Func
| Self::Return
)
}
}
+418
View File
@@ -0,0 +1,418 @@
use std::path::{Path, PathBuf};
use crate::assembler::TokenType;
use crate::{assembler::AssembleError, expect_token, expect_type, node};
use crate::assembler::model::{Node, Opcode, Token};
use common::prelude::*;
pub struct Parser {
tokens: Vec<Token>,
nodes: Vec<Node>,
}
#[derive(Debug)]
pub struct Program {
pub nodes: Vec<Node>,
}
impl Program {
#[must_use]
pub const fn new() -> Self {
Self { nodes: vec![] }
}
pub fn add_module(&mut self, module: Vec<Node>) {
self.nodes.extend(module);
}
pub fn parser(&mut self) -> Parser {
Parser {
tokens: vec![],
nodes: self.nodes.clone(),
}
}
}
impl Default for Program {
fn default() -> Self {
Self::new()
}
}
impl Parser {
pub fn parse_nodes(tokens: Vec<Token>) -> Result<Vec<Node>, AssembleError> {
let mut self_ = Self {
tokens: tokens.into_iter().rev().collect(),
nodes: vec![],
};
while !self_.tokens.is_empty() {
let ins = self_.parse_instruction()?;
self_.nodes.push(ins);
}
Ok(self_.nodes.clone())
}
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 node.opcode() == Opcode::Include {
let path_str =
expect_token!(node.args().get(1).ok_or(AssembleError::Generic)?, 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)
}
#[expect(clippy::too_many_lines, clippy::cognitive_complexity)]
fn parse_instruction(&mut self) -> Result<Node, AssembleError> {
if self.tokens.is_empty() {
unreachable!();
}
// check if the Node starts with a label
let label = expect_token!(self.peek_next()?, Symbol).ok();
if label.is_some() {
self.tokens.pop();
}
let opcode = expect_token!(self.next()?, Opcode)?;
let args: Vec<Token>;
#[allow(clippy::match_same_arms)]
match opcode {
// R-type instructions
Opcode::Mov | Opcode::CMov => {
let reg1 = expect_type!(self.next()?, Register, Symbol)?;
let reg2 = expect_type!(self.next()?, Register, Symbol)?;
args = vec![reg1, reg2];
}
Opcode::Ldb | Opcode::Ldbs | Opcode::Ldh | Opcode::Ldhs | Opcode::Ldw => {
let base = expect_type!(self.next()?, Register, Symbol)?;
let dest = expect_type!(self.next()?, Register)?;
let offset = match self.peek_next() {
Ok(next) if expect_type!(next.clone(), Immediate).is_ok() => self.next()?,
_ => Token::Immediate(0),
};
args = vec![base, dest, offset];
}
Opcode::Stb | Opcode::Sth | Opcode::Stw => {
let base = expect_type!(self.next()?, Register)?;
let dest = expect_type!(self.next()?, Register, Symbol)?;
let offset = match self.peek_next() {
Ok(next) if expect_type!(next.clone(), Immediate).is_ok() => self.next()?,
_ => Token::Immediate(0),
};
args = vec![base, dest, offset];
}
Opcode::Add
| Opcode::Sub
| Opcode::And
| Opcode::Or
| Opcode::Xor
| Opcode::Nand
| Opcode::Nor
| Opcode::Xnor => {
let src1 = expect_type!(self.next()?, Register, Symbol)?;
let src2 = expect_type!(self.next()?, Register, Symbol)?;
let dest = expect_type!(self.next()?, Register, Symbol)?;
args = vec![src1, src2, dest];
}
Opcode::Not => {
let src = expect_type!(self.next()?, Register, Symbol)?;
let dest = expect_type!(self.next()?, Register, Symbol)?;
args = vec![src, dest];
}
Opcode::Shl | Opcode::Shr => {
let src = expect_type!(self.next()?, Register, Symbol)?;
// First operand after src: could be immediate or register
let first = self.next()?;
let (r_shamt, i_shamt) = match first {
Token::Register(_) => (
first,
if let Ok(tok) = self.peek_next() {
if expect_type!(tok, Immediate).is_ok() {
self.next()?
} else {
Token::Immediate(0)
}
} else {
Token::Immediate(0)
},
),
Token::Immediate(_) => (Token::Register(Register::Zero), first),
_ => {
return Err(AssembleError::UnexpectedToken(first, TokenType::Immediate));
}
};
let dest = if let Ok(tok) = self.peek_next() {
if expect_type!(tok, Register).is_ok() {
self.next()?
} else {
src.clone() // Default to src if no dest specified
}
} else {
src.clone() // Default to src if no dest specified
};
args = vec![src, r_shamt, i_shamt, dest];
}
Opcode::Include => {
let mod_name = expect_type!(self.next()?, Symbol)?;
let path = expect_type!(self.next()?, StringLit)?;
args = vec![mod_name, path];
}
// Unconditional jump
Opcode::Jmp | Opcode::Jic | Opcode::Jnc => {
let imm = expect_type!(self.next()?, Immediate, Symbol)?;
let offset = match self.peek_next() {
Ok(token) => {
if expect_type!(token, Register).is_ok() {
self.next()?
} else {
Token::Register(Register::Zero)
}
}
Err(_) => Token::Register(Register::Zero),
};
args = vec![imm, offset];
}
Opcode::Ieq | Opcode::Ine | Opcode::Ilt | Opcode::Igt | Opcode::Ile | Opcode::Ige => {
let src1 = expect_type!(self.next()?, Register, Symbol)?;
let src2 = expect_type!(self.next()?, Register, Symbol)?;
let dest = expect_type!(self.next()?, Register, Symbol)?;
args = vec![src1, src2, dest];
}
Opcode::Jez | Opcode::Jnz => {
let condition = expect_type!(self.next()?, Register)?;
let imm = expect_type!(self.next()?, Immediate, Symbol)?;
let offset = match self.peek_next() {
Ok(token) => {
if expect_type!(token, Register).is_ok() {
self.next()?
} else {
Token::Register(Register::Zero)
}
}
Err(_) => Token::Register(Register::Zero),
};
args = vec![condition, imm, offset];
}
Opcode::Call => {
let addr = expect_type!(self.next()?, Symbol)?;
args = vec![addr];
}
// I-type instructions
Opcode::Lui | Opcode::Lli | Opcode::Lwi => {
let imm = expect_type!(self.next()?, Immediate, Symbol)?;
let reg = expect_type!(self.next()?, Register)?;
args = vec![imm, reg];
}
// Immediate Arithmetic
Opcode::AddI | Opcode::SubI => {
let reg = expect_type!(self.next()?, Register)?;
let imm = expect_type!(self.next()?, Immediate)?;
let reg2 = if expect_type!(self.peek_next()?, Register).is_ok() {
self.next()?
} else {
reg.clone()
};
args = vec![reg, imm, reg2];
}
// D-type pseudoinstructions (data definition)
Opcode::Resb | Opcode::Resh | Opcode::Resw => {
let name = expect_type!(self.next()?, Symbol)?;
let num = expect_type!(self.next()?, Immediate)?;
args = vec![name, num];
}
Opcode::Db | Opcode::Dh | Opcode::Dw => {
args = self.parse_data_definition(opcode)?;
}
// E-type pseudoinstructions (stack operations)
Opcode::Push | Opcode::Pop => {
let reg = expect_type!(self.next()?, Register, Symbol)?;
args = vec![reg];
}
// Special instructions
Opcode::Int => {
let val = expect_type!(self.next()?, Immediate)?;
args = vec![val];
}
// Instructions with no arguments
Opcode::Hlt
| Opcode::Nop
| Opcode::Ret
| Opcode::IRet
| Opcode::Return
| Opcode::Func => {
args = vec![];
}
Opcode::Data | Opcode::Segment => {
return Err(AssembleError::Generic);
}
}
Ok(node!(label, opcode, args: args))
}
fn parse_data_definition(&mut self, opcode: Opcode) -> Result<Vec<Token>, AssembleError> {
let mut values = Vec::new();
let name = expect_type!(self.next()?, Symbol)?;
values.push(name);
match opcode {
Opcode::Db => {
// db can take string literals or u8 immediates
while !self.tokens.is_empty() {
let token = self
.tokens
.last()
.expect("Expected a token for data definition, but found none");
match token {
Token::StringLit(_) => {
values.push(
self.tokens
.pop()
.expect("Expected a token for data definition, but found none"),
);
}
Token::Immediate(val) if u8::try_from(*val).is_ok() => {
values.push(
self.tokens
.pop()
.expect("Expected a token for data definition, but found none"),
);
}
_ => break,
}
}
}
Opcode::Dh => {
// dh can take u16 immediates
while !self.tokens.is_empty() {
let token = self
.tokens
.last()
.expect("Expected a token for data definition, but found none");
match token {
Token::StringLit(_) => {
values.push(
self.tokens
.pop()
.expect("Expected a token for data definition, but found none"),
);
}
Token::Immediate(val) if u16::try_from(*val).is_ok() => {
values.push(
self.tokens
.pop()
.expect("Expected a token for data definition, but found none"),
);
}
_ => break,
}
}
}
Opcode::Dw => {
// dw can take u32 immediates
while !self.tokens.is_empty() {
match self
.tokens
.last()
.expect("Expected a token for data definition, but found none")
{
Token::StringLit(_) => {
values.push(
self.tokens
.pop()
.expect("Expected a token for data definition, but found none"),
);
}
Token::Immediate(val) => {
values.push(
self.tokens
.pop()
.expect("Expected a token for data definition, but found none"),
);
}
_ => break,
}
}
}
_ => unreachable!(),
}
Ok(values)
}
fn next(&mut self) -> Result<Token, AssembleError> {
if self.tokens.is_empty() {
Err(AssembleError::UnexpectedEof)
} else {
Ok(self
.tokens
.pop()
.expect("tokens vector was unexpectedly empty in next()"))
}
}
fn peek_next(&self) -> Result<Token, AssembleError> {
if self.tokens.is_empty() {
Err(AssembleError::UnexpectedEof)
} else {
Ok(self
.tokens
.last()
.expect("peek_next called on empty tokens vector")
.clone())
}
}
}
+157
View File
@@ -0,0 +1,157 @@
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::AssembleError, node};
pub fn resolve_symbols(nodes: &mut [Node]) -> Result<(), AssembleError> {
let symbol_table = generate_symbol_table(nodes);
for node in nodes.iter_mut() {
match node.opcode() {
Opcode::Jmp | Opcode::Call | Opcode::Jic | Opcode::Jnc | Opcode::Lli | Opcode::Lui => {
if let Token::Symbol(symbol) = node
.arg(0)
.expect("Expected argument 0 for jump-like opcode")
{
if let Some(address) = symbol_table.get(&symbol) {
node.tokens[0] = Token::Immediate(*address);
} else {
return Err(AssembleError::UndefinedSymbol(symbol));
}
}
}
Opcode::Jez | Opcode::Jnz => {
if let Token::Symbol(symbol) = node
.arg(1)
.expect("Expected argument 0 for jump-like opcode")
{
if let Some(address) = symbol_table.get(&symbol) {
node.tokens[1] = Token::Immediate(*address);
} else {
return Err(AssembleError::UndefinedSymbol(symbol));
}
}
}
_ => (),
}
}
Ok(())
}
fn generate_symbol_table(nodes: &[Node]) -> HashMap<Symbol, u32> {
let mut table = HashMap::new();
for (i, node) in nodes.iter().enumerate() {
if let Some(symbol) = node.label() {
table.insert(symbol, 4 * i as u32);
}
}
table
}
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 {
if node.opcode() == Opcode::Include {
// we want the path, and the name
let name = if let Token::Symbol(name) = node
.arg(0)
.expect("Expected argument #0 for Include directive.")
{
name.name.clone()
} else {
unreachable!()
}; //node.2.get(0).unwrap()
let Ok(Token::StringLit(path)) = node.arg(1) else {
unreachable!()
};
let full_path = base_dir.join(path);
let canonical_path = full_path
.canonicalize()
.map_err(|_| AssembleError::InvalidFile(full_path.clone()))?;
let hash = quick_hash(&canonical_path);
dependencies.push((name, hash));
}
}
let mut changes = Vec::<(u32, u32, Symbol)>::new();
// now we resolve the symbols on all the nodes
// we need to check all operands for unresolved signals
for (i, node) in nodes.clone().iter().enumerate() {
let Node {
tokens: operands, ..
} = node;
for (j, token) in operands.iter().enumerate() {
if let Token::Symbol(symbol) = token {
for d in &dependencies {
if let Module::Unresolved(name) = symbol.module.clone() {
if name != d.0 {
continue;
}
let symbol = Symbol {
name: symbol.name.clone(),
module: Module::Resolved(d.1),
};
changes.push((i as u32, j as u32, symbol));
}
}
}
}
}
for (i, j, symbol) in changes {
nodes[i as usize].tokens[j as usize] = Token::Symbol(symbol);
}
Ok(nodes)
}
pub fn create_sections(nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
let mut res = Vec::<Node>::with_capacity(nodes.len());
res.push(node!(None, Opcode::Segment, Token::Immediate(0)));
for n in nodes.iter() {
if n.opcode() == Opcode::Data {
res.push(n.clone());
}
}
let start = res.len() + 2;
res.insert(
0,
node!(
None,
Opcode::Jmp,
Token::Immediate(start as u32 * 4),
Token::Register(Register::Zero)
),
);
for n in nodes.iter() {
if !matches!(n.opcode(), Opcode::Data | Opcode::Include) {
res.push(n.clone());
}
}
*nodes = res;
Ok(())
}
+16
View File
@@ -0,0 +1,16 @@
use super::Token;
#[derive(Debug, Clone)]
pub enum AssembleError {
UnexpectedToken { expected: String, got: Token },
UnexpectedEof,
}
impl AssembleError {
pub fn unexpected_token(token: Token, expected: &str) -> Self {
AssembleError::UnexpectedToken {
expected: expected.to_string(),
got: token,
}
}
}
+264
View File
@@ -0,0 +1,264 @@
use std::{str::FromStr, thread, time::Duration};
use common::{asm::AsmOpcode, prelude::Register};
use crate::assembler::{AssembleError, Module, Opcode, Symbol, Token, lexer::parse_opcode};
pub struct Lexer {
src: Vec<u8>,
module_id: u64,
}
impl Lexer {
pub fn new(src: String, module_id: u64) -> Self {
Self {
src: src.into_bytes().into_iter().rev().collect(),
module_id,
}
}
pub fn run(&mut self) -> Result<Vec<Token>, AssembleError> {
let mut tokens = Vec::new();
while !self.src.is_empty() {
// thread::sleep(Duration::from_millis(10));
// println!("{}", *self.src.last().unwrap() as char);
// println!("{:?}", tokens);
if self.src.ends_with(b"\n")
|| self.src.ends_with(b"\r")
|| self.src.ends_with(b" ")
|| self.src.ends_with(b",")
|| self.src.ends_with(b".")
|| self.src.ends_with(b":")
{
self.src.pop();
continue;
}
if self.src.ends_with(b"//") {
self.src.pop();
self.src.pop();
self.parse_comment();
continue;
}
if self.src.ends_with(b"*/") {
self.src.pop();
self.src.pop();
self.parse_comment_multiline();
continue;
}
if self.src.ends_with(b"\"") {
self.src.pop();
tokens.push(Token::StringLit(self.parse_string_literal()));
continue;
}
if self.src.ends_with(b"'") {
self.src.pop();
tokens.push(Token::CharLit(self.parse_char_literal()));
continue;
}
if self.src.last().unwrap().is_ascii_digit() {
tokens.push(Token::Immediate(self.parse_number()));
continue;
}
if matches!(self.src.last().unwrap(), b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_') {
let mut buffer = String::new();
while matches!(self.src.last().unwrap(), b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_')
{
buffer.push(self.src.pop().unwrap() as char);
}
if let Ok(opcode) = Opcode::from_str(&buffer) {
tokens.push(Token::Opcode(opcode));
continue;
}
if let Ok(register) = Register::from_str(&buffer) {
tokens.push(Token::Register(register));
continue;
}
// Check for qualified symbol: identifier::identifier
if self.src.ends_with(b"::") {
self.src.pop();
self.src.pop();
let mut rhs = String::new();
while matches!(
self.src.last(),
Some(b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_')
) {
rhs.push(self.src.pop().unwrap() as char);
}
if rhs.is_empty() {
return Err(AssembleError::Generic);
}
tokens.push(Token::Symbol(Symbol {
name: rhs,
module: Module::Unresolved(buffer),
}));
continue;
}
tokens.push(Token::Symbol(Symbol {
name: buffer,
module: Module::Resolved(self.module_id),
}));
continue;
}
}
Ok(tokens)
}
fn parse_number(&mut self) -> u32 {
match self.src.last_chunk::<2>().map(|[x, y]| [*y, *x]).as_ref() {
Some(b"0x") => {
self.src.pop();
self.src.pop();
let mut n = 0u32;
while let Some(&b) = self.src.last() {
if let Some(digit) = (b as char).to_digit(16) {
self.src.pop();
n = n * 16 + digit;
} else {
break;
}
}
n
}
Some(b"0b") => {
self.src.pop();
self.src.pop();
let mut n = 0u32;
while let Some(&b) = self.src.last() {
if b == b'0' || b == b'1' {
self.src.pop();
n = n * 2 + (b - b'0') as u32;
} else {
break;
}
}
n
}
Some(b"0o") => {
self.src.pop();
self.src.pop();
let mut n = 0u32;
while let Some(&b) = self.src.last() {
if matches!(b, b'0'..=b'7') {
self.src.pop();
n = n * 8 + (b - b'0') as u32;
} else {
break;
}
}
n
}
// decimal number
_ if let Some(x) = self.src.last()
&& x.is_ascii_digit() =>
{
let mut n = 0u32;
while let Some(&b) = self.src.last() {
if matches!(b, b'0'..=b'9') {
self.src.pop();
n = n * 10 + (b - b'0') as u32;
} else {
break;
}
}
n
}
_ => panic!("Invalid syntax"),
}
}
// returns nothing as the assembler discards comments
fn parse_comment(&mut self) {
while !self.src.is_empty() && !self.src.ends_with(b"\n") {
self.src.pop();
}
}
fn parse_comment_multiline(&mut self) {
while !self.src.is_empty() && !self.src.ends_with(b"*/") {
self.src.pop();
}
}
fn parse_string_literal(&mut self) -> String {
let mut result = String::new();
while self.src.last().is_some() && !self.src.ends_with(b"\"") {
let ch = self.src.pop().unwrap();
if ch == b'\\' {
let escaped = self
.src
.pop()
.expect("A file should never end with a backslash!");
result.push(match escaped {
b'n' => '\n',
b't' => '\t',
b'r' => '\r',
b'"' => '\"',
b'\\' => '\\',
_ => escaped as char,
});
continue;
}
result.push(ch as char);
}
self.src.pop().unwrap();
result
}
fn parse_char_literal(&mut self) -> char {
let ch = self
.src
.pop()
.expect("Unexpected EOF while parsing char literal");
if ch == b'\\' {
let escaped = self
.src
.pop()
.expect("A file should never end with a backslash!");
assert!(
self.src
.pop()
.expect("Unexpected EOF while parsing char literal")
!= b'\'',
"unterminated char literal"
);
match escaped {
b'n' => '\n',
b't' => '\t',
b'r' => '\r',
b'"' => '\"',
b'\\' => '\\',
_ => escaped as char,
}
} else {
assert!(
self.src
.pop()
.expect("Unexpected EOF while parsing char literal")
!= b'\'',
"unterminated char literal"
);
ch as char
}
}
}
+46
View File
@@ -0,0 +1,46 @@
// mod error;
pub mod lexer;
// pub mod parser;
use core::fmt;
use crate::assembler::{AssembleError, Symbol};
// use common::{asm::AsmOpcode, prelude::Register};
use lexer::Lexer;
// use parser::Parser;
pub fn asm(input: &str) -> Result<Vec<u8>, AssembleError> {
let mut lexer = Lexer::new(input.to_string(), 0);
let tokens = lexer.run()?;
// let ast = Parser::new(tokens).parse().unwrap();
// println!("{:#?}", ast);
// let ast = parser::parse(tokens)?;
Ok(vec![])
}
// #[derive(Debug, Clone)]
// pub enum Token {
// Symbol(Symbol),
// Register(Register),
// Immediate(u32),
// StringLit(String),
// CharLit(char),
// Opcode(AsmOpcode),
// }
// impl fmt::Display for Token {
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// match self {
// Self::Symbol(symbol) => write!(f, "{symbol}"),
// Self::Register(register) => write!(f, "{register}",),
// Self::Immediate(immediate) => write!(f, "{immediate}",),
// Self::StringLit(string_lit) => write!(f, "{string_lit}",),
// Self::CharLit(char_lit) => write!(f, "{char_lit}",),
// Self::Opcode(opcode) => write!(f, "{opcode}",),
// }
// }
// }
+130
View File
@@ -0,0 +1,130 @@
use std::path::PathBuf;
use common::asm::{AsmInstruction, AsmOpcode};
use crate::assembler::Symbol;
use crate::{expect_tt, expect_value};
use super::{Token, error::AssembleError};
pub struct AsmNode {
pub label: Option<Symbol>,
pub instruction: Option<AsmInstruction>,
}
pub struct Parser {
tokens: Vec<Token>,
nodes: Option<Vec<AsmInstruction>>,
dependencies: Vec<PathBuf>,
}
impl Parser {
pub fn new(tokens: Vec<Token>) -> Self {
Parser {
tokens,
nodes: Some(vec![]),
dependencies: vec![],
}
}
pub fn parse(&mut self) -> Result<Vec<AsmInstruction>, AssembleError> {
Ok(vec![])
}
fn parse_instruction(&mut self) -> Result<AsmNode, AssembleError> {
let label = expect_value!(self.peek_next()?, Symbol).ok();
if label.is_some() {
self.next()?;
}
let opcode = expect_value!(self.next()?, Opcode)?;
let ins = match opcode {
AsmOpcode::Mov => {
let src = expect_value!(self.next()?, Register)?;
let dest = expect_value!(self.next()?, Register)?;
AsmInstruction::Mov { src, dest }
}
AsmOpcode::CMov => {
let src = expect_value!(self.next()?, Register)?;
let dest = expect_value!(self.next()?, Register)?;
let condition = expect_value!(self.next()?, Register)?;
AsmInstruction::CMov {
src,
dest,
condition,
}
}
// AsmOpcode::Ldb => {
// let src = expect_value!(self.next()?, Register)?;
// let dest = expect_value!(self.next()?, Register)?;
// AsmInstruction::Ldb { src, dest }
// }
_ => {
todo!()
}
};
Ok(AsmNode {
label,
instruction: Some(ins),
})
}
fn next(&mut self) -> Result<Token, AssembleError> {
if self.tokens.is_empty() {
Err(AssembleError::UnexpectedEof)
} else {
Ok(self
.tokens
.pop()
.expect("tokens vector was unexpectedly empty in next()"))
}
}
fn peek_next(&self) -> Result<Token, AssembleError> {
if self.tokens.is_empty() {
Err(AssembleError::UnexpectedEof)
} else {
Ok(self
.tokens
.last()
.expect("peek_next called on empty tokens vector")
.clone())
}
}
}
#[macro_export]
macro_rules! expect_tt {
($token:expr, $($variant:ident),+) => {{
let tok = $token;
let tt = token.tt().to_string();
match tt.as_str() {
$(
stringify!($variant) => Ok(token),
)+
_ => {
// let expected = format!("[{}]", vec![$(stringify!($variant)),+].join(" | "));
Err(AssembleError::unexpected_token(
tok,
format!("[{}]", vec![$(stringify!($variant)),+].join(" | ")).as_str())
)
}
}
}};
}
#[macro_export]
macro_rules! expect_value {
($token:expr, $variant:ident) => {{
let tok = $token;
match tok.clone() {
Token::$variant(first, ..) => Ok(first),
_ => Err(AssembleError::unexpected_token(tok, stringify!($variant))),
}
}};
}
+21
View File
@@ -0,0 +1,21 @@
#![deny(
clippy::unwrap_used,
clippy::nursery,
clippy::perf,
clippy::pedantic,
clippy::complexity
)]
#![allow(
clippy::cast_possible_truncation,
clippy::missing_panics_doc,
clippy::missing_errors_doc,
clippy::match_wildcard_for_single_variants
)]
pub mod assembler;
pub mod assemblerv2;
// mod util;
pub mod prelude {
pub use crate::assembler::Assembler;
}
+33
View File
@@ -0,0 +1,33 @@
use assembler::prelude::*;
use clap::{Parser, arg, command};
use std::{fs, path::PathBuf};
#[derive(Parser, Debug, Clone)]
#[command(version, about, long_about = None)]
struct Args {
#[arg(short = 'i')]
pub input_path: PathBuf,
#[arg(short = 'o')]
pub output_path: PathBuf,
}
fn main() {
// Parse command line arguments
let args = Args::parse();
let mut engine = Assembler::new(PathBuf::from(args.input_path));
engine.start(());
let result = engine.output().expect("assembler failed.");
if let Err(e) = fs::write(args.output_path, result) {
eprintln!("Failed to write to output file: {e}");
std::process::exit(1);
}
// let input = fs::read_to_string("../../dsa_resources/framebuffer.dsa").unwrap();
// let output = asm(&input).unwrap();
// for token in output {
// println!("{:?}", token);
// }
}