Compare commits
17 Commits
691c91504b
...
c699b67d75
Author | SHA1 | Date | |
---|---|---|---|
c699b67d75 | |||
ccb5741666 | |||
9fcf19383c | |||
59ecaa0d92 | |||
bb69ce4968 | |||
1a65b4085f | |||
c622d59c93 | |||
fe4e41c435 | |||
8be6ce1549 | |||
4de346e3c0 | |||
efeefe0bfe | |||
bd356f11db | |||
8b79959288 | |||
82758ae333 | |||
b723ff2d06 | |||
3a68154ae5 | |||
b9459a19bb |
@ -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
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -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
6
examples/div_mod.reid
Normal file
@ -0,0 +1,6 @@
|
||||
// Arithmetic, function calls and imports!
|
||||
|
||||
fn main() -> u16 {
|
||||
|
||||
return (50 / 5 + 52 % 5);
|
||||
}
|
@ -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
3
examples/ptr.reid
Normal file → Executable 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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
153
graphics.reid
153
graphics.reid
@ -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;
|
||||
}
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
@ -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,
|
||||
|
@ -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")?)?,
|
||||
})
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
80
reid/src/ld.rs
Normal 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()
|
||||
}
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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, ¶m_ty);
|
||||
let types = import_type(¶m_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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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)]
|
||||
|
@ -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();
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user