Compare commits

..

9 Commits

Author SHA1 Message Date
Jesse Brault
5732c4d197 All the work to print 42 via a platform call. 2024-11-30 16:03:22 -06:00
Jesse Brault
a14eb550ce Much work on dvm implementation; not compiling yet. 2024-11-30 08:09:19 -06:00
Jesse Brault
21d4f6bb69 Some clean up. 2024-11-29 22:39:24 -06:00
Jesse Brault
438d0e7317 Start work on platform calls. 2024-11-29 22:35:01 -06:00
Jesse Brault
4fb7ada6b8 Very skeleton work on parser. 2024-11-29 21:25:21 -06:00
Jesse Brault
921a7fe834 Change crate name to 'deimos'; make compiler bin subproject. 2024-11-29 19:13:59 -06:00
Jesse Brault
66107b4310 Begin work on write_module. 2024-11-29 19:13:30 -06:00
Jesse Brault
c1618ba9a2 Move macros to util; basic work on load_module. 2024-11-29 17:01:43 -06:00
Jesse Brault
6c8911e7c9 Add macros for fetching 32 and 64 bits from instruction pipeline. 2024-11-29 13:13:16 -06:00
16 changed files with 675 additions and 144 deletions

4
Cargo.lock generated
View File

@ -1,7 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "deimos-lang"
name = "deimos"
version = "0.1.0"

View File

@ -1,6 +1,14 @@
[package]
name = "deimos-lang"
name = "deimos"
version = "0.1.0"
edition = "2021"
[[bin]]
name = "dmc"
path = "src/bin/compiler/main.rs"
[[bin]]
name = "dm"
path = "src/bin/dvm/main.rs"
[dependencies]

22
src/bin/compiler/main.rs Normal file
View 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
View 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!()
}

View File

@ -29,9 +29,10 @@ pub enum Token {
Dot,
Ellipsis,
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 peekable = input.chars().peekable();
while let Some(c) = peekable.next() {
@ -72,12 +73,28 @@ pub fn tokenize(input: &String) -> Result<Vec<Token>, &'static str> {
match count {
1 => tokens.push(Token::Dot),
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) {
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::CurlyClose, result[3]);
}
#[test]
fn simple_number() {
let result = tokenize(&String::from("123456")).unwrap();
assert_eq!(Token::NumberLiteral(String::from("123456")), result[0]);
}
}

View File

@ -1,2 +1,3 @@
mod lexer;
mod vm;
pub mod lexer;
pub mod parser;
pub mod vm;

8
src/parser/mod.rs Normal file
View 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
View 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),
}

View File

