Compare commits
No commits in common. "5732c4d197741a52b5eaf5df3d42925ea817b3d5" and "e4ee8fd2db29157f4a7d88611c968da74e34ed59" have entirely different histories.
5732c4d197
...
e4ee8fd2db
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -1,7 +1,7 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deimos"
|
name = "deimos-lang"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
10
Cargo.toml
10
Cargo.toml
@ -1,14 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "deimos"
|
name = "deimos-lang"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "dmc"
|
|
||||||
path = "src/bin/compiler/main.rs"
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "dm"
|
|
||||||
path = "src/bin/dvm/main.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
use deimos::lexer::tokenize;
|
|
||||||
use deimos::parser::parse;
|
|
||||||
use std::process::exit;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let src = String::from("print 42");
|
|
||||||
let tokenize_result = tokenize(&src);
|
|
||||||
if let Err(e) = tokenize_result {
|
|
||||||
eprintln!("{}", e);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
let tokens = tokenize_result.unwrap();
|
|
||||||
println!("{:?}", tokens);
|
|
||||||
let parse_result = parse(&tokens);
|
|
||||||
if let Err(e) = parse_result {
|
|
||||||
eprintln!("{}", e);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
let compilation_unit = parse_result.unwrap();
|
|
||||||
println!("{:?}", compilation_unit);
|
|
||||||
// TODO: compilation_unit to DmModule
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
use deimos::vm::op_codes::{add_mov_int, add_platform_call_to};
|
|
||||||
use deimos::vm::DmVirtualMachine;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let mut code: Vec<u8> = Vec::new();
|
|
||||||
add_mov_int(&mut code, 0, 42);
|
|
||||||
add_platform_call_to(&mut code, &String::from("std::core::print"), 0, 1, &vec![0u8]);
|
|
||||||
let mut vm = DmVirtualMachine::new();
|
|
||||||
vm.run(&mut code);
|
|
||||||
println!()
|
|
||||||
}
|
|
@ -29,10 +29,9 @@ pub enum Token {
|
|||||||
Dot,
|
Dot,
|
||||||
Ellipsis,
|
Ellipsis,
|
||||||
Abstract,
|
Abstract,
|
||||||
NumberLiteral(String),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tokenize(input: &String) -> Result<Vec<Token>, String> {
|
pub fn tokenize(input: &String) -> Result<Vec<Token>, &'static str> {
|
||||||
let mut tokens: Vec<Token> = Vec::new();
|
let mut tokens: Vec<Token> = Vec::new();
|
||||||
let mut peekable = input.chars().peekable();
|
let mut peekable = input.chars().peekable();
|
||||||
while let Some(c) = peekable.next() {
|
while let Some(c) = peekable.next() {
|
||||||
@ -73,28 +72,12 @@ pub fn tokenize(input: &String) -> Result<Vec<Token>, String> {
|
|||||||
match count {
|
match count {
|
||||||
1 => tokens.push(Token::Dot),
|
1 => tokens.push(Token::Dot),
|
||||||
3 => tokens.push(Token::Ellipsis),
|
3 => tokens.push(Token::Ellipsis),
|
||||||
_ => return Err(String::from("Unexpected number of tokens after '.'")),
|
_ => return Err("Unexpected number of tokens after '.'"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'0'..='9' => {
|
|
||||||
let mut buffer = String::new();
|
|
||||||
buffer.push(c);
|
|
||||||
while let Some(num_char) = peekable.next_if(|c| {
|
|
||||||
c.is_digit(10)
|
|
||||||
|| match c {
|
|
||||||
'_' | 'x' | 'L' | 'd' => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
buffer.push(num_char);
|
|
||||||
}
|
|
||||||
tokens.push(Token::NumberLiteral(buffer));
|
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
if let Some(token) = match_identifier_or_keyword(c, &mut peekable) {
|
if let Some(token) = match_identifier_or_keyword(c, &mut peekable) {
|
||||||
tokens.push(token);
|
tokens.push(token);
|
||||||
} else {
|
|
||||||
return Err(String::from(format!("Unexpected token: {}", c)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -245,10 +228,4 @@ mod tests {
|
|||||||
assert_eq!(Token::Identifier(String::from("props")), result[2]);
|
assert_eq!(Token::Identifier(String::from("props")), result[2]);
|
||||||
assert_eq!(Token::CurlyClose, result[3]);
|
assert_eq!(Token::CurlyClose, result[3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn simple_number() {
|
|
||||||
let result = tokenize(&String::from("123456")).unwrap();
|
|
||||||
assert_eq!(Token::NumberLiteral(String::from("123456")), result[0]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
pub mod lexer;
|
mod lexer;
|
||||||
pub mod parser;
|
mod vm;
|
||||||
pub mod vm;
|
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
mod types;
|
|
||||||
|
|
||||||
use crate::lexer::Token;
|
|
||||||
use crate::parser::types::AstNode;
|
|
||||||
|
|
||||||
pub fn parse(tokens: &Vec<Token>) -> Result<AstNode, String> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
pub type NodeChildren = Vec<Box<AstNode>>;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum AstNode {
|
|
||||||
CompilationUnit(NodeChildren),
|
|
||||||
BlockStatement(NodeChildren),
|
|
||||||
Statement(NodeChildren),
|
|
||||||
Expression(NodeChildren),
|
|
||||||
}
|
|
379
src/vm/mod.rs
379
src/vm/mod.rs
@ -1,314 +1,177 @@
|
|||||||
pub mod module;
|
mod op_codes;
|
||||||
pub mod op_codes;
|
|
||||||
pub mod platform;
|
|
||||||
pub mod types;
|
|
||||||
mod util;
|
|
||||||
|
|
||||||
use crate::vm::module::DmFunction;
|
|
||||||
use crate::vm::platform::init_platform_functions;
|
|
||||||
use op_codes::*;
|
use op_codes::*;
|
||||||
use std::alloc::{alloc_zeroed, dealloc, Layout};
|
use std::alloc::{alloc_zeroed, dealloc, Layout};
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::ops::Index;
|
|
||||||
use types::DmType;
|
|
||||||
use util::{get_32_le, get_64_le};
|
|
||||||
use crate::vm::DmValue::{DmInt, DmLong, DmPointer, DmUnit};
|
|
||||||
|
|
||||||
pub type PlatformFunction = fn(args: Vec<DmValue>, &mut DmVirtualMachine) -> DmValue;
|
struct DmObject {
|
||||||
|
pointer: *mut u8,
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
||||||
pub enum DmValue {
|
|
||||||
DmInt(i32),
|
|
||||||
DmLong(i64),
|
|
||||||
DmDouble(f64),
|
|
||||||
DmPointer(*mut DmObject),
|
|
||||||
DmUnit,
|
|
||||||
DmException(*mut DmObject)
|
|
||||||
}
|
|
||||||
|
|
||||||
enum CallFrame {
|
|
||||||
PlatformCall(PlatformCallFrame),
|
|
||||||
DeimosCall(DeimosCallFrame)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PlatformCallFrame {
|
|
||||||
pub name: String,
|
|
||||||
pub args: Vec<u64>,
|
|
||||||
pub arg_types: Vec<DmType>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DeimosCallFrame {
|
|
||||||
return_address: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DmVirtualMachine {
|
|
||||||
platform_functions: HashMap<String, PlatformFunction>,
|
|
||||||
ip: usize,
|
|
||||||
call_stack: Vec<CallFrame>,
|
|
||||||
registers: Vec<DmValue>,
|
|
||||||
register_state_stack: Vec<Vec<DmValue>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DmVirtualMachine {
|
|
||||||
pub fn new() -> DmVirtualMachine {
|
|
||||||
DmVirtualMachine {
|
|
||||||
ip: 0,
|
|
||||||
registers: Vec::new(),
|
|
||||||
platform_functions: init_platform_functions(),
|
|
||||||
call_stack: Vec::new(),
|
|
||||||
register_state_stack: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn call(
|
|
||||||
&mut self,
|
|
||||||
dm_function: &DmFunction,
|
|
||||||
args: Vec<DmValue>,
|
|
||||||
) -> DmValue {
|
|
||||||
// save current state
|
|
||||||
self.call_stack.push(CallFrame::DeimosCall(DeimosCallFrame {
|
|
||||||
return_address: self.ip,
|
|
||||||
}));
|
|
||||||
self.register_state_stack.push(self.registers.clone());
|
|
||||||
|
|
||||||
// zero registers and make sure there are enough for dm_function
|
|
||||||
self.registers.clear();
|
|
||||||
self.registers.resize(args.len(), DmValue::DmUnit);
|
|
||||||
|
|
||||||
// push args
|
|
||||||
for i in 0..args.len() {
|
|
||||||
self.registers.insert(i, args[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// run the byte code
|
|
||||||
self.run(&dm_function.byte_code);
|
|
||||||
|
|
||||||
// restore state
|
|
||||||
self.registers = self.register_state_stack.pop().unwrap();
|
|
||||||
if let CallFrame::DeimosCall(deimos_call_frame) = self.call_stack.pop().unwrap() {
|
|
||||||
self.ip = deimos_call_frame.return_address;
|
|
||||||
}
|
|
||||||
|
|
||||||
// return result
|
|
||||||
self.registers.get(0).unwrap().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(&mut self, code: &Vec<u8>) {
|
|
||||||
let mut i = 0;
|
|
||||||
while i < code.len() {
|
|
||||||
match code[i] {
|
|
||||||
MOV_INT => {
|
|
||||||
let target_register = code[i + 1] as usize;
|
|
||||||
let operand = get_32_le!(code, i, 2, u32);
|
|
||||||
self.registers.insert(target_register, DmInt(operand as i32));
|
|
||||||
i += 6;
|
|
||||||
}
|
|
||||||
MOV_LONG => {
|
|
||||||
let target_register = code[i + 1] as usize;
|
|
||||||
let operand = get_64_le!(code, i, 2, u64);
|
|
||||||
self.registers.insert(target_register, DmLong(operand as i64));
|
|
||||||
i += 10;
|
|
||||||
}
|
|
||||||
MOV_DOUBLE => { /* todo */ }
|
|
||||||
MOV_REGISTER => {
|
|
||||||
let target_register = code[i + 1] as usize;
|
|
||||||
let source_register = code[i + 2] as usize;
|
|
||||||
let source_value = self.registers.get(source_register).unwrap();
|
|
||||||
self.registers.insert(target_register, source_value.clone());
|
|
||||||
i += 3;
|
|
||||||
}
|
|
||||||
ALLOC => {
|
|
||||||
let target_register = code[i + 1] as usize;
|
|
||||||
let size = get_32_le!(code, i, 2, usize);
|
|
||||||
let layout = Layout::from_size_align(size, 4).unwrap();
|
|
||||||
let pointer = unsafe { alloc_zeroed(layout) };
|
|
||||||
let dm_object = Box::new(DmObject {
|
|
||||||
data: pointer,
|
|
||||||
size,
|
|
||||||
layout,
|
|
||||||
object_type: todo!(),
|
|
||||||
});
|
|
||||||
let dm_object_pointer = Box::into_raw(dm_object);
|
|
||||||
self.registers.insert(target_register, DmPointer(dm_object_pointer));
|
|
||||||
i += 6;
|
|
||||||
}
|
|
||||||
DEALLOC => {
|
|
||||||
let target_register = code[i + 1] as usize;
|
|
||||||
let target_value = self.registers.get(target_register).unwrap();
|
|
||||||
match target_value {
|
|
||||||
DmPointer(ptr) => unsafe {
|
|
||||||
let dm_object = Box::from_raw(ptr.clone());
|
|
||||||
dealloc(dm_object.data, dm_object.layout);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
panic!("Attempt to deallocate at the address stored in a register that is not a pointer.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.registers.insert(target_register, DmUnit);
|
|
||||||
i += 2;
|
|
||||||
}
|
|
||||||
MOV_INT_TO => {
|
|
||||||
let target_register = code[i + 1] as usize;
|
|
||||||
let target_value = self.registers.get(target_register).unwrap();
|
|
||||||
if let DmPointer(dm_object_ptr) = target_value {
|
|
||||||
let offset = get_32_le!(code, i, 2, isize);
|
|
||||||
let new_address = unsafe {
|
|
||||||
let dm_object = Box::from_raw(dm_object_ptr.clone());
|
|
||||||
let data_ptr = dm_object.data;
|
|
||||||
data_ptr.offset(offset).write(code[i + 6]);
|
|
||||||
data_ptr.offset(offset + 1).write(code[i + 7]);
|
|
||||||
data_ptr.offset(offset + 2).write(code[i + 8]);
|
|
||||||
data_ptr.offset(offset + 3).write(code[i + 9]);
|
|
||||||
Box::into_raw(dm_object)
|
|
||||||
};
|
|
||||||
self.registers.insert(target_register, DmPointer(new_address));
|
|
||||||
i += 10;
|
|
||||||
} else {
|
|
||||||
panic!("target_register {} is not a Pointer", target_register);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PLATFORM_CALL => {
|
|
||||||
i += 1;
|
|
||||||
|
|
||||||
let symbol_name_length = get_32_le!(code, i, 0, usize);
|
|
||||||
i += 4;
|
|
||||||
|
|
||||||
let symbol_name_raw = code[i..(i + symbol_name_length)].to_vec();
|
|
||||||
i += symbol_name_length;
|
|
||||||
|
|
||||||
let symbol_name = String::from_utf8(symbol_name_raw).unwrap();
|
|
||||||
|
|
||||||
let return_register = code[i + 1] as usize;
|
|
||||||
i += 1;
|
|
||||||
|
|
||||||
let arg_registers_length = code[i] as usize;
|
|
||||||
i += 1;
|
|
||||||
|
|
||||||
let arg_registers = code[i..(i + arg_registers_length)].to_vec();
|
|
||||||
i += arg_registers_length;
|
|
||||||
|
|
||||||
let mut args = Vec::new();
|
|
||||||
for arg_register in arg_registers {
|
|
||||||
let register_value = self.registers.get(arg_register as usize).unwrap();
|
|
||||||
args.push(register_value.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
let platform_function_result = self.platform_functions.get(&symbol_name);
|
|
||||||
if platform_function_result.is_none() {
|
|
||||||
panic!("Unknown platform function {}", symbol_name);
|
|
||||||
}
|
|
||||||
let platform_function = platform_function_result.unwrap();
|
|
||||||
|
|
||||||
let call_result = platform_function(args, self);
|
|
||||||
self.registers.insert(return_register, call_result);
|
|
||||||
}
|
|
||||||
_ => panic!("Invalid code instruction"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DmObjectType {
|
|
||||||
name: String,
|
|
||||||
properties: HashMap<String, DmObjectProperty>,
|
|
||||||
fields: HashMap<String, DmObjectField>,
|
|
||||||
methods: HashMap<String, DmFunction>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DmObject {
|
|
||||||
object_type: Box<DmObjectType>,
|
|
||||||
data: *mut u8,
|
|
||||||
size: usize,
|
size: usize,
|
||||||
layout: Layout,
|
layout: Layout,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DmObjectProperty {
|
pub fn run(code: &Vec<u8>, registers: &mut Vec<u64>, register_types: &mut Vec<RegisterType>) {
|
||||||
name: String,
|
let mut i = 0;
|
||||||
property_type: DmType,
|
while i < code.len() {
|
||||||
|
match code[i] {
|
||||||
|
MOV_INT => {
|
||||||
|
let target_register = code[i + 1] as usize;
|
||||||
|
let operand: u32 = code[i + 2] as u32
|
||||||
|
+ ((code[i + 3] as u32) << 8)
|
||||||
|
+ ((code[i + 4] as u32) << 16)
|
||||||
|
+ ((code[i + 5] as u32) << 24);
|
||||||
|
registers[target_register] = operand as u64;
|
||||||
|
register_types[target_register] = RegisterType::Int;
|
||||||
|
i += 6
|
||||||
|
}
|
||||||
|
MOV_LONG => {
|
||||||
|
let target_register = code[i + 1] as usize;
|
||||||
|
let operand: u64 = code[i + 2] as u64
|
||||||
|
+ ((code[i + 3] as u64) << 8)
|
||||||
|
+ ((code[i + 4] as u64) << 16)
|
||||||
|
+ ((code[i + 5] as u64) << 24)
|
||||||
|
+ ((code[i + 6] as u64) << 32)
|
||||||
|
+ ((code[i + 7] as u64) << 40)
|
||||||
|
+ ((code[i + 8] as u64) << 48)
|
||||||
|
+ ((code[i + 9] as u64) << 56);
|
||||||
|
registers[target_register] = operand;
|
||||||
|
register_types[target_register] = RegisterType::Long;
|
||||||
|
i += 10
|
||||||
|
}
|
||||||
|
MOV_DOUBLE => { /* todo */ }
|
||||||
|
MOV_REGISTER => {
|
||||||
|
let target_register = code[i + 1] as usize;
|
||||||
|
let source_register = code[i + 2] as usize;
|
||||||
|
registers[target_register] = registers[source_register];
|
||||||
|
register_types[target_register] = register_types[source_register].clone();
|
||||||
|
i += 3;
|
||||||
|
}
|
||||||
|
ALLOC => {
|
||||||
|
let target_register = code[i + 1] as usize;
|
||||||
|
let size = code[i + 2] as usize
|
||||||
|
+ ((code[i + 3] as usize) << 8)
|
||||||
|
+ ((code[i + 4] as usize) << 16)
|
||||||
|
+ ((code[i + 5] as usize) << 24);
|
||||||
|
let layout = Layout::from_size_align(size, 4).unwrap();
|
||||||
|
let pointer = unsafe { alloc_zeroed(layout) };
|
||||||
|
let dm_object = Box::new(DmObject {
|
||||||
|
pointer,
|
||||||
|
size,
|
||||||
|
layout,
|
||||||
|
});
|
||||||
|
let dm_object_pointer = Box::into_raw(dm_object) as u64;
|
||||||
|
registers[target_register] = dm_object_pointer;
|
||||||
|
register_types[target_register] = RegisterType::Pointer;
|
||||||
|
i += 6;
|
||||||
|
}
|
||||||
|
DEALLOC => {
|
||||||
|
let target_register = code[i + 1] as usize;
|
||||||
|
let box_address = registers[target_register];
|
||||||
|
unsafe {
|
||||||
|
let dm_object = Box::from_raw(box_address as *mut DmObject);
|
||||||
|
dealloc(dm_object.pointer, dm_object.layout);
|
||||||
|
}
|
||||||
|
registers[target_register] = 0;
|
||||||
|
register_types[target_register] = RegisterType::Int;
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
MOV_INT_TO => {
|
||||||
|
let target_register = code[i + 1] as usize;
|
||||||
|
if register_types[target_register] != RegisterType::Pointer {
|
||||||
|
panic!("target_register {} is not a Pointer", target_register);
|
||||||
|
}
|
||||||
|
let offset = convert_to_u32(&code[(i + 2)..(i + 6)]) as isize;
|
||||||
|
let box_address = registers[target_register];
|
||||||
|
let new_address = unsafe {
|
||||||
|
let dm_object = Box::from_raw(box_address as *mut DmObject);
|
||||||
|
let pointer = dm_object.pointer;
|
||||||
|
pointer.offset(offset).write(code[i + 6]);
|
||||||
|
pointer.offset(offset + 1).write(code[i + 7]);
|
||||||
|
pointer.offset(offset + 2).write(code[i + 8]);
|
||||||
|
pointer.offset(offset + 3).write(code[i + 9]);
|
||||||
|
Box::into_raw(dm_object) as u64
|
||||||
|
};
|
||||||
|
registers[target_register] = new_address;
|
||||||
|
i += 10;
|
||||||
|
}
|
||||||
|
_ => panic!("Invalid code instruction"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DmObjectField {
|
fn convert_to_u32(bytes: &[u8]) -> u32 {
|
||||||
name: String,
|
bytes[0] as u32 | (bytes[1] as u32) << 8 | (bytes[2] as u32) << 16 | (bytes[3] as u32) << 24
|
||||||
field_type: DmType,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod dvm_run_tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::alloc::dealloc;
|
use std::alloc::dealloc;
|
||||||
|
|
||||||
macro_rules! assert_register {
|
fn init_registers(n_registers: usize) -> (Vec<u64>, Vec<RegisterType>) {
|
||||||
( $expected: expr, $register: expr ) => {
|
(vec![0; n_registers], vec![RegisterType::Int; n_registers])
|
||||||
assert_eq!($expected, $register.unwrap().clone());
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn mov_1_as_int() {
|
fn mov_1_as_int() {
|
||||||
let mut code = Vec::new();
|
let mut code = Vec::new();
|
||||||
add_mov_int(&mut code, 0, 1);
|
add_mov_int(&mut code, 0, 1);
|
||||||
let mut vm = DmVirtualMachine::new();
|
let (mut registers, mut register_types) = init_registers(1);
|
||||||
vm.run(&code);
|
run(&code, &mut registers, &mut register_types);
|
||||||
assert_register!(DmInt(1), vm.registers.get(0));
|
assert_eq!(1, registers[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn move_65535_as_int() {
|
fn move_65535_as_int() {
|
||||||
let mut code = Vec::new();
|
let mut code = Vec::new();
|
||||||
add_mov_int(&mut code, 0, 0xffff);
|
add_mov_int(&mut code, 0, 0xffff);
|
||||||
let mut vm = DmVirtualMachine::new();
|
let (mut registers, mut register_types) = init_registers(1);
|
||||||
vm.run(&code);
|
run(&code, &mut registers, &mut register_types);
|
||||||
assert_register!(DmInt(0xffff), vm.registers.get(0));
|
assert_eq!(0xffff, registers[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn move_int_max_as_int() {
|
fn move_int_max_as_int() {
|
||||||
let mut code = Vec::new();
|
let mut code = Vec::new();
|
||||||
add_mov_int(&mut code, 0, 0x0fff_ffff);
|
add_mov_int(&mut code, 0, 0xffff_ffff);
|
||||||
let mut vm = DmVirtualMachine::new();
|
let (mut registers, mut register_types) = init_registers(1);
|
||||||
vm.run(&code);
|
run(&code, &mut registers, &mut register_types);
|
||||||
assert_register!(DmInt(0x0fff_ffff), vm.registers.get(0));
|
assert_eq!(0xffff_ffff, registers[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn move_register() {
|
fn move_register() {
|
||||||
let mut code = Vec::new();
|
let mut code = Vec::new();
|
||||||
add_mov_int(&mut code, 1, 1);
|
|
||||||
add_mov_register(&mut code, 0, 1);
|
add_mov_register(&mut code, 0, 1);
|
||||||
let mut vm = DmVirtualMachine::new();
|
let (mut registers, mut register_types) = init_registers(2);
|
||||||
vm.registers.resize(2, DmUnit);
|
registers[1] = 1;
|
||||||
vm.run(&code);
|
run(&code, &mut registers, &mut register_types);
|
||||||
assert_register!(DmInt(1), vm.registers.get(0));
|
assert_eq!(registers[0], 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
|
||||||
fn mov_int_to_register_as_address() {
|
fn mov_int_to_register_as_address() {
|
||||||
let mut code = Vec::new();
|
let mut code = Vec::new();
|
||||||
add_alloc(&mut code, 0, 4);
|
add_alloc(&mut code, 0, 4);
|
||||||
add_mov_int_to(&mut code, 0, 0, 0xff);
|
add_mov_int_to(&mut code, 0, 0, 0xff);
|
||||||
let mut vm = DmVirtualMachine::new();
|
let mut registers = vec![0; 16];
|
||||||
vm.run(&code);
|
let mut register_types = vec![RegisterType::Int; 16];
|
||||||
let box_address = vm.registers.get(0).unwrap().clone();
|
run(&code, &mut registers, &mut register_types);
|
||||||
match box_address {
|
let box_address = registers[0];
|
||||||
DmPointer(ptr) => unsafe {
|
unsafe {
|
||||||
let dm_object = Box::from_raw(ptr);
|
let dm_object = Box::from_raw(box_address as *mut DmObject);
|
||||||
assert_eq!(0xff, *dm_object.data);
|
assert_eq!(0xff, *dm_object.pointer);
|
||||||
dealloc(dm_object.data, dm_object.layout);
|
dealloc(dm_object.pointer, dm_object.layout);
|
||||||
},
|
|
||||||
_ => panic!("Target register is not a pointer"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
|
||||||
fn alloc_and_dealloc_expect_register_cleared() {
|
fn alloc_and_dealloc_expect_register_cleared() {
|
||||||
let mut code = Vec::new();
|
let mut code = Vec::new();
|
||||||
add_alloc(&mut code, 0, 4);
|
add_alloc(&mut code, 0, 4);
|
||||||
add_dealloc(&mut code, 0);
|
add_dealloc(&mut code, 0);
|
||||||
let mut vm = DmVirtualMachine::new();
|
let (mut registers, mut register_types) = init_registers(1);
|
||||||
vm.run(&code);
|
run(&code, &mut registers, &mut register_types);
|
||||||
assert_register!(DmUnit, vm.registers.get(0));
|
assert_eq!(0, registers[0]);
|
||||||
|
assert_eq!(RegisterType::Int, register_types[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
177
src/vm/module.rs
177
src/vm/module.rs
@ -1,177 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,3 @@
|
|||||||
use crate::push_bytes;
|
|
||||||
|
|
||||||
/// ## mov(register: u8, operand: u32)
|
/// ## mov(register: u8, operand: u32)
|
||||||
/// - 0: opcode
|
/// - 0: opcode
|
||||||
/// - 1: register
|
/// - 1: register
|
||||||
@ -37,24 +35,20 @@ pub const MOV_LONG_TO: u8 = 0x08;
|
|||||||
pub const MOV_DOUBLE_TO: u8 = 0x09;
|
pub const MOV_DOUBLE_TO: u8 = 0x09;
|
||||||
pub const MOV_REGISTER_TO: u8 = 0x0a;
|
pub const MOV_REGISTER_TO: u8 = 0x0a;
|
||||||
|
|
||||||
pub const PLATFORM_CALL: u8 = 0x10;
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
|
pub enum RegisterType {
|
||||||
macro_rules! push_number {
|
Int,
|
||||||
( $dest: expr, $num: expr ) => {
|
Long,
|
||||||
push_bytes!($dest, $num.to_le_bytes())
|
Double,
|
||||||
};
|
Pointer,
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! push_string {
|
|
||||||
( $dest: expr, $s: expr ) => {
|
|
||||||
push_bytes!($dest, $s.bytes())
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_mov_int(code: &mut Vec<u8>, register: u8, operand: u32) {
|
pub fn add_mov_int(code: &mut Vec<u8>, register: u8, operand: u32) {
|
||||||
code.push(MOV_INT);
|
code.push(MOV_INT);
|
||||||
code.push(register);
|
code.push(register);
|
||||||
push_number!(code, operand);
|
for b in operand.to_le_bytes() {
|
||||||
|
code.push(b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_mov_register(code: &mut Vec<u8>, target_register: u8, source_register: u8) {
|
pub fn add_mov_register(code: &mut Vec<u8>, target_register: u8, source_register: u8) {
|
||||||
@ -66,7 +60,9 @@ pub fn add_mov_register(code: &mut Vec<u8>, target_register: u8, source_register
|
|||||||
pub fn add_alloc(code: &mut Vec<u8>, register: u8, size: u32) {
|
pub fn add_alloc(code: &mut Vec<u8>, register: u8, size: u32) {
|
||||||
code.push(ALLOC);
|
code.push(ALLOC);
|
||||||
code.push(register);
|
code.push(register);
|
||||||
push_number!(code, size);
|
for b in size.to_le_bytes() {
|
||||||
|
code.push(b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_dealloc(code: &mut Vec<u8>, register: u8) {
|
pub fn add_dealloc(code: &mut Vec<u8>, register: u8) {
|
||||||
@ -77,17 +73,10 @@ pub fn add_dealloc(code: &mut Vec<u8>, register: u8) {
|
|||||||
pub fn add_mov_int_to(code: &mut Vec<u8>, register: u8, offset: u32, operand: u32) {
|
pub fn add_mov_int_to(code: &mut Vec<u8>, register: u8, offset: u32, operand: u32) {
|
||||||
code.push(MOV_INT_TO);
|
code.push(MOV_INT_TO);
|
||||||
code.push(register);
|
code.push(register);
|
||||||
push_number!(code, offset);
|
for b in offset.to_le_bytes() {
|
||||||
push_number!(code, operand);
|
code.push(b);
|
||||||
}
|
}
|
||||||
|
for b in operand.to_le_bytes() {
|
||||||
pub fn add_platform_call_to(code: &mut Vec<u8>, symbol_name: &String, return_register: u8, arg_registers_length: u8, arg_registers: &Vec<u8>) {
|
|
||||||
code.push(PLATFORM_CALL);
|
|
||||||
push_number!(code, symbol_name.len() as u32);
|
|
||||||
push_string!(code, symbol_name);
|
|
||||||
push_number!(code, return_register);
|
|
||||||
push_number!(code, arg_registers_length);
|
|
||||||
for &b in arg_registers {
|
|
||||||
code.push(b);
|
code.push(b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
use crate::vm::platform::std_lib::core::dm_print;
|
|
||||||
use crate::vm::PlatformFunction;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
mod std_lib;
|
|
||||||
|
|
||||||
pub fn init_platform_functions() -> HashMap<String, PlatformFunction> {
|
|
||||||
let mut fns: HashMap<String, PlatformFunction> = HashMap::new();
|
|
||||||
fns.insert(String::from("std::core::print"), dm_print);
|
|
||||||
fns
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
use crate::vm::types::DmType;
|
|
||||||
use crate::vm::DmValue::*;
|
|
||||||
use crate::vm::{DmObject, DmValue, DmVirtualMachine};
|
|
||||||
use DmType::*;
|
|
||||||
use crate::vm::module::DmFunction;
|
|
||||||
|
|
||||||
fn get_method<'a>(ptr: *mut DmObject, name: String) -> Option<&'a DmFunction> {
|
|
||||||
unsafe {
|
|
||||||
(*ptr).object_type.methods.get(&name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_rust_string_from_dm_object(ptr: *mut DmObject) -> String {
|
|
||||||
unsafe {
|
|
||||||
let dm_object = Box::from_raw(ptr);
|
|
||||||
format!("{}@{:?}", dm_object.object_type.name, dm_object.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_rust_string_from_dm_string(ptr: *mut DmObject) -> String {
|
|
||||||
unsafe {
|
|
||||||
let dm_string = Box::from_raw(ptr);
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dm_print(args: Vec<DmValue>, vm: &mut DmVirtualMachine) -> DmValue {
|
|
||||||
if args.len() != 1 {
|
|
||||||
return DmException(todo!("make an Exception object."))
|
|
||||||
}
|
|
||||||
match args[0] {
|
|
||||||
DmInt(i) => {
|
|
||||||
print!("{}", i);
|
|
||||||
}
|
|
||||||
DmLong(l) => {
|
|
||||||
print!("{}", l);
|
|
||||||
}
|
|
||||||
DmDouble(d) => {
|
|
||||||
print!("{}", d);
|
|
||||||
}
|
|
||||||
DmPointer(ptr) => {
|
|
||||||
if let Some(to_string) = get_method(ptr, String::from("to_string")) {
|
|
||||||
let call_result = vm.call(&to_string, vec![args[0]]);
|
|
||||||
match call_result {
|
|
||||||
DmPointer(dm_string_ptr) => {
|
|
||||||
let string = get_rust_string_from_dm_string(dm_string_ptr);
|
|
||||||
print!("{}", string);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// TODO: vm throw or return exception?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
print!("{}", get_rust_string_from_dm_object(ptr));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
DmUnit => {
|
|
||||||
print!("Unit")
|
|
||||||
},
|
|
||||||
DmException(e) => {
|
|
||||||
print!("Exception")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DmUnit
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
pub mod core;
|
|
@ -1,9 +0,0 @@
|
|||||||
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
|
|
||||||
pub enum DmType {
|
|
||||||
Int,
|
|
||||||
Long,
|
|
||||||
Double,
|
|
||||||
Pointer,
|
|
||||||
Unit,
|
|
||||||
Exception
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
#[macro_export]
|
|
||||||
macro_rules! get_32_le {
|
|
||||||
( $bytes: expr, $base: expr, $offset: literal, $T: ident ) => {
|
|
||||||
$bytes[$base + $offset] as $T
|
|
||||||
+ (($bytes[$base + $offset + 1] as $T) << 8)
|
|
||||||
+ (($bytes[$base + $offset + 2] as $T) << 16)
|
|
||||||
+ (($bytes[$base + $offset + 3] as $T) << 24)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! get_64_le {
|
|
||||||
( $bytes: expr, $base: expr, $offset: literal, $T: ident ) => {
|
|
||||||
$bytes[$base + $offset] as $T
|
|
||||||
+ (($bytes[$base + $offset + 1] as $T) << 8)
|
|
||||||
+ (($bytes[$base + $offset + 2] as $T) << 16)
|
|
||||||
+ (($bytes[$base + $offset + 3] as $T) << 24)
|
|
||||||
+ (($bytes[$base + $offset + 4] as $T) << 32)
|
|
||||||
+ (($bytes[$base + $offset + 5] as $T) << 40)
|
|
||||||
+ (($bytes[$base + $offset + 6] as $T) << 48)
|
|
||||||
+ (($bytes[$base + $offset + 7] as $T) << 56)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! push_bytes {
|
|
||||||
( $dest: expr, $src: expr ) => {
|
|
||||||
for b in $src {
|
|
||||||
$dest.push(b);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub use get_32_le;
|
|
||||||
pub use get_64_le;
|
|
||||||
pub use push_bytes;
|
|
Loading…
Reference in New Issue
Block a user