# Deimos ### Brief History Deimos lang is a project I properly began in November 2024 while a student at the University of Wisconsin-Madison. I was learning Rust at the time and was inspired by its elegance and complexity. I had had ideas for creating a language as early as 2020, but did not yet have the computer science skills necessary for designing and implementing it. I was mainly inspired by the linguistic problems at hand in language design, especially grammar design and parsing. After completing an introduction-to-compilers course in spring 2025 (as well as my summer internship), I returned to work on the project in the fall of that year. I grew very frustrated with the state of the code base after a few months; my best software engineering instincts and skills were running up against classic problems: inflexibility, unmaintainability, and lack of proper testing. As such, I rebooted the project in February 2026 from scratch. The old project code all resides under `/src`, whereas one should look in the following directories for the new, hopefully better, code: - `/dm`: the main entry program; takes a script and runs it. - `/dm-std-lib`: the runtime standard library for Deimos (Rust side) - `/dmc-lib`: the compiler proper - `/dvm-lib`: the virtual machine Currently, one can run the `hello.dm` example (from the `/examples` directory) by running: `cargo run -p dm -- examples/hello.dm` You will see `Hello, World!` as well as `None` printed to the console. The `None` is returned by the Deimos Virtual Machine (dvm) upon completion; I'm planning on having the `call()` function (in `dvm-lib/src/vm/mod.rs`) return whatever the `main` (Deimos lang) function returns, possibly limiting it in a manner similar to Rust (like a `Result<(), Error>` kind of thing). ## Goals - Fast and embeddable, like Lua - Scalable, like Java/JVM, but without the complexity - Elegant syntax, like Ruby/Rust - Convenient, like Groovy - Mathematically inspired, like Haskell ## Grammar Not all of this is implemented yet, nor is this complete by itself. ``` compilation_unit := module_level_declaration* module_level_declaration := function | extern_function function := FN identifier L_PARENS parameter_list? R_PARENS statement* END extern_function := EXTERN FN identifier L_PARENS parameter_list? R_PARENS identifier := [a-zA-Z_]+ parameter_list := parameter ( COMMA parameter )* parameter := identifier COLON type_use type_use := identifier statement := let_statement | expression_statement let_statement := LET identifier EQUALS expression expression_statement := expression expression := integer_literal | string_literal | call integer_literal := [0-9]+ string_literal := DOUBLE_QUOTE .* DOUBLE_QUOTE call := expression L_PARENS expression_list? R_PARENS expression_list := expression ( COMMA expression )* ``` It's been enough to chew on for now. ## Type System Again, not all implemented, but these are my ideas: - Primitives - Byte, Short, Int, Long (might have unsigned versions, too) - Char, String (yes, String is primitive; everything UTF-8) - Boolean - Arrays - Interfaces and Classes - Closures/Function pointers - Higher-kinded types/Traits like Haskell/Scala ## Examples The following demonstrate some planned features for the language. Basic hello world: ``` fn main() println("Hello, World") end ``` Map some cli arguments using closures: ``` fn main(args: Array) args .map { it.toUppercase() } .each { println(it) } end ``` Interface, classes, and some other things: ``` int Animal fn name() -> String fn speak() -> Void end class Dog(name: String) : Animal fn name() -> String = self.name fn speak() -> String = 'Woof' end class Cat(name: String) : Animal fn name() -> String = self.name fn speak() -> String self.name == 'Good Kitty' ? 'Meow' : 'MEOW' end end fn main() let doggo = Dog('Bearo') let kitty = Cat('Willow') let animals = [doggo, kitty] // inferred to be List animals.each { println(it.speak) } // Woof | MEOW end ``` Higher-kinded types and enums: ``` trait Functor[T] fn map(f: fn (t: T) -> U) -> Self end enum Option Some(T), None end impl Functor[T] for Option fn map(f: fn: (t: T) -> U) -> Self if self is Some(t) then Some(f(t)) else None end end end fn main() let x = Some(42) let y = x.map { it / 2 } if y is Some(twentyOne) then println(twentyOne) // 21 else throw Exception() end end ``` Higher-kinded types can ask for static methods (sort of like Rust): ``` trait Default static default() -> Self end int Animal fn speak() -> String end class Dog(name: String) : Animal, Default static fn default() -> Self = Dog('Skyeo') fn speak() -> String = 'My name is ' + self.name end fn main() let sixteenDogs = Array::fill::(16) // or something like this sixteenDogs.each { println(it.speak) } // My name is Skeyo (x16) end ``` ## Implementation ### Compiler I'm currently using a handwritten lexer and parser. The grammar is embedded throughout the methods of the Parser implementation; once the grammar is more stable, I will use a proper predictive-parsing algorithm using a parse table (built from FIRST and FOLLOW sets), which should be not only faster but much cleaner. Each AST node has the following methods: - `gather_name_declarations`: The first phase of name analysis. This just gathers all declared names (functions, parameters, variables, etc.), since the plan is to allow entities that are declared later in the file to be in scope (unlike C, where forward declarations are required to do so). - `check_name_usages`: Make sure that usages of names refer to entities that are in scope. - `type_check`: Assert that types of parameters/arguments match, etc. - `assemble`: Convert the AST nodes to Deimos assembly code. The first three methods must all be called, in the order listed above, as they constitute distinct, dependent phases of semantic analysis. If there are no `Diagnostic`s at all from those phases, `assemble` can be safely called. ### Deimos Assembly Code The `asm_*` modules contain a low-level set of Rust enums/structs which basically map one-to-one to the actual code used by the virtual machine. I'm trying to design this language with optimizations and register allocation algorithms in mind. ### Virtual Machine The virtual machine is inspired by the Lua and Java virtual machines. It is register-based, and uses a stack for function calls, etc. It supports calling Rust code from Deimos code via a `PlatformFunction` API.