@ -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 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 {
pointer: *mut u8,
size: usize,
layout: Layout,
pub type PlatformFunction = fn(args: Vec<DmValue>, &mut DmVirtualMachine) -> DmValue;
#[derive(Debug, Clone, Copy, PartialEq)]
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;
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
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: 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
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;
registers[target_register] = registers[source_register];
register_types[target_register] = register_types[source_register].clone();
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 = code[i + 2] as usize
+ ((code[i + 3] as usize) << 8)
+ ((code[i + 4] as usize) << 16)
+ ((code[i + 5] as usize) << 24);
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 {
pointer,
data: pointer,
size,
layout,
object_type: todo!(),
});
let dm_object_pointer = Box::into_raw(dm_object) as u64;
registers[target_register] = dm_object_pointer;
register_types[target_register] = RegisterType::Pointer;
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 box_address = registers[target_register];
unsafe {
let dm_object = Box::from_raw(box_address as *mut DmObject);
dealloc(dm_object.pointer, dm_object.layout);
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);
}
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;
}
MOV_INT_TO => {
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);
}
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;
}
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"),
}
}
}
fn convert_to_u32(bytes: &[u8]) -> u32 {
bytes[0] as u32 | (bytes[1] as u32) << 8 | (bytes[2] as u32) << 16 | (bytes[3] as u32) << 24
}
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,
layout: Layout,
}
pub struct DmObjectProperty {
name: String,
property_type: DmType,
}
pub struct DmObjectField {
name: String,
field_type: DmType,
}
#[cfg(test)]
mod tests {
mod dvm_run_tests {
use super::*;
use std::alloc::dealloc;
fn init_registers(n_registers: usize) -> (Vec<u64>, Vec<RegisterType>) {
(vec![0; n_registers], vec![RegisterType::Int; n_registers])
macro_rules! assert_register {
( $expected: expr, $register: expr ) => {
assert_eq!($expected, $register.unwrap().clone());
};
}
#[test]
fn mov_1_as_int() {
let mut code = Vec::new();
add_mov_int(&mut code, 0, 1);
let (mut registers, mut register_types) = init_registers(1);
run(&code, &mut registers, &mut register_types);
assert_eq!(1, registers[0]);
let mut vm = DmVirtualMachine::new();
vm.run(&code);
assert_register!(DmInt(1), vm.registers.get(0));
}
#[test]
fn move_65535_as_int() {
let mut code = Vec::new();
add_mov_int(&mut code, 0, 0xffff);
let (mut registers, mut register_types) = init_registers(1);
run(&code, &mut registers, &mut register_types);
assert_eq!(0xffff, registers[0]);
let mut vm = DmVirtualMachine::new();
vm.run(&code);
assert_register!(DmInt(0xffff), vm.registers.get(0));
}
#[test]
fn move_int_max_as_int() {
let mut code = Vec::new();
add_mov_int(&mut code, 0, 0xffff_ffff);
let (mut registers, mut register_types) = init_registers(1);
run(&code, &mut registers, &mut register_types);
assert_eq!(0xffff_ffff, registers[0]);
add_mov_int(&mut code, 0, 0x0fff_ffff);
let mut vm = DmVirtualMachine::new();
vm.run(&code);
assert_register!(DmInt(0x0fff_ffff), vm.registers.get(0));
}
#[test]
fn move_register() {
let mut code = Vec::new();
add_mov_int(&mut code, 1, 1);
add_mov_register(&mut code, 0, 1);
let (mut registers, mut register_types) = init_registers(2);
registers[1] = 1;
run(&code, &mut registers, &mut register_types);
assert_eq!(registers[0], 1);
let mut vm = DmVirtualMachine::new();
vm.registers.resize(2, DmUnit);
vm.run(&code);
assert_register!(DmInt(1), vm.registers.get(0));
}
#[test]
#[ignore]
fn mov_int_to_register_as_address() {
let mut code = Vec::new();
add_alloc(&mut code, 0, 4);
add_mov_int_to(&mut code, 0, 0, 0xff);
let mut registers = vec![0; 16];
let mut register_types = vec![RegisterType::Int; 16];
run(&code, &mut registers, &mut register_types);
let box_address = registers[0];
unsafe {
let dm_object = Box::from_raw(box_address as *mut DmObject);
assert_eq!(0xff, *dm_object.pointer);
dealloc(dm_object.pointer, dm_object.layout);
let mut vm = DmVirtualMachine::new();
vm.run(&code);
let box_address = vm.registers.get(0).unwrap().clone();
match box_address {
DmPointer(ptr) => unsafe {
let dm_object = Box::from_raw(ptr);
assert_eq!(0xff, *dm_object.data);
dealloc(dm_object.data, dm_object.layout);
},
_ => panic!("Target register is not a pointer"),
}
}
#[test]
#[ignore]
fn alloc_and_dealloc_expect_register_cleared() {
let mut code = Vec::new();
add_alloc(&mut code, 0, 4);
add_dealloc(&mut code, 0);
let (mut registers, mut register_types) = init_registers(1);
run(&code, &mut registers, &mut register_types);
assert_eq!(0, registers[0]);
assert_eq!(RegisterType::Int, register_types[0]);
let mut vm = DmVirtualMachine::new();
vm.run(&code);
assert_register!(DmUnit, vm.registers.get(0));
}
}

177
src/vm/module.rs Normal file
View 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);
}
}

View File

@ -1,3 +1,5 @@
use crate::push_bytes;
/// ## mov(register: u8, operand: u32)
/// - 0: opcode
/// - 1: register
@ -35,20 +37,24 @@ pub const MOV_LONG_TO: u8 = 0x08;
pub const MOV_DOUBLE_TO: u8 = 0x09;
pub const MOV_REGISTER_TO: u8 = 0x0a;
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum RegisterType {
Int,
Long,
Double,
Pointer,
pub const PLATFORM_CALL: u8 = 0x10;
macro_rules! push_number {
( $dest: expr, $num: expr ) => {
push_bytes!($dest, $num.to_le_bytes())
};
}
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) {
code.push(MOV_INT);
code.push(register);
for b in operand.to_le_bytes() {
code.push(b);
}
push_number!(code, operand);
}
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) {
code.push(ALLOC);
code.push(register);
for b in size.to_le_bytes() {
code.push(b);
}
push_number!(code, size);
}
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) {
code.push(MOV_INT_TO);
code.push(register);
for b in offset.to_le_bytes() {
code.push(b);
push_number!(code, offset);
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);
}
}

11
src/vm/platform/mod.rs Normal file
View 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
}

View 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
}

View File

@ -0,0 +1 @@
pub mod core;

9
src/vm/types.rs Normal file
View 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
View 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;