Compare commits
No commits in common. "61d7c66e17fa22a414b2a6f5c24a07470925e41f" and "6b855b8ebbd2f68a048408657f0b7820b0aa63f8" have entirely different histories.
61d7c66e17
...
6b855b8ebb
54
Cargo.lock
generated
54
Cargo.lock
generated
@ -86,9 +86,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.60"
|
||||
version = "4.5.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a"
|
||||
checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@ -96,9 +96,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.60"
|
||||
version = "4.5.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876"
|
||||
checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@ -108,9 +108,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.55"
|
||||
version = "4.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5"
|
||||
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
@ -120,9 +120,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "1.0.0"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
@ -135,17 +135,6 @@ 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"
|
||||
@ -197,7 +186,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"ast-generator",
|
||||
"clap",
|
||||
"codespan-reporting 0.12.0",
|
||||
"codespan-reporting",
|
||||
"cst-test-generator",
|
||||
"indoc",
|
||||
"log",
|
||||
@ -215,34 +204,9 @@ dependencies = [
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dm"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"codespan-reporting 0.13.1",
|
||||
"dm-std-lib",
|
||||
"dmc-lib",
|
||||
"dvm-lib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dm-std-lib"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"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"
|
||||
|
||||
@ -3,9 +3,9 @@ name = "deimos"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
#[[bin]]
|
||||
#name = "dm"
|
||||
#path = "src/bin/dvm/lib"
|
||||
[[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", "dm", "dm-std-lib", "dmc-lib", "dvm-lib"]
|
||||
members = ["ast-generator", "cst-test-generator", "dmc-lib"]
|
||||
|
||||
224
README.md
224
README.md
@ -1,224 +0,0 @@
|
||||
# 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<String>)
|
||||
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<Animal>
|
||||
animals.each { println(it.speak) } // Woof | MEOW
|
||||
end
|
||||
```
|
||||
|
||||
Higher-kinded types and enums:
|
||||
```
|
||||
trait Functor[T]
|
||||
fn <U> map(f: fn (t: T) -> U) -> Self<U>
|
||||
end
|
||||
|
||||
enum Option<T>
|
||||
Some(T),
|
||||
None
|
||||
end
|
||||
|
||||
impl<T> Functor[T] for Option<T>
|
||||
fn map(f: fn: (t: T) -> U) -> Self<U>
|
||||
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::<Dog>(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.
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
[package]
|
||||
name = "dm-std-lib"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
dvm-lib = { path = "../dvm-lib" }
|
||||
@ -1,61 +0,0 @@
|
||||
use dvm_lib::vm::value::Value;
|
||||
use dvm_lib::vm::{DvmContext, DvmState};
|
||||
use std::error::Error;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub fn add_all_std_core(context: &mut DvmContext) {
|
||||
let platform_functions = context.platform_functions_mut();
|
||||
platform_functions.insert(Rc::from("println"), std_core_println);
|
||||
}
|
||||
|
||||
struct StdCoreError {
|
||||
message: String,
|
||||
}
|
||||
|
||||
impl StdCoreError {
|
||||
pub fn new(message: &str) -> StdCoreError {
|
||||
Self {
|
||||
message: message.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for StdCoreError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for StdCoreError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for StdCoreError {}
|
||||
|
||||
pub fn std_core_println(
|
||||
_context: &DvmContext,
|
||||
_state: &DvmState,
|
||||
args: &[Value],
|
||||
) -> Result<Value, Box<dyn Error>> {
|
||||
let maybe_to_print = args.get(0);
|
||||
match maybe_to_print {
|
||||
None => Err(Box::new(StdCoreError::new("Missing to_print arg"))),
|
||||
Some(to_print) => {
|
||||
match to_print {
|
||||
Value::Int(i) => {
|
||||
println!("{}", i);
|
||||
}
|
||||
Value::String(s) => {
|
||||
println!("{}", s);
|
||||
}
|
||||
Value::Null => {
|
||||
println!("null");
|
||||
}
|
||||
}
|
||||
Ok(Value::Null)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
[package]
|
||||
name = "dm"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
dmc-lib = { path = "../dmc-lib" }
|
||||
dvm-lib = { path = "../dvm-lib" }
|
||||
dm-std-lib = { path = "../dm-std-lib" }
|
||||
clap = { version = "4.5.60", features = ["derive"] }
|
||||
codespan-reporting = "0.13.1"
|
||||
112
dm/src/main.rs
112
dm/src/main.rs
@ -1,112 +0,0 @@
|
||||
use clap::Parser;
|
||||
use codespan_reporting::diagnostic::Label;
|
||||
use codespan_reporting::files::SimpleFiles;
|
||||
use codespan_reporting::term;
|
||||
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
|
||||
use dm_std_lib::add_all_std_core;
|
||||
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 files: SimpleFiles<&str, &str> = SimpleFiles::new();
|
||||
let script_file_id = files.add(args.script.to_str().unwrap(), &input);
|
||||
|
||||
let parse_result = parse_compilation_unit(&input);
|
||||
let mut compilation_unit = match parse_result {
|
||||
Ok(compilation_unit) => compilation_unit,
|
||||
Err(diagnostics) => {
|
||||
check_and_report_diagnostics(&files, script_file_id, &diagnostics);
|
||||
unreachable!();
|
||||
}
|
||||
};
|
||||
|
||||
let mut symbol_table = SymbolTable::new();
|
||||
|
||||
let gather_names_diagnostics = compilation_unit.gather_declared_names(&mut symbol_table);
|
||||
check_and_report_diagnostics(&files, script_file_id, &gather_names_diagnostics);
|
||||
|
||||
let name_usages_diagnostics = compilation_unit.check_name_usages(&symbol_table);
|
||||
check_and_report_diagnostics(&files, script_file_id, &name_usages_diagnostics);
|
||||
|
||||
let type_check_diagnostics = compilation_unit.type_check(&symbol_table);
|
||||
check_and_report_diagnostics(&files, script_file_id, &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();
|
||||
|
||||
// add std::core fns
|
||||
add_all_std_core(&mut dvm_context);
|
||||
|
||||
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(
|
||||
files: &SimpleFiles<&str, &str>,
|
||||
script_file_id: usize,
|
||||
diagnostics: &[Diagnostic],
|
||||
) {
|
||||
if !diagnostics.is_empty() {
|
||||
let writer = StandardStream::stderr(ColorChoice::Always);
|
||||
let config = term::Config::default();
|
||||
for diagnostic in diagnostics {
|
||||
let csr_diagnostic = codespan_reporting::diagnostic::Diagnostic::error()
|
||||
.with_message(diagnostic.message())
|
||||
.with_label(Label::primary(
|
||||
script_file_id,
|
||||
diagnostic.start()..diagnostic.end(),
|
||||
));
|
||||
|
||||
term::emit_to_write_style(&mut writer.lock(), &config, files, &csr_diagnostic).unwrap();
|
||||
}
|
||||
if diagnostics.len() == 1 {
|
||||
eprintln!("There was 1 diagnostic. See above for more information.");
|
||||
} else {
|
||||
eprintln!(
|
||||
"There were {} diagnostics. See above for more information.",
|
||||
diagnostics.len()
|
||||
);
|
||||
}
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
@ -4,4 +4,3 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
dvm-lib = { path = "../dvm-lib" }
|
||||
|
||||
@ -2,19 +2,12 @@ use crate::asm::asm_instruction::AsmInstruction;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AsmBlock {
|
||||
name: String,
|
||||
id: usize,
|
||||
instructions: Vec<AsmInstruction>,
|
||||
}
|
||||
|
||||
impl AsmBlock {
|
||||
pub fn new(name: &str, instructions: Vec<AsmInstruction>) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
instructions,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn instructions(&self) -> &[AsmInstruction] {
|
||||
&self.instructions
|
||||
pub fn new(id: usize, instructions: Vec<AsmInstruction>) -> Self {
|
||||
Self { id, instructions }
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
use crate::asm::asm_block::AsmBlock;
|
||||
use crate::asm::asm_instruction::AsmInstruction;
|
||||
use dvm_lib::vm::function::Function;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AsmFunction {
|
||||
@ -15,15 +13,4 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,3 @@
|
||||
use dvm_lib::instruction::Instruction;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AsmInstruction {
|
||||
Move(Move),
|
||||
@ -10,25 +7,10 @@ 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(i32),
|
||||
IntegerLiteral(i64),
|
||||
Register(usize),
|
||||
StackFrameOffset(isize),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -44,18 +26,6 @@ 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)]
|
||||
@ -67,55 +37,29 @@ 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: Option<usize>,
|
||||
destination_register: usize,
|
||||
}
|
||||
|
||||
impl Pop {
|
||||
pub fn new(destination_register: usize) -> Self {
|
||||
Self {
|
||||
destination_register: Some(destination_register),
|
||||
destination_register,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
destination_register: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dvm(&self) -> Instruction {
|
||||
Instruction::Pop(self.destination_register)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InvokePlatformStatic {
|
||||
name: String,
|
||||
arg_count: usize,
|
||||
}
|
||||
|
||||
impl InvokePlatformStatic {
|
||||
pub fn new(name: &str, arg_count: usize) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
arg_count,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dvm(&self) -> Instruction {
|
||||
Instruction::InvokePlatformStatic(Rc::from(self.name.clone()), self.arg_count)
|
||||
pub fn new(name: &str) -> Self {
|
||||
Self { name: name.into() }
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,8 +76,4 @@ impl LoadConstant {
|
||||
destination_register,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dvm(&self) -> Instruction {
|
||||
Instruction::LoadStringConstant(Rc::from(self.name.clone()), self.destination_register)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,47 +1,3 @@
|
||||
pub mod asm_block;
|
||||
pub mod asm_function;
|
||||
pub mod asm_instruction;
|
||||
|
||||
#[cfg(test)]
|
||||
mod smoke_tests {
|
||||
use crate::asm::asm_function::AsmFunction;
|
||||
use crate::constants_table::ConstantsTable;
|
||||
use crate::parser::parse_compilation_unit;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
|
||||
fn assemble(src: &str) -> Vec<AsmFunction> {
|
||||
let parse_result = parse_compilation_unit(src);
|
||||
let mut compilation_unit = match parse_result {
|
||||
Ok(compilation_unit) => compilation_unit,
|
||||
Err(diagnostics) => {
|
||||
for diagnostic in diagnostics {
|
||||
eprintln!("{:?}", diagnostic);
|
||||
}
|
||||
panic!();
|
||||
}
|
||||
};
|
||||
let mut symbol_table = SymbolTable::new();
|
||||
compilation_unit.gather_declared_names(&mut symbol_table);
|
||||
compilation_unit.check_name_usages(&symbol_table);
|
||||
compilation_unit.type_check(&symbol_table);
|
||||
compilation_unit.assemble(&symbol_table, &mut ConstantsTable::new())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_statements() {
|
||||
let asm_functions = assemble(
|
||||
"
|
||||
extern fn println()
|
||||
fn main()
|
||||
let x = 42
|
||||
println(x)
|
||||
println(16)
|
||||
let y = \"Hello, World!\"
|
||||
println(y)
|
||||
end",
|
||||
);
|
||||
for asm_function in &asm_functions {
|
||||
println!("{:#?}", asm_function);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,114 +0,0 @@
|
||||
use crate::asm::asm_block::AsmBlock;
|
||||
use crate::asm::asm_function::AsmFunction;
|
||||
use crate::asm::asm_instruction::AsmInstruction;
|
||||
use crate::source_range::SourceRange;
|
||||
|
||||
pub struct AssembleContext {
|
||||
functions: Vec<AsmFunction>,
|
||||
current_function: Option<FunctionAssembler>,
|
||||
}
|
||||
|
||||
impl AssembleContext {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
functions: vec![],
|
||||
current_function: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn take_functions(&mut self) -> Vec<AsmFunction> {
|
||||
std::mem::take(&mut self.functions)
|
||||
}
|
||||
|
||||
pub fn new_function(&mut self, name: &str, source_range: &SourceRange) {
|
||||
self.current_function = Some(FunctionAssembler::new(name, source_range));
|
||||
}
|
||||
|
||||
pub fn complete_function(&mut self) {
|
||||
let mut function_assembler = self.current_function.take().unwrap();
|
||||
function_assembler.complete_block();
|
||||
let asm_function = function_assembler.result();
|
||||
self.functions.push(asm_function);
|
||||
}
|
||||
|
||||
pub fn new_block(&mut self, name: &str) {
|
||||
self.current_function.as_mut().unwrap().new_block(name);
|
||||
}
|
||||
|
||||
pub fn new_local_register(&mut self) -> usize {
|
||||
self.current_function.as_mut().unwrap().new_local_register()
|
||||
}
|
||||
|
||||
pub fn instruction(&mut self, to_add: AsmInstruction) {
|
||||
self.current_function.as_mut().unwrap().instruction(to_add);
|
||||
}
|
||||
}
|
||||
|
||||
struct FunctionAssembler {
|
||||
name: String,
|
||||
source_range: SourceRange,
|
||||
blocks: Vec<AsmBlock>,
|
||||
current_block: Option<BlockAssembler>,
|
||||
local_register_counter: usize,
|
||||
}
|
||||
|
||||
impl FunctionAssembler {
|
||||
fn new(name: &str, source_range: &SourceRange) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
source_range: source_range.clone(),
|
||||
blocks: vec![],
|
||||
current_block: None,
|
||||
local_register_counter: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn new_block(&mut self, name: &str) {
|
||||
let maybe_block_assembler = self.current_block.take();
|
||||
if let Some(mut block_assembler) = maybe_block_assembler {
|
||||
self.blocks.push(block_assembler.result());
|
||||
}
|
||||
self.current_block = Some(BlockAssembler::new(name))
|
||||
}
|
||||
|
||||
fn complete_block(&mut self) {
|
||||
let mut block_assembler = self.current_block.take().unwrap();
|
||||
self.blocks.push(block_assembler.result());
|
||||
}
|
||||
|
||||
fn result(&mut self) -> AsmFunction {
|
||||
AsmFunction::new(&self.name, std::mem::take(&mut self.blocks))
|
||||
}
|
||||
|
||||
fn new_local_register(&mut self) -> usize {
|
||||
let register = self.local_register_counter;
|
||||
self.local_register_counter += 1;
|
||||
register
|
||||
}
|
||||
|
||||
fn instruction(&mut self, to_add: AsmInstruction) {
|
||||
self.current_block.as_mut().unwrap().instruction(to_add);
|
||||
}
|
||||
}
|
||||
|
||||
struct BlockAssembler {
|
||||
name: String,
|
||||
instructions: Vec<AsmInstruction>,
|
||||
}
|
||||
|
||||
impl BlockAssembler {
|
||||
fn new(name: &str) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
instructions: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn instruction(&mut self, to_add: AsmInstruction) {
|
||||
self.instructions.push(to_add);
|
||||
}
|
||||
|
||||
fn result(&mut self) -> AsmBlock {
|
||||
AsmBlock::new(&self.name, std::mem::take(&mut self.instructions))
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,9 @@
|
||||
use crate::asm::asm_instruction::{
|
||||
AsmInstruction, InvokePlatformStatic, LoadConstant, Operand, Push,
|
||||
};
|
||||
use crate::ast::assemble_context::AssembleContext;
|
||||
use crate::ast::expression::Expression;
|
||||
use crate::constants_table::ConstantsTable;
|
||||
use crate::ast::function::FunctionLoweringContext;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::ir::ir_call::IrCall;
|
||||
use crate::ir::ir_expression::IrExpression;
|
||||
use crate::source_range::SourceRange;
|
||||
use crate::symbol::ExpressibleSymbol;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
use crate::type_info::TypeInfo;
|
||||
|
||||
@ -59,55 +56,14 @@ impl Call {
|
||||
}
|
||||
|
||||
// check that callee is callable
|
||||
let function_symbol = match self.callee.type_info() {
|
||||
TypeInfo::Function(function_symbol) => function_symbol,
|
||||
match self.callee.type_info() {
|
||||
TypeInfo::Function(_) => {}
|
||||
_ => {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
"Receiver is not callable",
|
||||
self.callee.source_range().start(),
|
||||
self.callee.source_range().end(),
|
||||
));
|
||||
return diagnostics;
|
||||
}
|
||||
};
|
||||
|
||||
// check arguments length
|
||||
let function_symbol_ref = function_symbol.borrow();
|
||||
let parameters = function_symbol_ref.parameters();
|
||||
if parameters.len() != self.arguments.len() {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
&format!(
|
||||
"Wrong number of arguments; expected {} but found {}",
|
||||
parameters.len(),
|
||||
self.arguments.len()
|
||||
),
|
||||
self.source_range.start(),
|
||||
self.source_range.end(),
|
||||
));
|
||||
}
|
||||
|
||||
if !diagnostics.is_empty() {
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
// check argument types
|
||||
for i in 0..parameters.len() {
|
||||
let parameter = ¶meters[i];
|
||||
let argument = &self.arguments[i];
|
||||
if !parameter
|
||||
.borrow()
|
||||
.type_info()
|
||||
.is_assignable_from(&argument.type_info())
|
||||
{
|
||||
diagnostics.push(Diagnostic::new(
|
||||
&format!(
|
||||
"Mismatched types; expected {} but found {}",
|
||||
parameter.borrow().type_info(),
|
||||
argument.type_info()
|
||||
),
|
||||
argument.source_range().start(),
|
||||
argument.source_range().end(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,79 +72,22 @@ impl Call {
|
||||
|
||||
pub fn type_info(&self) -> TypeInfo {
|
||||
match self.callee.type_info() {
|
||||
TypeInfo::Function(function_symbol) => function_symbol.borrow().return_type(),
|
||||
TypeInfo::Function(function_symbol) => function_symbol.return_type(),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assemble(
|
||||
&self,
|
||||
context: &mut AssembleContext,
|
||||
symbol_table: &SymbolTable,
|
||||
constants_table: &mut ConstantsTable,
|
||||
) {
|
||||
// push all args
|
||||
for argument in &self.arguments {
|
||||
match argument {
|
||||
Expression::Call(call) => {
|
||||
call.assemble(context, symbol_table, constants_table); // will leave return val on stack
|
||||
}
|
||||
Expression::IntegerLiteral(integer_literal) => {
|
||||
context.instruction(AsmInstruction::Push(Push::new(Operand::IntegerLiteral(
|
||||
integer_literal.value(),
|
||||
))));
|
||||
}
|
||||
Expression::String(string_literal) => {
|
||||
let name = constants_table.insert_string(string_literal.content());
|
||||
let temp_register = context.new_local_register();
|
||||
context.instruction(AsmInstruction::LoadConstant(LoadConstant::new(
|
||||
&name,
|
||||
temp_register,
|
||||
)));
|
||||
context.instruction(AsmInstruction::Push(Push::new(Operand::Register(
|
||||
temp_register,
|
||||
))));
|
||||
}
|
||||
Expression::Identifier(identifier) => match identifier.expressible_symbol() {
|
||||
ExpressibleSymbol::Function(_) => {
|
||||
panic!("Pushing function symbols not supported.")
|
||||
}
|
||||
ExpressibleSymbol::Parameter(parameter_symbol) => {
|
||||
context.instruction(AsmInstruction::Push(Push::new(
|
||||
Operand::StackFrameOffset(
|
||||
parameter_symbol.borrow().stack_frame_offset(),
|
||||
),
|
||||
)));
|
||||
}
|
||||
ExpressibleSymbol::Variable(variable_symbol) => {
|
||||
context.instruction(AsmInstruction::Push(Push::new(Operand::Register(
|
||||
variable_symbol.borrow().register(),
|
||||
))))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let function_symbol = match self.callee() {
|
||||
Expression::Identifier(identifier) => {
|
||||
let expressible_symbol = identifier.expressible_symbol();
|
||||
match expressible_symbol {
|
||||
ExpressibleSymbol::Function(function_symbol) => function_symbol.clone(),
|
||||
_ => panic!("Calling things other than functions not yet supported."),
|
||||
}
|
||||
}
|
||||
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) -> IrExpression {
|
||||
let function_name = match self.callee() {
|
||||
Expression::Identifier(identifier) => identifier.name(),
|
||||
_ => panic!("Calling things other than identifiers not yet supported."),
|
||||
};
|
||||
|
||||
let function_symbol = function_symbol.borrow();
|
||||
if function_symbol.is_platform() {
|
||||
let arg_count = function_symbol.parameters().len();
|
||||
context.instruction(AsmInstruction::InvokePlatformStatic(
|
||||
InvokePlatformStatic::new(function_symbol.name(), arg_count),
|
||||
));
|
||||
} else {
|
||||
todo!("non-platform invoke")
|
||||
}
|
||||
let arguments = self
|
||||
.arguments()
|
||||
.iter()
|
||||
.map(|arg| arg.lower_to_ir(context))
|
||||
.collect();
|
||||
IrExpression::Call(IrCall::new(function_name, arguments))
|
||||
}
|
||||
|
||||
pub fn source_range(&self) -> &SourceRange {
|
||||
|
||||
@ -1,28 +1,26 @@
|
||||
use crate::asm::asm_function::AsmFunction;
|
||||
use crate::ast::assemble_context::AssembleContext;
|
||||
use crate::ast::module_level_declaration::ModuleLevelDeclaration;
|
||||
use crate::constants_table::ConstantsTable;
|
||||
use crate::ast::function::Function;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::ir::Ir;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
|
||||
pub struct CompilationUnit {
|
||||
declarations: Vec<ModuleLevelDeclaration>,
|
||||
functions: Vec<Function>,
|
||||
}
|
||||
|
||||
impl CompilationUnit {
|
||||
pub fn new(declarations: Vec<ModuleLevelDeclaration>) -> Self {
|
||||
Self { declarations }
|
||||
pub fn new(functions: Vec<Function>) -> Self {
|
||||
Self { functions }
|
||||
}
|
||||
|
||||
pub fn declarations(&self) -> &[ModuleLevelDeclaration] {
|
||||
&self.declarations
|
||||
pub fn functions(&self) -> Vec<&Function> {
|
||||
self.functions.iter().collect()
|
||||
}
|
||||
|
||||
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
|
||||
let mut diagnostics = vec![];
|
||||
symbol_table.push_scope("compilation_unit_scope");
|
||||
for declaration in &mut self.declarations {
|
||||
diagnostics.append(&mut declaration.gather_declared_names(symbol_table));
|
||||
for function in &mut self.functions {
|
||||
diagnostics.append(&mut function.gather_declared_names(symbol_table));
|
||||
}
|
||||
symbol_table.pop_scope();
|
||||
diagnostics
|
||||
@ -30,29 +28,25 @@ impl CompilationUnit {
|
||||
|
||||
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||
let mut diagnostics = vec![];
|
||||
for declaration in &mut self.declarations {
|
||||
diagnostics.append(&mut declaration.check_name_usages(symbol_table));
|
||||
for function in &mut self.functions {
|
||||
diagnostics.append(&mut function.check_name_usages(symbol_table));
|
||||
}
|
||||
diagnostics
|
||||
}
|
||||
|
||||
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||
let mut diagnostics = vec![];
|
||||
for declaration in &mut self.declarations {
|
||||
diagnostics.append(&mut declaration.type_check(symbol_table));
|
||||
for function in &mut self.functions {
|
||||
diagnostics.append(&mut function.type_check(symbol_table));
|
||||
}
|
||||
diagnostics
|
||||
}
|
||||
|
||||
pub fn assemble(
|
||||
&self,
|
||||
symbol_table: &SymbolTable,
|
||||
constants_table: &mut ConstantsTable,
|
||||
) -> Vec<AsmFunction> {
|
||||
let mut context = AssembleContext::new();
|
||||
for declaration in &self.declarations {
|
||||
declaration.assemble(&mut context, symbol_table, constants_table);
|
||||
pub fn lower_to_ir(&self) -> Vec<Ir> {
|
||||
let mut irs = vec![];
|
||||
for function in &self.functions {
|
||||
irs.append(&mut function.lower_to_ir());
|
||||
}
|
||||
context.take_functions()
|
||||
irs
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
use crate::ast::call::Call;
|
||||
use crate::ast::function::FunctionLoweringContext;
|
||||
use crate::ast::identifier::Identifier;
|
||||
use crate::ast::integer_literal::IntegerLiteral;
|
||||
use crate::ast::string_literal::StringLiteral;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::ir::ir_expression::IrExpression;
|
||||
use crate::source_range::SourceRange;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
use crate::type_info::TypeInfo;
|
||||
@ -55,4 +57,13 @@ impl Expression {
|
||||
Expression::Identifier(identifier) => identifier.source_range(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) -> IrExpression {
|
||||
match self {
|
||||
Expression::Call(call) => call.lower_to_ir(context),
|
||||
Expression::IntegerLiteral(integer_literal) => integer_literal.lower_to_ir(context),
|
||||
Expression::String(string_literal) => string_literal.lower_to_ir(context),
|
||||
Expression::Identifier(identifier) => identifier.lower_to_ir(context),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
use crate::ast::assemble_context::AssembleContext;
|
||||
use crate::ast::expression::Expression;
|
||||
use crate::constants_table::ConstantsTable;
|
||||
use crate::ast::function::FunctionLoweringContext;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::ir::ir_expression::IrExpression;
|
||||
use crate::ir::ir_statement::IrStatement;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
|
||||
pub struct ExpressionStatement {
|
||||
@ -31,17 +32,21 @@ impl ExpressionStatement {
|
||||
self.expression.type_check(symbol_table)
|
||||
}
|
||||
|
||||
pub fn assemble(
|
||||
&self,
|
||||
context: &mut AssembleContext,
|
||||
symbol_table: &SymbolTable,
|
||||
constants_table: &mut ConstantsTable,
|
||||
) {
|
||||
match self.expression.as_ref() {
|
||||
Expression::Call(call) => {
|
||||
call.assemble(context, symbol_table, constants_table);
|
||||
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) {
|
||||
let ir_expression = self.expression.lower_to_ir(context);
|
||||
match ir_expression {
|
||||
IrExpression::Call(ir_call) => {
|
||||
context.add_statement(IrStatement::Call(ir_call));
|
||||
}
|
||||
IrExpression::Constant(ir_constant) => {
|
||||
unimplemented!()
|
||||
}
|
||||
IrExpression::IntegerLiteral(i) => {
|
||||
unimplemented!()
|
||||
}
|
||||
IrExpression::Variable(ir_variable) => {
|
||||
unimplemented!()
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,90 +0,0 @@
|
||||
use crate::ast::parameter::Parameter;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::source_range::SourceRange;
|
||||
use crate::symbol::FunctionSymbol;
|
||||
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct ExternFunction {
|
||||
declared_name: String,
|
||||
declared_name_source_range: SourceRange,
|
||||
parameters: Vec<Parameter>,
|
||||
}
|
||||
|
||||
impl ExternFunction {
|
||||
pub fn new(
|
||||
name: &str,
|
||||
declared_name_source_range: SourceRange,
|
||||
parameters: Vec<Parameter>,
|
||||
) -> ExternFunction {
|
||||
ExternFunction {
|
||||
declared_name: name.into(),
|
||||
declared_name_source_range,
|
||||
parameters,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn declared_name(&self) -> &str {
|
||||
&self.declared_name
|
||||
}
|
||||
|
||||
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
|
||||
let mut diagnostics = vec![];
|
||||
|
||||
let insert_result =
|
||||
symbol_table.insert_function_symbol(FunctionSymbol::new(&self.declared_name, true));
|
||||
|
||||
let mut maybe_function_symbol: Option<Rc<RefCell<FunctionSymbol>>> = None;
|
||||
match insert_result {
|
||||
Ok(function_symbol) => {
|
||||
maybe_function_symbol = Some(function_symbol);
|
||||
}
|
||||
Err(symbol_insert_error) => match symbol_insert_error {
|
||||
SymbolInsertError::AlreadyDeclared(already_declared) => {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
&format!(
|
||||
"Function {} already declared in current scope.",
|
||||
already_declared.name()
|
||||
),
|
||||
self.declared_name_source_range.start(),
|
||||
self.declared_name_source_range.end(),
|
||||
));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
symbol_table.push_scope(&format!("extern_function_scope({})", &self.declared_name));
|
||||
|
||||
let mut parameter_symbols = vec![];
|
||||
for parameter in &mut self.parameters {
|
||||
let parameter_result = parameter.gather_declared_names(symbol_table);
|
||||
match parameter_result {
|
||||
Ok(parameter_symbol) => {
|
||||
parameter_symbols.push(parameter_symbol);
|
||||
}
|
||||
Err(mut parameter_diagnostics) => {
|
||||
diagnostics.append(&mut parameter_diagnostics);
|
||||
}
|
||||
}
|
||||
}
|
||||
maybe_function_symbol
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
.set_parameters(parameter_symbols);
|
||||
|
||||
symbol_table.pop_scope();
|
||||
|
||||
diagnostics
|
||||
}
|
||||
|
||||
pub fn check_name_usages(&mut self, _symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||
// no-op (for now)
|
||||
vec![]
|
||||
}
|
||||
|
||||
pub fn type_check(&mut self, _symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||
// no-op (for now)
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,9 @@
|
||||
use crate::ast::assemble_context::AssembleContext;
|
||||
use crate::ast::statement::Statement;
|
||||
use crate::constants_table::ConstantsTable;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::ir::Ir;
|
||||
use crate::ir::ir_constant::IrConstant;
|
||||
use crate::ir::ir_function::IrFunction;
|
||||
use crate::ir::ir_statement::IrStatement;
|
||||
use crate::source_range::SourceRange;
|
||||
use crate::symbol::FunctionSymbol;
|
||||
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
||||
@ -36,8 +38,10 @@ impl Function {
|
||||
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
|
||||
let mut diagnostics = vec![];
|
||||
// insert function symbol
|
||||
let insert_result =
|
||||
symbol_table.insert_function_symbol(FunctionSymbol::new(self.declared_name(), false));
|
||||
let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new(
|
||||
self.declared_name(),
|
||||
&vec![], // todo
|
||||
));
|
||||
if let Err(symbol_insert_error) = insert_result {
|
||||
match symbol_insert_error {
|
||||
SymbolInsertError::AlreadyDeclared(already_declared) => {
|
||||
@ -52,9 +56,6 @@ impl Function {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// todo: parameters
|
||||
|
||||
symbol_table.push_scope(&format!("function_scope({})", self.declared_name()));
|
||||
for statement in &mut self.statements {
|
||||
diagnostics.append(&mut statement.gather_declared_names(symbol_table));
|
||||
@ -79,17 +80,63 @@ impl Function {
|
||||
diagnostics
|
||||
}
|
||||
|
||||
pub fn assemble(
|
||||
&self,
|
||||
context: &mut AssembleContext,
|
||||
symbol_table: &SymbolTable,
|
||||
constants_table: &mut ConstantsTable,
|
||||
) {
|
||||
context.new_function(&self.declared_name, &self.declared_name_source_range);
|
||||
context.new_block(&format!("{}_enter", self.declared_name));
|
||||
pub fn lower_to_ir(&self) -> Vec<Ir> {
|
||||
let mut context = FunctionLoweringContext::new();
|
||||
for statement in &self.statements {
|
||||
statement.assemble(context, symbol_table, constants_table);
|
||||
statement.lower_to_ir(&mut context);
|
||||
}
|
||||
context.complete_function();
|
||||
let mut irs = vec![];
|
||||
for constant in context.take_constants() {
|
||||
irs.push(Ir::Constant(constant));
|
||||
}
|
||||
let ir_function = IrFunction::new(&self.declared_name, context.take_statements());
|
||||
irs.push(Ir::Function(ir_function));
|
||||
irs
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FunctionLoweringContext {
|
||||
temp_variable_counter: usize,
|
||||
constant_counter: usize,
|
||||
constants: Vec<IrConstant>,
|
||||
statements: Vec<IrStatement>,
|
||||
}
|
||||
|
||||
impl FunctionLoweringContext {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
temp_variable_counter: 0,
|
||||
constant_counter: 0,
|
||||
constants: vec![],
|
||||
statements: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_temp_variable(&mut self) -> String {
|
||||
let temp_variable = format!("t_{}", self.temp_variable_counter);
|
||||
self.temp_variable_counter += 1;
|
||||
temp_variable
|
||||
}
|
||||
|
||||
pub fn next_constant_name(&mut self) -> String {
|
||||
let constant_name = format!("%const_{}", self.constant_counter);
|
||||
self.constant_counter += 1;
|
||||
constant_name
|
||||
}
|
||||
|
||||
pub fn add_constant(&mut self, constant: IrConstant) {
|
||||
self.constants.push(constant);
|
||||
}
|
||||
|
||||
pub fn take_constants(&mut self) -> Vec<IrConstant> {
|
||||
std::mem::take(&mut self.constants)
|
||||
}
|
||||
|
||||
pub fn add_statement(&mut self, statement: IrStatement) {
|
||||
self.statements.push(statement);
|
||||
}
|
||||
|
||||
pub fn take_statements(&mut self) -> Vec<IrStatement> {
|
||||
std::mem::take(&mut self.statements)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
use crate::ast::function::FunctionLoweringContext;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::ir::ir_expression::IrExpression;
|
||||
use crate::ir::ir_variable::IrVariable;
|
||||
use crate::source_range::SourceRange;
|
||||
use crate::symbol::ExpressibleSymbol;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
@ -53,17 +56,13 @@ impl Identifier {
|
||||
ExpressibleSymbol::Function(function_symbol) => {
|
||||
TypeInfo::Function(function_symbol.clone())
|
||||
}
|
||||
ExpressibleSymbol::Parameter(parameter_symbol) => {
|
||||
parameter_symbol.borrow().type_info().clone()
|
||||
}
|
||||
ExpressibleSymbol::Variable(variable_symbol) => {
|
||||
variable_symbol.borrow().type_info().clone()
|
||||
}
|
||||
ExpressibleSymbol::Parameter(parameter_symbol) => parameter_symbol.type_info().clone(),
|
||||
ExpressibleSymbol::Variable(variable_symbol) => variable_symbol.type_info().clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expressible_symbol(&self) -> &ExpressibleSymbol {
|
||||
self.expressible_symbol.as_ref().unwrap()
|
||||
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) -> IrExpression {
|
||||
IrExpression::Variable(IrVariable::new(self.name()))
|
||||
}
|
||||
|
||||
pub fn source_range(&self) -> &SourceRange {
|
||||
|
||||
@ -1,22 +1,28 @@
|
||||
use crate::ast::function::FunctionLoweringContext;
|
||||
use crate::ir::ir_expression::IrExpression;
|
||||
use crate::source_range::SourceRange;
|
||||
|
||||
pub struct IntegerLiteral {
|
||||
value: i32,
|
||||
value: i64,
|
||||
source_range: SourceRange,
|
||||
}
|
||||
|
||||
impl IntegerLiteral {
|
||||
pub fn new(value: i32, source_range: SourceRange) -> Self {
|
||||
pub fn new(value: i64, source_range: SourceRange) -> Self {
|
||||
Self {
|
||||
value,
|
||||
source_range,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(&self) -> i32 {
|
||||
pub fn value(&self) -> i64 {
|
||||
self.value
|
||||
}
|
||||
|
||||
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) -> IrExpression {
|
||||
IrExpression::IntegerLiteral(self.value)
|
||||
}
|
||||
|
||||
pub fn source_range(&self) -> &SourceRange {
|
||||
&self.source_range
|
||||
}
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
use crate::asm::asm_instruction::{AsmInstruction, LoadConstant, Move, Operand, Pop};
|
||||
use crate::ast::assemble_context::AssembleContext;
|
||||
use crate::ast::expression::Expression;
|
||||
use crate::constants_table::ConstantsTable;
|
||||
use crate::ast::function::FunctionLoweringContext;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::ir::ir_assign::IrAssign;
|
||||
use crate::ir::ir_statement::IrStatement;
|
||||
use crate::ir::ir_variable::IrVariable;
|
||||
use crate::source_range::SourceRange;
|
||||
use crate::symbol::{ExpressibleSymbol, VariableSymbol};
|
||||
use crate::symbol::VariableSymbol;
|
||||
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
||||
|
||||
pub struct LetStatement {
|
||||
declared_name: String,
|
||||
declared_name_source_range: SourceRange,
|
||||
initializer: Box<Expression>,
|
||||
scope_id: Option<usize>,
|
||||
}
|
||||
|
||||
impl LetStatement {
|
||||
@ -24,7 +24,6 @@ impl LetStatement {
|
||||
declared_name: declared_name.to_string(),
|
||||
declared_name_source_range,
|
||||
initializer: initializer.into(),
|
||||
scope_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,7 +60,6 @@ impl LetStatement {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.scope_id = Some(symbol_table.current_scope_id());
|
||||
diagnostics
|
||||
}
|
||||
|
||||
@ -75,61 +73,10 @@ impl LetStatement {
|
||||
diagnostics
|
||||
}
|
||||
|
||||
pub fn assemble(
|
||||
&self,
|
||||
context: &mut AssembleContext,
|
||||
symbol_table: &SymbolTable,
|
||||
constants_table: &mut ConstantsTable,
|
||||
) {
|
||||
let destination_register = context.new_local_register();
|
||||
|
||||
// save register to symbol
|
||||
let variable_symbol =
|
||||
symbol_table.get_variable_symbol(self.scope_id.unwrap(), self.declared_name());
|
||||
variable_symbol
|
||||
.borrow_mut()
|
||||
.set_register(destination_register);
|
||||
|
||||
match self.initializer() {
|
||||
Expression::Call(call) => {
|
||||
call.assemble(context, symbol_table, constants_table);
|
||||
context.instruction(AsmInstruction::Pop(Pop::new(destination_register)));
|
||||
}
|
||||
Expression::IntegerLiteral(integer_literal) => {
|
||||
context.instruction(AsmInstruction::Move(Move::new(
|
||||
Operand::IntegerLiteral(integer_literal.value()),
|
||||
destination_register,
|
||||
)));
|
||||
}
|
||||
Expression::String(string_literal) => {
|
||||
let name = constants_table.insert_string(string_literal.content());
|
||||
context.instruction(AsmInstruction::LoadConstant(LoadConstant::new(
|
||||
&name,
|
||||
destination_register,
|
||||
)));
|
||||
}
|
||||
Expression::Identifier(identifier) => {
|
||||
let expressible_symbol = identifier.expressible_symbol();
|
||||
match expressible_symbol {
|
||||
ExpressibleSymbol::Function(_) => {
|
||||
panic!("Moving functions to registers not yet supported");
|
||||
}
|
||||
ExpressibleSymbol::Parameter(parameter_symbol) => {
|
||||
context.instruction(AsmInstruction::Move(Move::new(
|
||||
Operand::StackFrameOffset(
|
||||
parameter_symbol.borrow().stack_frame_offset(),
|
||||
),
|
||||
destination_register,
|
||||
)));
|
||||
}
|
||||
ExpressibleSymbol::Variable(variable_symbol) => {
|
||||
context.instruction(AsmInstruction::Move(Move::new(
|
||||
Operand::Register(variable_symbol.borrow().register()),
|
||||
destination_register,
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) {
|
||||
let data = self.initializer.lower_to_ir(context);
|
||||
let destination = IrVariable::new(self.declared_name());
|
||||
let assign_statement = IrAssign::new(destination, data);
|
||||
context.add_statement(IrStatement::Assign(assign_statement));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,16 +1,83 @@
|
||||
pub mod assemble_context;
|
||||
pub mod call;
|
||||
pub mod compilation_unit;
|
||||
pub mod expression;
|
||||
pub mod expression_statement;
|
||||
pub mod extern_function;
|
||||
pub mod fqn;
|
||||
pub mod function;
|
||||
pub mod identifier;
|
||||
pub mod integer_literal;
|
||||
pub mod let_statement;
|
||||
pub mod module_level_declaration;
|
||||
pub mod parameter;
|
||||
pub mod statement;
|
||||
pub mod string_literal;
|
||||
pub mod type_use;
|
||||
|
||||
#[cfg(test)]
|
||||
mod name_tests {
|
||||
use crate::ir::Ir;
|
||||
use crate::ir::assemble_context::AssembleContext;
|
||||
use crate::parser::parse_compilation_unit;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
|
||||
#[test]
|
||||
fn smoke_screen() {
|
||||
let mut symbol_table = SymbolTable::new();
|
||||
let mut compilation_unit =
|
||||
parse_compilation_unit("fn println() end fn main() let x = 42 println(x) end");
|
||||
assert_eq!(
|
||||
compilation_unit
|
||||
.gather_declared_names(&mut symbol_table)
|
||||
.len(),
|
||||
0
|
||||
);
|
||||
assert_eq!(compilation_unit.check_name_usages(&symbol_table).len(), 0);
|
||||
let irs = compilation_unit.lower_to_ir();
|
||||
for ir in &irs {
|
||||
println!("{:#?}", ir);
|
||||
}
|
||||
for ir in &irs {
|
||||
match ir {
|
||||
Ir::Function(ir_function) => {
|
||||
let asm_function = ir_function.assemble(&mut AssembleContext::new());
|
||||
println!("{:#?}", asm_function);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hello_world() {
|
||||
let mut symbol_table = SymbolTable::new();
|
||||
let mut compilation_unit =
|
||||
parse_compilation_unit("fn println() end fn main() println(\"Hello, World!\") end");
|
||||
compilation_unit.gather_declared_names(&mut symbol_table);
|
||||
compilation_unit.check_name_usages(&symbol_table);
|
||||
compilation_unit.type_check(&symbol_table);
|
||||
let irs = compilation_unit.lower_to_ir();
|
||||
for ir in &irs {
|
||||
println!("{:#?}", ir);
|
||||
}
|
||||
for ir in &irs {
|
||||
match ir {
|
||||
Ir::Function(ir_function) => {
|
||||
let asm_function = ir_function.assemble(&mut AssembleContext::new());
|
||||
println!("{:#?}", asm_function);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_some_diagnostics() {
|
||||
let mut symbol_table = SymbolTable::new();
|
||||
let mut compilation_unit = parse_compilation_unit("fn main() notDefined(uhOh) end");
|
||||
assert_eq!(
|
||||
compilation_unit
|
||||
.gather_declared_names(&mut symbol_table)
|
||||
.len(),
|
||||
0
|
||||
);
|
||||
let name_usage_diagnostics = compilation_unit.check_name_usages(&symbol_table);
|
||||
assert_eq!(name_usage_diagnostics.len(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,58 +0,0 @@
|
||||
use crate::ast::assemble_context::AssembleContext;
|
||||
use crate::ast::extern_function::ExternFunction;
|
||||
use crate::ast::function::Function;
|
||||
use crate::constants_table::ConstantsTable;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
|
||||
pub enum ModuleLevelDeclaration {
|
||||
Function(Function),
|
||||
ExternFunction(ExternFunction),
|
||||
}
|
||||
|
||||
impl ModuleLevelDeclaration {
|
||||
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
|
||||
match self {
|
||||
ModuleLevelDeclaration::Function(function) => {
|
||||
function.gather_declared_names(symbol_table)
|
||||
}
|
||||
ModuleLevelDeclaration::ExternFunction(extern_function) => {
|
||||
extern_function.gather_declared_names(symbol_table)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||
match self {
|
||||
ModuleLevelDeclaration::Function(function) => function.check_name_usages(symbol_table),
|
||||
ModuleLevelDeclaration::ExternFunction(extern_function) => {
|
||||
extern_function.check_name_usages(symbol_table)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||
match self {
|
||||
ModuleLevelDeclaration::Function(function) => function.type_check(symbol_table),
|
||||
ModuleLevelDeclaration::ExternFunction(extern_function) => {
|
||||
extern_function.type_check(symbol_table)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assemble(
|
||||
&self,
|
||||
context: &mut AssembleContext,
|
||||
symbol_table: &SymbolTable,
|
||||
constants_table: &mut ConstantsTable,
|
||||
) {
|
||||
match self {
|
||||
ModuleLevelDeclaration::Function(function) => {
|
||||
function.assemble(context, symbol_table, constants_table)
|
||||
}
|
||||
ModuleLevelDeclaration::ExternFunction(_) => {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,48 +0,0 @@
|
||||
use crate::ast::type_use::TypeUse;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::source_range::SourceRange;
|
||||
use crate::symbol::ParameterSymbol;
|
||||
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
||||
use crate::type_info::TypeInfo;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct Parameter {
|
||||
declared_name: String,
|
||||
declared_name_source_range: SourceRange,
|
||||
type_use: TypeUse,
|
||||
}
|
||||
|
||||
impl Parameter {
|
||||
pub fn new(
|
||||
declared_name: &str,
|
||||
declared_name_source_range: SourceRange,
|
||||
type_use: TypeUse,
|
||||
) -> Self {
|
||||
Self {
|
||||
declared_name: declared_name.into(),
|
||||
declared_name_source_range,
|
||||
type_use,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gather_declared_names(
|
||||
&mut self,
|
||||
symbol_table: &mut SymbolTable,
|
||||
) -> Result<Rc<RefCell<ParameterSymbol>>, Vec<Diagnostic>> {
|
||||
let insert_result = symbol_table.insert_parameter_symbol(ParameterSymbol::new(
|
||||
&self.declared_name,
|
||||
TypeInfo::from_declared_name(self.type_use.declared_name()),
|
||||
));
|
||||
match insert_result {
|
||||
Ok(parameter_symbol) => Ok(parameter_symbol),
|
||||
Err(symbol_insert_error) => match symbol_insert_error {
|
||||
SymbolInsertError::AlreadyDeclared(already_declared) => Err(vec![Diagnostic::new(
|
||||
&format!("Parameter {} already declared.", already_declared.name()),
|
||||
self.declared_name_source_range.start(),
|
||||
self.declared_name_source_range.end(),
|
||||
)]),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
use crate::ast::assemble_context::AssembleContext;
|
||||
use crate::ast::expression_statement::ExpressionStatement;
|
||||
use crate::ast::function::FunctionLoweringContext;
|
||||
use crate::ast::let_statement::LetStatement;
|
||||
use crate::constants_table::ConstantsTable;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
|
||||
@ -38,18 +37,13 @@ impl Statement {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assemble(
|
||||
&self,
|
||||
context: &mut AssembleContext,
|
||||
symbol_table: &SymbolTable,
|
||||
constants_table: &mut ConstantsTable,
|
||||
) {
|
||||
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) {
|
||||
match self {
|
||||
Statement::Let(let_statement) => {
|
||||
let_statement.assemble(context, symbol_table, constants_table);
|
||||
let_statement.lower_to_ir(context);
|
||||
}
|
||||
Statement::Expression(expression_statement) => {
|
||||
expression_statement.assemble(context, symbol_table, constants_table);
|
||||
Statement::Expression(expression) => {
|
||||
expression.lower_to_ir(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
use crate::ast::function::FunctionLoweringContext;
|
||||
use crate::ir::ir_constant::{IrConstant, IrStringConstant};
|
||||
use crate::ir::ir_expression::IrExpression;
|
||||
use crate::source_range::SourceRange;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct StringLiteral {
|
||||
content: String,
|
||||
@ -17,6 +21,15 @@ impl StringLiteral {
|
||||
&self.content
|
||||
}
|
||||
|
||||
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) -> IrExpression {
|
||||
let ir_string_constant = Rc::new(IrStringConstant::new(
|
||||
self.content(),
|
||||
&context.next_constant_name(),
|
||||
));
|
||||
context.add_constant(IrConstant::String(ir_string_constant.clone()));
|
||||
IrExpression::Constant(IrConstant::String(ir_string_constant))
|
||||
}
|
||||
|
||||
pub fn source_range(&self) -> &SourceRange {
|
||||
&self.source_range
|
||||
}
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
use crate::source_range::SourceRange;
|
||||
|
||||
pub struct TypeUse {
|
||||
declared_name: String,
|
||||
declared_name_source_range: SourceRange,
|
||||
}
|
||||
|
||||
impl TypeUse {
|
||||
pub fn new(declared_name: &str, declared_name_source_range: SourceRange) -> Self {
|
||||
Self {
|
||||
declared_name: declared_name.into(),
|
||||
declared_name_source_range,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn declared_name(&self) -> &str {
|
||||
&self.declared_name
|
||||
}
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct ConstantsTable {
|
||||
string_counter: usize,
|
||||
strings_to_names: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl ConstantsTable {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
string_counter: 0,
|
||||
strings_to_names: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_string(&mut self, s: &str) -> String {
|
||||
let name = format!("s_{}", self.string_counter);
|
||||
self.string_counter += 1;
|
||||
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
|
||||
}
|
||||
}
|
||||
7
dmc-lib/src/ir/assemble_context.rs
Normal file
7
dmc-lib/src/ir/assemble_context.rs
Normal file
@ -0,0 +1,7 @@
|
||||
pub struct AssembleContext {}
|
||||
|
||||
impl AssembleContext {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
51
dmc-lib/src/ir/ir_assign.rs
Normal file
51
dmc-lib/src/ir/ir_assign.rs
Normal file
@ -0,0 +1,51 @@
|
||||
use crate::asm::asm_instruction::{AsmInstruction, LoadConstant, Move, Operand, Pop};
|
||||
use crate::ir::assemble_context::AssembleContext;
|
||||
use crate::ir::ir_constant::IrConstant;
|
||||
use crate::ir::ir_expression::IrExpression;
|
||||
use crate::ir::ir_variable::IrVariable;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IrAssign {
|
||||
destination: Box<IrVariable>,
|
||||
value: Box<IrExpression>,
|
||||
}
|
||||
|
||||
impl IrAssign {
|
||||
pub fn new(destination: IrVariable, value: IrExpression) -> Self {
|
||||
Self {
|
||||
destination: destination.into(),
|
||||
value: value.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assemble(&self, context: &mut AssembleContext) -> Vec<AsmInstruction> {
|
||||
let mut instructions = vec![];
|
||||
match self.value.as_ref() {
|
||||
IrExpression::Call(ir_call) => {
|
||||
instructions.append(&mut ir_call.assemble(context));
|
||||
instructions.push(AsmInstruction::Pop(Pop::new(self.destination.register())));
|
||||
}
|
||||
IrExpression::Constant(ir_constant) => match ir_constant {
|
||||
IrConstant::String(ir_string_constant) => {
|
||||
instructions.push(AsmInstruction::LoadConstant(LoadConstant::new(
|
||||
ir_string_constant.name(),
|
||||
self.destination.register(),
|
||||
)));
|
||||
}
|
||||
},
|
||||
IrExpression::IntegerLiteral(i) => {
|
||||
instructions.push(AsmInstruction::Move(Move::new(
|
||||
Operand::IntegerLiteral(*i),
|
||||
self.destination.register(),
|
||||
)));
|
||||
}
|
||||
IrExpression::Variable(ir_variable) => {
|
||||
instructions.push(AsmInstruction::Move(Move::new(
|
||||
Operand::Register(ir_variable.register()),
|
||||
self.destination.register(),
|
||||
)))
|
||||
}
|
||||
};
|
||||
instructions
|
||||
}
|
||||
}
|
||||
53
dmc-lib/src/ir/ir_call.rs
Normal file
53
dmc-lib/src/ir/ir_call.rs
Normal file
@ -0,0 +1,53 @@
|
||||
use crate::asm::asm_instruction::{
|
||||
AsmInstruction, InvokePlatformStatic, LoadConstant, Operand, Push,
|
||||
};
|
||||
use crate::ir::assemble_context::AssembleContext;
|
||||
use crate::ir::ir_constant::IrConstant;
|
||||
use crate::ir::ir_expression::IrExpression;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IrCall {
|
||||
name: String,
|
||||
arguments: Vec<IrExpression>,
|
||||
}
|
||||
|
||||
impl IrCall {
|
||||
pub fn new(name: &str, arguments: Vec<IrExpression>) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
arguments,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assemble(&self, context: &mut AssembleContext) -> Vec<AsmInstruction> {
|
||||
let mut instructions = vec![];
|
||||
for argument in &self.arguments {
|
||||
match argument {
|
||||
IrExpression::Call(ir_call) => {
|
||||
instructions.append(&mut ir_call.assemble(context));
|
||||
}
|
||||
IrExpression::Constant(ir_constant) => {
|
||||
match ir_constant {
|
||||
IrConstant::String(string_constant) => {
|
||||
instructions.push(AsmInstruction::LoadConstant(LoadConstant::new(
|
||||
string_constant.name(),
|
||||
0,
|
||||
)));
|
||||
}
|
||||
}
|
||||
instructions.push(AsmInstruction::Push(Push::new(Operand::Register(0))))
|
||||
}
|
||||
IrExpression::IntegerLiteral(i) => {
|
||||
instructions.push(AsmInstruction::Push(Push::new(Operand::IntegerLiteral(*i))));
|
||||
}
|
||||
IrExpression::Variable(ir_variable) => instructions.push(AsmInstruction::Push(
|
||||
Push::new(Operand::Register(ir_variable.register())),
|
||||
)),
|
||||
}
|
||||
}
|
||||
instructions.push(AsmInstruction::InvokePlatformStatic(
|
||||
InvokePlatformStatic::new(&self.name),
|
||||
));
|
||||
instructions
|
||||
}
|
||||
}
|
||||
29
dmc-lib/src/ir/ir_constant.rs
Normal file
29
dmc-lib/src/ir/ir_constant.rs
Normal file
@ -0,0 +1,29 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum IrConstant {
|
||||
String(Rc<IrStringConstant>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IrStringConstant {
|
||||
value: String,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl IrStringConstant {
|
||||
pub fn new(value: &str, name: &str) -> Self {
|
||||
Self {
|
||||
value: value.into(),
|
||||
name: name.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(&self) -> &str {
|
||||
&self.value
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
11
dmc-lib/src/ir/ir_expression.rs
Normal file
11
dmc-lib/src/ir/ir_expression.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use crate::ir::ir_call::IrCall;
|
||||
use crate::ir::ir_constant::IrConstant;
|
||||
use crate::ir::ir_variable::IrVariable;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum IrExpression {
|
||||
Call(IrCall),
|
||||
Constant(IrConstant),
|
||||
IntegerLiteral(i64),
|
||||
Variable(IrVariable),
|
||||
}
|
||||
28
dmc-lib/src/ir/ir_function.rs
Normal file
28
dmc-lib/src/ir/ir_function.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use crate::asm::asm_block::AsmBlock;
|
||||
use crate::asm::asm_function::AsmFunction;
|
||||
use crate::ir::assemble_context::AssembleContext;
|
||||
use crate::ir::ir_statement::IrStatement;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IrFunction {
|
||||
name: String,
|
||||
statements: Vec<IrStatement>,
|
||||
}
|
||||
|
||||
impl IrFunction {
|
||||
pub fn new(name: &str, statements: Vec<IrStatement>) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
statements,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assemble(&self, context: &mut AssembleContext) -> AsmFunction {
|
||||
let mut instructions = vec![];
|
||||
for statement in &self.statements {
|
||||
instructions.append(&mut statement.assemble(context));
|
||||
}
|
||||
let blocks = vec![AsmBlock::new(0, instructions)];
|
||||
AsmFunction::new(&self.name, blocks)
|
||||
}
|
||||
}
|
||||
19
dmc-lib/src/ir/ir_statement.rs
Normal file
19
dmc-lib/src/ir/ir_statement.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use crate::asm::asm_instruction::AsmInstruction;
|
||||
use crate::ir::assemble_context::AssembleContext;
|
||||
use crate::ir::ir_assign::IrAssign;
|
||||
use crate::ir::ir_call::IrCall;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum IrStatement {
|
||||
Assign(IrAssign),
|
||||
Call(IrCall),
|
||||
}
|
||||
|
||||
impl IrStatement {
|
||||
pub fn assemble(&self, context: &mut AssembleContext) -> Vec<AsmInstruction> {
|
||||
match self {
|
||||
IrStatement::Assign(ir_assign) => ir_assign.assemble(context),
|
||||
IrStatement::Call(ir_call) => ir_call.assemble(context),
|
||||
}
|
||||
}
|
||||
}
|
||||
22
dmc-lib/src/ir/ir_variable.rs
Normal file
22
dmc-lib/src/ir/ir_variable.rs
Normal file
@ -0,0 +1,22 @@
|
||||
#[derive(Debug)]
|
||||
pub struct IrVariable {
|
||||
name: String,
|
||||
register: Option<usize>,
|
||||
}
|
||||
|
||||
impl IrVariable {
|
||||
pub fn new(name: &str) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
register: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_register(&mut self, register: usize) {
|
||||
self.register = Some(register);
|
||||
}
|
||||
|
||||
pub fn register(&self) -> usize {
|
||||
0
|
||||
}
|
||||
}
|
||||
17
dmc-lib/src/ir/mod.rs
Normal file
17
dmc-lib/src/ir/mod.rs
Normal file
@ -0,0 +1,17 @@
|
||||
use crate::ir::ir_constant::IrConstant;
|
||||
use crate::ir::ir_function::IrFunction;
|
||||
|
||||
pub mod assemble_context;
|
||||
pub mod ir_assign;
|
||||
pub mod ir_call;
|
||||
pub mod ir_constant;
|
||||
pub mod ir_expression;
|
||||
pub mod ir_function;
|
||||
pub mod ir_statement;
|
||||
pub mod ir_variable;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Ir {
|
||||
Function(IrFunction),
|
||||
Constant(IrConstant),
|
||||
}
|
||||
@ -27,10 +27,8 @@ impl<'a> Lexer<'a> {
|
||||
let maybe_chunk = self.input.get(self.position..);
|
||||
if maybe_chunk.is_none() {
|
||||
return None;
|
||||
}
|
||||
chunk = maybe_chunk.unwrap();
|
||||
if chunk.is_empty() {
|
||||
return None;
|
||||
} else {
|
||||
chunk = maybe_chunk.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,10 +42,6 @@ impl<'a> Lexer<'a> {
|
||||
)
|
||||
} else if chunk.starts_with("=") {
|
||||
Token::new(self.position, self.position + 1, TokenKind::Equals)
|
||||
} else if chunk.starts_with(",") {
|
||||
Token::new(self.position, self.position + 1, TokenKind::Comma)
|
||||
} else if chunk.starts_with(":") {
|
||||
Token::new(self.position, self.position + 1, TokenKind::Colon)
|
||||
} else {
|
||||
// more than one char token
|
||||
if chunk.starts_with(|c: char| c.is_ascii_digit()) {
|
||||
@ -93,7 +87,6 @@ impl<'a> Lexer<'a> {
|
||||
"fn" => TokenKind::Fn,
|
||||
"end" => TokenKind::End,
|
||||
"let" => TokenKind::Let,
|
||||
"extern" => TokenKind::Extern,
|
||||
_ => TokenKind::Identifier,
|
||||
};
|
||||
Token::new(self.position, self.position + prefix.len(), token_kind)
|
||||
@ -161,17 +154,4 @@ mod tests {
|
||||
assert_next(&mut lexer, TokenKind::End, 3);
|
||||
assert_eq!(lexer.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blank_after_last_token_returns_none() {
|
||||
let mut lexer = Lexer::new("fn ");
|
||||
assert_next(&mut lexer, TokenKind::Fn, 2);
|
||||
assert_eq!(lexer.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extern_returned() {
|
||||
let mut lexer = Lexer::new("extern");
|
||||
assert_next(&mut lexer, TokenKind::Extern, 6);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
pub mod asm;
|
||||
pub mod ast;
|
||||
pub mod constants_table;
|
||||
pub mod diagnostic;
|
||||
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;
|
||||
mod asm;
|
||||
mod ast;
|
||||
mod diagnostic;
|
||||
mod ir;
|
||||
mod lexer;
|
||||
mod parser;
|
||||
mod scope;
|
||||
mod source_range;
|
||||
mod symbol;
|
||||
mod symbol_table;
|
||||
mod token;
|
||||
mod type_info;
|
||||
|
||||
@ -2,23 +2,18 @@ use crate::ast::call::Call;
|
||||
use crate::ast::compilation_unit::CompilationUnit;
|
||||
use crate::ast::expression::Expression;
|
||||
use crate::ast::expression_statement::ExpressionStatement;
|
||||
use crate::ast::extern_function::ExternFunction;
|
||||
use crate::ast::function::Function;
|
||||
use crate::ast::identifier::Identifier;
|
||||
use crate::ast::integer_literal::IntegerLiteral;
|
||||
use crate::ast::let_statement::LetStatement;
|
||||
use crate::ast::module_level_declaration::ModuleLevelDeclaration;
|
||||
use crate::ast::parameter::Parameter;
|
||||
use crate::ast::statement::Statement;
|
||||
use crate::ast::string_literal::StringLiteral;
|
||||
use crate::ast::type_use::TypeUse;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::lexer::Lexer;
|
||||
use crate::source_range::SourceRange;
|
||||
use crate::token::{Token, TokenKind};
|
||||
use std::str::FromStr;
|
||||
|
||||
pub fn parse_compilation_unit(input: &str) -> Result<CompilationUnit, Vec<Diagnostic>> {
|
||||
pub fn parse_compilation_unit(input: &str) -> CompilationUnit {
|
||||
let mut parser = Parser::new(input);
|
||||
parser.compilation_unit()
|
||||
}
|
||||
@ -40,22 +35,6 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn advance_until(&mut self, token_kinds: &[TokenKind]) {
|
||||
while self.current.is_some() {
|
||||
self.advance();
|
||||
match &self.current {
|
||||
None => {
|
||||
// reached eoi
|
||||
}
|
||||
Some(current) => {
|
||||
if token_kinds.contains(¤t.kind()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn advance(&mut self) {
|
||||
if self.lookahead.is_some() {
|
||||
// we've advanced at least once
|
||||
@ -69,9 +48,6 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
},
|
||||
}
|
||||
} else if self.lookahead.is_none() && self.current.is_some() {
|
||||
// we're on the last token
|
||||
self.current = None;
|
||||
} else {
|
||||
// we've not yet advanced, so fetch both
|
||||
// current
|
||||
@ -101,24 +77,22 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_advance(&mut self, token_kind: TokenKind) -> Result<Token, Vec<Diagnostic>> {
|
||||
fn expect_advance(&mut self, token_kind: TokenKind) -> Token {
|
||||
match self.current.take() {
|
||||
None => Err(vec![Diagnostic::new(
|
||||
&format!("Expected {:?} but found end-of-input.", token_kind),
|
||||
self.input.len(),
|
||||
self.input.len(),
|
||||
)]),
|
||||
None => {
|
||||
panic!("Expected {:?} but found end of input", token_kind);
|
||||
}
|
||||
Some(token) => {
|
||||
if token.kind() == token_kind {
|
||||
self.advance();
|
||||
Ok(token)
|
||||
token
|
||||
} else {
|
||||
self.advance_until(&[token_kind]);
|
||||
Err(vec![Diagnostic::new(
|
||||
&format!("Expected {:?} but found {:?}", token_kind, token.kind()),
|
||||
token.start(),
|
||||
token.end(),
|
||||
)])
|
||||
panic!(
|
||||
"Expected {:?} but found {:?} at {}",
|
||||
token_kind,
|
||||
token.kind(),
|
||||
token.start()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -126,7 +100,7 @@ impl<'a> Parser<'a> {
|
||||
|
||||
fn peek_current(&self, token_kind: TokenKind) -> bool {
|
||||
match &self.current {
|
||||
None => panic!("Unexpected end of input."),
|
||||
None => false,
|
||||
Some(token) => token.kind() == token_kind,
|
||||
}
|
||||
}
|
||||
@ -140,13 +114,6 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn peek_lookahead(&self, token_kind: TokenKind) -> bool {
|
||||
match &self.lookahead {
|
||||
None => panic!("Unexpected end of input."),
|
||||
Some(token) => token.kind() == token_kind,
|
||||
}
|
||||
}
|
||||
|
||||
fn sample_input(&self, start: usize, end: usize) -> &'a str {
|
||||
&self.input[start..end]
|
||||
}
|
||||
@ -155,226 +122,71 @@ impl<'a> Parser<'a> {
|
||||
self.sample_input(token.start(), token.end())
|
||||
}
|
||||
|
||||
pub fn compilation_unit(&mut self) -> Result<CompilationUnit, Vec<Diagnostic>> {
|
||||
let mut declarations = vec![];
|
||||
let mut diagnostics = vec![];
|
||||
self.advance(); // get started
|
||||
pub fn compilation_unit(&mut self) -> CompilationUnit {
|
||||
let mut functions = vec![];
|
||||
self.advance();
|
||||
while self.current.is_some() {
|
||||
let current = self.get_current();
|
||||
match current.kind() {
|
||||
TokenKind::Fn | TokenKind::Extern => {
|
||||
let declaration_result = self.module_level_declaration();
|
||||
match declaration_result {
|
||||
Ok(declaration) => declarations.push(declaration),
|
||||
Err(mut declaration_diagnostics) => {
|
||||
diagnostics.append(&mut declaration_diagnostics)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
&format!(
|
||||
"Expected any of: {:?}; found {:?}",
|
||||
[TokenKind::Fn, TokenKind::Extern],
|
||||
current.kind()
|
||||
),
|
||||
current.start(),
|
||||
current.end(),
|
||||
));
|
||||
self.advance_until(&[TokenKind::Fn, TokenKind::Extern]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if diagnostics.is_empty() {
|
||||
Ok(CompilationUnit::new(declarations))
|
||||
} else {
|
||||
Err(diagnostics)
|
||||
functions.push(self.function());
|
||||
}
|
||||
CompilationUnit::new(functions)
|
||||
}
|
||||
|
||||
fn module_level_declaration(&mut self) -> Result<ModuleLevelDeclaration, Vec<Diagnostic>> {
|
||||
let current = self.get_current();
|
||||
match current.kind() {
|
||||
TokenKind::Fn => {
|
||||
let function_result = self.function();
|
||||
match function_result {
|
||||
Ok(function) => Ok(ModuleLevelDeclaration::Function(function)),
|
||||
Err(function_diagnostics) => Err(function_diagnostics),
|
||||
}
|
||||
}
|
||||
TokenKind::Extern => {
|
||||
let extern_function_result = self.extern_function();
|
||||
match extern_function_result {
|
||||
Ok(extern_function) => {
|
||||
Ok(ModuleLevelDeclaration::ExternFunction(extern_function))
|
||||
}
|
||||
Err(extern_function_diagnostics) => Err(extern_function_diagnostics),
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn function(&mut self) -> Result<Function, Vec<Diagnostic>> {
|
||||
self.expect_advance(TokenKind::Fn)?;
|
||||
let identifier_token = self.expect_advance(TokenKind::Identifier)?;
|
||||
self.expect_advance(TokenKind::LeftParentheses)?;
|
||||
fn function(&mut self) -> Function {
|
||||
self.expect_advance(TokenKind::Fn);
|
||||
let identifier_token = self.expect_advance(TokenKind::Identifier);
|
||||
self.expect_advance(TokenKind::LeftParentheses);
|
||||
// add params
|
||||
self.expect_advance(TokenKind::RightParentheses)?;
|
||||
self.expect_advance(TokenKind::RightParentheses);
|
||||
let mut statements = vec![];
|
||||
let mut diagnostics = vec![];
|
||||
while self.current.is_some() && !self.peek_current(TokenKind::End) {
|
||||
let statement_result = self.statement();
|
||||
match statement_result {
|
||||
Ok(statement) => {
|
||||
statements.push(statement);
|
||||
}
|
||||
Err(mut statement_diagnostics) => {
|
||||
diagnostics.append(&mut statement_diagnostics);
|
||||
}
|
||||
}
|
||||
while !self.peek_current(TokenKind::End) {
|
||||
statements.push(self.statement());
|
||||
}
|
||||
|
||||
// if we're missing "end", append it to the other statement diagnostics
|
||||
let end_result = self.expect_advance(TokenKind::End);
|
||||
match end_result {
|
||||
Err(mut end_diagnostics) => {
|
||||
diagnostics.append(&mut end_diagnostics);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if diagnostics.is_empty() {
|
||||
Ok(Function::new(
|
||||
self.token_text(&identifier_token),
|
||||
SourceRange::new(identifier_token.start(), identifier_token.end()),
|
||||
statements,
|
||||
))
|
||||
} else {
|
||||
Err(diagnostics)
|
||||
}
|
||||
}
|
||||
|
||||
fn extern_function(&mut self) -> Result<ExternFunction, Vec<Diagnostic>> {
|
||||
self.expect_advance(TokenKind::Extern)?;
|
||||
self.expect_advance(TokenKind::Fn)?;
|
||||
let identifier_token = self.expect_advance(TokenKind::Identifier)?;
|
||||
self.expect_advance(TokenKind::LeftParentheses)?;
|
||||
|
||||
let mut diagnostics = vec![];
|
||||
|
||||
let mut maybe_parameters: Option<Vec<Parameter>> = None;
|
||||
let params_result = self.parameter_list();
|
||||
match params_result {
|
||||
Ok(parameters) => {
|
||||
maybe_parameters = Some(parameters);
|
||||
}
|
||||
Err(mut parameter_list_diagnostics) => {
|
||||
diagnostics.append(&mut parameter_list_diagnostics);
|
||||
}
|
||||
}
|
||||
|
||||
let right_parentheses_result = self.expect_advance(TokenKind::RightParentheses);
|
||||
match right_parentheses_result {
|
||||
Err(mut right_parentheses_diagnostics) => {
|
||||
diagnostics.append(&mut right_parentheses_diagnostics);
|
||||
}
|
||||
Ok(_) => {}
|
||||
}
|
||||
|
||||
if diagnostics.is_empty() {
|
||||
Ok(ExternFunction::new(
|
||||
self.token_text(&identifier_token),
|
||||
SourceRange::new(identifier_token.start(), identifier_token.end()),
|
||||
maybe_parameters.unwrap(),
|
||||
))
|
||||
} else {
|
||||
Err(diagnostics)
|
||||
}
|
||||
}
|
||||
|
||||
fn parameter_list(&mut self) -> Result<Vec<Parameter>, Vec<Diagnostic>> {
|
||||
let mut parameters = vec![];
|
||||
let mut diagnostics = vec![];
|
||||
while self.current.is_some() && self.peek_current(TokenKind::Identifier) {
|
||||
let parameter_result = self.parameter();
|
||||
match parameter_result {
|
||||
Ok(parameter) => {
|
||||
parameters.push(parameter);
|
||||
}
|
||||
Err(mut parameter_diagnostics) => {
|
||||
diagnostics.append(&mut parameter_diagnostics);
|
||||
}
|
||||
}
|
||||
if self.lookahead.is_some() && self.peek_lookahead(TokenKind::Comma) {
|
||||
self.advance();
|
||||
}
|
||||
}
|
||||
if diagnostics.is_empty() {
|
||||
Ok(parameters)
|
||||
} else {
|
||||
Err(diagnostics)
|
||||
}
|
||||
}
|
||||
|
||||
fn parameter(&mut self) -> Result<Parameter, Vec<Diagnostic>> {
|
||||
let identifier_token = self.expect_advance(TokenKind::Identifier)?;
|
||||
self.expect_advance(TokenKind::Colon)?;
|
||||
let type_use = self.type_use()?;
|
||||
Ok(Parameter::new(
|
||||
self.expect_advance(TokenKind::End);
|
||||
Function::new(
|
||||
self.token_text(&identifier_token),
|
||||
SourceRange::new(identifier_token.start(), identifier_token.end()),
|
||||
type_use,
|
||||
))
|
||||
statements,
|
||||
)
|
||||
}
|
||||
|
||||
fn type_use(&mut self) -> Result<TypeUse, Vec<Diagnostic>> {
|
||||
let identifier_token = self.expect_advance(TokenKind::Identifier)?;
|
||||
Ok(TypeUse::new(
|
||||
self.token_text(&identifier_token),
|
||||
SourceRange::new(identifier_token.start(), identifier_token.end()),
|
||||
))
|
||||
}
|
||||
|
||||
fn statement(&mut self) -> Result<Statement, Vec<Diagnostic>> {
|
||||
fn statement(&mut self) -> Statement {
|
||||
let current = self.get_current();
|
||||
match current.kind() {
|
||||
TokenKind::Let => Ok(Statement::Let(self.let_statement()?)),
|
||||
_ => Ok(Statement::Expression(self.expression_statement()?)),
|
||||
TokenKind::Let => self.let_statement(),
|
||||
_ => self.expression_statement(),
|
||||
}
|
||||
}
|
||||
|
||||
fn let_statement(&mut self) -> Result<LetStatement, Vec<Diagnostic>> {
|
||||
self.expect_advance(TokenKind::Let)?;
|
||||
let identifier = self.expect_advance(TokenKind::Identifier)?;
|
||||
self.expect_advance(TokenKind::Equals)?;
|
||||
let expression = self.expression()?;
|
||||
Ok(LetStatement::new(
|
||||
fn let_statement(&mut self) -> Statement {
|
||||
self.expect_advance(TokenKind::Let);
|
||||
let identifier = self.expect_advance(TokenKind::Identifier);
|
||||
self.expect_advance(TokenKind::Equals);
|
||||
let expression = self.expression();
|
||||
Statement::Let(LetStatement::new(
|
||||
self.token_text(&identifier),
|
||||
SourceRange::new(identifier.start(), identifier.end()),
|
||||
expression,
|
||||
))
|
||||
}
|
||||
|
||||
fn expression_statement(&mut self) -> Result<ExpressionStatement, Vec<Diagnostic>> {
|
||||
Ok(ExpressionStatement::new(self.expression()?))
|
||||
fn expression_statement(&mut self) -> Statement {
|
||||
Statement::Expression(ExpressionStatement::new(self.expression()))
|
||||
}
|
||||
|
||||
fn expression(&mut self) -> Result<Expression, Vec<Diagnostic>> {
|
||||
let current = self.get_current().clone(); // I don't love this clone
|
||||
let mut diagnostics = vec![];
|
||||
let mut expression = match current.kind() {
|
||||
fn expression(&mut self) -> Expression {
|
||||
let current = self.get_current();
|
||||
let mut result = match current.kind() {
|
||||
TokenKind::IntegerLiteral => {
|
||||
let raw = self.token_text(¤t);
|
||||
let raw = self.token_text(current);
|
||||
let source_range = SourceRange::new(current.start(), current.end());
|
||||
self.advance();
|
||||
Expression::IntegerLiteral(IntegerLiteral::new(
|
||||
i32::from_str(raw).unwrap(),
|
||||
i64::from_str(raw).unwrap(),
|
||||
source_range,
|
||||
))
|
||||
}
|
||||
TokenKind::String => {
|
||||
let with_quotes = self.token_text(¤t);
|
||||
let with_quotes = self.token_text(current);
|
||||
let source_range = SourceRange::new(current.start(), current.end());
|
||||
self.advance();
|
||||
Expression::String(StringLiteral::new(
|
||||
@ -383,73 +195,37 @@ impl<'a> Parser<'a> {
|
||||
))
|
||||
}
|
||||
TokenKind::Identifier => {
|
||||
let declared_name = self.token_text(¤t);
|
||||
let declared_name = self.token_text(current);
|
||||
let source_range = SourceRange::new(current.start(), current.end());
|
||||
self.advance();
|
||||
Expression::Identifier(Identifier::new(declared_name, source_range))
|
||||
}
|
||||
_ => {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
&format!(
|
||||
"Expected any of {:?} but found {:?}",
|
||||
[
|
||||
TokenKind::IntegerLiteral,
|
||||
TokenKind::String,
|
||||
TokenKind::Identifier
|
||||
],
|
||||
current.kind()
|
||||
),
|
||||
current.start(),
|
||||
current.end(),
|
||||
));
|
||||
|
||||
self.advance_until(&[
|
||||
TokenKind::IntegerLiteral,
|
||||
TokenKind::String,
|
||||
TokenKind::Identifier,
|
||||
]);
|
||||
if self.current.is_some() {
|
||||
let try_again_result = self.expression();
|
||||
match try_again_result {
|
||||
Ok(expression) => expression,
|
||||
Err(mut try_again_diagnostics) => {
|
||||
diagnostics.append(&mut try_again_diagnostics);
|
||||
return Err(diagnostics);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(diagnostics);
|
||||
}
|
||||
}
|
||||
_ => panic!("Unexpected token {:?}", current.kind()),
|
||||
};
|
||||
|
||||
// postfixes
|
||||
while let Some(current) = &self.current {
|
||||
match current.kind() {
|
||||
TokenKind::LeftParentheses => {
|
||||
expression = Expression::Call(self.call(expression)?);
|
||||
result = Expression::Call(self.call(result));
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
if diagnostics.is_empty() {
|
||||
Ok(expression)
|
||||
} else {
|
||||
Err(diagnostics)
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn call(&mut self, callee: Expression) -> Result<Call, Vec<Diagnostic>> {
|
||||
self.expect_advance(TokenKind::LeftParentheses)?;
|
||||
fn call(&mut self, callee: Expression) -> Call {
|
||||
self.expect_advance(TokenKind::LeftParentheses);
|
||||
let mut arguments = vec![];
|
||||
while self.current.is_some() && !self.peek_current(TokenKind::RightParentheses) {
|
||||
arguments.push(self.expression()?);
|
||||
while !self.peek_current(TokenKind::RightParentheses) {
|
||||
arguments.push(self.expression());
|
||||
}
|
||||
let right_parentheses_token = self.expect_advance(TokenKind::RightParentheses)?;
|
||||
let right_parentheses_token = self.expect_advance(TokenKind::RightParentheses);
|
||||
let source_range =
|
||||
SourceRange::new(callee.source_range().start(), right_parentheses_token.end());
|
||||
Ok(Call::new(callee, arguments, source_range))
|
||||
Call::new(callee, arguments, source_range)
|
||||
}
|
||||
}
|
||||
|
||||
@ -457,76 +233,17 @@ impl<'a> Parser<'a> {
|
||||
mod smoke_tests {
|
||||
use super::*;
|
||||
|
||||
fn smoke_test(input: &str) {
|
||||
let parse_result = parse_compilation_unit(input);
|
||||
match parse_result {
|
||||
Ok(_) => {}
|
||||
Err(diagnostics) => {
|
||||
eprintln!("{:#?}", diagnostics);
|
||||
panic!("There were diagnostics during parsing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn forty_two() {
|
||||
smoke_test("fn main() 42 end");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chained_calls() {
|
||||
smoke_test("fn main() getCl()() end");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extern_fn_with_param() {
|
||||
smoke_test("extern fn println(message: Any)")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod concrete_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parses_extern_fn() {
|
||||
let parse_result = parse_compilation_unit("extern fn println()");
|
||||
let compilation_unit = match parse_result {
|
||||
Ok(compilation_unit) => compilation_unit,
|
||||
Err(diagnostics) => {
|
||||
for diagnostic in diagnostics {
|
||||
eprintln!("{:?}", diagnostic);
|
||||
}
|
||||
panic!();
|
||||
}
|
||||
};
|
||||
let declarations = compilation_unit.declarations();
|
||||
assert_eq!(declarations.len(), 1);
|
||||
let extern_function = match &declarations[0] {
|
||||
ModuleLevelDeclaration::ExternFunction(extern_function) => extern_function,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(extern_function.declared_name(), "println");
|
||||
parse_compilation_unit("fn main() 42 end");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hello_world() {
|
||||
let parse_result = parse_compilation_unit("fn main() println(\"Hello, World!\") end");
|
||||
let compilation_unit = match parse_result {
|
||||
Ok(compilation_unit) => compilation_unit,
|
||||
Err(diagnostics) => {
|
||||
for diagnostic in &diagnostics {
|
||||
eprintln!("{:?}", diagnostic)
|
||||
}
|
||||
panic!()
|
||||
}
|
||||
};
|
||||
let declarations = compilation_unit.declarations();
|
||||
assert_eq!(declarations.len(), 1);
|
||||
let function = match &declarations[0] {
|
||||
ModuleLevelDeclaration::Function(function) => function,
|
||||
_ => panic!(),
|
||||
};
|
||||
let compilation_unit = parse_compilation_unit("fn main() println(\"Hello, World!\") end");
|
||||
let functions = compilation_unit.functions();
|
||||
assert_eq!(functions.len(), 1);
|
||||
let function = functions[0];
|
||||
assert_eq!(function.declared_name(), "main");
|
||||
let statements = function.statements();
|
||||
assert_eq!(statements.len(), 1);
|
||||
@ -555,36 +272,9 @@ mod concrete_tests {
|
||||
panic!("Expected expression");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod parse_failure_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn lone_end() {
|
||||
let parse_result = parse_compilation_unit("end");
|
||||
match parse_result {
|
||||
Err(diagnostics) => {
|
||||
assert_eq!(diagnostics.len(), 1);
|
||||
for diagnostic in &diagnostics {
|
||||
println!("{:?}", diagnostic)
|
||||
}
|
||||
}
|
||||
Ok(_) => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_ends() {
|
||||
let parse_result = parse_compilation_unit("end end");
|
||||
match parse_result {
|
||||
Err(diagnostics) => {
|
||||
// Should only have an error on the first end, since we advance until we find a
|
||||
// token we can recover from (fn or extern)
|
||||
assert_eq!(diagnostics.len(), 1);
|
||||
}
|
||||
Ok(_) => panic!(),
|
||||
}
|
||||
fn chained_calls() {
|
||||
parse_compilation_unit("fn main() getCl()() end");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,14 +1,13 @@
|
||||
use crate::symbol::{FunctionSymbol, ParameterSymbol, VariableSymbol};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct Scope {
|
||||
debug_name: String,
|
||||
parent_id: Option<usize>,
|
||||
function_symbols: HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>>,
|
||||
parameter_symbols: HashMap<Rc<str>, Rc<RefCell<ParameterSymbol>>>,
|
||||
variable_symbols: HashMap<Rc<str>, Rc<RefCell<VariableSymbol>>>,
|
||||
function_symbols: HashMap<Rc<str>, Rc<FunctionSymbol>>,
|
||||
parameter_symbols: HashMap<Rc<str>, Rc<ParameterSymbol>>,
|
||||
variable_symbols: HashMap<Rc<str>, Rc<VariableSymbol>>,
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
@ -22,27 +21,27 @@ impl Scope {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn function_symbols(&self) -> &HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>> {
|
||||
pub fn function_symbols(&self) -> &HashMap<Rc<str>, Rc<FunctionSymbol>> {
|
||||
&self.function_symbols
|
||||
}
|
||||
|
||||
pub fn function_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>> {
|
||||
pub fn function_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<FunctionSymbol>> {
|
||||
&mut self.function_symbols
|
||||
}
|
||||
|
||||
pub fn parameter_symbols(&self) -> &HashMap<Rc<str>, Rc<RefCell<ParameterSymbol>>> {
|
||||
pub fn parameter_symbols(&self) -> &HashMap<Rc<str>, Rc<ParameterSymbol>> {
|
||||
&self.parameter_symbols
|
||||
}
|
||||
|
||||
pub fn parameter_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<RefCell<ParameterSymbol>>> {
|
||||
pub fn parameter_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<ParameterSymbol>> {
|
||||
&mut self.parameter_symbols
|
||||
}
|
||||
|
||||
pub fn variable_symbols(&self) -> &HashMap<Rc<str>, Rc<RefCell<VariableSymbol>>> {
|
||||
pub fn variable_symbols(&self) -> &HashMap<Rc<str>, Rc<VariableSymbol>> {
|
||||
&self.variable_symbols
|
||||
}
|
||||
|
||||
pub fn variable_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<RefCell<VariableSymbol>>> {
|
||||
pub fn variable_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<VariableSymbol>> {
|
||||
&mut self.variable_symbols
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SourceRange {
|
||||
start: usize,
|
||||
end: usize,
|
||||
|
||||
@ -1,19 +1,16 @@
|
||||
use crate::type_info::TypeInfo;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct FunctionSymbol {
|
||||
name: Rc<str>,
|
||||
is_platform: bool,
|
||||
parameters: Option<Vec<Rc<RefCell<ParameterSymbol>>>>,
|
||||
parameters: Vec<Rc<ParameterSymbol>>,
|
||||
}
|
||||
|
||||
impl FunctionSymbol {
|
||||
pub fn new(name: &str, is_platform: bool) -> Self {
|
||||
pub fn new(name: &str, parameters: &[Rc<ParameterSymbol>]) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
is_platform,
|
||||
parameters: None,
|
||||
parameters: parameters.into(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,27 +22,18 @@ impl FunctionSymbol {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
pub fn set_parameters(&mut self, parameters: Vec<Rc<RefCell<ParameterSymbol>>>) {
|
||||
self.parameters = Some(parameters);
|
||||
}
|
||||
|
||||
pub fn parameters(&self) -> &[Rc<RefCell<ParameterSymbol>>] {
|
||||
self.parameters.as_ref().unwrap()
|
||||
pub fn parameters(&self) -> &[Rc<ParameterSymbol>] {
|
||||
&self.parameters
|
||||
}
|
||||
|
||||
pub fn return_type(&self) -> TypeInfo {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn is_platform(&self) -> bool {
|
||||
self.is_platform
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ParameterSymbol {
|
||||
name: Rc<str>,
|
||||
type_info: TypeInfo,
|
||||
stack_frame_offset: Option<isize>,
|
||||
}
|
||||
|
||||
impl ParameterSymbol {
|
||||
@ -53,7 +41,6 @@ impl ParameterSymbol {
|
||||
Self {
|
||||
name: name.into(),
|
||||
type_info,
|
||||
stack_frame_offset: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,20 +55,11 @@ impl ParameterSymbol {
|
||||
pub fn type_info(&self) -> &TypeInfo {
|
||||
&self.type_info
|
||||
}
|
||||
|
||||
pub fn set_stack_frame_offset(&mut self, offset: isize) {
|
||||
self.stack_frame_offset = Some(offset);
|
||||
}
|
||||
|
||||
pub fn stack_frame_offset(&self) -> isize {
|
||||
self.stack_frame_offset.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VariableSymbol {
|
||||
name: Rc<str>,
|
||||
type_info: TypeInfo,
|
||||
register: Option<usize>,
|
||||
}
|
||||
|
||||
impl VariableSymbol {
|
||||
@ -89,7 +67,6 @@ impl VariableSymbol {
|
||||
Self {
|
||||
name: name.into(),
|
||||
type_info,
|
||||
register: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,18 +81,10 @@ impl VariableSymbol {
|
||||
pub fn type_info(&self) -> &TypeInfo {
|
||||
&self.type_info
|
||||
}
|
||||
|
||||
pub fn set_register(&mut self, register: usize) {
|
||||
self.register = Some(register);
|
||||
}
|
||||
|
||||
pub fn register(&self) -> usize {
|
||||
self.register.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ExpressibleSymbol {
|
||||
Function(Rc<RefCell<FunctionSymbol>>),
|
||||
Parameter(Rc<RefCell<ParameterSymbol>>),
|
||||
Variable(Rc<RefCell<VariableSymbol>>),
|
||||
Function(Rc<FunctionSymbol>),
|
||||
Parameter(Rc<ParameterSymbol>),
|
||||
Variable(Rc<VariableSymbol>),
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
use crate::scope::Scope;
|
||||
use crate::symbol::{ExpressibleSymbol, FunctionSymbol, ParameterSymbol, VariableSymbol};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct SymbolTable {
|
||||
@ -51,35 +50,31 @@ impl SymbolTable {
|
||||
pub fn insert_function_symbol(
|
||||
&mut self,
|
||||
function_symbol: FunctionSymbol,
|
||||
) -> Result<Rc<RefCell<FunctionSymbol>>, SymbolInsertError> {
|
||||
) -> Result<(), SymbolInsertError> {
|
||||
if self.current_scope_has_name(function_symbol.name()) {
|
||||
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
|
||||
function_symbol.name(),
|
||||
)));
|
||||
}
|
||||
let name = function_symbol.name_owned();
|
||||
let as_rc = Rc::new(RefCell::new(function_symbol));
|
||||
|
||||
self.current_scope_mut()
|
||||
.function_symbols_mut()
|
||||
.insert(name, as_rc.clone());
|
||||
Ok(as_rc)
|
||||
.insert(function_symbol.name_owned(), Rc::new(function_symbol));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert_parameter_symbol(
|
||||
&mut self,
|
||||
parameter_symbol: ParameterSymbol,
|
||||
) -> Result<Rc<RefCell<ParameterSymbol>>, SymbolInsertError> {
|
||||
) -> Result<(), SymbolInsertError> {
|
||||
if self.current_scope_has_name(parameter_symbol.name()) {
|
||||
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
|
||||
parameter_symbol.name(),
|
||||
)));
|
||||
}
|
||||
let as_rc = Rc::new(RefCell::new(parameter_symbol));
|
||||
self.current_scope_mut()
|
||||
.parameter_symbols_mut()
|
||||
.insert(as_rc.borrow().name_owned(), as_rc.clone());
|
||||
Ok(as_rc)
|
||||
.insert(parameter_symbol.name_owned(), Rc::new(parameter_symbol));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert_variable_symbol(
|
||||
@ -91,10 +86,9 @@ impl SymbolTable {
|
||||
variable_symbol.name(),
|
||||
)));
|
||||
}
|
||||
self.current_scope_mut().variable_symbols_mut().insert(
|
||||
variable_symbol.name_owned(),
|
||||
Rc::new(RefCell::new(variable_symbol)),
|
||||
);
|
||||
self.current_scope_mut()
|
||||
.variable_symbols_mut()
|
||||
.insert(variable_symbol.name_owned(), Rc::new(variable_symbol));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -131,11 +125,6 @@ impl SymbolTable {
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_variable_symbol(&self, scope_id: usize, name: &str) -> Rc<RefCell<VariableSymbol>> {
|
||||
let scope = self.scopes.get(scope_id).unwrap();
|
||||
scope.variable_symbols().get(name).unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub enum SymbolInsertError {
|
||||
|
||||
@ -35,7 +35,4 @@ pub enum TokenKind {
|
||||
IntegerLiteral,
|
||||
LongLiteral,
|
||||
String,
|
||||
Extern,
|
||||
Comma,
|
||||
Colon,
|
||||
}
|
||||
|
||||
@ -1,56 +1,9 @@
|
||||
use crate::symbol::FunctionSymbol;
|
||||
use std::cell::RefCell;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum TypeInfo {
|
||||
Any,
|
||||
Integer,
|
||||
String,
|
||||
Function(Rc<RefCell<FunctionSymbol>>),
|
||||
}
|
||||
|
||||
impl Display for TypeInfo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
TypeInfo::Any => write!(f, "Any"),
|
||||
TypeInfo::Integer => write!(f, "Int"),
|
||||
TypeInfo::String => write!(f, "String"),
|
||||
TypeInfo::Function(function_symbol) => {
|
||||
write!(f, "fn(")?;
|
||||
for parameter in function_symbol.borrow().parameters() {
|
||||
parameter.borrow().type_info().fmt(f)?;
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeInfo {
|
||||
// This is very naive but works for now
|
||||
pub fn from_declared_name(declared_name: &str) -> Self {
|
||||
match declared_name {
|
||||
"Any" => TypeInfo::Any,
|
||||
"Int" => TypeInfo::Integer,
|
||||
"String" => TypeInfo::String,
|
||||
_ => panic!("Unknown type: {}", declared_name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_assignable_from(&self, other: &TypeInfo) -> bool {
|
||||
match self {
|
||||
TypeInfo::Any => true,
|
||||
TypeInfo::Integer => {
|
||||
matches!(other, TypeInfo::Integer)
|
||||
}
|
||||
TypeInfo::String => {
|
||||
matches!(other, TypeInfo::String)
|
||||
}
|
||||
TypeInfo::Function(_) => {
|
||||
unimplemented!("Type matching on Functions not yet supported.")
|
||||
}
|
||||
}
|
||||
}
|
||||
Function(Rc<FunctionSymbol>),
|
||||
}
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
[package]
|
||||
name = "dvm-lib"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
@ -1,22 +0,0 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
pub type Register = usize;
|
||||
pub type ConstantName = Rc<str>;
|
||||
pub type FunctionName = Rc<str>;
|
||||
pub type ArgCount = usize;
|
||||
|
||||
pub enum Instruction {
|
||||
MoveRegister(Register, Register),
|
||||
MoveInt(i32, Register),
|
||||
MoveStackFrameOffset(isize, Register),
|
||||
|
||||
PushRegister(Register),
|
||||
PushInt(i32),
|
||||
PushStackFrameOffset(isize),
|
||||
|
||||
InvokePlatformStatic(FunctionName, ArgCount),
|
||||
|
||||
LoadStringConstant(ConstantName, Register),
|
||||
|
||||
Pop(Option<Register>),
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
pub mod instruction;
|
||||
pub mod platform_function;
|
||||
pub mod vm;
|
||||
@ -1,6 +0,0 @@
|
||||
use crate::vm::value::Value;
|
||||
use crate::vm::{DvmContext, DvmState};
|
||||
use std::error::Error;
|
||||
|
||||
pub type PlatformFunction =
|
||||
fn(context: &DvmContext, state: &DvmState, args: &[Value]) -> Result<Value, Box<dyn Error>>;
|
||||
@ -1,31 +0,0 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -1,224 +0,0 @@
|
||||
use crate::instruction::Instruction;
|
||||
use crate::platform_function::PlatformFunction;
|
||||
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>,
|
||||
platform_functions: HashMap<Rc<str>, PlatformFunction>,
|
||||
constants: HashMap<Rc<str>, Constant>,
|
||||
}
|
||||
|
||||
impl DvmContext {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
functions: HashMap::new(),
|
||||
platform_functions: HashMap::new(),
|
||||
constants: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn functions(&self) -> &HashMap<Rc<str>, Function> {
|
||||
&self.functions
|
||||
}
|
||||
|
||||
pub fn functions_mut(&mut self) -> &mut HashMap<Rc<str>, Function> {
|
||||
&mut self.functions
|
||||
}
|
||||
|
||||
pub fn platform_functions(&self) -> &HashMap<Rc<str>, PlatformFunction> {
|
||||
&self.platform_functions
|
||||
}
|
||||
|
||||
pub fn platform_functions_mut(&mut self) -> &mut HashMap<Rc<str>, PlatformFunction> {
|
||||
&mut self.platform_functions
|
||||
}
|
||||
|
||||
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, arg_count) => {
|
||||
let stack = state.stack();
|
||||
let args = &stack[stack.len() - arg_count..];
|
||||
let platform_function = context
|
||||
.platform_functions()
|
||||
.get(function_name)
|
||||
.expect(&format!("Platform function {} not found", function_name));
|
||||
let result = platform_function(context, state, args);
|
||||
match result {
|
||||
Ok(return_value) => {
|
||||
// pop args off the stack
|
||||
let mut i: usize = 0;
|
||||
while i < *arg_count {
|
||||
state.stack_mut().pop();
|
||||
i += 1;
|
||||
}
|
||||
state.stack_mut().push(return_value);
|
||||
}
|
||||
Err(error) => {
|
||||
// Eventually we will have some kind of exception handling
|
||||
panic!("{}", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 // todo: returning results from main functions
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
extern fn println(message: Any)
|
||||
|
||||
fn main()
|
||||
let x = "Hello, World!"
|
||||
println(x)
|
||||
end
|
||||
@ -1,2 +0,0 @@
|
||||
fn main()
|
||||
println(42)
|
||||
Loading…
Reference in New Issue
Block a user