From db675b5601a4274c844a13e1bb3633be6332ba7c Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Mon, 2 Mar 2026 21:16:18 -0600 Subject: [PATCH] Write README.md. --- README.md | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..ddacd19 --- /dev/null +++ b/README.md @@ -0,0 +1,224 @@ +# 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. +