Compare commits

...

17 Commits

31 changed files with 620 additions and 389 deletions

View File

@ -42,15 +42,20 @@ Currently missing big features (TODOs) are:
- ~~Unary operators~~
- ~~Floats~~ (DONE)
- ~~Type casting~~ (DONE)
- Built-in Int/Float division and modulo
- ~~Built-in Int/Float division and modulo~~
- Loops
- Debug Information (PARTIALLY DONE)
- Ability to specify types in literals and variable definitions
- Intrinsic functions
- Not-Unary
Big features that I want later but are not necessary:
- Associated functions
- User-defined binary operations
- Asymmetric binary operations (e.g. string + u32)
- Error handling
- Lexing & parsing of whitespace and comments as well
- LSP implementation
Smaller features:
- Hex-numbers

View File

@ -1,30 +1,24 @@
// Arithmetic, function calls and imports!
struct Test {
field: i32,
second: [u32; 4]
field: i32,
second: [u32; 4],
}
fn test() -> Test {
let value = Test {
field: 5,
second: [6, 3, 4, 8],
};
return value;
let value = Test {
field: 5,
second: [6, 3, 4, 8],
};
return value;
}
fn main() -> u32 {
let mut value = test();
let mut value = test();
let mut a = &mut value;
let mut a = &mut value;
*a.second[2] = 15;
*a.second[2] = 5;
let b = 4;
if value.field < b {
return 0;
}
return value.second[2];
return *a.second[2];
}

View File

@ -8,11 +8,9 @@ fn other() -> i16 {
}
fn main() -> u32 {
let value = other() as u32;
let other_value = other() as f32;
let same_value = other() as i16;
let v = (allocate(4) as *u32);
let mut v = (allocate(4) as *u32);
v[0] = other() as u32;
return v[0];
}

6
examples/div_mod.reid Normal file
View File

@ -0,0 +1,6 @@
// Arithmetic, function calls and imports!
fn main() -> u16 {
return (50 / 5 + 52 % 5);
}

View File

