Compare commits
9 Commits
e4ee8fd2db
...
5732c4d197
Author | SHA1 | Date | |
---|---|---|---|
![]() |
5732c4d197 | ||
![]() |
a14eb550ce | ||
![]() |
21d4f6bb69 | ||
![]() |
438d0e7317 | ||
![]() |
4fb7ada6b8 | ||
![]() |
921a7fe834 | ||
![]() |
66107b4310 | ||
![]() |
c1618ba9a2 | ||
![]() |
6c8911e7c9 |
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 = 3
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deimos-lang"
|
name = "deimos"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
10
Cargo.toml
10
Cargo.toml
@ -1,6 +1,14 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "deimos-lang"
|
name = "deimos"
|
||||||
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]
|
||||||
|
22
src/bin/compiler/main.rs
Normal file
22
src/bin/compiler/main.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
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
|
||||||
|
}
|
11
src/bin/dvm/main.rs
Normal file
11
src/bin/dvm/main.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
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,9 +29,10 @@ pub enum Token {
|
|||||||
Dot,
|
Dot,
|
||||||
Ellipsis,
|
Ellipsis,
|
||||||
Abstract,
|
Abstract,
|
||||||
|
NumberLiteral(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tokenize(input: &String) -> Result<Vec<Token>, &'static str> {
|
pub fn tokenize(input: &String) -> Result<Vec<Token>, String> {
|
||||||
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() {
|
||||||
@ -72,12 +73,28 @@ pub fn tokenize(input: &String) -> Result<Vec<Token>, &'static str> {
|
|||||||
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("Unexpected number of tokens after '.'"),
|
_ => return Err(String::from("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)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -228,4 +245,10 @@ 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,2 +1,3 @@
|
|||||||
mod lexer;
|
pub mod lexer;
|
||||||
mod vm;
|
pub mod parser;
|
||||||
|
pub mod vm;
|
||||||
|
8
src/parser/mod.rs
Normal file
8
src/parser/mod.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
mod types;
|
||||||
|
|
||||||
|
use crate::lexer::Token;
|
||||||
|
use crate::parser::types::AstNode;
|
||||||
|
|
||||||
|
pub fn parse(tokens: &Vec<Token>) -> Result<AstNode, String> {
|
||||||
|
todo!()
|
||||||
|
}
|
11
src/parser/types.rs
Normal file
11
src/parser/types.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
pub type NodeChildren = Vec<Box<AstNode>>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum AstNode {
|
||||||
|
CompilationUnit(NodeChildren),
|
||||||
|
BlockStatement(NodeChildren),
|
||||||
|
Statement(NodeChildren),
|
||||||
|
Expression(NodeChildren),
|
||||||
|
}
|
307
src/vm/mod.rs
307
src/vm/mod.rs
@ -1,177 +1,314 @@
|
|||||||
mod op_codes;
|
pub mod module;
|
||||||
|
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};
|
||||||
|
|
||||||
struct DmObject {
|
pub type PlatformFunction = fn(args: Vec<DmValue>, &mut DmVirtualMachine) -> DmValue;
|
||||||
pointer: *mut u8,
|
|
||||||
size: usize,
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
layout: Layout,
|
pub enum DmValue {
|
||||||
|
DmInt(i32),
|
||||||
|
DmLong(i64),
|
||||||
|
DmDouble(f64),
|
||||||
|
DmPointer(*mut DmObject),
|
||||||
|
DmUnit,
|
||||||
|
DmException(*mut DmObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(code: &Vec<u8>, registers: &mut Vec<u64>, register_types: &mut Vec<RegisterType>) {
|
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;
|
let mut i = 0;
|
||||||
while i < code.len() {
|
while i < code.len() {
|
||||||
match code[i] {
|
match code[i] {
|
||||||
MOV_INT => {
|
MOV_INT => {
|
||||||
let target_register = code[i + 1] as usize;
|
let target_register = code[i + 1] as usize;
|
||||||
let operand: u32 = code[i + 2] as u32
|
let operand = get_32_le!(code, i, 2, u32);
|
||||||
+ ((code[i + 3] as u32) << 8)
|
self.registers.insert(target_register, DmInt(operand as i32));
|
||||||
+ ((code[i + 4] as u32) << 16)
|
i += 6;
|
||||||
+ ((code[i + 5] as u32) << 24);
|
|
||||||
registers[target_register] = operand as u64;
|
|
||||||
register_types[target_register] = RegisterType::Int;
|
|
||||||
i += 6
|
|
||||||
}
|
}
|
||||||
MOV_LONG => {
|
MOV_LONG => {
|
||||||
let target_register = code[i + 1] as usize;
|
let target_register = code[i + 1] as usize;
|
||||||
let operand: u64 = code[i + 2] as u64
|
let operand = get_64_le!(code, i, 2, u64);
|
||||||
+ ((code[i + 3] as u64) << 8)
|
self.registers.insert(target_register, DmLong(operand as i64));
|
||||||
+ ((code[i + 4] as u64) << 16)
|
i += 10;
|
||||||
+ ((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_DOUBLE => { /* todo */ }
|
||||||
MOV_REGISTER => {
|
MOV_REGISTER => {
|
||||||
let target_register = code[i + 1] as usize;
|
let target_register = code[i + 1] as usize;
|
||||||
let source_register = code[i + 2] as usize;
|
let source_register = code[i + 2] as usize;
|
||||||
registers[target_register] = registers[source_register];
|
let source_value = self.registers.get(source_register).unwrap();
|
||||||
register_types[target_register] = register_types[source_register].clone();
|
self.registers.insert(target_register, source_value.clone());
|
||||||
i += 3;
|
i += 3;
|
||||||
}
|
}
|
||||||
ALLOC => {
|
ALLOC => {
|
||||||
let target_register = code[i + 1] as usize;
|
let target_register = code[i + 1] as usize;
|
||||||
let size = code[i + 2] as usize
|
let size = get_32_le!(code, i, 2, 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 layout = Layout::from_size_align(size, 4).unwrap();
|
||||||
let pointer = unsafe { alloc_zeroed(layout) };
|
let pointer = unsafe { alloc_zeroed(layout) };
|
||||||
let dm_object = Box::new(DmObject {
|
let dm_object = Box::new(DmObject {
|
||||||
pointer,
|
data: pointer,
|
||||||
size,
|
size,
|
||||||
layout,
|
layout,
|
||||||
|
object_type: todo!(),
|
||||||
});
|
});
|
||||||
let dm_object_pointer = Box::into_raw(dm_object) as u64;
|
let dm_object_pointer = Box::into_raw(dm_object);
|
||||||
registers[target_register] = dm_object_pointer;
|
self.registers.insert(target_register, DmPointer(dm_object_pointer));
|
||||||
register_types[target_register] = RegisterType::Pointer;
|
|
||||||
i += 6;
|
i += 6;
|
||||||
}
|
}
|
||||||
DEALLOC => {
|
DEALLOC => {
|
||||||
let target_register = code[i + 1] as usize;
|
let target_register = code[i + 1] as usize;
|
||||||
let box_address = registers[target_register];
|
let target_value = self.registers.get(target_register).unwrap();
|
||||||
unsafe {
|
match target_value {
|
||||||
let dm_object = Box::from_raw(box_address as *mut DmObject);
|
DmPointer(ptr) => unsafe {
|
||||||
dealloc(dm_object.pointer, dm_object.layout);
|
let dm_object = Box::from_raw(ptr.clone());
|
||||||
|
dealloc(dm_object.data, dm_object.layout);
|
||||||
}
|
}
|
||||||
registers[target_register] = 0;
|
_ => {
|
||||||
register_types[target_register] = RegisterType::Int;
|
panic!("Attempt to deallocate at the address stored in a register that is not a pointer.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.registers.insert(target_register, DmUnit);
|
||||||
i += 2;
|
i += 2;
|
||||||
}
|
}
|
||||||
MOV_INT_TO => {
|
MOV_INT_TO => {
|
||||||
let target_register = code[i + 1] as usize;
|
let target_register = code[i + 1] as usize;
|
||||||
if register_types[target_register] != RegisterType::Pointer {
|
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);
|
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];
|
PLATFORM_CALL => {
|
||||||
let new_address = unsafe {
|
i += 1;
|
||||||
let dm_object = Box::from_raw(box_address as *mut DmObject);
|
|
||||||
let pointer = dm_object.pointer;
|
let symbol_name_length = get_32_le!(code, i, 0, usize);
|
||||||
pointer.offset(offset).write(code[i + 6]);
|
i += 4;
|
||||||
pointer.offset(offset + 1).write(code[i + 7]);
|
|
||||||
pointer.offset(offset + 2).write(code[i + 8]);
|
let symbol_name_raw = code[i..(i + symbol_name_length)].to_vec();
|
||||||
pointer.offset(offset + 3).write(code[i + 9]);
|
i += symbol_name_length;
|
||||||
Box::into_raw(dm_object) as u64
|
|
||||||
};
|
let symbol_name = String::from_utf8(symbol_name_raw).unwrap();
|
||||||
registers[target_register] = new_address;
|
|
||||||
i += 10;
|
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"),
|
_ => panic!("Invalid code instruction"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_to_u32(bytes: &[u8]) -> u32 {
|
pub struct DmObjectType {
|
||||||
bytes[0] as u32 | (bytes[1] as u32) << 8 | (bytes[2] as u32) << 16 | (bytes[3] as u32) << 24
|
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,
|
||||||
|
layout: Layout,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DmObjectProperty {
|
||||||
|
name: String,
|
||||||
|
property_type: DmType,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DmObjectField {
|
||||||
|
name: String,
|
||||||
|
field_type: DmType,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod dvm_run_tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::alloc::dealloc;
|
use std::alloc::dealloc;
|
||||||
|
|
||||||
fn init_registers(n_registers: usize) -> (Vec<u64>, Vec<RegisterType>) {
|
macro_rules! assert_register {
|
||||||
(vec![0; n_registers], vec![RegisterType::Int; n_registers])
|
( $expected: expr, $register: expr ) => {
|
||||||
|
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 registers, mut register_types) = init_registers(1);
|
let mut vm = DmVirtualMachine::new();
|
||||||
run(&code, &mut registers, &mut register_types);
|
vm.run(&code);
|
||||||
assert_eq!(1, registers[0]);
|
assert_register!(DmInt(1), vm.registers.get(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 registers, mut register_types) = init_registers(1);
|
let mut vm = DmVirtualMachine::new();
|
||||||
run(&code, &mut registers, &mut register_types);
|
vm.run(&code);
|
||||||
assert_eq!(0xffff, registers[0]);
|
assert_register!(DmInt(0xffff), vm.registers.get(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, 0xffff_ffff);
|
add_mov_int(&mut code, 0, 0x0fff_ffff);
|
||||||
let (mut registers, mut register_types) = init_registers(1);
|
let mut vm = DmVirtualMachine::new();
|
||||||
run(&code, &mut registers, &mut register_types);
|
vm.run(&code);
|
||||||
assert_eq!(0xffff_ffff, registers[0]);
|
assert_register!(DmInt(0x0fff_ffff), vm.registers.get(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 registers, mut register_types) = init_registers(2);
|
let mut vm = DmVirtualMachine::new();
|
||||||
registers[1] = 1;
|
vm.registers.resize(2, DmUnit);
|
||||||
run(&code, &mut registers, &mut register_types);
|
vm.run(&code);
|
||||||
assert_eq!(registers[0], 1);
|
assert_register!(DmInt(1), vm.registers.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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 registers = vec![0; 16];
|
let mut vm = DmVirtualMachine::new();
|
||||||
let mut register_types = vec![RegisterType::Int; 16];
|
vm.run(&code);
|
||||||
run(&code, &mut registers, &mut register_types);
|
let box_address = vm.registers.get(0).unwrap().clone();
|
||||||
let box_address = registers[0];
|
match box_address {
|
||||||
unsafe {
|
DmPointer(ptr) => unsafe {
|
||||||
let dm_object = Box::from_raw(box_address as *mut DmObject);
|
let dm_object = Box::from_raw(ptr);
|
||||||
assert_eq!(0xff, *dm_object.pointer);
|
assert_eq!(0xff, *dm_object.data);
|
||||||
dealloc(dm_object.pointer, dm_object.layout);
|
dealloc(dm_object.data, 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 registers, mut register_types) = init_registers(1);
|
let mut vm = DmVirtualMachine::new();
|
||||||
run(&code, &mut registers, &mut register_types);
|
vm.run(&code);
|
||||||
assert_eq!(0, registers[0]);
|
assert_register!(DmUnit, vm.registers.get(0));
|
||||||
assert_eq!(RegisterType::Int, register_types[0]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
177
src/vm/module.rs
Normal file
177
src/vm/module.rs
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
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,3 +1,5 @@
|
|||||||
|
use crate::push_bytes;
|
||||||
|
|
||||||
/// ## mov(register: u8, operand: u32)
|
/// ## mov(register: u8, operand: u32)
|
||||||
/// - 0: opcode
|
/// - 0: opcode
|
||||||
/// - 1: register
|
/// - 1: register
|
||||||
@ -35,20 +37,24 @@ 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;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
pub const PLATFORM_CALL: u8 = 0x10;
|
||||||
pub enum RegisterType {
|
|
||||||
Int,
|
macro_rules! push_number {
|
||||||
Long,
|
( $dest: expr, $num: expr ) => {
|
||||||
Double,
|
push_bytes!($dest, $num.to_le_bytes())
|
||||||
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);
|
||||||
for b in operand.to_le_bytes() {
|
push_number!(code, operand);
|
||||||
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) {
|
||||||
@ -60,9 +66,7 @@ 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);
|
||||||
for b in size.to_le_bytes() {
|
push_number!(code, size);
|
||||||
code.push(b);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_dealloc(code: &mut Vec<u8>, register: u8) {
|
pub fn add_dealloc(code: &mut Vec<u8>, register: u8) {
|
||||||
@ -73,10 +77,17 @@ 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);
|
||||||
for b in offset.to_le_bytes() {
|
push_number!(code, offset);
|
||||||
code.push(b);
|
push_number!(code, operand);
|
||||||
}
|
}
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
src/vm/platform/mod.rs
Normal file
11
src/vm/platform/mod.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
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
|
||||||
|
}
|
65
src/vm/platform/std_lib/core.rs
Normal file
65
src/vm/platform/std_lib/core.rs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
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
src/vm/platform/std_lib/mod.rs
Normal file
1
src/vm/platform/std_lib/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod core;
|
9
src/vm/types.rs
Normal file
9
src/vm/types.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
|
||||||
|
pub enum DmType {
|
||||||
|
Int,
|
||||||
|
Long,
|
||||||
|
Double,
|
||||||
|
Pointer,
|
||||||
|
Unit,
|
||||||
|
Exception
|
||||||
|
}
|
36
src/vm/util.rs
Normal file
36
src/vm/util.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#[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