deimos-lang/src/vm/module.rs

178 lines
4.7 KiB
Rust

use crate::get_32_le;
use std::collections::HashMap;
use std::io::Read;
pub const DEIMOS_MAGIC_NUMBER: u64 = 0x00_00_64_65_69_6d_6f_73; // ascii 'deimos'
const DEIMOS_MAGIC_STRING: [u8; 6] = [0x64, 0x65, 0x69, 0x6d, 0x6f, 0x73]; // ascii 'deimos'
pub const VERSION_STRING: &str = "0.1.0";
pub struct DmModule {
name: String,
version: String,
constants: HashMap<String, DmConstant>,
functions: HashMap<String, DmFunction>,
}
pub enum DmConstant {
Int(i32),
Long(i64),
Double(f64),
String(String),
}
pub struct DmFunction {
pub name: String,
pub byte_code: Vec<u8>,
pub num_registers: usize,
}
const CONST_SYMBOL: u8 = 0x01;
const FUNCTION_SYMBOL: u8 = 0x02;
enum SymbolType {
Constant,
Function,
}
struct DmSymbol {
name: String,
symbol_type: SymbolType,
address: u32,
}
macro_rules! push_byte_array {
( $dest: expr, $arr: expr ) => {
for b in $arr {
$dest.push(b);
}
};
}
macro_rules! push_number_le {
( $dest: expr, $num: expr ) => {
for b in $num.to_le_bytes() {
$dest.push(b);
}
};
}
macro_rules! push_string {
( $dest: expr, $s: expr ) => {
for b in $s.len().to_le_bytes() {
$dest.push(b);
}
for b in $s.bytes() {
$dest.push(b);
}
};
}
pub fn write_module(module: DmModule) -> Vec<u8> {
// Push magic number
let mut result: Vec<u8> = Vec::new();
push_byte_array!(result, DEIMOS_MAGIC_STRING);
// Push version string
push_string!(result, module.version);
// Push module name length, little endian
push_string!(result, module.name);
result
}
pub fn load_module(bytes: &[u8]) -> Result<DmModule, String> {
let mut ip: usize = 0;
// Check for magic number at bytes 0..5
if !check_deimos(&bytes) {
return Err(String::from("Not a valid Deimos module."));
}
ip = 6;
// Get version string length from bytes 6..9
let version_string_length = get_32_le!(bytes, 6, 0, usize);
ip = 10;
// Bytes 10..(10 + version_string_length) are the version string, in utf8
let mut version_string_raw: Vec<u8> = Vec::with_capacity(version_string_length);
let version_string_end = ip + version_string_length;
while ip < version_string_end {
version_string_raw.push(bytes[ip]);
ip += 1;
}
let version_string = String::from_utf8(version_string_raw).unwrap();
// Check version string. We'll use this in the future to not load modules compiled later than
// current version.
if version_string != "0.1.0" {
return Err(String::from("Invalid Deimos module version."));
}
// TODO: extract module name
// Holder for Symbols we will extract from the symbol table bytes
let mut symbols: Vec<DmSymbol> = Vec::new();
// Get the symbol table length and calculate how far we need to read
let symbol_table_length = get_32_le!(bytes, 10 + version_string_length, 0, usize);
let symbol_table_end = ip + symbol_table_length;
// For each "row" in the symbol table,
// 1. Get the type
// 2. Obtain the name's length and then get the name in utf8
// 3. Grab the address to the actual symbol in the module bytes
while ip < symbol_table_end {
let type_byte = bytes[ip];
ip += 1;
let name_string_length = get_32_le!(bytes, ip, 0, usize);
ip += 4;
let name_string_raw = bytes[ip..ip + name_string_length].to_vec();
let name = String::from_utf8(name_string_raw).unwrap();
ip += name_string_length;
let address = get_32_le!(bytes, ip, 0, u32);
ip += 4;
let symbol_type = match type_byte {
CONST_SYMBOL => SymbolType::Constant,
FUNCTION_SYMBOL => SymbolType::Function,
_ => return Err(String::from("Invalid Deimos symbol type.")),
};
symbols.push(DmSymbol {
name,
address,
symbol_type,
});
}
todo!()
}
fn check_deimos(bytes: &[u8]) -> bool {
if bytes.len() < 6 {
return false;
}
let first_six = read_as_u64(&bytes[0..6]);
first_six == DEIMOS_MAGIC_NUMBER
}
fn read_as_u64(bytes: &[u8]) -> u64 {
let mut result = 0u64;
let max_shift = (bytes.len() - 1) * 8;
for i in 0..bytes.len() {
result |= (bytes[i] as u64) << (max_shift - i * 8);
}
result
}
#[cfg(test)]
mod read_as_u64_tests {
use crate::vm::module::{read_as_u64, DEIMOS_MAGIC_NUMBER};
#[test]
fn read_6_bytes() {
let bytes = vec![0x64u8, 0x65u8, 0x69u8, 0x6du8, 0x6fu8, 0x73u8]; // ascii 'deimos'
let result = read_as_u64(&bytes);
assert_eq!(DEIMOS_MAGIC_NUMBER, result);
}
}