@ -10,7 +10,7 @@ fn main() -> i32 {
add_char(&mut test, '!');
set_char(&mut test, 'B', 0);
add_num_to_str(&mut test, 7);
add_num_to_str(&mut test, 1234);
print(&test);

3
examples/ptr.reid Normal file → Executable file
View File

@ -1,8 +1,9 @@
// Arithmetic, function calls and imports!
import std::allocate;
fn main() -> u8 {
let mut ptr = malloc(4);
let mut ptr = allocate(4);
ptr[0] = 5;

View File

@ -1,11 +1,12 @@
import std::from_str;
import std::print;
import std::int_div;
fn main() -> i32 {
let hello = "hello world";
let hello = from_str("hello world");
print(hello);
print(&hello);
return int_div(15, 5).quotient;
}

View File

@ -1,7 +0,0 @@
fn foo() -> f32 { return 1.0; }
fn main() -> u8 {
let mut a = 0;
a = (foo() * 1.0) as u8;
return a;
}

View File

@ -1,153 +0,0 @@
struct StringBuffer {
buffer: [char; 64],
}
struct SDL_Thing {} // TODO: replace with proper dummy structs now that we can cast
struct SDL_FRect { x: f32, y: f32, w: f32, h: f32 }
struct SDL_Rect { x: i32, y: i32, w: i32, h: i32 }
extern fn SDL_malloc(size: u64) -> *SDL_Thing;
extern fn SDL_Init(flags: u32) -> bool;
extern fn SDL_Quit();
extern fn SDL_CreateWindowAndRenderer(title: *char, width: i32, height: i32, flags: i32,
window_out: &mut *SDL_Thing, renderer_out: &mut *SDL_Thing) -> bool;
extern fn SDL_Delay(ms: u32);
extern fn SDL_SetRenderDrawColor(renderer: *SDL_Thing, r: u8, g: u8, b: u8, a: u8);
extern fn SDL_RenderClear(renderer: *SDL_Thing);
extern fn SDL_RenderPresent(renderer: *SDL_Thing);
extern fn SDL_HasEvent(event_type: u32) -> bool;
extern fn SDL_PumpEvents();
extern fn SDL_FlushEvents(min_type: u32, max_type: u32);
extern fn SDL_GetTicks() -> u64;
extern fn SDL_snprintf(text: &mut StringBuffer, maxlen: u64, fmt: *char, var: u64) -> i32;
extern fn SDL_SetWindowTitle(window: *SDL_Thing, title: &StringBuffer) -> bool;
extern fn SDL_CreateTexture(renderer: *SDL_Thing,
pixel_format: u32, texture_access: u32, width: u32, height: u32) -> *SDL_Thing;
extern fn SDL_RenderTexture(renderer: *SDL_Thing,
texture: *SDL_Thing, srcfrect: &SDL_FRect, dstfrect: &SDL_FRect) -> bool;
extern fn SDL_UpdateTexture(texture: *SDL_Thing, rect: &SDL_Rect, pixels: *u8, pitch: u32) -> bool;
extern fn SDL_Log(fmt: *char, string_var: *char);
extern fn SDL_GetError() -> *char;
extern fn SDL_GetWindowSize(window: *SDL_Thing, w: &mut i32, h: &mut i32) -> bool;
struct GameState {
renderer: *SDL_Thing,
window: *SDL_Thing,
render_texture: *SDL_Thing,
frame_counter: u64,
pixels: *u8,
pixels_w: u32,
pixels_h: u32,
}
fn main() -> i32 {
let SDL_INIT_VIDEO = 32;
let SDL_WINDOW_RESIZABLE = 32;
let SDL_PIXELFORMAT_RGBA8888 = 373694468;
let SDL_PIXELFORMAT_ABGR8888 = 376840196;
let SDL_TEXTUREACCESS_STREAMING = 1;
let init_success = SDL_Init(SDL_INIT_VIDEO);
if init_success == false {
SDL_Log("SDL init failed: %s", SDL_GetError());
return 1;
}
let mut window = placeholder_ptr();
let mut renderer = placeholder_ptr();
let gfx_init_success = SDL_CreateWindowAndRenderer(
"graphical reid program", 640, 480, SDL_WINDOW_RESIZABLE,
&mut window, &mut renderer
);
if gfx_init_success == false {
SDL_Log("SDL renderer and window creation failed: %s", SDL_GetError());
return 1;
}
let width = 256;
let height = 256;
let render_texture = SDL_CreateTexture(renderer,
SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, width, height);
let pixels_len = (width * height * 4) as u64;
let pixels = SDL_malloc(pixels_len) as *u8;
let mut game_state = GameState {
renderer: renderer,
window: window,
render_texture: render_texture,
frame_counter: 0,
pixels: pixels,
pixels_w: width,
pixels_h: height,
};
frame_loop(game_state);
// TODO: maybe clean up resources here but also it's a bit of a waste of energy
SDL_Quit();
return 0;
}
fn frame_loop(game_state: GameState) -> GameState {
SDL_PumpEvents();
if SDL_HasEvent(256) { // SDL_EVENT_QUIT
return game_state;
}
SDL_FlushEvents(0, 4294967296);
let mut screen_width = 0;
let mut screen_height = 0;
SDL_GetWindowSize(game_state.window, &mut screen_width, &mut screen_height);
let renderer = game_state.renderer;
SDL_SetRenderDrawColor(renderer, 0, 50, 90, 255);
SDL_RenderClear(renderer);
let w = game_state.pixels_w;
let h = game_state.pixels_h;
draw_pixels(game_state.pixels, 0, 0, w, h, game_state.frame_counter);
let texture_area = SDL_Rect { x: 0, y: 0, w: w as i32, h: h as i32 };
if SDL_UpdateTexture(game_state.render_texture, &texture_area, game_state.pixels, 4 * w) == false {
SDL_Log("UpdateTexture error: %s", SDL_GetError());
}
let src = SDL_FRect { x: 0.0, y: 0.0, w: w as f32, h: h as f32 };
let dst = SDL_FRect { x: 0.0, y: 0.0, w: screen_width as f32, h: screen_height as f32 };
if SDL_RenderTexture(renderer, game_state.render_texture, &src, &dst) == false {
SDL_Log("RenderTexture error: %s", SDL_GetError());
}
SDL_RenderPresent(renderer);
SDL_Delay(16);
game_state.frame_counter = game_state.frame_counter + 1;
let mut s = StringBuffer {};
SDL_snprintf(&mut s, 64, "graphical reid program frame %u", game_state.frame_counter);
SDL_SetWindowTitle(game_state.window, &s);
return frame_loop(game_state);
}
fn placeholder_ptr() -> *SDL_Thing {
return SDL_malloc(1);
}
fn draw_pixels(pixels: *u8, x: u32, y: u32, w: u32, h: u32, frame_counter: u64) -> i32 {
if y >= h {
return 0;
}
let index = (x + y * w) * 4;
pixels[index + 0] = x as u8;
pixels[index + 1] = y as u8;
pixels[index + 2] = frame_counter as u8;
pixels[index + 3] = 255;
let mut new_x = x + 1;
let mut new_y = y;
if new_x >= w {
new_x = 0;
new_y = y + 1;
}
draw_pixels(pixels, new_x, new_y, w, h, frame_counter);
return 0;
}

View File

@ -16,8 +16,7 @@ BINARY="$(echo $1 | cut -d'.' -f1)"".out"
echo $1
make clean SRC=$1 ; make SRC=$1 && echo ""
cargo run --example cli $@ && \
$BINARY ; echo "Return value: ""$?"
## Command from: clang -v hello.o -o test

View File

@ -4,8 +4,8 @@
use std::{cell::RefCell, rc::Rc};
use crate::{
Block, BlockData, CustomTypeKind, FunctionData, Instr, InstructionData, ModuleData,
NamedStruct, TerminatorKind, Type, TypeCategory, TypeData,
Block, BlockData, CompileResult, CustomTypeKind, ErrorKind, FunctionData, Instr,
InstructionData, ModuleData, NamedStruct, TerminatorKind, Type, TypeCategory, TypeData,
debug_information::{
DebugInformation, DebugLocationValue, DebugMetadataValue, DebugProgramValue,
InstructionDebugRecordData,
@ -151,7 +151,7 @@ impl Builder {
block_val: &BlockValue,
data: InstructionData,
name: String,
) -> Result<InstructionValue, ()> {
) -> CompileResult<InstructionValue> {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(block_val.0.0.0);
@ -236,14 +236,14 @@ impl Builder {
&self,
block: &BlockValue,
value: TerminatorKind,
) -> Result<(), ()> {
) -> CompileResult<()> {
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(())
Err(ErrorKind::Null)
} else {
block.data.terminator = Some(value);
Ok(())
@ -255,14 +255,14 @@ impl Builder {
&self,
block: &BlockValue,
location: DebugLocationValue,
) -> Result<(), ()> {
) -> CompileResult<()> {
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_location {
Err(())
Err(ErrorKind::Null)
} else {
block.data.terminator_location = Some(location);
Ok(())
@ -270,7 +270,7 @@ impl Builder {
}
}
pub(crate) unsafe fn delete_block(&self, block: &BlockValue) -> Result<(), ()> {
pub(crate) unsafe fn delete_block(&self, block: &BlockValue) -> CompileResult<()> {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(block.0.0.0);
@ -349,7 +349,7 @@ impl Builder {
self.modules.clone()
}
pub fn check_instruction(&self, instruction: &InstructionValue) -> Result<(), ()> {
pub fn check_instruction(&self, instruction: &InstructionValue) -> CompileResult<()> {
unsafe {
match self.instr_data(&instruction).kind {
Instr::Param(_) => Ok(()),
@ -372,7 +372,7 @@ impl Builder {
if t.comparable() || t.category() != TypeCategory::Integer {
Ok(())
} else {
Err(()) // TODO error: Types not comparable
Err(ErrorKind::Null) // TODO error: Types not comparable
}
}
Instr::FCmp(_, lhs, rhs) => {
@ -380,17 +380,17 @@ impl Builder {
if t.comparable() || t.category() != TypeCategory::Real {
Ok(())
} else {
Err(()) // TODO error: Types not comparable
Err(ErrorKind::Null) // TODO error: Types not comparable
}
}
Instr::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
return Err(ErrorKind::Null); // 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
return Err(ErrorKind::Null); // TODO error: params do not match
}
}
Ok(())
@ -403,7 +403,7 @@ impl Builder {
// incoming values come from blocks that are added later
// than the one where this one exists.
let first = iter.next().ok_or(())?;
let first = iter.next().ok_or(ErrorKind::Null)?;
for item in iter {
match_types(first, item, &self)?;
}
@ -416,10 +416,10 @@ impl Builder {
if *ptr_ty_inner == load_ty {
Ok(())
} else {
Err(()) // TODO error: inner type mismatch
Err(ErrorKind::Null) // TODO error: inner type mismatch
}
} else {
Err(()) // TODO error: not a pointer
Err(ErrorKind::Null) // TODO error: not a pointer
}
}
Instr::Store(ptr, _) => {
@ -427,7 +427,7 @@ impl Builder {
if let Type::Ptr(_) = ty {
Ok(())
} else {
Err(()) // TODO error: not a pointer
Err(ErrorKind::Null) // TODO error: not a pointer
}
}
Instr::ArrayAlloca(_, _) => Ok(()),
@ -435,26 +435,27 @@ impl Builder {
let ptr_ty = ptr_val.get_type(&self)?;
match ptr_ty {
Type::Ptr(_) => Ok(()),
_ => Err(()),
_ => Err(ErrorKind::Null),
}
}
Instr::GetStructElemPtr(ptr_val, idx) => {
let ptr_ty = ptr_val.get_type(&self)?;
dbg!(&ptr_ty);
if let Type::Ptr(ty) = ptr_ty {
if let Type::CustomType(val) = *ty {
match self.type_data(&val).kind {
CustomTypeKind::NamedStruct(NamedStruct(_, fields)) => {
if fields.len() <= idx as usize {
return Err(()); // TODO error: no such field
return Err(ErrorKind::Null); // TODO error: no such field
}
}
}
Ok(())
} else {
Err(()) // TODO error: not a struct
Err(ErrorKind::Null) // TODO error: not a struct
}
} else {
Err(()) // TODO error: not a pointer
Err(ErrorKind::Null) // TODO error: not a pointer
}
}
Instr::ExtractValue(val, _) => {
@ -464,7 +465,7 @@ impl Builder {
CustomTypeKind::NamedStruct(_) => Ok(()),
},
Type::Array(_, _) => Ok(()),
_ => Err(()),
_ => Err(ErrorKind::Null),
}
}
Instr::Trunc(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
@ -545,7 +546,7 @@ impl InstructionValue {
}
}
pub(crate) fn get_type(&self, builder: &Builder) -> Result<Type, ()> {
pub(crate) fn get_type(&self, builder: &Builder) -> CompileResult<Type> {
use Instr::*;
unsafe {
match &builder.instr_data(self).kind {
@ -554,7 +555,7 @@ impl InstructionValue {
.params
.get(*nth)
.cloned()
.ok_or(()),
.ok_or(ErrorKind::Null),
Constant(c) => Ok(c.get_type()),
Add(lhs, rhs) => match_types(lhs, rhs, &builder),
FAdd(lhs, rhs) => match_types(lhs, rhs, &builder),
@ -572,7 +573,10 @@ impl InstructionValue {
ICmp(_, _, _) => Ok(Type::Bool),
FCmp(_, _, _) => 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)),
Phi(values) => values
.first()
.ok_or(ErrorKind::Null)
.and_then(|v| v.get_type(&builder)),
Alloca(ty) => Ok(Type::Ptr(Box::new(ty.clone()))),
Load(_, ty) => Ok(ty.clone()),
Store(_, value) => value.get_type(builder),
@ -614,7 +618,7 @@ impl InstructionValue {
}
}
Type::Array(elem_ty, _) => *elem_ty.clone(),
_ => return Err(()),
_ => return Err(ErrorKind::Null),
})
}
Trunc(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
@ -633,9 +637,9 @@ impl InstructionValue {
}
}
fn cast_to(&self, builder: &Builder, ty: &Type) -> Result<Instr, ()> {
fn cast_to(&self, builder: &Builder, ty: &Type) -> CompileResult<Instr> {
self.get_type(builder)?
.cast_instruction(*self, &ty)
.ok_or(())
.ok_or(ErrorKind::Null)
}
}

View File

@ -17,6 +17,14 @@ mod fmt;
mod pad_adapter;
mod util;
#[derive(thiserror::Error, Debug, Clone, PartialEq, PartialOrd)]
pub enum ErrorKind {
#[error("NULL error, should never occur!")]
Null,
}
pub type CompileResult<T> = Result<T, ErrorKind>;
#[derive(Debug)]
pub struct Context {
builder: Builder,
@ -254,7 +262,7 @@ impl<'builder> Block<'builder> {
&mut self,
name: T,
instruction: Instr,
) -> Result<InstructionValue, ()> {
) -> CompileResult<InstructionValue> {
unsafe {
self.builder.add_instruction(
&self.value,
@ -268,7 +276,7 @@ impl<'builder> Block<'builder> {
}
}
pub fn build(&mut self, instruction: Instr) -> Result<InstructionValue, ()> {
pub fn build(&mut self, instruction: Instr) -> CompileResult<InstructionValue> {
unsafe {
let name = instruction.default_name().to_owned();
self.builder.add_instruction(
@ -297,16 +305,16 @@ impl<'builder> Block<'builder> {
}
}
pub fn terminate(&mut self, instruction: TerminatorKind) -> Result<(), ()> {
pub fn terminate(&mut self, instruction: TerminatorKind) -> CompileResult<()> {
unsafe { self.builder.terminate(&self.value, instruction) }
}
pub fn set_terminator_location(&mut self, location: DebugLocationValue) -> Result<(), ()> {
pub fn set_terminator_location(&mut self, location: DebugLocationValue) -> CompileResult<()> {
unsafe { self.builder.set_terminator_location(&self.value, location) }
}
/// Delete block if it is unused. Return true if deleted, false if not.
pub fn delete_if_unused(&mut self) -> Result<bool, ()> {
pub fn delete_if_unused(&mut self) -> CompileResult<bool> {
unsafe {
if !self.builder.is_block_used(self.value()) {
self.builder.delete_block(&self.value)?;
@ -682,7 +690,7 @@ pub enum TypeCategory {
}
impl TerminatorKind {
pub(crate) fn get_type(&self, builder: &Builder) -> Result<Type, ()> {
pub(crate) fn get_type(&self, builder: &Builder) -> CompileResult<Type> {
use TerminatorKind::*;
match self {
Ret(instr_val) => instr_val.get_type(builder),

View File

@ -14,7 +14,7 @@ use llvm_sys::{
};
use crate::{
Type,
CompileResult, ErrorKind, Type,
builder::{Builder, InstructionValue},
};
@ -117,12 +117,16 @@ pub fn match_types(
lhs: &InstructionValue,
rhs: &InstructionValue,
builder: &Builder,
) -> Result<Type, ()> {
) -> CompileResult<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(()) }
if lhs_t == rhs_t {
Ok(lhs_t)
} else {
Err(ErrorKind::Null)
}
} else {
Err(())
Err(ErrorKind::Null)
}
}

View File

@ -1,11 +1,18 @@
use std::{env, fs, path::PathBuf};
use reid::{compile_simple, CustomIRs};
use reid::{compile_simple, ld::LDRunner, CustomIRs};
use reid_lib::compile::CompileOutput;
fn main() -> Result<(), std::io::Error> {
let args: Vec<String> = env::args().collect();
if let Some(filename) = args.get(1) {
let mut iter = args.into_iter().skip(1);
if let Some(filename) = iter.next() {
let mut libraries = Vec::new();
while let Some(libname) = iter.next() {
libraries.push(libname);
}
dbg!(&filename);
let path = PathBuf::from(filename).canonicalize().unwrap();
let parent = path.with_extension("");
let llvm_ir_path = parent.with_extension("ll");
@ -17,6 +24,7 @@ fn main() -> Result<(), std::io::Error> {
let before = std::time::SystemTime::now();
let text = fs::read_to_string(&path)?;
match compile_simple(&text, PathBuf::from(&path)) {
Ok((
CompileOutput {
@ -45,6 +53,15 @@ fn main() -> Result<(), std::io::Error> {
"Compilation took: {:.2}ms\n",
(after.duration_since(before).unwrap().as_micros() as f32) / 1000.
);
println!("Linking {:?}", &object_path);
let linker = std::env::var("LD").unwrap_or("ld".to_owned());
let mut linker = LDRunner::from_command(&linker).with_library("c");
for library in libraries {
linker = linker.with_library(&library);
}
linker.invoke(&object_path, &object_path.with_extension("out"));
}
Err(e) => panic!("{}", e),
};

View File

@ -93,14 +93,36 @@ fn str_length(string: *char, position: u32) -> u32 {
}
pub fn add_num_to_str(string: &mut String, num: u64) {
if num == 0 { add_char(string, '0'); }
else if num == 1 { add_char(string, '1'); }
else if num == 2 { add_char(string, '2'); }
else if num == 3 { add_char(string, '3'); }
else if num == 4 { add_char(string, '4'); }
else if num == 5 { add_char(string, '5'); }
else if num == 6 { add_char(string, '6'); }
else if num == 7 { add_char(string, '7'); }
else if num == 8 { add_char(string, '8'); }
else if num == 9 { add_char(string, '9'); }
if num > 10 {
add_num_to_str(string, num / 10)
}
let rem = num % 10;
if rem == 0 { add_char(string, '0'); }
else if rem == 1 { add_char(string, '1'); }
else if rem == 2 { add_char(string, '2'); }
else if rem == 3 { add_char(string, '3'); }
else if rem == 4 { add_char(string, '4'); }
else if rem == 5 { add_char(string, '5'); }
else if rem == 6 { add_char(string, '6'); }
else if rem == 7 { add_char(string, '7'); }
else if rem == 8 { add_char(string, '8'); }
else if rem == 9 { add_char(string, '9'); }
}
pub fn clamp(min: f32, max: f32, value: f32) -> f32 {
if value > max {
return max;
}
if value < min {
return min;
}
return value;
}
pub fn abs(f: f32) -> f32 {
if f < 0.0 {
return f * (0.0 - 1.0);
}
return f;
}

View File

@ -81,6 +81,8 @@ pub enum BinaryOperator {
Add,
Minus,
Mult,
Div,
Mod,
And,
LT,
@ -95,9 +97,11 @@ impl BinaryOperator {
pub fn get_precedence(&self) -> i8 {
use BinaryOperator::*;
match &self {
Minus => 5,
Add => 10,
Minus => 10,
Mult => 20,
Mult => 15,
Div => 20,
Mod => 20,
And => 100,
LT => 100,
LE => 100,

View File

@ -350,6 +350,8 @@ impl Parse for BinaryOperator {
(Some(Token::Plus), _) => BinaryOperator::Add,
(Some(Token::Minus), _) => BinaryOperator::Minus,
(Some(Token::Star), _) => BinaryOperator::Mult,
(Some(Token::Slash), _) => BinaryOperator::Div,
(Some(Token::Percent), _) => BinaryOperator::Mod,
(_, _) => Err(stream.expected_err("expected operator")?)?,
})
}

View File

@ -50,6 +50,7 @@ impl ast::Module {
block.into_mir(module_id),
(*range).as_meta(module_id),
),
source: module_id,
};
functions.push(def);
}
@ -70,6 +71,7 @@ impl ast::Module {
.map(|p| (p.0, p.1 .0.into_mir(module_id)))
.collect(),
kind: mir::FunctionDefinitionKind::Extern(false),
source: module_id,
};
functions.push(def);
}
@ -286,6 +288,8 @@ impl ast::BinaryOperator {
ast::BinaryOperator::Minus => mir::BinaryOperator::Minus,
ast::BinaryOperator::Mult => mir::BinaryOperator::Mult,
ast::BinaryOperator::And => mir::BinaryOperator::And,
ast::BinaryOperator::Div => mir::BinaryOperator::Div,
ast::BinaryOperator::Mod => mir::BinaryOperator::Mod,
ast::BinaryOperator::LT => mir::BinaryOperator::Cmp(mir::CmpOperator::LT),
ast::BinaryOperator::LE => mir::BinaryOperator::Cmp(mir::CmpOperator::LE),
ast::BinaryOperator::GT => mir::BinaryOperator::Cmp(mir::CmpOperator::GT),

View File

@ -20,8 +20,15 @@ use crate::{
self, implement::TypeCategory, CustomTypeKey, Metadata, NamedVariableRef, SourceModuleId,
StructField, StructType, TypeDefinition, TypeDefinitionKind, TypeKind, VagueLiteral,
},
util::try_all,
};
#[derive(thiserror::Error, Debug, Clone, PartialEq, PartialOrd)]
pub enum ErrorKind {
#[error("NULL error, should never occur!")]
Null,
}
/// Context that contains all of the given modules as complete codegenerated
/// LLIR that can then be finally compiled into LLVM IR.
#[derive(Debug)]
@ -39,16 +46,16 @@ impl<'ctx> CodegenContext<'ctx> {
impl mir::Context {
/// Compile MIR [`Context`] into [`CodegenContext`] containing LLIR.
pub fn codegen<'ctx>(&self, context: &'ctx Context) -> CodegenContext<'ctx> {
pub fn codegen<'ctx>(&self, context: &'ctx Context) -> Result<CodegenContext<'ctx>, ErrorKind> {
let mut modules = HashMap::new();
let mut modules_sorted = self.modules.iter().map(|(_, m)| m).collect::<Vec<_>>();
modules_sorted.sort_by(|m1, m2| m2.module_id.cmp(&m1.module_id));
for module in &modules_sorted {
let codegen = module.codegen(context, modules.clone());
let codegen = module.codegen(context, modules.clone())?;
modules.insert(module.module_id, codegen);
}
CodegenContext { context }
Ok(CodegenContext { context })
}
}
@ -194,7 +201,7 @@ impl mir::Module {
&'ctx self,
context: &'ctx Context,
modules: HashMap<SourceModuleId, ModuleCodegen<'ctx>>,
) -> ModuleCodegen<'ctx> {
) -> Result<ModuleCodegen<'ctx>, ErrorKind> {
let mut module = context.module(&self.name, self.is_main);
let tokens = &self.tokens;
@ -430,7 +437,7 @@ impl mir::Module {
};
let state = State::default();
if let Some(ret) = block.codegen(&mut scope, &state) {
if let Some(ret) = block.codegen(&mut scope, &state)? {
scope.block.terminate(Term::Ret(ret.instr())).unwrap();
} else {
if !scope.block.delete_if_unused().unwrap() {
@ -451,7 +458,7 @@ impl mir::Module {
}
}
ModuleCodegen { module }
Ok(ModuleCodegen { module })
}
}
@ -460,9 +467,9 @@ impl mir::Block {
&self,
mut scope: &mut Scope<'ctx, 'a>,
state: &State,
) -> Option<StackValue> {
) -> Result<Option<StackValue>, ErrorKind> {
for stmt in &self.statements {
stmt.codegen(&mut scope, state).map(|s| {
stmt.codegen(&mut scope, state)?.map(|s| {
if let Some(debug) = &scope.debug {
let location = stmt.1.into_debug(scope.tokens, debug.scope).unwrap();
let loc_val = debug.info.location(&debug.scope, location);
@ -472,22 +479,30 @@ impl mir::Block {
}
if let Some((kind, expr)) = &self.return_expression {
let ret = expr.codegen(&mut scope, &mut state.load(true));
let ret = expr.codegen(&mut scope, &mut state.load(true))?;
match kind {
mir::ReturnKind::Hard => {
scope.block.terminate(Term::Ret(ret?.instr())).unwrap();
None
if let Some(ret) = ret {
scope.block.terminate(Term::Ret(ret.instr())).unwrap();
} else {
scope.block.terminate(Term::RetVoid).unwrap();
}
Ok(None)
}
mir::ReturnKind::Soft => ret,
mir::ReturnKind::Soft => Ok(ret),
}
} else {
None
Ok(None)
}
}
}
impl mir::Statement {
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, state: &State) -> Option<StackValue> {
fn codegen<'ctx, 'a>(
&self,
scope: &mut Scope<'ctx, 'a>,
state: &State,
) -> Result<Option<StackValue>, ErrorKind> {
let location = scope.debug.clone().map(|d| {
let location = self.1.into_debug(scope.tokens, d.scope).unwrap();
d.info.location(&d.scope, location)
@ -495,7 +510,7 @@ impl mir::Statement {
match &self.0 {
mir::StmtKind::Let(NamedVariableRef(ty, name, _), mutable, expression) => {
let value = expression.codegen(scope, &state).unwrap();
let value = expression.codegen(scope, &state)?.unwrap();
let alloca = scope
.block
@ -545,14 +560,17 @@ impl mir::Statement {
},
);
}
None
Ok(None)
}
mir::StmtKind::Set(lhs, rhs) => {
let lhs_value = lhs
.codegen(scope, &state.load(false))
.codegen(scope, &state.load(false))?
.expect("non-returning LHS snuck into codegen!");
let rhs_value = rhs.codegen(scope, state)?;
let Some(rhs_value) = rhs_value else {
return Ok(None);
};
let backing_var = if let Some(var) = lhs.backing_var() {
&format!("store.{}", var.1)
@ -576,7 +594,7 @@ impl mir::Statement {
}
};
None
Ok(None)
}
mir::StmtKind::Import(_) => todo!(),
mir::StmtKind::Expression(expression) => expression.codegen(scope, state),
@ -585,7 +603,11 @@ impl mir::Statement {
}
impl mir::Expression {
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, state: &State) -> Option<StackValue> {
fn codegen<'ctx, 'a>(
&self,
scope: &mut Scope<'ctx, 'a>,
state: &State,
) -> Result<Option<StackValue>, ErrorKind> {
let location = if let Some(debug) = &scope.debug {
Some(debug.info.location(
&debug.scope,
@ -634,11 +656,11 @@ impl mir::Expression {
)),
mir::ExprKind::BinOp(binop, lhs_exp, rhs_exp) => {
let lhs = lhs_exp
.codegen(scope, state)
.codegen(scope, state)?
.expect("lhs has no return value")
.instr();
let rhs = rhs_exp
.codegen(scope, state)
.codegen(scope, state)?
.expect("rhs has no return value")
.instr();
let lhs_type = lhs_exp
@ -659,9 +681,57 @@ impl mir::Expression {
(mir::BinaryOperator::And, _, _) => Instr::And(lhs, rhs),
(mir::BinaryOperator::Cmp(i), _, false) => Instr::ICmp(i.predicate(), lhs, rhs),
(mir::BinaryOperator::Cmp(i), _, true) => Instr::FCmp(i.predicate(), lhs, rhs),
(mir::BinaryOperator::Div, false, false) => Instr::UDiv(lhs, rhs),
(mir::BinaryOperator::Div, true, false) => Instr::SDiv(lhs, rhs),
(mir::BinaryOperator::Div, _, true) => Instr::FDiv(lhs, rhs),
(mir::BinaryOperator::Mod, false, false) => {
let div = scope
.block
.build(Instr::UDiv(lhs, rhs))
.unwrap()
.maybe_location(&mut scope.block, location);
let mul = scope
.block
.build(Instr::Mul(rhs, div))
.unwrap()
.maybe_location(&mut scope.block, location);
Instr::Sub(lhs, mul)
}
(mir::BinaryOperator::Mod, true, false) => {
let div = scope
.block
.build(Instr::SDiv(lhs, rhs))
.unwrap()
.maybe_location(&mut scope.block, location);
let mul = scope
.block
.build(Instr::Mul(rhs, div))
.unwrap()
.maybe_location(&mut scope.block, location);
Instr::Sub(lhs, mul)
}
(mir::BinaryOperator::Mod, _, true) => {
let div = scope
.block
.build(Instr::FDiv(lhs, rhs))
.unwrap()
.maybe_location(&mut scope.block, location);
let mul = scope
.block
.build(Instr::Mul(rhs, div))
.unwrap()
.maybe_location(&mut scope.block, location);
Instr::Sub(lhs, mul)
}
};
Some(StackValue(
StackValueKind::Immutable(scope.block.build(instr).unwrap()),
StackValueKind::Immutable(
scope
.block
.build(instr)
.unwrap()
.maybe_location(&mut scope.block, location),
),
lhs_type,
))
}
@ -673,11 +743,17 @@ impl mir::Expression {
let ret_type = ret_type_kind.get_type(scope.type_values, scope.types);
let params = call
.parameters
.iter()
.map(|e| e.codegen(scope, state).unwrap())
.collect::<Vec<_>>();
let params = try_all(
call.parameters
.iter()
.map(|e| e.codegen(scope, state))
.collect::<Vec<_>>(),
)
.map_err(|e| e.first().cloned().unwrap())?
.into_iter()
.map(|v| v.unwrap())
.collect::<Vec<_>>();
let param_instrs = params.iter().map(|e| e.instr()).collect();
let callee = scope
.functions
@ -734,13 +810,13 @@ impl mir::Expression {
None
}
}
mir::ExprKind::If(if_expression) => if_expression.codegen(scope, state),
mir::ExprKind::If(if_expression) => if_expression.codegen(scope, state)?,
mir::ExprKind::Block(block) => {
let inner = scope.function.ir.block("inner");
scope.block.terminate(Term::Br(inner.value())).unwrap();
let mut inner_scope = scope.with_block(inner);
let ret = if let Some(ret) = block.codegen(&mut inner_scope, state) {
let ret = if let Some(ret) = block.codegen(&mut inner_scope, state)? {
Some(ret)
} else {
None
@ -752,18 +828,13 @@ impl mir::Expression {
}
mir::ExprKind::Indexed(expression, val_t, idx_expr) => {
let StackValue(kind, ty) = expression
.codegen(scope, &state.load(false))
.codegen(scope, &state.load(false))?
.expect("array returned none!");
let idx = idx_expr
.codegen(scope, &state.load(true))
.codegen(scope, &state.load(true))?
.expect("index returned none!")
.instr();
let first = scope
.block
.build_named("array.zero", Instr::Constant(ConstValue::U32(0)))
.unwrap();
let TypeKind::CodegenPtr(inner) = ty else {
panic!();
};
@ -772,7 +843,7 @@ impl mir::Expression {
let loaded = scope
.block
.build_named(
"array.load",
"load",
Instr::Load(
kind.instr(),
inner.get_type(scope.type_values, scope.types),
@ -782,15 +853,36 @@ impl mir::Expression {
(
scope
.block
.build_named(format!("array.gep"), Instr::GetElemPtr(loaded, vec![idx]))
.build_named(format!("gep"), Instr::GetElemPtr(loaded, vec![idx]))
.unwrap()
.maybe_location(&mut scope.block, location),
*further_inner,
)
} else if let TypeKind::CodegenPtr(further_inner) = *inner.clone() {
let TypeKind::Array(_, _) = *further_inner else {
panic!();
};
(
scope
.block
.build_named(
format!("array.gep"),
Instr::GetElemPtr(kind.instr(), vec![idx]),
)
.unwrap()
.maybe_location(&mut scope.block, location),
val_t.clone(),
)
} else {
let TypeKind::Array(_, _) = *inner else {
panic!();
};
let first = scope
.block
.build_named("array.zero", Instr::Constant(ConstValue::U32(0)))
.unwrap();
(
scope
.block
@ -829,10 +921,17 @@ impl mir::Expression {
}
}
mir::ExprKind::Array(expressions) => {
let stack_value_list = expressions
.iter()
.map(|e| e.codegen(scope, state).unwrap())
.collect::<Vec<_>>();
let stack_value_list: Vec<_> = try_all(
expressions
.iter()
.map(|e| e.codegen(scope, state))
.collect::<Vec<_>>(),
)
.map_err(|e| e.first().cloned().unwrap())?
.into_iter()
.map(|v| v.unwrap())
.collect();
let instr_list = stack_value_list
.iter()
.map(|s| s.instr())
@ -896,11 +995,12 @@ impl mir::Expression {
))
}
mir::ExprKind::Accessed(expression, type_kind, field) => {
let struct_val = expression.codegen(scope, &state.load(false)).unwrap();
let struct_val = expression.codegen(scope, &state.load(false))?.unwrap();
let TypeKind::CodegenPtr(inner) = &struct_val.1 else {
panic!("tried accessing non-pointer");
};
dbg!(&inner);
let TypeKind::CustomType(key) = *inner.clone() else {
panic!("tried accessing non-custom-type");
};
@ -948,7 +1048,12 @@ impl mir::Expression {
}
mir::ExprKind::Struct(name, items) => {
let type_key = CustomTypeKey(name.clone(), scope.module_id);
let struct_ty = Type::CustomType(*scope.type_values.get(&type_key)?);
let struct_ty = Type::CustomType({
let Some(a) = scope.type_values.get(&type_key) else {
return Ok(None);
};
*a
});
let load_n = format!("{}.load", name);
@ -967,7 +1072,7 @@ impl mir::Expression {
.build_named(gep_n, Instr::GetStructElemPtr(struct_ptr, i as u32))
.unwrap()
.maybe_location(&mut scope.block, location);
if let Some(val) = exp.codegen(scope, state) {
if let Some(val) = exp.codegen(scope, state)? {
scope
.block
.build_named(store_n, Instr::Store(elem_ptr, val.instr()))
@ -992,9 +1097,14 @@ impl mir::Expression {
.stack_values
.get(&varref.1)
.expect("Variable reference not found?!");
let TypeKind::CodegenPtr(ptr_inner) = &v.1 else {
panic!();
};
Some(StackValue(
StackValueKind::mutable(*mutable, v.0.instr()),
v.1.clone(),
TypeKind::Borrow(Box::new(*ptr_inner.clone()), *mutable),
))
}
mir::ExprKind::Deref(varref) => {
@ -1053,6 +1163,7 @@ impl mir::Expression {
}
mir::ExprKind::CastTo(expression, type_kind) => {
let val = expression.codegen(scope, state)?;
let Some(val) = val else { return Ok(None) };
if val.1 == *type_kind {
Some(val)
@ -1113,13 +1224,17 @@ impl mir::Expression {
if let Some(value) = &value {
value.instr().maybe_location(&mut scope.block, location);
}
value
Ok(value)
}
}
impl mir::IfExpression {
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, state: &State) -> Option<StackValue> {
let condition = self.0.codegen(scope, state).unwrap();
fn codegen<'ctx, 'a>(
&self,
scope: &mut Scope<'ctx, 'a>,
state: &State,
) -> Result<Option<StackValue>, ErrorKind> {
let condition = self.0.codegen(scope, state)?.unwrap();
// Create blocks
let mut then_b = scope.function.ir.block("then");
@ -1151,7 +1266,7 @@ impl mir::IfExpression {
// Generate then-block content
let mut then_scope = scope.with_block(then_b);
let then_res = self.1.codegen(&mut then_scope, state);
let then_res = self.1.codegen(&mut then_scope, state)?;
then_scope.block.terminate(Term::Br(after_bb)).ok();
let else_res = if let Some(else_expr) = self.2.as_ref() {
@ -1162,7 +1277,7 @@ impl mir::IfExpression {
.terminate(Term::CondBr(condition.instr(), then_bb, else_bb))
.unwrap();
let opt = else_expr.codegen(&mut else_scope, state);
let opt = else_expr.codegen(&mut else_scope, state)?;
else_scope.block.terminate(Term::Br(after_bb)).ok();
@ -1184,7 +1299,7 @@ impl mir::IfExpression {
scope.swap_block(after_b);
if then_res.is_none() && else_res.is_none() {
None
Ok(None)
} else {
let mut incoming = Vec::from(then_res.as_slice());
incoming.extend(else_res.clone());
@ -1212,7 +1327,7 @@ impl mir::IfExpression {
(Mutable(_), Mutable(_)) => StackValue(Mutable(instr), lhs_val.1),
},
};
Some(value)
Ok(Some(value))
}
}
}

View File

@ -4,6 +4,7 @@ use std::{
};
use crate::{
codegen,
lexer::{self, Cursor, FullToken, Position},
mir::{self, pass, Metadata, SourceModuleId},
token_stream::{self, TokenRange},
@ -30,6 +31,8 @@ pub enum ErrorKind {
TypeInferenceError(#[source] mir::pass::Error<mir::typecheck::ErrorKind>),
#[error("{}{}", label("(Linker) "), .0.kind)]
LinkerError(#[from] mir::pass::Error<mir::linker::ErrorKind>),
#[error("{}{}", label("(Codegen) "), .0)]
CodegenError(#[from] codegen::ErrorKind),
}
impl ErrorKind {
@ -50,6 +53,9 @@ impl ErrorKind {
ErrorKind::TypeCheckError(error) => error.metadata,
ErrorKind::TypeInferenceError(error) => error.metadata,
ErrorKind::LinkerError(error) => error.metadata,
ErrorKind::CodegenError(error) => match error {
codegen::ErrorKind::Null => Default::default(),
},
}
}
}
@ -155,7 +161,7 @@ impl ReidError {
})
}
pub fn from_kind<U>(errors: Vec<ErrorKind>, map: ErrorModules) -> ReidError {
pub fn from_kind(errors: Vec<ErrorKind>, map: ErrorModules) -> ReidError {
ReidError { map, errors }
}
}

80
reid/src/ld.rs Normal file
View File

@ -0,0 +1,80 @@
use std::{path::PathBuf, process::Command, thread, time::Duration};
pub struct LDRunner {
command: String,
dynamic_linker: String,
libraries: Vec<String>,
}
impl LDRunner {
pub fn from_command(command: &str) -> LDRunner {
LDRunner {
command: command.to_owned(),
dynamic_linker: "ld-linux-x86-64.so.2".to_string(),
libraries: Default::default(),
}
}
pub fn with_library(mut self, lib: &str) -> LDRunner {
self.libraries.push(lib.to_owned());
self
}
pub fn invoke(&self, input: &PathBuf, out_path: &PathBuf) {
let input_path = input.canonicalize().unwrap();
let dyn_linker_path = find_objectfile(&self.dynamic_linker);
let crt1_path = find_objectfile("crt1.o");
println!("LDRunner: Using dynamic linker at: {:?}", dyn_linker_path);
let mut ld = Command::new(&self.command);
ld.arg("-dynamic-linker")
.arg(dyn_linker_path)
.arg(crt1_path);
for library in &self.libraries {
ld.arg(format!("-l{}", library));
}
ld.arg(input_path.to_str().unwrap())
.arg("-o")
.arg(out_path.to_str().unwrap());
println!(
"LDRunner: Executing linker to objfile at {:?} => {:?}",
input_path, out_path
);
dbg!(&ld);
ld.spawn().expect("Unable to execute ld!");
thread::sleep(Duration::from_millis(100));
println!("Setting executable bit to {:?}..", out_path);
Command::new("chmod")
.arg("+x")
.arg(out_path)
.spawn()
.unwrap();
thread::sleep(Duration::from_millis(100));
}
}
fn find_objectfile(name: &str) -> String {
let whereis = Command::new("whereis")
.arg(&name)
.output()
.expect("Unable to execute whereis");
let whereis_output = String::from_utf8(whereis.stdout).unwrap();
whereis_output
.split(" ")
.skip(1)
.next()
.expect(&format!("Unable to find {}: {}", name, whereis_output))
.split("\n")
.next()
.unwrap()
.to_owned()
}

View File

@ -2,7 +2,7 @@ use std::{fmt::Debug, str::Chars};
static DECIMAL_NUMERICS: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
#[derive(Debug, Eq, PartialEq, Clone, PartialOrd, Ord)]
#[derive(Eq, PartialEq, Clone, PartialOrd, Ord)]
pub enum Token {
/// Values
Identifier(String),
@ -56,6 +56,10 @@ pub enum Token {
Star,
/// `-`
Minus,
/// `/`
Slash,
/// `%`
Percent,
/// `>`
GreaterThan,
@ -83,6 +87,8 @@ pub enum Token {
/// `.`
Dot,
Unknown(char),
Eof,
}
@ -149,6 +155,18 @@ impl ToString for Token {
Token::Comma => String::from(','),
Token::Dot => String::from('.'),
Token::Eof => String::new(),
Token::Slash => String::from('/'),
Token::Percent => String::from('%'),
Token::Unknown(val) => val.to_string(),
}
}
}
impl std::fmt::Debug for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Unknown(value) => write!(f, "Unknown(\'{}\')", value.to_string()),
_ => write!(f, "{}", self.to_string()),
}
}
}
@ -163,7 +181,7 @@ pub struct FullToken {
impl Debug for FullToken {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"{:?} (Ln {}, Col {})",
"Token({:?}) (Ln {}, Col {})",
self.token, self.position.1, self.position.0
))
}
@ -343,8 +361,10 @@ pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error
'}' => Token::BraceClose,
',' => Token::Comma,
'.' => Token::Dot,
// Invalid token
_ => Err(Error::InvalidToken(*character, cursor.position))?,
'/' => Token::Slash,
'%' => Token::Percent,
// Unknown token
value => Token::Unknown(*value),
};
tokens.push(FullToken {

View File

@ -54,7 +54,8 @@ use crate::{ast::TopLevelStatement, lexer::Token, token_stream::TokenStream};
mod ast;
mod codegen;
mod error_raporting;
pub mod error_raporting;
pub mod ld;
mod lexer;
pub mod mir;
mod pad_adapter;
@ -137,7 +138,7 @@ pub fn perform_all_passes<'map>(
dbg!(&state);
if !state.errors.is_empty() {
return Err(ReidError::from_kind::<()>(
return Err(ReidError::from_kind(
state.errors.iter().map(|e| e.clone().into()).collect(),
module_map.clone(),
));
@ -157,7 +158,7 @@ pub fn perform_all_passes<'map>(
dbg!(&state);
if !state.errors.is_empty() {
return Err(ReidError::from_kind::<()>(
return Err(ReidError::from_kind(
state
.errors
.iter()
@ -177,7 +178,7 @@ pub fn perform_all_passes<'map>(
dbg!(&state);
if !state.errors.is_empty() {
return Err(ReidError::from_kind::<()>(
return Err(ReidError::from_kind(
state
.errors
.iter()
@ -214,7 +215,10 @@ pub fn compile_and_pass<'map>(
println!("{}", &mir_context);
let mut context = Context::new(format!("Reid ({})", env!("CARGO_PKG_VERSION")));
let codegen_modules = mir_context.codegen(&mut context);
let codegen_modules = match mir_context.codegen(&mut context) {
Ok(modules) => modules,
Err(e) => Err(ReidError::from_kind(vec![e.into()], module_map.clone()))?,
};
#[cfg(debug_assertions)]
println!("{}", &codegen_modules.context);

View File

@ -32,7 +32,7 @@ impl Display for Context {
impl Display for Module {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Module({}) {{", self.name)?;
writeln!(f, "Module({}) ({}) {{", self.name, self.module_id)?;
let mut state = Default::default();
let mut inner_f = PadAdapter::wrap(f, &mut state);
@ -299,6 +299,8 @@ impl Display for BinaryOperator {
BinaryOperator::Mult => write!(f, "*"),
BinaryOperator::And => write!(f, "&&"),
BinaryOperator::Cmp(op) => Display::fmt(op, f),
BinaryOperator::Div => write!(f, "/"),
BinaryOperator::Mod => write!(f, "%"),
}
}
}
@ -324,7 +326,7 @@ impl Display for Metadata {
impl Display for SourceModuleId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Mod {}", self.0)
write!(f, "{}", self.0)
}
}
@ -362,7 +364,7 @@ impl Display for TypeKind {
Display::fmt(len, f)?;
f.write_char(']')
}
TypeKind::CustomType(CustomTypeKey(name, mod_id)) => write!(f, "{}:{}", mod_id, name),
TypeKind::CustomType(CustomTypeKey(name, mod_id)) => write!(f, "{}@{}", name, mod_id),
TypeKind::Borrow(type_kind, false) => {
write!(f, "&")?;
Display::fmt(type_kind, f)

View File

@ -104,14 +104,20 @@ impl TypeKind {
BinaryOperator::Mult => self.clone(),
BinaryOperator::And => TypeKind::Bool,
BinaryOperator::Cmp(_) => TypeKind::Bool,
BinaryOperator::Div => self.clone(),
BinaryOperator::Mod => self.clone(),
}
}
/// Reverse of binop_type, where the given hint is the known required output
/// type of the binop, and the output is the hint for the lhs/rhs type.
pub fn binop_hint(&self, op: &BinaryOperator) -> Option<TypeKind> {
match op {
BinaryOperator::Add | BinaryOperator::Minus | BinaryOperator::Mult => {
Some(self.clone())
}
BinaryOperator::Add
| BinaryOperator::Minus
| BinaryOperator::Mult
| BinaryOperator::Div
| BinaryOperator::Mod => Some(self.clone()),
BinaryOperator::And => None,
BinaryOperator::Cmp(_) => None,
}
@ -465,12 +471,16 @@ impl Expression {
}
}
pub fn is_zero(&self) -> Option<bool> {
Some(self.num_value()? == 0)
pub fn is_zero(&self) -> Result<Option<bool>, ErrorKind> {
if let Some(val) = self.num_value()? {
Ok(Some(val == 0))
} else {
Ok(None)
}
}
pub fn num_value(&self) -> Option<i128> {
match &self.0 {
pub fn num_value(&self) -> Result<Option<i128>, ErrorKind> {
Ok(match &self.0 {
ExprKind::Variable(_) => None,
ExprKind::Indexed(..) => None,
ExprKind::Accessed(..) => None,
@ -478,22 +488,46 @@ impl Expression {
ExprKind::Struct(..) => None,
ExprKind::Literal(literal) => literal.num_value(),
ExprKind::BinOp(op, lhs, rhs) => match op {
BinaryOperator::Add => Some(lhs.num_value()? + rhs.num_value()?),
BinaryOperator::Minus => Some(lhs.num_value()? - rhs.num_value()?),
BinaryOperator::Mult => Some(lhs.num_value()? * rhs.num_value()?),
BinaryOperator::Add => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a + b),
BinaryOperator::Minus => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a - b),
BinaryOperator::Mult => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a * b),
BinaryOperator::And => None,
BinaryOperator::Cmp(_) => None,
BinaryOperator::Div => {
let rhs_value = rhs.num_value()?;
if rhs_value == Some(0) {
Err(ErrorKind::DivideZero)?
}
maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a / b)
}
BinaryOperator::Mod => {
let rhs_value = rhs.num_value()?;
if rhs_value == Some(0) {
Err(ErrorKind::DivideZero)?
}
maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a % b)
}
},
ExprKind::FunctionCall(..) => None,
ExprKind::If(_) => None,
ExprKind::Block(_) => None,
ExprKind::Borrow(_, _) => None,
ExprKind::Deref(_) => None,
ExprKind::CastTo(expression, _) => expression.num_value(),
}
ExprKind::CastTo(expression, _) => expression.num_value()?,
})
}
}
fn maybe<T>(lhs: Option<i128>, rhs: Option<i128>, fun: T) -> Option<i128>
where
T: FnOnce(i128, i128) -> i128,
{
if let (Some(lhs), Some(rhs)) = (lhs, rhs) {
Some(fun(lhs, rhs))
} else {
None
}
}
impl IfExpression {
pub fn return_type(
&self,

View File

@ -103,6 +103,8 @@ impl<'map> Pass for LinkerPass<'map> {
let mut modules_to_process: Vec<Rc<RefCell<_>>> = modules.values().cloned().collect();
let mut already_imported_types = HashSet::<CustomTypeKey>::new();
while let Some(module) = modules_to_process.pop() {
let mut importer_module = module.borrow_mut();
@ -232,28 +234,30 @@ impl<'map> Pass for LinkerPass<'map> {
}
}
fn import_type(_: &String, ty: &TypeKind) -> (TypeKind, Vec<CustomTypeKey>) {
fn import_type(ty: &TypeKind) -> Vec<CustomTypeKey> {
let mut imported_types = Vec::new();
let ty = match &ty {
TypeKind::CustomType(key) => {
imported_types.push(key.clone());
TypeKind::CustomType(key.clone())
}
_ => ty.clone(),
match &ty {
TypeKind::CustomType(key) => imported_types.push(key.clone()),
TypeKind::Borrow(ty, _) => imported_types.extend(import_type(ty)),
TypeKind::Array(ty, _) => imported_types.extend(import_type(ty)),
TypeKind::UserPtr(ty) => imported_types.extend(import_type(ty)),
TypeKind::CodegenPtr(ty) => imported_types.extend(import_type(ty)),
_ => {}
};
(ty, imported_types)
imported_types
}
let mut imported_types = Vec::new();
let (return_type, types) = import_type(&imported_mod_name, &func.return_type);
let types = import_type(&func.return_type);
let return_type = func.return_type.clone();
imported_types.extend(types);
let mut param_tys = Vec::new();
for (param_name, param_ty) in &func.parameters {
let (param_type, types) = import_type(&imported_mod_name, &param_ty);
let types = import_type(&param_ty);
imported_types.extend(types);
param_tys.push((param_name.clone(), param_type));
param_tys.push((param_name.clone(), param_ty.clone()));
}
fn find_inner_types(
@ -303,6 +307,14 @@ impl<'map> Pass for LinkerPass<'map> {
seen.extend(inner);
}
// TODO: Unable to import same-named type from multiple places..
let seen = seen
.difference(&already_imported_types)
.cloned()
.collect::<HashSet<_>>();
already_imported_types.extend(seen.clone());
for typekey in seen.into_iter() {
let typedef = imported_mod_typedefs
.iter()
@ -317,9 +329,10 @@ impl<'map> Pass for LinkerPass<'map> {
name: func_name,
is_pub: false,
is_imported: false,
return_type: return_type,
return_type,
parameters: param_tys,
kind: super::FunctionDefinitionKind::Extern(true),
source: imported_mod_id,
});
}
}

View File

@ -218,6 +218,8 @@ pub enum BinaryOperator {
Add,
Minus,
Mult,
Div,
Mod,
And,
Cmp(CmpOperator),
}
@ -291,6 +293,7 @@ pub struct FunctionDefinition {
pub return_type: TypeKind,
pub parameters: Vec<(String, TypeKind)>,
pub kind: FunctionDefinitionKind,
pub source: SourceModuleId,
}
#[derive(Debug)]

View File

@ -295,7 +295,7 @@ impl Module {
scope
.types
.set(
CustomTypeKey(typedef.name.clone(), self.module_id),
CustomTypeKey(typedef.name.clone(), typedef.source_module),
typedef.clone(),
)
.ok();

View File

@ -68,6 +68,8 @@ pub enum ErrorKind {
NegativeUnsignedValue(TypeKind),
#[error("Cannot cast type {0} into type {1}!")]
NotCastableTo(TypeKind, TypeKind),
#[error("Cannot divide by zero")]
DivideZero,
}
/// Struct used to implement a type-checking pass that can be performed on the
@ -426,7 +428,7 @@ impl Expression {
let both_t = lhs_type.collapse_into(&rhs_type)?;
if *op == BinaryOperator::Minus && !lhs_type.signed() {
if let (Some(lhs_val), Some(rhs_val)) = (lhs.num_value(), rhs.num_value()) {
if let (Some(lhs_val), Some(rhs_val)) = (lhs.num_value()?, rhs.num_value()?) {
if lhs_val < rhs_val {
return Err(ErrorKind::NegativeUnsignedValue(lhs_type));
}

View File

@ -235,7 +235,7 @@ impl std::iter::Sum for TokenRange {
#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Error {
#[error("Expected {} got \"{}\"", .0, .1.to_string())]
#[error("Expected {} got \"{:?}\"", .0, .1)]
Expected(String, Token, TokenRange),
#[error("Source file contains no tokens")]
FileEmpty,

View File

@ -1,79 +1,122 @@
use std::{
alloc::System,
path::PathBuf,
process::Command,
thread,
time::{Duration, SystemTime},
};
use reid::{
compile_module,
ld::LDRunner,
mir::{self},
parse_module, perform_all_passes,
};
use reid_lib::Context;
use util::assert_err;
mod util;
fn test(source: &str, name: &str) {
let mut map = Default::default();
let (id, tokens) = assert_err(parse_module(source, name, &mut map));
let module = assert_err(compile_module(id, tokens, &mut map, None, true));
fn test(source: &str, name: &str, expected_exit_code: i32) {
assert_err(assert_err(std::panic::catch_unwind(|| {
let mut map = Default::default();
let (id, tokens) = assert_err(parse_module(source, name, &mut map));
assert_err(perform_all_passes(
&mut mir::Context::from(vec![module], Default::default()),
&mut map,
));
}
let module = assert_err(compile_module(id, tokens, &mut map, None, true));
let mut mir_context = mir::Context::from(vec![module], Default::default());
assert_err(perform_all_passes(&mut mir_context, &mut map));
pub static ARRAY: &str = include_str!("../../reid_src/array.reid");
pub static FIBONACCI: &str = include_str!("../../reid_src/fibonacci.reid");
pub static HELLO_WORLD: &str = include_str!("../../reid_src/hello_world.reid");
pub static MUTABLE: &str = include_str!("../../reid_src/mutable.reid");
pub static STRINGS: &str = include_str!("../../reid_src/strings.reid");
pub static STRUCTS: &str = include_str!("../../reid_src/struct.reid");
pub static ARRAY_STRUCTS: &str = include_str!("../../reid_src/array_structs.reid");
pub static BORROW: &str = include_str!("../../reid_src/borrow.reid");
pub static ARITHMETIC: &str = include_str!("../../reid_src/arithmetic.reid");
let context = Context::new(format!("Reid ({})", env!("CARGO_PKG_VERSION")));
#[test]
fn array_compiles_well() {
test(ARRAY, "array");
let codegen = assert_err(mir_context.codegen(&context));
let output = codegen.compile().output();
let time = SystemTime::now();
let in_path = PathBuf::from(format!(
"/tmp/temp-{}.o",
time.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_nanos()
));
std::fs::write(&in_path, &output.obj_buffer).expect("Could not write OBJ-file!");
let out_path = in_path.with_extension("out");
LDRunner::from_command("ld")
.with_library("c")
.invoke(&in_path, &out_path);
std::fs::remove_file(in_path).unwrap();
let executed = Command::new(&out_path).output();
std::fs::remove_file(out_path).unwrap();
assert_eq!(expected_exit_code, executed.unwrap().status.code().unwrap());
Ok::<(), ()>(())
})))
}
#[test]
fn fibonacci_compiles_well() {
test(FIBONACCI, "fibonacci");
fn arithmetic_compiles_well() {
test(include_str!("../../examples/arithmetic.reid"), "test", 48);
}
#[test]
fn hello_world_compiles_well() {
test(HELLO_WORLD, "hello_world");
}
#[test]
fn mutable_compiles_well() {
test(MUTABLE, "mutable");
}
#[test]
fn strings_compiles_well() {
test(STRINGS, "strings");
}
#[test]
fn arrays_compiles_well() {
test(ARRAY, "array");
}
#[test]
fn struct_compiles_well() {
test(STRUCTS, "struct");
}
#[test]
fn array_structs_compiles_well() {
test(ARRAY_STRUCTS, "array_structs");
test(include_str!("../../examples/array_structs.reid"), "test", 5);
}
#[test]
fn borrow_structs_compiles_well() {
test(BORROW, "borrow");
fn array_compiles_well() {
test(include_str!("../../examples/array.reid"), "test", 3);
}
#[test]
fn arithmetic_structs_compiles_well() {
test(ARITHMETIC, "arithmetic");
fn borrow_compiles_well() {
test(include_str!("../../examples/borrow.reid"), "test", 17);
}
#[test]
fn borrow_hard_compiles_well() {
test(include_str!("../../examples/borrow_hard.reid"), "test", 17);
}
#[test]
fn cast_compiles_well() {
test(include_str!("../../examples/cast.reid"), "test", 6);
}
#[test]
fn char_compiles_well() {
test(include_str!("../../examples/char.reid"), "test", 98);
}
#[test]
fn div_mod_compiles_well() {
test(include_str!("../../examples/div_mod.reid"), "test", 12);
}
#[test]
fn fibonacci_compiles_well() {
test(include_str!("../../examples/fibonacci.reid"), "test", 1);
}
#[test]
fn float_compiles_well() {
test(include_str!("../../examples/float.reid"), "test", 1);
}
#[test]
fn hello_world_compiles_well() {
test(include_str!("../../examples/hello_world.reid"), "test", 0);
}
#[test]
fn mutable_compiles_well() {
test(include_str!("../../examples/mutable.reid"), "test", 21);
}
#[test]
fn ptr_compiles_well() {
test(include_str!("../../examples/ptr.reid"), "test", 5);
}
#[test]
fn std_test_compiles_well() {
test(include_str!("../../examples/std_test.reid"), "test", 3);
}
#[test]
fn strings_compiles_well() {
test(include_str!("../../examples/strings.reid"), "test", 5);
}
#[test]
fn struct_compiles_well() {
test(include_str!("../../examples/struct.reid"), "test", 17);
}