Very minimal, but e2e hello world sort of working.

This commit is contained in:
Jesse Brault 2026-03-01 16:11:57 -06:00
parent 6593a1cfd1
commit 0960516c4a
22 changed files with 530 additions and 38 deletions

46
Cargo.lock generated
View File

@ -86,9 +86,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.5.23"
version = "4.5.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a"
dependencies = [
"clap_builder",
"clap_derive",
@ -96,9 +96,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.23"
version = "4.5.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876"
dependencies = [
"anstream",
"anstyle",
@ -108,9 +108,9 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "4.5.18"
version = "4.5.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5"
dependencies = [
"heck",
"proc-macro2",
@ -120,9 +120,9 @@ dependencies = [
[[package]]
name = "clap_lex"
version = "0.7.4"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
[[package]]
name = "codespan-reporting"
@ -135,6 +135,17 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "codespan-reporting"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681"
dependencies = [
"serde",
"termcolor",
"unicode-width",
]
[[package]]
name = "colorchoice"
version = "1.0.3"
@ -186,7 +197,7 @@ version = "0.1.0"
dependencies = [
"ast-generator",
"clap",
"codespan-reporting",
"codespan-reporting 0.12.0",
"cst-test-generator",
"indoc",
"log",
@ -204,9 +215,26 @@ dependencies = [
"crypto-common",
]
[[package]]
name = "dm"
version = "0.1.0"
dependencies = [
"clap",
"codespan-reporting 0.13.1",
"dmc-lib",
"dvm-lib",
]
[[package]]
name = "dmc-lib"
version = "0.1.0"
dependencies = [
"dvm-lib",
]
[[package]]
name = "dvm-lib"
version = "0.1.0"
[[package]]
name = "encoding_rs"

View File

@ -3,9 +3,9 @@ name = "deimos"
version = "0.1.0"
edition = "2021"
[[bin]]
name = "dm"
path = "src/bin/dvm/main.rs"
#[[bin]]
#name = "dm"
#path = "src/bin/dvm/main.rs"
[[bin]]
name = "dmc"
@ -25,4 +25,4 @@ cst-test-generator = { path = "cst-test-generator" }
[workspace]
resolver = "3"
members = ["ast-generator", "cst-test-generator", "dmc-lib"]
members = ["ast-generator", "cst-test-generator", "dm", "dmc-lib", "dvm-lib"]

10
dm/Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "dm"
version = "0.1.0"
edition = "2024"
[dependencies]
dmc-lib = { path = "../dmc-lib" }
dvm-lib = { path = "../dvm-lib" }
clap = { version = "4.5.60", features = ["derive"] }
codespan-reporting = "0.13.1"

72
dm/src/main.rs Normal file
View File

@ -0,0 +1,72 @@
use clap::Parser;
use dmc_lib::constants_table::ConstantsTable;
use dmc_lib::diagnostic::Diagnostic;
use dmc_lib::parser::parse_compilation_unit;
use dmc_lib::symbol_table::SymbolTable;
use dvm_lib::vm::constant::{Constant, StringConstant};
use dvm_lib::vm::{DvmContext, DvmState, call};
use std::path::PathBuf;
use std::rc::Rc;
#[derive(Debug, Parser)]
#[command(name = "dm", about = "Deimos", version = "0.1.0", long_about = None)]
struct Cli {
script: PathBuf,
#[arg(long)]
show_asm: bool,
}
fn main() {
let args = Cli::parse();
let input = std::fs::read_to_string(&args.script).unwrap();
let mut compilation_unit = parse_compilation_unit(&input);
let mut symbol_table = SymbolTable::new();
let gather_names_diagnostics = compilation_unit.gather_declared_names(&mut symbol_table);
check_and_report_diagnostics(&gather_names_diagnostics);
let name_usages_diagnostics = compilation_unit.check_name_usages(&symbol_table);
check_and_report_diagnostics(&name_usages_diagnostics);
let type_check_diagnostics = compilation_unit.type_check(&symbol_table);
check_and_report_diagnostics(&type_check_diagnostics);
let mut constants_table = ConstantsTable::new();
let asm_functions = compilation_unit.assemble(&symbol_table, &mut constants_table);
if args.show_asm {
for asm_function in &asm_functions {
println!("{:?}", asm_function);
}
}
let mut dvm_context = DvmContext::new();
for asm_function in &asm_functions {
let function = asm_function.dvm();
dvm_context.add_function(function);
}
for (name, content) in &constants_table.string_constants() {
dvm_context.add_constant(Constant::String(StringConstant::new(
Rc::from(name.clone()),
content.as_str(),
)));
}
let mut dvm_state = DvmState::new();
let result = call(&dvm_context, &mut dvm_state, "main", vec![]);
println!("{:?}", result);
}
fn check_and_report_diagnostics(diagnostics: &[Diagnostic]) {
if !diagnostics.is_empty() {
for diagnostic in diagnostics {
println!("{:?}", diagnostic);
}
std::process::exit(1);
}
}

View File

@ -4,3 +4,4 @@ version = "0.1.0"
edition = "2024"
[dependencies]
dvm-lib = { path = "../dvm-lib" }

View File

@ -13,4 +13,8 @@ impl AsmBlock {
instructions,
}
}
pub fn instructions(&self) -> &[AsmInstruction] {
&self.instructions
}
}

View File

@ -1,4 +1,6 @@
use crate::asm::asm_block::AsmBlock;
use crate::asm::asm_instruction::AsmInstruction;
use dvm_lib::vm::function::Function;
#[derive(Debug)]
pub struct AsmFunction {
@ -13,4 +15,15 @@ impl AsmFunction {
blocks,
}
}
pub fn dvm(&self) -> Function {
// very naive impl
let dvm_instructions = self
.blocks
.iter()
.flat_map(|block| block.instructions().iter().map(AsmInstruction::dvm))
.collect::<Vec<_>>();
Function::new(&self.name, dvm_instructions, 16)
}
}

View File

@ -1,3 +1,6 @@
use dvm_lib::instruction::Instruction;
use std::rc::Rc;
#[derive(Debug)]
pub enum AsmInstruction {
Move(Move),
@ -7,11 +10,25 @@ pub enum AsmInstruction {
LoadConstant(LoadConstant),
}
impl AsmInstruction {
pub fn dvm(&self) -> Instruction {
match self {
AsmInstruction::Move(asm_move) => asm_move.dvm(),
AsmInstruction::Push(push) => push.dvm(),
AsmInstruction::Pop(pop) => pop.dvm(),
AsmInstruction::InvokePlatformStatic(invoke_platform_static) => {
invoke_platform_static.dvm()
}
AsmInstruction::LoadConstant(load_constant) => load_constant.dvm(),
}
}
}
#[derive(Debug)]
pub enum Operand {
IntegerLiteral(i64),
IntegerLiteral(i32),
Register(usize),
StackFrameOffset(usize),
StackFrameOffset(isize),
}
#[derive(Debug)]
@ -27,6 +44,18 @@ impl Move {
destination_register,
}
}
pub fn dvm(&self) -> Instruction {
match self.source {
Operand::IntegerLiteral(i) => Instruction::MoveInt(i, self.destination_register),
Operand::Register(register) => {
Instruction::MoveRegister(register, self.destination_register)
}
Operand::StackFrameOffset(offset) => {
Instruction::MoveStackFrameOffset(offset, self.destination_register)
}
}
}
}
#[derive(Debug)]
@ -38,19 +67,37 @@ impl Push {
pub fn new(source: Operand) -> Self {
Self { source }
}
pub fn dvm(&self) -> Instruction {
match self.source {
Operand::IntegerLiteral(i) => Instruction::PushInt(i),
Operand::Register(register) => Instruction::PushRegister(register),
Operand::StackFrameOffset(offset) => Instruction::PushStackFrameOffset(offset),
}
}
}
#[derive(Debug)]
pub struct Pop {
destination_register: usize,
destination_register: Option<usize>,
}
impl Pop {
pub fn new(destination_register: usize) -> Self {
Self {
destination_register,
destination_register: Some(destination_register),
}
}
pub fn empty() -> Self {
Self {
destination_register: None,
}
}
pub fn dvm(&self) -> Instruction {
Instruction::Pop(self.destination_register)
}
}
#[derive(Debug)]
@ -62,6 +109,10 @@ impl InvokePlatformStatic {
pub fn new(name: &str) -> Self {
Self { name: name.into() }
}
pub fn dvm(&self) -> Instruction {
Instruction::InvokePlatformStatic(Rc::from(self.name.clone()))
}
}
#[derive(Debug)]
@ -77,4 +128,8 @@ impl LoadConstant {
destination_register,
}
}
pub fn dvm(&self) -> Instruction {
Instruction::LoadStringConstant(Rc::from(self.name.clone()), self.destination_register)
}
}

