Compare commits
39 Commits
llvm-rewri
...
main
Author | SHA1 | Date | |
---|---|---|---|
14e0dcbe15 | |||
85b2ebf04a | |||
615fec6e52 | |||
d04a70e464 | |||
383302c1c2 | |||
b19a32cd8a | |||
1aa9b3e76c | |||
94c4ec0613 | |||
d757ac4eb3 | |||
c50474cc8e | |||
50f5f3cc70 | |||
f7fa69fbe2 | |||
a907fec967 | |||
9710d17e00 | |||
257496aae2 | |||
197f0b22f3 | |||
b84672ef8c | |||
8afb2c2572 | |||
974647b401 | |||
d5daaa0e87 | |||
49df6c9ed9 | |||
46560d8541 | |||
14283afe59 | |||
2e99ec3a80 | |||
fb876e3ef5 | |||
cdbc4593a8 | |||
9b9fcd4ec4 | |||
95b3ffe8ef | |||
22737f022e | |||
a7292f4719 | |||
9b68ecb614 | |||
a366d22470 | |||
12dc457b99 | |||
0932af2e3b | |||
48ae533f33 | |||
35efa78a56 | |||
58117d86e4 | |||
454cefafc9 | |||
20dfdfec9f |
72
README.md
72
README.md
@ -1,5 +1,75 @@
|
|||||||
# Reid-LLVM
|
# Reid-LLVM
|
||||||
Attempt at re-creating Reid, this time using LLVM.
|
Reid is a toy-language compiler I'm working on to learn LLVM (and compiler
|
||||||
|
development).
|
||||||
|
|
||||||
|
Reid only uses [llvm-sys](https://gitlab.com/taricorp/llvm-sys.rs), which
|
||||||
|
provide very minimal bindings from the LLVM C-API to Rust. `reid_llvm`-crate
|
||||||
|
contains the relevant abstraction to produce a more Rust'y API from that.
|
||||||
|
|
||||||
|
Much of the syntax in Reid is directly inspired by rust, but mostly it is driven
|
||||||
|
by simplicity.
|
||||||
|
|
||||||
|
Reid is currently able to (non-exhaustively):
|
||||||
|
- Do basic algebra (e.g. Add, Sub, Mult)
|
||||||
|
- Resolve complex one-liners correctly using PEDMAS (e.g. `5 + 2 * 5 - 5 *
|
||||||
|
5` is calculated correctly)
|
||||||
|
- Declare and call functions with varying parameters and return types
|
||||||
|
- Perform type-checking and type-inference such that return-types and
|
||||||
|
parameter types must always match.
|
||||||
|
- Do simple logic-operations (e.g. If/And/Or)
|
||||||
|
|
||||||
|
An example program of Reid, that calculates the 5th fibonacci number (and uses
|
||||||
|
Rust for highlighting) is:
|
||||||
|
```rust
|
||||||
|
fn main() -> u16 {
|
||||||
|
return fibonacci(5);
|
||||||
|
}
|
||||||
|
fn fibonacci(n: u16) -> u16 {
|
||||||
|
if n <= 2 {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return fibonacci(n-1) + fibonacci(n-2);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Currently missing relevant features (TODOs) are:
|
||||||
|
- Arrays
|
||||||
|
- Structs (and custom types as such)
|
||||||
|
- Extern functions
|
||||||
|
- Strings
|
||||||
|
- Loops
|
||||||
|
|
||||||
|
### Why "Reid"
|
||||||
|
|
||||||
|
[ᚱ is an Elder Futhark rune](https://en.wikipedia.org/wiki/Raido) which means
|
||||||
|
"ride" or **"journey"**. As this language is meant for me to primarily learn
|
||||||
|
language design and compiler development, it is more about the *journey* rather
|
||||||
|
than the destination. ᚱ is written as "Reið" in Icelandic, which is the
|
||||||
|
inspiration behind "Reid" here.
|
||||||
|
|
||||||
|
### Why "Reid-LLVM"?
|
||||||
|
|
||||||
|
Because I have another project also called Reid, which only compiles to a
|
||||||
|
Virtual Instruction Set Architecture (V-ISA) that is executed via a custom-made
|
||||||
|
Virtual Machine. It is still hosted
|
||||||
|
[here](https://git.teascade.net/teascade/reid), but do note that it is very old
|
||||||
|
and not as representative of my skills as a programmer today as this one.
|
||||||
|
|
||||||
|
## What is currently being tested?
|
||||||
|
|
||||||
|
Currently when testing the compiler I run `./libtest.sh fibonacci` or
|
||||||
|
`./libtest.sh arithmetic`.
|
||||||
|
|
||||||
|
What `./libtest.sh $1` does, is it compiles and runs the rust example found with
|
||||||
|
name `$1`, which may exist in any of the two projects. The two mentioned above
|
||||||
|
are in `reid/examples`.
|
||||||
|
|
||||||
|
All examples currently end up producing a `hello.o` and `hello.asm` file to the
|
||||||
|
root directory, which is then linked with `ldd` to produce a `main`, which is
|
||||||
|
finally executed.
|
||||||
|
|
||||||
|
This is currently very work-in-progress and many things about this repository
|
||||||
|
change erratically.
|
||||||
|
|
||||||
## Various notes in order to get this working properly
|
## Various notes in order to get this working properly
|
||||||
This is what worked for me, might not (probably) work for you, depending on
|
This is what worked for me, might not (probably) work for you, depending on
|
||||||
|
@ -1,105 +1,60 @@
|
|||||||
use reid_lib::{
|
use reid_lib::{ConstValue, Context, Instr, CmpPredicate, TerminatorKind, Type};
|
||||||
Context, IntPredicate,
|
|
||||||
types::{BasicType, IntegerValue, Value},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn main() {
|
fn main() {
|
||||||
// Notes from inkwell:
|
use ConstValue::*;
|
||||||
// - Creating new values should probably just be functions in the context
|
use Instr::*;
|
||||||
// - Creating functions should probably be functions from module
|
|
||||||
// - Builder could well be it's own struct
|
|
||||||
// - Although, I do like the fact where blocks move the builder by itself..
|
|
||||||
|
|
||||||
let context = Context::new();
|
let context = Context::new();
|
||||||
|
|
||||||
let module = context.module("testmodule");
|
let mut module = context.module("test");
|
||||||
|
|
||||||
let int_32 = context.type_i32();
|
let main = module.function("main", Type::I32, Vec::new());
|
||||||
|
let mut m_entry = main.block("entry");
|
||||||
|
|
||||||
let fibonacci = module.add_function(int_32.function_type(vec![int_32.into()]), "fibonacci");
|
let fibonacci = module.function("fibonacci", Type::I32, vec![Type::I32]);
|
||||||
let mut f_main = fibonacci.block("main");
|
|
||||||
|
|
||||||
let param = fibonacci
|
let arg = m_entry.build(Constant(I32(5))).unwrap();
|
||||||
.get_param::<IntegerValue>(0, int_32.into())
|
let fibonacci_call = m_entry
|
||||||
|
.build(FunctionCall(fibonacci.value(), vec![arg]))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut cmp = f_main
|
m_entry
|
||||||
.integer_compare(¶m, &int_32.from_unsigned(3), &IntPredicate::ULT, "cmp")
|
.terminate(TerminatorKind::Ret(fibonacci_call))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut done = fibonacci.block("done");
|
let mut f_entry = fibonacci.block("entry");
|
||||||
let mut recurse = fibonacci.block("recurse");
|
|
||||||
f_main.conditional_br(&cmp, &done, &recurse).unwrap();
|
|
||||||
|
|
||||||
done.ret(&int_32.from_unsigned(1)).unwrap();
|
let num_3 = f_entry.build(Constant(I32(3))).unwrap();
|
||||||
|
let param_n = f_entry.build(Param(0)).unwrap();
|
||||||
let minus_one = recurse
|
let cond = f_entry
|
||||||
.sub(¶m, &int_32.from_unsigned(1), "minus_one")
|
.build(ICmp(CmpPredicate::LT, param_n, num_3))
|
||||||
.unwrap();
|
|
||||||
let minus_two = recurse
|
|
||||||
.sub(¶m, &int_32.from_unsigned(2), "minus_two")
|
|
||||||
.unwrap();
|
|
||||||
let one: IntegerValue = recurse
|
|
||||||
.call(&fibonacci, vec![Value::Integer(minus_one)], "call_one")
|
|
||||||
.unwrap();
|
|
||||||
let two = recurse
|
|
||||||
.call(&fibonacci, vec![Value::Integer(minus_two)], "call_two")
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let add = recurse.add(&one, &two, "add").unwrap();
|
let mut then_b = fibonacci.block("then");
|
||||||
|
let mut else_b = fibonacci.block("else");
|
||||||
|
|
||||||
recurse.ret(&add).unwrap();
|
f_entry
|
||||||
|
.terminate(TerminatorKind::CondBr(cond, then_b.value(), else_b.value()))
|
||||||
let main_f = module.add_function(int_32.function_type(Vec::new()), "main");
|
|
||||||
|
|
||||||
let mut main_b = main_f.block("main");
|
|
||||||
let call: IntegerValue = main_b
|
|
||||||
.call(
|
|
||||||
&fibonacci,
|
|
||||||
vec![Value::Integer(int_32.from_unsigned(8))],
|
|
||||||
"fib_call",
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
main_b.ret(&call).unwrap();
|
|
||||||
|
|
||||||
// let secondary = module.add_function(int_32.function_type(&[]), "secondary");
|
let ret_const = then_b.build(Constant(I32(1))).unwrap();
|
||||||
// let s_entry = secondary.block("entry");
|
then_b.terminate(TerminatorKind::Ret(ret_const)).unwrap();
|
||||||
// s_entry.ret(&int_32.from_signed(54)).unwrap();
|
|
||||||
|
|
||||||
// let function = module.add_function(int_32.function_type(&[]), "main");
|
let const_1 = else_b.build(Constant(I32(1))).unwrap();
|
||||||
|
let const_2 = else_b.build(Constant(I32(2))).unwrap();
|
||||||
|
let param_1 = else_b.build(Sub(param_n, const_1)).unwrap();
|
||||||
|
let param_2 = else_b.build(Sub(param_n, const_2)).unwrap();
|
||||||
|
let call_1 = else_b
|
||||||
|
.build(FunctionCall(fibonacci.value(), vec![param_1]))
|
||||||
|
.unwrap();
|
||||||
|
let call_2 = else_b
|
||||||
|
.build(FunctionCall(fibonacci.value(), vec![param_2]))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// let entry = function.block("entry");
|
let add = else_b.build(Add(call_1, call_2)).unwrap();
|
||||||
|
|
||||||
// let call = entry.call(&secondary, vec![], "call").unwrap();
|
else_b.terminate(TerminatorKind::Ret(add)).unwrap();
|
||||||
// let add = entry.add(&int_32.from_signed(100), &call, "add").unwrap();
|
|
||||||
// let rhs_cmp = int_32.from_signed(200);
|
|
||||||
|
|
||||||
// let cond_res = entry
|
dbg!(&context);
|
||||||
// .integer_compare(&add, &rhs_cmp, &IntPredicate::SLT, "cmp")
|
|
||||||
// .unwrap();
|
|
||||||
|
|
||||||
// let (lhs, rhs) = entry.conditional_br(&cond_res, "lhs", "rhs").unwrap();
|
context.compile();
|
||||||
|
|
||||||
// let left = lhs.add(&call, &int_32.from_signed(20), "add").unwrap();
|
|
||||||
// let right = rhs.add(&call, &int_32.from_signed(30), "add").unwrap();
|
|
||||||
|
|
||||||
// let final_block = function.block("final");
|
|
||||||
// let phi = final_block
|
|
||||||
// .phi::<IntegerValue>(&int_32, "phi")
|
|
||||||
// .unwrap()
|
|
||||||
// .add_incoming(&left, &lhs)
|
|
||||||
// .add_incoming(&right, &rhs)
|
|
||||||
// .build();
|
|
||||||
|
|
||||||
// lhs.br(&final_block).unwrap();
|
|
||||||
// rhs.br(&final_block).unwrap();
|
|
||||||
|
|
||||||
// let val = final_block
|
|
||||||
// .add(&phi, &int_32.from_signed(11), "add")
|
|
||||||
// .unwrap();
|
|
||||||
// final_block.ret(&val).unwrap();
|
|
||||||
|
|
||||||
match module.print_to_string() {
|
|
||||||
Ok(v) => println!("{}", v),
|
|
||||||
Err(e) => println!("Err: {:?}", e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
411
reid-llvm-lib/src/builder.rs
Normal file
411
reid-llvm-lib/src/builder.rs
Normal file
@ -0,0 +1,411 @@
|
|||||||
|
//! This module contains simply [`Builder`] and it's related utility Values.
|
||||||
|
//! Builder is the actual struct being modified when building the LLIR.
|
||||||
|
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use llvm_sys::core::{LLVMBuildAlloca, LLVMBuildLoad2, LLVMBuildStore};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
BlockData, ConstValue, FunctionData, Instr, InstructionData, ModuleData, TerminatorKind, Type,
|
||||||
|
util::match_types,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
|
||||||
|
pub struct ModuleValue(pub(crate) usize);
|
||||||
|
|
||||||
|
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
|
||||||
|
pub struct FunctionValue(pub(crate) ModuleValue, pub(crate) usize);
|
||||||
|
|
||||||
|
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
|
||||||
|
pub struct BlockValue(pub(crate) FunctionValue, pub(crate) usize);
|
||||||
|
|
||||||
|
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
|
||||||
|
pub struct InstructionValue(pub(crate) BlockValue, pub(crate) usize);
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ModuleHolder {
|
||||||
|
pub(crate) value: ModuleValue,
|
||||||
|
pub(crate) data: ModuleData,
|
||||||
|
pub(crate) functions: Vec<FunctionHolder>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct FunctionHolder {
|
||||||
|
pub(crate) value: FunctionValue,
|
||||||
|
pub(crate) data: FunctionData,
|
||||||
|
pub(crate) blocks: Vec<BlockHolder>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct BlockHolder {
|
||||||
|
pub(crate) value: BlockValue,
|
||||||
|
pub(crate) data: BlockData,
|
||||||
|
pub(crate) instructions: Vec<InstructionHolder>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct InstructionHolder {
|
||||||
|
pub(crate) value: InstructionValue,
|
||||||
|
pub(crate) data: InstructionData,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct Builder {
|
||||||
|
modules: Rc<RefCell<Vec<ModuleHolder>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Builder {
|
||||||
|
pub fn new() -> Builder {
|
||||||
|
Builder {
|
||||||
|
modules: Rc::new(RefCell::new(Vec::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_module(&self, data: ModuleData) -> ModuleValue {
|
||||||
|
let value = ModuleValue(self.modules.borrow().len());
|
||||||
|
self.modules.borrow_mut().push(ModuleHolder {
|
||||||
|
value,
|
||||||
|
data,
|
||||||
|
functions: Vec::new(),
|
||||||
|
});
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn add_function(
|
||||||
|
&self,
|
||||||
|
mod_val: &ModuleValue,
|
||||||
|
data: FunctionData,
|
||||||
|
) -> FunctionValue {
|
||||||
|
unsafe {
|
||||||
|
let mut modules = self.modules.borrow_mut();
|
||||||
|
let module = modules.get_unchecked_mut(mod_val.0);
|
||||||
|
let value = FunctionValue(module.value, module.functions.len());
|
||||||
|
module.functions.push(FunctionHolder {
|
||||||
|
value,
|
||||||
|
data,
|
||||||
|
blocks: Vec::new(),
|
||||||
|
});
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn add_block(&self, fun_val: &FunctionValue, data: BlockData) -> BlockValue {
|
||||||
|
unsafe {
|
||||||
|
let mut modules = self.modules.borrow_mut();
|
||||||
|
let module = modules.get_unchecked_mut(fun_val.0.0);
|
||||||
|
let function = module.functions.get_unchecked_mut(fun_val.1);
|
||||||
|
let value = BlockValue(function.value, function.blocks.len());
|
||||||
|
function.blocks.push(BlockHolder {
|
||||||
|
value,
|
||||||
|
data,
|
||||||
|
instructions: Vec::new(),
|
||||||
|
});
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn add_instruction(
|
||||||
|
&self,
|
||||||
|
block_val: &BlockValue,
|
||||||
|
data: InstructionData,
|
||||||
|
) -> Result<InstructionValue, ()> {
|
||||||
|
unsafe {
|
||||||
|
let mut modules = self.modules.borrow_mut();
|
||||||
|
let module = modules.get_unchecked_mut(block_val.0.0.0);
|
||||||
|
let function = module.functions.get_unchecked_mut(block_val.0.1);
|
||||||
|
let block = function.blocks.get_unchecked_mut(block_val.1);
|
||||||
|
let value = InstructionValue(block.value, block.instructions.len());
|
||||||
|
block.instructions.push(InstructionHolder { value, data });
|
||||||
|
|
||||||
|
// Drop modules so that it is no longer mutable borrowed
|
||||||
|
// (check_instruction requires an immutable borrow).
|
||||||
|
drop(modules);
|
||||||
|
|
||||||
|
self.check_instruction(&value)?;
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn terminate(
|
||||||
|
&self,
|
||||||
|
block: &BlockValue,
|
||||||
|
value: TerminatorKind,
|
||||||
|
) -> Result<(), ()> {
|
||||||
|
unsafe {
|
||||||
|
let mut modules = self.modules.borrow_mut();
|
||||||
|
let module = modules.get_unchecked_mut(block.0.0.0);
|
||||||
|
let function = module.functions.get_unchecked_mut(block.0.1);
|
||||||
|
let block = function.blocks.get_unchecked_mut(block.1);
|
||||||
|
if let Some(_) = &block.data.terminator {
|
||||||
|
Err(())
|
||||||
|
} else {
|
||||||
|
block.data.terminator = Some(value);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn delete_block(&self, block: &BlockValue) -> Result<(), ()> {
|
||||||
|
unsafe {
|
||||||
|
let mut modules = self.modules.borrow_mut();
|
||||||
|
let module = modules.get_unchecked_mut(block.0.0.0);
|
||||||
|
let function = module.functions.get_unchecked_mut(block.0.1);
|
||||||
|
let block = function.blocks.get_unchecked_mut(block.1);
|
||||||
|
block.data.deleted = true;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) unsafe fn module_data(&self, value: &ModuleValue) -> ModuleData {
|
||||||
|
unsafe { self.modules.borrow().get_unchecked(value.0).data.clone() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn function_data(&self, value: &FunctionValue) -> FunctionData {
|
||||||
|
unsafe {
|
||||||
|
self.modules
|
||||||
|
.borrow()
|
||||||
|
.get_unchecked(value.0.0)
|
||||||
|
.functions
|
||||||
|
.get_unchecked(value.1)
|
||||||
|
.data
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) unsafe fn block_data(&self, value: &BlockValue) -> BlockData {
|
||||||
|
unsafe {
|
||||||
|
self.modules
|
||||||
|
.borrow()
|
||||||
|
.get_unchecked(value.0.0.0)
|
||||||
|
.functions
|
||||||
|
.get_unchecked(value.0.1)
|
||||||
|
.blocks
|
||||||
|
.get_unchecked(value.1)
|
||||||
|
.data
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn instr_data(&self, value: &InstructionValue) -> InstructionData {
|
||||||
|
unsafe {
|
||||||
|
self.modules
|
||||||
|
.borrow()
|
||||||
|
.get_unchecked(value.0.0.0.0)
|
||||||
|
.functions
|
||||||
|
.get_unchecked(value.0.0.1)
|
||||||
|
.blocks
|
||||||
|
.get_unchecked(value.0.1)
|
||||||
|
.instructions
|
||||||
|
.get_unchecked(value.1)
|
||||||
|
.data
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_modules(&self) -> Rc<RefCell<Vec<ModuleHolder>>> {
|
||||||
|
self.modules.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_instruction(&self, instruction: &InstructionValue) -> Result<(), ()> {
|
||||||
|
use super::Instr::*;
|
||||||
|
unsafe {
|
||||||
|
match self.instr_data(&instruction).kind {
|
||||||
|
Param(_) => Ok(()),
|
||||||
|
Constant(_) => Ok(()),
|
||||||
|
Add(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
|
||||||
|
Sub(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
|
||||||
|
Mult(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
|
||||||
|
And(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
|
||||||
|
ICmp(_, lhs, rhs) => {
|
||||||
|
let t = match_types(&lhs, &rhs, self)?;
|
||||||
|
if t.comparable() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(()) // TODO error: Types not comparable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FunctionCall(fun, params) => {
|
||||||
|
let param_types = self.function_data(&fun).params;
|
||||||
|
if param_types.len() != params.len() {
|
||||||
|
return Err(()); // TODO error: invalid amount of params
|
||||||
|
}
|
||||||
|
for (a, b) in param_types.iter().zip(params) {
|
||||||
|
if *a != b.get_type(&self)? {
|
||||||
|
return Err(()); // TODO error: params do not match
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Phi(vals) => {
|
||||||
|
let mut iter = vals.iter();
|
||||||
|
// TODO error: Phi must contain at least one item
|
||||||
|
|
||||||
|
// TODO error: compile can actually crash here if any of the
|
||||||
|
// incoming values come from blocks that are added later
|
||||||
|
// than the one where this one exists.
|
||||||
|
|
||||||
|
let first = iter.next().ok_or(())?;
|
||||||
|
for item in iter {
|
||||||
|
match_types(first, item, &self)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Alloca(_, _) => Ok(()),
|
||||||
|
Load(ptr, _) => {
|
||||||
|
if let Ok(ty) = ptr.get_type(&self) {
|
||||||
|
if let Type::Ptr(_) = ty {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Store(ptr, _) => {
|
||||||
|
if let Ok(ty) = ptr.get_type(&self) {
|
||||||
|
if let Type::Ptr(_) = ty {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_block_used(&self, block_v: BlockValue) -> bool {
|
||||||
|
unsafe {
|
||||||
|
let modules = self.modules.borrow();
|
||||||
|
let module = modules.get_unchecked(block_v.0.0.0);
|
||||||
|
let function = module.functions.get_unchecked(block_v.0.1);
|
||||||
|
let block = function.blocks.get_unchecked(block_v.1);
|
||||||
|
|
||||||
|
if block.instructions.len() > 0 || block.data.terminator.is_some() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for other in &function.blocks {
|
||||||
|
if let Some(term) = &other.data.terminator {
|
||||||
|
match term {
|
||||||
|
TerminatorKind::Ret(_) => {}
|
||||||
|
TerminatorKind::RetVoid => {}
|
||||||
|
TerminatorKind::Br(other_val) => {
|
||||||
|
if other_val == &block_v {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TerminatorKind::CondBr(_, then_other_v, else_other_v) => {
|
||||||
|
if then_other_v == &block_v || else_other_v == &block_v {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstructionValue {
|
||||||
|
pub(crate) fn get_type(&self, builder: &Builder) -> Result<Type, ()> {
|
||||||
|
use Instr::*;
|
||||||
|
unsafe {
|
||||||
|
match &builder.instr_data(self).kind {
|
||||||
|
Param(nth) => builder
|
||||||
|
.function_data(&self.0.0)
|
||||||
|
.params
|
||||||
|
.get(*nth)
|
||||||
|
.cloned()
|
||||||
|
.ok_or(()),
|
||||||
|
Constant(c) => Ok(c.get_type()),
|
||||||
|
Add(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||||
|
Sub(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||||
|
Mult(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||||
|
And(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||||
|
ICmp(_, _, _) => Ok(Type::Bool),
|
||||||
|
FunctionCall(function_value, _) => Ok(builder.function_data(function_value).ret),
|
||||||
|
Phi(values) => values.first().ok_or(()).and_then(|v| v.get_type(&builder)),
|
||||||
|
Alloca(_, ty) => Ok(Type::Ptr(Box::new(ty.clone()))),
|
||||||
|
Load(_, ty) => Ok(Type::Ptr(Box::new(ty.clone()))),
|
||||||
|
Store(_, value) => value.get_type(builder),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConstValue {
|
||||||
|
pub fn get_type(&self) -> Type {
|
||||||
|
use Type::*;
|
||||||
|
match self {
|
||||||
|
ConstValue::I8(_) => I8,
|
||||||
|
ConstValue::I16(_) => I16,
|
||||||
|
ConstValue::I32(_) => I32,
|
||||||
|
ConstValue::I64(_) => I64,
|
||||||
|
ConstValue::I128(_) => I128,
|
||||||
|
ConstValue::U8(_) => U8,
|
||||||
|
ConstValue::U16(_) => U16,
|
||||||
|
ConstValue::U32(_) => U32,
|
||||||
|
ConstValue::U64(_) => U64,
|
||||||
|
ConstValue::U128(_) => U128,
|
||||||
|
ConstValue::Bool(_) => Bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Type {
|
||||||
|
pub fn comparable(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Type::I8 => true,
|
||||||
|
Type::I16 => true,
|
||||||
|
Type::I32 => true,
|
||||||
|
Type::I64 => true,
|
||||||
|
Type::I128 => true,
|
||||||
|
Type::U8 => true,
|
||||||
|
Type::U16 => true,
|
||||||
|
Type::U32 => true,
|
||||||
|
Type::U64 => true,
|
||||||
|
Type::U128 => true,
|
||||||
|
Type::Bool => true,
|
||||||
|
Type::Void => false,
|
||||||
|
Type::Ptr(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn signed(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Type::I8 => true,
|
||||||
|
Type::I16 => true,
|
||||||
|
Type::I32 => true,
|
||||||
|
Type::I64 => true,
|
||||||
|
Type::I128 => true,
|
||||||
|
Type::U8 => false,
|
||||||
|
Type::U16 => false,
|
||||||
|
Type::U32 => false,
|
||||||
|
Type::U64 => false,
|
||||||
|
Type::U128 => false,
|
||||||
|
Type::Bool => false,
|
||||||
|
Type::Void => false,
|
||||||
|
Type::Ptr(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TerminatorKind {
|
||||||
|
pub(crate) fn get_type(&self, builder: &Builder) -> Result<Type, ()> {
|
||||||
|
use TerminatorKind::*;
|
||||||
|
match self {
|
||||||
|
Ret(instr_val) => instr_val.get_type(builder),
|
||||||
|
RetVoid => Ok(Type::Void),
|
||||||
|
Br(_) => Ok(Type::Void),
|
||||||
|
CondBr(_, _, _) => Ok(Type::Void),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
451
reid-llvm-lib/src/compile.rs
Normal file
451
reid-llvm-lib/src/compile.rs
Normal file
@ -0,0 +1,451 @@
|
|||||||
|
//! This module contains all of the relevant code that is needed to compile the
|
||||||
|
//! LLIR ([`Context`]) into LLVM IR. This module is the only one that interfaces
|
||||||
|
//! with the LLVM API.
|
||||||
|
|
||||||
|
use std::{collections::HashMap, ffi::CString, ptr::null_mut};
|
||||||
|
|
||||||
|
use llvm_sys::{
|
||||||
|
LLVMIntPredicate,
|
||||||
|
analysis::LLVMVerifyModule,
|
||||||
|
core::*,
|
||||||
|
prelude::*,
|
||||||
|
target::{
|
||||||
|
LLVM_InitializeAllAsmParsers, LLVM_InitializeAllAsmPrinters, LLVM_InitializeAllTargetInfos,
|
||||||
|
LLVM_InitializeAllTargetMCs, LLVM_InitializeAllTargets, LLVMSetModuleDataLayout,
|
||||||
|
},
|
||||||
|
target_machine::{
|
||||||
|
LLVMCodeGenFileType, LLVMCreateTargetDataLayout, LLVMCreateTargetMachine,
|
||||||
|
LLVMGetDefaultTargetTriple, LLVMGetTargetFromTriple, LLVMTargetMachineEmitToFile,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::util::{ErrorMessageHolder, from_cstring, into_cstring};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
CmpPredicate, ConstValue, Context, TerminatorKind, Type,
|
||||||
|
builder::{
|
||||||
|
BlockHolder, BlockValue, Builder, FunctionHolder, FunctionValue, InstructionHolder,
|
||||||
|
InstructionValue, ModuleHolder,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct LLVMContext {
|
||||||
|
context_ref: LLVMContextRef,
|
||||||
|
builder_ref: LLVMBuilderRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
pub fn compile(&self) {
|
||||||
|
unsafe {
|
||||||
|
let context_ref = LLVMContextCreate();
|
||||||
|
|
||||||
|
let context = LLVMContext {
|
||||||
|
context_ref,
|
||||||
|
builder_ref: LLVMCreateBuilderInContext(context_ref),
|
||||||
|
};
|
||||||
|
|
||||||
|
for holder in self.builder.get_modules().borrow().iter() {
|
||||||
|
holder.compile(&context, &self.builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMDisposeBuilder(context.builder_ref);
|
||||||
|
LLVMContextDispose(context.context_ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LLVMModule<'a> {
|
||||||
|
builder: &'a Builder,
|
||||||
|
context_ref: LLVMContextRef,
|
||||||
|
builder_ref: LLVMBuilderRef,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
module_ref: LLVMModuleRef,
|
||||||
|
functions: HashMap<FunctionValue, LLVMFunction>,
|
||||||
|
blocks: HashMap<BlockValue, LLVMBasicBlockRef>,
|
||||||
|
values: HashMap<InstructionValue, LLVMValue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct LLVMFunction {
|
||||||
|
type_ref: LLVMTypeRef,
|
||||||
|
value_ref: LLVMValueRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LLVMValue {
|
||||||
|
_ty: Type,
|
||||||
|
value_ref: LLVMValueRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleHolder {
|
||||||
|
fn compile(&self, context: &LLVMContext, builder: &Builder) {
|
||||||
|
unsafe {
|
||||||
|
let module_ref = LLVMModuleCreateWithNameInContext(
|
||||||
|
into_cstring(&self.data.name).as_ptr(),
|
||||||
|
context.context_ref,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Compile the contents
|
||||||
|
|
||||||
|
let mut functions = HashMap::new();
|
||||||
|
|
||||||
|
for function in &self.functions {
|
||||||
|
functions.insert(
|
||||||
|
function.value,
|
||||||
|
function.compile_signature(context, module_ref),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut module = LLVMModule {
|
||||||
|
builder,
|
||||||
|
context_ref: context.context_ref,
|
||||||
|
builder_ref: context.builder_ref,
|
||||||
|
module_ref,
|
||||||
|
functions,
|
||||||
|
blocks: HashMap::new(),
|
||||||
|
values: HashMap::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
for function in &self.functions {
|
||||||
|
function.compile(&mut module);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVM_InitializeAllTargets();
|
||||||
|
LLVM_InitializeAllTargetInfos();
|
||||||
|
LLVM_InitializeAllTargetMCs();
|
||||||
|
LLVM_InitializeAllAsmParsers();
|
||||||
|
LLVM_InitializeAllAsmPrinters();
|
||||||
|
|
||||||
|
let triple = LLVMGetDefaultTargetTriple();
|
||||||
|
|
||||||
|
let mut target: _ = null_mut();
|
||||||
|
let mut err = ErrorMessageHolder::null();
|
||||||
|
LLVMGetTargetFromTriple(triple, &mut target, err.borrow_mut());
|
||||||
|
println!("{:?}, {:?}", from_cstring(triple), target);
|
||||||
|
err.into_result().unwrap();
|
||||||
|
|
||||||
|
let target_machine = LLVMCreateTargetMachine(
|
||||||
|
target,
|
||||||
|
triple,
|
||||||
|
c"generic".as_ptr(),
|
||||||
|
c"".as_ptr(),
|
||||||
|
llvm_sys::target_machine::LLVMCodeGenOptLevel::LLVMCodeGenLevelNone,
|
||||||
|
llvm_sys::target_machine::LLVMRelocMode::LLVMRelocDefault,
|
||||||
|
llvm_sys::target_machine::LLVMCodeModel::LLVMCodeModelDefault,
|
||||||
|
);
|
||||||
|
|
||||||
|
let data_layout = LLVMCreateTargetDataLayout(target_machine);
|
||||||
|
LLVMSetTarget(module_ref, triple);
|
||||||
|
LLVMSetModuleDataLayout(module_ref, data_layout);
|
||||||
|
|
||||||
|
let mut err = ErrorMessageHolder::null();
|
||||||
|
LLVMVerifyModule(
|
||||||
|
module_ref,
|
||||||
|
llvm_sys::analysis::LLVMVerifierFailureAction::LLVMPrintMessageAction,
|
||||||
|
err.borrow_mut(),
|
||||||
|
);
|
||||||
|
err.into_result().unwrap();
|
||||||
|
|
||||||
|
let mut err = ErrorMessageHolder::null();
|
||||||
|
LLVMTargetMachineEmitToFile(
|
||||||
|
target_machine,
|
||||||
|
module_ref,
|
||||||
|
CString::new("hello.asm").unwrap().into_raw(),
|
||||||
|
LLVMCodeGenFileType::LLVMAssemblyFile,
|
||||||
|
err.borrow_mut(),
|
||||||
|
);
|
||||||
|
err.into_result().unwrap();
|
||||||
|
|
||||||
|
let mut err = ErrorMessageHolder::null();
|
||||||
|
LLVMTargetMachineEmitToFile(
|
||||||
|
target_machine,
|
||||||
|
module_ref,
|
||||||
|
CString::new("hello.o").unwrap().into_raw(),
|
||||||
|
LLVMCodeGenFileType::LLVMObjectFile,
|
||||||
|
err.borrow_mut(),
|
||||||
|
);
|
||||||
|
err.into_result().unwrap();
|
||||||
|
|
||||||
|
let module_str = from_cstring(LLVMPrintModuleToString(module_ref));
|
||||||
|
println!("{}", module_str.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FunctionHolder {
|
||||||
|
unsafe fn compile_signature(
|
||||||
|
&self,
|
||||||
|
context: &LLVMContext,
|
||||||
|
module_ref: LLVMModuleRef,
|
||||||
|
) -> LLVMFunction {
|
||||||
|
unsafe {
|
||||||
|
let ret_type = self.data.ret.as_llvm(context.context_ref);
|
||||||
|
let mut param_types: Vec<LLVMTypeRef> = self
|
||||||
|
.data
|
||||||
|
.params
|
||||||
|
.iter()
|
||||||
|
.map(|t| t.as_llvm(context.context_ref))
|
||||||
|
.collect();
|
||||||
|
let param_ptr = param_types.as_mut_ptr();
|
||||||
|
let param_len = param_types.len();
|
||||||
|
|
||||||
|
let fn_type = LLVMFunctionType(ret_type, param_ptr, param_len as u32, 0);
|
||||||
|
|
||||||
|
let function_ref =
|
||||||
|
LLVMAddFunction(module_ref, into_cstring(&self.data.name).as_ptr(), fn_type);
|
||||||
|
|
||||||
|
LLVMFunction {
|
||||||
|
type_ref: fn_type,
|
||||||
|
value_ref: function_ref,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn compile(&self, module: &mut LLVMModule) {
|
||||||
|
unsafe {
|
||||||
|
let own_function = *module.functions.get(&self.value).unwrap();
|
||||||
|
|
||||||
|
for block in &self.blocks {
|
||||||
|
if block.data.deleted {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let block_ref = LLVMCreateBasicBlockInContext(
|
||||||
|
module.context_ref,
|
||||||
|
into_cstring(&self.data.name).as_ptr(),
|
||||||
|
);
|
||||||
|
LLVMAppendExistingBasicBlock(own_function.value_ref, block_ref);
|
||||||
|
module.blocks.insert(block.value, block_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
for block in &self.blocks {
|
||||||
|
block.compile(module, &own_function);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockHolder {
|
||||||
|
unsafe fn compile(&self, module: &mut LLVMModule, function: &LLVMFunction) {
|
||||||
|
unsafe {
|
||||||
|
if self.data.deleted {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let block_ref = *module.blocks.get(&self.value).unwrap();
|
||||||
|
LLVMPositionBuilderAtEnd(module.builder_ref, block_ref);
|
||||||
|
|
||||||
|
for instruction in &self.instructions {
|
||||||
|
let key = instruction.value;
|
||||||
|
let ret = instruction.compile(module, function, block_ref);
|
||||||
|
module.values.insert(key, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.data
|
||||||
|
.terminator
|
||||||
|
.clone()
|
||||||
|
.expect(&format!(
|
||||||
|
"Block {} does not have a terminator!",
|
||||||
|
self.data.name
|
||||||
|
))
|
||||||
|
.compile(module, function, block_ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstructionHolder {
|
||||||
|
unsafe fn compile(
|
||||||
|
&self,
|
||||||
|
module: &LLVMModule,
|
||||||
|
function: &LLVMFunction,
|
||||||
|
_block: LLVMBasicBlockRef,
|
||||||
|
) -> LLVMValue {
|
||||||
|
let _ty = self.value.get_type(module.builder).unwrap();
|
||||||
|
let val = unsafe {
|
||||||
|
use super::Instr::*;
|
||||||
|
match &self.data.kind {
|
||||||
|
Param(nth) => LLVMGetParam(function.value_ref, *nth as u32),
|
||||||
|
Constant(val) => val.as_llvm(module.context_ref),
|
||||||
|
Add(lhs, rhs) => {
|
||||||
|
let lhs_val = module.values.get(&lhs).unwrap().value_ref;
|
||||||
|
let rhs_val = module.values.get(&rhs).unwrap().value_ref;
|
||||||
|
LLVMBuildAdd(module.builder_ref, lhs_val, rhs_val, c"add".as_ptr())
|
||||||
|
}
|
||||||
|
Sub(lhs, rhs) => {
|
||||||
|
let lhs_val = module.values.get(&lhs).unwrap().value_ref;
|
||||||
|
let rhs_val = module.values.get(&rhs).unwrap().value_ref;
|
||||||
|
LLVMBuildSub(module.builder_ref, lhs_val, rhs_val, c"sub".as_ptr())
|
||||||
|
}
|
||||||
|
Mult(lhs, rhs) => {
|
||||||
|
let lhs_val = module.values.get(&lhs).unwrap().value_ref;
|
||||||
|
let rhs_val = module.values.get(&rhs).unwrap().value_ref;
|
||||||
|
LLVMBuildMul(module.builder_ref, lhs_val, rhs_val, c"mul".as_ptr())
|
||||||
|
}
|
||||||
|
And(lhs, rhs) => {
|
||||||
|
let lhs_val = module.values.get(&lhs).unwrap().value_ref;
|
||||||
|
let rhs_val = module.values.get(&rhs).unwrap().value_ref;
|
||||||
|
LLVMBuildAnd(module.builder_ref, lhs_val, rhs_val, c"and".as_ptr())
|
||||||
|
}
|
||||||
|
ICmp(pred, lhs, rhs) => {
|
||||||
|
let lhs = module.values.get(&lhs).unwrap();
|
||||||
|
let rhs_val = module.values.get(&rhs).unwrap().value_ref;
|
||||||
|
LLVMBuildICmp(
|
||||||
|
module.builder_ref,
|
||||||
|
// Signedness from LHS
|
||||||
|
pred.as_llvm_int(lhs._ty.signed()),
|
||||||
|
lhs.value_ref,
|
||||||
|
rhs_val,
|
||||||
|
c"icmp".as_ptr(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
FunctionCall(function_value, instruction_values) => {
|
||||||
|
let fun = module.functions.get(&function_value).unwrap();
|
||||||
|
let mut param_list: Vec<LLVMValueRef> = instruction_values
|
||||||
|
.iter()
|
||||||
|
.map(|i| module.values.get(i).unwrap().value_ref)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
LLVMBuildCall2(
|
||||||
|
module.builder_ref,
|
||||||
|
fun.type_ref,
|
||||||
|
fun.value_ref,
|
||||||
|
param_list.as_mut_ptr(),
|
||||||
|
param_list.len() as u32,
|
||||||
|
c"call".as_ptr(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Phi(values) => {
|
||||||
|
let mut inc_values = Vec::new();
|
||||||
|
let mut inc_blocks = Vec::new();
|
||||||
|
for item in values {
|
||||||
|
inc_values.push(module.values.get(&item).unwrap().value_ref);
|
||||||
|
inc_blocks.push(*module.blocks.get(&item.0).unwrap());
|
||||||
|
}
|
||||||
|
let phi = LLVMBuildPhi(
|
||||||
|
module.builder_ref,
|
||||||
|
_ty.as_llvm(module.context_ref),
|
||||||
|
c"phi".as_ptr(),
|
||||||
|
);
|
||||||
|
LLVMAddIncoming(
|
||||||
|
phi,
|
||||||
|
inc_values.as_mut_ptr(),
|
||||||
|
inc_blocks.as_mut_ptr(),
|
||||||
|
values.len() as u32,
|
||||||
|
);
|
||||||
|
phi
|
||||||
|
}
|
||||||
|
Alloca(name, ty) => LLVMBuildAlloca(
|
||||||
|
module.builder_ref,
|
||||||
|
ty.as_llvm(module.context_ref),
|
||||||
|
into_cstring(name).as_ptr(),
|
||||||
|
),
|
||||||
|
Load(ptr, ty) => LLVMBuildLoad2(
|
||||||
|
module.builder_ref,
|
||||||
|
ty.as_llvm(module.context_ref),
|
||||||
|
module.values.get(&ptr).unwrap().value_ref,
|
||||||
|
c"load".as_ptr(),
|
||||||
|
),
|
||||||
|
Store(ptr, val) => LLVMBuildStore(
|
||||||
|
module.builder_ref,
|
||||||
|
module.values.get(&val).unwrap().value_ref,
|
||||||
|
module.values.get(&ptr).unwrap().value_ref,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
LLVMValue {
|
||||||
|
_ty,
|
||||||
|
value_ref: val,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TerminatorKind {
|
||||||
|
fn compile(
|
||||||
|
&self,
|
||||||
|
module: &LLVMModule,
|
||||||
|
_function: &LLVMFunction,
|
||||||
|
_block: LLVMBasicBlockRef,
|
||||||
|
) -> LLVMValue {
|
||||||
|
let _ty = self.get_type(module.builder).unwrap();
|
||||||
|
let val = unsafe {
|
||||||
|
match self {
|
||||||
|
TerminatorKind::Ret(val) => {
|
||||||
|
let value = module.values.get(val).unwrap();
|
||||||
|
LLVMBuildRet(module.builder_ref, value.value_ref)
|
||||||
|
}
|
||||||
|
TerminatorKind::RetVoid => LLVMBuildRetVoid(module.builder_ref),
|
||||||
|
TerminatorKind::Br(block_value) => {
|
||||||
|
let dest = *module.blocks.get(block_value).unwrap();
|
||||||
|
LLVMBuildBr(module.builder_ref, dest)
|
||||||
|
}
|
||||||
|
TerminatorKind::CondBr(cond, then_b, else_b) => {
|
||||||
|
let cond_val = module.values.get(cond).unwrap().value_ref;
|
||||||
|
let then_bb = *module.blocks.get(then_b).unwrap();
|
||||||
|
let else_bb = *module.blocks.get(else_b).unwrap();
|
||||||
|
LLVMBuildCondBr(module.builder_ref, cond_val, then_bb, else_bb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
LLVMValue {
|
||||||
|
_ty,
|
||||||
|
value_ref: val,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CmpPredicate {
|
||||||
|
fn as_llvm_int(&self, signed: bool) -> LLVMIntPredicate {
|
||||||
|
use CmpPredicate::*;
|
||||||
|
use LLVMIntPredicate::*;
|
||||||
|
match (self, signed) {
|
||||||
|
(LT, true) => LLVMIntSLT,
|
||||||
|
(LE, true) => LLVMIntSLE,
|
||||||
|
(GT, true) => LLVMIntSGT,
|
||||||
|
(GE, true) => LLVMIntSGE,
|
||||||
|
(LT, false) => LLVMIntULT,
|
||||||
|
(LE, false) => LLVMIntULE,
|
||||||
|
(GT, false) => LLVMIntUGT,
|
||||||
|
(GE, false) => LLVMIntUGE,
|
||||||
|
(EQ, _) => LLVMIntEQ,
|
||||||
|
(NE, _) => LLVMIntNE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConstValue {
|
||||||
|
fn as_llvm(&self, context: LLVMContextRef) -> LLVMValueRef {
|
||||||
|
unsafe {
|
||||||
|
let t = self.get_type().as_llvm(context);
|
||||||
|
match *self {
|
||||||
|
ConstValue::Bool(val) => LLVMConstInt(t, val as u64, 1),
|
||||||
|
ConstValue::I8(val) => LLVMConstInt(t, val as u64, 1),
|
||||||
|
ConstValue::I16(val) => LLVMConstInt(t, val as u64, 1),
|
||||||
|
ConstValue::I32(val) => LLVMConstInt(t, val as u64, 1),
|
||||||
|
ConstValue::I64(val) => LLVMConstInt(t, val as u64, 1),
|
||||||
|
ConstValue::I128(val) => LLVMConstInt(t, val as u64, 1),
|
||||||
|
ConstValue::U8(val) => LLVMConstInt(t, val as u64, 1),
|
||||||
|
ConstValue::U16(val) => LLVMConstInt(t, val as u64, 1),
|
||||||
|
ConstValue::U32(val) => LLVMConstInt(t, val as u64, 1),
|
||||||
|
ConstValue::U64(val) => LLVMConstInt(t, val as u64, 1),
|
||||||
|
ConstValue::U128(val) => LLVMConstInt(t, val as u64, 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Type {
|
||||||
|
fn as_llvm(&self, context: LLVMContextRef) -> LLVMTypeRef {
|
||||||
|
use Type::*;
|
||||||
|
unsafe {
|
||||||
|
match self {
|
||||||
|
I8 | U8 => LLVMInt8TypeInContext(context),
|
||||||
|
I16 | U16 => LLVMInt16TypeInContext(context),
|
||||||
|
I32 | U32 => LLVMInt32TypeInContext(context),
|
||||||
|
I64 | U64 => LLVMInt64TypeInContext(context),
|
||||||
|
I128 | U128 => LLVMInt128TypeInContext(context),
|
||||||
|
Bool => LLVMInt1TypeInContext(context),
|
||||||
|
Void => LLVMVoidType(),
|
||||||
|
Ptr(ty) => LLVMPointerType(ty.as_llvm(context), 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
167
reid-llvm-lib/src/debug.rs
Normal file
167
reid-llvm-lib/src/debug.rs
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
//! Debug implementations for relevant types
|
||||||
|
|
||||||
|
use std::fmt::{Debug, Write};
|
||||||
|
|
||||||
|
use crate::{CmpPredicate, Instr, InstructionData, TerminatorKind, builder::*};
|
||||||
|
|
||||||
|
impl Debug for Builder {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_list().entries(self.get_modules().borrow().iter());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for ModuleHolder {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_tuple(&format!("{}({:#?}) ", self.data.name, self.value))
|
||||||
|
.field(&self.functions)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for FunctionHolder {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_tuple(&format!(
|
||||||
|
"{}({:?}) -> {:?} ",
|
||||||
|
self.data.name, self.data.params, self.data.ret
|
||||||
|
))
|
||||||
|
.field(&self.blocks)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for BlockHolder {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let deleted = if self.data.deleted { " (deleted)" } else { "" };
|
||||||
|
f.debug_tuple(&format!(
|
||||||
|
"{}[{:?}]{} ",
|
||||||
|
&self.data.name, &self.value, deleted
|
||||||
|
))
|
||||||
|
.field(&self.instructions)
|
||||||
|
.field(&self.data.terminator)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for InstructionHolder {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.value.fmt(f)?;
|
||||||
|
f.write_str(" = ")?;
|
||||||
|
self.data.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for InstructionData {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.kind.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for ModuleValue {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "M[{:0>2}]", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for FunctionValue {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "F[{:0>2}-{:0>2}]", &self.0.0, self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for BlockValue {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "B[{:0>2}-{:0>2}-{:0>2}]", &self.0.0.0, &self.0.1, self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for InstructionValue {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"I[{:0>2}-{:0>2}-{:0>2}-{:0>2}]",
|
||||||
|
&self.0.0.0.0, &self.0.0.1, &self.0.1, self.1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Instr {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Param(nth) => fmt_call(f, &"Param", &nth),
|
||||||
|
Self::Constant(c) => c.fmt(f),
|
||||||
|
Self::Add(lhs, rhs) => fmt_binop(f, lhs, &"+", rhs),
|
||||||
|
Self::Sub(lhs, rhs) => fmt_binop(f, lhs, &"-", rhs),
|
||||||
|
Self::Mult(lhs, rhs) => fmt_binop(f, lhs, &"*", rhs),
|
||||||
|
Self::And(lhs, rhs) => fmt_binop(f, lhs, &"&&", rhs),
|
||||||
|
Self::Phi(val) => fmt_call(f, &"Phi", &val),
|
||||||
|
Self::ICmp(cmp, lhs, rhs) => fmt_binop(f, lhs, cmp, rhs),
|
||||||
|
Self::FunctionCall(fun, params) => fmt_call(f, fun, params),
|
||||||
|
Instr::Alloca(name, ty) => write!(f, "alloca<{:?}>({})", ty, name),
|
||||||
|
Instr::Load(val, ty) => write!(f, "load<{:?}>({:?})", ty, val),
|
||||||
|
Instr::Store(ptr, val) => write!(f, "store({:?} = {:?})", ptr, val),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_binop(
|
||||||
|
f: &mut std::fmt::Formatter<'_>,
|
||||||
|
lhs: &impl std::fmt::Debug,
|
||||||
|
op: &impl std::fmt::Debug,
|
||||||
|
rhs: &impl std::fmt::Debug,
|
||||||
|
) -> std::fmt::Result {
|
||||||
|
lhs.fmt(f)?;
|
||||||
|
f.write_char(' ')?;
|
||||||
|
op.fmt(f)?;
|
||||||
|
f.write_char(' ')?;
|
||||||
|
rhs.fmt(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_call(
|
||||||
|
f: &mut std::fmt::Formatter<'_>,
|
||||||
|
fun: &impl std::fmt::Debug,
|
||||||
|
params: &impl std::fmt::Debug,
|
||||||
|
) -> std::fmt::Result {
|
||||||
|
fun.fmt(f)?;
|
||||||
|
f.write_char('(')?;
|
||||||
|
params.fmt(f)?;
|
||||||
|
f.write_char(')')
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for CmpPredicate {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::LT => write!(f, "<"),
|
||||||
|
Self::GT => write!(f, ">"),
|
||||||
|
Self::LE => write!(f, "<="),
|
||||||
|
Self::GE => write!(f, ">="),
|
||||||
|
Self::EQ => write!(f, "=="),
|
||||||
|
Self::NE => write!(f, "!="),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for TerminatorKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Ret(val) => {
|
||||||
|
write!(f, "Ret ")?;
|
||||||
|
val.fmt(f)
|
||||||
|
}
|
||||||
|
Self::RetVoid => write!(f, "Void Ret"),
|
||||||
|
Self::Br(val) => {
|
||||||
|
write!(f, "Br ")?;
|
||||||
|
val.fmt(f)
|
||||||
|
}
|
||||||
|
Self::CondBr(cond, b1, b2) => {
|
||||||
|
write!(f, "CondBr ")?;
|
||||||
|
cond.fmt(f)?;
|
||||||
|
write!(f, " ? ")?;
|
||||||
|
b1.fmt(f)?;
|
||||||
|
write!(f, " : ")?;
|
||||||
|
b2.fmt(f)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,454 +1,225 @@
|
|||||||
use std::ffi::CString;
|
//! Reid LLVM Lib is an ergonomic Rust'y API which is used to produce a
|
||||||
|
//! Low-Level IR (LLIR) using [`Context`] and [`Builder`]. This Builder can then
|
||||||
|
//! be used at the end to compile said LLIR into LLVM IR.
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::net::Incoming;
|
|
||||||
use std::ptr::null_mut;
|
|
||||||
|
|
||||||
use llvm_sys::analysis::LLVMVerifyModule;
|
use builder::{BlockValue, Builder, FunctionValue, InstructionValue, ModuleValue};
|
||||||
use llvm_sys::target::{
|
|
||||||
LLVM_InitializeAllAsmParsers, LLVM_InitializeAllAsmPrinters, LLVM_InitializeAllTargetInfos,
|
|
||||||
LLVM_InitializeAllTargetMCs, LLVM_InitializeAllTargets, LLVMSetModuleDataLayout,
|
|
||||||
};
|
|
||||||
use llvm_sys::target_machine::{
|
|
||||||
LLVMCodeGenFileType, LLVMCreateTargetDataLayout, LLVMCreateTargetMachine,
|
|
||||||
LLVMGetDefaultTargetTriple, LLVMGetTargetFromTriple, LLVMTargetMachineEmitToFile,
|
|
||||||
};
|
|
||||||
use llvm_sys::{LLVMBuilder, LLVMContext, LLVMIntPredicate, core::*, prelude::*};
|
|
||||||
use types::{BasicType, BasicValue, FunctionType, IntegerType, Value};
|
|
||||||
use util::{ErrorMessageHolder, from_cstring, into_cstring};
|
|
||||||
|
|
||||||
pub mod types;
|
pub mod builder;
|
||||||
|
pub mod compile;
|
||||||
|
mod debug;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
pub enum IntPredicate {
|
// pub struct InstructionValue(BlockValue, usize);
|
||||||
SLT,
|
|
||||||
SGT,
|
|
||||||
|
|
||||||
ULT,
|
|
||||||
UGT,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntPredicate {
|
|
||||||
pub fn as_llvm(&self) -> LLVMIntPredicate {
|
|
||||||
match *self {
|
|
||||||
Self::SLT => LLVMIntPredicate::LLVMIntSLT,
|
|
||||||
Self::SGT => LLVMIntPredicate::LLVMIntSGT,
|
|
||||||
Self::ULT => LLVMIntPredicate::LLVMIntULT,
|
|
||||||
Self::UGT => LLVMIntPredicate::LLVMIntUGT,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
pub(crate) context_ref: *mut LLVMContext,
|
builder: Builder,
|
||||||
pub(crate) builder_ref: *mut LLVMBuilder,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
pub fn new() -> Context {
|
pub fn new() -> Context {
|
||||||
unsafe {
|
Context {
|
||||||
// Set up a context, module and builder in that context.
|
builder: Builder::new(),
|
||||||
let context = LLVMContextCreate();
|
|
||||||
let builder = LLVMCreateBuilderInContext(context);
|
|
||||||
|
|
||||||
Context {
|
|
||||||
context_ref: context,
|
|
||||||
builder_ref: builder,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_i1<'a>(&'a self) -> IntegerType<'a> {
|
pub fn module<'ctx>(&'ctx self, name: &str) -> Module<'ctx> {
|
||||||
IntegerType::in_context(&self, 1)
|
let value = self.builder.add_module(ModuleData {
|
||||||
}
|
name: name.to_owned(),
|
||||||
|
});
|
||||||
pub fn type_i8<'a>(&'a self) -> IntegerType<'a> {
|
Module {
|
||||||
IntegerType::in_context(&self, 8)
|
phantom: PhantomData,
|
||||||
}
|
builder: self.builder.clone(),
|
||||||
|
value,
|
||||||
pub fn type_i16<'a>(&'a self) -> IntegerType<'a> {
|
}
|
||||||
IntegerType::in_context(&self, 16)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn type_i32<'a>(&'a self) -> IntegerType<'a> {
|
|
||||||
IntegerType::in_context(&self, 32)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn module(&self, name: &str) -> Module {
|
|
||||||
Module::with_name(self, name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Context {
|
#[derive(Debug, Clone, Hash)]
|
||||||
fn drop(&mut self) {
|
pub struct ModuleData {
|
||||||
// Clean up. Values created in the context mostly get cleaned up there.
|
name: String,
|
||||||
unsafe {
|
|
||||||
LLVMDisposeBuilder(self.builder_ref);
|
|
||||||
LLVMContextDispose(self.context_ref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Module<'ctx> {
|
pub struct Module<'ctx> {
|
||||||
context: &'ctx Context,
|
phantom: PhantomData<&'ctx ()>,
|
||||||
module_ref: LLVMModuleRef,
|
builder: Builder,
|
||||||
name: CString,
|
value: ModuleValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx> Module<'ctx> {
|
impl<'ctx> Module<'ctx> {
|
||||||
fn with_name(context: &'ctx Context, name: &str) -> Module<'ctx> {
|
pub fn function(&mut self, name: &str, ret: Type, params: Vec<Type>) -> Function<'ctx> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let cstring_name = into_cstring(name);
|
|
||||||
let module_ref =
|
|
||||||
LLVMModuleCreateWithNameInContext(cstring_name.as_ptr(), context.context_ref);
|
|
||||||
Module {
|
|
||||||
context,
|
|
||||||
module_ref,
|
|
||||||
name: cstring_name,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_function(&'ctx self, fn_type: FunctionType<'ctx>, name: &str) -> Function<'ctx> {
|
|
||||||
unsafe {
|
|
||||||
let name_cstring = into_cstring(name);
|
|
||||||
let function_ref =
|
|
||||||
LLVMAddFunction(self.module_ref, name_cstring.as_ptr(), fn_type.llvm_type());
|
|
||||||
Function {
|
Function {
|
||||||
module: self,
|
phantom: PhantomData,
|
||||||
fn_type,
|
builder: self.builder.clone(),
|
||||||
name: name_cstring,
|
value: self.builder.add_function(
|
||||||
fn_ref: function_ref,
|
&self.value,
|
||||||
|
FunctionData {
|
||||||
|
name: name.to_owned(),
|
||||||
|
ret,
|
||||||
|
params,
|
||||||
|
},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_to_string(&self) -> Result<String, String> {
|
pub fn value(&self) -> ModuleValue {
|
||||||
unsafe {
|
self.value
|
||||||
LLVM_InitializeAllTargets();
|
|
||||||
LLVM_InitializeAllTargetInfos();
|
|
||||||
LLVM_InitializeAllTargetMCs();
|
|
||||||
LLVM_InitializeAllAsmParsers();
|
|
||||||
LLVM_InitializeAllAsmPrinters();
|
|
||||||
|
|
||||||
let triple = LLVMGetDefaultTargetTriple();
|
|
||||||
|
|
||||||
let mut target: _ = null_mut();
|
|
||||||
let mut err = ErrorMessageHolder::null();
|
|
||||||
LLVMGetTargetFromTriple(triple, &mut target, err.borrow_mut());
|
|
||||||
println!("{:?}, {:?}", from_cstring(triple), target);
|
|
||||||
err.into_result().unwrap();
|
|
||||||
|
|
||||||
let target_machine = LLVMCreateTargetMachine(
|
|
||||||
target,
|
|
||||||
triple,
|
|
||||||
c"generic".as_ptr(),
|
|
||||||
c"".as_ptr(),
|
|
||||||
llvm_sys::target_machine::LLVMCodeGenOptLevel::LLVMCodeGenLevelNone,
|
|
||||||
llvm_sys::target_machine::LLVMRelocMode::LLVMRelocDefault,
|
|
||||||
llvm_sys::target_machine::LLVMCodeModel::LLVMCodeModelDefault,
|
|
||||||
);
|
|
||||||
|
|
||||||
let data_layout = LLVMCreateTargetDataLayout(target_machine);
|
|
||||||
LLVMSetTarget(self.module_ref, triple);
|
|
||||||
LLVMSetModuleDataLayout(self.module_ref, data_layout);
|
|
||||||
|
|
||||||
let mut err = ErrorMessageHolder::null();
|
|
||||||
LLVMVerifyModule(
|
|
||||||
self.module_ref,
|
|
||||||
llvm_sys::analysis::LLVMVerifierFailureAction::LLVMPrintMessageAction,
|
|
||||||
err.borrow_mut(),
|
|
||||||
);
|
|
||||||
err.into_result().unwrap();
|
|
||||||
|
|
||||||
let mut err = ErrorMessageHolder::null();
|
|
||||||
LLVMTargetMachineEmitToFile(
|
|
||||||
target_machine,
|
|
||||||
self.module_ref,
|
|
||||||
CString::new("hello.asm").unwrap().into_raw(),
|
|
||||||
LLVMCodeGenFileType::LLVMAssemblyFile,
|
|
||||||
err.borrow_mut(),
|
|
||||||
);
|
|
||||||
err.into_result().unwrap();
|
|
||||||
|
|
||||||
let mut err = ErrorMessageHolder::null();
|
|
||||||
LLVMTargetMachineEmitToFile(
|
|
||||||
target_machine,
|
|
||||||
self.module_ref,
|
|
||||||
CString::new("hello.o").unwrap().into_raw(),
|
|
||||||
LLVMCodeGenFileType::LLVMObjectFile,
|
|
||||||
err.borrow_mut(),
|
|
||||||
);
|
|
||||||
err.into_result().unwrap();
|
|
||||||
|
|
||||||
from_cstring(LLVMPrintModuleToString(self.module_ref)).ok_or("UTF-8 error".to_owned())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Drop for Module<'a> {
|
#[derive(Debug, Clone, Hash)]
|
||||||
fn drop(&mut self) {
|
pub struct FunctionData {
|
||||||
// Clean up. Values created in the context mostly get cleaned up there.
|
name: String,
|
||||||
unsafe {
|
ret: Type,
|
||||||
LLVMDisposeModule(self.module_ref);
|
params: Vec<Type>,
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Function<'ctx> {
|
pub struct Function<'ctx> {
|
||||||
module: &'ctx Module<'ctx>,
|
phantom: PhantomData<&'ctx ()>,
|
||||||
name: CString,
|
builder: Builder,
|
||||||
fn_type: FunctionType<'ctx>,
|
value: FunctionValue,
|
||||||
fn_ref: LLVMValueRef,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx> Function<'ctx> {
|
impl<'ctx> Function<'ctx> {
|
||||||
pub fn block<T: Into<String>>(&'ctx self, name: T) -> BasicBlock<'ctx> {
|
pub fn block(&self, name: &str) -> Block<'ctx> {
|
||||||
BasicBlock::in_function(&self, name.into())
|
unsafe {
|
||||||
|
Block {
|
||||||
|
phantom: PhantomData,
|
||||||
|
builder: self.builder.clone(),
|
||||||
|
value: self.builder.add_block(
|
||||||
|
&self.value,
|
||||||
|
BlockData {
|
||||||
|
name: name.to_owned(),
|
||||||
|
terminator: None,
|
||||||
|
deleted: false,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_param<T: BasicValue<'ctx>>(
|
pub fn value(&self) -> FunctionValue {
|
||||||
&'ctx self,
|
self.value
|
||||||
nth: usize,
|
|
||||||
param_type: T::BaseType,
|
|
||||||
) -> Result<T, String> {
|
|
||||||
if let Some(actual_type) = self.fn_type.param_types.iter().nth(nth) {
|
|
||||||
if param_type.llvm_type() != *actual_type {
|
|
||||||
return Err(String::from("Wrong type"));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(String::from("nth too large"));
|
|
||||||
}
|
|
||||||
unsafe { Ok(T::from_llvm(LLVMGetParam(self.fn_ref, nth as u32))) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BasicBlock<'ctx> {
|
#[derive(Debug, Clone, Hash)]
|
||||||
function: &'ctx Function<'ctx>,
|
pub struct BlockData {
|
||||||
builder_ref: LLVMBuilderRef,
|
|
||||||
name: String,
|
name: String,
|
||||||
blockref: LLVMBasicBlockRef,
|
terminator: Option<TerminatorKind>,
|
||||||
inserted: bool,
|
deleted: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx> BasicBlock<'ctx> {
|
pub struct Block<'builder> {
|
||||||
fn in_function(function: &'ctx Function<'ctx>, name: String) -> BasicBlock<'ctx> {
|
phantom: PhantomData<&'builder ()>,
|
||||||
|
builder: Builder,
|
||||||
|
value: BlockValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'builder> Block<'builder> {
|
||||||
|
pub fn build(&mut self, instruction: Instr) -> Result<InstructionValue, ()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let block_name = into_cstring(name.clone());
|
self.builder
|
||||||
let block_ref = LLVMCreateBasicBlockInContext(
|
.add_instruction(&self.value, InstructionData { kind: instruction })
|
||||||
function.module.context.context_ref,
|
}
|
||||||
block_name.as_ptr(),
|
}
|
||||||
);
|
|
||||||
LLVMAppendExistingBasicBlock(function.fn_ref, block_ref);
|
pub fn terminate(&mut self, instruction: TerminatorKind) -> Result<(), ()> {
|
||||||
BasicBlock {
|
unsafe { self.builder.terminate(&self.value, instruction) }
|
||||||
function: function,
|
}
|
||||||
builder_ref: function.module.context.builder_ref,
|
|
||||||
name,
|
/// Delete block if it is unused. Return true if deleted, false if not.
|
||||||
blockref: block_ref,
|
pub fn delete_if_unused(&mut self) -> Result<bool, ()> {
|
||||||
inserted: false,
|
unsafe {
|
||||||
|
if !self.builder.is_block_used(self.value()) {
|
||||||
|
self.builder.delete_block(&self.value)?;
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
pub fn value(&self) -> BlockValue {
|
||||||
pub fn integer_compare<T: BasicValue<'ctx>>(
|
self.value
|
||||||
&self,
|
|
||||||
lhs: &T,
|
|
||||||
rhs: &T,
|
|
||||||
comparison: &IntPredicate,
|
|
||||||
name: &str,
|
|
||||||
) -> Result<T, ()> {
|
|
||||||
unsafe {
|
|
||||||
LLVMPositionBuilderAtEnd(self.builder_ref, self.blockref);
|
|
||||||
let value = LLVMBuildICmp(
|
|
||||||
self.builder_ref,
|
|
||||||
comparison.as_llvm(),
|
|
||||||
lhs.llvm_value(),
|
|
||||||
rhs.llvm_value(),
|
|
||||||
into_cstring(name).as_ptr(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(T::from_llvm(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn call<T: BasicValue<'ctx>>(
|
|
||||||
&self,
|
|
||||||
callee: &Function<'ctx>,
|
|
||||||
params: Vec<Value<'ctx>>,
|
|
||||||
name: &str,
|
|
||||||
) -> Result<T, ()> {
|
|
||||||
if params.len() != callee.fn_type.param_types.len() {
|
|
||||||
return Err(()); // TODO invalid amount of parameters
|
|
||||||
}
|
|
||||||
for (t1, t2) in callee.fn_type.param_types.iter().zip(¶ms) {
|
|
||||||
if t1 != &t2.llvm_type() {
|
|
||||||
return Err(()); // TODO wrong types in parameters
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !T::BaseType::is_type(callee.fn_type.return_type) {
|
|
||||||
return Err(()); // TODO wrong return type
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
let mut param_list: Vec<LLVMValueRef> = params.iter().map(|p| p.llvm_value()).collect();
|
|
||||||
LLVMPositionBuilderAtEnd(self.builder_ref, self.blockref);
|
|
||||||
let ret_val = LLVMBuildCall2(
|
|
||||||
self.builder_ref,
|
|
||||||
callee.fn_type.llvm_type(),
|
|
||||||
callee.fn_ref,
|
|
||||||
param_list.as_mut_ptr(),
|
|
||||||
param_list.len() as u32,
|
|
||||||
into_cstring(name).as_ptr(),
|
|
||||||
);
|
|
||||||
Ok(T::from_llvm(ret_val))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn add<T: BasicValue<'ctx>>(&self, lhs: &T, rhs: &T, name: &str) -> Result<T, ()> {
|
|
||||||
if lhs.llvm_type() != rhs.llvm_type() {
|
|
||||||
return Err(()); // TODO error
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
LLVMPositionBuilderAtEnd(self.builder_ref, self.blockref);
|
|
||||||
let add_value_ref = LLVMBuildAdd(
|
|
||||||
self.builder_ref,
|
|
||||||
lhs.llvm_value(),
|
|
||||||
rhs.llvm_value(),
|
|
||||||
into_cstring(name).as_ptr(),
|
|
||||||
);
|
|
||||||
Ok(T::from_llvm(add_value_ref))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn sub<T: BasicValue<'ctx>>(&self, lhs: &T, rhs: &T, name: &str) -> Result<T, ()> {
|
|
||||||
dbg!(lhs, rhs);
|
|
||||||
dbg!(lhs.llvm_type(), rhs.llvm_type());
|
|
||||||
if lhs.llvm_type() != rhs.llvm_type() {
|
|
||||||
return Err(()); // TODO error
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
LLVMPositionBuilderAtEnd(self.builder_ref, self.blockref);
|
|
||||||
let add_value_ref = LLVMBuildSub(
|
|
||||||
self.builder_ref,
|
|
||||||
lhs.llvm_value(),
|
|
||||||
rhs.llvm_value(),
|
|
||||||
into_cstring(name).as_ptr(),
|
|
||||||
);
|
|
||||||
Ok(T::from_llvm(add_value_ref))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn phi<PhiValue: BasicValue<'ctx>>(
|
|
||||||
&self,
|
|
||||||
phi_type: &PhiValue::BaseType,
|
|
||||||
name: &str,
|
|
||||||
) -> Result<PhiBuilder<'ctx, PhiValue>, ()> {
|
|
||||||
unsafe {
|
|
||||||
LLVMPositionBuilderAtEnd(self.builder_ref, self.blockref);
|
|
||||||
let phi_node = LLVMBuildPhi(
|
|
||||||
self.builder_ref,
|
|
||||||
phi_type.llvm_type(),
|
|
||||||
into_cstring(name).as_ptr(),
|
|
||||||
);
|
|
||||||
Ok(PhiBuilder::new(phi_node))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn br(&mut self, into: &BasicBlock<'ctx>) -> Result<(), ()> {
|
|
||||||
self.try_insert()?;
|
|
||||||
unsafe {
|
|
||||||
LLVMPositionBuilderAtEnd(self.builder_ref, self.blockref);
|
|
||||||
LLVMBuildBr(self.builder_ref, into.blockref);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn conditional_br<T: BasicValue<'ctx>>(
|
|
||||||
&mut self,
|
|
||||||
condition: &T,
|
|
||||||
lhs: &BasicBlock<'ctx>,
|
|
||||||
rhs: &BasicBlock<'ctx>,
|
|
||||||
) -> Result<(), ()> {
|
|
||||||
self.try_insert()?;
|
|
||||||
unsafe {
|
|
||||||
LLVMPositionBuilderAtEnd(self.builder_ref, self.blockref);
|
|
||||||
LLVMBuildCondBr(
|
|
||||||
self.builder_ref,
|
|
||||||
condition.llvm_value(),
|
|
||||||
lhs.blockref,
|
|
||||||
rhs.blockref,
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn ret<T: BasicValue<'ctx>>(&mut self, return_value: &T) -> Result<(), ()> {
|
|
||||||
if self.function.fn_type.return_type != return_value.llvm_type() {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
self.try_insert()?;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
LLVMPositionBuilderAtEnd(self.builder_ref, self.blockref);
|
|
||||||
LLVMBuildRet(self.builder_ref, return_value.llvm_value());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_insert(&mut self) -> Result<(), ()> {
|
|
||||||
if self.inserted {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
self.inserted = true;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx> Drop for BasicBlock<'ctx> {
|
#[derive(Clone, Hash)]
|
||||||
fn drop(&mut self) {
|
pub struct InstructionData {
|
||||||
if !self.inserted {
|
kind: Instr,
|
||||||
unsafe {
|
|
||||||
LLVMDeleteBasicBlock(self.blockref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PhiBuilder<'ctx, PhiValue: BasicValue<'ctx>> {
|
#[derive(Clone, Copy, Hash)]
|
||||||
phi_node: LLVMValueRef,
|
pub enum CmpPredicate {
|
||||||
phantom: PhantomData<&'ctx PhiValue>,
|
LT,
|
||||||
|
LE,
|
||||||
|
GT,
|
||||||
|
GE,
|
||||||
|
EQ,
|
||||||
|
NE,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx, PhiValue: BasicValue<'ctx>> PhiBuilder<'ctx, PhiValue> {
|
#[derive(Clone, Hash)]
|
||||||
fn new(phi_node: LLVMValueRef) -> PhiBuilder<'ctx, PhiValue> {
|
pub enum Instr {
|
||||||
PhiBuilder {
|
Param(usize),
|
||||||
phi_node,
|
Constant(ConstValue),
|
||||||
phantom: PhantomData,
|
Add(InstructionValue, InstructionValue),
|
||||||
}
|
Sub(InstructionValue, InstructionValue),
|
||||||
}
|
Mult(InstructionValue, InstructionValue),
|
||||||
|
And(InstructionValue, InstructionValue),
|
||||||
|
Phi(Vec<InstructionValue>),
|
||||||
|
|
||||||
pub fn add_incoming(&self, value: &PhiValue, block: &BasicBlock<'ctx>) -> &Self {
|
Alloca(String, Type),
|
||||||
let mut values = vec![value.llvm_value()];
|
Load(InstructionValue, Type),
|
||||||
let mut blocks = vec![block.blockref];
|
Store(InstructionValue, InstructionValue),
|
||||||
unsafe {
|
|
||||||
LLVMAddIncoming(
|
|
||||||
self.phi_node,
|
|
||||||
values.as_mut_ptr(),
|
|
||||||
blocks.as_mut_ptr(),
|
|
||||||
values.len() as u32,
|
|
||||||
);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build(&self) -> PhiValue {
|
/// Integer Comparison
|
||||||
unsafe { PhiValue::from_llvm(self.phi_node) }
|
ICmp(CmpPredicate, InstructionValue, InstructionValue),
|
||||||
}
|
|
||||||
|
FunctionCall(FunctionValue, Vec<InstructionValue>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||||
|
pub enum Type {
|
||||||
|
I8,
|
||||||
|
I16,
|
||||||
|
I32,
|
||||||
|
I64,
|
||||||
|
I128,
|
||||||
|
U8,
|
||||||
|
U16,
|
||||||
|
U32,
|
||||||
|
U64,
|
||||||
|
U128,
|
||||||
|
Bool,
|
||||||
|
Void,
|
||||||
|
Ptr(Box<Type>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Hash)]
|
||||||
|
pub enum ConstValue {
|
||||||
|
I8(i8),
|
||||||
|
I16(i16),
|
||||||
|
I32(i32),
|
||||||
|
I64(i64),
|
||||||
|
I128(i128),
|
||||||
|
U8(u8),
|
||||||
|
U16(u16),
|
||||||
|
U32(u32),
|
||||||
|
U64(u64),
|
||||||
|
U128(u128),
|
||||||
|
Bool(bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Hash)]
|
||||||
|
pub enum TerminatorKind {
|
||||||
|
Ret(InstructionValue),
|
||||||
|
RetVoid,
|
||||||
|
Br(BlockValue),
|
||||||
|
CondBr(InstructionValue, BlockValue, BlockValue),
|
||||||
}
|
}
|
||||||
|
@ -1,336 +0,0 @@
|
|||||||
use std::{any::Any, marker::PhantomData, ptr::null_mut};
|
|
||||||
|
|
||||||
use llvm_sys::{
|
|
||||||
LLVMTypeKind,
|
|
||||||
core::*,
|
|
||||||
prelude::{LLVMTypeRef, LLVMValueRef},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{BasicBlock, Context, PhiBuilder};
|
|
||||||
|
|
||||||
pub trait BasicType<'ctx> {
|
|
||||||
fn llvm_type(&self) -> LLVMTypeRef;
|
|
||||||
|
|
||||||
fn is_type(llvm_type: LLVMTypeRef) -> bool
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
|
|
||||||
unsafe fn from_llvm(context: &'ctx Context, llvm_type: LLVMTypeRef) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
|
|
||||||
fn function_type(&self, params: Vec<TypeEnum>) -> FunctionType<'ctx> {
|
|
||||||
unsafe {
|
|
||||||
let mut typerefs: Vec<LLVMTypeRef> = params.iter().map(|b| b.llvm_type()).collect();
|
|
||||||
let param_ptr = typerefs.as_mut_ptr();
|
|
||||||
let param_len = typerefs.len();
|
|
||||||
FunctionType {
|
|
||||||
phantom: PhantomData,
|
|
||||||
return_type: self.llvm_type(),
|
|
||||||
param_types: typerefs,
|
|
||||||
type_ref: LLVMFunctionType(self.llvm_type(), param_ptr, param_len as u32, 0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn array_type(&'ctx self, length: u32) -> ArrayType<'ctx>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
ArrayType {
|
|
||||||
phantom: PhantomData,
|
|
||||||
element_type: self.llvm_type(),
|
|
||||||
length,
|
|
||||||
type_ref: unsafe { LLVMArrayType(self.llvm_type(), length) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> PartialEq for &dyn BasicType<'ctx> {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.llvm_type() == other.llvm_type()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> PartialEq<LLVMTypeRef> for &dyn BasicType<'ctx> {
|
|
||||||
fn eq(&self, other: &LLVMTypeRef) -> bool {
|
|
||||||
self.llvm_type() == *other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct IntegerType<'ctx> {
|
|
||||||
context: &'ctx Context,
|
|
||||||
type_ref: LLVMTypeRef,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> BasicType<'ctx> for IntegerType<'ctx> {
|
|
||||||
fn llvm_type(&self) -> LLVMTypeRef {
|
|
||||||
self.type_ref
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn from_llvm(context: &'ctx Context, llvm_type: LLVMTypeRef) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
IntegerType {
|
|
||||||
context,
|
|
||||||
type_ref: llvm_type,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_type(llvm_type: LLVMTypeRef) -> bool {
|
|
||||||
unsafe { LLVMGetTypeKind(llvm_type) == LLVMTypeKind::LLVMIntegerTypeKind }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> IntegerType<'ctx> {
|
|
||||||
pub(crate) fn in_context(context: &Context, width: u32) -> IntegerType {
|
|
||||||
let type_ref = unsafe {
|
|
||||||
match width {
|
|
||||||
128 => LLVMInt128TypeInContext(context.context_ref),
|
|
||||||
64 => LLVMInt64TypeInContext(context.context_ref),
|
|
||||||
32 => LLVMInt32TypeInContext(context.context_ref),
|
|
||||||
16 => LLVMInt16TypeInContext(context.context_ref),
|
|
||||||
8 => LLVMInt8TypeInContext(context.context_ref),
|
|
||||||
1 => LLVMInt1TypeInContext(context.context_ref),
|
|
||||||
_ => LLVMIntTypeInContext(context.context_ref, width),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
IntegerType { context, type_ref }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_signed(&self, value: i64) -> IntegerValue<'ctx> {
|
|
||||||
self.from_const(value as u64, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_unsigned(&self, value: i64) -> IntegerValue<'ctx> {
|
|
||||||
self.from_const(value as u64, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_const(&self, value: u64, sign: bool) -> IntegerValue<'ctx> {
|
|
||||||
unsafe {
|
|
||||||
IntegerValue::from_llvm(LLVMConstInt(
|
|
||||||
self.type_ref,
|
|
||||||
value,
|
|
||||||
match sign {
|
|
||||||
true => 1,
|
|
||||||
false => 0,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct FunctionType<'ctx> {
|
|
||||||
phantom: PhantomData<&'ctx ()>,
|
|
||||||
pub(crate) return_type: LLVMTypeRef,
|
|
||||||
pub(crate) param_types: Vec<LLVMTypeRef>,
|
|
||||||
type_ref: LLVMTypeRef,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> BasicType<'ctx> for FunctionType<'ctx> {
|
|
||||||
fn llvm_type(&self) -> LLVMTypeRef {
|
|
||||||
self.type_ref
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn from_llvm(_context: &'ctx Context, fn_type: LLVMTypeRef) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
unsafe {
|
|
||||||
let param_count = LLVMCountParamTypes(fn_type);
|
|
||||||
let param_types_ptr: *mut LLVMTypeRef = null_mut();
|
|
||||||
LLVMGetParamTypes(fn_type, param_types_ptr);
|
|
||||||
let param_types: Vec<LLVMTypeRef> =
|
|
||||||
std::slice::from_raw_parts(param_types_ptr, param_count as usize)
|
|
||||||
.iter()
|
|
||||||
.map(|t| *t)
|
|
||||||
.collect();
|
|
||||||
FunctionType {
|
|
||||||
phantom: PhantomData,
|
|
||||||
return_type: LLVMGetReturnType(fn_type),
|
|
||||||
param_types,
|
|
||||||
type_ref: fn_type,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_type(llvm_type: LLVMTypeRef) -> bool {
|
|
||||||
unsafe { LLVMGetTypeKind(llvm_type) == LLVMTypeKind::LLVMFunctionTypeKind }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct ArrayType<'ctx> {
|
|
||||||
phantom: PhantomData<&'ctx ()>,
|
|
||||||
element_type: LLVMTypeRef,
|
|
||||||
length: u32,
|
|
||||||
type_ref: LLVMTypeRef,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> BasicType<'ctx> for ArrayType<'ctx> {
|
|
||||||
fn llvm_type(&self) -> LLVMTypeRef {
|
|
||||||
self.type_ref
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn from_llvm(context: &'ctx Context, llvm_type: LLVMTypeRef) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
unsafe {
|
|
||||||
let length = LLVMGetArrayLength(llvm_type);
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_type(llvm_type: LLVMTypeRef) -> bool {
|
|
||||||
unsafe { LLVMGetTypeKind(llvm_type) == LLVMTypeKind::LLVMArrayTypeKind }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum TypeEnum<'ctx> {
|
|
||||||
Integer(IntegerType<'ctx>),
|
|
||||||
Array(ArrayType<'ctx>),
|
|
||||||
Function(FunctionType<'ctx>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> From<IntegerType<'ctx>> for TypeEnum<'ctx> {
|
|
||||||
fn from(int: IntegerType<'ctx>) -> Self {
|
|
||||||
TypeEnum::Integer(int)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> From<ArrayType<'ctx>> for TypeEnum<'ctx> {
|
|
||||||
fn from(arr: ArrayType<'ctx>) -> Self {
|
|
||||||
TypeEnum::Array(arr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> From<FunctionType<'ctx>> for TypeEnum<'ctx> {
|
|
||||||
fn from(func: FunctionType<'ctx>) -> Self {
|
|
||||||
TypeEnum::Function(func)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> TypeEnum<'ctx> {
|
|
||||||
fn inner_basic(&'ctx self) -> &'ctx dyn BasicType<'ctx> {
|
|
||||||
match self {
|
|
||||||
TypeEnum::Integer(integer_type) => integer_type,
|
|
||||||
TypeEnum::Array(array_type) => array_type,
|
|
||||||
TypeEnum::Function(function_type) => function_type,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> BasicType<'ctx> for TypeEnum<'ctx> {
|
|
||||||
fn llvm_type(&self) -> LLVMTypeRef {
|
|
||||||
self.inner_basic().llvm_type()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_type(llvm_type: LLVMTypeRef) -> bool
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn from_llvm(context: &'ctx Context, llvm_type: LLVMTypeRef) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
unsafe {
|
|
||||||
match LLVMGetTypeKind(llvm_type) {
|
|
||||||
LLVMTypeKind::LLVMIntegerTypeKind => {
|
|
||||||
TypeEnum::Integer(IntegerType::from_llvm(context, llvm_type))
|
|
||||||
}
|
|
||||||
LLVMTypeKind::LLVMArrayTypeKind => {
|
|
||||||
TypeEnum::Array(ArrayType::from_llvm(context, llvm_type))
|
|
||||||
}
|
|
||||||
LLVMTypeKind::LLVMFunctionTypeKind => {
|
|
||||||
TypeEnum::Function(FunctionType::from_llvm(context, llvm_type))
|
|
||||||
}
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait BasicValue<'ctx>: std::fmt::Debug {
|
|
||||||
type BaseType: BasicType<'ctx>;
|
|
||||||
unsafe fn from_llvm(value: LLVMValueRef) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
fn llvm_value(&self) -> LLVMValueRef;
|
|
||||||
fn llvm_type(&self) -> LLVMTypeRef;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct IntegerValue<'ctx> {
|
|
||||||
phantom: PhantomData<&'ctx ()>,
|
|
||||||
pub(crate) value_ref: LLVMValueRef,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> BasicValue<'ctx> for IntegerValue<'ctx> {
|
|
||||||
type BaseType = IntegerType<'ctx>;
|
|
||||||
|
|
||||||
unsafe fn from_llvm(value: LLVMValueRef) -> Self {
|
|
||||||
IntegerValue {
|
|
||||||
phantom: PhantomData,
|
|
||||||
value_ref: value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn llvm_value(&self) -> LLVMValueRef {
|
|
||||||
self.value_ref
|
|
||||||
}
|
|
||||||
|
|
||||||
fn llvm_type(&self) -> LLVMTypeRef {
|
|
||||||
unsafe { LLVMTypeOf(self.value_ref) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum Value<'ctx> {
|
|
||||||
Integer(IntegerValue<'ctx>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> BasicValue<'ctx> for Value<'ctx> {
|
|
||||||
type BaseType = TypeEnum<'ctx>;
|
|
||||||
|
|
||||||
unsafe fn from_llvm(value: LLVMValueRef) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
unsafe {
|
|
||||||
use LLVMTypeKind::*;
|
|
||||||
|
|
||||||
let llvm_type = LLVMTypeOf(value);
|
|
||||||
let type_kind = LLVMGetTypeKind(llvm_type);
|
|
||||||
match type_kind {
|
|
||||||
LLVMIntegerTypeKind => Value::Integer(IntegerValue::from_llvm(value)),
|
|
||||||
_ => panic!("asd"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn llvm_value(&self) -> LLVMValueRef {
|
|
||||||
match self {
|
|
||||||
Self::Integer(i) => i.llvm_value(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn llvm_type(&self) -> LLVMTypeRef {
|
|
||||||
match self {
|
|
||||||
Self::Integer(i) => i.llvm_type(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> From<IntegerValue<'ctx>> for Value<'ctx> {
|
|
||||||
fn from(value: IntegerValue<'ctx>) -> Self {
|
|
||||||
Value::Integer(value)
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,6 +5,11 @@ use std::{
|
|||||||
|
|
||||||
use llvm_sys::error::LLVMDisposeErrorMessage;
|
use llvm_sys::error::LLVMDisposeErrorMessage;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
Type,
|
||||||
|
builder::{Builder, InstructionValue},
|
||||||
|
};
|
||||||
|
|
||||||
pub fn into_cstring<T: Into<String>>(value: T) -> CString {
|
pub fn into_cstring<T: Into<String>>(value: T) -> CString {
|
||||||
let string = value.into();
|
let string = value.into();
|
||||||
unsafe { CString::from_vec_with_nul_unchecked((string + "\0").into_bytes()) }
|
unsafe { CString::from_vec_with_nul_unchecked((string + "\0").into_bytes()) }
|
||||||
@ -24,6 +29,8 @@ fn cstring_to_err(value: *mut c_char) -> Result<(), String> {
|
|||||||
.map_or(Ok(()), |s| Err(s))
|
.map_or(Ok(()), |s| Err(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Utility struct for LLVM's Error Messages, which need to be disposed
|
||||||
|
/// manually.
|
||||||
pub struct ErrorMessageHolder(*mut c_char);
|
pub struct ErrorMessageHolder(*mut c_char);
|
||||||
|
|
||||||
impl ErrorMessageHolder {
|
impl ErrorMessageHolder {
|
||||||
@ -49,3 +56,19 @@ impl Drop for ErrorMessageHolder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Make sure types for given instructions match. Return Ok(type) if they do,
|
||||||
|
/// and error otherwise.
|
||||||
|
pub fn match_types(
|
||||||
|
lhs: &InstructionValue,
|
||||||
|
rhs: &InstructionValue,
|
||||||
|
builder: &Builder,
|
||||||
|
) -> Result<Type, ()> {
|
||||||
|
let lhs_type = lhs.get_type(&builder);
|
||||||
|
let rhs_type = rhs.get_type(&builder);
|
||||||
|
if let (Ok(lhs_t), Ok(rhs_t)) = (lhs_type, rhs_type) {
|
||||||
|
if lhs_t == rhs_t { Ok(lhs_t) } else { Err(()) }
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use reid::compile;
|
use reid::compile;
|
||||||
|
|
||||||
pub static ARITHMETIC: &str = include_str!("./reid/arithmetic.reid");
|
pub static MUTABLE: &str = include_str!("./reid/mutable.reid");
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let text = match compile(ARITHMETIC) {
|
let text = match compile(MUTABLE) {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(e) => panic!("{}", e),
|
Err(e) => panic!("{}", e),
|
||||||
};
|
};
|
@ -1,13 +0,0 @@
|
|||||||
// Arithmetic, function calls and imports!
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let test = 9;
|
|
||||||
let simpleAdd = 2 + 2;
|
|
||||||
let simpleSub = 7 - 2; // 14
|
|
||||||
|
|
||||||
if simpleAdd < test {
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
return arithmetic + simpleSub + boop;
|
|
||||||
}
|
|
@ -1,12 +1,12 @@
|
|||||||
// Main
|
// Main
|
||||||
fn main() {
|
fn main() -> bool {
|
||||||
return fibonacci(10);
|
return 5 == fibonacci(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fibonacci
|
// Fibonacci
|
||||||
fn fibonacci(value: i32) -> i32 {
|
fn fibonacci(value: u16) -> u16 {
|
||||||
if value < 3 {
|
if value <= 2 {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return fibonacci(value - 1) + fibonacci(value - 2);
|
fibonacci(value - 1) + fibonacci(value - 2)
|
||||||
}
|
}
|
||||||
|
15
reid/examples/reid/mutable.reid
Normal file
15
reid/examples/reid/mutable.reid
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Arithmetic, function calls and imports!
|
||||||
|
|
||||||
|
fn indirection() -> bool {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> u16 {
|
||||||
|
let mut test = 5;
|
||||||
|
|
||||||
|
if indirection() {
|
||||||
|
test = 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
return test;
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
use reid::mir::*;
|
use reid::mir::{self, *};
|
||||||
use reid_lib::Context;
|
use reid_lib::Context;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -7,6 +7,7 @@ fn main() {
|
|||||||
|
|
||||||
let fibonacci = FunctionDefinition {
|
let fibonacci = FunctionDefinition {
|
||||||
name: fibonacci_name.clone(),
|
name: fibonacci_name.clone(),
|
||||||
|
return_type: TypeKind::I32,
|
||||||
parameters: vec![(fibonacci_n.clone(), TypeKind::I32)],
|
parameters: vec![(fibonacci_n.clone(), TypeKind::I32)],
|
||||||
kind: FunctionDefinitionKind::Local(
|
kind: FunctionDefinitionKind::Local(
|
||||||
Block {
|
Block {
|
||||||
@ -16,7 +17,7 @@ fn main() {
|
|||||||
// If N < 3
|
// If N < 3
|
||||||
Box::new(Expression(
|
Box::new(Expression(
|
||||||
ExprKind::BinOp(
|
ExprKind::BinOp(
|
||||||
BinaryOperator::Logic(LogicOperator::GreaterThan),
|
BinaryOperator::Cmp(CmpOperator::GT),
|
||||||
Box::new(Expression(
|
Box::new(Expression(
|
||||||
ExprKind::Variable(VariableReference(
|
ExprKind::Variable(VariableReference(
|
||||||
TypeKind::I32,
|
TypeKind::I32,
|
||||||
@ -126,6 +127,7 @@ fn main() {
|
|||||||
|
|
||||||
let main = FunctionDefinition {
|
let main = FunctionDefinition {
|
||||||
name: "main".to_owned(),
|
name: "main".to_owned(),
|
||||||
|
return_type: TypeKind::I32,
|
||||||
parameters: vec![],
|
parameters: vec![],
|
||||||
kind: FunctionDefinitionKind::Local(
|
kind: FunctionDefinitionKind::Local(
|
||||||
Block {
|
Block {
|
||||||
@ -150,22 +152,24 @@ fn main() {
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mir_context = mir::Context {
|
||||||
|
modules: vec![Module {
|
||||||
|
name: "test module".to_owned(),
|
||||||
|
imports: vec![],
|
||||||
|
functions: vec![fibonacci, main],
|
||||||
|
}],
|
||||||
|
};
|
||||||
println!("test1");
|
println!("test1");
|
||||||
|
|
||||||
let module = Module {
|
|
||||||
name: "test module".to_owned(),
|
|
||||||
imports: vec![],
|
|
||||||
functions: vec![fibonacci, main],
|
|
||||||
};
|
|
||||||
|
|
||||||
println!("test2");
|
|
||||||
let context = Context::new();
|
let context = Context::new();
|
||||||
let codegen_module = module.codegen(&context);
|
let codegen = mir_context.codegen(&context);
|
||||||
|
println!("test2");
|
||||||
|
|
||||||
|
codegen.compile();
|
||||||
println!("test3");
|
println!("test3");
|
||||||
|
|
||||||
match codegen_module.module.print_to_string() {
|
// match codegen_module.module.print_to_string() {
|
||||||
Ok(v) => println!("{}", v),
|
// Ok(v) => println!("{}", v),
|
||||||
Err(e) => println!("Err: {:?}", e),
|
// Err(e) => println!("Err: {:?}", e),
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
//! This is the module that contains relevant code to parsing Reid, that is to
|
||||||
|
//! say transforming a Vec of FullTokens into a loose parsed AST that can be
|
||||||
|
//! used for unwrapping syntax sugar, and then be transformed into Reid MIR.
|
||||||
use crate::token_stream::TokenRange;
|
use crate::token_stream::TokenRange;
|
||||||
|
|
||||||
pub mod parse;
|
pub mod parse;
|
||||||
@ -8,12 +11,23 @@ pub struct Type(pub TypeKind, pub TokenRange);
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum TypeKind {
|
pub enum TypeKind {
|
||||||
|
Bool,
|
||||||
|
I8,
|
||||||
|
I16,
|
||||||
I32,
|
I32,
|
||||||
|
I64,
|
||||||
|
I128,
|
||||||
|
U8,
|
||||||
|
U16,
|
||||||
|
U32,
|
||||||
|
U64,
|
||||||
|
U128,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Literal {
|
pub enum Literal {
|
||||||
I32(i32),
|
Number(u64),
|
||||||
|
Bool(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -36,7 +50,12 @@ pub enum BinaryOperator {
|
|||||||
Mult,
|
Mult,
|
||||||
|
|
||||||
And,
|
And,
|
||||||
LessThan,
|
LT,
|
||||||
|
LE,
|
||||||
|
GT,
|
||||||
|
GE,
|
||||||
|
EQ,
|
||||||
|
NE,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BinaryOperator {
|
impl BinaryOperator {
|
||||||
@ -47,22 +66,43 @@ impl BinaryOperator {
|
|||||||
Minus => 10,
|
Minus => 10,
|
||||||
Mult => 20,
|
Mult => 20,
|
||||||
And => 100,
|
And => 100,
|
||||||
LessThan => 100,
|
LT => 100,
|
||||||
|
LE => 100,
|
||||||
|
GT => 100,
|
||||||
|
GE => 100,
|
||||||
|
EQ => 100,
|
||||||
|
NE => 100,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FunctionCallExpression(pub String, pub Vec<Expression>, pub TokenRange);
|
pub struct FunctionCallExpression(
|
||||||
|
pub String,
|
||||||
|
pub Vec<Expression>,
|
||||||
|
#[allow(dead_code)] pub TokenRange,
|
||||||
|
);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct IfExpression(pub Expression, pub Block, pub Option<Block>, pub TokenRange);
|
pub struct IfExpression(
|
||||||
|
pub Expression,
|
||||||
|
pub Block,
|
||||||
|
pub Option<Block>,
|
||||||
|
#[allow(dead_code)] pub TokenRange,
|
||||||
|
);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct LetStatement(pub String, pub Expression, pub TokenRange);
|
pub struct LetStatement(
|
||||||
|
pub String,
|
||||||
|
pub Option<Type>,
|
||||||
|
/// Mutability
|
||||||
|
pub bool,
|
||||||
|
pub Expression,
|
||||||
|
pub TokenRange,
|
||||||
|
);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ImportStatement(Vec<String>, pub TokenRange);
|
pub struct ImportStatement(pub Vec<String>, pub TokenRange);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FunctionDefinition(pub FunctionSignature, pub Block, pub TokenRange);
|
pub struct FunctionDefinition(pub FunctionSignature, pub Block, pub TokenRange);
|
||||||
@ -72,6 +112,7 @@ pub struct FunctionSignature {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
pub args: Vec<(String, Type)>,
|
pub args: Vec<(String, Type)>,
|
||||||
pub return_type: Option<Type>,
|
pub return_type: Option<Type>,
|
||||||
|
#[allow(dead_code)]
|
||||||
pub range: TokenRange,
|
pub range: TokenRange,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +132,11 @@ pub struct Block(
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum BlockLevelStatement {
|
pub enum BlockLevelStatement {
|
||||||
Let(LetStatement),
|
Let(LetStatement),
|
||||||
Import(ImportStatement),
|
/// Try to set a variable to a specified expression value
|
||||||
|
Set(String, Expression, TokenRange),
|
||||||
|
Import {
|
||||||
|
_i: ImportStatement,
|
||||||
|
},
|
||||||
Expression(Expression),
|
Expression(Expression),
|
||||||
Return(ReturnType, Expression),
|
Return(ReturnType, Expression),
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::ast::*;
|
use crate::ast::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
lexer::Token,
|
lexer::Token,
|
||||||
token_stream::{Error, TokenRange, TokenStream},
|
token_stream::{Error, TokenStream},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait Parse
|
pub trait Parse
|
||||||
@ -15,7 +15,17 @@ impl Parse for Type {
|
|||||||
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
|
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
|
||||||
let kind = if let Some(Token::Identifier(ident)) = stream.next() {
|
let kind = if let Some(Token::Identifier(ident)) = stream.next() {
|
||||||
Ok(match &*ident {
|
Ok(match &*ident {
|
||||||
|
"bool" => TypeKind::Bool,
|
||||||
|
"i8" => TypeKind::I8,
|
||||||
|
"i16" => TypeKind::I16,
|
||||||
"i32" => TypeKind::I32,
|
"i32" => TypeKind::I32,
|
||||||
|
"i64" => TypeKind::I64,
|
||||||
|
"i128" => TypeKind::I128,
|
||||||
|
"u8" => TypeKind::U8,
|
||||||
|
"u16" => TypeKind::U16,
|
||||||
|
"u32" => TypeKind::U32,
|
||||||
|
"u64" => TypeKind::U64,
|
||||||
|
"u128" => TypeKind::U128,
|
||||||
_ => panic!("asd"),
|
_ => panic!("asd"),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -28,47 +38,58 @@ impl Parse for Type {
|
|||||||
|
|
||||||
impl Parse for Expression {
|
impl Parse for Expression {
|
||||||
fn parse(mut stream: TokenStream) -> Result<Expression, Error> {
|
fn parse(mut stream: TokenStream) -> Result<Expression, Error> {
|
||||||
let lhs = parse_primary_expression(&mut stream)?;
|
let lhs = stream.parse::<PrimaryExpression>()?.0;
|
||||||
parse_binop_rhs(&mut stream, lhs, None)
|
parse_binop_rhs(&mut stream, lhs, None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_primary_expression(stream: &mut TokenStream) -> Result<Expression, Error> {
|
#[derive(Debug)]
|
||||||
use ExpressionKind as Kind;
|
pub struct PrimaryExpression(Expression);
|
||||||
|
|
||||||
if let Ok(exp) = stream.parse() {
|
impl Parse for PrimaryExpression {
|
||||||
Ok(Expression(
|
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
|
||||||
Kind::FunctionCall(Box::new(exp)),
|
use ExpressionKind as Kind;
|
||||||
stream.get_range().unwrap(),
|
|
||||||
))
|
let expr = if let Ok(exp) = stream.parse() {
|
||||||
} else if let Ok(block) = stream.parse() {
|
Expression(
|
||||||
Ok(Expression(
|
Kind::FunctionCall(Box::new(exp)),
|
||||||
Kind::BlockExpr(Box::new(block)),
|
|
||||||
stream.get_range().unwrap(),
|
|
||||||
))
|
|
||||||
} else if let Ok(ifexpr) = stream.parse() {
|
|
||||||
Ok(Expression(
|
|
||||||
Kind::IfExpr(Box::new(ifexpr)),
|
|
||||||
stream.get_range().unwrap(),
|
|
||||||
))
|
|
||||||
} else if let Some(token) = stream.next() {
|
|
||||||
Ok(match &token {
|
|
||||||
Token::Identifier(v) => {
|
|
||||||
Expression(Kind::VariableName(v.clone()), stream.get_range().unwrap())
|
|
||||||
}
|
|
||||||
Token::DecimalValue(v) => Expression(
|
|
||||||
Kind::Literal(Literal::I32(v.parse().unwrap())),
|
|
||||||
stream.get_range().unwrap(),
|
stream.get_range().unwrap(),
|
||||||
),
|
)
|
||||||
Token::ParenOpen => {
|
} else if let Ok(block) = stream.parse() {
|
||||||
let exp = stream.parse()?;
|
Expression(
|
||||||
stream.expect(Token::ParenClose)?;
|
Kind::BlockExpr(Box::new(block)),
|
||||||
exp
|
stream.get_range().unwrap(),
|
||||||
|
)
|
||||||
|
} else if let Ok(ifexpr) = stream.parse() {
|
||||||
|
Expression(Kind::IfExpr(Box::new(ifexpr)), stream.get_range().unwrap())
|
||||||
|
} else if let Some(token) = stream.next() {
|
||||||
|
match &token {
|
||||||
|
Token::Identifier(v) => {
|
||||||
|
Expression(Kind::VariableName(v.clone()), stream.get_range().unwrap())
|
||||||
|
}
|
||||||
|
Token::DecimalValue(v) => Expression(
|
||||||
|
Kind::Literal(Literal::Number(v.parse().unwrap())),
|
||||||
|
stream.get_range().unwrap(),
|
||||||
|
),
|
||||||
|
Token::True => Expression(
|
||||||
|
Kind::Literal(Literal::Bool(true)),
|
||||||
|
stream.get_range().unwrap(),
|
||||||
|
),
|
||||||
|
Token::False => Expression(
|
||||||
|
Kind::Literal(Literal::Bool(false)),
|
||||||
|
stream.get_range().unwrap(),
|
||||||
|
),
|
||||||
|
Token::ParenOpen => {
|
||||||
|
let exp = stream.parse()?;
|
||||||
|
stream.expect(Token::ParenClose)?;
|
||||||
|
exp
|
||||||
|
}
|
||||||
|
_ => Err(stream.expected_err("identifier, constant or parentheses")?)?,
|
||||||
}
|
}
|
||||||
_ => Err(stream.expected_err("identifier, constant or parentheses")?)?,
|
} else {
|
||||||
})
|
Err(stream.expected_err("expression")?)?
|
||||||
} else {
|
};
|
||||||
Err(stream.expected_err("expression")?)?
|
Ok(PrimaryExpression(expr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +116,7 @@ fn parse_binop_rhs(
|
|||||||
stream.parse_if::<BinaryOperator, _>(|b| b.get_precedence() >= expr_precedence)
|
stream.parse_if::<BinaryOperator, _>(|b| b.get_precedence() >= expr_precedence)
|
||||||
{
|
{
|
||||||
let curr_token_prec = op.get_precedence();
|
let curr_token_prec = op.get_precedence();
|
||||||
let mut rhs = parse_primary_expression(stream)?;
|
let mut rhs = stream.parse::<PrimaryExpression>()?.0;
|
||||||
|
|
||||||
if let Ok(next_op) = stream.parse_peek::<BinaryOperator>() {
|
if let Ok(next_op) = stream.parse_peek::<BinaryOperator>() {
|
||||||
let next_prec = next_op.get_precedence();
|
let next_prec = next_op.get_precedence();
|
||||||
@ -124,7 +145,25 @@ impl Parse for BinaryOperator {
|
|||||||
stream.next();
|
stream.next();
|
||||||
BinaryOperator::And
|
BinaryOperator::And
|
||||||
}
|
}
|
||||||
(Some(Token::LessThan), _) => BinaryOperator::LessThan,
|
(Some(Token::LessThan), Some(Token::Equals)) => {
|
||||||
|
stream.next();
|
||||||
|
BinaryOperator::LE
|
||||||
|
}
|
||||||
|
(Some(Token::GreaterThan), Some(Token::Equals)) => {
|
||||||
|
stream.next();
|
||||||
|
BinaryOperator::GE
|
||||||
|
}
|
||||||
|
(Some(Token::Equals), Some(Token::Equals)) => {
|
||||||
|
stream.next();
|
||||||
|
BinaryOperator::EQ
|
||||||
|
}
|
||||||
|
(Some(Token::Exclamation), Some(Token::Equals)) => {
|
||||||
|
stream.next();
|
||||||
|
BinaryOperator::NE
|
||||||
|
}
|
||||||
|
|
||||||
|
(Some(Token::LessThan), _) => BinaryOperator::LT,
|
||||||
|
(Some(Token::GreaterThan), _) => BinaryOperator::GT,
|
||||||
|
|
||||||
(Some(Token::Plus), _) => BinaryOperator::Add,
|
(Some(Token::Plus), _) => BinaryOperator::Add,
|
||||||
(Some(Token::Minus), _) => BinaryOperator::Minus,
|
(Some(Token::Minus), _) => BinaryOperator::Minus,
|
||||||
@ -165,10 +204,17 @@ impl Parse for FunctionCallExpression {
|
|||||||
impl Parse for IfExpression {
|
impl Parse for IfExpression {
|
||||||
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
|
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
|
||||||
stream.expect(Token::If)?;
|
stream.expect(Token::If)?;
|
||||||
|
let cond = stream.parse()?;
|
||||||
|
let then_b = stream.parse()?;
|
||||||
|
let else_b = if let Ok(_) = stream.expect(Token::Else) {
|
||||||
|
Some(stream.parse()?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
Ok(IfExpression(
|
Ok(IfExpression(
|
||||||
stream.parse()?,
|
cond,
|
||||||
stream.parse()?,
|
then_b,
|
||||||
None,
|
else_b,
|
||||||
stream.get_range().unwrap(),
|
stream.get_range().unwrap(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -177,6 +223,7 @@ impl Parse for IfExpression {
|
|||||||
impl Parse for LetStatement {
|
impl Parse for LetStatement {
|
||||||
fn parse(mut stream: TokenStream) -> Result<LetStatement, Error> {
|
fn parse(mut stream: TokenStream) -> Result<LetStatement, Error> {
|
||||||
stream.expect(Token::LetKeyword)?;
|
stream.expect(Token::LetKeyword)?;
|
||||||
|
let mutability = stream.expect(Token::MutKeyword).is_ok();
|
||||||
|
|
||||||
if let Some(Token::Identifier(variable)) = stream.next() {
|
if let Some(Token::Identifier(variable)) = stream.next() {
|
||||||
stream.expect(Token::Equals)?;
|
stream.expect(Token::Equals)?;
|
||||||
@ -185,6 +232,8 @@ impl Parse for LetStatement {
|
|||||||
stream.expect(Token::Semi)?;
|
stream.expect(Token::Semi)?;
|
||||||
Ok(LetStatement(
|
Ok(LetStatement(
|
||||||
variable,
|
variable,
|
||||||
|
None, // TODO add possibility to name type
|
||||||
|
mutability,
|
||||||
expression,
|
expression,
|
||||||
stream.get_range().unwrap(),
|
stream.get_range().unwrap(),
|
||||||
))
|
))
|
||||||
@ -304,7 +353,9 @@ impl Parse for BlockLevelStatement {
|
|||||||
use BlockLevelStatement as Stmt;
|
use BlockLevelStatement as Stmt;
|
||||||
Ok(match stream.peek() {
|
Ok(match stream.peek() {
|
||||||
Some(Token::LetKeyword) => Stmt::Let(stream.parse()?),
|
Some(Token::LetKeyword) => Stmt::Let(stream.parse()?),
|
||||||
Some(Token::ImportKeyword) => Stmt::Import(stream.parse()?),
|
Some(Token::ImportKeyword) => Stmt::Import {
|
||||||
|
_i: stream.parse()?,
|
||||||
|
},
|
||||||
Some(Token::ReturnKeyword) => {
|
Some(Token::ReturnKeyword) => {
|
||||||
stream.next();
|
stream.next();
|
||||||
let exp = stream.parse()?;
|
let exp = stream.parse()?;
|
||||||
@ -312,20 +363,40 @@ impl Parse for BlockLevelStatement {
|
|||||||
Stmt::Return(ReturnType::Hard, exp)
|
Stmt::Return(ReturnType::Hard, exp)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if let Ok(e) = stream.parse() {
|
if let Ok(SetStatement(ident, expr, range)) = stream.parse() {
|
||||||
if stream.expect(Token::Semi).is_ok() {
|
Stmt::Set(ident, expr, range)
|
||||||
Stmt::Expression(e)
|
|
||||||
} else {
|
|
||||||
Stmt::Return(ReturnType::Soft, e)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Err(stream.expected_err("expression")?)?
|
if let Ok(e) = stream.parse() {
|
||||||
|
if stream.expect(Token::Semi).is_ok() {
|
||||||
|
Stmt::Expression(e)
|
||||||
|
} else {
|
||||||
|
Stmt::Return(ReturnType::Soft, e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(stream.expected_err("expression")?)?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SetStatement(String, Expression, TokenRange);
|
||||||
|
|
||||||
|
impl Parse for SetStatement {
|
||||||
|
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
|
||||||
|
if let Some(Token::Identifier(ident)) = stream.next() {
|
||||||
|
stream.expect(Token::Equals)?;
|
||||||
|
let expr = stream.parse()?;
|
||||||
|
stream.expect(Token::Semi)?;
|
||||||
|
Ok(Self(ident, expr, stream.get_range().unwrap()))
|
||||||
|
} else {
|
||||||
|
Err(stream.expected_err("identifier")?)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Parse for TopLevelStatement {
|
impl Parse for TopLevelStatement {
|
||||||
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
|
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
|
||||||
use TopLevelStatement as Stmt;
|
use TopLevelStatement as Stmt;
|
||||||
|
@ -1,257 +1,18 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast,
|
ast::{self},
|
||||||
mir::{self, StmtKind, VariableReference},
|
mir::{self, StmtKind, VariableReference},
|
||||||
token_stream::TokenRange,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
impl mir::Context {
|
||||||
pub enum InferredType {
|
pub fn from(modules: Vec<ast::Module>) -> mir::Context {
|
||||||
FromVariable(String, TokenRange),
|
mir::Context {
|
||||||
FunctionReturn(String, TokenRange),
|
modules: modules.iter().map(|m| m.process()).collect(),
|
||||||
Static(mir::TypeKind, TokenRange),
|
|
||||||
OneOf(Vec<InferredType>),
|
|
||||||
Void(TokenRange),
|
|
||||||
DownstreamError(IntoMIRError, TokenRange),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn all_ok<T, E>(result: Vec<Result<T, E>>) -> Option<Vec<T>> {
|
|
||||||
let mut res = Vec::with_capacity(result.len());
|
|
||||||
for item in result {
|
|
||||||
if let Ok(item) = item {
|
|
||||||
res.push(item);
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InferredType {
|
|
||||||
fn collapse(
|
|
||||||
&self,
|
|
||||||
state: &mut State,
|
|
||||||
scope: &VirtualScope,
|
|
||||||
) -> Result<mir::TypeKind, IntoMIRError> {
|
|
||||||
match self {
|
|
||||||
InferredType::FromVariable(name, token_range) => {
|
|
||||||
if let Some(inferred) = scope.get_var(name) {
|
|
||||||
let temp = inferred.collapse(state, scope);
|
|
||||||
state.note(temp)
|
|
||||||
} else {
|
|
||||||
state.err(IntoMIRError::VariableNotDefined(name.clone(), *token_range))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
InferredType::FunctionReturn(name, token_range) => {
|
|
||||||
if let Some(type_kind) = scope.get_return_type(name) {
|
|
||||||
Ok(*type_kind)
|
|
||||||
} else {
|
|
||||||
state.err(IntoMIRError::VariableNotDefined(name.clone(), *token_range))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
InferredType::Static(type_kind, _) => Ok(*type_kind),
|
|
||||||
InferredType::OneOf(inferred_types) => {
|
|
||||||
let collapsed = all_ok(
|
|
||||||
inferred_types
|
|
||||||
.iter()
|
|
||||||
.map(|t| {
|
|
||||||
let temp = t.collapse(state, scope);
|
|
||||||
state.note(temp)
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
if let Some(list) = collapsed {
|
|
||||||
if let Some(first) = list.first() {
|
|
||||||
if list.iter().all(|i| i == first) {
|
|
||||||
Ok((*first).into())
|
|
||||||
} else {
|
|
||||||
state.err(IntoMIRError::ConflictingType(self.get_range()))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state.err(IntoMIRError::VoidType(self.get_range()))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state.err(IntoMIRError::DownstreamError(self.get_range()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
InferredType::Void(token_range) => state.err(IntoMIRError::VoidType(*token_range)),
|
|
||||||
InferredType::DownstreamError(e, _) => state.err(e.clone()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_range(&self) -> TokenRange {
|
|
||||||
match &self {
|
|
||||||
InferredType::FromVariable(_, token_range) => *token_range,
|
|
||||||
InferredType::FunctionReturn(_, token_range) => *token_range,
|
|
||||||
InferredType::Static(_, token_range) => *token_range,
|
|
||||||
InferredType::OneOf(inferred_types) => {
|
|
||||||
inferred_types.iter().map(|i| i.get_range()).sum()
|
|
||||||
}
|
|
||||||
InferredType::Void(token_range) => *token_range,
|
|
||||||
InferredType::DownstreamError(_, range) => *range,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct VirtualVariable {
|
|
||||||
name: String,
|
|
||||||
inferred: InferredType,
|
|
||||||
meta: mir::Metadata,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct VirtualFunctionSignature {
|
|
||||||
name: String,
|
|
||||||
return_type: mir::TypeKind,
|
|
||||||
parameter_types: Vec<mir::TypeKind>,
|
|
||||||
metadata: mir::Metadata,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum VirtualStorageError {
|
|
||||||
KeyAlreadyExists(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct VirtualStorage<T> {
|
|
||||||
storage: HashMap<String, Vec<T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> VirtualStorage<T> {
|
|
||||||
fn set(&mut self, name: String, value: T) -> Result<(), VirtualStorageError> {
|
|
||||||
let result = if let Some(list) = self.storage.get_mut(&name) {
|
|
||||||
list.push(value);
|
|
||||||
Err(VirtualStorageError::KeyAlreadyExists(name.clone()))
|
|
||||||
} else {
|
|
||||||
self.storage.insert(name, vec![value]);
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get(&self, name: &String) -> Option<&T> {
|
|
||||||
if let Some(list) = self.storage.get(name) {
|
|
||||||
list.first()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Default for VirtualStorage<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
storage: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum IntoMIRError {
|
|
||||||
DuplicateVariable(String, TokenRange),
|
|
||||||
DuplicateFunction(String, TokenRange),
|
|
||||||
VariableNotDefined(String, TokenRange),
|
|
||||||
FunctionNotDefined(String, TokenRange),
|
|
||||||
DownstreamError(TokenRange),
|
|
||||||
ConflictingType(TokenRange),
|
|
||||||
VoidType(TokenRange),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct VirtualScope {
|
|
||||||
variables: VirtualStorage<VirtualVariable>,
|
|
||||||
functions: VirtualStorage<VirtualFunctionSignature>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VirtualScope {
|
|
||||||
pub fn set_var(&mut self, variable: VirtualVariable) -> Result<(), IntoMIRError> {
|
|
||||||
let range = variable.meta.range;
|
|
||||||
match self.variables.set(variable.name.clone(), variable) {
|
|
||||||
Ok(_) => Ok(()),
|
|
||||||
Err(VirtualStorageError::KeyAlreadyExists(n)) => {
|
|
||||||
Err(IntoMIRError::DuplicateVariable(n, range))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_fun(&mut self, function: VirtualFunctionSignature) -> Result<(), IntoMIRError> {
|
|
||||||
let range = function.metadata.range;
|
|
||||||
match self.functions.set(function.name.clone(), function) {
|
|
||||||
Ok(_) => Ok(()),
|
|
||||||
Err(VirtualStorageError::KeyAlreadyExists(n)) => {
|
|
||||||
Err(IntoMIRError::DuplicateVariable(n, range))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_var(&self, name: &String) -> Option<&InferredType> {
|
|
||||||
self.variables.get(name).map(|v| &v.inferred)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_return_type(&self, name: &String) -> Option<&mir::TypeKind> {
|
|
||||||
self.functions.get(name).map(|v| &v.return_type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for VirtualScope {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
variables: Default::default(),
|
|
||||||
functions: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct State {
|
|
||||||
errors: Vec<IntoMIRError>,
|
|
||||||
fatal: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl State {
|
|
||||||
fn note<T: std::fmt::Debug>(
|
|
||||||
&mut self,
|
|
||||||
value: Result<T, IntoMIRError>,
|
|
||||||
) -> Result<T, IntoMIRError> {
|
|
||||||
dbg!(&value);
|
|
||||||
if let Err(e) = &value {
|
|
||||||
self.errors.push(e.clone());
|
|
||||||
}
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
fn err<T>(&mut self, error: IntoMIRError) -> Result<T, IntoMIRError> {
|
|
||||||
self.errors.push(error.clone());
|
|
||||||
Err(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for State {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
errors: Default::default(),
|
|
||||||
fatal: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ast::Module {
|
impl ast::Module {
|
||||||
pub fn process(&self) -> mir::Module {
|
fn process(&self) -> mir::Module {
|
||||||
let mut state = State::default();
|
|
||||||
let mut scope = VirtualScope::default();
|
|
||||||
|
|
||||||
for stmt in &self.top_level_statements {
|
|
||||||
match stmt {
|
|
||||||
FunctionDefinition(ast::FunctionDefinition(signature, _, range)) => {
|
|
||||||
state.note(scope.set_fun(VirtualFunctionSignature {
|
|
||||||
name: signature.name.clone(),
|
|
||||||
return_type: signature.return_type.into(),
|
|
||||||
parameter_types: signature.args.iter().map(|p| p.1.into()).collect(),
|
|
||||||
metadata: (*range).into(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut imports = Vec::new();
|
let mut imports = Vec::new();
|
||||||
let mut functions = Vec::new();
|
let mut functions = Vec::new();
|
||||||
|
|
||||||
@ -264,35 +25,25 @@ impl ast::Module {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
FunctionDefinition(ast::FunctionDefinition(signature, block, range)) => {
|
FunctionDefinition(ast::FunctionDefinition(signature, block, range)) => {
|
||||||
for (name, ptype) in &signature.args {
|
let def = mir::FunctionDefinition {
|
||||||
state.note(scope.set_var(VirtualVariable {
|
name: signature.name.clone(),
|
||||||
name: name.clone(),
|
return_type: signature
|
||||||
inferred: InferredType::Static((*ptype).into(), *range),
|
.return_type
|
||||||
meta: ptype.1.into(),
|
.map(|r| r.0.into())
|
||||||
}));
|
.unwrap_or(mir::TypeKind::Void),
|
||||||
}
|
parameters: signature
|
||||||
|
.args
|
||||||
dbg!(&signature);
|
.iter()
|
||||||
|
.cloned()
|
||||||
if let Some(mir_block) = block.process(&mut state, &mut scope) {
|
.map(|p| (p.0, p.1.into()))
|
||||||
let def = mir::FunctionDefinition {
|
.collect(),
|
||||||
name: signature.name.clone(),
|
kind: mir::FunctionDefinitionKind::Local(block.into_mir(), (*range).into()),
|
||||||
parameters: signature
|
};
|
||||||
.args
|
functions.push(def);
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(|p| (p.0, p.1.into()))
|
|
||||||
.collect(),
|
|
||||||
kind: mir::FunctionDefinitionKind::Local(mir_block, (*range).into()),
|
|
||||||
};
|
|
||||||
functions.push(def);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dbg!(&state);
|
|
||||||
|
|
||||||
// TODO do something with state here
|
// TODO do something with state here
|
||||||
|
|
||||||
mir::Module {
|
mir::Module {
|
||||||
@ -304,79 +55,56 @@ impl ast::Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ast::Block {
|
impl ast::Block {
|
||||||
pub fn process(&self, state: &mut State, scope: &mut VirtualScope) -> Option<mir::Block> {
|
pub fn into_mir(&self) -> mir::Block {
|
||||||
let mut mir_statements = Vec::new();
|
let mut mir_statements = Vec::new();
|
||||||
|
|
||||||
for statement in &self.0 {
|
for statement in &self.0 {
|
||||||
let (kind, range): (Option<mir::StmtKind>, TokenRange) = match statement {
|
let (kind, range) = match statement {
|
||||||
ast::BlockLevelStatement::Let(s_let) => {
|
ast::BlockLevelStatement::Let(s_let) => (
|
||||||
let res = s_let.1.infer_return_type().collapse(state, scope);
|
mir::StmtKind::Let(
|
||||||
let collapsed = state.note(res);
|
mir::VariableReference(
|
||||||
let inferred = match &collapsed {
|
s_let
|
||||||
Ok(t) => InferredType::Static(*t, s_let.2),
|
.1
|
||||||
Err(e) => InferredType::DownstreamError(e.clone(), s_let.2),
|
.map(|t| t.0.into())
|
||||||
};
|
.unwrap_or(mir::TypeKind::Vague(mir::VagueType::Unknown)),
|
||||||
state
|
s_let.0.clone(),
|
||||||
.note(scope.set_var(VirtualVariable {
|
s_let.4.into(),
|
||||||
name: s_let.0.clone(),
|
),
|
||||||
inferred,
|
|
||||||
meta: s_let.2.into(),
|
|
||||||
}))
|
|
||||||
.ok();
|
|
||||||
|
|
||||||
(
|
|
||||||
collapsed.ok().and_then(|t| {
|
|
||||||
s_let.1.process(state, scope).map(|e| {
|
|
||||||
mir::StmtKind::Let(
|
|
||||||
mir::VariableReference(t, s_let.0.clone(), s_let.2.into()),
|
|
||||||
e,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
s_let.2,
|
s_let.2,
|
||||||
)
|
s_let.3.process(),
|
||||||
}
|
),
|
||||||
ast::BlockLevelStatement::Import(_) => todo!(),
|
s_let.4,
|
||||||
ast::BlockLevelStatement::Expression(e) => (
|
|
||||||
e.process(state, scope).map(|e| StmtKind::Expression(e)),
|
|
||||||
e.1,
|
|
||||||
),
|
),
|
||||||
ast::BlockLevelStatement::Return(_, e) => (
|
ast::BlockLevelStatement::Set(name, expression, range) => (
|
||||||
e.process(state, scope).map(|e| StmtKind::Expression(e)),
|
StmtKind::Set(
|
||||||
e.1,
|
VariableReference(
|
||||||
|
mir::TypeKind::Vague(mir::VagueType::Unknown),
|
||||||
|
name.clone(),
|
||||||
|
(*range).into(),
|
||||||
|
),
|
||||||
|
expression.process(),
|
||||||
|
),
|
||||||
|
*range,
|
||||||
),
|
),
|
||||||
|
ast::BlockLevelStatement::Import { _i } => todo!(),
|
||||||
|
ast::BlockLevelStatement::Expression(e) => (StmtKind::Expression(e.process()), e.1),
|
||||||
|
ast::BlockLevelStatement::Return(_, e) => (StmtKind::Expression(e.process()), e.1),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(kind) = kind {
|
mir_statements.push(mir::Statement(kind, range.into()));
|
||||||
mir_statements.push(mir::Statement(kind, range.into()));
|
|
||||||
} else {
|
|
||||||
state.fatal = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let return_expression = if let Some(r) = &self.1 {
|
let return_expression = if let Some(r) = &self.1 {
|
||||||
if let Some(expr) = r.1.process(state, scope) {
|
Some((r.0.into(), Box::new(r.1.process())))
|
||||||
Some((r.0.into(), Box::new(expr)))
|
|
||||||
} else {
|
|
||||||
state.fatal = true;
|
|
||||||
None?
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(mir::Block {
|
mir::Block {
|
||||||
statements: mir_statements,
|
statements: mir_statements,
|
||||||
return_expression,
|
return_expression,
|
||||||
meta: self.2.into(),
|
meta: self.2.into(),
|
||||||
})
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn infer_return_type(&self) -> InferredType {
|
|
||||||
self.1
|
|
||||||
.as_ref()
|
|
||||||
.map(|(_, expr)| expr.infer_return_type())
|
|
||||||
.unwrap_or(InferredType::Void(self.2))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,111 +118,40 @@ impl From<ast::ReturnType> for mir::ReturnKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ast::Expression {
|
impl ast::Expression {
|
||||||
fn process(&self, state: &mut State, scope: &mut VirtualScope) -> Option<mir::Expression> {
|
fn process(&self) -> mir::Expression {
|
||||||
let kind = match &self.0 {
|
let kind = match &self.0 {
|
||||||
ast::ExpressionKind::VariableName(name) => {
|
ast::ExpressionKind::VariableName(name) => mir::ExprKind::Variable(VariableReference(
|
||||||
let ty = scope.get_var(name);
|
mir::TypeKind::Vague(mir::VagueType::Unknown),
|
||||||
if let Some(ty) = ty {
|
name.clone(),
|
||||||
let res = ty.collapse(state, scope);
|
self.1.into(),
|
||||||
state
|
)),
|
||||||
.note(res)
|
ast::ExpressionKind::Literal(literal) => mir::ExprKind::Literal(literal.mir()),
|
||||||
.map(|result| {
|
ast::ExpressionKind::Binop(binary_operator, lhs, rhs) => mir::ExprKind::BinOp(
|
||||||
mir::ExprKind::Variable(VariableReference(
|
binary_operator.mir(),
|
||||||
result,
|
Box::new(lhs.process()),
|
||||||
name.clone(),
|
Box::new(rhs.process()),
|
||||||
self.1.into(),
|
),
|
||||||
))
|
|
||||||
})
|
|
||||||
.ok()
|
|
||||||
} else {
|
|
||||||
state
|
|
||||||
.err(IntoMIRError::VariableNotDefined(
|
|
||||||
name.clone(),
|
|
||||||
self.1.into(),
|
|
||||||
))
|
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ast::ExpressionKind::Literal(literal) => Some(mir::ExprKind::Literal(literal.mir())),
|
|
||||||
ast::ExpressionKind::Binop(binary_operator, lhs, rhs) => {
|
|
||||||
let mir_lhs = lhs.process(state, scope);
|
|
||||||
let mir_rhs = rhs.process(state, scope);
|
|
||||||
Some(mir::ExprKind::BinOp(
|
|
||||||
binary_operator.mir(),
|
|
||||||
Box::new(mir_lhs?),
|
|
||||||
Box::new(mir_rhs?),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
ast::ExpressionKind::FunctionCall(fn_call_expr) => {
|
ast::ExpressionKind::FunctionCall(fn_call_expr) => {
|
||||||
if let Some(fn_type) = scope.get_return_type(&fn_call_expr.0).cloned() {
|
mir::ExprKind::FunctionCall(mir::FunctionCall {
|
||||||
let parameters = all_ok(
|
name: fn_call_expr.0.clone(),
|
||||||
fn_call_expr
|
return_type: mir::TypeKind::Vague(mir::VagueType::Unknown),
|
||||||
.1
|
parameters: fn_call_expr.1.iter().map(|e| e.process()).collect(),
|
||||||
.iter()
|
})
|
||||||
.map(|e| {
|
|
||||||
e.process(state, scope)
|
|
||||||
.ok_or(IntoMIRError::DownstreamError(self.1.into()))
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
if let Some(parameters) = parameters {
|
|
||||||
Some(mir::ExprKind::FunctionCall(mir::FunctionCall {
|
|
||||||
name: fn_call_expr.0.clone(),
|
|
||||||
return_type: fn_type,
|
|
||||||
parameters,
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state
|
|
||||||
.err(IntoMIRError::FunctionNotDefined(
|
|
||||||
fn_call_expr.0.clone(),
|
|
||||||
self.1,
|
|
||||||
))
|
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ast::ExpressionKind::BlockExpr(block) => {
|
|
||||||
block.process(state, scope).map(|b| mir::ExprKind::Block(b))
|
|
||||||
}
|
}
|
||||||
|
ast::ExpressionKind::BlockExpr(block) => mir::ExprKind::Block(block.into_mir()),
|
||||||
ast::ExpressionKind::IfExpr(if_expression) => {
|
ast::ExpressionKind::IfExpr(if_expression) => {
|
||||||
let cond = if_expression.0.process(state, scope);
|
let cond = if_expression.0.process();
|
||||||
let then_block = if_expression.1.process(state, scope);
|
let then_block = if_expression.1.into_mir();
|
||||||
let else_block = if let Some(el) = &if_expression.2 {
|
let else_block = if let Some(el) = &if_expression.2 {
|
||||||
Some(el.process(state, scope)?)
|
Some(el.into_mir())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
Some(mir::ExprKind::If(mir::IfExpression(
|
mir::ExprKind::If(mir::IfExpression(Box::new(cond), then_block, else_block))
|
||||||
Box::new(cond?),
|
|
||||||
then_block?,
|
|
||||||
else_block,
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
kind.map(|k| mir::Expression(k, self.1.into()))
|
mir::Expression(kind, self.1.into())
|
||||||
}
|
|
||||||
|
|
||||||
fn infer_return_type(&self) -> InferredType {
|
|
||||||
use ast::ExpressionKind::*;
|
|
||||||
match &self.0 {
|
|
||||||
VariableName(name) => InferredType::FromVariable(name.clone(), self.1),
|
|
||||||
Literal(lit) => InferredType::Static(lit.mir().as_type(), self.1),
|
|
||||||
Binop(_, lhs, rhs) => {
|
|
||||||
InferredType::OneOf(vec![lhs.infer_return_type(), rhs.infer_return_type()])
|
|
||||||
}
|
|
||||||
FunctionCall(fncall) => InferredType::FunctionReturn(fncall.0.clone(), self.1),
|
|
||||||
BlockExpr(block) => block.infer_return_type(),
|
|
||||||
IfExpr(exp) => {
|
|
||||||
let mut types = vec![exp.1.infer_return_type()];
|
|
||||||
if let Some(e) = &exp.2 {
|
|
||||||
types.push(e.infer_return_type())
|
|
||||||
}
|
|
||||||
InferredType::OneOf(types)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -505,9 +162,12 @@ impl ast::BinaryOperator {
|
|||||||
ast::BinaryOperator::Minus => mir::BinaryOperator::Minus,
|
ast::BinaryOperator::Minus => mir::BinaryOperator::Minus,
|
||||||
ast::BinaryOperator::Mult => mir::BinaryOperator::Mult,
|
ast::BinaryOperator::Mult => mir::BinaryOperator::Mult,
|
||||||
ast::BinaryOperator::And => mir::BinaryOperator::And,
|
ast::BinaryOperator::And => mir::BinaryOperator::And,
|
||||||
ast::BinaryOperator::LessThan => {
|
ast::BinaryOperator::LT => mir::BinaryOperator::Cmp(mir::CmpOperator::LT),
|
||||||
mir::BinaryOperator::Logic(mir::LogicOperator::LessThan)
|
ast::BinaryOperator::LE => mir::BinaryOperator::Cmp(mir::CmpOperator::LE),
|
||||||
}
|
ast::BinaryOperator::GT => mir::BinaryOperator::Cmp(mir::CmpOperator::GT),
|
||||||
|
ast::BinaryOperator::GE => mir::BinaryOperator::Cmp(mir::CmpOperator::GE),
|
||||||
|
ast::BinaryOperator::EQ => mir::BinaryOperator::Cmp(mir::CmpOperator::EQ),
|
||||||
|
ast::BinaryOperator::NE => mir::BinaryOperator::Cmp(mir::CmpOperator::NE),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -515,7 +175,8 @@ impl ast::BinaryOperator {
|
|||||||
impl ast::Literal {
|
impl ast::Literal {
|
||||||
fn mir(&self) -> mir::Literal {
|
fn mir(&self) -> mir::Literal {
|
||||||
match *self {
|
match *self {
|
||||||
ast::Literal::I32(v) => mir::Literal::I32(v),
|
ast::Literal::Number(v) => mir::Literal::Vague(mir::VagueLiteral::Number(v)),
|
||||||
|
ast::Literal::Bool(v) => mir::Literal::Bool(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -523,7 +184,17 @@ impl ast::Literal {
|
|||||||
impl From<ast::TypeKind> for mir::TypeKind {
|
impl From<ast::TypeKind> for mir::TypeKind {
|
||||||
fn from(value: ast::TypeKind) -> Self {
|
fn from(value: ast::TypeKind) -> Self {
|
||||||
match value {
|
match value {
|
||||||
|
ast::TypeKind::Bool => mir::TypeKind::Bool,
|
||||||
|
ast::TypeKind::I8 => mir::TypeKind::I8,
|
||||||
|
ast::TypeKind::I16 => mir::TypeKind::I16,
|
||||||
ast::TypeKind::I32 => mir::TypeKind::I32,
|
ast::TypeKind::I32 => mir::TypeKind::I32,
|
||||||
|
ast::TypeKind::I64 => mir::TypeKind::I64,
|
||||||
|
ast::TypeKind::I128 => mir::TypeKind::I128,
|
||||||
|
ast::TypeKind::U8 => mir::TypeKind::U8,
|
||||||
|
ast::TypeKind::U16 => mir::TypeKind::U16,
|
||||||
|
ast::TypeKind::U32 => mir::TypeKind::U32,
|
||||||
|
ast::TypeKind::U64 => mir::TypeKind::U64,
|
||||||
|
ast::TypeKind::U128 => mir::TypeKind::U128,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -533,12 +204,3 @@ impl From<ast::Type> for mir::TypeKind {
|
|||||||
value.0.into()
|
value.0.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Option<ast::Type>> for mir::TypeKind {
|
|
||||||
fn from(value: Option<ast::Type>) -> Self {
|
|
||||||
match value {
|
|
||||||
Some(v) => v.into(),
|
|
||||||
None => mir::TypeKind::Void,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,49 +1,86 @@
|
|||||||
use std::{collections::HashMap, mem, ops::Deref};
|
use std::{collections::HashMap, mem};
|
||||||
|
|
||||||
use crate::mir::{self, types::ReturnType, TypeKind, VariableReference};
|
use llvm_sys::core::LLVMBuildStore;
|
||||||
use reid_lib::{
|
use reid_lib::{
|
||||||
types::{BasicType, BasicValue, IntegerValue, TypeEnum, Value},
|
builder::InstructionValue, Block, CmpPredicate, ConstValue, Context, Function, Instr, Module,
|
||||||
BasicBlock, Context, Function, IntPredicate, Module,
|
TerminatorKind as Term, Type,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ModuleCodegen<'ctx> {
|
use crate::mir::{self, types::ReturnType, TypeKind, VariableReference};
|
||||||
context: &'ctx Context,
|
|
||||||
pub module: Module<'ctx>,
|
/// Context that contains all of the given modules as complete codegenerated
|
||||||
|
/// LLIR that can then be finally compiled into LLVM IR.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CodegenContext<'ctx> {
|
||||||
|
modules: Vec<ModuleCodegen<'ctx>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx> CodegenContext<'ctx> {
|
||||||
|
/// Compile contained LLIR into LLVM IR and produce `hello.o` and
|
||||||
|
/// `hello.asm`
|
||||||
|
pub fn compile(&self) {
|
||||||
|
for module in &self.modules {
|
||||||
|
module.context.compile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl mir::Context {
|
||||||
|
/// Compile MIR [`Context`] into [`CodegenContext`] containing LLIR.
|
||||||
|
pub fn codegen<'ctx>(&self, context: &'ctx Context) -> CodegenContext<'ctx> {
|
||||||
|
let mut modules = Vec::new();
|
||||||
|
for module in &self.modules {
|
||||||
|
modules.push(module.codegen(context));
|
||||||
|
}
|
||||||
|
CodegenContext { modules }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ModuleCodegen<'ctx> {
|
||||||
|
pub context: &'ctx Context,
|
||||||
|
_module: Module<'ctx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx> std::fmt::Debug for ModuleCodegen<'ctx> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.context.fmt(f)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl mir::Module {
|
impl mir::Module {
|
||||||
pub fn codegen<'ctx>(&self, context: &'ctx Context) -> ModuleCodegen<'ctx> {
|
fn codegen<'ctx>(&self, context: &'ctx Context) -> ModuleCodegen<'ctx> {
|
||||||
let module = context.module(&self.name);
|
let mut module = context.module(&self.name);
|
||||||
|
|
||||||
let mut functions = HashMap::new();
|
let mut functions = HashMap::new();
|
||||||
|
|
||||||
for function in &self.functions {
|
for function in &self.functions {
|
||||||
let ret_type = function.return_type().unwrap().get_type(&context);
|
let param_types: Vec<Type> = function
|
||||||
let fn_type = ret_type.function_type(
|
.parameters
|
||||||
function
|
.iter()
|
||||||
.parameters
|
.map(|(_, p)| p.get_type())
|
||||||
.iter()
|
.collect();
|
||||||
.map(|(_, p)| p.get_type(&context))
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let func = match &function.kind {
|
let func = match &function.kind {
|
||||||
mir::FunctionDefinitionKind::Local(_, _) => {
|
mir::FunctionDefinitionKind::Local(_, _) => {
|
||||||
module.add_function(fn_type, &function.name)
|
module.function(&function.name, function.return_type.get_type(), param_types)
|
||||||
}
|
}
|
||||||
mir::FunctionDefinitionKind::Extern(_) => todo!(),
|
mir::FunctionDefinitionKind::Extern => todo!(),
|
||||||
};
|
};
|
||||||
functions.insert(function.name.clone(), func);
|
functions.insert(function.name.clone(), func);
|
||||||
}
|
}
|
||||||
|
|
||||||
for mir_function in &self.functions {
|
for mir_function in &self.functions {
|
||||||
let function = functions.get(&mir_function.name).unwrap();
|
let function = functions.get(&mir_function.name).unwrap();
|
||||||
|
let mut entry = function.block("entry");
|
||||||
|
|
||||||
let mut stack_values = HashMap::new();
|
let mut stack_values = HashMap::new();
|
||||||
for (i, (p_name, p_type)) in mir_function.parameters.iter().enumerate() {
|
for (i, (p_name, p_ty)) in mir_function.parameters.iter().enumerate() {
|
||||||
stack_values.insert(
|
stack_values.insert(
|
||||||
p_name.clone(),
|
p_name.clone(),
|
||||||
function.get_param(i, p_type.get_type(&context)).unwrap(),
|
StackValue(
|
||||||
|
StackValueKind::Immutable(entry.build(Instr::Param(i)).unwrap()),
|
||||||
|
p_ty.get_type(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,48 +88,66 @@ impl mir::Module {
|
|||||||
context,
|
context,
|
||||||
module: &module,
|
module: &module,
|
||||||
function,
|
function,
|
||||||
block: function.block("entry"),
|
block: entry,
|
||||||
functions: functions.clone(),
|
functions: &functions,
|
||||||
stack_values,
|
stack_values,
|
||||||
};
|
};
|
||||||
match &mir_function.kind {
|
match &mir_function.kind {
|
||||||
mir::FunctionDefinitionKind::Local(block, _) => {
|
mir::FunctionDefinitionKind::Local(block, _) => {
|
||||||
if let Some(ret) = block.codegen(&mut scope) {
|
if let Some(ret) = block.codegen(&mut scope) {
|
||||||
scope.block.ret(&ret).unwrap();
|
scope.block.terminate(Term::Ret(ret)).unwrap();
|
||||||
|
} else {
|
||||||
|
if !scope.block.delete_if_unused().unwrap() {
|
||||||
|
// Add a void return just in case if the block
|
||||||
|
// wasn't unused but didn't have a terminator yet
|
||||||
|
scope.block.terminate(Term::RetVoid).ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mir::FunctionDefinitionKind::Extern(_) => {}
|
mir::FunctionDefinitionKind::Extern => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ModuleCodegen { context, module }
|
ModuleCodegen {
|
||||||
|
context,
|
||||||
|
_module: module,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Scope<'ctx> {
|
pub struct Scope<'ctx, 'a> {
|
||||||
context: &'ctx Context,
|
context: &'ctx Context,
|
||||||
module: &'ctx Module<'ctx>,
|
module: &'ctx Module<'ctx>,
|
||||||
function: &'ctx Function<'ctx>,
|
function: &'ctx Function<'ctx>,
|
||||||
block: BasicBlock<'ctx>,
|
block: Block<'ctx>,
|
||||||
functions: HashMap<String, Function<'ctx>>,
|
functions: &'a HashMap<String, Function<'ctx>>,
|
||||||
stack_values: HashMap<String, Value<'ctx>>,
|
stack_values: HashMap<String, StackValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx> Scope<'ctx> {
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub fn with_block(&self, block: BasicBlock<'ctx>) -> Scope<'ctx> {
|
pub struct StackValue(StackValueKind, Type);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum StackValueKind {
|
||||||
|
Immutable(InstructionValue),
|
||||||
|
Mutable(InstructionValue),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, 'a> Scope<'ctx, 'a> {
|
||||||
|
fn with_block(&self, block: Block<'ctx>) -> Scope<'ctx, 'a> {
|
||||||
Scope {
|
Scope {
|
||||||
block,
|
block,
|
||||||
context: self.context,
|
|
||||||
function: self.function,
|
function: self.function,
|
||||||
|
context: self.context,
|
||||||
module: self.module,
|
module: self.module,
|
||||||
functions: self.functions.clone(),
|
functions: self.functions,
|
||||||
stack_values: self.stack_values.clone(),
|
stack_values: self.stack_values.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes the block out from this scope, swaps the given block in it's place
|
/// Takes the block out from this scope, swaps the given block in it's place
|
||||||
/// and returns the old block.
|
/// and returns the old block.
|
||||||
pub fn swap_block(&mut self, block: BasicBlock<'ctx>) -> BasicBlock<'ctx> {
|
fn swap_block(&mut self, block: Block<'ctx>) -> Block<'ctx> {
|
||||||
let mut old_block = block;
|
let mut old_block = block;
|
||||||
mem::swap(&mut self.block, &mut old_block);
|
mem::swap(&mut self.block, &mut old_block);
|
||||||
old_block
|
old_block
|
||||||
@ -100,13 +155,35 @@ impl<'ctx> Scope<'ctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl mir::Statement {
|
impl mir::Statement {
|
||||||
pub fn codegen<'ctx>(&self, scope: &mut Scope<'ctx>) -> Option<Value<'ctx>> {
|
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>) -> Option<InstructionValue> {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
mir::StmtKind::Let(VariableReference(_, name, _), expression) => {
|
mir::StmtKind::Let(VariableReference(ty, name, _), mutable, expression) => {
|
||||||
let value = expression.codegen(scope).unwrap();
|
let value = expression.codegen(scope).unwrap();
|
||||||
scope.stack_values.insert(name.clone(), value);
|
scope.stack_values.insert(
|
||||||
|
name.clone(),
|
||||||
|
StackValue(
|
||||||
|
match mutable {
|
||||||
|
true => StackValueKind::Mutable(value),
|
||||||
|
false => StackValueKind::Immutable(value),
|
||||||
|
},
|
||||||
|
ty.get_type(),
|
||||||
|
),
|
||||||
|
);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
mir::StmtKind::Set(var, val) => {
|
||||||
|
if let Some(StackValue(kind, ty)) = scope.stack_values.get(&var.1).cloned() {
|
||||||
|
match kind {
|
||||||
|
StackValueKind::Immutable(ptr) => {
|
||||||
|
let expression = val.codegen(scope).unwrap();
|
||||||
|
Some(scope.block.build(Instr::Store(ptr, expression)).unwrap())
|
||||||
|
}
|
||||||
|
StackValueKind::Mutable(_) => panic!(""),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("")
|
||||||
|
}
|
||||||
|
}
|
||||||
// mir::StmtKind::If(if_expression) => if_expression.codegen(scope),
|
// mir::StmtKind::If(if_expression) => if_expression.codegen(scope),
|
||||||
mir::StmtKind::Import(_) => todo!(),
|
mir::StmtKind::Import(_) => todo!(),
|
||||||
mir::StmtKind::Expression(expression) => expression.codegen(scope),
|
mir::StmtKind::Expression(expression) => expression.codegen(scope),
|
||||||
@ -115,92 +192,110 @@ impl mir::Statement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl mir::IfExpression {
|
impl mir::IfExpression {
|
||||||
pub fn codegen<'ctx>(&self, scope: &mut Scope<'ctx>) -> Option<Value<'ctx>> {
|
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>) -> Option<InstructionValue> {
|
||||||
let condition = self.0.codegen(scope).unwrap();
|
let condition = self.0.codegen(scope).unwrap();
|
||||||
|
|
||||||
// Create blocks
|
// Create blocks
|
||||||
let then_bb = scope.function.block("then");
|
let then_b = scope.function.block("then");
|
||||||
let after_bb = scope.function.block("after");
|
let mut else_b = scope.function.block("else");
|
||||||
let mut before_bb = scope.swap_block(after_bb);
|
let after_b = scope.function.block("after");
|
||||||
|
|
||||||
let mut then_scope = scope.with_block(then_bb);
|
// Store for convenience
|
||||||
|
let then_bb = then_b.value();
|
||||||
|
let else_bb = else_b.value();
|
||||||
|
let after_bb = after_b.value();
|
||||||
|
|
||||||
|
// Generate then-block content
|
||||||
|
let mut then_scope = scope.with_block(then_b);
|
||||||
let then_res = self.1.codegen(&mut then_scope);
|
let then_res = self.1.codegen(&mut then_scope);
|
||||||
then_scope.block.br(&scope.block).ok();
|
then_scope.block.terminate(Term::Br(after_bb)).ok();
|
||||||
|
|
||||||
let else_bb = scope.function.block("else");
|
let else_res = if let Some(else_block) = &self.2 {
|
||||||
let mut else_scope = scope.with_block(else_bb);
|
let mut else_scope = scope.with_block(else_b);
|
||||||
|
scope
|
||||||
let else_opt = if let Some(else_block) = &self.2 {
|
.block
|
||||||
before_bb
|
.terminate(Term::CondBr(condition, then_bb, else_bb))
|
||||||
.conditional_br(&condition, &then_scope.block, &else_scope.block)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let opt = else_block.codegen(&mut else_scope);
|
let opt = else_block.codegen(&mut else_scope);
|
||||||
|
|
||||||
if let Some(ret) = opt {
|
if let Some(ret) = opt {
|
||||||
else_scope.block.br(&scope.block).ok();
|
else_scope.block.terminate(Term::Br(after_bb)).ok();
|
||||||
Some((else_scope.block, ret))
|
Some(ret)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
else_scope.block.br(&scope.block).unwrap();
|
else_b.terminate(Term::Br(after_bb)).unwrap();
|
||||||
before_bb
|
scope
|
||||||
.conditional_br(&condition, &then_scope.block, &scope.block)
|
.block
|
||||||
|
.terminate(Term::CondBr(condition, then_bb, after_bb))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
if then_res.is_none() && else_opt.is_none() {
|
// Swap block to the after-block so that construction can continue correctly
|
||||||
None
|
scope.swap_block(after_b);
|
||||||
} else if let Ok(ret_type) = self.1.return_type() {
|
|
||||||
let phi = scope
|
|
||||||
.block
|
|
||||||
.phi(&ret_type.get_type(scope.context), "phi")
|
|
||||||
.unwrap();
|
|
||||||
if let Some(then_ret) = then_res {
|
|
||||||
phi.add_incoming(&then_ret, &then_scope.block);
|
|
||||||
}
|
|
||||||
if let Some((else_bb, else_ret)) = else_opt {
|
|
||||||
phi.add_incoming(&else_ret, &else_bb);
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(phi.build())
|
if then_res.is_none() && else_res.is_none() {
|
||||||
} else {
|
|
||||||
None
|
None
|
||||||
|
} else {
|
||||||
|
let mut incoming = Vec::from(then_res.as_slice());
|
||||||
|
incoming.extend(else_res);
|
||||||
|
Some(scope.block.build(Instr::Phi(incoming)).unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl mir::Expression {
|
impl mir::Expression {
|
||||||
pub fn codegen<'ctx>(&self, scope: &mut Scope<'ctx>) -> Option<Value<'ctx>> {
|
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>) -> Option<InstructionValue> {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
mir::ExprKind::Variable(varref) => {
|
mir::ExprKind::Variable(varref) => {
|
||||||
|
varref.0.is_known().expect("variable type unknown");
|
||||||
let v = scope
|
let v = scope
|
||||||
.stack_values
|
.stack_values
|
||||||
.get(&varref.1)
|
.get(&varref.1)
|
||||||
.expect("Variable reference not found?!");
|
.expect("Variable reference not found?!");
|
||||||
Some(v.clone())
|
Some(match v.0 {
|
||||||
}
|
StackValueKind::Immutable(val) => val.clone(),
|
||||||
mir::ExprKind::Literal(lit) => Some(lit.codegen(scope.context)),
|
StackValueKind::Mutable(val) => {
|
||||||
mir::ExprKind::BinOp(binop, lhs_exp, rhs_exp) => {
|
scope.block.build(Instr::Load(val, v.1.clone())).unwrap()
|
||||||
let lhs = lhs_exp.codegen(scope).expect("lhs has no return value");
|
|
||||||
let rhs = rhs_exp.codegen(scope).expect("rhs has no return value");
|
|
||||||
Some(match binop {
|
|
||||||
mir::BinaryOperator::Add => scope.block.add(&lhs, &rhs, "add").unwrap(),
|
|
||||||
mir::BinaryOperator::Minus => scope.block.sub(&lhs, &rhs, "sub").unwrap(),
|
|
||||||
mir::BinaryOperator::Mult => todo!(),
|
|
||||||
mir::BinaryOperator::And => todo!(),
|
|
||||||
mir::BinaryOperator::Logic(l) => {
|
|
||||||
let ret_type = lhs_exp.return_type().expect("No ret type in lhs?");
|
|
||||||
scope
|
|
||||||
.block
|
|
||||||
.integer_compare(&lhs, &rhs, &l.int_predicate(ret_type.signed()), "cmp")
|
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
mir::ExprKind::Literal(lit) => Some(lit.as_const(&mut scope.block)),
|
||||||
|
mir::ExprKind::BinOp(binop, lhs_exp, rhs_exp) => {
|
||||||
|
lhs_exp
|
||||||
|
.return_type()
|
||||||
|
.expect("No ret type in lhs?")
|
||||||
|
.1
|
||||||
|
.is_known()
|
||||||
|
.expect("lhs ret type is unknown");
|
||||||
|
rhs_exp
|
||||||
|
.return_type()
|
||||||
|
.expect("No ret type in rhs?")
|
||||||
|
.1
|
||||||
|
.is_known()
|
||||||
|
.expect("rhs ret type is unknown");
|
||||||
|
|
||||||
|
let lhs = lhs_exp.codegen(scope).expect("lhs has no return value");
|
||||||
|
let rhs = rhs_exp.codegen(scope).expect("rhs has no return value");
|
||||||
|
Some(match binop {
|
||||||
|
mir::BinaryOperator::Add => scope.block.build(Instr::Add(lhs, rhs)).unwrap(),
|
||||||
|
mir::BinaryOperator::Minus => scope.block.build(Instr::Sub(lhs, rhs)).unwrap(),
|
||||||
|
mir::BinaryOperator::Mult => scope.block.build(Instr::Mult(lhs, rhs)).unwrap(),
|
||||||
|
mir::BinaryOperator::And => scope.block.build(Instr::And(lhs, rhs)).unwrap(),
|
||||||
|
mir::BinaryOperator::Cmp(l) => scope
|
||||||
|
.block
|
||||||
|
.build(Instr::ICmp(l.int_predicate(), lhs, rhs))
|
||||||
|
.unwrap(),
|
||||||
|
})
|
||||||
|
}
|
||||||
mir::ExprKind::FunctionCall(call) => {
|
mir::ExprKind::FunctionCall(call) => {
|
||||||
|
call.return_type
|
||||||
|
.is_known()
|
||||||
|
.expect("function return type unknown");
|
||||||
|
|
||||||
let params = call
|
let params = call
|
||||||
.parameters
|
.parameters
|
||||||
.iter()
|
.iter()
|
||||||
@ -210,13 +305,21 @@ impl mir::Expression {
|
|||||||
.functions
|
.functions
|
||||||
.get(&call.name)
|
.get(&call.name)
|
||||||
.expect("function not found!");
|
.expect("function not found!");
|
||||||
Some(scope.block.call(callee, params, "call").unwrap())
|
Some(
|
||||||
|
scope
|
||||||
|
.block
|
||||||
|
.build(Instr::FunctionCall(callee.value(), params))
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
mir::ExprKind::If(if_expression) => if_expression.codegen(scope),
|
mir::ExprKind::If(if_expression) => if_expression.codegen(scope),
|
||||||
mir::ExprKind::Block(block) => {
|
mir::ExprKind::Block(block) => {
|
||||||
let mut inner_scope = scope.with_block(scope.function.block("inner"));
|
let mut inner_scope = scope.with_block(scope.function.block("inner"));
|
||||||
if let Some(ret) = block.codegen(&mut inner_scope) {
|
if let Some(ret) = block.codegen(&mut inner_scope) {
|
||||||
inner_scope.block.br(&scope.block);
|
inner_scope
|
||||||
|
.block
|
||||||
|
.terminate(Term::Br(scope.block.value()))
|
||||||
|
.unwrap();
|
||||||
Some(ret)
|
Some(ret)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -226,31 +329,36 @@ impl mir::Expression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl mir::LogicOperator {
|
impl mir::CmpOperator {
|
||||||
fn int_predicate(&self, signed: bool) -> IntPredicate {
|
fn int_predicate(&self) -> CmpPredicate {
|
||||||
match (self, signed) {
|
match self {
|
||||||
(mir::LogicOperator::LessThan, true) => IntPredicate::SLT,
|
mir::CmpOperator::LT => CmpPredicate::LT,
|
||||||
(mir::LogicOperator::GreaterThan, true) => IntPredicate::SGT,
|
mir::CmpOperator::GT => CmpPredicate::GT,
|
||||||
(mir::LogicOperator::LessThan, false) => IntPredicate::ULT,
|
mir::CmpOperator::LE => CmpPredicate::LE,
|
||||||
(mir::LogicOperator::GreaterThan, false) => IntPredicate::UGT,
|
mir::CmpOperator::GE => CmpPredicate::GE,
|
||||||
|
mir::CmpOperator::EQ => CmpPredicate::EQ,
|
||||||
|
mir::CmpOperator::NE => CmpPredicate::NE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl mir::Block {
|
impl mir::Block {
|
||||||
pub fn codegen<'ctx>(&self, mut scope: &mut Scope<'ctx>) -> Option<Value<'ctx>> {
|
fn codegen<'ctx, 'a>(&self, mut scope: &mut Scope<'ctx, 'a>) -> Option<InstructionValue> {
|
||||||
for stmt in &self.statements {
|
for stmt in &self.statements {
|
||||||
stmt.codegen(&mut scope);
|
stmt.codegen(&mut scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((kind, expr)) = &self.return_expression {
|
if let Some((kind, expr)) = &self.return_expression {
|
||||||
let ret = expr.codegen(&mut scope).unwrap();
|
if let Some(ret) = expr.codegen(&mut scope) {
|
||||||
match kind {
|
match kind {
|
||||||
mir::ReturnKind::Hard => {
|
mir::ReturnKind::Hard => {
|
||||||
scope.block.ret(&ret).unwrap();
|
scope.block.terminate(Term::Ret(ret)).unwrap();
|
||||||
None
|
None
|
||||||
|
}
|
||||||
|
mir::ReturnKind::Soft => Some(ret),
|
||||||
}
|
}
|
||||||
mir::ReturnKind::Soft => Some(ret),
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -259,21 +367,44 @@ impl mir::Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl mir::Literal {
|
impl mir::Literal {
|
||||||
pub fn codegen<'ctx>(&self, context: &'ctx Context) -> Value<'ctx> {
|
fn as_const(&self, block: &mut Block) -> InstructionValue {
|
||||||
let val: IntegerValue<'ctx> = match *self {
|
block.build(self.as_const_kind()).unwrap()
|
||||||
mir::Literal::I32(val) => context.type_i32().from_signed(val as i64),
|
}
|
||||||
mir::Literal::I16(val) => context.type_i16().from_signed(val as i64),
|
|
||||||
};
|
fn as_const_kind(&self) -> Instr {
|
||||||
Value::Integer(val)
|
Instr::Constant(match *self {
|
||||||
|
mir::Literal::I8(val) => ConstValue::I8(val),
|
||||||
|
mir::Literal::I16(val) => ConstValue::I16(val),
|
||||||
|
mir::Literal::I32(val) => ConstValue::I32(val),
|
||||||
|
mir::Literal::I64(val) => ConstValue::I64(val),
|
||||||
|
mir::Literal::I128(val) => ConstValue::I128(val),
|
||||||
|
mir::Literal::U8(val) => ConstValue::U8(val),
|
||||||
|
mir::Literal::U16(val) => ConstValue::U16(val),
|
||||||
|
mir::Literal::U32(val) => ConstValue::U32(val),
|
||||||
|
mir::Literal::U64(val) => ConstValue::U64(val),
|
||||||
|
mir::Literal::U128(val) => ConstValue::U128(val),
|
||||||
|
mir::Literal::Bool(val) => ConstValue::Bool(val),
|
||||||
|
mir::Literal::Vague(_) => panic!("Got vague literal!"),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypeKind {
|
impl TypeKind {
|
||||||
fn get_type<'ctx>(&self, context: &'ctx Context) -> TypeEnum<'ctx> {
|
fn get_type(&self) -> Type {
|
||||||
match &self {
|
match &self {
|
||||||
TypeKind::I32 => TypeEnum::Integer(context.type_i32()),
|
TypeKind::I8 => Type::I8,
|
||||||
TypeKind::I16 => TypeEnum::Integer(context.type_i16()),
|
TypeKind::I16 => Type::I16,
|
||||||
|
TypeKind::I32 => Type::I32,
|
||||||
|
TypeKind::I64 => Type::I64,
|
||||||
|
TypeKind::I128 => Type::I128,
|
||||||
|
TypeKind::U8 => Type::U8,
|
||||||
|
TypeKind::U16 => Type::U16,
|
||||||
|
TypeKind::U32 => Type::U32,
|
||||||
|
TypeKind::U64 => Type::U64,
|
||||||
|
TypeKind::U128 => Type::U128,
|
||||||
|
TypeKind::Bool => Type::Bool,
|
||||||
TypeKind::Void => panic!("Void not a supported type"),
|
TypeKind::Void => panic!("Void not a supported type"),
|
||||||
|
TypeKind::Vague(_) => panic!("Tried to compile a vague type!"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,8 @@ pub enum Token {
|
|||||||
// Keywords
|
// Keywords
|
||||||
/// `let`
|
/// `let`
|
||||||
LetKeyword,
|
LetKeyword,
|
||||||
|
/// `mut`
|
||||||
|
MutKeyword,
|
||||||
/// `import`
|
/// `import`
|
||||||
ImportKeyword,
|
ImportKeyword,
|
||||||
/// `return`
|
/// `return`
|
||||||
@ -22,6 +24,12 @@ pub enum Token {
|
|||||||
Arrow,
|
Arrow,
|
||||||
/// `if`
|
/// `if`
|
||||||
If,
|
If,
|
||||||
|
/// `else`
|
||||||
|
Else,
|
||||||
|
/// `true`
|
||||||
|
True,
|
||||||
|
/// `false`
|
||||||
|
False,
|
||||||
|
|
||||||
// Symbols
|
// Symbols
|
||||||
/// `;`
|
/// `;`
|
||||||
@ -43,6 +51,8 @@ pub enum Token {
|
|||||||
LessThan,
|
LessThan,
|
||||||
/// `&`
|
/// `&`
|
||||||
Et,
|
Et,
|
||||||
|
/// `!`
|
||||||
|
Exclamation,
|
||||||
|
|
||||||
/// `(`
|
/// `(`
|
||||||
ParenOpen,
|
ParenOpen,
|
||||||
@ -75,6 +85,7 @@ impl From<Token> for String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A token with a position
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FullToken {
|
pub struct FullToken {
|
||||||
pub token: Token,
|
pub token: Token,
|
||||||
@ -92,7 +103,7 @@ impl Debug for FullToken {
|
|||||||
|
|
||||||
pub type Position = (u32, u32);
|
pub type Position = (u32, u32);
|
||||||
|
|
||||||
pub struct Cursor<'a> {
|
struct Cursor<'a> {
|
||||||
pub position: Position,
|
pub position: Position,
|
||||||
char_stream: Chars<'a>,
|
char_stream: Chars<'a>,
|
||||||
}
|
}
|
||||||
@ -122,6 +133,8 @@ impl<'a> Cursor<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Take source text and produce a list of [`FullToken`]s from it, ie.
|
||||||
|
/// tokenizing it.
|
||||||
pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error> {
|
pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error> {
|
||||||
let to_tokenize = to_tokenize.into();
|
let to_tokenize = to_tokenize.into();
|
||||||
let mut cursor = Cursor {
|
let mut cursor = Cursor {
|
||||||
@ -132,6 +145,9 @@ pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error
|
|||||||
let mut tokens = Vec::new();
|
let mut tokens = Vec::new();
|
||||||
|
|
||||||
while let Some(character) = &cursor.next() {
|
while let Some(character) = &cursor.next() {
|
||||||
|
// Save "current" token first character position
|
||||||
|
let position = (cursor.position.0 - 1, cursor.position.1);
|
||||||
|
|
||||||
let variant = match character {
|
let variant = match character {
|
||||||
// Whitespace
|
// Whitespace
|
||||||
w if w.is_whitespace() => continue,
|
w if w.is_whitespace() => continue,
|
||||||
@ -156,10 +172,14 @@ pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error
|
|||||||
// Check for keywords
|
// Check for keywords
|
||||||
let variant = match value.as_str() {
|
let variant = match value.as_str() {
|
||||||
"let" => Token::LetKeyword,
|
"let" => Token::LetKeyword,
|
||||||
|
"mut" => Token::MutKeyword,
|
||||||
"import" => Token::ImportKeyword,
|
"import" => Token::ImportKeyword,
|
||||||
"return" => Token::ReturnKeyword,
|
"return" => Token::ReturnKeyword,
|
||||||
"fn" => Token::FnKeyword,
|
"fn" => Token::FnKeyword,
|
||||||
"if" => Token::If,
|
"if" => Token::If,
|
||||||
|
"else" => Token::Else,
|
||||||
|
"true" => Token::True,
|
||||||
|
"false" => Token::False,
|
||||||
_ => Token::Identifier(value),
|
_ => Token::Identifier(value),
|
||||||
};
|
};
|
||||||
variant
|
variant
|
||||||
@ -190,6 +210,7 @@ pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error
|
|||||||
'>' => Token::GreaterThan,
|
'>' => Token::GreaterThan,
|
||||||
'<' => Token::LessThan,
|
'<' => Token::LessThan,
|
||||||
'&' => Token::Et,
|
'&' => Token::Et,
|
||||||
|
'!' => Token::Exclamation,
|
||||||
'(' => Token::ParenOpen,
|
'(' => Token::ParenOpen,
|
||||||
')' => Token::ParenClose,
|
')' => Token::ParenClose,
|
||||||
'{' => Token::BraceOpen,
|
'{' => Token::BraceOpen,
|
||||||
@ -201,7 +222,7 @@ pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error
|
|||||||
|
|
||||||
tokens.push(FullToken {
|
tokens.push(FullToken {
|
||||||
token: variant,
|
token: variant,
|
||||||
position: cursor.position,
|
position,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,45 @@
|
|||||||
|
//! Reid is a toy-language compiler I'm working on to learn LLVM (and compiler
|
||||||
|
//! development).
|
||||||
|
//!
|
||||||
|
//! Reid only uses [llvm-sys](https://gitlab.com/taricorp/llvm-sys.rs), which
|
||||||
|
//! provide very minimal bindings from the LLVM C-API to Rust. `reid_llvm`-crate
|
||||||
|
//! contains the relevant abstraction to produce a more Rust'y API from that.
|
||||||
|
//!
|
||||||
|
//! Much of the syntax in Reid is directly inspired by rust, but mostly it is
|
||||||
|
//! driven by simplicity.
|
||||||
|
//!
|
||||||
|
//! Reid is currently able to (non-exhaustively):
|
||||||
|
//! - Do basic algebra (e.g. Add, Sub, Mult)
|
||||||
|
//! - Resolve complex one-liners correctly using PEDMAS (e.g. `5 + 2 * 5 - 5 *
|
||||||
|
//! 5` is calculated correctly)
|
||||||
|
//! - Declare and call functions with varying parameters and return types
|
||||||
|
//! - Perform type-checking and type-inference such that return-types and
|
||||||
|
//! parameter types must always match.
|
||||||
|
//! - Do simple logic-operations (e.g. If/And/Or)
|
||||||
|
//!
|
||||||
|
//! An example program of Reid, that calculates the 5th fibonacci number (and
|
||||||
|
//! uses Rust for highlighting) is:
|
||||||
|
//! ```rust
|
||||||
|
//! fn main() -> u16 {
|
||||||
|
//! return fibonacci(5);
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! fn fibonacci(n: u16) -> u16 {
|
||||||
|
//! if n <= 2 {
|
||||||
|
//! return 1;
|
||||||
|
//! }
|
||||||
|
//! return fibonacci(n-1) + fibonacci(n-2);
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! Currently missing relevant features (TODOs) are:
|
||||||
|
//! - Arrays
|
||||||
|
//! - Structs (and custom types as such)
|
||||||
|
//! - Extern functions
|
||||||
|
//! - Strings
|
||||||
|
//! - Loops
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use mir::typecheck::TypeCheck;
|
||||||
use reid_lib::Context;
|
use reid_lib::Context;
|
||||||
|
|
||||||
use crate::{ast::TopLevelStatement, lexer::Token, token_stream::TokenStream};
|
use crate::{ast::TopLevelStatement, lexer::Token, token_stream::TokenStream};
|
||||||
@ -6,13 +48,9 @@ mod ast;
|
|||||||
mod codegen;
|
mod codegen;
|
||||||
mod lexer;
|
mod lexer;
|
||||||
pub mod mir;
|
pub mod mir;
|
||||||
|
mod pad_adapter;
|
||||||
mod token_stream;
|
mod token_stream;
|
||||||
|
mod util;
|
||||||
// TODO:
|
|
||||||
// 1. Make it so that TopLevelStatement can only be import or function def
|
|
||||||
// 2. Make BlockLevelStatement, that has everything TopLevelStatement has now
|
|
||||||
// 3. Make it so all codegen is done with a Block-struct, that represents a
|
|
||||||
// single proper block
|
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum ReidError {
|
pub enum ReidError {
|
||||||
@ -20,10 +58,13 @@ pub enum ReidError {
|
|||||||
LexerError(#[from] lexer::Error),
|
LexerError(#[from] lexer::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
ParserError(#[from] token_stream::Error),
|
ParserError(#[from] token_stream::Error),
|
||||||
// #[error(transparent)]
|
#[error("Errors during typecheck: {0:?}")]
|
||||||
// CodegenError(#[from] codegen::Error),
|
TypeCheckErrors(Vec<mir::pass::Error<mir::typecheck::ErrorKind>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Takes in a bit of source code, parses and compiles it and produces `hello.o`
|
||||||
|
/// and `hello.asm` from it, which can be linked using `ld` to produce an
|
||||||
|
/// executable file.
|
||||||
pub fn compile(source: &str) -> Result<String, ReidError> {
|
pub fn compile(source: &str) -> Result<String, ReidError> {
|
||||||
let tokens = lexer::tokenize(source)?;
|
let tokens = lexer::tokenize(source)?;
|
||||||
|
|
||||||
@ -35,7 +76,6 @@ pub fn compile(source: &str) -> Result<String, ReidError> {
|
|||||||
|
|
||||||
while !matches!(token_stream.peek().unwrap_or(Token::Eof), Token::Eof) {
|
while !matches!(token_stream.peek().unwrap_or(Token::Eof), Token::Eof) {
|
||||||
let statement = token_stream.parse::<TopLevelStatement>()?;
|
let statement = token_stream.parse::<TopLevelStatement>()?;
|
||||||
dbg!(&statement);
|
|
||||||
statements.push(statement);
|
statements.push(statement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,15 +85,24 @@ pub fn compile(source: &str) -> Result<String, ReidError> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
dbg!(&ast_module);
|
dbg!(&ast_module);
|
||||||
let mir_module = ast_module.process();
|
let mut mir_context = mir::Context::from(vec![ast_module]);
|
||||||
|
|
||||||
dbg!(&mir_module);
|
println!("{}", &mir_context);
|
||||||
|
|
||||||
|
let state = mir_context.pass(&mut TypeCheck);
|
||||||
|
dbg!(&state);
|
||||||
|
|
||||||
|
println!("{}", &mir_context);
|
||||||
|
|
||||||
|
if !state.errors.is_empty() {
|
||||||
|
return Err(ReidError::TypeCheckErrors(state.errors));
|
||||||
|
}
|
||||||
|
|
||||||
let mut context = Context::new();
|
let mut context = Context::new();
|
||||||
let cogegen_module = mir_module.codegen(&mut context);
|
let codegen_modules = mir_context.codegen(&mut context);
|
||||||
|
|
||||||
Ok(match cogegen_module.module.print_to_string() {
|
dbg!(&codegen_modules);
|
||||||
Ok(v) => v,
|
codegen_modules.compile();
|
||||||
Err(e) => panic!("Err: {:?}", e),
|
|
||||||
})
|
Ok(String::new())
|
||||||
}
|
}
|
||||||
|
213
reid/src/mir/display.rs
Normal file
213
reid/src/mir/display.rs
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
use std::fmt::{Debug, Display, Write};
|
||||||
|
|
||||||
|
use crate::pad_adapter::PadAdapter;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl Display for Context {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
for module in &self.modules {
|
||||||
|
Display::fmt(&module, f)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Module {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
writeln!(f, "Module({}) {{", self.name)?;
|
||||||
|
|
||||||
|
let mut state = Default::default();
|
||||||
|
let mut inner_f = PadAdapter::wrap(f, &mut state);
|
||||||
|
|
||||||
|
for import in &self.imports {
|
||||||
|
writeln!(inner_f, "{}", import)?;
|
||||||
|
}
|
||||||
|
for fun in &self.functions {
|
||||||
|
writeln!(inner_f, "{}", fun)?;
|
||||||
|
}
|
||||||
|
writeln!(f, "}}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Import {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "import {}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for FunctionDefinition {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"fn {}({}) -> {} ",
|
||||||
|
self.name,
|
||||||
|
self.parameters
|
||||||
|
.iter()
|
||||||
|
.map(|(n, t)| format!("{}: {}", n, t))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", "),
|
||||||
|
self.return_type
|
||||||
|
)?;
|
||||||
|
Display::fmt(&self.kind, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for FunctionDefinitionKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Local(block, _) => {
|
||||||
|
write!(f, "{}", block)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Self::Extern => write!(f, "<External>"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Block {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
writeln!(f, "{{")?;
|
||||||
|
let mut state = Default::default();
|
||||||
|
let mut inner_f = PadAdapter::wrap(f, &mut state);
|
||||||
|
for statement in &self.statements {
|
||||||
|
write!(inner_f, "{}", statement)?;
|
||||||
|
}
|
||||||
|
if let Some(ret) = &self.return_expression {
|
||||||
|
match ret.0 {
|
||||||
|
ReturnKind::Hard => writeln!(inner_f, "Return(Hard): {}", ret.1),
|
||||||
|
ReturnKind::Soft => writeln!(inner_f, "Return(Soft): {}", ret.1),
|
||||||
|
}?;
|
||||||
|
} else {
|
||||||
|
writeln!(inner_f, "No Return")?;
|
||||||
|
}
|
||||||
|
writeln!(f, "}}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Statement {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
writeln!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for StmtKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Let(var, mutable, block) => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"let{} {} = {}",
|
||||||
|
if *mutable { " mut" } else { "" },
|
||||||
|
var,
|
||||||
|
block
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Self::Set(var, expr) => write!(f, "{} = {}", var, expr),
|
||||||
|
Self::Import(n) => write!(f, "import {}", n),
|
||||||
|
Self::Expression(exp) => Display::fmt(exp, f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Expression {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_char('(')?;
|
||||||
|
Display::fmt(&self.0, f)?;
|
||||||
|
f.write_char(')')?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ExprKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Variable(var) => Display::fmt(var, f),
|
||||||
|
Self::Literal(lit) => Display::fmt(lit, f),
|
||||||
|
Self::BinOp(op, lhs, rhs) => write!(f, "{} {} {}", lhs, op, rhs),
|
||||||
|
Self::FunctionCall(fc) => Display::fmt(fc, f),
|
||||||
|
Self::If(if_exp) => Display::fmt(&if_exp, f),
|
||||||
|
Self::Block(block) => Display::fmt(block, f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for IfExpression {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "if {} ", self.0)?;
|
||||||
|
Display::fmt(&self.1, f)?;
|
||||||
|
if let Some(e) = &self.2 {
|
||||||
|
Display::fmt(&e, f)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for FunctionCall {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}<{}>(", self.name, self.return_type)?;
|
||||||
|
for (i, param) in self.parameters.iter().enumerate() {
|
||||||
|
Display::fmt(param, f)?;
|
||||||
|
if i < (self.parameters.len() - 1) {
|
||||||
|
write!(f, ", ")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write!(f, ")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for VariableReference {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "v(\"{}\", {})", &self.1, &self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Literal {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::I8(val) => write!(f, "{}i8", val),
|
||||||
|
Self::I16(val) => write!(f, "{}i16", val),
|
||||||
|
Self::I32(val) => write!(f, "{}i32", val),
|
||||||
|
Self::I64(val) => write!(f, "{}i64", val),
|
||||||
|
Self::I128(val) => write!(f, "{}i128", val),
|
||||||
|
Self::U8(val) => write!(f, "{}u8", val),
|
||||||
|
Self::U16(val) => write!(f, "{}u16", val),
|
||||||
|
Self::U32(val) => write!(f, "{}u32", val),
|
||||||
|
Self::U64(val) => write!(f, "{}u64", val),
|
||||||
|
Self::U128(val) => write!(f, "{}u128", val),
|
||||||
|
Self::Bool(val) => write!(f, "{}", val),
|
||||||
|
Self::Vague(val) => val.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for BinaryOperator {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
BinaryOperator::Add => write!(f, "+"),
|
||||||
|
BinaryOperator::Minus => write!(f, "-"),
|
||||||
|
BinaryOperator::Mult => write!(f, "*"),
|
||||||
|
BinaryOperator::And => write!(f, "&&"),
|
||||||
|
BinaryOperator::Cmp(op) => Display::fmt(op, f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for CmpOperator {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
CmpOperator::LT => write!(f, "<"),
|
||||||
|
CmpOperator::LE => write!(f, "<="),
|
||||||
|
CmpOperator::GT => write!(f, ">"),
|
||||||
|
CmpOperator::GE => write!(f, ">="),
|
||||||
|
CmpOperator::EQ => write!(f, "=="),
|
||||||
|
CmpOperator::NE => write!(f, "!="),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Metadata {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{:?}", self.range)
|
||||||
|
}
|
||||||
|
}
|
@ -1,55 +1,158 @@
|
|||||||
/// In this module are defined structs that are used for performing passes on
|
//! In this module are defined structs that are used for performing passes on
|
||||||
/// Reid. It contains a simplified version of Reid which must already be
|
//! Reid. It contains a simplified version of Reid which can be e.g.
|
||||||
/// type-checked beforehand.
|
//! typechecked.
|
||||||
|
|
||||||
use crate::token_stream::TokenRange;
|
use crate::token_stream::TokenRange;
|
||||||
|
|
||||||
|
mod display;
|
||||||
|
pub mod pass;
|
||||||
|
pub mod typecheck;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
pub struct Metadata {
|
pub struct Metadata {
|
||||||
pub range: TokenRange,
|
pub range: TokenRange,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::ops::Add for Metadata {
|
||||||
|
type Output = Metadata;
|
||||||
|
|
||||||
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
Metadata {
|
||||||
|
range: self.range + rhs.range,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<TokenRange> for Metadata {
|
impl From<TokenRange> for Metadata {
|
||||||
fn from(value: TokenRange) -> Self {
|
fn from(value: TokenRange) -> Self {
|
||||||
Metadata { range: value }
|
Metadata { range: value }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Metadata {
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
|
||||||
fn default() -> Self {
|
pub enum TypeKind {
|
||||||
Metadata {
|
#[error("bool")]
|
||||||
range: Default::default(),
|
Bool,
|
||||||
}
|
#[error("i8")]
|
||||||
}
|
I8,
|
||||||
|
#[error("i16")]
|
||||||
|
I16,
|
||||||
|
#[error("i32")]
|
||||||
|
I32,
|
||||||
|
#[error("i64")]
|
||||||
|
I64,
|
||||||
|
#[error("i128")]
|
||||||
|
I128,
|
||||||
|
#[error("u8")]
|
||||||
|
U8,
|
||||||
|
#[error("u16")]
|
||||||
|
U16,
|
||||||
|
#[error("u32")]
|
||||||
|
U32,
|
||||||
|
#[error("u64")]
|
||||||
|
U64,
|
||||||
|
#[error("u128")]
|
||||||
|
U128,
|
||||||
|
#[error("void")]
|
||||||
|
Void,
|
||||||
|
#[error(transparent)]
|
||||||
|
Vague(#[from] VagueType),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
|
||||||
pub enum TypeKind {
|
pub enum VagueType {
|
||||||
I32,
|
#[error("Unknown")]
|
||||||
I16,
|
Unknown,
|
||||||
Void,
|
#[error("Number")]
|
||||||
|
Number,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypeKind {
|
||||||
|
pub fn is_known(&self) -> Result<TypeKind, VagueType> {
|
||||||
|
if let TypeKind::Vague(vague) = self {
|
||||||
|
Err(*vague)
|
||||||
|
} else {
|
||||||
|
Ok(*self)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypeKind {
|
impl TypeKind {
|
||||||
pub fn signed(&self) -> bool {
|
pub fn signed(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
_ => true,
|
TypeKind::Void => false,
|
||||||
|
TypeKind::Vague(_) => false,
|
||||||
|
TypeKind::Bool => false,
|
||||||
|
TypeKind::I8 => false,
|
||||||
|
TypeKind::I16 => false,
|
||||||
|
TypeKind::I32 => false,
|
||||||
|
TypeKind::I64 => false,
|
||||||
|
TypeKind::I128 => false,
|
||||||
|
TypeKind::U8 => false,
|
||||||
|
TypeKind::U16 => false,
|
||||||
|
TypeKind::U32 => false,
|
||||||
|
TypeKind::U64 => false,
|
||||||
|
TypeKind::U128 => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_maths(&self) -> bool {
|
||||||
|
use TypeKind::*;
|
||||||
|
match &self {
|
||||||
|
I8 => true,
|
||||||
|
I16 => true,
|
||||||
|
I32 => true,
|
||||||
|
I64 => true,
|
||||||
|
I128 => true,
|
||||||
|
U8 => true,
|
||||||
|
U16 => true,
|
||||||
|
U32 => true,
|
||||||
|
U64 => true,
|
||||||
|
U128 => true,
|
||||||
|
Bool => true,
|
||||||
|
Vague(_) => false,
|
||||||
|
Void => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum Literal {
|
pub enum Literal {
|
||||||
I32(i32),
|
I8(i8),
|
||||||
I16(i16),
|
I16(i16),
|
||||||
|
I32(i32),
|
||||||
|
I64(i64),
|
||||||
|
I128(i128),
|
||||||
|
U8(u8),
|
||||||
|
U16(u16),
|
||||||
|
U32(u32),
|
||||||
|
U64(u64),
|
||||||
|
U128(u128),
|
||||||
|
Bool(bool),
|
||||||
|
Vague(VagueLiteral),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum VagueLiteral {
|
||||||
|
Number(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Literal {
|
impl Literal {
|
||||||
pub fn as_type(self: &Literal) -> TypeKind {
|
pub fn as_type(self: &Literal) -> TypeKind {
|
||||||
match self {
|
match self {
|
||||||
Literal::I32(_) => TypeKind::I32,
|
Literal::I8(_) => TypeKind::I8,
|
||||||
Literal::I16(_) => TypeKind::I16,
|
Literal::I16(_) => TypeKind::I16,
|
||||||
|
Literal::I32(_) => TypeKind::I32,
|
||||||
|
Literal::I64(_) => TypeKind::I64,
|
||||||
|
Literal::I128(_) => TypeKind::I128,
|
||||||
|
Literal::U8(_) => TypeKind::U8,
|
||||||
|
Literal::U16(_) => TypeKind::U16,
|
||||||
|
Literal::U32(_) => TypeKind::U32,
|
||||||
|
Literal::U64(_) => TypeKind::U64,
|
||||||
|
Literal::U128(_) => TypeKind::U128,
|
||||||
|
Literal::Bool(_) => TypeKind::Bool,
|
||||||
|
Literal::Vague(VagueLiteral::Number(_)) => TypeKind::Vague(VagueType::Number),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,16 +163,21 @@ pub enum BinaryOperator {
|
|||||||
Minus,
|
Minus,
|
||||||
Mult,
|
Mult,
|
||||||
And,
|
And,
|
||||||
Logic(LogicOperator),
|
Cmp(CmpOperator),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Specifically the operators that LLVM likes to take in as "icmp" parameters
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum LogicOperator {
|
pub enum CmpOperator {
|
||||||
LessThan,
|
LT,
|
||||||
GreaterThan,
|
LE,
|
||||||
|
GT,
|
||||||
|
GE,
|
||||||
|
EQ,
|
||||||
|
NE,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum ReturnKind {
|
pub enum ReturnKind {
|
||||||
Hard,
|
Hard,
|
||||||
Soft,
|
Soft,
|
||||||
@ -108,6 +216,7 @@ pub struct FunctionCall {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FunctionDefinition {
|
pub struct FunctionDefinition {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub return_type: TypeKind,
|
||||||
pub parameters: Vec<(String, TypeKind)>,
|
pub parameters: Vec<(String, TypeKind)>,
|
||||||
pub kind: FunctionDefinitionKind,
|
pub kind: FunctionDefinitionKind,
|
||||||
}
|
}
|
||||||
@ -116,8 +225,23 @@ pub struct FunctionDefinition {
|
|||||||
pub enum FunctionDefinitionKind {
|
pub enum FunctionDefinitionKind {
|
||||||
/// Actual definition block and surrounding signature range
|
/// Actual definition block and surrounding signature range
|
||||||
Local(Block, Metadata),
|
Local(Block, Metadata),
|
||||||
/// Return Type
|
Extern,
|
||||||
Extern(TypeKind),
|
}
|
||||||
|
|
||||||
|
impl FunctionDefinition {
|
||||||
|
fn block_meta(&self) -> Metadata {
|
||||||
|
match &self.kind {
|
||||||
|
FunctionDefinitionKind::Local(block, _) => block.meta,
|
||||||
|
FunctionDefinitionKind::Extern => Metadata::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Metadata {
|
||||||
|
match &self.kind {
|
||||||
|
FunctionDefinitionKind::Local(_, metadata) => *metadata,
|
||||||
|
FunctionDefinitionKind::Extern => Metadata::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -133,8 +257,9 @@ pub struct Statement(pub StmtKind, pub Metadata);
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum StmtKind {
|
pub enum StmtKind {
|
||||||
/// Variable name+type, evaluation
|
/// Variable name++mutability+type, evaluation
|
||||||
Let(VariableReference, Expression),
|
Let(VariableReference, bool, Expression),
|
||||||
|
Set(VariableReference, Expression),
|
||||||
Import(Import),
|
Import(Import),
|
||||||
Expression(Expression),
|
Expression(Expression),
|
||||||
}
|
}
|
||||||
@ -145,3 +270,8 @@ pub struct Module {
|
|||||||
pub imports: Vec<Import>,
|
pub imports: Vec<Import>,
|
||||||
pub functions: Vec<FunctionDefinition>,
|
pub functions: Vec<FunctionDefinition>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Context {
|
||||||
|
pub modules: Vec<Module>,
|
||||||
|
}
|
||||||
|
284
reid/src/mir/pass.rs
Normal file
284
reid/src/mir/pass.rs
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
//! This module contains relevant code for [`Pass`] and shared code between
|
||||||
|
//! passes. Passes can be performed on Reid MIR to e.g. typecheck the code.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::error::Error as STDError;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug, Clone)]
|
||||||
|
pub enum SimplePassError {
|
||||||
|
#[error("Function not defined: {0}")]
|
||||||
|
FunctionAlreadyDefined(String),
|
||||||
|
#[error("Variable not defined: {0}")]
|
||||||
|
VariableAlreadyDefined(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Error<TErr: STDError> {
|
||||||
|
metadata: Metadata,
|
||||||
|
kind: TErr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TErr: STDError> std::fmt::Display for Error<TErr> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "Error at {}: {}", self.metadata, self.kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TErr: STDError> STDError for Error<TErr> {
|
||||||
|
fn source(&self) -> Option<&(dyn STDError + 'static)> {
|
||||||
|
self.kind.source()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Storage<T: std::fmt::Debug>(HashMap<String, T>);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct State<TErr: STDError> {
|
||||||
|
pub errors: Vec<Error<TErr>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TErr: STDError> State<TErr> {
|
||||||
|
fn new() -> State<TErr> {
|
||||||
|
State {
|
||||||
|
errors: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn or_else<U, T: Into<Metadata> + Clone + Copy>(
|
||||||
|
&mut self,
|
||||||
|
result: Result<U, TErr>,
|
||||||
|
default: U,
|
||||||
|
meta: T,
|
||||||
|
) -> U {
|
||||||
|
match result {
|
||||||
|
Ok(t) => t,
|
||||||
|
Err(e) => {
|
||||||
|
self.errors.push(Error {
|
||||||
|
metadata: meta.into(),
|
||||||
|
kind: e,
|
||||||
|
});
|
||||||
|
default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ok<T: Into<Metadata> + Clone + Copy, U>(
|
||||||
|
&mut self,
|
||||||
|
result: Result<U, TErr>,
|
||||||
|
meta: T,
|
||||||
|
) -> Option<U> {
|
||||||
|
match result {
|
||||||
|
Ok(v) => Some(v),
|
||||||
|
Err(e) => {
|
||||||
|
self.errors.push(Error {
|
||||||
|
metadata: meta.into(),
|
||||||
|
kind: e,
|
||||||
|
});
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: std::fmt::Debug> Default for Storage<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone + std::fmt::Debug> Storage<T> {
|
||||||
|
pub fn set(&mut self, key: String, value: T) -> Result<T, ()> {
|
||||||
|
if let Some(_) = self.0.get(&key) {
|
||||||
|
Err(())
|
||||||
|
} else {
|
||||||
|
self.0.insert(key, value.clone());
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, key: &String) -> Option<&T> {
|
||||||
|
self.0.get(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug)]
|
||||||
|
pub struct Scope {
|
||||||
|
pub function_returns: Storage<ScopeFunction>,
|
||||||
|
pub variables: Storage<TypeKind>,
|
||||||
|
/// Hard Return type of this scope, if inside a function
|
||||||
|
pub return_type_hint: Option<TypeKind>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ScopeFunction {
|
||||||
|
pub ret: TypeKind,
|
||||||
|
pub params: Vec<TypeKind>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scope {
|
||||||
|
pub fn inner(&self) -> Scope {
|
||||||
|
Scope {
|
||||||
|
function_returns: self.function_returns.clone(),
|
||||||
|
variables: self.variables.clone(),
|
||||||
|
return_type_hint: self.return_type_hint,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PassState<'st, 'sc, TError: STDError + Clone> {
|
||||||
|
state: &'st mut State<TError>,
|
||||||
|
pub scope: &'sc mut Scope,
|
||||||
|
inner: Vec<Scope>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'st, 'sc, TError: STDError + Clone> PassState<'st, 'sc, TError> {
|
||||||
|
fn from(state: &'st mut State<TError>, scope: &'sc mut Scope) -> Self {
|
||||||
|
PassState {
|
||||||
|
state,
|
||||||
|
scope,
|
||||||
|
inner: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn or_else<U, TMeta: Into<Metadata> + Clone + Copy>(
|
||||||
|
&mut self,
|
||||||
|
result: Result<U, TError>,
|
||||||
|
default: U,
|
||||||
|
meta: TMeta,
|
||||||
|
) -> U {
|
||||||
|
self.state.or_else(result, default, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ok<TMeta: Into<Metadata> + Clone + Copy, U>(
|
||||||
|
&mut self,
|
||||||
|
result: Result<U, TError>,
|
||||||
|
meta: TMeta,
|
||||||
|
) -> Option<U> {
|
||||||
|
self.state.ok(result, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inner(&mut self) -> PassState<TError> {
|
||||||
|
self.inner.push(self.scope.inner());
|
||||||
|
let scope = self.inner.last_mut().unwrap();
|
||||||
|
PassState {
|
||||||
|
state: self.state,
|
||||||
|
scope,
|
||||||
|
inner: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Pass {
|
||||||
|
type TError: STDError + Clone;
|
||||||
|
|
||||||
|
fn module(&mut self, _module: &mut Module, mut _state: PassState<Self::TError>) {}
|
||||||
|
fn function(
|
||||||
|
&mut self,
|
||||||
|
_function: &mut FunctionDefinition,
|
||||||
|
mut _state: PassState<Self::TError>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
fn block(&mut self, _block: &mut Block, mut _state: PassState<Self::TError>) {}
|
||||||
|
fn stmt(&mut self, _stmt: &mut Statement, mut _state: PassState<Self::TError>) {}
|
||||||
|
fn expr(&mut self, _expr: &mut Expression, mut _state: PassState<Self::TError>) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
pub fn pass<T: Pass>(&mut self, pass: &mut T) -> State<T::TError> {
|
||||||
|
let mut state = State::new();
|
||||||
|
let mut scope = Scope::default();
|
||||||
|
for module in &mut self.modules {
|
||||||
|
module.pass(pass, &mut state, &mut scope);
|
||||||
|
}
|
||||||
|
state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Module {
|
||||||
|
fn pass<T: Pass>(&mut self, pass: &mut T, state: &mut State<T::TError>, scope: &mut Scope) {
|
||||||
|
for function in &self.functions {
|
||||||
|
scope
|
||||||
|
.function_returns
|
||||||
|
.set(
|
||||||
|
function.name.clone(),
|
||||||
|
ScopeFunction {
|
||||||
|
ret: function.return_type,
|
||||||
|
params: function.parameters.iter().map(|v| v.1).collect(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
pass.module(self, PassState::from(state, scope));
|
||||||
|
|
||||||
|
for function in &mut self.functions {
|
||||||
|
function.pass(pass, state, scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FunctionDefinition {
|
||||||
|
fn pass<T: Pass>(&mut self, pass: &mut T, state: &mut State<T::TError>, scope: &mut Scope) {
|
||||||
|
for param in &self.parameters {
|
||||||
|
scope.variables.set(param.0.clone(), param.1).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
pass.function(self, PassState::from(state, scope));
|
||||||
|
|
||||||
|
match &mut self.kind {
|
||||||
|
FunctionDefinitionKind::Local(block, _) => {
|
||||||
|
scope.return_type_hint = Some(self.return_type);
|
||||||
|
block.pass(pass, state, scope);
|
||||||
|
}
|
||||||
|
FunctionDefinitionKind::Extern => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Block {
|
||||||
|
fn pass<T: Pass>(&mut self, pass: &mut T, state: &mut State<T::TError>, scope: &mut Scope) {
|
||||||
|
let mut scope = scope.inner();
|
||||||
|
|
||||||
|
for statement in &mut self.statements {
|
||||||
|
statement.pass(pass, state, &mut scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
pass.block(self, PassState::from(state, &mut scope));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Statement {
|
||||||
|
fn pass<T: Pass>(&mut self, pass: &mut T, state: &mut State<T::TError>, scope: &mut Scope) {
|
||||||
|
match &mut self.0 {
|
||||||
|
StmtKind::Let(_, mutable, expression) => {
|
||||||
|
expression.pass(pass, state, scope);
|
||||||
|
}
|
||||||
|
StmtKind::Set(variable_reference, expression) => {} // TODO
|
||||||
|
StmtKind::Import(_) => todo!(),
|
||||||
|
StmtKind::Expression(expression) => {
|
||||||
|
expression.pass(pass, state, scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pass.stmt(self, PassState::from(state, scope));
|
||||||
|
|
||||||
|
match &mut self.0 {
|
||||||
|
StmtKind::Let(variable_reference, mutable, _) => scope
|
||||||
|
.variables
|
||||||
|
.set(variable_reference.1.clone(), variable_reference.0)
|
||||||
|
.ok(),
|
||||||
|
StmtKind::Set(variable_reference, expression) => None, // TODO
|
||||||
|
StmtKind::Import(_) => todo!(),
|
||||||
|
StmtKind::Expression(_) => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Expression {
|
||||||
|
fn pass<T: Pass>(&mut self, pass: &mut T, state: &mut State<T::TError>, scope: &mut Scope) {
|
||||||
|
pass.expr(self, PassState::from(state, scope));
|
||||||
|
}
|
||||||
|
}
|
416
reid/src/mir/typecheck.rs
Normal file
416
reid/src/mir/typecheck.rs
Normal file
@ -0,0 +1,416 @@
|
|||||||
|
//! This module contains code relevant to doing a type checking pass on the MIR.
|
||||||
|
//! During typechecking relevant types are also coerced if possible.
|
||||||
|
use std::{convert::Infallible, iter};
|
||||||
|
|
||||||
|
use crate::{mir::*, util::try_all};
|
||||||
|
use TypeKind::*;
|
||||||
|
use VagueType::*;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
pass::{Pass, PassState, ScopeFunction},
|
||||||
|
types::ReturnType,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug, Clone)]
|
||||||
|
pub enum ErrorKind {
|
||||||
|
#[error("NULL error, should never occur!")]
|
||||||
|
Null,
|
||||||
|
#[error("Type is vague: {0}")]
|
||||||
|
TypeIsVague(VagueType),
|
||||||
|
#[error("Can not coerce {0} to vague type {1}")]
|
||||||
|
HintIsVague(TypeKind, VagueType),
|
||||||
|
#[error("Literal {0} can not be coerced to type {1}")]
|
||||||
|
LiteralIncompatible(Literal, TypeKind),
|
||||||
|
#[error("Types {0} and {1} are incompatible")]
|
||||||
|
TypesIncompatible(TypeKind, TypeKind),
|
||||||
|
#[error("Variable not defined: {0}")]
|
||||||
|
VariableNotDefined(String),
|
||||||
|
#[error("Function not defined: {0}")]
|
||||||
|
FunctionNotDefined(String),
|
||||||
|
#[error("Type is vague: {0}")]
|
||||||
|
ReturnTypeMismatch(TypeKind, TypeKind),
|
||||||
|
#[error("Function not defined: {0}")]
|
||||||
|
FunctionAlreadyDefined(String),
|
||||||
|
#[error("Variable not defined: {0}")]
|
||||||
|
VariableAlreadyDefined(String),
|
||||||
|
#[error("Function {0} was given {1} parameters, but {2} were expected")]
|
||||||
|
InvalidAmountParameters(String, usize, usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Struct used to implement a type-checking pass that can be performed on the
|
||||||
|
/// MIR.
|
||||||
|
pub struct TypeCheck;
|
||||||
|
|
||||||
|
impl Pass for TypeCheck {
|
||||||
|
type TError = ErrorKind;
|
||||||
|
|
||||||
|
fn module(&mut self, module: &mut Module, mut state: PassState<ErrorKind>) {
|
||||||
|
for function in &mut module.functions {
|
||||||
|
let res = function.typecheck(&mut state);
|
||||||
|
state.ok(res, function.block_meta());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FunctionDefinition {
|
||||||
|
fn typecheck(&mut self, state: &mut PassState<ErrorKind>) -> Result<TypeKind, ErrorKind> {
|
||||||
|
for param in &self.parameters {
|
||||||
|
let param_t = state.or_else(param.1.assert_known(), Vague(Unknown), self.signature());
|
||||||
|
let res = state
|
||||||
|
.scope
|
||||||
|
.variables
|
||||||
|
.set(param.0.clone(), param_t)
|
||||||
|
.or(Err(ErrorKind::VariableAlreadyDefined(param.0.clone())));
|
||||||
|
state.ok(res, self.signature());
|
||||||
|
}
|
||||||
|
|
||||||
|
let return_type = self.return_type.clone();
|
||||||
|
let inferred = match &mut self.kind {
|
||||||
|
FunctionDefinitionKind::Local(block, _) => {
|
||||||
|
state.scope.return_type_hint = Some(self.return_type);
|
||||||
|
block.typecheck(state, Some(return_type))
|
||||||
|
}
|
||||||
|
FunctionDefinitionKind::Extern => Ok(Vague(Unknown)),
|
||||||
|
};
|
||||||
|
|
||||||
|
match inferred {
|
||||||
|
Ok(t) => try_collapse(&return_type, &t)
|
||||||
|
.or(Err(ErrorKind::ReturnTypeMismatch(return_type, t))),
|
||||||
|
Err(e) => Ok(state.or_else(Err(e), return_type, self.block_meta())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Block {
|
||||||
|
fn typecheck(
|
||||||
|
&mut self,
|
||||||
|
state: &mut PassState<ErrorKind>,
|
||||||
|
hint_t: Option<TypeKind>,
|
||||||
|
) -> Result<TypeKind, ErrorKind> {
|
||||||
|
let mut state = state.inner();
|
||||||
|
|
||||||
|
let mut early_return = None;
|
||||||
|
|
||||||
|
for statement in &mut self.statements {
|
||||||
|
let ret = match &mut statement.0 {
|
||||||
|
// TODO
|
||||||
|
StmtKind::Let(variable_reference, mutable, expression) => {
|
||||||
|
let res = expression.typecheck(&mut state, Some(variable_reference.0));
|
||||||
|
|
||||||
|
// If expression resolution itself was erronous, resolve as
|
||||||
|
// Unknown.
|
||||||
|
let res = state.or_else(res, Vague(Unknown), expression.1);
|
||||||
|
|
||||||
|
// Make sure the expression and variable type really is the same
|
||||||
|
let res_t = state.or_else(
|
||||||
|
res.collapse_into(&variable_reference.0),
|
||||||
|
Vague(Unknown),
|
||||||
|
variable_reference.2 + expression.1,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Make sure expression/variable type is NOT vague anymore
|
||||||
|
let res_t =
|
||||||
|
state.or_else(res_t.or_default(), Vague(Unknown), variable_reference.2);
|
||||||
|
|
||||||
|
// Update typing to be more accurate
|
||||||
|
variable_reference.0 = res_t;
|
||||||
|
|
||||||
|
// Variable might already be defined, note error
|
||||||
|
let res = state
|
||||||
|
.scope
|
||||||
|
.variables
|
||||||
|
.set(variable_reference.1.clone(), variable_reference.0)
|
||||||
|
.or(Err(ErrorKind::VariableAlreadyDefined(
|
||||||
|
variable_reference.1.clone(),
|
||||||
|
)));
|
||||||
|
state.ok(res, variable_reference.2);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
StmtKind::Set(variable_reference, expression) => None, // TODO
|
||||||
|
StmtKind::Import(_) => todo!(),
|
||||||
|
StmtKind::Expression(expression) => {
|
||||||
|
let res = expression.typecheck(&mut state, None);
|
||||||
|
state.or_else(res, Void, expression.1);
|
||||||
|
if let Ok((kind, _)) = expression.return_type() {
|
||||||
|
Some((kind, expression))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some((ReturnKind::Hard, _)) = ret {
|
||||||
|
early_return = early_return.or(ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO should actually probably prune all instructions after this one
|
||||||
|
// as to not cause problems in codegen later (when unable to delete the
|
||||||
|
// block)
|
||||||
|
if let Some((ReturnKind::Hard, expr)) = early_return {
|
||||||
|
let hint = state.scope.return_type_hint;
|
||||||
|
let res = expr.typecheck(&mut state, hint);
|
||||||
|
return Ok(state.or_else(res, Vague(Unknown), expr.1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((return_kind, expr)) = &mut self.return_expression {
|
||||||
|
// Use function return type as hint if return is hard.
|
||||||
|
let ret_hint_t = match return_kind {
|
||||||
|
ReturnKind::Hard => state.scope.return_type_hint,
|
||||||
|
ReturnKind::Soft => hint_t,
|
||||||
|
};
|
||||||
|
let res = expr.typecheck(&mut state, ret_hint_t);
|
||||||
|
Ok(state.or_else(res, Vague(Unknown), expr.1))
|
||||||
|
} else {
|
||||||
|
Ok(Void)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Expression {
|
||||||
|
fn typecheck(
|
||||||
|
&mut self,
|
||||||
|
state: &mut PassState<ErrorKind>,
|
||||||
|
hint_t: Option<TypeKind>,
|
||||||
|
) -> Result<TypeKind, ErrorKind> {
|
||||||
|
match &mut self.0 {
|
||||||
|
ExprKind::Variable(var_ref) => {
|
||||||
|
let existing = state.or_else(
|
||||||
|
state
|
||||||
|
.scope
|
||||||
|
.variables
|
||||||
|
.get(&var_ref.1)
|
||||||
|
.copied()
|
||||||
|
.ok_or(ErrorKind::VariableNotDefined(var_ref.1.clone())),
|
||||||
|
Vague(Unknown),
|
||||||
|
var_ref.2,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update typing to be more accurate
|
||||||
|
var_ref.0 = state.or_else(
|
||||||
|
var_ref.0.collapse_into(&existing),
|
||||||
|
Vague(Unknown),
|
||||||
|
var_ref.2,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(var_ref.0)
|
||||||
|
}
|
||||||
|
ExprKind::Literal(literal) => {
|
||||||
|
*literal = literal.try_coerce(hint_t)?;
|
||||||
|
Ok(literal.as_type())
|
||||||
|
}
|
||||||
|
ExprKind::BinOp(op, lhs, rhs) => {
|
||||||
|
// TODO make sure lhs and rhs can actually do this binary
|
||||||
|
// operation once relevant
|
||||||
|
let lhs_res = lhs.typecheck(state, None);
|
||||||
|
let lhs_type = state.or_else(lhs_res, Vague(Unknown), lhs.1);
|
||||||
|
let rhs_res = rhs.typecheck(state, Some(lhs_type));
|
||||||
|
let rhs_type = state.or_else(rhs_res, Vague(Unknown), rhs.1);
|
||||||
|
|
||||||
|
if let Some(collapsed) = state.ok(rhs_type.collapse_into(&rhs_type), self.1) {
|
||||||
|
// Try to coerce both sides again with collapsed type
|
||||||
|
lhs.typecheck(state, Some(collapsed)).ok();
|
||||||
|
rhs.typecheck(state, Some(collapsed)).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = lhs_type.binop_type(&op, &rhs_type)?;
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
ExprKind::FunctionCall(function_call) => {
|
||||||
|
let true_function = state
|
||||||
|
.scope
|
||||||
|
.function_returns
|
||||||
|
.get(&function_call.name)
|
||||||
|
.cloned()
|
||||||
|
.ok_or(ErrorKind::FunctionNotDefined(function_call.name.clone()));
|
||||||
|
|
||||||
|
if let Ok(f) = true_function {
|
||||||
|
let param_len_given = function_call.parameters.len();
|
||||||
|
let param_len_expected = f.params.len();
|
||||||
|
|
||||||
|
// Check that there are the same number of parameters given
|
||||||
|
// as expected
|
||||||
|
if param_len_given != param_len_expected {
|
||||||
|
state.ok::<_, Infallible>(
|
||||||
|
Err(ErrorKind::InvalidAmountParameters(
|
||||||
|
function_call.name.clone(),
|
||||||
|
param_len_given,
|
||||||
|
param_len_expected,
|
||||||
|
)),
|
||||||
|
self.1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let true_params_iter = f.params.into_iter().chain(iter::repeat(Vague(Unknown)));
|
||||||
|
|
||||||
|
for (param, true_param_t) in
|
||||||
|
function_call.parameters.iter_mut().zip(true_params_iter)
|
||||||
|
{
|
||||||
|
// Typecheck every param separately
|
||||||
|
let param_res = param.typecheck(state, Some(true_param_t));
|
||||||
|
let param_t = state.or_else(param_res, Vague(Unknown), param.1);
|
||||||
|
state.ok(param_t.collapse_into(&true_param_t), param.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure function return type is the same as the claimed
|
||||||
|
// return type
|
||||||
|
let ret_t = try_collapse(&f.ret, &function_call.return_type)?;
|
||||||
|
// Update typing to be more accurate
|
||||||
|
function_call.return_type = ret_t;
|
||||||
|
Ok(ret_t)
|
||||||
|
} else {
|
||||||
|
Ok(function_call.return_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::If(IfExpression(cond, lhs, rhs)) => {
|
||||||
|
// TODO make sure cond_res is Boolean here
|
||||||
|
let cond_res = cond.typecheck(state, Some(Bool));
|
||||||
|
let cond_t = state.or_else(cond_res, Vague(Unknown), cond.1);
|
||||||
|
state.ok(cond_t.collapse_into(&Bool), cond.1);
|
||||||
|
|
||||||
|
// Typecheck then/else return types and make sure they are the
|
||||||
|
// same, if else exists.
|
||||||
|
let then_res = lhs.typecheck(state, hint_t);
|
||||||
|
let then_ret_t = state.or_else(then_res, Vague(Unknown), lhs.meta);
|
||||||
|
let else_ret_t = if let Some(else_block) = rhs {
|
||||||
|
let res = else_block.typecheck(state, hint_t);
|
||||||
|
state.or_else(res, Vague(Unknown), else_block.meta)
|
||||||
|
} else {
|
||||||
|
// TODO assert that then_ret_t is Void
|
||||||
|
Vague(Unknown)
|
||||||
|
};
|
||||||
|
|
||||||
|
let collapsed = then_ret_t.collapse_into(&else_ret_t)?;
|
||||||
|
if let Some(rhs) = rhs {
|
||||||
|
// If rhs existed, typecheck both sides to perform type
|
||||||
|
// coercion.
|
||||||
|
let lhs_res = lhs.typecheck(state, Some(collapsed));
|
||||||
|
let rhs_res = rhs.typecheck(state, Some(collapsed));
|
||||||
|
state.ok(lhs_res, lhs.meta);
|
||||||
|
state.ok(rhs_res, rhs.meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(collapsed)
|
||||||
|
}
|
||||||
|
ExprKind::Block(block) => block.typecheck(state, hint_t),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Literal {
|
||||||
|
/// Try to coerce this literal, ie. convert it to a more specific type in
|
||||||
|
/// regards to the given hint if any.
|
||||||
|
fn try_coerce(self, hint: Option<TypeKind>) -> Result<Self, ErrorKind> {
|
||||||
|
if let Some(hint) = hint {
|
||||||
|
use Literal as L;
|
||||||
|
use VagueLiteral as VagueL;
|
||||||
|
Ok(match (self, hint) {
|
||||||
|
(L::I8(_), I8) => self,
|
||||||
|
(L::I16(_), I16) => self,
|
||||||
|
(L::I32(_), I32) => self,
|
||||||
|
(L::I64(_), I64) => self,
|
||||||
|
(L::I128(_), I128) => self,
|
||||||
|
(L::U8(_), U8) => self,
|
||||||
|
(L::U16(_), U16) => self,
|
||||||
|
(L::U32(_), U32) => self,
|
||||||
|
(L::U64(_), U64) => self,
|
||||||
|
(L::U128(_), U128) => self,
|
||||||
|
(L::Bool(_), Bool) => self,
|
||||||
|
(L::Vague(VagueL::Number(v)), I8) => L::I8(v as i8),
|
||||||
|
(L::Vague(VagueL::Number(v)), I16) => L::I16(v as i16),
|
||||||
|
(L::Vague(VagueL::Number(v)), I32) => L::I32(v as i32),
|
||||||
|
(L::Vague(VagueL::Number(v)), I64) => L::I64(v as i64),
|
||||||
|
(L::Vague(VagueL::Number(v)), I128) => L::I128(v as i128),
|
||||||
|
(L::Vague(VagueL::Number(v)), U8) => L::U8(v as u8),
|
||||||
|
(L::Vague(VagueL::Number(v)), U16) => L::U16(v as u16),
|
||||||
|
(L::Vague(VagueL::Number(v)), U32) => L::U32(v as u32),
|
||||||
|
(L::Vague(VagueL::Number(v)), U64) => L::U64(v as u64),
|
||||||
|
(L::Vague(VagueL::Number(v)), U128) => L::U128(v as u128),
|
||||||
|
// Default type for number literal if unable to find true type.
|
||||||
|
(L::Vague(VagueL::Number(v)), Vague(Number)) => L::I32(v as i32),
|
||||||
|
(_, Vague(_)) => self,
|
||||||
|
_ => Err(ErrorKind::LiteralIncompatible(self, hint))?,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypeKind {
|
||||||
|
/// Assert that a type is already known and not vague. Return said type or
|
||||||
|
/// error.
|
||||||
|
fn assert_known(&self) -> Result<TypeKind, ErrorKind> {
|
||||||
|
self.is_known().map_err(ErrorKind::TypeIsVague)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to collapse a type on itself producing a default type if one exists,
|
||||||
|
/// Error if not.
|
||||||
|
fn or_default(&self) -> Result<TypeKind, ErrorKind> {
|
||||||
|
match self {
|
||||||
|
Vague(vague_type) => match vague_type {
|
||||||
|
Unknown => Err(ErrorKind::TypeIsVague(*vague_type)),
|
||||||
|
Number => Ok(TypeKind::I32),
|
||||||
|
},
|
||||||
|
_ => Ok(*self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn binop_type(&self, op: &BinaryOperator, other: &TypeKind) -> Result<TypeKind, ErrorKind> {
|
||||||
|
let res = self.collapse_into(other)?;
|
||||||
|
Ok(match op {
|
||||||
|
BinaryOperator::Add => res,
|
||||||
|
BinaryOperator::Minus => res,
|
||||||
|
BinaryOperator::Mult => res,
|
||||||
|
BinaryOperator::And => res,
|
||||||
|
BinaryOperator::Cmp(_) => Bool,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_collapse(lhs: &TypeKind, rhs: &TypeKind) -> Result<TypeKind, ErrorKind> {
|
||||||
|
lhs.collapse_into(rhs)
|
||||||
|
.or(rhs.collapse_into(lhs))
|
||||||
|
.or(Err(ErrorKind::TypesIncompatible(*lhs, *rhs)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Collapsable: Sized + Clone {
|
||||||
|
/// Try to narrow two types into one singular type. E.g. Vague(Number) and
|
||||||
|
/// I32 could be narrowed to just I32.
|
||||||
|
fn collapse_into(&self, other: &Self) -> Result<Self, ErrorKind>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Collapsable for TypeKind {
|
||||||
|
fn collapse_into(&self, other: &TypeKind) -> Result<TypeKind, ErrorKind> {
|
||||||
|
if self == other {
|
||||||
|
return Ok(self.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
match (self, other) {
|
||||||
|
(Vague(Number), other) | (other, Vague(Number)) => match other {
|
||||||
|
Vague(Unknown) => Ok(Vague(Number)),
|
||||||
|
Vague(Number) => Ok(Vague(Number)),
|
||||||
|
I8 | I16 | I32 | I64 | I128 | U8 | U16 | U32 | U64 | U128 => Ok(*other),
|
||||||
|
_ => Err(ErrorKind::TypesIncompatible(*self, *other)),
|
||||||
|
},
|
||||||
|
(Vague(Unknown), other) | (other, Vague(Unknown)) => Ok(other.clone()),
|
||||||
|
_ => Err(ErrorKind::TypesIncompatible(*self, *other)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Collapsable for ScopeFunction {
|
||||||
|
fn collapse_into(&self, other: &ScopeFunction) -> Result<ScopeFunction, ErrorKind> {
|
||||||
|
Ok(ScopeFunction {
|
||||||
|
ret: self.ret.collapse_into(&other.ret)?,
|
||||||
|
params: try_all(
|
||||||
|
self.params
|
||||||
|
.iter()
|
||||||
|
.zip(&other.params)
|
||||||
|
.map(|(p1, p2)| p1.collapse_into(&p2))
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
.map_err(|e| e.first().unwrap().clone())?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -9,36 +9,47 @@ pub enum ReturnTypeOther {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait ReturnType {
|
pub trait ReturnType {
|
||||||
fn return_type(&self) -> Result<TypeKind, ReturnTypeOther>;
|
fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReturnType for Block {
|
impl ReturnType for Block {
|
||||||
fn return_type(&self) -> Result<TypeKind, ReturnTypeOther> {
|
fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
|
||||||
self.return_expression
|
self.return_expression
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.ok_or(ReturnTypeOther::NoBlockReturn(self.meta.range))
|
.ok_or(ReturnTypeOther::NoBlockReturn(self.meta.range))
|
||||||
.and_then(|(_, stmt)| stmt.return_type())
|
.and_then(|(kind, stmt)| Ok((*kind, stmt.return_type()?.1)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReturnType for Statement {
|
impl ReturnType for Statement {
|
||||||
fn return_type(&self) -> Result<TypeKind, ReturnTypeOther> {
|
fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
|
||||||
use StmtKind::*;
|
use StmtKind::*;
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Expression(e) => e.return_type(),
|
Expression(e) => e.return_type(),
|
||||||
|
Set(_, _) => todo!(),
|
||||||
Import(_) => Err(ReturnTypeOther::Import(self.1.range)),
|
Import(_) => Err(ReturnTypeOther::Import(self.1.range)),
|
||||||
Let(_, _) => Err(ReturnTypeOther::Let(self.1.range)),
|
Let(_, _, _) => Err(ReturnTypeOther::Let(self.1.range)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReturnType for Expression {
|
impl ReturnType for Expression {
|
||||||
fn return_type(&self) -> Result<TypeKind, ReturnTypeOther> {
|
fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
|
||||||
use ExprKind::*;
|
use ExprKind::*;
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Literal(lit) => Ok(lit.as_type()),
|
Literal(lit) => Ok((ReturnKind::Soft, lit.as_type())),
|
||||||
Variable(var) => var.return_type(),
|
Variable(var) => var.return_type(),
|
||||||
BinOp(_, expr, _) => expr.return_type(),
|
BinOp(_, then_e, else_e) => {
|
||||||
|
let then_r = then_e.return_type()?;
|
||||||
|
let else_r = else_e.return_type()?;
|
||||||
|
|
||||||
|
let kind = if then_r.0 == ReturnKind::Hard && else_r.0 == ReturnKind::Hard {
|
||||||
|
ReturnKind::Hard
|
||||||
|
} else {
|
||||||
|
ReturnKind::Soft
|
||||||
|
};
|
||||||
|
Ok((kind, then_r.1))
|
||||||
|
}
|
||||||
Block(block) => block.return_type(),
|
Block(block) => block.return_type(),
|
||||||
FunctionCall(fcall) => fcall.return_type(),
|
FunctionCall(fcall) => fcall.return_type(),
|
||||||
If(expr) => expr.return_type(),
|
If(expr) => expr.return_type(),
|
||||||
@ -47,28 +58,32 @@ impl ReturnType for Expression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ReturnType for IfExpression {
|
impl ReturnType for IfExpression {
|
||||||
fn return_type(&self) -> Result<TypeKind, ReturnTypeOther> {
|
fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
|
||||||
self.1.return_type()
|
let then_r = self.1.return_type()?;
|
||||||
|
if let Some(else_b) = &self.2 {
|
||||||
|
let else_r = else_b.return_type()?;
|
||||||
|
dbg!(&then_r, &else_r);
|
||||||
|
|
||||||
|
let kind = if then_r.0 == ReturnKind::Hard && else_r.0 == ReturnKind::Hard {
|
||||||
|
ReturnKind::Hard
|
||||||
|
} else {
|
||||||
|
ReturnKind::Soft
|
||||||
|
};
|
||||||
|
Ok((kind, then_r.1))
|
||||||
|
} else {
|
||||||
|
Ok((ReturnKind::Soft, then_r.1))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReturnType for VariableReference {
|
impl ReturnType for VariableReference {
|
||||||
fn return_type(&self) -> Result<TypeKind, ReturnTypeOther> {
|
fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
|
||||||
Ok(self.0)
|
Ok((ReturnKind::Soft, self.0.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReturnType for FunctionCall {
|
impl ReturnType for FunctionCall {
|
||||||
fn return_type(&self) -> Result<TypeKind, ReturnTypeOther> {
|
fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
|
||||||
Ok(self.return_type)
|
Ok((ReturnKind::Soft, self.return_type.clone()))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ReturnType for FunctionDefinition {
|
|
||||||
fn return_type(&self) -> Result<TypeKind, ReturnTypeOther> {
|
|
||||||
match &self.kind {
|
|
||||||
FunctionDefinitionKind::Local(block, _) => block.return_type(),
|
|
||||||
FunctionDefinitionKind::Extern(type_kind) => Ok(*type_kind),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
69
reid/src/pad_adapter.rs
Normal file
69
reid/src/pad_adapter.rs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
//! Copied from
|
||||||
|
//! https://github.com/rust-lang/rust/blob/6b3ae3f6e45a33c2d95fa0362c9b2593e567fd34/library/core/src/fmt/builders.rs#L102
|
||||||
|
|
||||||
|
// Copyright (c) The Rust Project Contributors
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
pub struct PadAdapter<'buf, 'state> {
|
||||||
|
buf: &'buf mut (dyn fmt::Write + 'buf),
|
||||||
|
state: &'state mut PadAdapterState,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PadAdapterState {
|
||||||
|
on_newline: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PadAdapterState {
|
||||||
|
fn default() -> Self {
|
||||||
|
PadAdapterState { on_newline: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'buf, 'state> PadAdapter<'buf, 'state> {
|
||||||
|
pub fn wrap<'slot, 'fmt: 'buf + 'slot>(
|
||||||
|
fmt: &'fmt mut fmt::Formatter<'_>,
|
||||||
|
state: &'state mut PadAdapterState,
|
||||||
|
) -> Self {
|
||||||
|
PadAdapter { buf: fmt, state }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Write for PadAdapter<'_, '_> {
|
||||||
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
|
for s in s.split_inclusive('\n') {
|
||||||
|
if self.state.on_newline {
|
||||||
|
self.buf.write_str(" ")?;
|
||||||
|
}
|
||||||
|
self.state.on_newline = s.ends_with('\n');
|
||||||
|
self.buf.write_str(s)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_char(&mut self, c: char) -> fmt::Result {
|
||||||
|
if self.state.on_newline {
|
||||||
|
self.buf.write_str(" ")?;
|
||||||
|
}
|
||||||
|
self.state.on_newline = c == '\n';
|
||||||
|
self.buf.write_char(c)
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,14 @@
|
|||||||
|
//! Contains relevant code for parsing tokens received from
|
||||||
|
//! Lexing/Tokenizing-stage.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::parse::Parse,
|
ast::parse::Parse,
|
||||||
lexer::{FullToken, Position, Token},
|
lexer::{FullToken, Position, Token},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Utility struct that is able to parse [`FullToken`]s while being
|
||||||
|
/// failure-resistance in that it can backtrack easily, and is able to keep
|
||||||
|
/// track of parsed Token-ranges easily.
|
||||||
pub struct TokenStream<'a, 'b> {
|
pub struct TokenStream<'a, 'b> {
|
||||||
ref_position: Option<&'b mut usize>,
|
ref_position: Option<&'b mut usize>,
|
||||||
tokens: &'a [FullToken],
|
tokens: &'a [FullToken],
|
||||||
@ -76,6 +82,7 @@ impl<'a, 'b> TokenStream<'a, 'b> {
|
|||||||
/// Parse the next item with Parse-trait, also mapping it with the given
|
/// Parse the next item with Parse-trait, also mapping it with the given
|
||||||
/// function. The token-stream is only consumed, if the inner function
|
/// function. The token-stream is only consumed, if the inner function
|
||||||
/// retuns an Ok.
|
/// retuns an Ok.
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn parse_map<T: Parse + std::fmt::Debug, F, O>(&mut self, inner: F) -> Result<O, Error>
|
pub fn parse_map<T: Parse + std::fmt::Debug, F, O>(&mut self, inner: F) -> Result<O, Error>
|
||||||
where
|
where
|
||||||
F: Fn(T) -> Result<O, Error>,
|
F: Fn(T) -> Result<O, Error>,
|
||||||
@ -156,7 +163,9 @@ impl Drop for TokenStream<'_, '_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
/// Index-range that can be used with the original array of [`FullToken`]s to
|
||||||
|
/// retrieve the precise location of a failure.
|
||||||
|
#[derive(Default, Clone, Copy)]
|
||||||
pub struct TokenRange {
|
pub struct TokenRange {
|
||||||
pub start: usize,
|
pub start: usize,
|
||||||
pub end: usize,
|
pub end: usize,
|
||||||
@ -168,15 +177,6 @@ impl std::fmt::Debug for TokenRange {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TokenRange {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
start: Default::default(),
|
|
||||||
end: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Add for TokenRange {
|
impl std::ops::Add for TokenRange {
|
||||||
type Output = TokenRange;
|
type Output = TokenRange;
|
||||||
|
|
||||||
|
21
reid/src/util.rs
Normal file
21
reid/src/util.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//! Take a list of Results, and try to unwrap all of them. Returns a Result
|
||||||
|
//! which either contains every Result unwrapped, or every error that exists
|
||||||
|
//! within the array.
|
||||||
|
|
||||||
|
pub fn try_all<U, E>(list: Vec<Result<U, E>>) -> Result<Vec<U>, Vec<E>> {
|
||||||
|
let mut successes = Vec::with_capacity(list.len());
|
||||||
|
let mut failures = Vec::with_capacity(list.len());
|
||||||
|
|
||||||
|
for item in list {
|
||||||
|
match item {
|
||||||
|
Ok(s) => successes.push(s),
|
||||||
|
Err(e) => failures.push(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if failures.len() > 0 {
|
||||||
|
Err(failures)
|
||||||
|
} else {
|
||||||
|
Ok(successes)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user