Compare commits

..

39 Commits

Author SHA1 Message Date
14e0dcbe15 (broken) add part 1 of mutability 2025-07-12 02:38:31 +03:00
85b2ebf04a Add mutability parsing 2025-07-11 21:32:20 +03:00
615fec6e52 Add TODO, remove TODOs 2025-07-09 22:44:33 +03:00
d04a70e464 Remove unused variable 2025-07-09 22:33:56 +03:00
383302c1c2 Fix old fibonacci not working due to last change 2025-07-09 22:32:47 +03:00
b19a32cd8a Make early returns work even without an explicit return 2025-07-09 22:20:08 +03:00
1aa9b3e76c Add RetVoid just in case 2025-07-09 22:01:32 +03:00
94c4ec0613 Add a block garbage-collector that will delete an unused block at end 2025-07-09 21:57:48 +03:00
d757ac4eb3 Fiddle around with if-expression codegen 2025-07-09 21:12:39 +03:00
c50474cc8e Update README.md 2025-07-09 20:27:12 +03:00
50f5f3cc70 Update lib.rs docs 2025-07-09 20:16:52 +03:00
f7fa69fbe2 Add docs to lib.rs 2025-07-09 20:13:16 +03:00
a907fec967 Fix module docs 2025-07-09 20:07:30 +03:00
9710d17e00 Add a bunch of documentation 2025-07-09 20:01:24 +03:00
257496aae2 Rename LogicOperator to CmpOperator 2025-07-09 19:24:07 +03:00
197f0b22f3 Add boolean literals 2025-07-09 19:17:03 +03:00
b84672ef8c Add Mult and And -operators to the whole chain 2025-07-09 19:08:21 +03:00
8afb2c2572 Fix bug in Logic Operator conversion 2025-07-09 19:00:03 +03:00
974647b401 Fix FullToken positions 2025-07-09 18:54:51 +03:00
d5daaa0e87 Improve Debug and Display implementations 2025-07-09 18:47:40 +03:00
49df6c9ed9 Add a bunch of new integer comparison operators 2025-07-09 16:05:08 +03:00
46560d8541 Add all types u8 -> i128 and fix signedness in icmp 2025-07-08 23:47:44 +03:00
14283afe59 Separate pass-common code to pass.rs 2025-07-08 21:44:04 +03:00
2e99ec3a80 Fix warnings 2025-07-08 18:48:18 +03:00
fb876e3ef5 Add type from let through parser to MIR 2025-07-08 18:41:10 +03:00
cdbc4593a8 Move hard_hint to scope 2025-07-08 18:31:04 +03:00
9b9fcd4ec4 Make AST contain only abstract Numbers 2025-07-08 00:48:28 +03:00
95b3ffe8ef Add custom debug format for LLIR 2025-07-08 00:04:35 +03:00
22737f022e Fix variable type let inference 2025-07-07 23:40:35 +03:00
a7292f4719 Fix comparison return type in typechecking 2025-07-07 23:31:32 +03:00
9b68ecb614 Add some security checks, typecheck for condition to be a boolean 2025-07-07 23:25:07 +03:00
a366d22470 Add typechecking 2025-07-07 23:03:21 +03:00
12dc457b99 Add mandatory return type for MIR Function 2025-07-07 18:32:37 +03:00
0932af2e3b Simplify transformation from AST to MIR 2025-07-06 23:01:28 +03:00
48ae533f33 Remove the old files 2025-07-06 20:49:28 +03:00
35efa78a56 Connect all the wires again 2025-07-06 20:49:13 +03:00
58117d86e4 Make a more Rusty LLIR for the lib that is compiled to LLVM IR 2025-07-06 19:47:05 +03:00
454cefafc9 Add middleware PrimaryExpression-struct to make TokenRanges correct 2025-07-05 18:02:06 +03:00
20dfdfec9f update README.md 2025-07-05 01:52:03 +03:00
27 changed files with 3171 additions and 1526 deletions

View File

@ -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

View File

@ -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(&param, &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(&param, &int_32.from_unsigned(1), "minus_one") .build(ICmp(CmpPredicate::LT, param_n, num_3))
.unwrap();
let minus_two = recurse
.sub(&param, &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),
}
} }

View 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),
}
}
}

View 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
View 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(())
}
}
}
}

View File

@ -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(&params) {
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),
} }

View File

@ -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)
}
}

View File

@ -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(())
}
}

View File

@ -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),
}; };

View File

@ -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;
}

View File

@ -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)
} }

View 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;
}

View File

@ -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),
} // }
} }

View File

@ -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),
} }

View File

@ -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;

View File

@ -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,
}
}
}

View File

@ -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!"),
} }
} }
} }

View File

@ -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,
}); });
} }

View File

@ -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
View 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)
}
}

View File

@ -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
View 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
View 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())?,
})
}
}

View File

@ -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
View 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)
}
}

View File

@ -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
View 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)
}
}