View File

@ -3,19 +3,19 @@ use crate::ir::ir_expression::IrExpression;
use crate::source_range::SourceRange;
pub struct IntegerLiteral {
value: i64,
value: i32,
source_range: SourceRange,
}
impl IntegerLiteral {
pub fn new(value: i64, source_range: SourceRange) -> Self {
pub fn new(value: i32, source_range: SourceRange) -> Self {
Self {
value,
source_range,
}
}
pub fn value(&self) -> i64 {
pub fn value(&self) -> i32 {
self.value
}

View File

@ -19,4 +19,12 @@ impl ConstantsTable {
self.strings_to_names.insert(s.into(), name.clone());
name
}
pub fn string_constants(&self) -> HashMap<String, String> {
let mut constants = HashMap::new();
self.strings_to_names.iter().for_each(|(content, name)| {
constants.insert(name.clone(), content.clone());
});
constants
}
}

View File

@ -6,6 +6,6 @@ use crate::ir::ir_variable::IrVariable;
pub enum IrExpression {
Call(IrCall),
Constant(IrConstant),
IntegerLiteral(i64),
IntegerLiteral(i32),
Variable(IrVariable),
}

View File

@ -1,13 +1,13 @@
mod asm;
mod ast;
mod constants_table;
mod diagnostic;
mod ir;
mod lexer;
mod parser;
mod scope;
mod source_range;
mod symbol;
mod symbol_table;
mod token;
mod type_info;
pub mod asm;
pub mod ast;
pub mod constants_table;
pub mod diagnostic;
pub mod ir;
pub mod lexer;
pub mod parser;
pub mod scope;
pub mod source_range;
pub mod symbol;
pub mod symbol_table;
pub mod token;
pub mod type_info;

View File

@ -181,7 +181,7 @@ impl<'a> Parser<'a> {
let source_range = SourceRange::new(current.start(), current.end());
self.advance();
Expression::IntegerLiteral(IntegerLiteral::new(
i64::from_str(raw).unwrap(),
i32::from_str(raw).unwrap(),
source_range,
))
}

View File

@ -35,7 +35,7 @@ impl FunctionSymbol {
pub struct ParameterSymbol {
name: Rc<str>,
type_info: TypeInfo,
stack_frame_offset: Option<usize>,
stack_frame_offset: Option<isize>,
}
impl ParameterSymbol {
@ -59,11 +59,11 @@ impl ParameterSymbol {
&self.type_info
}
pub fn set_stack_frame_offset(&mut self, offset: usize) {
pub fn set_stack_frame_offset(&mut self, offset: isize) {
self.stack_frame_offset = Some(offset);
}
pub fn stack_frame_offset(&self) -> usize {
pub fn stack_frame_offset(&self) -> isize {
self.stack_frame_offset.unwrap()
}
}

6
dvm-lib/Cargo.toml Normal file
View File

@ -0,0 +1,6 @@
[package]
name = "dvm-lib"
version = "0.1.0"
edition = "2024"
[dependencies]

View File

@ -0,0 +1,21 @@
use std::rc::Rc;
pub type Register = usize;
pub type ConstantName = Rc<str>;
pub type FunctionName = Rc<str>;
pub enum Instruction {
MoveRegister(Register, Register),
MoveInt(i32, Register),
MoveStackFrameOffset(isize, Register),
PushRegister(Register),
PushInt(i32),
PushStackFrameOffset(isize),
InvokePlatformStatic(FunctionName),
LoadStringConstant(ConstantName, Register),
Pop(Option<Register>),
}

2
dvm-lib/src/lib.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod instruction;
pub mod vm;

View File

@ -0,0 +1,31 @@
use std::rc::Rc;
pub enum Constant {
String(StringConstant),
}
pub struct StringConstant {
name: Rc<str>,
content: Rc<str>,
}
impl StringConstant {
pub fn new(name: Rc<str>, content: &str) -> Self {
Self {
name,
content: content.into(),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn name_owned(&self) -> Rc<str> {
self.name.clone()
}
pub fn content_owned(&self) -> Rc<str> {
self.content.clone()
}
}

View File

@ -0,0 +1,34 @@
use crate::instruction::Instruction;
use std::rc::Rc;
pub struct Function {
name: Rc<str>,
instructions: Vec<Instruction>,
register_count: usize,
}
impl Function {
pub fn new(name: &str, instructions: Vec<Instruction>, register_count: usize) -> Self {
Self {
name: name.into(),
instructions,
register_count,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn name_owned(&self) -> Rc<str> {
self.name.clone()
}
pub fn instructions(&self) -> &Vec<Instruction> {
&self.instructions
}
pub fn register_count(&self) -> usize {
self.register_count
}
}

187
dvm-lib/src/vm/mod.rs Normal file
View File

@ -0,0 +1,187 @@
use crate::instruction::Instruction;
use crate::vm::constant::Constant;
use crate::vm::function::Function;
use crate::vm::value::Value;
use std::collections::HashMap;
use std::rc::Rc;
pub mod constant;
pub mod function;
pub mod value;
pub struct DvmContext {
functions: HashMap<Rc<str>, Function>,
constants: HashMap<Rc<str>, Constant>,
}
impl DvmContext {
pub fn new() -> Self {
Self {
functions: HashMap::new(),
constants: HashMap::new(),
}
}
pub fn add_function(&mut self, function: Function) {
self.functions.insert(function.name_owned(), function);
}
pub fn constants(&self) -> &HashMap<Rc<str>, Constant> {
&self.constants
}
pub fn add_constant(&mut self, constant: Constant) {
match &constant {
Constant::String(string_constant) => {
self.constants
.insert(string_constant.name_owned(), constant);
}
}
}
}
pub struct DvmState {
stack: Vec<Value>,
registers: Vec<Value>,
ip: usize,
fp: usize,
}
impl DvmState {
pub fn new() -> Self {
Self {
stack: vec![],
registers: vec![],
ip: 0,
fp: 0,
}
}
pub fn stack(&self) -> &Vec<Value> {
&self.stack
}
pub fn stack_mut(&mut self) -> &mut Vec<Value> {
&mut self.stack
}
pub fn registers(&self) -> &Vec<Value> {
&self.registers
}
pub fn registers_mut(&mut self) -> &mut Vec<Value> {
&mut self.registers
}
pub fn ensure_registers(&mut self, count: usize) {
self.registers.resize_with(count, Default::default);
}
pub fn ip(&self) -> usize {
self.ip
}
pub fn increment_ip(&mut self) {
self.ip += 1;
}
pub fn fp(&self) -> usize {
self.fp
}
pub fn set_fp(&mut self, fp: usize) {
self.fp = fp;
}
}
pub fn call(
context: &DvmContext,
state: &mut DvmState,
function_name: &str,
arguments: Vec<Value>,
) -> Option<Value> {
let function = context
.functions
.get(function_name)
.expect(&format!("Function {} not found", function_name));
let instructions = function.instructions();
state.ensure_registers(function.register_count());
// put each arg on the stack
for argument in arguments {
state.stack_mut().push(argument);
}
while state.ip() < instructions.len() {
let instruction = &instructions[state.ip()];
match instruction {
/* Move instructions */
Instruction::MoveRegister(source, destination) => {
// copy value from one register to another register
let value = state.registers()[*source].clone();
state.registers_mut()[*destination] = value;
}
Instruction::MoveInt(value, destination) => {
state.registers_mut()[*destination] = Value::Int(*value);
}
Instruction::MoveStackFrameOffset(offset, destination) => {
// copy a value offset from the current frame pointer (fp) to a register
let value_index = state
.fp()
.checked_add_signed(*offset)
.expect("Overflow when adding offset to fp");
let value = state.stack()[value_index].clone();
state.registers_mut()[*destination] = value;
}
/* Push instructions */
Instruction::PushRegister(source) => {
// copy a value from a register to the top of the stack
let value = state.registers()[*source].clone();
state.stack_mut().push(value);
}
Instruction::PushInt(value) => {
state.stack_mut().push(Value::Int(*value));
}
Instruction::PushStackFrameOffset(offset) => {
// copy a value from somewhere on the stack to the top of the stack
let value_index = state
.fp()
.checked_add_signed(*offset)
.expect("Overflow when adding offset to fp");
let value = state.stack()[value_index].clone();
state.stack_mut().push(value);
}
/* Invoke instructions */
Instruction::InvokePlatformStatic(function_name) => {
if function_name.as_ref() == "println" {
println!("{:?}", state.stack());
println!("{:?}", state.registers());
}
}
/* Load constant instructions */
Instruction::LoadStringConstant(constant_name, destination) => {
let constant = &context.constants()[constant_name];
match constant {
Constant::String(string_constant) => {
state.registers_mut()[*destination] =
Value::String(string_constant.content_owned());
}
}
}
/* Pop instructions */
Instruction::Pop(maybe_register) => {
let value = state.stack_mut().pop().unwrap();
if let Some(register) = maybe_register {
state.registers_mut()[*register] = value;
}
}
}
state.increment_ip();
}
None
}

14
dvm-lib/src/vm/value.rs Normal file
View File

@ -0,0 +1,14 @@
use std::rc::Rc;
#[derive(Clone, Debug)]
pub enum Value {
Int(i32),
String(Rc<str>),
Null,
}
impl Default for Value {
fn default() -> Value {
Value::Null
}
}

6
examples/hello.dm Normal file
View File

@ -0,0 +1,6 @@
fn println() end
fn main()
let x = "Hello, World!"
println(x)
end