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]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.60"
|
version = "4.5.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a"
|
checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@ -96,9 +96,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.60"
|
version = "4.5.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876"
|
checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@ -108,9 +108,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.55"
|
version = "4.5.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5"
|
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@ -120,9 +120,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "1.0.0"
|
version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
|
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codespan-reporting"
|
name = "codespan-reporting"
|
||||||
@ -135,17 +135,6 @@ dependencies = [
|
|||||||
"unicode-width",
|
"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]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
@ -197,7 +186,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"ast-generator",
|
"ast-generator",
|
||||||
"clap",
|
"clap",
|
||||||
"codespan-reporting 0.12.0",
|
"codespan-reporting",
|
||||||
"cst-test-generator",
|
"cst-test-generator",
|
||||||
"indoc",
|
"indoc",
|
||||||
"log",
|
"log",
|
||||||
@ -215,34 +204,9 @@ dependencies = [
|
|||||||
"crypto-common",
|
"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]]
|
[[package]]
|
||||||
name = "dmc-lib"
|
name = "dmc-lib"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
|
||||||
"dvm-lib",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "dvm-lib"
|
|
||||||
version = "0.1.0"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
|
|||||||
@ -3,9 +3,9 @@ name = "deimos"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
#[[bin]]
|
[[bin]]
|
||||||
#name = "dm"
|
name = "dm"
|
||||||
#path = "src/bin/dvm/lib"
|
path = "src/bin/dvm/main.rs"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "dmc"
|
name = "dmc"
|
||||||
@ -25,4 +25,4 @@ cst-test-generator = { path = "cst-test-generator" }
|
|||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
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"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
dvm-lib = { path = "../dvm-lib" }
|
|
||||||
|
|||||||
@ -2,19 +2,12 @@ use crate::asm::asm_instruction::AsmInstruction;
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AsmBlock {
|
pub struct AsmBlock {
|
||||||
name: String,
|
id: usize,
|
||||||
instructions: Vec<AsmInstruction>,
|
instructions: Vec<AsmInstruction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsmBlock {
|
impl AsmBlock {
|
||||||
pub fn new(name: &str, instructions: Vec<AsmInstruction>) -> Self {
|
pub fn new(id: usize, instructions: Vec<AsmInstruction>) -> Self {
|
||||||
Self {
|
Self { id, instructions }
|
||||||
name: name.into(),
|
|
||||||
instructions,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn instructions(&self) -> &[AsmInstruction] {
|
|
||||||
&self.instructions
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
use crate::asm::asm_block::AsmBlock;
|
use crate::asm::asm_block::AsmBlock;
|
||||||
use crate::asm::asm_instruction::AsmInstruction;
|
|
||||||
use dvm_lib::vm::function::Function;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AsmFunction {
|
pub struct AsmFunction {
|
||||||
@ -15,15 +13,4 @@ impl AsmFunction {
|
|||||||
blocks,
|
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)]
|
#[derive(Debug)]
|
||||||
pub enum AsmInstruction {
|
pub enum AsmInstruction {
|
||||||
Move(Move),
|
Move(Move),
|
||||||
@ -10,25 +7,10 @@ pub enum AsmInstruction {
|
|||||||
LoadConstant(LoadConstant),
|
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)]
|
#[derive(Debug)]
|
||||||
pub enum Operand {
|
pub enum Operand {
|
||||||
IntegerLiteral(i32),
|
IntegerLiteral(i64),
|
||||||
Register(usize),
|
Register(usize),
|
||||||
StackFrameOffset(isize),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -44,18 +26,6 @@ impl Move {
|
|||||||
destination_register,
|
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)]
|
#[derive(Debug)]
|
||||||
@ -67,55 +37,29 @@ impl Push {
|
|||||||
pub fn new(source: Operand) -> Self {
|
pub fn new(source: Operand) -> Self {
|
||||||
Self { source }
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct Pop {
|
pub struct Pop {
|
||||||
destination_register: Option<usize>,
|
destination_register: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pop {
|
impl Pop {
|
||||||
pub fn new(destination_register: usize) -> Self {
|
pub fn new(destination_register: usize) -> Self {
|
||||||
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)]
|
#[derive(Debug)]
|
||||||
pub struct InvokePlatformStatic {
|
pub struct InvokePlatformStatic {
|
||||||
name: String,
|
name: String,
|
||||||
arg_count: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InvokePlatformStatic {
|
impl InvokePlatformStatic {
|
||||||
pub fn new(name: &str, arg_count: usize) -> Self {
|
pub fn new(name: &str) -> Self {
|
||||||
Self {
|
Self { name: name.into() }
|
||||||
name: name.into(),
|
|
||||||
arg_count,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dvm(&self) -> Instruction {
|
|
||||||
Instruction::InvokePlatformStatic(Rc::from(self.name.clone()), self.arg_count)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,8 +76,4 @@ impl LoadConstant {
|
|||||||
destination_register,
|
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_block;
|
||||||
pub mod asm_function;
|
pub mod asm_function;
|
||||||
pub mod asm_instruction;
|
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::ast::expression::Expression;
|
||||||
use crate::constants_table::ConstantsTable;
|
use crate::ast::function::FunctionLoweringContext;
|
||||||
use crate::diagnostic::Diagnostic;
|
use crate::diagnostic::Diagnostic;
|
||||||
|
use crate::ir::ir_call::IrCall;
|
||||||
|
use crate::ir::ir_expression::IrExpression;
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
use crate::symbol::ExpressibleSymbol;
|
|
||||||
use crate::symbol_table::SymbolTable;
|
use crate::symbol_table::SymbolTable;
|
||||||
use crate::type_info::TypeInfo;
|
use crate::type_info::TypeInfo;
|
||||||
|
|
||||||
@ -59,55 +56,14 @@ impl Call {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check that callee is callable
|
// check that callee is callable
|
||||||
let function_symbol = match self.callee.type_info() {
|
match self.callee.type_info() {
|
||||||
TypeInfo::Function(function_symbol) => function_symbol,
|
TypeInfo::Function(_) => {}
|
||||||
_ => {
|
_ => {
|
||||||
diagnostics.push(Diagnostic::new(
|
diagnostics.push(Diagnostic::new(
|
||||||
"Receiver is not callable",
|
"Receiver is not callable",
|
||||||
self.callee.source_range().start(),
|
self.callee.source_range().start(),
|
||||||
self.callee.source_range().end(),
|
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 {
|
pub fn type_info(&self) -> TypeInfo {
|
||||||
match self.callee.type_info() {
|
match self.callee.type_info() {
|
||||||
TypeInfo::Function(function_symbol) => function_symbol.borrow().return_type(),
|
TypeInfo::Function(function_symbol) => function_symbol.return_type(),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assemble(
|
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) -> IrExpression {
|
||||||
&self,
|
let function_name = match self.callee() {
|
||||||
context: &mut AssembleContext,
|
Expression::Identifier(identifier) => identifier.name(),
|
||||||
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."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => panic!("Calling things other than identifiers not yet supported."),
|
_ => panic!("Calling things other than identifiers not yet supported."),
|
||||||
};
|
};
|
||||||
|
let arguments = self
|
||||||
let function_symbol = function_symbol.borrow();
|
.arguments()
|
||||||
if function_symbol.is_platform() {
|
.iter()
|
||||||
let arg_count = function_symbol.parameters().len();
|
.map(|arg| arg.lower_to_ir(context))
|
||||||
context.instruction(AsmInstruction::InvokePlatformStatic(
|
.collect();
|
||||||
InvokePlatformStatic::new(function_symbol.name(), arg_count),
|
IrExpression::Call(IrCall::new(function_name, arguments))
|
||||||
));
|
|
||||||
} else {
|
|
||||||
todo!("non-platform invoke")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn source_range(&self) -> &SourceRange {
|
pub fn source_range(&self) -> &SourceRange {
|
||||||
|
|||||||
@ -1,28 +1,26 @@
|
|||||||
use crate::asm::asm_function::AsmFunction;
|
use crate::ast::function::Function;
|
||||||
use crate::ast::assemble_context::AssembleContext;
|
|
||||||
use crate::ast::module_level_declaration::ModuleLevelDeclaration;
|
|
||||||
use crate::constants_table::ConstantsTable;
|
|
||||||
use crate::diagnostic::Diagnostic;
|
use crate::diagnostic::Diagnostic;
|
||||||
|
use crate::ir::Ir;
|
||||||
use crate::symbol_table::SymbolTable;
|
use crate::symbol_table::SymbolTable;
|
||||||
|
|
||||||
pub struct CompilationUnit {
|
pub struct CompilationUnit {
|
||||||
declarations: Vec<ModuleLevelDeclaration>,
|
functions: Vec<Function>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompilationUnit {
|
impl CompilationUnit {
|
||||||
pub fn new(declarations: Vec<ModuleLevelDeclaration>) -> Self {
|
pub fn new(functions: Vec<Function>) -> Self {
|
||||||
Self { declarations }
|
Self { functions }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn declarations(&self) -> &[ModuleLevelDeclaration] {
|
pub fn functions(&self) -> Vec<&Function> {
|
||||||
&self.declarations
|
self.functions.iter().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
|
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
|
||||||
let mut diagnostics = vec![];
|
let mut diagnostics = vec![];
|
||||||
symbol_table.push_scope("compilation_unit_scope");
|
symbol_table.push_scope("compilation_unit_scope");
|
||||||
for declaration in &mut self.declarations {
|
for function in &mut self.functions {
|
||||||
diagnostics.append(&mut declaration.gather_declared_names(symbol_table));
|
diagnostics.append(&mut function.gather_declared_names(symbol_table));
|
||||||
}
|
}
|
||||||
symbol_table.pop_scope();
|
symbol_table.pop_scope();
|
||||||
diagnostics
|
diagnostics
|
||||||
@ -30,29 +28,25 @@ impl CompilationUnit {
|
|||||||
|
|
||||||
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||||
let mut diagnostics = vec![];
|
let mut diagnostics = vec![];
|
||||||
for declaration in &mut self.declarations {
|
for function in &mut self.functions {
|
||||||
diagnostics.append(&mut declaration.check_name_usages(symbol_table));
|
diagnostics.append(&mut function.check_name_usages(symbol_table));
|
||||||
}
|
}
|
||||||
diagnostics
|
diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||||
let mut diagnostics = vec![];
|
let mut diagnostics = vec![];
|
||||||
for declaration in &mut self.declarations {
|
for function in &mut self.functions {
|
||||||
diagnostics.append(&mut declaration.type_check(symbol_table));
|
diagnostics.append(&mut function.type_check(symbol_table));
|
||||||
}
|
}
|
||||||
diagnostics
|
diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assemble(
|
pub fn lower_to_ir(&self) -> Vec<Ir> {
|
||||||
&self,
|
let mut irs = vec![];
|
||||||
symbol_table: &SymbolTable,
|
for function in &self.functions {
|
||||||
constants_table: &mut ConstantsTable,
|
irs.append(&mut function.lower_to_ir());
|
||||||
) -> Vec<AsmFunction> {
|
|
||||||
let mut context = AssembleContext::new();
|
|
||||||
for declaration in &self.declarations {
|
|
||||||
declaration.assemble(&mut context, symbol_table, constants_table);
|
|
||||||
}
|
}
|
||||||
context.take_functions()
|
irs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
use crate::ast::call::Call;
|
use crate::ast::call::Call;
|
||||||
|
use crate::ast::function::FunctionLoweringContext;
|
||||||
use crate::ast::identifier::Identifier;
|
use crate::ast::identifier::Identifier;
|
||||||
use crate::ast::integer_literal::IntegerLiteral;
|
use crate::ast::integer_literal::IntegerLiteral;
|
||||||
use crate::ast::string_literal::StringLiteral;
|
use crate::ast::string_literal::StringLiteral;
|
||||||
use crate::diagnostic::Diagnostic;
|
use crate::diagnostic::Diagnostic;
|
||||||
|
use crate::ir::ir_expression::IrExpression;
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
use crate::symbol_table::SymbolTable;
|
use crate::symbol_table::SymbolTable;
|
||||||
use crate::type_info::TypeInfo;
|
use crate::type_info::TypeInfo;
|
||||||
@ -55,4 +57,13 @@ impl Expression {
|
|||||||
Expression::Identifier(identifier) => identifier.source_range(),
|
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::ast::expression::Expression;
|
||||||
use crate::constants_table::ConstantsTable;
|
use crate::ast::function::FunctionLoweringContext;
|
||||||
use crate::diagnostic::Diagnostic;
|
use crate::diagnostic::Diagnostic;
|
||||||
|
use crate::ir::ir_expression::IrExpression;
|
||||||
|
use crate::ir::ir_statement::IrStatement;
|
||||||
use crate::symbol_table::SymbolTable;
|
use crate::symbol_table::SymbolTable;
|
||||||
|
|
||||||
pub struct ExpressionStatement {
|
pub struct ExpressionStatement {
|
||||||
@ -31,17 +32,21 @@ impl ExpressionStatement {
|
|||||||
self.expression.type_check(symbol_table)
|
self.expression.type_check(symbol_table)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assemble(
|
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) {
|
||||||
&self,
|
let ir_expression = self.expression.lower_to_ir(context);
|
||||||
context: &mut AssembleContext,
|
match ir_expression {
|
||||||
symbol_table: &SymbolTable,
|
IrExpression::Call(ir_call) => {
|
||||||
constants_table: &mut ConstantsTable,
|
context.add_statement(IrStatement::Call(ir_call));
|
||||||
) {
|
}
|
||||||
match self.expression.as_ref() {
|
IrExpression::Constant(ir_constant) => {
|
||||||
Expression::Call(call) => {
|
unimplemented!()
|
||||||
call.assemble(context, symbol_table, constants_table);
|
}
|
||||||
|
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::ast::statement::Statement;
|
||||||
use crate::constants_table::ConstantsTable;
|
|
||||||
use crate::diagnostic::Diagnostic;
|
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::source_range::SourceRange;
|
||||||
use crate::symbol::FunctionSymbol;
|
use crate::symbol::FunctionSymbol;
|
||||||
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
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> {
|
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
|
||||||
let mut diagnostics = vec![];
|
let mut diagnostics = vec![];
|
||||||
// insert function symbol
|
// insert function symbol
|
||||||
let insert_result =
|
let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new(
|
||||||
symbol_table.insert_function_symbol(FunctionSymbol::new(self.declared_name(), false));
|
self.declared_name(),
|
||||||
|
&vec![], // todo
|
||||||
|
));
|
||||||
if let Err(symbol_insert_error) = insert_result {
|
if let Err(symbol_insert_error) = insert_result {
|
||||||
match symbol_insert_error {
|
match symbol_insert_error {
|
||||||
SymbolInsertError::AlreadyDeclared(already_declared) => {
|
SymbolInsertError::AlreadyDeclared(already_declared) => {
|
||||||
@ -52,9 +56,6 @@ impl Function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: parameters
|
|
||||||
|
|
||||||
symbol_table.push_scope(&format!("function_scope({})", self.declared_name()));
|
symbol_table.push_scope(&format!("function_scope({})", self.declared_name()));
|
||||||
for statement in &mut self.statements {
|
for statement in &mut self.statements {
|
||||||
diagnostics.append(&mut statement.gather_declared_names(symbol_table));
|
diagnostics.append(&mut statement.gather_declared_names(symbol_table));
|
||||||
@ -79,17 +80,63 @@ impl Function {
|
|||||||
diagnostics
|
diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assemble(
|
pub fn lower_to_ir(&self) -> Vec<Ir> {
|
||||||
&self,
|
let mut context = FunctionLoweringContext::new();
|
||||||
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));
|
|
||||||
for statement in &self.statements {
|
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::diagnostic::Diagnostic;
|
||||||
|
use crate::ir::ir_expression::IrExpression;
|
||||||
|
use crate::ir::ir_variable::IrVariable;
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
use crate::symbol::ExpressibleSymbol;
|
use crate::symbol::ExpressibleSymbol;
|
||||||
use crate::symbol_table::SymbolTable;
|
use crate::symbol_table::SymbolTable;
|
||||||
@ -53,17 +56,13 @@ impl Identifier {
|
|||||||
ExpressibleSymbol::Function(function_symbol) => {
|
ExpressibleSymbol::Function(function_symbol) => {
|
||||||
TypeInfo::Function(function_symbol.clone())
|
TypeInfo::Function(function_symbol.clone())
|
||||||
}
|
}
|
||||||
ExpressibleSymbol::Parameter(parameter_symbol) => {
|
ExpressibleSymbol::Parameter(parameter_symbol) => parameter_symbol.type_info().clone(),
|
||||||
parameter_symbol.borrow().type_info().clone()
|
ExpressibleSymbol::Variable(variable_symbol) => variable_symbol.type_info().clone(),
|
||||||
}
|
|
||||||
ExpressibleSymbol::Variable(variable_symbol) => {
|
|
||||||
variable_symbol.borrow().type_info().clone()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expressible_symbol(&self) -> &ExpressibleSymbol {
|
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) -> IrExpression {
|
||||||
self.expressible_symbol.as_ref().unwrap()
|
IrExpression::Variable(IrVariable::new(self.name()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn source_range(&self) -> &SourceRange {
|
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;
|
use crate::source_range::SourceRange;
|
||||||
|
|
||||||
pub struct IntegerLiteral {
|
pub struct IntegerLiteral {
|
||||||
value: i32,
|
value: i64,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntegerLiteral {
|
impl IntegerLiteral {
|
||||||
pub fn new(value: i32, source_range: SourceRange) -> Self {
|
pub fn new(value: i64, source_range: SourceRange) -> Self {
|
||||||
Self {
|
Self {
|
||||||
value,
|
value,
|
||||||
source_range,
|
source_range,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn value(&self) -> i32 {
|
pub fn value(&self) -> i64 {
|
||||||
self.value
|
self.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) -> IrExpression {
|
||||||
|
IrExpression::IntegerLiteral(self.value)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn source_range(&self) -> &SourceRange {
|
pub fn source_range(&self) -> &SourceRange {
|
||||||
&self.source_range
|
&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::ast::expression::Expression;
|
||||||
use crate::constants_table::ConstantsTable;
|
use crate::ast::function::FunctionLoweringContext;
|
||||||
use crate::diagnostic::Diagnostic;
|
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::source_range::SourceRange;
|
||||||
use crate::symbol::{ExpressibleSymbol, VariableSymbol};
|
use crate::symbol::VariableSymbol;
|
||||||
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
||||||
|
|
||||||
pub struct LetStatement {
|
pub struct LetStatement {
|
||||||
declared_name: String,
|
declared_name: String,
|
||||||
declared_name_source_range: SourceRange,
|
declared_name_source_range: SourceRange,
|
||||||
initializer: Box<Expression>,
|
initializer: Box<Expression>,
|
||||||
scope_id: Option<usize>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LetStatement {
|
impl LetStatement {
|
||||||
@ -24,7 +24,6 @@ impl LetStatement {
|
|||||||
declared_name: declared_name.to_string(),
|
declared_name: declared_name.to_string(),
|
||||||
declared_name_source_range,
|
declared_name_source_range,
|
||||||
initializer: initializer.into(),
|
initializer: initializer.into(),
|
||||||
scope_id: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +60,6 @@ impl LetStatement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.scope_id = Some(symbol_table.current_scope_id());
|
|
||||||
diagnostics
|
diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,61 +73,10 @@ impl LetStatement {
|
|||||||
diagnostics
|
diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assemble(
|
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) {
|
||||||
&self,
|
let data = self.initializer.lower_to_ir(context);
|
||||||
context: &mut AssembleContext,
|
let destination = IrVariable::new(self.declared_name());
|
||||||
symbol_table: &SymbolTable,
|
let assign_statement = IrAssign::new(destination, data);
|
||||||
constants_table: &mut ConstantsTable,
|
context.add_statement(IrStatement::Assign(assign_statement));
|
||||||
) {
|
|
||||||
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,
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,83 @@
|
|||||||
pub mod assemble_context;
|
|
||||||
pub mod call;
|
pub mod call;
|
||||||
pub mod compilation_unit;
|
pub mod compilation_unit;
|
||||||
pub mod expression;
|
pub mod expression;
|
||||||
pub mod expression_statement;
|
pub mod expression_statement;
|
||||||
pub mod extern_function;
|
|
||||||
pub mod fqn;
|
pub mod fqn;
|
||||||
pub mod function;
|
pub mod function;
|
||||||
pub mod identifier;
|
pub mod identifier;
|
||||||
pub mod integer_literal;
|
pub mod integer_literal;
|
||||||
pub mod let_statement;
|
pub mod let_statement;
|
||||||
pub mod module_level_declaration;
|
|
||||||
pub mod parameter;
|
|
||||||
pub mod statement;
|
pub mod statement;
|
||||||
pub mod string_literal;
|
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::expression_statement::ExpressionStatement;
|
||||||
|
use crate::ast::function::FunctionLoweringContext;
|
||||||
use crate::ast::let_statement::LetStatement;
|
use crate::ast::let_statement::LetStatement;
|
||||||
use crate::constants_table::ConstantsTable;
|
|
||||||
use crate::diagnostic::Diagnostic;
|
use crate::diagnostic::Diagnostic;
|
||||||
use crate::symbol_table::SymbolTable;
|
use crate::symbol_table::SymbolTable;
|
||||||
|
|
||||||
@ -38,18 +37,13 @@ impl Statement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assemble(
|
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) {
|
||||||
&self,
|
|
||||||
context: &mut AssembleContext,
|
|
||||||
symbol_table: &SymbolTable,
|
|
||||||
constants_table: &mut ConstantsTable,
|
|
||||||
) {
|
|
||||||
match self {
|
match self {
|
||||||
Statement::Let(let_statement) => {
|
Statement::Let(let_statement) => {
|
||||||
let_statement.assemble(context, symbol_table, constants_table);
|
let_statement.lower_to_ir(context);
|
||||||
}
|
}
|
||||||
Statement::Expression(expression_statement) => {
|
Statement::Expression(expression) => {
|
||||||
expression_statement.assemble(context, symbol_table, constants_table);
|
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 crate::source_range::SourceRange;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct StringLiteral {
|
pub struct StringLiteral {
|
||||||
content: String,
|
content: String,
|
||||||
@ -17,6 +21,15 @@ impl StringLiteral {
|
|||||||
&self.content
|
&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 {
|
pub fn source_range(&self) -> &SourceRange {
|
||||||
&self.source_range
|
&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..);
|
let maybe_chunk = self.input.get(self.position..);
|
||||||
if maybe_chunk.is_none() {
|
if maybe_chunk.is_none() {
|
||||||
return None;
|
return None;
|
||||||
}
|
} else {
|
||||||
chunk = maybe_chunk.unwrap();
|
chunk = maybe_chunk.unwrap();
|
||||||
if chunk.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,10 +42,6 @@ impl<'a> Lexer<'a> {
|
|||||||
)
|
)
|
||||||
} else if chunk.starts_with("=") {
|
} else if chunk.starts_with("=") {
|
||||||
Token::new(self.position, self.position + 1, TokenKind::Equals)
|
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 {
|
} else {
|
||||||
// more than one char token
|
// more than one char token
|
||||||
if chunk.starts_with(|c: char| c.is_ascii_digit()) {
|
if chunk.starts_with(|c: char| c.is_ascii_digit()) {
|
||||||
@ -93,7 +87,6 @@ impl<'a> Lexer<'a> {
|
|||||||
"fn" => TokenKind::Fn,
|
"fn" => TokenKind::Fn,
|
||||||
"end" => TokenKind::End,
|
"end" => TokenKind::End,
|
||||||
"let" => TokenKind::Let,
|
"let" => TokenKind::Let,
|
||||||
"extern" => TokenKind::Extern,
|
|
||||||
_ => TokenKind::Identifier,
|
_ => TokenKind::Identifier,
|
||||||
};
|
};
|
||||||
Token::new(self.position, self.position + prefix.len(), token_kind)
|
Token::new(self.position, self.position + prefix.len(), token_kind)
|
||||||
@ -161,17 +154,4 @@ mod tests {
|
|||||||
assert_next(&mut lexer, TokenKind::End, 3);
|
assert_next(&mut lexer, TokenKind::End, 3);
|
||||||
assert_eq!(lexer.next(), None);
|
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;
|
mod asm;
|
||||||
pub mod ast;
|
mod ast;
|
||||||
pub mod constants_table;
|
mod diagnostic;
|
||||||
pub mod diagnostic;
|
mod ir;
|
||||||
pub mod lexer;
|
mod lexer;
|
||||||
pub mod parser;
|
mod parser;
|
||||||
pub mod scope;
|
mod scope;
|
||||||
pub mod source_range;
|
mod source_range;
|
||||||
pub mod symbol;
|
mod symbol;
|
||||||
pub mod symbol_table;
|
mod symbol_table;
|
||||||
pub mod token;
|
mod token;
|
||||||
pub mod type_info;
|
mod type_info;
|
||||||
|
|||||||
@ -2,23 +2,18 @@ use crate::ast::call::Call;
|
|||||||
use crate::ast::compilation_unit::CompilationUnit;
|
use crate::ast::compilation_unit::CompilationUnit;
|
||||||
use crate::ast::expression::Expression;
|
use crate::ast::expression::Expression;
|
||||||
use crate::ast::expression_statement::ExpressionStatement;
|
use crate::ast::expression_statement::ExpressionStatement;
|
||||||
use crate::ast::extern_function::ExternFunction;
|
|
||||||
use crate::ast::function::Function;
|
use crate::ast::function::Function;
|
||||||
use crate::ast::identifier::Identifier;
|
use crate::ast::identifier::Identifier;
|
||||||
use crate::ast::integer_literal::IntegerLiteral;
|
use crate::ast::integer_literal::IntegerLiteral;
|
||||||
use crate::ast::let_statement::LetStatement;
|
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::statement::Statement;
|
||||||
use crate::ast::string_literal::StringLiteral;
|
use crate::ast::string_literal::StringLiteral;
|
||||||
use crate::ast::type_use::TypeUse;
|
|
||||||
use crate::diagnostic::Diagnostic;
|
|
||||||
use crate::lexer::Lexer;
|
use crate::lexer::Lexer;
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
use crate::token::{Token, TokenKind};
|
use crate::token::{Token, TokenKind};
|
||||||
use std::str::FromStr;
|
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);
|
let mut parser = Parser::new(input);
|
||||||
parser.compilation_unit()
|
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) {
|
fn advance(&mut self) {
|
||||||
if self.lookahead.is_some() {
|
if self.lookahead.is_some() {
|
||||||
// we've advanced at least once
|
// 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 {
|
} else {
|
||||||
// we've not yet advanced, so fetch both
|
// we've not yet advanced, so fetch both
|
||||||
// current
|
// 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() {
|
match self.current.take() {
|
||||||
None => Err(vec![Diagnostic::new(
|
None => {
|
||||||
&format!("Expected {:?} but found end-of-input.", token_kind),
|
panic!("Expected {:?} but found end of input", token_kind);
|
||||||
self.input.len(),
|
}
|
||||||
self.input.len(),
|
|
||||||
)]),
|
|
||||||
Some(token) => {
|
Some(token) => {
|
||||||
if token.kind() == token_kind {
|
if token.kind() == token_kind {
|
||||||
self.advance();
|
self.advance();
|
||||||
Ok(token)
|
token
|
||||||
} else {
|
} else {
|
||||||
self.advance_until(&[token_kind]);
|
panic!(
|
||||||
Err(vec![Diagnostic::new(
|
"Expected {:?} but found {:?} at {}",
|
||||||
&format!("Expected {:?} but found {:?}", token_kind, token.kind()),
|
token_kind,
|
||||||
token.start(),
|
token.kind(),
|
||||||
token.end(),
|
token.start()
|
||||||
)])
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,7 +100,7 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
fn peek_current(&self, token_kind: TokenKind) -> bool {
|
fn peek_current(&self, token_kind: TokenKind) -> bool {
|
||||||
match &self.current {
|
match &self.current {
|
||||||
None => panic!("Unexpected end of input."),
|
None => false,
|
||||||
Some(token) => token.kind() == token_kind,
|
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 {
|
fn sample_input(&self, start: usize, end: usize) -> &'a str {
|
||||||
&self.input[start..end]
|
&self.input[start..end]
|
||||||
}
|
}
|
||||||
@ -155,226 +122,71 @@ impl<'a> Parser<'a> {
|
|||||||
self.sample_input(token.start(), token.end())
|
self.sample_input(token.start(), token.end())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compilation_unit(&mut self) -> Result<CompilationUnit, Vec<Diagnostic>> {
|
pub fn compilation_unit(&mut self) -> CompilationUnit {
|
||||||
let mut declarations = vec![];
|
let mut functions = vec![];
|
||||||
let mut diagnostics = vec![];
|
self.advance();
|
||||||
self.advance(); // get started
|
|
||||||
while self.current.is_some() {
|
while self.current.is_some() {
|
||||||
let current = self.get_current();
|
functions.push(self.function());
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
CompilationUnit::new(functions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn module_level_declaration(&mut self) -> Result<ModuleLevelDeclaration, Vec<Diagnostic>> {
|
fn function(&mut self) -> Function {
|
||||||
let current = self.get_current();
|
self.expect_advance(TokenKind::Fn);
|
||||||
match current.kind() {
|
let identifier_token = self.expect_advance(TokenKind::Identifier);
|
||||||
TokenKind::Fn => {
|
self.expect_advance(TokenKind::LeftParentheses);
|
||||||
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)?;
|
|
||||||
// add params
|
// add params
|
||||||
self.expect_advance(TokenKind::RightParentheses)?;
|
self.expect_advance(TokenKind::RightParentheses);
|
||||||
let mut statements = vec![];
|
let mut statements = vec![];
|
||||||
let mut diagnostics = vec![];
|
while !self.peek_current(TokenKind::End) {
|
||||||
while self.current.is_some() && !self.peek_current(TokenKind::End) {
|
statements.push(self.statement());
|
||||||
let statement_result = self.statement();
|
|
||||||
match statement_result {
|
|
||||||
Ok(statement) => {
|
|
||||||
statements.push(statement);
|
|
||||||
}
|
}
|
||||||
Err(mut statement_diagnostics) => {
|
self.expect_advance(TokenKind::End);
|
||||||
diagnostics.append(&mut statement_diagnostics);
|
Function::new(
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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),
|
self.token_text(&identifier_token),
|
||||||
SourceRange::new(identifier_token.start(), identifier_token.end()),
|
SourceRange::new(identifier_token.start(), identifier_token.end()),
|
||||||
statements,
|
statements,
|
||||||
))
|
)
|
||||||
} else {
|
|
||||||
Err(diagnostics)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extern_function(&mut self) -> Result<ExternFunction, Vec<Diagnostic>> {
|
fn statement(&mut self) -> Statement {
|
||||||
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.token_text(&identifier_token),
|
|
||||||
SourceRange::new(identifier_token.start(), identifier_token.end()),
|
|
||||||
type_use,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
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>> {
|
|
||||||
let current = self.get_current();
|
let current = self.get_current();
|
||||||
match current.kind() {
|
match current.kind() {
|
||||||
TokenKind::Let => Ok(Statement::Let(self.let_statement()?)),
|
TokenKind::Let => self.let_statement(),
|
||||||
_ => Ok(Statement::Expression(self.expression_statement()?)),
|
_ => self.expression_statement(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn let_statement(&mut self) -> Result<LetStatement, Vec<Diagnostic>> {
|
fn let_statement(&mut self) -> Statement {
|
||||||
self.expect_advance(TokenKind::Let)?;
|
self.expect_advance(TokenKind::Let);
|
||||||
let identifier = self.expect_advance(TokenKind::Identifier)?;
|
let identifier = self.expect_advance(TokenKind::Identifier);
|
||||||
self.expect_advance(TokenKind::Equals)?;
|
self.expect_advance(TokenKind::Equals);
|
||||||
let expression = self.expression()?;
|
let expression = self.expression();
|
||||||
Ok(LetStatement::new(
|
Statement::Let(LetStatement::new(
|
||||||
self.token_text(&identifier),
|
self.token_text(&identifier),
|
||||||
SourceRange::new(identifier.start(), identifier.end()),
|
SourceRange::new(identifier.start(), identifier.end()),
|
||||||
expression,
|
expression,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expression_statement(&mut self) -> Result<ExpressionStatement, Vec<Diagnostic>> {
|
fn expression_statement(&mut self) -> Statement {
|
||||||
Ok(ExpressionStatement::new(self.expression()?))
|
Statement::Expression(ExpressionStatement::new(self.expression()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expression(&mut self) -> Result<Expression, Vec<Diagnostic>> {
|
fn expression(&mut self) -> Expression {
|
||||||
let current = self.get_current().clone(); // I don't love this clone
|
let current = self.get_current();
|
||||||
let mut diagnostics = vec![];
|
let mut result = match current.kind() {
|
||||||
let mut expression = match current.kind() {
|
|
||||||
TokenKind::IntegerLiteral => {
|
TokenKind::IntegerLiteral => {
|
||||||
let raw = self.token_text(¤t);
|
let raw = self.token_text(current);
|
||||||
let source_range = SourceRange::new(current.start(), current.end());
|
let source_range = SourceRange::new(current.start(), current.end());
|
||||||
self.advance();
|
self.advance();
|
||||||
Expression::IntegerLiteral(IntegerLiteral::new(
|
Expression::IntegerLiteral(IntegerLiteral::new(
|
||||||
i32::from_str(raw).unwrap(),
|
i64::from_str(raw).unwrap(),
|
||||||
source_range,
|
source_range,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
TokenKind::String => {
|
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());
|
let source_range = SourceRange::new(current.start(), current.end());
|
||||||
self.advance();
|
self.advance();
|
||||||
Expression::String(StringLiteral::new(
|
Expression::String(StringLiteral::new(
|
||||||
@ -383,73 +195,37 @@ impl<'a> Parser<'a> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
TokenKind::Identifier => {
|
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());
|
let source_range = SourceRange::new(current.start(), current.end());
|
||||||
self.advance();
|
self.advance();
|
||||||
Expression::Identifier(Identifier::new(declared_name, source_range))
|
Expression::Identifier(Identifier::new(declared_name, source_range))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => panic!("Unexpected token {:?}", current.kind()),
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// postfixes
|
// postfixes
|
||||||
while let Some(current) = &self.current {
|
while let Some(current) = &self.current {
|
||||||
match current.kind() {
|
match current.kind() {
|
||||||
TokenKind::LeftParentheses => {
|
TokenKind::LeftParentheses => {
|
||||||
expression = Expression::Call(self.call(expression)?);
|
result = Expression::Call(self.call(result));
|
||||||
}
|
}
|
||||||
_ => break,
|
_ => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if diagnostics.is_empty() {
|
result
|
||||||
Ok(expression)
|
|
||||||
} else {
|
|
||||||
Err(diagnostics)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, callee: Expression) -> Result<Call, Vec<Diagnostic>> {
|
fn call(&mut self, callee: Expression) -> Call {
|
||||||
self.expect_advance(TokenKind::LeftParentheses)?;
|
self.expect_advance(TokenKind::LeftParentheses);
|
||||||
let mut arguments = vec![];
|
let mut arguments = vec![];
|
||||||
while self.current.is_some() && !self.peek_current(TokenKind::RightParentheses) {
|
while !self.peek_current(TokenKind::RightParentheses) {
|
||||||
arguments.push(self.expression()?);
|
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 =
|
let source_range =
|
||||||
SourceRange::new(callee.source_range().start(), right_parentheses_token.end());
|
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 {
|
mod smoke_tests {
|
||||||
use super::*;
|
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]
|
#[test]
|
||||||
fn forty_two() {
|
fn forty_two() {
|
||||||
smoke_test("fn main() 42 end");
|
parse_compilation_unit("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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hello_world() {
|
fn hello_world() {
|
||||||
let parse_result = parse_compilation_unit("fn main() println(\"Hello, World!\") end");
|
let compilation_unit = parse_compilation_unit("fn main() println(\"Hello, World!\") end");
|
||||||
let compilation_unit = match parse_result {
|
let functions = compilation_unit.functions();
|
||||||
Ok(compilation_unit) => compilation_unit,
|
assert_eq!(functions.len(), 1);
|
||||||
Err(diagnostics) => {
|
let function = functions[0];
|
||||||
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!(),
|
|
||||||
};
|
|
||||||
assert_eq!(function.declared_name(), "main");
|
assert_eq!(function.declared_name(), "main");
|
||||||
let statements = function.statements();
|
let statements = function.statements();
|
||||||
assert_eq!(statements.len(), 1);
|
assert_eq!(statements.len(), 1);
|
||||||
@ -555,36 +272,9 @@ mod concrete_tests {
|
|||||||
panic!("Expected expression");
|
panic!("Expected expression");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod parse_failure_tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lone_end() {
|
fn chained_calls() {
|
||||||
let parse_result = parse_compilation_unit("end");
|
parse_compilation_unit("fn main() getCl()() 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!(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
use crate::symbol::{FunctionSymbol, ParameterSymbol, VariableSymbol};
|
use crate::symbol::{FunctionSymbol, ParameterSymbol, VariableSymbol};
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
debug_name: String,
|
debug_name: String,
|
||||||
parent_id: Option<usize>,
|
parent_id: Option<usize>,
|
||||||
function_symbols: HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>>,
|
function_symbols: HashMap<Rc<str>, Rc<FunctionSymbol>>,
|
||||||
parameter_symbols: HashMap<Rc<str>, Rc<RefCell<ParameterSymbol>>>,
|
parameter_symbols: HashMap<Rc<str>, Rc<ParameterSymbol>>,
|
||||||
variable_symbols: HashMap<Rc<str>, Rc<RefCell<VariableSymbol>>>,
|
variable_symbols: HashMap<Rc<str>, Rc<VariableSymbol>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scope {
|
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
|
&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
|
&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
|
&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
|
&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
|
&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
|
&mut self.variable_symbols
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct SourceRange {
|
pub struct SourceRange {
|
||||||
start: usize,
|
start: usize,
|
||||||
end: usize,
|
end: usize,
|
||||||
|
|||||||
@ -1,19 +1,16 @@
|
|||||||
use crate::type_info::TypeInfo;
|
use crate::type_info::TypeInfo;
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct FunctionSymbol {
|
pub struct FunctionSymbol {
|
||||||
name: Rc<str>,
|
name: Rc<str>,
|
||||||
is_platform: bool,
|
parameters: Vec<Rc<ParameterSymbol>>,
|
||||||
parameters: Option<Vec<Rc<RefCell<ParameterSymbol>>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FunctionSymbol {
|
impl FunctionSymbol {
|
||||||
pub fn new(name: &str, is_platform: bool) -> Self {
|
pub fn new(name: &str, parameters: &[Rc<ParameterSymbol>]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
is_platform,
|
parameters: parameters.into(),
|
||||||
parameters: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,27 +22,18 @@ impl FunctionSymbol {
|
|||||||
self.name.clone()
|
self.name.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_parameters(&mut self, parameters: Vec<Rc<RefCell<ParameterSymbol>>>) {
|
pub fn parameters(&self) -> &[Rc<ParameterSymbol>] {
|
||||||
self.parameters = Some(parameters);
|
&self.parameters
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parameters(&self) -> &[Rc<RefCell<ParameterSymbol>>] {
|
|
||||||
self.parameters.as_ref().unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn return_type(&self) -> TypeInfo {
|
pub fn return_type(&self) -> TypeInfo {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_platform(&self) -> bool {
|
|
||||||
self.is_platform
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ParameterSymbol {
|
pub struct ParameterSymbol {
|
||||||
name: Rc<str>,
|
name: Rc<str>,
|
||||||
type_info: TypeInfo,
|
type_info: TypeInfo,
|
||||||
stack_frame_offset: Option<isize>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParameterSymbol {
|
impl ParameterSymbol {
|
||||||
@ -53,7 +41,6 @@ impl ParameterSymbol {
|
|||||||
Self {
|
Self {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
type_info,
|
type_info,
|
||||||
stack_frame_offset: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,20 +55,11 @@ impl ParameterSymbol {
|
|||||||
pub fn type_info(&self) -> &TypeInfo {
|
pub fn type_info(&self) -> &TypeInfo {
|
||||||
&self.type_info
|
&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 {
|
pub struct VariableSymbol {
|
||||||
name: Rc<str>,
|
name: Rc<str>,
|
||||||
type_info: TypeInfo,
|
type_info: TypeInfo,
|
||||||
register: Option<usize>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VariableSymbol {
|
impl VariableSymbol {
|
||||||
@ -89,7 +67,6 @@ impl VariableSymbol {
|
|||||||
Self {
|
Self {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
type_info,
|
type_info,
|
||||||
register: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,18 +81,10 @@ impl VariableSymbol {
|
|||||||
pub fn type_info(&self) -> &TypeInfo {
|
pub fn type_info(&self) -> &TypeInfo {
|
||||||
&self.type_info
|
&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 {
|
pub enum ExpressibleSymbol {
|
||||||
Function(Rc<RefCell<FunctionSymbol>>),
|
Function(Rc<FunctionSymbol>),
|
||||||
Parameter(Rc<RefCell<ParameterSymbol>>),
|
Parameter(Rc<ParameterSymbol>),
|
||||||
Variable(Rc<RefCell<VariableSymbol>>),
|
Variable(Rc<VariableSymbol>),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
use crate::symbol::{ExpressibleSymbol, FunctionSymbol, ParameterSymbol, VariableSymbol};
|
use crate::symbol::{ExpressibleSymbol, FunctionSymbol, ParameterSymbol, VariableSymbol};
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct SymbolTable {
|
pub struct SymbolTable {
|
||||||
@ -51,35 +50,31 @@ impl SymbolTable {
|
|||||||
pub fn insert_function_symbol(
|
pub fn insert_function_symbol(
|
||||||
&mut self,
|
&mut self,
|
||||||
function_symbol: FunctionSymbol,
|
function_symbol: FunctionSymbol,
|
||||||
) -> Result<Rc<RefCell<FunctionSymbol>>, SymbolInsertError> {
|
) -> Result<(), SymbolInsertError> {
|
||||||
if self.current_scope_has_name(function_symbol.name()) {
|
if self.current_scope_has_name(function_symbol.name()) {
|
||||||
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
|
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
|
||||||
function_symbol.name(),
|
function_symbol.name(),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
let name = function_symbol.name_owned();
|
|
||||||
let as_rc = Rc::new(RefCell::new(function_symbol));
|
|
||||||
|
|
||||||
self.current_scope_mut()
|
self.current_scope_mut()
|
||||||
.function_symbols_mut()
|
.function_symbols_mut()
|
||||||
.insert(name, as_rc.clone());
|
.insert(function_symbol.name_owned(), Rc::new(function_symbol));
|
||||||
Ok(as_rc)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_parameter_symbol(
|
pub fn insert_parameter_symbol(
|
||||||
&mut self,
|
&mut self,
|
||||||
parameter_symbol: ParameterSymbol,
|
parameter_symbol: ParameterSymbol,
|
||||||
) -> Result<Rc<RefCell<ParameterSymbol>>, SymbolInsertError> {
|
) -> Result<(), SymbolInsertError> {
|
||||||
if self.current_scope_has_name(parameter_symbol.name()) {
|
if self.current_scope_has_name(parameter_symbol.name()) {
|
||||||
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
|
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
|
||||||
parameter_symbol.name(),
|
parameter_symbol.name(),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
let as_rc = Rc::new(RefCell::new(parameter_symbol));
|
|
||||||
self.current_scope_mut()
|
self.current_scope_mut()
|
||||||
.parameter_symbols_mut()
|
.parameter_symbols_mut()
|
||||||
.insert(as_rc.borrow().name_owned(), as_rc.clone());
|
.insert(parameter_symbol.name_owned(), Rc::new(parameter_symbol));
|
||||||
Ok(as_rc)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_variable_symbol(
|
pub fn insert_variable_symbol(
|
||||||
@ -91,10 +86,9 @@ impl SymbolTable {
|
|||||||
variable_symbol.name(),
|
variable_symbol.name(),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
self.current_scope_mut().variable_symbols_mut().insert(
|
self.current_scope_mut()
|
||||||
variable_symbol.name_owned(),
|
.variable_symbols_mut()
|
||||||
Rc::new(RefCell::new(variable_symbol)),
|
.insert(variable_symbol.name_owned(), Rc::new(variable_symbol));
|
||||||
);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,11 +125,6 @@ impl SymbolTable {
|
|||||||
}
|
}
|
||||||
None
|
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 {
|
pub enum SymbolInsertError {
|
||||||
|
|||||||
@ -35,7 +35,4 @@ pub enum TokenKind {
|
|||||||
IntegerLiteral,
|
IntegerLiteral,
|
||||||
LongLiteral,
|
LongLiteral,
|
||||||
String,
|
String,
|
||||||
Extern,
|
|
||||||
Comma,
|
|
||||||
Colon,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,56 +1,9 @@
|
|||||||
use crate::symbol::FunctionSymbol;
|
use crate::symbol::FunctionSymbol;
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::fmt::{Display, Formatter};
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum TypeInfo {
|
pub enum TypeInfo {
|
||||||
Any,
|
|
||||||
Integer,
|
Integer,
|
||||||
String,
|
String,
|
||||||
Function(Rc<RefCell<FunctionSymbol>>),
|
Function(Rc<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.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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