Compare commits

...

55 Commits

Author SHA1 Message Date
8d0e3d03d5 Improve syntax highlighting 2025-08-02 03:41:08 +03:00
34e31549b3 add some syntax highlighting 2025-08-02 03:09:21 +03:00
0ba25db4c8 Start adding syntax highlighting 2025-08-02 00:14:20 +03:00
314f44304a Update README.md 2025-08-01 23:59:05 +03:00
08f7725ce7 Compile cpu_raytracer example in e2e tests, but don't run it 2025-08-01 22:46:46 +03:00
f89b26bf74 Improve LSP hover typing 2025-08-01 22:41:46 +03:00
4fada0036c Fix debug info for structs 2025-07-31 23:25:46 +03:00
4f0ee72c83 Edit example a bit, fix macro generation in function parameters 2025-07-31 22:48:16 +03:00
deed96bbfd Fix bitwise operators requiring U64 for rhs 2025-07-31 22:17:58 +03:00
1e094eeea0 Allow wider expressions for when self is not taken as borrow 2025-07-29 23:55:31 +03:00
3adb745576 Fix struct recursion testing 2025-07-29 23:38:26 +03:00
8f7b785664 Fix two small bugs, add new example to test 2025-07-29 23:16:56 +03:00
c7aacfe756 Refactor code a little bit 2025-07-29 21:56:50 +03:00
b71c253942 Add types to hovers in LSP, fix around and add metas 2025-07-29 21:39:14 +03:00
7d3aaa143a Start adding type-information to tooltips 2025-07-29 20:44:15 +03:00
6619f1f0a9 Add simple error diagnostic from parser 2025-07-29 19:53:12 +03:00
bc59b6f575 Start adding LSP implementation 2025-07-29 17:48:45 +03:00
c262418f88 Add comments and whitespace to lexer 2025-07-29 16:41:07 +03:00
2dd3a5904b Add whitespace to lexer 2025-07-29 16:37:58 +03:00
ff1da716e9 Update README.md 2025-07-29 16:08:54 +03:00
7c6d634287 Update README.md 2025-07-29 16:04:26 +03:00
b0442e5685 Add documentation for include_bytes! 2025-07-29 16:02:54 +03:00
2303bf757a Rename macro to include_bytes!() 2025-07-29 15:57:26 +03:00
7234cad5f0 Allow &[ty; _] to be cast into *ty 2025-07-29 15:56:06 +03:00
baa068a371 Load file contents relative to module path instead of PWD 2025-07-29 15:44:14 +03:00
8b1d1084a6 Improve formatting for globals 2025-07-29 15:25:14 +03:00
f5f55079a9 Make global identifier for macro-generated globals better 2025-07-29 15:19:14 +03:00
baa7bafafc Add length-intrinsic 2025-07-29 01:15:09 +03:00
f700c577f1 Add reading file to buffer macro, only works for one global per file 2025-07-29 00:50:07 +03:00
ebe7fc8d75 Add GetGlobal "instruction" 2025-07-29 00:29:04 +03:00
140d963d9b Read file contents to binary within macro 2025-07-29 00:18:50 +03:00
480ba5155a Initialize global arrays in macros 2025-07-29 00:07:40 +03:00
2207c3df83 Add initial support for array globals 2025-07-29 00:06:47 +03:00
735c3231b1 Make macros generate globals 2025-07-28 23:49:22 +03:00
50a875ad21 Add compilation of global values 2025-07-28 23:32:47 +03:00
30257e1a2b Add global api support for llvm-lib 2025-07-28 23:20:13 +03:00
a7ac974f46 Change macro pass workflow a little bit 2025-07-28 22:57:06 +03:00
3d8f4bbd24 Implement macro-pass 2025-07-28 22:37:24 +03:00
33ed1fd813 Add macro call convention 2025-07-28 22:18:30 +03:00
67a5fcd002 Basically revert everything, create macro-pass 2025-07-28 22:11:46 +03:00
80bdf4eba8 Merge branch 'main' into macros 2025-07-28 22:03:06 +03:00
bd8994bb37 Fix linking associated functions, fix other examples 2025-07-28 22:02:49 +03:00
2e153922f1 Start adding macros 2025-07-28 21:54:51 +03:00
ea6458dddc Update documentation 2025-07-28 21:26:22 +03:00
014ba2f638 Update documentation 2025-07-28 21:23:51 +03:00
89850d7b4f Change intrinsic alloca to malloc and actually use libc malloc 2025-07-28 21:22:15 +03:00
13be3e9c02 Namespace all functions, except those that are explicitly extern 2025-07-28 21:13:53 +03:00
5026013df3 Add intrinsic malloc, use that in alloca 2025-07-28 20:46:58 +03:00
beaba4e7de Mangle function names, except for user defined externs 2025-07-28 20:25:36 +03:00
e14efa2ea7 Update allocator to remove dynamic allocations from lists/structs 2025-07-28 19:52:49 +03:00
ccee457cf4 Update documentation 2025-07-28 19:27:21 +03:00
3f81104c99 Add part of Ray Tracing in One Weekend as an example 2025-07-28 19:22:41 +03:00
b643c13582 Use metadata instead of names for allocator identification 2025-07-28 19:22:03 +03:00
e412a2e1d7 Fix allocator 2025-07-28 19:04:37 +03:00
1b1a5934f5 Implement lexical scopes 2025-07-28 18:40:42 +03:00
67 changed files with 4702 additions and 669 deletions

959
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
[workspace]
members = [
"reid",
"reid-llvm-lib"
]
"reid-llvm-lib",
"reid-lsp"
]

View File

@ -12,6 +12,9 @@ by simplicity.
Specifications and a bunch of [documentation for the language can be found
here](./documentation/).
An example of a real whole program (a CPU pathtracer) can be found [in
examples/cpu_raytracer.reid](./examples/cpu_raytracer.reid), go have a look!
Reid is currently able to (non-exhaustively):
- Do basic algebra binary and unary-operations (e.g. Add, Sub, Div, Mult, And,
Not)
@ -68,17 +71,17 @@ Currently missing big features (TODOs) are:
Big features that I want later but are not necessary:
- ~~User-defined binary operations~~ (DONE)
- ~~Asymmetric binary operations (e.g. string + u32)~~ (DONE)
- Error handling
- Lexing & parsing of whitespace and comments as well
- ~~Error handling~~ (Not Doing It)
- ~~Lexing & parsing of whitespace and comments as well~~ (DONE)
- LSP implementation
Smaller features:
- ~~Hex-numbers~~
- ~~Bitwise operations~~
- ~~Easier way to initialize arrays with a single value~~
- ~~Void-returns (`return;` for void-returning functions)~~
- ~~Only include standard library at all if it is imported~~
- Lexical scopes for Debug Information
- ~~Hex-numbers~~ (DONE)
- ~~Bitwise operations~~ (DONE)
- ~~Easier way to initialize arrays with a single value~~ (DONE)
- ~~Void-returns (`return;` for void-returning functions)~~ (DONE)
- ~~Only include standard library at all if it is imported~~ (DONE)
- ~~Lexical scopes for Debug Information~~ (DONE)
### Why "Reid"
@ -154,10 +157,6 @@ cmake llvm -B build -DCMAKE_BUILD_TYPE=MinSizeRel -DLLVM_ENABLE_ASSERTIONS=ON -D
ninja -j23
```
*Also Note:* Building LLVM with `Ninja` was not successful for me, but this
method was. Ninja may be successful with you, to try it, add `-G Ninja` to the
`cmake`-command, and instead of `make` run `ninja install`.
### Building this crate itself
Assuming `llvm-project` from the previous step was at
@ -167,6 +166,5 @@ Assuming `llvm-project` from the previous step was at
LLVM_SYS_201_PREFIX=/path/llvm-project/build cargo build
```
## In conclusion
Good luck! It took me a good 10 hours to figure this out for myself, I sure hope
these instructions help both myself and someone else in the future!
Alternatively assuming you have LLVM 20.1 or newer installed you may use omit
the environment variable entirely and use dynamic linking instead

View File

@ -27,7 +27,9 @@ in-depth, but when you're feeling up to it, you can read about them
## Syntax and general information
Syntax for Reid is very much inspired by rust, and examples of the language can
be found in the [examples](../examples/)-folder.
be found in the [examples](../examples/)-folder. A larger example of a whole
program written in Reid (a CPU Pathtracer) can be found [in
examples/cpu_raytracer.reid](../examples/cpu_raytracer.reid).
In Reid **modules** (or files) on the top-level are comprised of imports, type
definitions, binop-definitions, functions and type-associated function blocks.
@ -81,7 +83,11 @@ Common token used throughout this document to express parts of grammar include:
"i8" | "i16" | "i32" | "i64" | "i128" |
"f16" | "f32" | "f32b" | "f64" | "f80" | "f128" | "f128ppc"
<binop> :: "+" | "-" | "*" | "/" | "%" | "&&" | <cmp>
<binop> :: "+" | "-" | "*"
| "/" | "%" | "&&" | "||"
| "&" | "|" | "^" | ">>"
| "<<" | <cmp>
<cmp> :: "<" | "<=" | "==" | "!=" | ">=" | >"
<unary> :: "+" | "-" | "!"
```
@ -255,6 +261,9 @@ calls, literals, or if-expressions. Types of supported expressions include:
*associated type* with given parameters.
- **Accessing function calls**, a shorthand to call associated function calls
which have `&self` or `&mut self` as their first parameter.
- **Macro invocations** for invoking **macros** which are evaluated at
compile-time rather than runtime. Currently it is not possible to define
your own macros, but there are some pre-defined in the intrinsics.
- **Block-expressions**, which can return a value to the higher-level expression
if they have a statement with a soft-return. Otherwise they return void.
- **If-expressions**, which can execute one of two expressions depending on the
@ -272,7 +281,7 @@ In formal grammar:
<indexing> | <accessing> |
<binary-exp> | <unary-exp> |
<function-call> | <accessing-function-call> | <assoc-function-call>
<block> | <if-expr> | <cast> |
<macro-invocation> | <block> | <if-expr> | <cast> |
( "(" <expression> ")" )
<variable> :: <ident>
@ -288,6 +297,7 @@ In formal grammar:
<function-call> :: <expression> "(" [ <expression> ( "," <expression> )* ] ")"
<accessing-function-call> :: <accessing> "(" [ <expression> ( "," <expression> )* ] ")"
<assoc-function-call> :: <type> "::" <function-call>
<macro-invocation> :: <expression> "!(" [ <expression> ( "," <expression> )* ] ")"
<if-expr> :: "if" <expression> <expression> [ "else" <expression> ]
<cast> :: <expression> "as" <type>
```
@ -306,6 +316,7 @@ test.first // Accessing
func(value, 14) // Function call
Test::get_field(&test); // Associated function call
test.get_field(); // Same, but using a the dot-form shorthand
include_bytes!("./test"); // Macro invocation
if varname {} else {} // If-expression
value as u32 // cast
(value + 2) // Binop within parenthesis

View File

@ -7,14 +7,32 @@ pre-existing binary-operators, but also some regular functions and associated
functions (that every type has by-default). This document lists them all (except
for the binary operators, because there are hundreds of those).
### Global Intrinsics
#### `malloc(size: u64) -> *u8`
Allocates `size` bytes and returns a pointer of `u8` of length `size`.
```rust
i32::malloc(40); // Reserves 40 bytes
```
### Macro Intrinsics
#### `include_bytes!(path: *char) -> &[u8; _]`
Attempts to load file from `path` (relative to module) into memory and includes
it into the compiled binary directly. Returns a borrow to an array containing
bytes from the file. Array length varies depending on the file contents.
### Associated Intrinsics
#### `<T>::sizeof() -> u64`
Simply returns the size of type `T` in bits.
Simply returns the size of type `T` in bytes.
```rust
i32::sizeof(); // Returns 32
i32::sizeof(); // Returns 4
```
#### `<T>::null() -> *T`
@ -25,12 +43,15 @@ Returns a null-pointer of type `T`.
i32::null(); // Returns *i32 (null-ptr)
```
#### `<T>::alloca(size: u64) -> *T`
#### `<T>::malloc(size: u64) -> *T`
Allocates `T::sizeof() * size` bits and returns a pointer to `T`.
Allocates `T::sizeof() * size` bytes and returns a pointer to `T`.
**Note:** This does not seem to work correctly currently.
```rust
i32::alloca(30); // Returns *i32
i32::malloc(30); // Returns *i32
// Equivalent to
malloc(i32::sizeof() * 30) as *i32
```

View File

@ -1,6 +0,0 @@
pub fn main() -> u32 {
let b = 4;
let c = b + 4;
return c;
}

View File

@ -25,7 +25,7 @@ fn main() -> u32 {
print(from_str("sizeof i32: ") + i32::sizeof());
let nullptr = i32::null();
let mut list = u64::alloca(15);
let mut list = u64::malloc(15);
list[4] = 17;
print(from_str("value: ") + list[4]);

View File

@ -1,6 +0,0 @@
import std::String;
import std::print;
fn main() {
print(String::new() + "hello")
}

View File

@ -1,6 +1,5 @@
// Arithmetic, function calls and imports!
import std::allocate;
import std::print;
fn other() -> i16 {
@ -9,7 +8,7 @@ fn other() -> i16 {
fn main() -> u32 {
let mut v = (allocate(4) as *u32);
let mut v = (malloc(4) as *u32);
v[0] = other() as u32;
return v[0];

466
examples/cpu_raytracer.reid Normal file
View File

@ -0,0 +1,466 @@
// First half of Ray Tracing in One Weekend, rendered to a SDL3 window rather
// than an image file. Needs to be linked against SDL3, i.e.
// `./cli cpu_raytracer.reid SDL3`
import std::print;
import std::String;
///////////////////
/// SDL externs ///
///////////////////
// Helper struct for stack allocated const sized strings, because structs are
// easier to create uninit than arrays.
struct SDL_Window {}
struct SDL_Renderer {}
struct SDL_Texture {}
struct SDL_Event { type: u32, reserved: [u8; 124] }
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) -> *u8;
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_Window, renderer_out: &mut *SDL_Renderer) -> bool;
extern fn SDL_Delay(ms: u32);
extern fn SDL_SetRenderDrawColor(renderer: *SDL_Renderer, r: u8, g: u8, b: u8, a: u8);
extern fn SDL_RenderClear(renderer: *SDL_Renderer);
extern fn SDL_RenderPresent(renderer: *SDL_Renderer);
extern fn SDL_HasEvent(event_type: u32) -> bool;
extern fn SDL_PollEvent(event: &mut SDL_Event) -> bool;
extern fn SDL_PumpEvents();
extern fn SDL_FlushEvents(min_type: u32, max_type: u32);
extern fn SDL_GetTicks() -> u64;
extern fn SDL_SetWindowTitle(window: *SDL_Window, title: *char) -> bool;
extern fn SDL_CreateTexture(renderer: *SDL_Renderer,
pixel_format: u32, texture_access: u32, width: u32, height: u32) -> *SDL_Texture;
extern fn SDL_RenderTexture(renderer: *SDL_Renderer,
texture: *SDL_Texture, srcfrect: &SDL_FRect, dstfrect: &SDL_FRect) -> bool;
extern fn SDL_UpdateTexture(texture: *SDL_Texture, rect: &SDL_Rect, pixels: *u8, pitch: u32) -> bool;
extern fn SDL_GetError() -> *char;
extern fn SDL_GetWindowSize(window: *SDL_Window, w: &mut i32, h: &mut i32) -> bool;
extern fn SDL_rand(max_exclusive: u32) -> u32;
extern fn SDL_SetTextureScaleMode(texture: *SDL_Texture, scale_mode: i32) -> bool;
extern fn SDL_sqrtf(value: f32) -> f32;
extern fn SDL_randf() -> f32;
extern fn SDL_powf(value: f32, power: f32) -> f32;
// SDL error reporting helper
fn print_sdl_error(context: *char) {
let mut message = String::new();
message = message + context + ": " + SDL_GetError();
print(message);
message.free();
}
/////////////////////////////////
/// Main setup and frame loop ///
/////////////////////////////////
struct GameState {
renderer: *SDL_Renderer,
window: *SDL_Window,
render_texture: *SDL_Texture,
frame_counter: u32,
last_fps_reset: u64,
pixels: *u8,
pixels_w: u32,
pixels_h: u32,
pixels_bpp: 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_PIXELFORMAT_RGB24 = 386930691;
let SDL_PIXELFORMAT_BGR24 = 390076419;
let SDL_PIXELFORMAT_RGB96_FLOAT = 454057996;
let SDL_PIXELFORMAT_BGR96_FLOAT = 457203724;
let SDL_TEXTUREACCESS_STREAMING = 1;
let SDL_SCALEMODE_NEAREST = 0;
let SDL_SCALEMODE_LINEAR = 1;
let SDL_SCALEMODE_PIXELART = 2;
let init_success = SDL_Init(SDL_INIT_VIDEO);
if init_success == false {
print_sdl_error("SDL init failed");
return 1;
}
let mut window = SDL_Window::null();
let mut renderer = SDL_Renderer::null();
let gfx_init_success = SDL_CreateWindowAndRenderer(
"cpu raytracer", 640, 480, SDL_WINDOW_RESIZABLE,
&mut window, &mut renderer
);
if gfx_init_success == false {
print_sdl_error("SDL renderer and window creation failed");
return 1;
}
let width = 128;
let height = 64;
let bpp = 4;
let render_texture = SDL_CreateTexture(renderer,
SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, width, height);
SDL_SetTextureScaleMode(render_texture, SDL_SCALEMODE_NEAREST);
let pixels_len = (width * height * bpp) as u64;
let pixels = SDL_malloc(pixels_len);
let mut game_state = GameState {
renderer: renderer,
window: window,
render_texture: render_texture,
frame_counter: 0,
last_fps_reset: 0,
pixels: pixels,
pixels_w: width,
pixels_h: height,
pixels_bpp: bpp,
};
while frame_loop(&mut game_state) {}
SDL_Quit();
return 0;
}
fn frame_loop(game_state: &mut GameState) -> bool {
let mut event = SDL_Event { type: 0, reserved: [0; 124] };
while (SDL_PollEvent(&mut event)) {
if event.type == 256 { // SDL_EVENT_QUIT
return false;
}
}
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;
let bpp = *game_state.pixels_bpp;
for y in 0..h {
for x in 0..w {
render_pixel(x, y, game_state);
}
}
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 as *u8, bpp * w) == false {
print_sdl_error("UpdateTexture error");
}
let src = SDL_FRect { x: 0.0, y: 0.0, w: w as f32, h: h as f32 };
let aspect_ratio = src.w / src.h;
let scaled_width = screen_height as f32 * aspect_ratio;
let dst = SDL_FRect { x: (screen_width as f32 - scaled_width) / 2.0, y: 0.0, w: scaled_width, h: screen_height as f32 };
if SDL_RenderTexture(renderer, *game_state.render_texture, &src, &dst) == false {
print_sdl_error("RenderTexture error");
}
SDL_RenderPresent(renderer);
SDL_Delay(1);
*game_state.frame_counter = *game_state.frame_counter + 1;
let t = SDL_GetTicks();
if (t - *game_state.last_fps_reset) >= 1000 {
let mut title = String::new();
title = title + "cpu raytracer (" + *game_state.frame_counter as u64 + " fps)";
SDL_SetWindowTitle(*game_state.window, title.inner);
title.free();
*game_state.frame_counter = 0;
*game_state.last_fps_reset = t;
}
return true;
}
fn render_pixel(x: u32, y: u32, game_state: &mut GameState) {
let w = *game_state.pixels_w;
let h = *game_state.pixels_h;
let bpp = *game_state.pixels_bpp;
let samples = 8;
let old_sample_weight = 0.9;
let new_sample_weight = 0.1 / samples as f32;
let mut rgb = vec_mul_scalar(old_sample_weight, [
srgb_to_linear(*game_state.pixels[(x + y * w) * bpp + 0]),
srgb_to_linear(*game_state.pixels[(x + y * w) * bpp + 1]),
srgb_to_linear(*game_state.pixels[(x + y * w) * bpp + 2])
]);
for sample in 0..samples {
rgb = vec_add(rgb, vec_mul_scalar(new_sample_weight, shade(x, y, *game_state.frame_counter, w, h)));
}
*game_state.pixels[(x + y * w) * bpp + 0] = linear_to_srgb(rgb[0]);
*game_state.pixels[(x + y * w) * bpp + 1] = linear_to_srgb(rgb[1]);
*game_state.pixels[(x + y * w) * bpp + 2] = linear_to_srgb(rgb[2]);
*game_state.pixels[(x + y * w) * bpp + 3] = 255;
}
/////////////////
/// Rendering ///
/////////////////
struct Ray {
origin: [f32; 3],
direction: [f32; 3],
}
struct Material {
// 0 = lambertian diffuse
// 1 = mirror
type: u32,
// Generally the "color" of the surface (linear factors of how much of each
// color channel this surface does not absorb), but the idea is that the
// type governs what this means.
linear_color: [f32; 3],
}
struct Hit {
hit: bool,
front_face: bool,
distance: f32,
normal: [f32; 3],
position: [f32; 3],
material: Material,
}
impl Hit {
fn none() -> Hit {
Hit {
hit: false, front_face: true, distance: 0.0, normal: [0.0; 3], position: [0.0; 3],
material: Material { type: 0, linear_color: [0.0; 3] },
}
}
}
struct Sphere {
center: [f32; 3],
radius: f32,
material: Material,
}
fn shade(x: u32, y: u32, t: u32, w: u32, h: u32) -> [f32; 3] {
let jitter_x = SDL_randf() - 0.5;
let jitter_y = SDL_randf() - 0.5;
let pixel_scale = 1.0 / h as f32;
let pixel_pos = [
(x as f32 + jitter_x) * pixel_scale,
1.0 - (y as f32 + jitter_y) * pixel_scale,
-1.0
];
let camera_pos = [w as f32 * 0.5f32 * pixel_scale, h as f32 * 0.5f32 * pixel_scale, 0.0f32];
let dir = vec_normalize(vec_sub(pixel_pos, camera_pos));
let ray = Ray { origin: camera_pos, direction: dir };
let beige_lambertian = Material { type: 0, linear_color: [0.3, 0.2, 0.1] };
let green_lambertian = Material { type: 0, linear_color: [0.1, 0.5, 0.06] };
let greenish_mirror = Material { type: 1, linear_color: [0.9, 1.0, 0.95] };
let spheres = [
// Ground
Sphere { center: vec_sub(camera_pos, [0.0, 100001.0, 0.0]), radius: 100000.0, material: beige_lambertian },
// Centered unit sphere
Sphere { center: vec_add(camera_pos, [0.0, 0.0, 0.0 - 5.0]), radius: 1.0, material: green_lambertian },
// The unit sphere on the right
Sphere { center: vec_add(camera_pos, [2.0, 0.0, 0.0 - 6.0]), radius: 1.0, material: greenish_mirror }
];
return shade_world(ray, &spheres, 3);
}
fn shade_world(ray: Ray, spheres: &[Sphere; 3], bounces_left: u8) -> [f32; 3] {
if bounces_left == 0 {
return [0.0, 0.0, 0.0];
}
let mut closest_hit = Hit::none();
closest_hit.distance = 100.0;
for i in 0..3 {
let sphere_hit = ray_sphere_closest_hit(ray, *spheres[i], [0.001, closest_hit.distance]);
if sphere_hit.hit {
closest_hit = sphere_hit;
}
}
if closest_hit.hit {
//return vec_mul_scalar(0.5, vec_add(closest_hit.normal, [1.0, 1.0, 1.0])); // normal
//return vec_mul_scalar(closest_hit.distance / 10.0, [1.0, 1.0, 1.0]); // depth
if closest_hit.material.type == 0 {
let bounce_dir = vec_normalize(vec_add(closest_hit.normal, random_unit_vec()));
let bounce_ray = Ray { origin: closest_hit.position, direction: bounce_dir };
return vec_mul_componentwise(
closest_hit.material.linear_color,
shade_world(bounce_ray, spheres, bounces_left - 1)
);
} else if closest_hit.material.type == 1 {
let bounce_dir = vec_reflect(ray.direction, closest_hit.normal);
let bounce_ray = Ray { origin: closest_hit.position, direction: bounce_dir };
return vec_mul_componentwise(
closest_hit.material.linear_color,
shade_world(bounce_ray, spheres, bounces_left - 1)
);
} else {
return [1.0, 0.0, 1.0];
}
}
return shade_sky(ray);
}
fn shade_sky(ray: Ray) -> [f32; 3] {
let a = 0.5 * (ray.direction[1] + 1.0);
return vec_add(
vec_mul_scalar(1.0 - a, [1.0, 1.0, 1.0]),
vec_mul_scalar(a, [0.5, 0.7, 1.0])
);
}
// Returns the distance from the ray origin to the sphere, or -1.0 if the ray doesn't hit.
fn ray_sphere_closest_hit(ray: Ray, sphere: Sphere, interval: [f32; 2]) -> Hit {
let to_sphere = vec_sub(sphere.center, ray.origin);
let h = vec_dot(ray.direction, to_sphere);
let c = vec_length_squared(to_sphere) - sphere.radius * sphere.radius;
let discriminant = h * h - c;
if discriminant < 0.0 {
return Hit::none();
}
let discriminant_sqrt = SDL_sqrtf(discriminant);
let mut distance = h - discriminant_sqrt;
if interval_surrounds(interval, distance) == false {
distance = h + discriminant_sqrt;
if interval_surrounds(interval, distance) == false {
return Hit::none();
}
}
let hit_position = vec_add(ray.origin, vec_mul_scalar(distance, ray.direction));
let mut front_face = true;
let mut normal = vec_normalize(vec_sub(hit_position, sphere.center));
if vec_dot(normal, ray.direction) > 0.0 {
normal = vec_mul_scalar(-1.0, normal);
front_face = false;
}
return Hit {
hit: true,
front_face: front_face,
distance: distance,
normal: normal,
position: hit_position,
material: sphere.material,
};
}
//////////////////
/// Other math ///
//////////////////
fn clamp(min: f32, max: f32, value: f32) -> f32 {
if value > max {
return max;
}
if value < min {
return min;
}
return value;
}
fn abs(f: f32) -> f32 {
if f < 0.0 {
return f * -1.0;
}
return f;
}
fn vec_add(lhs: [f32; 3], rhs: [f32; 3]) -> [f32; 3] {
return [lhs[0] + rhs[0], lhs[1] + rhs[1], lhs[2] + rhs[2]];
}
fn vec_sub(lhs: [f32; 3], rhs: [f32; 3]) -> [f32; 3] {
return [lhs[0] - rhs[0], lhs[1] - rhs[1], lhs[2] - rhs[2]];
}
fn vec_dot(lhs: [f32; 3], rhs: [f32; 3]) -> f32 {
return lhs[0] * rhs[0] + lhs[1] * rhs[1] + lhs[2] * rhs[2];
}
fn vec_mul_componentwise(lhs: [f32; 3], rhs: [f32; 3]) -> [f32; 3] {
return [lhs[0] * rhs[0], lhs[1] * rhs[1], lhs[2] * rhs[2]];
}
fn vec_mul_scalar(lhs: f32, rhs: [f32; 3]) -> [f32; 3] {
return [lhs * rhs[0], lhs * rhs[1], lhs * rhs[2]];
}
fn vec_normalize(v: [f32; 3]) -> [f32; 3] {
let len_reciprocal = 1.0f32 / SDL_sqrtf(vec_length_squared(v));
return vec_mul_scalar(len_reciprocal, v);
}
fn vec_length_squared(v: [f32; 3]) -> f32 {
return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
}
fn vec_abs(v: [f32; 3]) -> [f32; 3] {
return [abs(v[0]), abs(v[1]), abs(v[2])];
}
fn vec_reflect(direction: [f32; 3], normal: [f32; 3]) -> [f32; 3] {
return vec_sub(direction, vec_mul_scalar(2.0f32 * vec_dot(direction, normal), normal));
}
fn interval_surrounds(interval: [f32; 2], value: f32) -> bool {
return (interval[0] < value) && (value < interval[1]);
}
fn random_unit_vec() -> [f32; 3] {
let mut point = [
SDL_randf() * 2.0f32 - 1.0f32,
SDL_randf() * 2.0f32 - 1.0f32,
SDL_randf() * 2.0f32 - 1.0f32
];
let mut lensq = vec_length_squared(point);
while lensq > 1.0 {
point = [
SDL_randf() * 2.0f32 - 1.0f32,
SDL_randf() * 2.0f32 - 1.0f32,
SDL_randf() * 2.0f32 - 1.0f32
];
lensq = vec_length_squared(point);
}
let len_reciprocal = 1.0f32 / SDL_sqrtf(lensq);
return vec_mul_scalar(len_reciprocal, point);
}
fn random_unit_vec_on_hemi(normal: [f32; 3]) -> [f32; 3] {
let rand_vec = random_unit_vec();
if vec_dot(rand_vec, normal) < 0.0f32 {
return vec_mul_scalar(0.0f32 - 1.0f32, rand_vec);
}
return rand_vec;
}
fn linear_to_srgb(linear: f32) -> u8 {
let mut floating_srgb = 0.0;
if linear <= 0.0031308f32 {
floating_srgb = 12.92f32 * linear;
} else {
floating_srgb = SDL_powf(linear as f32, 1.0 / 2.4) * 1.055f32 - 0.055f32;
}
let clamped = clamp(0.0, 1.0, floating_srgb);
return (clamped * 255.999) as u8;
}
fn srgb_to_linear(srgb: u8) -> f32 {
let floating_srgb = srgb as f32 / 255.0;
if floating_srgb <= 0.04045f32 {
return floating_srgb / 12.92f32;
}
return SDL_powf((floating_srgb as f32 + 0.055) / 1.055, 2.4);
}

View File

@ -1,12 +1,13 @@
// Main
fn main() -> bool {
return 144 == fibonacci(0xc);
}
// Fibonacci
fn fibonacci(value: u16) -> u16 {
if value <= 2 {
return 1;
}
fibonacci(value - 1) + fibonacci(value - 2)
}
a
(
b
)
x
(
(
c
xyz
)
)
(
a

9
examples/macro_easy.reid Normal file
View File

@ -0,0 +1,9 @@
import std::String;
import std::print;
fn main() -> u8 {
let bytes = include_bytes!("./macro_easy_file.txt");
print(String::new() + bytes.length());
print(String::new() + (include_bytes!("./macro_easy_file.txt") as *u8)[1] as u64);
return (include_bytes!("./macro_easy_file.txt") as *u8)[0];
}

View File

@ -0,0 +1 @@
hello

View File

@ -0,0 +1,25 @@
struct Game {}
impl Game {
pub fn run_frame(&mut self) {}
}
struct Platform {
game: Game,
}
impl Platform {
pub fn new() -> Platform {
return Platform { game: Game {} };
}
pub fn run_frame(&mut self) {
*self.game.run_frame();
}
}
fn main() -> i32 {
let mut platform = Platform::new();
platform.run_frame();
return 0;
}

View File

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

View File

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

View File

@ -1,30 +1,23 @@
use reid_lib::{CmpPredicate, ConstValue, Context, FunctionFlags, Instr, TerminatorKind, Type};
use reid_lib::{CmpPredicate, ConstValueKind, Context, FunctionFlags, Instr, TerminatorKind, Type};
fn main() {
use ConstValue::*;
use ConstValueKind::*;
use Instr::*;
let context = Context::new("libtest");
let module = context.module("test", true);
let main = module.function("main", Type::I32, Vec::new(), FunctionFlags::default());
let main = module.function("main", None, Type::I32, Vec::new(), FunctionFlags::default());
let mut m_entry = main.block("entry");
let fibonacci = module.function(
"fibonacci",
Type::I32,
vec![Type::I32],
FunctionFlags::default(),
);
let fibonacci = module.function("fibonacci", None, Type::I32, vec![Type::I32], FunctionFlags::default());
let arg = m_entry.build_named("const", Constant(I32(5))).unwrap();
let fibonacci_call = m_entry
.build_named("const", FunctionCall(fibonacci.value(), vec![arg]))
.unwrap();
m_entry
.terminate(TerminatorKind::Ret(fibonacci_call))
.unwrap();
m_entry.terminate(TerminatorKind::Ret(fibonacci_call)).unwrap();
let mut f_entry = fibonacci.block("entry");
@ -59,7 +52,5 @@ fn main() {
else_b.terminate(TerminatorKind::Ret(add)).unwrap();
dbg!(&context);
context.compile(None, Vec::new());
}

View File

@ -4,8 +4,8 @@
use std::{cell::RefCell, rc::Rc};
use crate::{
Block, BlockData, CompileResult, CustomTypeKind, ErrorKind, FunctionData, Instr, InstructionData, ModuleData,
NamedStruct, TerminatorKind, Type, TypeCategory, TypeData,
Block, BlockData, CompileResult, ConstValueKind, CustomTypeKind, ErrorKind, FunctionData, Instr, InstructionData,
ModuleData, NamedStruct, TerminatorKind, Type, TypeCategory, TypeData,
debug_information::{
DebugInformation, DebugLocationValue, DebugMetadataValue, DebugScopeValue, InstructionDebugRecordData,
},
@ -27,6 +27,12 @@ pub struct BlockValue(pub(crate) FunctionValue, pub(crate) usize);
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
pub struct InstructionValue(pub(crate) BlockValue, pub(crate) usize);
#[derive(Debug, Clone, Hash, Copy, PartialEq, Eq)]
pub struct ConstantValue(pub(crate) ModuleValue, pub(crate) usize);
#[derive(Debug, Clone, Hash, Copy, PartialEq, Eq)]
pub struct GlobalValue(pub(crate) ModuleValue, pub(crate) usize);
#[derive(Clone)]
pub struct ModuleHolder {
pub(crate) value: ModuleValue,
@ -34,6 +40,20 @@ pub struct ModuleHolder {
pub(crate) functions: Vec<FunctionHolder>,
pub(crate) types: Vec<TypeHolder>,
pub(crate) debug_information: Option<DebugInformation>,
pub(crate) constants: Vec<ConstantValueHolder>,
pub(crate) globals: Vec<GlobalValueHolder>,
}
#[derive(Clone)]
pub struct ConstantValueHolder {
pub(crate) value: ConstantValue,
pub(crate) kind: ConstValueKind,
}
#[derive(Clone)]
pub struct GlobalValueHolder {
pub(crate) value: GlobalValue,
pub(crate) name: String,
pub(crate) initializer: ConstantValue,
}
#[derive(Clone)]
@ -88,6 +108,8 @@ impl Builder {
functions: Vec::new(),
types: Vec::new(),
debug_information: None,
constants: Vec::new(),
globals: Vec::new(),
});
value
}
@ -168,6 +190,43 @@ impl Builder {
}
}
pub(crate) unsafe fn build_constant(&self, module: ModuleValue, kind: ConstValueKind) -> ConstantValue {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(module.0);
let value = ConstantValue(module.value, module.constants.len());
module.constants.push(ConstantValueHolder { value, kind });
value
}
}
pub(crate) unsafe fn add_global(
&self,
module: ModuleValue,
name: String,
initializer: ConstantValue,
) -> GlobalValue {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(module.0);
let value = GlobalValue(module.value, module.globals.len());
module.globals.push(GlobalValueHolder {
value,
name,
initializer,
});
value
}
}
pub(crate) unsafe fn find_function(&self, module: ModuleValue, name: &String) -> Option<FunctionValue> {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(module.0);
module.functions.iter().find(|f| f.data.name == *name).map(|f| f.value)
}
}
pub(crate) unsafe fn add_instruction_location(&self, value: &InstructionValue, location: DebugLocationValue) {
unsafe {
let mut modules = self.modules.borrow_mut();
@ -323,6 +382,24 @@ impl Builder {
self.modules.clone()
}
pub(crate) fn get_global_initializer(&self, value: GlobalValue) -> ConstantValue {
unsafe {
let modules = self.modules.borrow();
let module = modules.get_unchecked(value.0.0);
let global = module.globals.get_unchecked(value.1);
global.initializer
}
}
pub(crate) fn get_const_kind(&self, value: ConstantValue) -> ConstValueKind {
unsafe {
let modules = self.modules.borrow();
let module = modules.get_unchecked(value.0.0);
let constant = module.constants.get_unchecked(value.1);
constant.kind.clone()
}
}
pub fn check_instruction(&self, instruction: &InstructionValue) -> CompileResult<()> {
unsafe {
match self.instr_data(&instruction).kind {
@ -533,30 +610,10 @@ impl Builder {
Instr::PtrToInt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::IntToPtr(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::BitCast(..) => Ok(()),
Instr::ShiftRightLogical(_, rhs) => {
let rhs_ty = rhs.get_type(&self)?;
if rhs_ty.category() == TypeCategory::UnsignedInteger {
Ok(())
} else {
Err(ErrorKind::Null)
}
}
Instr::ShiftRightArithmetic(_, rhs) => {
let rhs_ty = rhs.get_type(&self)?;
if rhs_ty.category() == TypeCategory::UnsignedInteger {
Ok(())
} else {
Err(ErrorKind::Null)
}
}
Instr::ShiftLeft(_, rhs) => {
let rhs_ty = rhs.get_type(&self)?;
if rhs_ty.category() == TypeCategory::UnsignedInteger {
Ok(())
} else {
Err(ErrorKind::Null)
}
}
Instr::ShiftRightLogical(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::ShiftRightArithmetic(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::ShiftLeft(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::GetGlobal(_) => Ok(()),
}
}
}
@ -708,6 +765,11 @@ impl InstructionValue {
ShiftRightLogical(lhs, _) => lhs.get_type(builder),
ShiftRightArithmetic(lhs, _) => lhs.get_type(builder),
ShiftLeft(lhs, _) => lhs.get_type(builder),
GetGlobal(global_value) => {
let constant = builder.get_global_initializer(*global_value);
let kind = builder.get_const_kind(constant);
Ok(kind.get_type())
}
}
}
}

View File

@ -27,13 +27,13 @@ use llvm_sys::{
use crate::{
CustomTypeKind,
builder::{TypeHolder, TypeValue},
builder::{ConstantValue, GlobalValue, TypeHolder, TypeValue},
debug_information::*,
util::{ErrorMessageHolder, MemoryBufferHolder, from_cstring, into_cstring},
};
use super::{
CmpPredicate, ConstValue, Context, TerminatorKind, Type,
CmpPredicate, ConstValueKind, Context, TerminatorKind, Type,
builder::{
BlockHolder, BlockValue, Builder, FunctionHolder, FunctionValue, InstructionHolder, InstructionValue,
ModuleHolder,
@ -123,8 +123,6 @@ impl CompiledModule {
let llvm_ir =
from_cstring(LLVMPrintModuleToString(self.module_ref)).expect("Unable to print LLVM IR to string");
println!("{}", llvm_ir);
let mut err = ErrorMessageHolder::null();
LLVMVerifyModule(
self.module_ref,
@ -193,6 +191,8 @@ pub struct LLVMModule<'a> {
blocks: HashMap<BlockValue, LLVMBasicBlockRef>,
values: HashMap<InstructionValue, LLVMValue>,
types: HashMap<TypeValue, LLVMTypeRef>,
constants: HashMap<ConstantValue, LLVMValue>,
globals: HashMap<GlobalValue, LLVMValueRef>,
debug: Option<LLVMDebugInformation<'a>>,
}
@ -237,6 +237,8 @@ impl ModuleHolder {
// Compile the contents
let mut types = HashMap::new();
let mut constants = HashMap::new();
let mut globals = HashMap::new();
let mut metadata = HashMap::new();
let mut scopes = HashMap::new();
let mut locations = HashMap::new();
@ -320,6 +322,29 @@ impl ModuleHolder {
types.insert(ty.value, ty.compile_type(context, &types));
}
for constant in &self.constants {
constants.insert(
constant.value,
LLVMValue {
ty: constant.kind.get_type(),
value_ref: constant
.kind
.as_llvm(context.context_ref, context.builder_ref, &constants, &types),
},
);
}
for global in &self.globals {
let initializer = constants.get(&global.initializer).expect("No initializer?");
let global_value = LLVMAddGlobal(
module_ref,
initializer.ty.as_llvm(context.context_ref, &types),
into_cstring(global.name.clone()).as_ptr(),
);
LLVMSetInitializer(global_value, initializer.value_ref);
globals.insert(global.value, global_value);
}
let mut functions = HashMap::new();
for function in &self.functions {
let func = function.compile_signature(context, module_ref, &types, &mut debug);
@ -358,6 +383,8 @@ impl ModuleHolder {
types,
blocks: HashMap::new(),
values: HashMap::new(),
constants,
globals,
debug,
};
@ -399,12 +426,12 @@ impl DebugScopeHolder {
unsafe {
let scope = match &self.data.kind {
DebugScopeKind::CodegenContext => panic!(),
DebugScopeKind::LexicalScope => LLVMDIBuilderCreateLexicalBlock(
DebugScopeKind::LexicalScope(data) => LLVMDIBuilderCreateLexicalBlock(
di_builder,
parent,
file,
self.data.location.as_ref().unwrap().pos.line,
self.data.location.as_ref().unwrap().pos.column,
data.location.pos.line,
data.location.pos.column,
),
DebugScopeKind::Subprogram(_) => panic!(),
};
@ -513,7 +540,7 @@ impl DebugTypeHolder {
field.pos.map(|p| p.line).unwrap_or(1),
field.size_bits,
0,
1,
field.offset,
field.flags.as_llvm(),
*debug.types.get(&field.ty).unwrap(),
)
@ -587,9 +614,15 @@ impl FunctionHolder {
let param_ptr = param_types.as_mut_ptr();
let param_len = param_types.len();
let name = if self.data.flags.is_main {
c"main"
} else {
&into_cstring(&self.data.linkage_name.clone().unwrap_or(self.data.name.clone()))
};
let fn_type = LLVMFunctionType(ret_type, param_ptr, param_len as u32, 0);
let function_ref = LLVMAddFunction(module_ref, into_cstring(&self.data.name).as_ptr(), fn_type);
let function_ref = LLVMAddFunction(module_ref, name.as_ptr(), fn_type);
if self.data.flags.inline {
let attribute = LLVMCreateEnumAttribute(context.context_ref, LLVMEnumAttribute::AlwaysInline as u32, 0);
@ -599,7 +632,6 @@ impl FunctionHolder {
let metadata = if let Some(debug) = debug {
if let Some(scope_value) = &self.debug_info {
let scope_data = debug.info.get_scope_data(scope_value).unwrap();
dbg!(&debug.info.get_scope());
let mangled_length_ptr = &mut 0;
let mangled_name = LLVMGetValueName2(function_ref, mangled_length_ptr);
@ -607,7 +639,7 @@ impl FunctionHolder {
let subprogram = match scope_data.kind {
DebugScopeKind::CodegenContext => panic!(),
DebugScopeKind::LexicalScope => panic!(),
DebugScopeKind::LexicalScope(_) => panic!(),
DebugScopeKind::Subprogram(subprogram) => LLVMDIBuilderCreateFunction(
debug.builder,
*debug.scopes.get(&scope_data.parent.unwrap()).unwrap(),
@ -727,7 +759,7 @@ impl InstructionHolder {
use super::Instr::*;
match &self.data.kind {
Param(nth) => LLVMGetParam(function.value_ref, *nth as u32),
Constant(val) => val.as_llvm(module),
Constant(val) => val.as_llvm(module.context_ref, module.builder_ref, &module.constants, &module.types),
Add(lhs, rhs) => {
let lhs_val = module.values.get(&lhs).unwrap().value_ref;
let rhs_val = module.values.get(&rhs).unwrap().value_ref;
@ -1018,6 +1050,7 @@ impl InstructionHolder {
let rhs_val = module.values.get(&rhs).unwrap().value_ref;
LLVMBuildShl(module.builder_ref, lhs_val, rhs_val, name.as_ptr())
}
GetGlobal(global_value) => module.globals.get(global_value).unwrap().clone(),
}
};
if let Some(record) = &self.record {
@ -1139,32 +1172,50 @@ impl CmpPredicate {
}
}
impl ConstValue {
fn as_llvm(&self, module: &LLVMModule) -> LLVMValueRef {
impl ConstValueKind {
fn as_llvm(
&self,
context: LLVMContextRef,
builder: LLVMBuilderRef,
constants: &HashMap<ConstantValue, LLVMValue>,
types: &HashMap<TypeValue, LLVMTypeRef>,
) -> LLVMValueRef {
unsafe {
let t = self.get_type().as_llvm(module.context_ref, &module.types);
let t = self.get_type().as_llvm(context, &types);
match self {
ConstValue::Bool(val) => LLVMConstInt(t, *val as u64, 1),
ConstValue::I8(val) => LLVMConstInt(t, *val as u64, 1),
ConstValue::I16(val) => LLVMConstInt(t, *val as u64, 1),
ConstValue::I32(val) => LLVMConstInt(t, *val as u64, 1),
ConstValue::I64(val) => LLVMConstInt(t, *val as u64, 1),
ConstValue::I128(val) => LLVMConstInt(t, *val as u64, 1),
ConstValue::U8(val) => LLVMConstInt(t, *val as u64, 1),
ConstValue::U16(val) => LLVMConstInt(t, *val as u64, 1),
ConstValue::U32(val) => LLVMConstInt(t, *val as u64, 1),
ConstValue::U64(val) => LLVMConstInt(t, *val as u64, 1),
ConstValue::U128(val) => LLVMConstInt(t, *val as u64, 1),
ConstValue::Str(val) => {
LLVMBuildGlobalString(module.builder_ref, into_cstring(val).as_ptr(), c"string".as_ptr())
ConstValueKind::Bool(val) => LLVMConstInt(t, *val as u64, 1),
ConstValueKind::I8(val) => LLVMConstInt(t, *val as u64, 1),
ConstValueKind::I16(val) => LLVMConstInt(t, *val as u64, 1),
ConstValueKind::I32(val) => LLVMConstInt(t, *val as u64, 1),
ConstValueKind::I64(val) => LLVMConstInt(t, *val as u64, 1),
ConstValueKind::I128(val) => LLVMConstInt(t, *val as u64, 1),
ConstValueKind::U8(val) => LLVMConstInt(t, *val as u64, 1),
ConstValueKind::U16(val) => LLVMConstInt(t, *val as u64, 1),
ConstValueKind::U32(val) => LLVMConstInt(t, *val as u64, 1),
ConstValueKind::U64(val) => LLVMConstInt(t, *val as u64, 1),
ConstValueKind::U128(val) => LLVMConstInt(t, *val as u64, 1),
ConstValueKind::Str(val) => {
LLVMBuildGlobalString(builder, into_cstring(val).as_ptr(), c"string".as_ptr())
}
ConstValueKind::F16(val) => LLVMConstReal(t, *val as f64),
ConstValueKind::F32B(val) => LLVMConstReal(t, *val as f64),
ConstValueKind::F32(val) => LLVMConstReal(t, *val as f64),
ConstValueKind::F64(val) => LLVMConstReal(t, *val as f64),
ConstValueKind::F80(val) => LLVMConstReal(t, *val as f64),
ConstValueKind::F128(val) => LLVMConstReal(t, *val as f64),
ConstValueKind::F128PPC(val) => LLVMConstReal(t, *val as f64),
ConstValueKind::Array(constant_values, elem_ty) => {
let mut values = constant_values
.iter()
.map(|v| constants.get(v).unwrap().value_ref)
.collect::<Vec<_>>();
LLVMConstArray2(
elem_ty.as_llvm(context, &types),
values.as_mut_ptr(),
values.len() as u64,
)
}
ConstValue::F16(val) => LLVMConstReal(t, *val as f64),
ConstValue::F32B(val) => LLVMConstReal(t, *val as f64),
ConstValue::F32(val) => LLVMConstReal(t, *val as f64),
ConstValue::F64(val) => LLVMConstReal(t, *val as f64),
ConstValue::F80(val) => LLVMConstReal(t, *val as f64),
ConstValue::F128(val) => LLVMConstReal(t, *val as f64),
ConstValue::F128PPC(val) => LLVMConstReal(t, *val as f64),
}
}
}

View File

@ -71,10 +71,6 @@ impl DebugInformation {
inner_scopes: Vec::new(),
data: DebugScopeData {
parent: None,
location: Some(DebugLocation {
scope: DebugScopeValue(Vec::new()),
pos: DebugPosition { line: 0, column: 0 },
}),
kind: DebugScopeKind::CodegenContext,
},
})),
@ -86,32 +82,6 @@ impl DebugInformation {
)
}
pub fn inner_scope(&self, parent: &DebugScopeValue, location: DebugLocation) -> DebugScopeValue {
unsafe {
let mut outer_scope = RefMut::map(self.scope.borrow_mut(), |mut v| {
for i in &parent.0 {
v = v.inner_scopes.get_unchecked_mut(*i);
}
v
});
let mut arr = parent.0.clone();
arr.push(parent.0.len());
let value = DebugScopeValue(arr);
outer_scope.inner_scopes.push(DebugScopeHolder {
value: value.clone(),
inner_scopes: Vec::new(),
data: DebugScopeData {
parent: Some(parent.clone()),
location: Some(location),
kind: DebugScopeKind::LexicalScope,
},
});
value
}
}
pub fn location(&self, scope_value: &DebugScopeValue, location: DebugLocation) -> DebugLocationValue {
let value = DebugLocationValue(scope_value.clone(), self.locations.borrow().len());
let location = DebugLocationHolder {
@ -162,13 +132,36 @@ impl DebugInformation {
inner_scopes: Vec::new(),
data: DebugScopeData {
parent: Some(parent.clone()),
location: None,
kind: DebugScopeKind::Subprogram(kind),
},
});
value
}
}
pub fn lexical_scope(&self, parent: &DebugScopeValue, data: DebugLexicalScope) -> DebugScopeValue {
unsafe {
let mut outer_scope = RefMut::map(self.scope.borrow_mut(), |mut v| {
for i in &parent.0 {
v = v.inner_scopes.get_unchecked_mut(*i);
}
v
});
let mut arr = parent.0.clone();
arr.push(outer_scope.inner_scopes.len());
let value = DebugScopeValue(arr);
outer_scope.inner_scopes.push(DebugScopeHolder {
value: value.clone(),
inner_scopes: Vec::new(),
data: DebugScopeData {
parent: Some(parent.clone()),
kind: DebugScopeKind::LexicalScope(data),
},
});
value
}
}
pub fn get_metadata(&self, value: DebugMetadataValue) -> DebugMetadata {
unsafe { self.metadata.borrow().get_unchecked(value.0).data.clone() }
@ -352,17 +345,21 @@ pub enum DwarfEncoding {
#[derive(Debug, Clone)]
pub struct DebugScopeData {
pub parent: Option<DebugScopeValue>,
pub location: Option<DebugLocation>,
pub kind: DebugScopeKind,
}
#[derive(Debug, Clone)]
pub enum DebugScopeKind {
CodegenContext,
LexicalScope,
LexicalScope(DebugLexicalScope),
Subprogram(DebugSubprogramData),
}
#[derive(Debug, Clone)]
pub struct DebugLexicalScope {
pub location: DebugLocation,
}
#[derive(Debug, Clone)]
pub struct DebugSubprogramData {
/// Function name.

View File

@ -392,6 +392,7 @@ impl Debug for Instr {
Instr::ShiftRightLogical(lhs, rhs) => fmt_binop(f, lhs, &">>l", rhs),
Instr::ShiftRightArithmetic(lhs, rhs) => fmt_binop(f, lhs, &">>a", rhs),
Instr::ShiftLeft(lhs, rhs) => fmt_binop(f, lhs, &"<<", rhs),
Instr::GetGlobal(global_value) => write!(f, "global {:?}", global_value),
}
}
}

View File

@ -8,7 +8,10 @@ use builder::{BlockValue, Builder, FunctionValue, InstructionValue, ModuleValue,
use debug_information::{DebugFileData, DebugInformation, DebugLocationValue, DebugMetadataValue};
use fmt::PrintableModule;
use crate::debug_information::DebugScopeValue;
use crate::{
builder::{ConstantValue, GlobalValue},
debug_information::DebugScopeValue,
};
pub mod builder;
pub mod compile;
@ -66,7 +69,14 @@ pub struct Module<'ctx> {
}
impl<'ctx> Module<'ctx> {
pub fn function(&self, name: &str, ret: Type, params: Vec<Type>, flags: FunctionFlags) -> Function<'ctx> {
pub fn function(
&self,
name: &str,
linkage: Option<String>,
ret: Type,
params: Vec<Type>,
flags: FunctionFlags,
) -> Function<'ctx> {
unsafe {
Function {
phantom: PhantomData,
@ -75,6 +85,7 @@ impl<'ctx> Module<'ctx> {
&self.value,
FunctionData {
name: name.to_owned(),
linkage_name: linkage,
ret,
params,
flags,
@ -113,6 +124,14 @@ impl<'ctx> Module<'ctx> {
pub fn get_debug_info(&self) -> &Option<DebugInformation> {
&self.debug_info
}
pub fn add_constant(&self, constant: ConstValueKind) -> ConstantValue {
unsafe { self.builder.build_constant(self.value, constant) }
}
pub fn add_global<T: Into<String>>(&self, name: T, constant: ConstantValue) -> GlobalValue {
unsafe { self.builder.add_global(self.value, name.into(), constant) }
}
}
impl<'ctx> Drop for Module<'ctx> {
@ -126,6 +145,7 @@ impl<'ctx> Drop for Module<'ctx> {
#[derive(Debug, Clone, Hash)]
pub struct FunctionData {
name: String,
linkage_name: Option<String>,
ret: Type,
params: Vec<Type>,
flags: FunctionFlags,
@ -256,6 +276,7 @@ impl Instr {
Instr::ShiftRightLogical(..) => "lshr",
Instr::ShiftRightArithmetic(..) => "ashr",
Instr::ShiftLeft(..) => "shl",
Instr::GetGlobal(..) => "global",
}
}
}
@ -290,6 +311,10 @@ impl<'builder> Block<'builder> {
}
}
pub fn find_function(&mut self, name: &String) -> Option<FunctionValue> {
unsafe { self.builder.find_function(self.value.0.0, name) }
}
pub fn set_instr_location(&self, instruction: InstructionValue, location: DebugLocationValue) {
unsafe {
self.builder.add_instruction_location(&instruction, location);
@ -348,7 +373,8 @@ pub enum CmpPredicate {
#[derive(Clone)]
pub enum Instr {
Param(usize),
Constant(ConstValue),
Constant(ConstValueKind),
GetGlobal(GlobalValue),
/// Add two integers
Add(InstructionValue, InstructionValue),
@ -474,7 +500,7 @@ pub enum Type {
}
#[derive(Debug, Clone)]
pub enum ConstValue {
pub enum ConstValueKind {
I8(i8),
I16(i16),
I32(i32),
@ -494,6 +520,7 @@ pub enum ConstValue {
F80(f64),
F128(f64),
F128PPC(f64),
Array(Vec<ConstantValue>, Type),
}
#[derive(Clone, Hash)]
@ -518,29 +545,30 @@ pub enum CustomTypeKind {
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct NamedStruct(pub String, pub Vec<Type>);
impl ConstValue {
impl ConstValueKind {
pub fn get_type(&self) -> Type {
use Type::*;
match self {
ConstValue::I8(_) => I8,
ConstValue::I16(_) => I16,
ConstValue::I32(_) => I32,
ConstValue::I64(_) => I64,
ConstValue::I128(_) => I128,
ConstValue::U8(_) => U8,
ConstValue::U16(_) => U16,
ConstValue::U32(_) => U32,
ConstValue::U64(_) => U64,
ConstValue::U128(_) => U128,
ConstValue::Str(_) => Type::Ptr(Box::new(U8)),
ConstValue::Bool(_) => Bool,
ConstValue::F16(_) => F16,
ConstValue::F32B(_) => F32B,
ConstValue::F32(_) => F32,
ConstValue::F64(_) => F64,
ConstValue::F80(_) => F80,
ConstValue::F128(_) => F128,
ConstValue::F128PPC(_) => F128PPC,
ConstValueKind::I8(_) => I8,
ConstValueKind::I16(_) => I16,
ConstValueKind::I32(_) => I32,
ConstValueKind::I64(_) => I64,
ConstValueKind::I128(_) => I128,
ConstValueKind::U8(_) => U8,
ConstValueKind::U16(_) => U16,
ConstValueKind::U32(_) => U32,
ConstValueKind::U64(_) => U64,
ConstValueKind::U128(_) => U128,
ConstValueKind::Str(_) => Type::Ptr(Box::new(U8)),
ConstValueKind::Bool(_) => Bool,
ConstValueKind::F16(_) => F16,
ConstValueKind::F32B(_) => F32B,
ConstValueKind::F32(_) => F32,
ConstValueKind::F64(_) => F64,
ConstValueKind::F80(_) => F80,
ConstValueKind::F128(_) => F128,
ConstValueKind::F128PPC(_) => F128PPC,
ConstValueKind::Array(vals, ty) => Type::Array(Box::new(ty.clone()), vals.len() as u64),
}
}
}

6
reid-lsp/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.vscode
node_modules
dist
package-lock.json
pnpm-lock.yaml
tsconfig.tsbuildinfo

1
reid-lsp/.npmrc Normal file
View File

@ -0,0 +1 @@
enable-pre-post-scripts = true

View File

@ -0,0 +1,5 @@
import { defineConfig } from '@vscode/test-cli';
export default defineConfig({
files: 'out/test/**/*.test.js',
});

15
reid-lsp/.vscodeignore Normal file
View File

@ -0,0 +1,15 @@
.vscode/**
.vscode-test/**
out/**
node_modules/**
src/**
client/**
.gitignore
.yarnrc
webpack.config.js
vsc-extension-quickstart.md
**/tsconfig.json
**/eslint.config.mjs
**/*.map
**/*.ts
**/.vscode-test.*

9
reid-lsp/CHANGELOG.md Normal file
View File

@ -0,0 +1,9 @@
# Change Log
All notable changes to the "reid-lsp" extension will be documented in this file.
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
## [Unreleased]
- Initial release

11
reid-lsp/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "reid-lsp"
version = "0.1.0"
edition = "2024"
[dependencies]
socket = "0.0.7"
tokio = { version = "1.47.0", features = ["full"] }
tower-lsp = "0.20.0"
reid = { path = "../reid", version = "1.0.0-beta.2", registry="gitea-teascade", features=[] }
dashmap = "6.1.0"

71
reid-lsp/README.md Normal file
View File

@ -0,0 +1,71 @@
# reid-lsp README
This is the README for your extension "reid-lsp". After writing up a brief description, we recommend including the following sections.
## Features
Describe specific features of your extension including screenshots of your extension in action. Image paths are relative to this README file.
For example if there is an image subfolder under your extension project workspace:
\!\[feature X\]\(images/feature-x.png\)
> Tip: Many popular extensions utilize animations. This is an excellent way to show off your extension! We recommend short, focused animations that are easy to follow.
## Requirements
If you have any requirements or dependencies, add a section describing those and how to install and configure them.
## Extension Settings
Include if your extension adds any VS Code settings through the `contributes.configuration` extension point.
For example:
This extension contributes the following settings:
* `myExtension.enable`: Enable/disable this extension.
* `myExtension.thing`: Set to `blah` to do something.
## Known Issues
Calling out known issues can help limit users opening duplicate issues against your extension.
## Release Notes
Users appreciate release notes as you update your extension.
### 1.0.0
Initial release of ...
### 1.0.1
Fixed issue #.
### 1.1.0
Added features X, Y, and Z.
---
## Following extension guidelines
Ensure that you've read through the extensions guidelines and follow the best practices for creating your extension.
* [Extension Guidelines](https://code.visualstudio.com/api/references/extension-guidelines)
## Working with Markdown
You can author your README using Visual Studio Code. Here are some useful editor keyboard shortcuts:
* Split the editor (`Cmd+\` on macOS or `Ctrl+\` on Windows and Linux).
* Toggle preview (`Shift+Cmd+V` on macOS or `Shift+Ctrl+V` on Windows and Linux).
* Press `Ctrl+Space` (Windows, Linux, macOS) to see a list of Markdown snippets.
## For more information
* [Visual Studio Code's Markdown Support](http://code.visualstudio.com/docs/languages/markdown)
* [Markdown Syntax Reference](https://help.github.com/articles/markdown-basics/)
**Enjoy!**

View File

@ -0,0 +1,27 @@
{
"name": "reid-lsp",
"displayName": "Reid Language Server",
"description": "Language Server Extension for Reid",
"version": "0.0.1",
"engines": {
"vscode": "^1.102.0"
},
"main": "../out/extension.js",
"devDependencies": {
"@types/mocha": "^10.0.10",
"@types/node": "20.x",
"@types/vscode": "^1.102.0",
"@typescript-eslint/eslint-plugin": "^8.31.1",
"@typescript-eslint/parser": "^8.31.1",
"@vscode/test-cli": "^0.0.11",
"@vscode/test-electron": "^2.5.2",
"eslint": "^9.25.1",
"ts-loader": "^9.5.2",
"typescript": "^5.8.3",
"webpack": "^5.99.7",
"webpack-cli": "^6.0.1"
},
"dependencies": {
"vscode-languageclient": "^9.0.1"
}
}

View File

@ -0,0 +1,72 @@
/* --------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import * as path from 'path';
import { workspace, ExtensionContext, window } from 'vscode';
import {
Executable,
LanguageClient,
LanguageClientOptions,
ServerOptions,
TransportKind
} from 'vscode-languageclient/node';
let client: LanguageClient;
export function activate(context: ExtensionContext) {
const traceOutputChannel = window.createOutputChannel("Reid Language Server trace");
const command = process.env.SERVER_PATH || "reid-language-server";
const run: Executable = {
command,
options: {
env: {
...process.env,
RUST_LOG: "debug",
}
}
};
const serverOptions: ServerOptions = {
run,
debug: run,
};
// Options to control the language client
const clientOptions: LanguageClientOptions = {
// Register the server for plain text documents
documentSelector: [{ scheme: 'file', language: 'reid' }],
synchronize: {
// Notify the server about file changes to '.clientrc files contained in the workspace
fileEvents: workspace.createFileSystemWatcher('**/.clientrc')
}
};
// Create the language client and start the client.
client = new LanguageClient(
'reid-lsp',
'Reid Language Server',
serverOptions,
clientOptions
);
client.info("hello");
workspace.onDidOpenTextDocument((e) => {
});
// Start the client. This will also launch the server
client.start();
}
export function deactivate(): Thenable<void> | undefined {
if (!client) {
return undefined;
}
return client.stop();
}

View File

@ -0,0 +1,15 @@
import * as assert from 'assert';
// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as vscode from 'vscode';
// import * as myExtension from '../../extension';
suite('Extension Test Suite', () => {
vscode.window.showInformationMessage('Start all tests.');
test('Sample test', () => {
assert.strictEqual(-1, [1, 2, 3].indexOf(5));
assert.strictEqual(-1, [1, 2, 3].indexOf(0));
});
});

View File

@ -0,0 +1,24 @@
{
"compilerOptions": {
"module": "Node16",
"target": "ES2022",
"lib": [
"ES2022"
],
"sourceMap": true,
"rootDir": "src",
"outDir": "../dist",
"strict": true /* enable all strict type-checking options */
/* Additional Checks */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
},
"include": [
"src"
],
"exclude": [
"node_modules",
".vscode-test"
]
}

View File

@ -0,0 +1,28 @@
import typescriptEslint from "@typescript-eslint/eslint-plugin";
import tsParser from "@typescript-eslint/parser";
export default [{
files: ["**/*.ts"],
}, {
plugins: {
"@typescript-eslint": typescriptEslint,
},
languageOptions: {
parser: tsParser,
ecmaVersion: 2022,
sourceType: "module",
},
rules: {
"@typescript-eslint/naming-convention": ["warn", {
selector: "import",
format: ["camelCase", "PascalCase"],
}],
curly: "warn",
eqeqeq: "warn",
"no-throw-literal": "warn",
semi: "warn",
},
}];

84
reid-lsp/package.json Normal file
View File

@ -0,0 +1,84 @@
{
"name": "reid-lsp",
"displayName": "Reid Language Server",
"description": "Language Server Extension for Reid",
"version": "0.0.1",
"engines": {
"vscode": "^1.102.0"
},
"categories": [
"Other"
],
"activationEvents": [
"onLanguage:reid"
],
"main": "./dist/extension.js",
"contributes": {
"languages": [
{
"id": "reid",
"extensions": [
".reid"
]
}
],
"configuration": {
"type": "object",
"title": "reid-language-server",
"properties": {
"nrs-language-server.trace.server": {
"type": "string",
"scope": "window",
"enum": [
"off",
"messages",
"verbose"
],
"enumDescriptions": [
"No traces",
"Error only",
"Full log"
],
"default": "off",
"description": "Traces the communication between VS Code and the language server."
}
}
},
"grammars": [
{
"language": "reid",
"scopeName": "source.reid",
"path": "./syntaxes/grammar.json"
}
]
},
"scripts": {
"vscode:prepublish": "pnpm run package",
"compile": "npx js-yaml syntaxes/grammar.yaml > syntaxes/grammar.json && webpack",
"watch": "webpack --watch",
"package": "webpack --mode production --devtool hidden-source-map",
"compile-tests": "tsc -p . --outDir out",
"watch-tests": "tsc -p . -w --outDir out",
"pretest": "pnpm run compile-tests && pnpm run compile && pnpm run lint",
"lint": "eslint src",
"test": "vscode-test"
},
"devDependencies": {
"@types/mocha": "^10.0.10",
"@types/node": "20.x",
"@types/vscode": "^1.102.0",
"@typescript-eslint/eslint-plugin": "^8.31.1",
"@typescript-eslint/parser": "^8.31.1",
"@vscode/test-cli": "^0.0.11",
"@vscode/test-electron": "^2.5.2",
"eslint": "^9.25.1",
"js-yaml": "^4.1.0",
"ts-loader": "^9.5.2",
"typescript": "^5.8.3",
"webpack": "^5.99.7",
"webpack-cli": "^6.0.1"
},
"dependencies": {
"vscode-languageclient": "^9.0.1"
}
}

490
reid-lsp/src/main.rs Normal file
View File

@ -0,0 +1,490 @@
use std::collections::HashMap;
use std::path::PathBuf;
use dashmap::DashMap;
use reid::ast::lexer::{FullToken, Position};
use reid::error_raporting::{ErrorModules, ReidError};
use reid::mir::{
self, Context, FunctionCall, FunctionDefinition, FunctionParam, IfExpression, SourceModuleId, StructType, TypeKind,
WhileStatement,
};
use reid::{compile_module, parse_module, perform_all_passes};
use tower_lsp::lsp_types::{
self, CompletionItem, CompletionOptions, CompletionParams, CompletionResponse, Diagnostic, DiagnosticSeverity,
DidChangeTextDocumentParams, DidOpenTextDocumentParams, Hover, HoverContents, HoverParams, HoverProviderCapability,
InitializeParams, InitializeResult, InitializedParams, MarkedString, MarkupContent, MarkupKind, MessageType, OneOf,
Range, ServerCapabilities, TextDocumentItem, TextDocumentSyncCapability, TextDocumentSyncKind,
TextDocumentSyncOptions, WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities,
};
use tower_lsp::{Client, LanguageServer, LspService, Server, jsonrpc};
#[derive(Debug)]
struct Backend {
client: Client,
tokens: DashMap<String, Vec<FullToken>>,
ast: DashMap<String, reid::ast::Module>,
types: DashMap<String, DashMap<FullToken, Option<TypeKind>>>,
}
#[tower_lsp::async_trait]
impl LanguageServer for Backend {
async fn initialize(&self, _: InitializeParams) -> jsonrpc::Result<InitializeResult> {
self.client
.log_message(MessageType::INFO, "Initializing Reid Language Server")
.await;
let sync = TextDocumentSyncOptions {
open_close: Some(true),
change: Some(TextDocumentSyncKind::FULL),
will_save: None,
will_save_wait_until: None,
save: None,
};
Ok(InitializeResult {
capabilities: ServerCapabilities {
hover_provider: Some(HoverProviderCapability::Simple(true)),
completion_provider: Some(CompletionOptions { ..Default::default() }),
text_document_sync: Some(TextDocumentSyncCapability::Options(sync)),
workspace: Some(WorkspaceServerCapabilities {
workspace_folders: Some(WorkspaceFoldersServerCapabilities {
supported: Some(true),
change_notifications: Some(OneOf::Left(true)),
}),
file_operations: None,
}),
..Default::default()
},
..Default::default()
})
}
async fn initialized(&self, _: InitializedParams) {
self.client
.log_message(MessageType::INFO, "Reid Language Server initialized hello!")
.await;
}
async fn shutdown(&self) -> jsonrpc::Result<()> {
Ok(())
}
async fn completion(&self, params: CompletionParams) -> jsonrpc::Result<Option<CompletionResponse>> {
Ok(Some(CompletionResponse::Array(vec![
CompletionItem::new_simple("Hello".to_string(), "Some detail".to_string()),
CompletionItem::new_simple("Bye".to_string(), "More detail".to_string()),
])))
}
async fn hover(&self, params: HoverParams) -> jsonrpc::Result<Option<Hover>> {
let path = PathBuf::from(params.text_document_position_params.text_document.uri.path());
let file_name = path.file_name().unwrap().to_str().unwrap().to_owned();
let tokens = self.tokens.get(&file_name);
let position = params.text_document_position_params.position;
let token = if let Some(tokens) = &tokens {
tokens.iter().find(|tok| {
tok.position.1 == position.line + 1
&& (tok.position.0 <= position.character + 1
&& (tok.position.0 + tok.token.len() as u32) > position.character + 1)
})
} else {
None
};
let (range, ty) = if let Some(token) = token {
if let Some(possible_ty) = self.types.get(&file_name).unwrap().get(token) {
let start = token.position;
let end = token.position.add(token.token.len() as u32);
let range = Range {
start: lsp_types::Position {
line: (start.1 as i32 - 1).max(0) as u32,
character: (start.0 as i32 - 1).max(0) as u32,
},
end: lsp_types::Position {
line: (end.1 as i32 - 1).max(0) as u32,
character: (end.0 as i32 - 1).max(0) as u32,
},
};
if let Some(ty) = possible_ty.clone() {
(Some(range), format!("{}", ty))
} else {
(Some(range), String::from("no type"))
}
} else {
(None, String::from("no token"))
}
} else {
(None, String::from("no token"))
};
let contents = HoverContents::Markup(MarkupContent {
kind: MarkupKind::Markdown,
value: format!("`{ty}`"),
});
Ok(Some(Hover { contents, range }))
}
async fn did_open(&self, params: DidOpenTextDocumentParams) {
self.recompile(TextDocumentItem {
uri: params.text_document.uri,
language_id: params.text_document.language_id,
version: params.text_document.version,
text: params.text_document.text,
})
.await
}
async fn did_change(&self, params: DidChangeTextDocumentParams) {
self.recompile(TextDocumentItem {
text: params.content_changes[0].text.clone(),
uri: params.text_document.uri,
version: params.text_document.version,
language_id: String::new(),
})
.await
}
}
impl Backend {
async fn recompile(&self, params: TextDocumentItem) {
let path = PathBuf::from(params.uri.clone().path());
let file_name = path.file_name().unwrap().to_str().unwrap().to_owned();
let mut map = Default::default();
let parse_res = parse(&params.text, path.clone(), &mut map);
let (tokens, result) = match parse_res {
Ok((module_id, tokens)) => (tokens.clone(), compile(module_id, tokens, path, &mut map)),
Err(e) => (Vec::new(), Err(e)),
};
let mut diagnostics = Vec::new();
match result {
Ok(Some(result)) => {
self.tokens.insert(file_name.clone(), result.tokens);
self.types.insert(file_name.clone(), result.types);
}
Ok(_) => {}
Err(mut reid_error) => {
reid_error.errors.dedup();
for error in reid_error.errors {
let meta = error.get_meta();
let positions = meta
.range
.into_position(&tokens)
.unwrap_or((Position(0, 0), Position(0, 0)));
self.client.log_message(MessageType::INFO, format!("{:?}", &meta)).await;
self.client
.log_message(MessageType::INFO, format!("{:?}", &positions))
.await;
diagnostics.push(Diagnostic {
range: Range {
start: lsp_types::Position {
line: ((positions.0.1 as i32) - 1).max(0) as u32,
character: ((positions.0.0 as i32) - 1).max(0) as u32,
},
end: lsp_types::Position {
line: ((positions.1.1 as i32) - 1).max(0) as u32,
character: ((positions.1.0 as i32) - 1).max(0) as u32,
},
},
severity: Some(DiagnosticSeverity::ERROR),
code: None,
code_description: None,
source: Some(error.get_type_str().to_owned()),
message: format!("{}", error),
related_information: None,
tags: None,
data: None,
});
self.client.log_message(MessageType::INFO, format!("{}", error)).await;
}
}
}
self.client
.publish_diagnostics(params.uri.clone(), diagnostics, Some(params.version))
.await;
}
}
struct CompileResult {
tokens: Vec<FullToken>,
types: DashMap<FullToken, Option<TypeKind>>,
}
fn parse(source: &str, path: PathBuf, map: &mut ErrorModules) -> Result<(SourceModuleId, Vec<FullToken>), ReidError> {
let file_name = path.file_name().unwrap().to_str().unwrap().to_owned();
Ok(parse_module(source, file_name.clone(), map)?)
}
fn compile(
module_id: SourceModuleId,
tokens: Vec<FullToken>,
path: PathBuf,
map: &mut ErrorModules,
) -> Result<Option<CompileResult>, ReidError> {
let token_types = DashMap::new();
let module = compile_module(module_id, tokens, map, Some(path.clone()), true)?;
let module_id = module.module_id;
let mut context = Context::from(vec![module], path.parent().unwrap().to_owned());
perform_all_passes(&mut context, map)?;
for module in context.modules.into_values() {
if module.module_id != module_id {
continue;
}
for (idx, token) in module.tokens.iter().enumerate() {
token_types.insert(token.clone(), find_type_in_context(&module, idx));
}
return Ok(Some(CompileResult {
tokens: module.tokens,
types: token_types,
}));
}
return Ok(None);
}
#[tokio::main]
async fn main() {
let stdin = tokio::io::stdin();
let stdout = tokio::io::stdout();
let (service, socket) = LspService::new(|client| Backend {
client,
ast: DashMap::new(),
tokens: DashMap::new(),
types: DashMap::new(),
});
Server::new(stdin, stdout, socket).serve(service).await;
}
pub fn find_type_in_context(module: &mir::Module, token_idx: usize) -> Option<TypeKind> {
for import in &module.imports {
if import.1.contains(token_idx) {
return None;
}
}
for typedef in &module.typedefs {
if !typedef.meta.contains(token_idx) {
continue;
}
match &typedef.kind {
mir::TypeDefinitionKind::Struct(StructType(fields)) => {
for field in fields {
if field.2.contains(token_idx) {
return Some(field.1.clone());
}
}
}
}
}
for binop in &module.binop_defs {
if let Some(meta) = binop.block_meta() {
if !meta.contains(token_idx) {
continue;
}
} else {
continue;
}
return match &binop.fn_kind {
mir::FunctionDefinitionKind::Local(block, _) => find_type_in_block(&block, module.module_id, token_idx),
mir::FunctionDefinitionKind::Extern(_) => None,
mir::FunctionDefinitionKind::Intrinsic(_) => None,
};
}
for (_, function) in &module.associated_functions {
if !(function.signature() + function.block_meta()).contains(token_idx) {
continue;
}
for param in &function.parameters {
if param.meta.contains(token_idx) {
return Some(param.ty.clone());
}
}
return match &function.kind {
mir::FunctionDefinitionKind::Local(block, _) => find_type_in_block(&block, module.module_id, token_idx),
mir::FunctionDefinitionKind::Extern(_) => None,
mir::FunctionDefinitionKind::Intrinsic(_) => None,
};
}
for function in &module.functions {
if !(function.signature() + function.block_meta()).contains(token_idx) {
continue;
}
for param in &function.parameters {
if param.meta.contains(token_idx) {
return Some(param.ty.clone());
}
}
return match &function.kind {
mir::FunctionDefinitionKind::Local(block, _) => find_type_in_block(&block, module.module_id, token_idx),
mir::FunctionDefinitionKind::Extern(_) => None,
mir::FunctionDefinitionKind::Intrinsic(_) => None,
};
}
None
}
pub fn find_type_in_block(block: &mir::Block, module_id: SourceModuleId, token_idx: usize) -> Option<TypeKind> {
if !block.meta.contains(token_idx) {
return None;
}
for statement in &block.statements {
if !statement.1.contains(token_idx) {
continue;
}
match &statement.0 {
mir::StmtKind::Let(named_variable_ref, _, expression) => {
if named_variable_ref.2.contains(token_idx) {
return expression
.return_type(&Default::default(), module_id)
.ok()
.map(|(_, ty)| ty);
} else {
return find_type_in_expr(&expression, module_id, token_idx);
}
}
mir::StmtKind::Set(lhs, rhs) => {
return find_type_in_expr(lhs, module_id, token_idx).or(find_type_in_expr(rhs, module_id, token_idx));
}
mir::StmtKind::Import(_) => {}
mir::StmtKind::Expression(expression) => return find_type_in_expr(expression, module_id, token_idx),
mir::StmtKind::While(WhileStatement { condition, block, .. }) => {
return find_type_in_expr(condition, module_id, token_idx)
.or(find_type_in_block(block, module_id, token_idx));
}
}
}
if let Some((_, Some(return_exp))) = &block.return_expression {
if let Some(ty) = find_type_in_expr(return_exp, module_id, token_idx) {
return Some(ty);
}
}
None
}
pub fn find_type_in_expr(expr: &mir::Expression, module_id: SourceModuleId, token_idx: usize) -> Option<TypeKind> {
if !expr.1.contains(token_idx) {
return None;
}
match &expr.0 {
mir::ExprKind::Variable(named_variable_ref) => Some(named_variable_ref.0.clone()),
mir::ExprKind::Indexed(value, type_kind, index_expr) => Some(
find_type_in_expr(&value, module_id, token_idx)
.or(find_type_in_expr(&index_expr, module_id, token_idx))
.unwrap_or(type_kind.clone()),
),
mir::ExprKind::Accessed(expression, type_kind, _, meta) => {
if meta.contains(token_idx) {
Some(type_kind.clone())
} else {
find_type_in_expr(&expression, module_id, token_idx)
}
}
mir::ExprKind::Array(expressions) => {
for expr in expressions {
if let Some(ty) = find_type_in_expr(expr, module_id, token_idx) {
return Some(ty);
}
}
None
}
mir::ExprKind::Struct(name, items) => {
for (_, expr, meta) in items {
if meta.contains(token_idx) {
return expr.return_type(&Default::default(), module_id).map(|(_, t)| t).ok();
}
if let Some(ty) = find_type_in_expr(expr, module_id, token_idx) {
return Some(ty);
}
}
Some(TypeKind::CustomType(mir::CustomTypeKey(name.clone(), module_id)))
}
mir::ExprKind::Literal(literal) => Some(literal.as_type()),
mir::ExprKind::BinOp(_, lhs, rhs, type_kind) => {
if let Some(ty) = find_type_in_expr(lhs, module_id, token_idx) {
return Some(ty);
}
if let Some(ty) = find_type_in_expr(rhs, module_id, token_idx) {
return Some(ty);
}
Some(type_kind.clone())
}
mir::ExprKind::FunctionCall(FunctionCall {
return_type,
parameters,
..
}) => {
for expr in parameters {
if let Some(ty) = find_type_in_expr(expr, module_id, token_idx) {
return Some(ty);
}
}
Some(return_type.clone())
}
mir::ExprKind::AssociatedFunctionCall(
_,
FunctionCall {
return_type,
parameters,
..
},
) => {
for expr in parameters {
if let Some(ty) = find_type_in_expr(expr, module_id, token_idx) {
return Some(ty);
}
}
Some(return_type.clone())
}
mir::ExprKind::If(IfExpression(cond, then_e, else_e)) => find_type_in_expr(&cond, module_id, token_idx)
.or(find_type_in_expr(&then_e, module_id, token_idx))
.or(else_e.clone().and_then(|e| find_type_in_expr(&e, module_id, token_idx))),
mir::ExprKind::Block(block) => find_type_in_block(block, module_id, token_idx),
mir::ExprKind::Borrow(expression, mutable) => {
if let Some(ty) = find_type_in_expr(&expression, module_id, token_idx) {
return Some(ty);
}
if let Ok(inner) = expression.return_type(&Default::default(), module_id).map(|(_, ty)| ty) {
Some(TypeKind::Borrow(Box::new(inner.clone()), *mutable))
} else {
None
}
}
mir::ExprKind::Deref(expression) => {
if let Some(ty) = find_type_in_expr(&expression, module_id, token_idx) {
return Some(ty);
}
if let Ok(TypeKind::Borrow(inner, _)) =
expression.return_type(&Default::default(), module_id).map(|(_, ty)| ty)
{
Some(*inner.clone())
} else {
None
}
}
mir::ExprKind::CastTo(expression, type_kind) => {
Some(find_type_in_expr(&expression, module_id, token_idx).unwrap_or(type_kind.clone()))
}
mir::ExprKind::GlobalRef(_, type_kind) => Some(type_kind.clone()),
}
}

View File

@ -0,0 +1,395 @@
{
"scopeName": "source.reid",
"patterns": [
{
"include": "#import"
},
{
"include": "#expression"
}
],
"repository": {
"import": {
"begin": "(import)\\s*",
"end": ";",
"beginCaptures": {
"1": {
"name": "keyword"
}
},
"endCaptures": {
"0": {
"name": "punctuation.semi.reid"
}
},
"patterns": [
{
"include": "#identifier"
},
{
"include": "#punctiation"
}
]
},
"punctuation": {
"patterns": [
{
"match": "::",
"name": "keyword.operator.namespace.reid"
},
{
"match": ";",
"name": "punctuation.semi.reid"
},
{
"match": ".",
"name": "punctuation.dot.reid"
},
{
"match": ",",
"name": "punctuation.comma.reid"
}
]
},
"expression": {
"patterns": [
{
"include": "#comment"
},
{
"include": "#fn-signature"
},
{
"include": "#common-type"
},
{
"include": "#binop-impl"
},
{
"include": "#type-impl"
},
{
"include": "#struct-definition"
},
{
"include": "#block"
},
{
"include": "#binop"
},
{
"include": "#namespace"
},
{
"include": "#cast"
},
{
"include": "#function-call"
},
{
"include": "#parenthesis"
},
{
"include": "#array"
},
{
"include": "#keywords"
},
{
"include": "#struct-expression"
},
{
"include": "#number-literal"
},
{
"include": "#string-literal"
},
{
"include": "#identifier"
},
{
"include": "#punctuation"
}
]
},
"comment": {
"match": "\\/\\/(.|\\/)*",
"name": "comment.line.double-slash.reid"
},
"fn-signature": {
"begin": "(fn)\\s*(\\w+)\\(",
"beginCaptures": {
"1": {
"name": "keyword.fn.reid"
},
"2": {
"name": "entity.name.function.reid"
}
},
"end": "\\)",
"patterns": [
{
"include": "#annotated-identifier"
},
{
"include": "#keywords"
},
{
"include": "#binop"
}
],
"endCaptures": {
"2": {
"name": "entity.name.type.reid"
}
}
},
"type-impl": {
"begin": "(impl)\\s* (\\w+)\\s* \\{\n",
"end": "\\}",
"captures": {
"1": {
"name": "keyword.impl.reid"
},
"2": {
"name": "entity.name.type"
}
},
"patterns": [
{
"include": "#expression"
}
]
},
"binop-impl": {
"begin": "(impl)\\s+(binop)\\s+\\(((.*)\\s*:\\s*(.*))\\)(.*)\\(((.*)\\s*:\\s*(.*))\\)\\s*->\\s*(\\w+)\\s*\\{",
"end": "\\}",
"beginCaptures": {
"1": {
"name": "keyword.impl.reid"
},
"2": {
"name": "keyword.impl.reid"
},
"4": {
"name": "variable.parameter.binop.reid"
},
"5": {
"name": "entity.name.type.parameter.binop.reid"
},
"6": {
"name": "keyword.operator.math.reid"
},
"8": {
"name": "variable.parameter.binop.reid"
},
"9": {
"name": "entity.name.type.parameter.binop.reid"
},
"10": {
"name": "entity.name.type.return.binop.reid"
}
},
"patterns": [
{
"include": "#expression"
}
]
},
"struct-definition": {
"begin": "(struct)\\s*(\\w+)\\s*\\{",
"end": "\\}",
"captures": {
"1": {
"name": "keyword.struct.reid"
},
"2": {
"name": "entity.name.type"
}
},
"patterns": [
{
"include": "#annotated-identifier"
}
]
},
"struct-expression": {
"begin": "([A-Z]\\w*)\\s*\\{",
"end": "\\}",
"captures": {
"1": {
"name": "entity.name.type.struct.reid"
}
},
"patterns": [
{
"include": "#expression"
}
]
},
"number-literal": {
"patterns": [
{
"match": "0x[0-9a-fA-F]+(\\.[0-9a-fA-F]+)?",
"name": "constant.hexadecimal"
},
{
"match": "0o[0-7]+(\\.[0-7]+)?",
"name": "constant.octal"
},
{
"match": "0b[01]+(\\.[01]+)?",
"name": "constant.binary"
},
{
"match": "[0-9]+(\\.[0-9]+)?",
"name": "constant.numeric"
}
]
},
"string-literal": {
"begin": "\"",
"end": "\"",
"name": "string.quoted.double",
"patterns": [
{
"match": "\\.",
"name": "constant.character.escape"
}
]
},
"block": {
"begin": "\\{",
"end": "\\}",
"patterns": [
{
"include": "#expression"
}
]
},
"namespace": {
"match": "(\\w+)(\\:\\:)",
"captures": {
"1": {
"name": "entity.name.function.reid"
},
"2": {
"name": "keyword.operator.namespace.reid"
}
}
},
"cast": {
"match": "(as)\\s+(\\w+)",
"captures": {
"1": {
"name": "keyword.cast.reid"
},
"2": {
"name": "entity.name.type.reid"
}
}
},
"function-call": {
"begin": "(\\w+)?\\(",
"end": "\\)",
"beginCaptures": {
"1": {
"name": "entity.name.function.reid"
}
},
"patterns": [
{
"include": "#expression"
}
]
},
"parenthesis": {
"begin": "\\(",
"end": "\\)",
"beginCaptures": {
"0": {
"name": "keyword.operator.parenthesis.reid"
}
},
"endCaptures": {
"0": {
"name": "keyword.operator.parenthesis.reid"
}
},
"patterns": [
{
"include": "#expression"
}
]
},
"annotated-identifier": {
"begin": "(\\w+)\\:",
"end": ",",
"beginCaptures": {
"1": {
"name": "variable.language.reid"
}
},
"patterns": [
{
"include": "#expression"
}
]
},
"identifier": {
"patterns": [
{
"match": "[A-Z]\\w*",
"name": "entity.name.type.reid"
},
{
"match": "\\w+",
"name": "variable.language.reid"
}
]
},
"keywords": {
"patterns": [
{
"match": "let|mut|pub|extern",
"name": "storage.type.reid"
},
{
"match": "if|return",
"name": "keyword.control"
},
{
"match": "self",
"name": "variable.language.self.reid"
}
]
},
"binop": {
"match": "\\<\\=|\\>\\=|\\=\\=|\\<|\\>|\\*|\\+|\\-|\\^|\\&\\&|\\&",
"name": "keyword.operator.math.reid"
},
"array": {
"begin": "\\[",
"end": "\\]",
"beginCaptures": {
"0": {
"name": "entity.name.type.array.reid"
}
},
"endCaptures": {
"0": {
"name": "entity.name.type.array.reid"
}
},
"patterns": [
{
"include": "#expression"
}
]
},
"common-type": {
"match": "u8|u16|u32|u64|u128|i8|i16|i32|i64|i128|bool",
"name": "entity.name.type.common.reid"
}
}
}

View File

@ -0,0 +1,232 @@
scopeName: source.reid
patterns:
- include: "#import"
- include: "#expression"
repository:
# function-definition:
# begin: "(fn)\\s*(\\w+)\\(((\\w+)\\s*\\:\\s*(\\w+),?)*\\)\\s*->\\s*(\\w+)\\s*\\{"
# end: "\\}"
# beginCaptures:
# 1:
# name: "keyword.other"
# 2:
# name: "entity.name.function"
# 4:
# name: "entity.name.parameter"
# 5:
# name: "entity.name.type"
# 6:
# name: "entity.name.type"
# patterns:
# - include: "#type"
# - include: "#expression"
import:
begin: "(import)\\s*"
end: ";"
beginCaptures:
1:
name: keyword
endCaptures:
0:
name: punctuation.semi.reid
patterns:
- include: "#identifier"
- include: "#punctiation"
punctuation:
patterns:
- match: "::"
name: keyword.operator.namespace.reid
- match: ";"
name: punctuation.semi.reid
- match: "."
name: punctuation.dot.reid
- match: ","
name: punctuation.comma.reid
expression:
patterns:
- include: "#comment"
- include: "#fn-signature"
- include: "#common-type"
- include: "#binop-impl"
- include: "#type-impl"
- include: "#struct-definition"
- include: "#block"
- include: "#binop"
- include: "#namespace"
- include: "#cast"
- include: "#function-call"
- include: "#parenthesis"
- include: "#array"
- include: "#keywords"
- include: "#struct-expression"
- include: "#number-literal"
- include: "#string-literal"
- include: "#identifier"
- include: "#punctuation"
comment:
match: "\\/\\/(.|\\/)*"
name: comment.line.double-slash.reid
fn-signature:
begin: "(fn)\\s*(\\w+)\\("
beginCaptures:
1:
name: keyword.fn.reid
2:
name: entity.name.function.reid
end: "\\)"
patterns:
- include: "#annotated-identifier"
- include: "#keywords"
- include: "#binop"
endCaptures:
2:
name: entity.name.type.reid
type-impl:
begin: >
(impl)\s*
(\w+)\s*
\{
end: "\\}"
captures:
1:
name: keyword.impl.reid
2:
name: entity.name.type
patterns:
- include: "#expression"
binop-impl:
begin: "(impl)\\s+(binop)\\s+\\(((.*)\\s*:\\s*(.*))\\)(.*)\\(((.*)\\s*:\\s*(.*))\\)\\s*->\\s*(\\w+)\\s*\\{"
end: "\\}"
beginCaptures:
1:
name: keyword.impl.reid
2:
name: keyword.impl.reid
4:
name: variable.parameter.binop.reid
5:
name: entity.name.type.parameter.binop.reid
6:
name: keyword.operator.math.reid
8:
name: variable.parameter.binop.reid
9:
name: entity.name.type.parameter.binop.reid
10:
name: entity.name.type.return.binop.reid
patterns:
- include: "#expression"
struct-definition:
begin: "(struct)\\s*(\\w+)\\s*\\{"
end: "\\}"
captures:
1:
name: keyword.struct.reid
2:
name: entity.name.type
patterns:
- include: "#annotated-identifier"
struct-expression:
begin: "([A-Z]\\w*)\\s*\\{"
end: "\\}"
captures:
1:
name: entity.name.type.struct.reid
patterns:
- include: "#expression"
number-literal:
patterns:
- match: "0x[0-9a-fA-F]+(\\.[0-9a-fA-F]+)?"
name: "constant.hexadecimal"
- match: "0o[0-7]+(\\.[0-7]+)?"
name: "constant.octal"
- match: "0b[01]+(\\.[01]+)?"
name: "constant.binary"
- match: "[0-9]+(\\.[0-9]+)?"
name: "constant.numeric"
string-literal:
begin: '"'
end: '"'
name: string.quoted.double
patterns:
- match: "\\."
name: constant.character.escape
block:
begin: "\\{"
end: "\\}"
patterns:
- include: "#expression"
namespace:
match: "(\\w+)(\\:\\:)"
captures:
1:
name: entity.name.function.reid
2:
name: keyword.operator.namespace.reid
cast:
match: "(as)\\s+(\\w+)"
captures:
1:
name: keyword.cast.reid
2:
name: entity.name.type.reid
function-call:
begin: "(\\w+)?\\("
end: "\\)"
beginCaptures:
1:
name: entity.name.function.reid
patterns:
- include: "#expression"
parenthesis:
begin: "\\("
end: "\\)"
beginCaptures:
0:
name: keyword.operator.parenthesis.reid
endCaptures:
0:
name: keyword.operator.parenthesis.reid
patterns:
- include: "#expression"
annotated-identifier:
begin: "(\\w+)\\:"
end: ","
beginCaptures:
1:
name: variable.language.reid
patterns:
- include: "#expression"
identifier:
patterns:
- match: "[A-Z]\\w*"
name: entity.name.type.reid
- match: "\\w+"
name: variable.language.reid
keywords:
patterns:
- match: "let|mut|pub|extern"
name: "storage.type.reid"
- match: "if|return"
name: "keyword.control"
- match: "self"
name: "variable.language.self.reid"
binop:
match: "\\<\\=|\\>\\=|\\=\\=|\\<|\\>|\\*|\\+|\\-|\\^|\\&\\&|\\&"
name: keyword.operator.math.reid
array:
begin: "\\["
end: "\\]"
beginCaptures:
0:
name: entity.name.type.array.reid
endCaptures:
0:
name: entity.name.type.array.reid
patterns:
- include: "#expression"
common-type:
match: "u8|u16|u32|u64|u128|i8|i16|i32|i64|i128|bool"
name: entity.name.type.common.reid

29
reid-lsp/tsconfig.json Normal file
View File

@ -0,0 +1,29 @@
{
"compilerOptions": {
"module": "Node16",
"target": "ES2022",
"lib": [
"ES2022"
],
"sourceMap": true,
"rootDir": "src",
"outDir": "out",
"strict": true /* enable all strict type-checking options */
/* Additional Checks */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
},
"include": [
"src"
],
"exclude": [
"node_modules",
".vscode-test"
],
"references": [
{
"path": "./client/"
},
]
}

View File

@ -0,0 +1,48 @@
# Welcome to your VS Code Extension
## What's in the folder
* This folder contains all of the files necessary for your extension.
* `package.json` - this is the manifest file in which you declare your extension and command.
* The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesnt yet need to load the plugin.
* `src/extension.ts` - this is the main file where you will provide the implementation of your command.
* The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
* We pass the function containing the implementation of the command as the second parameter to `registerCommand`.
## Setup
* install the recommended extensions (amodio.tsl-problem-matcher, ms-vscode.extension-test-runner, and dbaeumer.vscode-eslint)
## Get up and running straight away
* Press `F5` to open a new window with your extension loaded.
* Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`.
* Set breakpoints in your code inside `src/extension.ts` to debug your extension.
* Find output from your extension in the debug console.
## Make changes
* You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`.
* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
## Explore the API
* You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`.
## Run tests
* Install the [Extension Test Runner](https://marketplace.visualstudio.com/items?itemName=ms-vscode.extension-test-runner)
* Run the "watch" task via the **Tasks: Run Task** command. Make sure this is running, or tests might not be discovered.
* Open the Testing view from the activity bar and click the Run Test" button, or use the hotkey `Ctrl/Cmd + ; A`
* See the output of the test result in the Test Results view.
* Make changes to `src/test/extension.test.ts` or create new test files inside the `test` folder.
* The provided test runner will only consider files matching the name pattern `**.test.ts`.
* You can create folders inside the `test` folder to structure your tests any way you want.
## Go further
* Reduce the extension size and improve the startup time by [bundling your extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension).
* [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VS Code extension marketplace.
* Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration).

View File

@ -0,0 +1,48 @@
//@ts-check
'use strict';
const path = require('path');
//@ts-check
/** @typedef {import('webpack').Configuration} WebpackConfig **/
/** @type WebpackConfig */
const extensionConfig = {
target: 'node', // VS Code extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/
mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production')
entry: './client/src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/
output: {
// the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/
path: path.resolve(__dirname, 'dist'),
filename: 'extension.js',
libraryTarget: 'commonjs2'
},
externals: {
vscode: 'commonjs vscode' // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/
// modules added here also need to be added in the .vscodeignore file
},
resolve: {
// support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader
extensions: ['.ts', '.js']
},
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: [
{
loader: 'ts-loader'
}
]
}
]
},
devtool: 'nosources-source-map',
infrastructureLogging: {
level: "log", // enables logging required for problem matchers
},
};
module.exports = [extensionConfig];

View File

@ -10,6 +10,8 @@ edition = "2021"
default = ["color"]
color = ["colored"]
log_output = []
context_debug = []
[dependencies]
## Make it easier to generate errors

View File

@ -12,7 +12,6 @@ fn main() -> Result<(), std::io::Error> {
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");
@ -21,6 +20,7 @@ fn main() -> Result<(), std::io::Error> {
let mir_path = parent.with_extension("mir");
let asm_path = parent.with_extension("asm");
#[cfg(feature = "log_output")]
let before = std::time::SystemTime::now();
let text = fs::read_to_string(&path)?;
@ -31,33 +31,39 @@ fn main() -> Result<(), std::io::Error> {
match compile_simple(&text, PathBuf::from(&path), Some(cpu), vec![features]) {
Ok((
CompileOutput {
triple,
triple: _triple,
assembly,
obj_buffer,
llvm_ir,
llvm_ir: _llvm_ir,
},
CustomIRs { llir, mir },
)) => {
println!("{}", llvm_ir);
#[cfg(feature = "log_output")]
{
println!("{}", _llvm_ir);
println!("Compiled with triple: {}\n", &_triple);
println!("Output LLVM IR to {:?}", llvm_ir_path);
println!("Output Assembly to {:?}", asm_path);
println!("Output Object-file to {:?}\n", object_path);
println!("Output LLIR-file to {:?}\n", llir_path);
println!("Output MIR-file to {:?}\n", mir_path);
}
let after = std::time::SystemTime::now();
println!("Compiled with triple: {}\n", &triple);
fs::write(&llvm_ir_path, &llvm_ir).expect("Could not write LLVM IR -file!");
println!("Output LLVM IR to {:?}", llvm_ir_path);
fs::write(&llvm_ir_path, &_llvm_ir).expect("Could not write LLVM IR -file!");
fs::write(&asm_path, &assembly).expect("Could not write Assembly-file!");
println!("Output Assembly to {:?}", asm_path);
fs::write(&object_path, &obj_buffer).expect("Could not write Object-file!");
println!("Output Object-file to {:?}\n", object_path);
fs::write(&llir_path, &llir).expect("Could not write LLIR-file!");
println!("Output LLIR-file to {:?}\n", llir_path);
fs::write(&mir_path, &mir).expect("Could not write MIR-file!");
println!("Output MIR-file to {:?}\n", mir_path);
println!(
"Compilation took: {:.2}ms\n",
(after.duration_since(before).unwrap().as_micros() as f32) / 1000.
);
#[cfg(feature = "log_output")]
{
let after = std::time::SystemTime::now();
println!(
"Compilation took: {:.2}ms\n",
(after.duration_since(before).unwrap().as_micros() as f32) / 1000.
);
println!("Linking {:?}", &object_path);
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");
@ -69,6 +75,7 @@ fn main() -> Result<(), std::io::Error> {
Err(e) => panic!("{}", e),
};
} else {
#[cfg(feature = "log_output")]
println!("Please input compiled file path!")
}
Ok(())

View File

@ -1,6 +1,5 @@
extern fn puts(message: *char) -> i32;
extern fn malloc(size: u64) -> *u8;
extern fn free(ptr: *u8);
extern fn div(numerator: i32, denominator: i32) -> div_t;
@ -14,7 +13,7 @@ struct String {
impl String {
pub fn new() -> String {
String {
inner: allocate(0),
inner: char::malloc(0),
length: 0,
max_length: 0,
must_be_freed: true,
@ -42,7 +41,7 @@ impl String {
pub fn add_char(&mut self, c: char) {
if ((*self).length + 1) >= (*self).max_length {
let new = allocate((*self).max_length + 4) as *char;
let new = char::malloc((*self).max_length + 4);
copy_bits((*self).inner, new, (*self).max_length);
if (*self).must_be_freed == true {
@ -120,13 +119,9 @@ pub fn int_div(numerator: i32, denominator: i32) -> div_t {
return div(numerator, denominator);
}
pub fn allocate(size: u64) -> *u8 {
malloc(size)
}
pub fn new_string() -> String {
String {
inner: allocate(0),
inner: char::malloc(0),
length: 0,
max_length: 0,
must_be_freed: true,
@ -148,7 +143,7 @@ pub fn from_str(str: *char) -> String {
pub fn add_char(string: &mut String, c: char) {
if ((*string).length + 1) >= (*string).max_length {
let new = allocate((*string).max_length + 4) as *char;
let new = char::malloc((*string).max_length + 4);
copy_bits((*string).inner, new, (*string).max_length);
if (*string).must_be_freed == true {

View File

@ -7,7 +7,7 @@ static HEXADECIMAL_NUMERICS: &[char] = &[
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
];
#[derive(Eq, PartialEq, Clone, PartialOrd, Ord)]
#[derive(Eq, PartialEq, Clone, PartialOrd, Ord, Hash)]
pub enum Token {
/// Values
Identifier(String),
@ -114,6 +114,8 @@ pub enum Token {
Unknown(char),
Whitespace(String),
Comment(String),
Eof,
}
@ -192,6 +194,8 @@ impl ToString for Token {
Token::Eof => String::new(),
Token::Slash => String::from('/'),
Token::Percent => String::from('%'),
Token::Whitespace(val) => val.clone(),
Token::Comment(val) => format!("//{}", val.clone()),
Token::Unknown(val) => val.to_string(),
}
}
@ -207,7 +211,7 @@ impl std::fmt::Debug for Token {
}
/// A token with a position
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FullToken {
pub token: Token,
pub position: Position,
@ -293,13 +297,25 @@ pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error
let variant = match character {
// Whitespace
w if w.is_whitespace() => continue,
w if w.is_whitespace() => {
let mut whitespace = String::from(*w);
while let Some(w) = cursor.first() {
if !w.is_whitespace() {
break;
}
whitespace.push(cursor.next().unwrap());
}
Token::Whitespace(whitespace)
}
// Comments
'/' if cursor.first() == Some('/') => {
let mut comment = String::new();
while !matches!(cursor.first(), Some('\n') | None) {
cursor.next();
if let Some(c) = cursor.next() {
comment.push(c);
}
}
continue;
Token::Comment(comment)
}
'\"' | '\'' => {
let mut value = String::new();

View File

@ -1,7 +1,7 @@
//! This is the module that contains relevant code to parsing Reid, that is to
//! say transforming a Vec of FullTokens into a loose parsed AST that can be
//! used for unwrapping syntax sugar, and then be transformed into Reid MIR.
use std::path::PathBuf;
use std::{fs::Metadata, path::PathBuf};
use token_stream::TokenRange;
@ -88,7 +88,7 @@ pub enum ExpressionKind {
/// Array-indexed, e.g. <expr>[<expr>]
Indexed(Box<Expression>, Box<Expression>),
/// Struct-accessed, e.g. <expr>.<expr>
Accessed(Box<Expression>, String),
Accessed(Box<Expression>, String, TokenRange),
/// Associated function call, but with a shorthand
AccessCall(Box<Expression>, Box<FunctionCallExpression>),
Binop(BinaryOperator, Box<Expression>, Box<Expression>),
@ -159,7 +159,12 @@ impl BinaryOperator {
}
#[derive(Debug, Clone)]
pub struct FunctionCallExpression(pub String, pub Vec<Expression>, pub TokenRange);
pub struct FunctionCallExpression {
pub name: String,
pub params: Vec<Expression>,
pub range: TokenRange,
pub is_macro: bool,
}
#[derive(Debug, Clone)]
pub struct IfExpression(
@ -188,7 +193,7 @@ pub struct FunctionDefinition(pub FunctionSignature, pub bool, pub Block, pub To
pub struct FunctionSignature {
pub name: String,
pub self_kind: SelfKind,
pub params: Vec<(String, Type)>,
pub params: Vec<(String, Type, TokenRange)>,
pub return_type: Option<Type>,
#[allow(dead_code)]
pub range: TokenRange,
@ -196,9 +201,9 @@ pub struct FunctionSignature {
#[derive(Debug, Clone)]
pub enum SelfKind {
Owned(TypeKind),
Borrow(TypeKind),
MutBorrow(TypeKind),
Owned(Type),
Borrow(Type),
MutBorrow(Type),
None,
}
@ -211,7 +216,7 @@ pub enum ReturnType {
#[derive(Debug, Clone)]
pub struct StructExpression {
name: String,
fields: Vec<(String, Expression)>,
fields: Vec<(String, Expression, TokenRange)>,
range: TokenRange,
}

View File

@ -191,10 +191,11 @@ where
),
expr.0 .1,
),
ExpressionKind::Accessed(value_expr, index_name) => Expression(
ExpressionKind::Accessed(value_expr, index_name, range) => Expression(
ExpressionKind::Accessed(
Box::new(apply_inner(PrimaryExpression(*value_expr.clone()), fun)),
index_name.clone(),
*range,
),
expr.0 .1,
),
@ -399,9 +400,9 @@ impl Parse for PrimaryExpression {
);
}
ValueIndex::Dot(val) => match val {
DotIndexKind::StructValueIndex(name) => {
DotIndexKind::StructValueIndex(name, range) => {
expr = Expression(
ExpressionKind::Accessed(Box::new(expr), name),
ExpressionKind::Accessed(Box::new(expr), name, range),
stream.get_range().unwrap(),
);
}
@ -537,8 +538,20 @@ impl Parse for BinaryOperator {
impl Parse for FunctionCallExpression {
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
if let Some(Token::Identifier(name)) = stream.next() {
let is_macro = if let Some(Token::Exclamation) = stream.peek() {
stream.next(); // Consume !
true
} else {
false
};
let args = stream.parse::<FunctionArgs>()?;
Ok(FunctionCallExpression(name, args.0, stream.get_range().unwrap()))
Ok(FunctionCallExpression {
name,
params: args.0,
range: stream.get_range().unwrap(),
is_macro,
})
} else {
Err(stream.expected_err("identifier")?)
}
@ -656,7 +669,7 @@ impl Parse for FunctionDefinition {
}
#[derive(Debug)]
struct FunctionParam(String, Type);
struct FunctionParam(String, Type, TokenRange);
impl Parse for FunctionParam {
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
@ -664,7 +677,7 @@ impl Parse for FunctionParam {
return Err(stream.expected_err("parameter name")?);
};
stream.expect(Token::Colon)?;
Ok(FunctionParam(arg_name, stream.parse()?))
Ok(FunctionParam(arg_name, stream.parse()?, stream.get_range().unwrap()))
}
}
@ -696,9 +709,18 @@ impl Parse for SelfParam {
};
if name == "self" {
match kind {
SelfParamKind::BorrowMut => Ok(SelfParam(SelfKind::MutBorrow(TypeKind::Unknown))),
SelfParamKind::Borrow => Ok(SelfParam(SelfKind::Borrow(TypeKind::Unknown))),
SelfParamKind::Owned => Ok(SelfParam(SelfKind::Owned(TypeKind::Unknown))),
SelfParamKind::BorrowMut => Ok(SelfParam(SelfKind::MutBorrow(Type(
TypeKind::Unknown,
stream.get_range_prev().unwrap(),
)))),
SelfParamKind::Borrow => Ok(SelfParam(SelfKind::Borrow(Type(
TypeKind::Unknown,
stream.get_range_prev().unwrap(),
)))),
SelfParamKind::Owned => Ok(SelfParam(SelfKind::Owned(Type(
TypeKind::Unknown,
stream.get_range_prev().unwrap(),
)))),
}
} else {
Err(stream.expected_err("self parameter")?)
@ -717,11 +739,11 @@ impl Parse for FunctionSignature {
match &self_kind {
SelfKind::None => {
if let Ok(param) = stream.parse::<FunctionParam>() {
params.push((param.0, param.1));
params.push((param.0, param.1, param.2));
while let Some(Token::Comma) = stream.peek() {
stream.next();
let param = stream.parse::<FunctionParam>()?;
params.push((param.0, param.1));
params.push((param.0, param.1, param.2));
}
}
}
@ -729,7 +751,7 @@ impl Parse for FunctionSignature {
while let Some(Token::Comma) = stream.peek() {
stream.next();
let param = stream.parse::<FunctionParam>()?;
params.push((param.0, param.1));
params.push((param.0, param.1, param.2));
}
}
}
@ -797,9 +819,10 @@ impl Parse for StructExpression {
let Some(Token::Identifier(name)) = stream.next() else {
return Err(stream.expected_err("struct identifier")?);
};
stream.expect(Token::BraceOpen)?;
let named_list = stream.parse::<NamedFieldList<Expression>>()?;
let fields = named_list.0.into_iter().map(|f| (f.0, f.1)).collect();
let fields = named_list.0.into_iter().map(|f| (f.0, f.1, f.2)).collect();
stream.expect(Token::BraceClose)?;
@ -876,7 +899,7 @@ impl Parse for ArrayValueIndex {
#[derive(Debug, Clone)]
pub enum DotIndexKind {
StructValueIndex(String),
StructValueIndex(String, TokenRange),
FunctionCall(FunctionCallExpression),
}
@ -885,13 +908,14 @@ impl Parse for DotIndexKind {
stream.expect(Token::Dot)?;
if let Some(Token::Identifier(name)) = stream.next() {
if let Ok(args) = stream.parse::<FunctionArgs>() {
Ok(Self::FunctionCall(FunctionCallExpression(
Ok(Self::FunctionCall(FunctionCallExpression {
name,
args.0,
stream.get_range_prev().unwrap(),
)))
params: args.0,
range: stream.get_range_prev().unwrap(),
is_macro: false,
}))
} else {
Ok(Self::StructValueIndex(name))
Ok(Self::StructValueIndex(name, stream.get_range().unwrap()))
}
} else {
return Err(stream.expected_err("struct index (number)")?);
@ -1091,9 +1115,9 @@ impl Parse for AssociatedFunctionBlock {
Some(Token::FnKeyword) | Some(Token::PubKeyword) => {
let mut fun: FunctionDefinition = stream.parse()?;
fun.0.self_kind = match fun.0.self_kind {
SelfKind::Owned(_) => SelfKind::Owned(ty.0.clone()),
SelfKind::Borrow(_) => SelfKind::Borrow(ty.0.clone()),
SelfKind::MutBorrow(_) => SelfKind::MutBorrow(ty.0.clone()),
SelfKind::Owned(_) => SelfKind::Owned(ty.clone()),
SelfKind::Borrow(_) => SelfKind::Borrow(ty.clone()),
SelfKind::MutBorrow(_) => SelfKind::MutBorrow(ty.clone()),
SelfKind::None => SelfKind::None,
};
functions.push(fun);

View File

@ -3,8 +3,8 @@ use std::path::PathBuf;
use crate::{
ast::{self},
mir::{
self, CustomTypeKey, ModuleMap, NamedVariableRef, ReturnKind, SourceModuleId, StmtKind, StructField,
StructType, WhileStatement,
self, CustomTypeKey, FunctionParam, ModuleMap, NamedVariableRef, ReturnKind, SourceModuleId, StmtKind,
StructField, StructType, WhileStatement,
},
};
@ -48,9 +48,14 @@ impl ast::Module {
.params
.iter()
.cloned()
.map(|p| (p.0, p.1 .0.into_mir(module_id)))
.map(|p| mir::FunctionParam {
name: p.0,
ty: p.1 .0.into_mir(module_id),
meta: p.2.as_meta(module_id),
})
.collect(),
kind: mir::FunctionDefinitionKind::Extern(false),
source: Some(module_id),
};
functions.push(def);
}
@ -88,9 +93,17 @@ impl ast::Module {
signature_range,
}) => {
binops.push(mir::BinopDefinition {
lhs: (lhs.0.clone(), lhs.1 .0.into_mir(module_id)),
lhs: mir::FunctionParam {
name: lhs.0.clone(),
ty: lhs.1 .0.into_mir(module_id),
meta: lhs.1 .1.as_meta(module_id),
},
op: op.mir(),
rhs: (rhs.0.clone(), rhs.1 .0.into_mir(module_id)),
rhs: mir::FunctionParam {
name: rhs.0.clone(),
ty: rhs.1 .0.into_mir(module_id),
meta: rhs.1 .1.as_meta(module_id),
},
return_type: return_ty.0.into_mir(module_id),
fn_kind: mir::FunctionDefinitionKind::Local(
block.into_mir(module_id),
@ -115,6 +128,7 @@ impl ast::Module {
imports,
associated_functions,
functions,
globals: Vec::new(),
path: self.path.clone(),
is_main: self.is_main,
tokens: self.tokens,
@ -129,25 +143,29 @@ impl ast::FunctionDefinition {
let mut params = Vec::new();
match &signature.self_kind {
ast::SelfKind::Borrow(type_kind) => params.push((
"self".to_owned(),
mir::TypeKind::Borrow(Box::new(type_kind.into_mir(module_id)), false),
)),
ast::SelfKind::MutBorrow(type_kind) => params.push((
"self".to_owned(),
mir::TypeKind::Borrow(Box::new(type_kind.into_mir(module_id)), true),
)),
ast::SelfKind::Owned(type_kind) => params.push(("self".to_owned(), type_kind.into_mir(module_id))),
ast::SelfKind::Borrow(ty) => params.push(mir::FunctionParam {
name: "self".to_owned(),
ty: mir::TypeKind::Borrow(Box::new(ty.0.into_mir(module_id)), false),
meta: ty.1.as_meta(module_id),
}),
ast::SelfKind::MutBorrow(ty) => params.push(mir::FunctionParam {
name: "self".to_owned(),
ty: mir::TypeKind::Borrow(Box::new(ty.0.into_mir(module_id)), true),
meta: ty.1.as_meta(module_id),
}),
ast::SelfKind::Owned(ty) => params.push(mir::FunctionParam {
name: "self".to_owned(),
ty: ty.0.into_mir(module_id),
meta: ty.1.as_meta(module_id),
}),
ast::SelfKind::None => {}
}
params.extend(
signature
.params
.iter()
.cloned()
.map(|p| (p.0, p.1 .0.into_mir(module_id))),
);
params.extend(signature.params.iter().cloned().map(|p| FunctionParam {
name: p.0,
ty: p.1 .0.into_mir(module_id),
meta: p.2.as_meta(module_id),
}));
mir::FunctionDefinition {
name: signature.name.clone(),
linkage_name: None,
@ -160,6 +178,7 @@ impl ast::FunctionDefinition {
.unwrap_or(mir::TypeKind::Void),
parameters: params,
kind: mir::FunctionDefinitionKind::Local(block.into_mir(module_id), (range).as_meta(module_id)),
source: Some(module_id),
}
}
}
@ -322,10 +341,11 @@ impl ast::Expression {
mir::TypeKind::Vague(mir::VagueType::Unknown),
),
ast::ExpressionKind::FunctionCall(fn_call_expr) => mir::ExprKind::FunctionCall(mir::FunctionCall {
name: fn_call_expr.0.clone(),
name: fn_call_expr.name.clone(),
return_type: mir::TypeKind::Vague(mir::VagueType::Unknown),
parameters: fn_call_expr.1.iter().map(|e| e.process(module_id)).collect(),
meta: fn_call_expr.2.as_meta(module_id),
parameters: fn_call_expr.params.iter().map(|e| e.process(module_id)).collect(),
meta: fn_call_expr.range.as_meta(module_id),
is_macro: fn_call_expr.is_macro,
}),
ast::ExpressionKind::BlockExpr(block) => mir::ExprKind::Block(block.into_mir(module_id)),
ast::ExpressionKind::IfExpr(if_expression) => {
@ -355,13 +375,14 @@ impl ast::Expression {
struct_init
.fields
.iter()
.map(|(n, e)| (n.clone(), e.process(module_id)))
.map(|(n, e, r)| (n.clone(), e.process(module_id), r.as_meta(module_id)))
.collect(),
),
ast::ExpressionKind::Accessed(expression, name) => mir::ExprKind::Accessed(
ast::ExpressionKind::Accessed(expression, name, name_range) => mir::ExprKind::Accessed(
Box::new(expression.process(module_id)),
mir::TypeKind::Vague(mir::VagueType::Unknown),
name.clone(),
name_range.as_meta(module_id),
),
ast::ExpressionKind::Borrow(expr, mutable) => {
mir::ExprKind::Borrow(Box::new(expr.process(module_id)), *mutable)
@ -409,14 +430,15 @@ impl ast::Expression {
ast::ExpressionKind::AssociatedFunctionCall(ty, fn_call_expr) => mir::ExprKind::AssociatedFunctionCall(
ty.0.into_mir(module_id),
mir::FunctionCall {
name: fn_call_expr.0.clone(),
name: fn_call_expr.name.clone(),
return_type: mir::TypeKind::Vague(mir::VagueType::Unknown),
parameters: fn_call_expr.1.iter().map(|e| e.process(module_id)).collect(),
meta: fn_call_expr.2.as_meta(module_id),
parameters: fn_call_expr.params.iter().map(|e| e.process(module_id)).collect(),
meta: fn_call_expr.range.as_meta(module_id),
is_macro: fn_call_expr.is_macro,
},
),
ast::ExpressionKind::AccessCall(expression, fn_call_expr) => {
let mut params: Vec<_> = fn_call_expr.1.iter().map(|e| e.process(module_id)).collect();
let mut params: Vec<_> = fn_call_expr.params.iter().map(|e| e.process(module_id)).collect();
params.insert(
0,
mir::Expression(
@ -424,13 +446,18 @@ impl ast::Expression {
expression.1.as_meta(module_id),
),
);
if fn_call_expr.is_macro {
panic!("Macros aren't supported as access-calls!");
};
mir::ExprKind::AssociatedFunctionCall(
mir::TypeKind::Vague(mir::VagueType::Unknown),
mir::FunctionCall {
name: fn_call_expr.0.clone(),
name: fn_call_expr.name.clone(),
return_type: mir::TypeKind::Vague(mir::VagueType::Unknown),
parameters: params,
meta: fn_call_expr.2.as_meta(module_id),
meta: fn_call_expr.range.as_meta(module_id),
is_macro: fn_call_expr.is_macro,
},
)
}

View File

@ -42,20 +42,18 @@ impl<'a, 'b> TokenStream<'a, 'b> {
/// Useful in conjunction with [`TokenStream::next`]
pub fn expecting_err<T: Into<String>>(&mut self, expected: T) -> Result<Error, Error> {
let next_token = self.peek().unwrap_or(Token::Eof);
let pos = self.next_token(self.position).0;
Ok(Error::Expected(
expected.into(),
next_token,
TokenRange {
start: self.position,
end: self.position,
},
TokenRange { start: pos, end: pos },
))
}
pub fn expect(&mut self, token: Token) -> Result<(), Error> {
if let Some(peeked) = self.peek() {
if token == peeked {
self.position += 1;
if let (pos, Some(peeked)) = self.next_token(self.position) {
if token == peeked.token {
self.position = pos + 1;
Ok(())
} else {
Err(self.expecting_err(token)?)
@ -66,37 +64,25 @@ impl<'a, 'b> TokenStream<'a, 'b> {
}
pub fn next(&mut self) -> Option<Token> {
let value = if self.tokens.len() < self.position {
None
} else {
Some(self.tokens[self.position].token.clone())
};
self.position += 1;
value
let (position, token) = self.next_token(self.position);
self.position = position + 1;
token.map(|t| t.token.clone())
}
pub fn previous(&mut self) -> Option<Token> {
if (self.position as i32 - 1) < 0 {
None
} else {
Some(self.tokens[self.position - 1].token.clone())
}
let (_, token) = self.previous_token(self.position);
token.map(|t| t.token.clone())
}
pub fn peek(&mut self) -> Option<Token> {
if self.tokens.len() < self.position {
None
} else {
Some(self.tokens[self.position].token.clone())
}
let (_, token) = self.next_token(self.position);
token.map(|t| t.token.clone())
}
pub fn peek2(&mut self) -> Option<Token> {
if self.tokens.len() < (self.position + 1) {
None
} else {
Some(self.tokens[self.position + 1].token.clone())
}
let (pos2, _) = self.next_token(self.position);
let (_, token) = self.next_token(pos2 + 1);
token.map(|t| t.token.clone())
}
/// Parse the next value of trait Parse. If the parse succeeded, the related
@ -185,9 +171,32 @@ impl<'a, 'b> TokenStream<'a, 'b> {
pub fn get_range_prev(&self) -> Option<TokenRange> {
self.ref_position.as_ref().map(|ref_pos| TokenRange {
start: **ref_pos,
end: self.position - 1,
end: self.previous_token(self.position).0,
})
}
fn previous_token(&self, mut from: usize) -> (usize, Option<&'a FullToken>) {
from -= 1;
while let Some(token) = self.tokens.get(from) {
if matches!(token.token, Token::Whitespace(_) | Token::Comment(_)) {
from -= 1;
} else {
break;
}
}
(from, self.tokens.get(from))
}
fn next_token(&self, mut from: usize) -> (usize, Option<&'a FullToken>) {
while let Some(token) = self.tokens.get(from) {
if matches!(token.token, Token::Whitespace(_) | Token::Comment(_)) {
from += 1;
} else {
break;
}
}
(from, self.tokens.get(from))
}
}
impl Drop for TokenStream<'_, '_> {
@ -217,8 +226,8 @@ impl std::ops::Add for TokenRange {
fn add(self, rhs: Self) -> Self::Output {
TokenRange {
start: self.start.min(rhs.start),
end: self.end.min(rhs.end),
start: self.start.min(rhs.start).min(rhs.end),
end: self.end.max(rhs.end).max(rhs.start),
}
}
}

View File

@ -2,12 +2,12 @@ use std::collections::HashMap;
use reid_lib::{
builder::{InstructionValue, TypeValue},
Block,
Block, Instr,
};
use mir::{CustomTypeKey, FunctionCall, FunctionDefinitionKind, IfExpression, TypeKind, WhileStatement};
use crate::mir;
use crate::mir::{self, FunctionParam, Metadata, SourceModuleId};
#[derive(Debug)]
pub struct Allocator {
@ -16,21 +16,18 @@ pub struct Allocator {
pub struct AllocatorScope<'ctx, 'a> {
pub(super) block: &'a mut Block<'ctx>,
pub(super) mod_id: SourceModuleId,
pub(super) type_values: &'a HashMap<CustomTypeKey, TypeValue>,
}
impl Allocator {
pub fn from(
func: &FunctionDefinitionKind,
params: &Vec<(String, TypeKind)>,
scope: &mut AllocatorScope,
) -> Allocator {
pub fn from(func: &FunctionDefinitionKind, params: &Vec<FunctionParam>, scope: &mut AllocatorScope) -> Allocator {
func.allocate(scope, params)
}
pub fn allocate(&mut self, name: &String, ty: &TypeKind) -> Option<InstructionValue> {
pub fn allocate(&mut self, meta: &Metadata, ty: &TypeKind) -> Option<InstructionValue> {
let mut allocs = self.allocations.iter().cloned().enumerate();
let val = allocs.find(|a| a.1 .0 == *name && a.1 .1 == *ty);
let val = allocs.find(|a| a.1 .0 == *meta && a.1 .1 == *ty);
if let Some((i, _)) = val {
self.allocations.remove(i);
}
@ -39,13 +36,13 @@ impl Allocator {
}
#[derive(Clone, Debug)]
pub struct Allocation(String, TypeKind, InstructionValue);
pub struct Allocation(Metadata, TypeKind, InstructionValue);
impl mir::FunctionDefinitionKind {
fn allocate<'ctx, 'a>(
&self,
scope: &mut AllocatorScope<'ctx, 'a>,
parameters: &Vec<(String, TypeKind)>,
parameters: &Vec<mir::FunctionParam>,
) -> Allocator {
let mut allocated = Vec::new();
match &self {
@ -54,11 +51,11 @@ impl mir::FunctionDefinitionKind {
let allocation = scope
.block
.build_named(
param.0.clone(),
reid_lib::Instr::Alloca(param.1.get_type(scope.type_values)),
param.name.clone(),
reid_lib::Instr::Alloca(param.ty.get_type(scope.type_values)),
)
.unwrap();
allocated.push(Allocation(param.0.clone(), param.1.clone(), allocation));
allocated.push(Allocation(param.meta, param.ty.clone(), allocation));
}
allocated.extend(block.allocate(scope));
}
@ -103,7 +100,7 @@ impl mir::Statement {
)
.unwrap();
allocated.push(Allocation(
named_variable_ref.1.clone(),
named_variable_ref.2,
named_variable_ref.0.clone(),
allocation,
));
@ -136,17 +133,42 @@ impl mir::Expression {
allocated.extend(expr.allocate(scope));
allocated.extend(idx.allocate(scope));
}
mir::ExprKind::Accessed(expression, _, _) => {
mir::ExprKind::Accessed(expression, ..) => {
allocated.extend(expression.allocate(scope));
}
mir::ExprKind::Array(expressions) => {
let (_, ty) = self.return_type(&Default::default(), scope.mod_id).unwrap();
let TypeKind::Array(elem_ty, _) = &ty else { panic!() };
let array_name = format!("{}.{}", elem_ty, expressions.len());
let allocation = scope
.block
.build_named(array_name, Instr::Alloca(ty.get_type(scope.type_values)))
.unwrap();
allocated.push(Allocation(self.1, ty, allocation));
for expression in expressions {
allocated.extend(expression.allocate(scope));
}
}
mir::ExprKind::Struct(_, items) => {
for (_, expression) in items {
mir::ExprKind::Struct(name, items) => {
let (_, ty) = self.return_type(&Default::default(), scope.mod_id).unwrap();
let allocation = scope
.block
.build_named(name, Instr::Alloca(ty.get_type(scope.type_values)))
.unwrap();
allocated.push(Allocation(self.1, ty, allocation));
for (field_name, expression, _) in items {
allocated.extend(expression.allocate(scope));
let (_, ty) = expression.return_type(&Default::default(), scope.mod_id).unwrap();
let allocation = scope
.block
.build_named(field_name, Instr::Alloca(ty.get_type(scope.type_values)))
.unwrap();
allocated.push(Allocation(expression.1, ty, allocation));
}
}
mir::ExprKind::Literal(_) => {}
@ -154,11 +176,7 @@ impl mir::Expression {
allocated.extend(lhs.allocate(scope));
allocated.extend(rhs.allocate(scope));
}
mir::ExprKind::FunctionCall(FunctionCall { parameters, .. }) => {
for param in parameters {
allocated.extend(param.allocate(scope));
}
}
mir::ExprKind::FunctionCall(fn_call) => allocated.extend(fn_call.allocate(&fn_call.name, scope)),
mir::ExprKind::If(IfExpression(cond, then_ex, else_ex)) => {
allocated.extend(cond.allocate(scope));
allocated.extend(then_ex.allocate(scope));
@ -174,11 +192,30 @@ impl mir::Expression {
mir::ExprKind::CastTo(expression, _) => {
allocated.extend(expression.allocate(scope));
}
mir::ExprKind::AssociatedFunctionCall(_, FunctionCall { parameters, .. }) => {
for param in parameters {
allocated.extend(param.allocate(scope));
}
mir::ExprKind::AssociatedFunctionCall(ty, fn_call) => {
allocated.extend(fn_call.allocate(&format!("{}::{}", ty, fn_call.name), scope))
}
mir::ExprKind::GlobalRef(..) => {}
}
allocated
}
}
impl mir::FunctionCall {
fn allocate<'ctx, 'a>(&self, name: &String, scope: &mut AllocatorScope<'ctx, 'a>) -> Vec<Allocation> {
let mut allocated = Vec::new();
for param in &self.parameters {
allocated.extend(param.allocate(scope));
}
if self.return_type != TypeKind::Void {
let allocation = scope
.block
.build_named(name, Instr::Alloca(self.return_type.get_type(scope.type_values)))
.unwrap();
allocated.push(Allocation(self.meta, self.return_type.clone(), allocation));
}
allocated

View File

@ -1,8 +1,11 @@
use reid_lib::{builder::InstructionValue, CmpPredicate, ConstValue, Instr, Type};
use reid_lib::{builder::InstructionValue, CmpPredicate, ConstValueKind, Instr, Type};
use crate::{
codegen::{ErrorKind, StackValueKind},
mir::{BinaryOperator, BinopDefinition, CmpOperator, FunctionDefinition, FunctionDefinitionKind, TypeKind},
mir::{
BinaryOperator, BinopDefinition, CmpOperator, FunctionDefinition, FunctionDefinitionKind, FunctionParam,
TypeKind,
},
};
use super::scope::{Scope, StackValue};
@ -30,13 +33,53 @@ const FLOATS: [TypeKind; 7] = [
TypeKind::F128PPC,
];
const INTRINSIC_IDENT: &str = "reid.intrinsic";
const MALLOC_IDENT: &str = "malloc";
pub fn form_intrinsics() -> Vec<FunctionDefinition> {
let intrinsics = Vec::new();
let mut intrinsics = Vec::new();
intrinsics.push(FunctionDefinition {
name: MALLOC_IDENT.to_owned(),
linkage_name: Some("malloc".to_owned()),
is_pub: false,
is_imported: true,
return_type: TypeKind::UserPtr(Box::new(TypeKind::U8)),
parameters: vec![FunctionParam {
name: "size".to_owned(),
ty: TypeKind::U64,
meta: Default::default(),
}],
kind: FunctionDefinitionKind::Extern(false),
source: None,
});
intrinsics
}
pub fn get_intrinsic_assoc_func(ty: &TypeKind, name: &str) -> Option<FunctionDefinition> {
if let TypeKind::Array(_, len) = ty {
match name {
"length" => {
return Some(FunctionDefinition {
name: "length".to_owned(),
linkage_name: None,
is_pub: true,
is_imported: false,
return_type: TypeKind::U64,
parameters: vec![FunctionParam {
name: String::from("self"),
ty: TypeKind::Borrow(Box::new(ty.clone()), false),
meta: Default::default(),
}],
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicConst(*len))),
source: None,
});
}
_ => {}
}
}
match name {
"sizeof" => Some(FunctionDefinition {
name: "sizeof".to_owned(),
@ -46,15 +89,21 @@ pub fn get_intrinsic_assoc_func(ty: &TypeKind, name: &str) -> Option<FunctionDef
return_type: TypeKind::U64,
parameters: Vec::new(),
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSizeOf(ty.clone()))),
source: None,
}),
"alloca" => Some(FunctionDefinition {
name: "alloca".to_owned(),
"malloc" => Some(FunctionDefinition {
name: "malloc".to_owned(),
linkage_name: None,
is_pub: true,
is_imported: false,
return_type: TypeKind::UserPtr(Box::new(ty.clone())),
parameters: vec![(String::from("size"), TypeKind::U64)],
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicAlloca(ty.clone()))),
parameters: vec![FunctionParam {
name: String::from("size"),
ty: TypeKind::U64,
meta: Default::default(),
}],
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicMalloc(ty.clone()))),
source: None,
}),
"null" => Some(FunctionDefinition {
name: "null".to_owned(),
@ -64,6 +113,7 @@ pub fn get_intrinsic_assoc_func(ty: &TypeKind, name: &str) -> Option<FunctionDef
return_type: TypeKind::UserPtr(Box::new(ty.clone())),
parameters: Vec::new(),
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicNullPtr(ty.clone()))),
source: None,
}),
_ => None,
}
@ -74,9 +124,17 @@ where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
{
BinopDefinition {
lhs: ("lhs".to_owned(), ty.clone()),
lhs: FunctionParam {
name: "lhs".to_owned(),
ty: ty.clone(),
meta: Default::default(),
},
op,
rhs: ("rhs".to_owned(), ty.clone()),
rhs: FunctionParam {
name: "rhs".to_owned(),
ty: ty.clone(),
meta: Default::default(),
},
return_type: ty.clone(),
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSimpleInstr(fun))),
meta: Default::default(),
@ -89,9 +147,17 @@ where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
{
BinopDefinition {
lhs: ("lhs".to_owned(), lhs.clone()),
lhs: FunctionParam {
name: "lhs".to_owned(),
ty: lhs.clone(),
meta: Default::default(),
},
op,
rhs: ("rhs".to_owned(), rhs.clone()),
rhs: FunctionParam {
name: "rhs".to_owned(),
ty: rhs.clone(),
meta: Default::default(),
},
return_type: lhs.clone(),
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSimpleInstr(fun))),
meta: Default::default(),
@ -104,9 +170,17 @@ where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
{
BinopDefinition {
lhs: ("lhs".to_owned(), ty.clone()),
lhs: FunctionParam {
name: "lhs".to_owned(),
ty: ty.clone(),
meta: Default::default(),
},
op,
rhs: ("rhs".to_owned(), ty.clone()),
rhs: FunctionParam {
name: "rhs".to_owned(),
ty: ty.clone(),
meta: Default::default(),
},
return_type: TypeKind::Bool,
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicBooleanInstr(fun))),
meta: Default::default(),
@ -173,26 +247,17 @@ pub fn form_intrinsic_binops() -> Vec<BinopDefinition> {
scope.block.build(Instr::XOr(lhs, rhs)).unwrap()
}));
if ty.signed() {
intrinsics.push(complex_binop_def(
BitshiftRight,
&ty,
&TypeKind::U64,
|scope, lhs, rhs| scope.block.build(Instr::ShiftRightArithmetic(lhs, rhs)).unwrap(),
));
intrinsics.push(complex_binop_def(BitshiftRight, &ty, &ty, |scope, lhs, rhs| {
scope.block.build(Instr::ShiftRightArithmetic(lhs, rhs)).unwrap()
}));
} else {
intrinsics.push(complex_binop_def(
BitshiftRight,
&ty,
&TypeKind::U64,
|scope, lhs, rhs| scope.block.build(Instr::ShiftRightLogical(lhs, rhs)).unwrap(),
));
intrinsics.push(complex_binop_def(BitshiftRight, &ty, &ty, |scope, lhs, rhs| {
scope.block.build(Instr::ShiftRightLogical(lhs, rhs)).unwrap()
}));
}
intrinsics.push(complex_binop_def(
BitshiftLeft,
&ty,
&TypeKind::U64,
|scope, lhs, rhs| scope.block.build(Instr::ShiftLeft(lhs, rhs)).unwrap(),
));
intrinsics.push(complex_binop_def(BitshiftLeft, &ty, &ty, |scope, lhs, rhs| {
scope.block.build(Instr::ShiftLeft(lhs, rhs)).unwrap()
}));
}
for ty in INTEGERS.iter().chain(&[TypeKind::Bool, TypeKind::Char]) {
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::EQ), &ty, |scope, lhs, rhs| {
@ -312,21 +377,32 @@ impl IntrinsicFunction for IntrinsicSizeOf {
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, _: &[StackValue]) -> Result<StackValue, ErrorKind> {
let instr = scope
.block
.build(Instr::Constant(reid_lib::ConstValue::U64(self.0.size_of())))
.build(Instr::Constant(reid_lib::ConstValueKind::U64(
self.0.size_of(&scope.type_map) / 8,
)))
.unwrap();
Ok(StackValue(StackValueKind::Literal(instr), self.0.clone()))
}
}
#[derive(Clone, Debug)]
pub struct IntrinsicAlloca(TypeKind);
impl IntrinsicFunction for IntrinsicAlloca {
pub struct IntrinsicMalloc(TypeKind);
impl IntrinsicFunction for IntrinsicMalloc {
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, params: &[StackValue]) -> Result<StackValue, ErrorKind> {
let amount = params.get(0).unwrap();
let instr = scope
let function = scope
.block
.build(Instr::ArrayAlloca(self.0.get_type(scope.type_values), amount.instr()))
.find_function(&format!("{}.{}", INTRINSIC_IDENT, MALLOC_IDENT))
.unwrap();
let sizeof = scope
.block
.build(Instr::Constant(ConstValueKind::U64(
self.0.size_of(&scope.type_map) / 8,
)))
.unwrap();
let bytes = scope.block.build(Instr::Mul(sizeof, amount.instr())).unwrap();
let instr = scope.block.build(Instr::FunctionCall(function, vec![bytes])).unwrap();
Ok(StackValue(StackValueKind::Literal(instr), self.0.clone()))
}
}
@ -335,7 +411,7 @@ impl IntrinsicFunction for IntrinsicAlloca {
pub struct IntrinsicNullPtr(TypeKind);
impl IntrinsicFunction for IntrinsicNullPtr {
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, _: &[StackValue]) -> Result<StackValue, ErrorKind> {
let zero = scope.block.build(Instr::Constant(ConstValue::I8(0))).unwrap();
let zero = scope.block.build(Instr::Constant(ConstValueKind::I8(0))).unwrap();
let instr = scope
.block
.build(Instr::IntToPtr(
@ -349,6 +425,14 @@ impl IntrinsicFunction for IntrinsicNullPtr {
))
}
}
#[derive(Clone, Debug)]
pub struct IntrinsicConst(u64);
impl IntrinsicFunction for IntrinsicConst {
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, _: &[StackValue]) -> Result<StackValue, ErrorKind> {
let zero = scope.block.build(Instr::Constant(ConstValueKind::U64(self.0))).unwrap();
Ok(StackValue(StackValueKind::Literal(zero), TypeKind::U64))
}
}
// impl IntrinsicFunction for IntrinsicIAdd {
// fn codegen<'ctx, 'a>(

View File

@ -3,12 +3,14 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc};
use allocator::{Allocator, AllocatorScope};
use intrinsics::*;
use reid_lib::{
builder::ConstantValue,
compile::CompiledModule,
debug_information::{
DebugFileData, DebugLocalVariable, DebugLocation, DebugMetadata, DebugRecordKind, DebugSubprogramData,
DebugSubprogramOptionals, DebugSubprogramType, DebugTypeData, DwarfFlags, InstructionDebugRecordData,
DebugFileData, DebugLexicalScope, DebugLocalVariable, DebugLocation, DebugMetadata, DebugRecordKind,
DebugSubprogramData, DebugSubprogramOptionals, DebugSubprogramType, DebugTypeData, DwarfFlags,
InstructionDebugRecordData,
},
CmpPredicate, ConstValue, Context, CustomTypeKind, Function, FunctionFlags, Instr, Module, NamedStruct,
CmpPredicate, ConstValueKind, Context, CustomTypeKind, Function, FunctionFlags, Instr, Module, NamedStruct,
TerminatorKind as Term, Type,
};
use scope::*;
@ -18,8 +20,8 @@ use crate::{
self,
implement::TypeCategory,
pass::{AssociatedFunctionKey, BinopKey},
CustomTypeKey, FunctionCall, FunctionDefinitionKind, NamedVariableRef, SourceModuleId, StructField, StructType,
TypeDefinition, TypeDefinitionKind, TypeKind, WhileStatement,
CustomTypeKey, FunctionCall, FunctionDefinitionKind, FunctionParam, NamedVariableRef, SourceModuleId,
StructField, StructType, TypeDefinition, TypeDefinitionKind, TypeKind, WhileStatement,
},
util::try_all,
};
@ -67,6 +69,7 @@ impl mir::Context {
#[derive(Clone)]
struct ModuleCodegen<'ctx> {
name: String,
module: Module<'ctx>,
}
@ -94,6 +97,29 @@ impl Default for State {
}
}
impl mir::GlobalKind {
fn codegen<'ctx>(
&'ctx self,
context: &'ctx Context,
types: &HashMap<CustomTypeKey, reid_lib::builder::TypeValue>,
module: &Module,
) -> Result<(ConstantValue, TypeKind), ErrorKind> {
Ok(match self {
mir::GlobalKind::Literal(literal) => (module.add_constant(literal.as_const_kind()), literal.as_type()),
mir::GlobalKind::Array(globals) => {
let values = try_all(globals.into_iter().map(|g| g.codegen(context, types, module)).collect())
.map_err(|e| e.first().unwrap().clone())?;
let elem_ty = values.iter().map(|(_, t)| t.clone()).next().unwrap_or(TypeKind::Void);
let values = values.iter().map(|(v, _)| *v).collect::<Vec<_>>();
(
module.add_constant(ConstValueKind::Array(values, elem_ty.get_type(&types))),
TypeKind::Array(Box::new(elem_ty), globals.len() as u64),
)
}
})
}
}
impl mir::Module {
fn codegen<'ctx>(
&'ctx self,
@ -118,6 +144,7 @@ impl mir::Module {
let mut types = HashMap::new();
let mut type_values = HashMap::new();
let mut debug_types = HashMap::new();
let mut type_map = HashMap::new();
macro_rules! insert_debug {
($kind:expr) => {
@ -127,8 +154,7 @@ impl mir::Module {
&compile_unit,
&debug,
&debug_types,
&type_values,
&types,
&type_map,
self.module_id,
&self.tokens,
&modules,
@ -156,6 +182,8 @@ impl mir::Module {
for typedef in typedefs {
let type_key = CustomTypeKey(typedef.name.clone(), typedef.source_module);
type_map.insert(type_key.clone(), typedef.clone());
let type_value = match &typedef.kind {
TypeDefinitionKind::Struct(StructType(fields)) => {
module.custom_type(CustomTypeKind::NamedStruct(NamedStruct(
@ -172,22 +200,47 @@ impl mir::Module {
};
types.insert(type_value, typedef.clone());
type_values.insert(type_key.clone(), type_value);
insert_debug!(&TypeKind::CustomType(type_key.clone()));
}
let mut globals = HashMap::new();
for global in &self.globals {
let (const_value, _) = global.kind.codegen(context, &type_values, &module)?;
globals.insert(global.name.clone(), module.add_global(&global.name, const_value));
}
let mut functions = HashMap::new();
for function in &self.functions {
let param_types: Vec<Type> = function
.parameters
.iter()
.map(|(_, p)| p.get_type(&type_values))
.map(|FunctionParam { ty, .. }| ty.get_type(&type_values))
.collect();
let is_main = self.is_main && function.name == "main";
let module_prefix = if let Some(module) = function.source {
if module == self.module_id {
format!("reid.{}.", self.name)
} else {
format!("reid.{}.", modules.get(&module).unwrap().name)
}
} else {
format!("reid.intrinsic.")
};
let linkage_name = function.linkage_name.clone().unwrap_or(function.name.clone());
let full_name = format!(
"{}{}",
module_prefix,
function.linkage_name.clone().unwrap_or(function.name.clone())
);
let func = match &function.kind {
mir::FunctionDefinitionKind::Local(_, _) => Some(module.function(
&function.linkage_name.clone().unwrap_or(function.name.clone()),
&full_name,
None,
function.return_type.get_type(&type_values),
param_types,
FunctionFlags {
@ -198,7 +251,16 @@ impl mir::Module {
},
)),
mir::FunctionDefinitionKind::Extern(imported) => Some(module.function(
&function.linkage_name.clone().unwrap_or(function.name.clone()),
&full_name,
if function.source == None {
Some(function.linkage_name.clone().unwrap())
} else {
if !*imported {
Some(linkage_name.clone())
} else {
None
}
},
function.return_type.get_type(&type_values),
param_types,
FunctionFlags {
@ -221,13 +283,24 @@ impl mir::Module {
let param_types: Vec<Type> = function
.parameters
.iter()
.map(|(_, p)| p.get_type(&type_values))
.map(|FunctionParam { ty, .. }| ty.get_type(&type_values))
.collect();
let is_main = self.is_main && function.name == "main";
let module_prefix = if let Some(module) = function.source {
if module == self.module_id {
format!("reid.{}.", self.name)
} else {
format!("reid.{}.", modules.get(&module).unwrap().name)
}
} else {
format!("reid.intrinsic.")
};
let full_name = format!("{}{}::{}", module_prefix, ty, function.name);
let func = match &function.kind {
mir::FunctionDefinitionKind::Local(_, _) => Some(module.function(
&format!("{}::{}", ty, function.name),
&full_name,
None,
function.return_type.get_type(&type_values),
param_types,
FunctionFlags {
@ -238,7 +311,8 @@ impl mir::Module {
},
)),
mir::FunctionDefinitionKind::Extern(imported) => Some(module.function(
&function.linkage_name.clone().unwrap_or(function.name.clone()),
&full_name,
None,
function.return_type.get_type(&type_values),
param_types,
FunctionFlags {
@ -262,11 +336,11 @@ impl mir::Module {
for binop in &self.binop_defs {
let binop_fn_name = format!(
"binop.{}.{:?}.{}.{}",
binop.lhs.1, binop.op, binop.rhs.1, binop.return_type
binop.lhs.ty, binop.op, binop.rhs.ty, binop.return_type
);
binops.insert(
BinopKey {
params: (binop.lhs.1.clone(), binop.rhs.1.clone()),
params: (binop.lhs.ty.clone(), binop.rhs.ty.clone()),
operator: binop.op,
},
StackBinopDefinition {
@ -276,8 +350,9 @@ impl mir::Module {
FunctionDefinitionKind::Local(..) => {
let ir_function = module.function(
&binop_fn_name,
None,
binop.return_type.get_type(&type_values),
vec![binop.lhs.1.get_type(&type_values), binop.rhs.1.get_type(&type_values)],
vec![binop.lhs.ty.get_type(&type_values), binop.rhs.ty.get_type(&type_values)],
FunctionFlags {
is_pub: binop.exported,
is_imported: binop.exported,
@ -292,6 +367,7 @@ impl mir::Module {
&mut AllocatorScope {
block: &mut entry,
type_values: &type_values,
mod_id: self.module_id,
},
);
@ -307,6 +383,8 @@ impl mir::Module {
functions: &functions,
types: &types,
type_values: &type_values,
type_map: &type_map,
globals: &globals,
stack_values: HashMap::new(),
debug: Some(Debug {
info: &debug,
@ -340,8 +418,9 @@ impl mir::Module {
}
FunctionDefinitionKind::Extern(imported) => ScopeFunctionKind::UserGenerated(module.function(
&binop_fn_name,
None,
binop.return_type.get_type(&type_values),
vec![binop.lhs.1.get_type(&type_values), binop.rhs.1.get_type(&type_values)],
vec![binop.lhs.ty.get_type(&type_values), binop.rhs.ty.get_type(&type_values)],
FunctionFlags {
is_extern: true,
is_imported: *imported,
@ -366,6 +445,7 @@ impl mir::Module {
&mut AllocatorScope {
block: &mut entry,
type_values: &type_values,
mod_id: self.module_id,
},
);
@ -381,12 +461,14 @@ impl mir::Module {
functions: &functions,
types: &types,
type_values: &type_values,
type_map: &type_map,
stack_values: HashMap::new(),
debug: Some(Debug {
info: &debug,
scope: compile_unit.clone(),
types: &debug_types,
}),
globals: &globals,
binops: &binops,
allocator: Rc::new(RefCell::new(allocator)),
};
@ -425,6 +507,7 @@ impl mir::Module {
&mut AllocatorScope {
block: &mut entry,
type_values: &type_values,
mod_id: self.module_id,
},
);
@ -440,12 +523,14 @@ impl mir::Module {
functions: &functions,
types: &types,
type_values: &type_values,
type_map: &type_map,
stack_values: HashMap::new(),
debug: Some(Debug {
info: &debug,
scope: compile_unit.clone(),
types: &debug_types,
}),
globals: &globals,
binops: &binops,
allocator: Rc::new(RefCell::new(allocator)),
};
@ -471,7 +556,10 @@ impl mir::Module {
}
}
Ok(ModuleCodegen { module })
Ok(ModuleCodegen {
name: self.name.clone(),
module,
})
}
}
@ -481,7 +569,7 @@ impl FunctionDefinitionKind {
name: String,
is_pub: bool,
scope: &mut Scope,
parameters: &Vec<(String, TypeKind)>,
parameters: &Vec<FunctionParam>,
return_type: &TypeKind,
ir_function: &Function,
debug_location: Option<DebugLocation>,
@ -525,57 +613,31 @@ impl FunctionDefinitionKind {
}
// Compile actual IR part
for (i, (p_name, p_ty)) in parameters.iter().enumerate() {
for (i, p) in parameters.iter().enumerate() {
// Codegen actual parameters
let arg_name = format!("arg.{}", p_name);
let arg_name = format!("arg.{}", p.name);
let param = scope
.block
.build_named(format!("{}.get", arg_name), Instr::Param(i))
.unwrap();
let alloca = scope.allocate(&p_name, &p_ty).unwrap();
let alloca = scope.allocate(&p.meta, &p.ty).unwrap();
scope
.block
.build_named(format!("{}.store", arg_name), Instr::Store(alloca, param))
.unwrap();
scope.stack_values.insert(
p_name.clone(),
p.name.clone(),
StackValue(
StackValueKind::mutable(p_ty.is_mutable(), alloca),
TypeKind::CodegenPtr(Box::new(p_ty.clone())),
StackValueKind::mutable(p.ty.is_mutable(), alloca),
TypeKind::CodegenPtr(Box::new(p.ty.clone())),
),
);
// Generate debug info
// if let (Some(debug_scope), Some(location)) = (
// &debug_scope,
// mir_function.signature().into_debug(tokens, compile_unit),
// ) {
// debug.metadata(
// &location,
// DebugMetadata::ParamVar(DebugParamVariable {
// name: p_name.clone(),
// arg_idx: i as u32,
// ty: p_ty.get_debug_type_hard(
// *debug_scope,
// &debug,
// &debug_types,
// &type_values,
// &types,
// self.module_id,
// &self.tokens,
// &modules,
// ),
// always_preserve: true,
// flags: DwarfFlags,
// }),
// );
// }
}
let state = State::default();
if let Some(ret) = block.codegen(scope, &state)? {
if let Some(ret) = block.codegen(scope, &state, false)? {
scope.block.terminate(Term::Ret(ret.instr())).unwrap();
} else {
if !scope.block.delete_if_unused().unwrap() {
@ -604,7 +666,20 @@ impl mir::Block {
&self,
mut scope: &mut Scope<'ctx, 'a>,
state: &State,
create_debug_scope: bool,
) -> Result<Option<StackValue>, ErrorKind> {
let parent_scope = if let Some(debug) = &mut scope.debug {
let parent_scope = debug.scope.clone();
if create_debug_scope {
let location = self.meta.into_debug(scope.tokens, &debug.scope).unwrap();
let scope = debug.info.lexical_scope(&debug.scope, DebugLexicalScope { location });
debug.scope = scope;
}
Some(parent_scope)
} else {
None
};
for stmt in &self.statements {
stmt.codegen(&mut scope, state)?.map(|s| {
if let Some(debug) = &scope.debug {
@ -615,7 +690,7 @@ impl mir::Block {
});
}
if let Some((kind, expr)) = &self.return_expression {
let return_value = if let Some((kind, expr)) = &self.return_expression {
if let Some(expr) = expr {
let ret = expr.codegen(&mut scope, &mut state.load(true))?;
match kind {
@ -638,7 +713,13 @@ impl mir::Block {
}
} else {
Ok(None)
};
if let Some(parent_scope) = parent_scope {
scope.debug.as_mut().unwrap().scope = parent_scope;
}
return_value
}
}
@ -650,11 +731,11 @@ impl mir::Statement {
});
match &self.0 {
mir::StmtKind::Let(NamedVariableRef(ty, name, _), mutable, expression) => {
mir::StmtKind::Let(NamedVariableRef(ty, name, meta), mutable, expression) => {
let value = expression.codegen(scope, &state)?.unwrap();
let alloca = scope
.allocate(name, &value.1)
.allocate(meta, &value.1)
.unwrap()
.maybe_location(&mut scope.block, location.clone());
@ -742,7 +823,7 @@ impl mir::Statement {
let condition_res = condition.codegen(&mut condition_scope, state)?.unwrap();
let true_instr = condition_scope
.block
.build(Instr::Constant(ConstValue::Bool(true)))
.build(Instr::Constant(ConstValueKind::Bool(true)))
.unwrap();
let check = condition_scope
.block
@ -759,7 +840,7 @@ impl mir::Statement {
.unwrap();
let mut condition_true_scope = scope.with_block(condition_true_block);
block.codegen(&mut condition_true_scope, state)?;
block.codegen(&mut condition_true_scope, state, true)?;
condition_true_scope
.block
@ -933,7 +1014,7 @@ impl mir::Expression {
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, true)? {
Some(ret)
} else {
None
@ -989,7 +1070,7 @@ impl mir::Expression {
let first = scope
.block
.build_named("array.zero", Instr::Constant(ConstValue::U32(0)))
.build_named("array.zero", Instr::Constant(ConstValueKind::U32(0)))
.unwrap();
(
scope
@ -1043,8 +1124,10 @@ impl mir::Expression {
let load_n = format!("{}.load", array_name);
let array = scope
.block
.build_named(&array_name, Instr::Alloca(array_ty.clone()))
.allocate(
&self.1,
&TypeKind::Array(Box::new(elem_ty_kind.clone()), expressions.len() as u64),
)
.unwrap()
.maybe_location(&mut scope.block, location.clone());
@ -1054,11 +1137,11 @@ impl mir::Expression {
let index_expr = scope
.block
.build_named(index.to_string(), Instr::Constant(ConstValue::U32(index as u32)))
.build_named(index.to_string(), Instr::Constant(ConstValueKind::U32(index as u32)))
.unwrap();
let first = scope
.block
.build_named("zero", Instr::Constant(ConstValue::U32(0)))
.build_named("zero", Instr::Constant(ConstValueKind::U32(0)))
.unwrap();
let ptr = scope
.block
@ -1083,7 +1166,7 @@ impl mir::Expression {
TypeKind::Array(Box::new(elem_ty_kind), instr_list.len() as u64),
))
}
mir::ExprKind::Accessed(expression, type_kind, field) => {
mir::ExprKind::Accessed(expression, type_kind, field, _) => {
let struct_val = expression.codegen(scope, &state.load(false))?.unwrap();
let TypeKind::CodegenPtr(inner) = &struct_val.1 else {
@ -1141,12 +1224,11 @@ impl mir::Expression {
let load_n = format!("{}.load", name);
let struct_ptr = scope
.block
.build_named(name, Instr::Alloca(ty.clone()))
.allocate(&self.1, &TypeKind::CustomType(type_key.clone()))
.unwrap()
.maybe_location(&mut scope.block, location.clone());
for (field_n, exp) in items {
for (field_n, exp, _) in items {
let gep_n = format!("{}.{}.gep", name, field_n);
let store_n = format!("{}.{}.store", name, field_n);
let i = indices.clone().find(|(_, f)| f.0 == *field_n).unwrap().0;
@ -1235,24 +1317,47 @@ impl mir::Expression {
if val.1 == *type_kind {
Some(val)
} else {
match (&val.1, type_kind) {
(TypeKind::CodegenPtr(inner), TypeKind::UserPtr(_)) => match *inner.clone() {
TypeKind::UserPtr(_) => Some(StackValue(
val.0.derive(
scope
.block
.build(Instr::BitCast(
val.instr(),
Type::Ptr(Box::new(type_kind.get_type(scope.type_values))),
))
.unwrap(),
),
TypeKind::CodegenPtr(Box::new(type_kind.clone())),
)),
_ => panic!(),
},
(TypeKind::UserPtr(_), TypeKind::UserPtr(_))
| (TypeKind::Char, TypeKind::U8)
let (ty, other) = if !state.should_load {
let TypeKind::CodegenPtr(inner) = &val.1 else {
panic!();
};
(*inner.clone(), TypeKind::CodegenPtr(Box::new(type_kind.clone())))
} else {
(val.1.clone(), type_kind.clone())
};
dbg!(&ty, type_kind);
match (&ty, type_kind) {
(TypeKind::UserPtr(_), TypeKind::UserPtr(_)) => Some(StackValue(
val.0.derive(
scope
.block
.build(Instr::BitCast(val.instr(), other.get_type(scope.type_values)))
.unwrap(),
),
other.clone(),
)),
(TypeKind::Borrow(ty1, _), TypeKind::UserPtr(ty2)) => {
if let TypeKind::Array(ty1, _) = ty1.as_ref() {
if ty1 == ty2 {
Some(StackValue(
val.0.derive(
scope
.block
.build(Instr::BitCast(val.instr(), other.get_type(scope.type_values)))
.unwrap(),
),
other,
))
} else {
return Err(ErrorKind::Null).unwrap();
}
} else {
return Err(ErrorKind::Null).unwrap();
}
}
(TypeKind::Char, TypeKind::U8)
| (TypeKind::U8, TypeKind::Char)
| (TypeKind::U8, TypeKind::I8) => Some(StackValue(
val.0.derive(
@ -1264,8 +1369,7 @@ impl mir::Expression {
type_kind.clone(),
)),
_ => {
let cast_instr = val
.1
let cast_instr = ty
.get_type(scope.type_values)
.cast_instruction(val.instr(), &type_kind.get_type(scope.type_values))
.unwrap();
@ -1279,6 +1383,33 @@ impl mir::Expression {
}
}
mir::ExprKind::AssociatedFunctionCall(ty, call) => codegen_function_call(Some(ty), call, scope, state)?,
mir::ExprKind::GlobalRef(global_name, ty) => {
let global_value = scope.globals.get(global_name).unwrap();
let value = scope.block.build(Instr::GetGlobal(global_value.clone())).unwrap();
if !state.should_load {
let allocated = scope
.block
.build(Instr::Alloca(ty.get_type(scope.type_values)))
.unwrap();
scope
.block
.build(Instr::Store(allocated, value))
.unwrap()
.maybe_location(&mut scope.block, location.clone());
let a = Some(StackValue(
StackValueKind::Literal(allocated),
TypeKind::CodegenPtr(Box::new(ty.clone())),
));
a
} else {
let a = Some(StackValue(StackValueKind::Literal(value), ty.clone()));
a
}
}
};
if let Some(value) = &value {
value.instr().maybe_location(&mut scope.block, location.clone());
@ -1352,8 +1483,9 @@ fn codegen_function_call<'ctx, 'a>(
let ptr = if ret_type_kind != TypeKind::Void {
let ptr = scope
.block
.build_named(&call.name, Instr::Alloca(ret_type.clone()))
.allocator
.borrow_mut()
.allocate(&call.meta, &call.return_type)
.unwrap();
scope
.block

View File

@ -1,7 +1,7 @@
use std::{cell::RefCell, collections::HashMap, mem, rc::Rc};
use reid_lib::{
builder::{InstructionValue, TypeValue},
builder::{GlobalValue, InstructionValue, TypeValue},
debug_information::{DebugInformation, DebugLocation, DebugScopeValue, DebugTypeValue},
Block, Context, Function, Instr, Module,
};
@ -10,7 +10,7 @@ use crate::{
lexer::FullToken,
mir::{
pass::{AssociatedFunctionKey, BinopKey},
CustomTypeKey, SourceModuleId, TypeDefinition, TypeKind,
CustomTypeKey, FunctionParam, Metadata, SourceModuleId, TypeDefinition, TypeKind,
},
};
@ -26,10 +26,12 @@ pub struct Scope<'ctx, 'scope> {
pub(super) block: Block<'ctx>,
pub(super) types: &'scope HashMap<TypeValue, TypeDefinition>,
pub(super) type_values: &'scope HashMap<CustomTypeKey, TypeValue>,
pub(super) type_map: &'scope HashMap<CustomTypeKey, TypeDefinition>,
pub(super) assoc_functions: &'scope HashMap<AssociatedFunctionKey, ScopeFunctionKind<'ctx>>,
pub(super) functions: &'scope HashMap<String, ScopeFunctionKind<'ctx>>,
pub(super) binops: &'scope HashMap<BinopKey, StackBinopDefinition<'ctx>>,
pub(super) stack_values: HashMap<String, StackValue>,
pub(super) globals: &'scope HashMap<String, GlobalValue>,
pub(super) debug: Option<Debug<'ctx>>,
pub(super) allocator: Rc<RefCell<Allocator>>,
}
@ -48,9 +50,11 @@ impl<'ctx, 'a> Scope<'ctx, 'a> {
functions: self.functions,
types: self.types,
type_values: self.type_values,
type_map: self.type_map,
stack_values: self.stack_values.clone(),
debug: self.debug.clone(),
allocator: self.allocator.clone(),
globals: self.globals,
binops: self.binops,
}
}
@ -67,8 +71,8 @@ impl<'ctx, 'a> Scope<'ctx, 'a> {
self.type_values.get(key).and_then(|v| self.types.get(v))
}
pub fn allocate(&self, name: &String, ty: &TypeKind) -> Option<InstructionValue> {
self.allocator.borrow_mut().allocate(name, ty)
pub fn allocate(&self, meta: &Metadata, ty: &TypeKind) -> Option<InstructionValue> {
self.allocator.borrow_mut().allocate(meta, ty)
}
}
@ -129,7 +133,7 @@ impl StackValueKind {
}
pub struct StackBinopDefinition<'ctx> {
pub(super) parameters: ((String, TypeKind), (String, TypeKind)),
pub(super) parameters: (FunctionParam, FunctionParam),
pub(super) return_ty: TypeKind,
pub(super) kind: ScopeFunctionKind<'ctx>,
}
@ -147,14 +151,14 @@ impl<'ctx> StackBinopDefinition<'ctx> {
rhs: StackValue,
scope: &mut Scope<'ctx, 'a>,
) -> Result<StackValue, ErrorKind> {
let (lhs, rhs) = if lhs.1 == self.parameters.0 .1 && rhs.1 == self.parameters.1 .1 {
let (lhs, rhs) = if lhs.1 == self.parameters.0.ty && rhs.1 == self.parameters.1.ty {
(lhs, rhs)
} else {
(rhs, lhs)
};
let name = format!(
"binop.{}.{}.{}.call",
self.parameters.0 .1, self.parameters.1 .1, self.return_ty
self.parameters.0.ty, self.parameters.1.ty, self.return_ty
);
self.kind.codegen(&name, &[lhs, rhs], &self.return_ty, None, scope)
}

View File

@ -6,7 +6,7 @@ use reid_lib::{
DebugArrayType, DebugBasicType, DebugFieldType, DebugInformation, DebugLocation, DebugPointerType,
DebugPosition, DebugScopeValue, DebugStructType, DebugTypeData, DebugTypeValue, DwarfEncoding, DwarfFlags,
},
Block, CmpPredicate, ConstValue, Instr, Type,
Block, CmpPredicate, ConstValueKind, Instr, Type,
};
use crate::{
@ -34,34 +34,36 @@ impl mir::CmpOperator {
impl mir::Literal {
pub(super) fn as_const(&self, block: &mut Block) -> InstructionValue {
block.build_named(format!("{}", self), self.as_const_kind()).unwrap()
block
.build_named(format!("{}", self), Instr::Constant(self.as_const_kind()))
.unwrap()
}
pub(super) fn as_const_kind(&self) -> Instr {
Instr::Constant(match self.clone() {
mir::Literal::I8(val) => ConstValue::I8(val),
mir::Literal::I16(val) => ConstValue::I16(val),
mir::Literal::I32(val) => ConstValue::I32(val),
mir::Literal::I64(val) => ConstValue::I64(val),
mir::Literal::I128(val) => ConstValue::I128(val),
mir::Literal::U8(val) => ConstValue::U8(val),
mir::Literal::U16(val) => ConstValue::U16(val),
mir::Literal::U32(val) => ConstValue::U32(val),
mir::Literal::U64(val) => ConstValue::U64(val),
mir::Literal::U128(val) => ConstValue::U128(val),
mir::Literal::Bool(val) => ConstValue::Bool(val),
mir::Literal::String(val) => ConstValue::Str(val.clone()),
mir::Literal::Vague(VagueLiteral::Number(val)) => ConstValue::I32(val as i32),
mir::Literal::Vague(VagueLiteral::Decimal(val)) => ConstValue::F32(val as f32),
mir::Literal::F16(val) => ConstValue::F16(val),
mir::Literal::F32B(val) => ConstValue::F32B(val),
mir::Literal::F32(val) => ConstValue::F32(val),
mir::Literal::F64(val) => ConstValue::F64(val),
mir::Literal::F80(val) => ConstValue::F80(val),
mir::Literal::F128(val) => ConstValue::F128(val),
mir::Literal::F128PPC(val) => ConstValue::F128PPC(val),
mir::Literal::Char(c) => ConstValue::U8(c as u8),
})
pub(super) fn as_const_kind(&self) -> ConstValueKind {
match self.clone() {
mir::Literal::I8(val) => ConstValueKind::I8(val),
mir::Literal::I16(val) => ConstValueKind::I16(val),
mir::Literal::I32(val) => ConstValueKind::I32(val),
mir::Literal::I64(val) => ConstValueKind::I64(val),
mir::Literal::I128(val) => ConstValueKind::I128(val),
mir::Literal::U8(val) => ConstValueKind::U8(val),
mir::Literal::U16(val) => ConstValueKind::U16(val),
mir::Literal::U32(val) => ConstValueKind::U32(val),
mir::Literal::U64(val) => ConstValueKind::U64(val),
mir::Literal::U128(val) => ConstValueKind::U128(val),
mir::Literal::Bool(val) => ConstValueKind::Bool(val),
mir::Literal::String(val) => ConstValueKind::Str(val.clone()),
mir::Literal::Vague(VagueLiteral::Number(val)) => ConstValueKind::I32(val as i32),
mir::Literal::Vague(VagueLiteral::Decimal(val)) => ConstValueKind::F32(val as f32),
mir::Literal::F16(val) => ConstValueKind::F16(val),
mir::Literal::F32B(val) => ConstValueKind::F32B(val),
mir::Literal::F32(val) => ConstValueKind::F32(val),
mir::Literal::F64(val) => ConstValueKind::F64(val),
mir::Literal::F80(val) => ConstValueKind::F80(val),
mir::Literal::F128(val) => ConstValueKind::F128(val),
mir::Literal::F128PPC(val) => ConstValueKind::F128PPC(val),
mir::Literal::Char(c) => ConstValueKind::U8(c as u8),
}
}
}
@ -107,8 +109,7 @@ impl TypeKind {
&debug.scope,
debug.info,
debug.types,
scope.type_values,
scope.types,
scope.type_map,
scope.module_id,
scope.tokens,
scope.modules,
@ -120,8 +121,7 @@ impl TypeKind {
scope: &DebugScopeValue,
debug_info: &DebugInformation,
debug_types: &HashMap<TypeKind, DebugTypeValue>,
type_values: &HashMap<CustomTypeKey, TypeValue>,
types: &HashMap<TypeValue, TypeDefinition>,
type_map: &HashMap<CustomTypeKey, TypeDefinition>,
local_mod: SourceModuleId,
tokens: &Vec<FullToken>,
modules: &HashMap<SourceModuleId, ModuleCodegen>,
@ -140,13 +140,12 @@ impl TypeKind {
scope,
debug_info,
debug_types,
type_values,
types,
type_map,
local_mod,
tokens,
modules,
),
size_bits: self.size_of(),
size_bits: self.size_of(type_map),
})
}
TypeKind::Array(elem_ty, len) => {
@ -154,21 +153,20 @@ impl TypeKind {
scope,
debug_info,
debug_types,
type_values,
types,
type_map,
local_mod,
tokens,
modules,
);
DebugTypeData::Array(DebugArrayType {
size_bits: self.size_of(),
size_bits: self.size_of(type_map),
align_bits: self.alignment(),
element_type: elem_ty,
length: *len,
})
}
TypeKind::CustomType(key) => {
let typedef = types.get(type_values.get(key).unwrap()).unwrap();
let typedef = type_map.get(key).unwrap();
match &typedef.kind {
TypeDefinitionKind::Struct(struct_type) => {
let mut fields = Vec::new();
@ -184,21 +182,20 @@ impl TypeKind {
name: field.0.clone(),
scope: scope.clone(),
pos: location.map(|l| l.pos),
size_bits: field.1.size_of(),
size_bits: field.1.size_of(type_map),
offset: size_bits,
flags: DwarfFlags,
ty: field.1.get_debug_type_hard(
scope,
debug_info,
debug_types,
type_values,
types,
type_map,
local_mod,
tokens,
modules,
),
});
size_bits += field.1.size_of();
size_bits += field.1.size_of(type_map);
}
{
let location = if typedef.source_module != local_mod {
@ -220,7 +217,7 @@ impl TypeKind {
}
_ => DebugTypeData::Basic(DebugBasicType {
name,
size_bits: self.size_of(),
size_bits: self.size_of(type_map),
encoding: match self {
TypeKind::Bool => DwarfEncoding::Boolean,
TypeKind::I8 => DwarfEncoding::SignedChar,

View File

@ -7,7 +7,7 @@ use crate::{
ast::token_stream::{self, TokenRange},
codegen,
lexer::{self, Cursor, FullToken, Position},
mir::{self, pass, typecheck, Metadata, SourceModuleId},
mir::{self, macros, pass, typecheck, Metadata, SourceModuleId},
};
use crate::mir::typecheck::ErrorKind as TypecheckError;
@ -33,6 +33,8 @@ pub enum ErrorKind {
TypeInferenceError(#[source] mir::pass::Error<TypecheckError>),
#[error("{}{}", label("(Linker) "), .0.kind)]
LinkerError(#[from] mir::pass::Error<mir::linker::ErrorKind>),
#[error("{}{}", label("(Macro) "), .0)]
MacroError(#[from] mir::pass::Error<macros::ErrorKind>),
#[error("{}{}", label("(Codegen) "), .0)]
CodegenError(#[from] codegen::ErrorKind),
}
@ -48,7 +50,7 @@ impl ErrorKind {
}
impl ErrorKind {
fn get_meta(&self) -> Metadata {
pub fn get_meta(&self) -> Metadata {
match &self {
ErrorKind::LexerError(error) => error.metadata,
ErrorKind::ParserError(error) => error.metadata,
@ -58,6 +60,19 @@ impl ErrorKind {
ErrorKind::CodegenError(error) => match error {
codegen::ErrorKind::Null => Default::default(),
},
ErrorKind::MacroError(error) => error.metadata,
}
}
pub fn get_type_str(&self) -> &str {
match self {
ErrorKind::LexerError(_) => "lexer",
ErrorKind::ParserError(_) => "parser",
ErrorKind::TypeCheckError(_) => "typechecker",
ErrorKind::TypeInferenceError(_) => "type-inferrer",
ErrorKind::LinkerError(_) => "linker",
ErrorKind::MacroError(_) => "macro-pass",
ErrorKind::CodegenError(_) => "codegen",
}
}
}
@ -79,7 +94,7 @@ pub struct ErrorModule {
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ErrorModules {
module_map: HashMap<mir::SourceModuleId, ErrorModule>,
pub(super) module_map: HashMap<mir::SourceModuleId, ErrorModule>,
module_counter: mir::SourceModuleId,
}
@ -117,7 +132,7 @@ impl ErrorModules {
#[derive(Debug, Clone, PartialEq)]
pub struct ReidError {
map: ErrorModules,
errors: Vec<ErrorKind>,
pub errors: Vec<ErrorKind>,
}
impl ReidError {
@ -182,9 +197,7 @@ impl std::fmt::Display for ReidError {
let module = self.map.module(&meta.source_module_id);
let position = if let Some(module) = module {
if let Some(tokens) = &module.tokens {
let range_tokens = meta.range.into_tokens(&tokens);
get_position(&range_tokens).or(meta.position.map(|p| (p, p)))
meta.range.into_position(tokens).or(meta.position.map(|p| (p, p)))
} else if let Some(position) = meta.position {
Some((position, position))
} else {
@ -234,6 +247,11 @@ impl TokenRange {
.take(self.end + 1 - self.start)
.collect::<Vec<_>>()
}
pub fn into_position<'v>(&self, tokens: &'v Vec<FullToken>) -> Option<(Position, Position)> {
let tokens = self.into_tokens(tokens);
get_position(&tokens)
}
}
fn get_position(tokens: &Vec<&FullToken>) -> Option<(Position, Position)> {

View File

@ -26,12 +26,11 @@ impl LDRunner {
let dyn_linker_path = find_objectfile(&self.dynamic_linker);
let crt1_path = find_objectfile("crt1.o");
#[cfg(feature = "log_output")]
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);
ld.arg("-dynamic-linker").arg(dyn_linker_path).arg(crt1_path);
for library in &self.libraries {
ld.arg(format!("-l{}", library));
@ -41,22 +40,21 @@ impl LDRunner {
.arg("-o")
.arg(out_path.to_str().unwrap());
#[cfg(feature = "log_output")]
println!(
"LDRunner: Executing linker to objfile at {:?} => {:?}",
input_path, out_path
);
#[cfg(feature = "log_output")]
dbg!(&ld);
ld.spawn().expect("Unable to execute ld!");
thread::sleep(Duration::from_millis(100));
#[cfg(feature = "log_output")]
println!("Setting executable bit to {:?}..", out_path);
Command::new("chmod")
.arg("+x")
.arg(out_path)
.spawn()
.unwrap();
Command::new("chmod").arg("+x").arg(out_path).spawn().unwrap();
thread::sleep(Duration::from_millis(100));
}
}

View File

@ -8,22 +8,36 @@
//! Much of the syntax in Reid is directly inspired by rust, but mostly it is
//! driven by simplicity.
//!
//! Specifications and a bunch of [documentation for the language can be found
//! here](./documentation/).
//!
//! An example of a real whole program (a CPU pathtracer) can be found [in
//! examples/cpu_raytracer.reid](./examples/cpu_raytracer.reid), go have a look!
//!
//! Reid is currently able to (non-exhaustively):
//! - Do basic algebra (e.g. Add, Sub, Mult)
//! - Do basic algebra binary and unary-operations (e.g. Add, Sub, Div, Mult,
//! And, Not)
//! - Resolve complex one-liners correctly using PEDMAS (e.g. `5 + 2 * 5 - 5 *
//! 5` is calculated correctly)
//! - Handle borrows/derefs, pointers.
//! - Declare and call functions with varying parameters and return types
//! - Perform type-checking and type-inference such that return-types and
//! parameter types must always match.
//! - Do simple logic-operations (e.g. If/And/Or)
//! - Handle, access, define and initialize structs and arrays.
//! - Define and execute For/While loops
//! - Output detailed debug information
//! - Define extern functions that can be linked to outside modules such as
//! `libc`.
//! - Define custom binary operations for any two types that hasn't been defined
//! previously (such as `u16 + u32`).
//!
//! An example program of Reid, that calculates the 5th fibonacci number (and
//! uses Rust for highlighting) is:
//!
//! An example program of Reid, that calculates the 5th fibonacci number:
//! ```reid
//! fn main() -> u16 {
//! return fibonacci(5);
//! }
//!
//! fn fibonacci(n: u16) -> u16 {
//! if n <= 2 {
//! return 1;
@ -32,16 +46,13 @@
//! }
//! ```
//!
//! Currently missing relevant features (TODOs) are:
//! - ~~Arrays~~ (DONE)
//! - Structs (and custom types as such)
//! - ~~Extern functions~~ (DONE)
//! - ~~Strings~~ (DONE)
//! - Loops
//! - Debug Symbols
//! TODOs still (see README.md for more)
//! - Error handling
//! - Lexing & parsing of whitespace and comments as well
//! - LSP implementation
//! ```
use std::{path::PathBuf, thread, time::Duration};
use std::{collections::HashMap, path::PathBuf};
use ast::{
lexer::{self, FullToken, Token},
@ -56,9 +67,12 @@ use mir::{
};
use reid_lib::{compile::CompileOutput, Context};
use crate::ast::TopLevelStatement;
use crate::{
ast::TopLevelStatement,
mir::macros::{form_macros, MacroModule, MacroPass},
};
mod ast;
pub mod ast;
mod codegen;
pub mod error_raporting;
pub mod ld;
@ -79,6 +93,7 @@ pub fn parse_module<'map, T: Into<String>>(
map.set_tokens(id, tokens.clone());
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:#?}", &tokens);
Ok((id, tokens))
@ -113,6 +128,7 @@ pub fn compile_module<'map>(
};
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
dbg!(&ast_module);
Ok(ast_module.process(module_id))
@ -123,27 +139,17 @@ pub fn perform_all_passes<'map>(
module_map: &'map mut ErrorModules,
) -> Result<(), ReidError> {
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
dbg!(&context);
let mut binops = BinopMap::default();
for module in &mut context.modules {
for intrinsic in form_intrinsic_binops() {
binops
.set(
mir::pass::BinopKey {
params: (intrinsic.lhs.1.clone(), intrinsic.rhs.1.clone()),
operator: intrinsic.op,
},
mir::pass::ScopeBinopDef {
hands: (intrinsic.lhs.1.clone(), intrinsic.rhs.1.clone()),
operator: intrinsic.op,
return_ty: intrinsic.return_type.clone(),
},
)
.ok();
module.1.binop_defs.insert(0, intrinsic);
}
}
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:#}", &context);
let state = context.pass(&mut LinkerPass {
module_map,
is_lib: true,
})?;
for module in &mut context.modules {
for intrinsic in form_intrinsics() {
@ -152,18 +158,13 @@ pub fn perform_all_passes<'map>(
}
#[cfg(debug_assertions)]
println!("{:#}", &context);
let state = context.pass(&mut LinkerPass {
module_map,
is_lib: true,
})?;
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:-^100}", "LINKER OUTPUT");
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:#}", &context);
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
dbg!(&state);
if !state.errors.is_empty() {
@ -173,17 +174,69 @@ pub fn perform_all_passes<'map>(
));
}
let mut macro_modules: HashMap<_, MacroModule> = HashMap::new();
for (k, v) in &context.modules {
macro_modules.insert(k.clone(), v.into());
}
let mut macro_pass = MacroPass {
macros: form_macros(),
module_map: macro_modules,
};
let state = context.pass(&mut macro_pass)?;
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:-^100}", "MACRO OUTPUT");
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:#}", &context);
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
dbg!(&state);
if !state.errors.is_empty() {
return Err(ReidError::from_kind(
state.errors.iter().map(|e| e.clone().into()).collect(),
module_map.clone(),
));
}
let mut binops = BinopMap::default();
for module in &mut context.modules {
for intrinsic in form_intrinsic_binops() {
binops
.set(
mir::pass::BinopKey {
params: (intrinsic.lhs.ty.clone(), intrinsic.rhs.ty.clone()),
operator: intrinsic.op,
},
mir::pass::ScopeBinopDef {
hands: (intrinsic.lhs.ty.clone(), intrinsic.rhs.ty.clone()),
operator: intrinsic.op,
return_ty: intrinsic.return_type.clone(),
},
)
.ok();
module.1.binop_defs.insert(0, intrinsic);
}
}
let mut refs = TypeRefs::with_binops(binops);
let state = context.pass(&mut TypeInference { refs: &mut refs })?;
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:-^100}", "TYPE INFERRER OUTPUT");
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{}", &refs);
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:#}", &context);
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
dbg!(&state);
if !state.errors.is_empty() {
@ -200,10 +253,13 @@ pub fn perform_all_passes<'map>(
let state = context.pass(&mut TypeCheck { refs: &refs })?;
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:-^100}", "TYPECHECKER OUTPUT");
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:#}", &context);
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
dbg!(&state);
if !state.errors.is_empty() {
@ -217,6 +273,9 @@ pub fn perform_all_passes<'map>(
));
}
#[cfg(feature = "context_debug")]
dbg!(&context);
Ok(())
}
@ -241,8 +300,10 @@ pub fn compile_and_pass<'map>(
perform_all_passes(&mut mir_context, module_map)?;
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:-^100}", "FINAL OUTPUT");
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:#}", &mir_context);
let mut context = Context::new(format!("Reid ({})", env!("CARGO_PKG_VERSION")));
@ -252,6 +313,7 @@ pub fn compile_and_pass<'map>(
};
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{}", &codegen_modules.context);
let compiled = codegen_modules.compile(cpu, features);

View File

@ -49,6 +49,9 @@ impl Display for Module {
for typedef in &self.typedefs {
writeln!(inner_f, "{}", typedef)?;
}
for global in &self.globals {
writeln!(inner_f, "global {} = {}", global.name, global.kind)?;
}
for (ty, fun) in &self.associated_functions {
writeln!(inner_f, "(Assoc {}) {}", ty, fun)?;
}
@ -59,6 +62,26 @@ impl Display for Module {
}
}
impl Display for GlobalKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GlobalKind::Literal(literal) => Display::fmt(literal, f),
GlobalKind::Array(global_kinds) => {
f.write_char('[')?;
let mut iter = global_kinds.iter();
if let Some(global) = iter.next() {
Display::fmt(global, f)?;
while let Some(global) = iter.next() {
write!(f, ", ")?;
Display::fmt(global, f)?;
}
}
f.write_char(']')
}
}
}
}
impl Display for Import {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "import {}", self.0.join("::"))
@ -71,11 +94,11 @@ impl Display for BinopDefinition {
f,
"{}impl binop ({}: {:#}) {} ({}: {:#}) -> {:#} ",
if self.exported { "exported " } else { "" },
self.lhs.0,
self.lhs.1,
self.lhs.name,
self.lhs.ty,
self.op,
self.rhs.0,
self.rhs.1,
self.rhs.name,
self.rhs.ty,
self.return_type
)?;
Display::fmt(&self.fn_kind, f)
@ -132,7 +155,7 @@ impl Display for FunctionDefinition {
self.name,
self.parameters
.iter()
.map(|(n, t)| format!("{}: {:#}", n, t))
.map(|FunctionParam { name, ty, .. }| format!("{}: {:#}", name, ty))
.collect::<Vec<_>>()
.join(", "),
self.return_type
@ -256,9 +279,9 @@ impl Display for ExprKind {
let mut state = Default::default();
let mut inner_f = PadAdapter::wrap(f, &mut state);
let mut iter = items.iter();
if let Some((name, expr)) = iter.next() {
if let Some((name, expr, _)) = iter.next() {
write!(inner_f, "\n{}: {}", name, expr)?;
while let Some((name, expr)) = iter.next() {
while let Some((name, expr, _)) = iter.next() {
writeln!(inner_f, ",")?;
write!(inner_f, "{}: {}", name, expr)?;
}
@ -266,7 +289,7 @@ impl Display for ExprKind {
}
f.write_char('}')
}
ExprKind::Accessed(expression, type_kind, name) => {
ExprKind::Accessed(expression, type_kind, name, _) => {
Display::fmt(&expression, f)?;
write_access(f, name)?;
write!(f, "<{}>", type_kind)
@ -280,6 +303,7 @@ impl Display for ExprKind {
write!(f, "::")?;
Display::fmt(function_call, f)
}
ExprKind::GlobalRef(global_value, type_kind) => write!(f, "global<{}>(${})", type_kind, global_value),
}
}
}

View File

@ -1,3 +1,5 @@
use reid_lib::builder::TypeValue;
use crate::util::maybe;
use super::{typecheck::typerefs::TypeRefs, *};
@ -57,7 +59,7 @@ impl TypeKind {
}
}
pub fn size_of(&self) -> u64 {
pub fn size_of(&self, map: &HashMap<CustomTypeKey, TypeDefinition>) -> u64 {
match self {
TypeKind::Bool => 1,
TypeKind::I8 => 8,
@ -72,8 +74,16 @@ impl TypeKind {
TypeKind::U128 => 128,
TypeKind::Void => 0,
TypeKind::Char => 8,
TypeKind::Array(type_kind, len) => type_kind.size_of() * (*len as u64),
TypeKind::CustomType(..) => 32,
TypeKind::Array(type_kind, len) => type_kind.size_of(map) * (*len as u64),
TypeKind::CustomType(key) => match &map.get(key).unwrap().kind {
TypeDefinitionKind::Struct(struct_type) => {
let mut size = 0;
for field in &struct_type.0 {
size += field.1.size_of(map)
}
size
}
},
TypeKind::CodegenPtr(_) => 64,
TypeKind::Vague(_) => panic!("Tried to sizeof a vague type!"),
TypeKind::Borrow(..) => 64,
@ -405,7 +415,7 @@ impl Expression {
TypeKind::Array(Box::new(first.1), expressions.len() as u64),
))
}
Accessed(_, type_kind, _) => Ok((ReturnKind::Soft, type_kind.clone())),
Accessed(_, type_kind, ..) => Ok((ReturnKind::Soft, type_kind.clone())),
Struct(name, _) => Ok((
ReturnKind::Soft,
TypeKind::CustomType(CustomTypeKey(name.clone(), mod_id)),
@ -429,6 +439,7 @@ impl Expression {
Err(_) => Ok((ReturnKind::Soft, type_kind.clone())),
},
AssociatedFunctionCall(_, fcall) => fcall.return_type(),
GlobalRef(_, type_kind) => Ok((ReturnKind::Soft, type_kind.clone())),
}
}
@ -436,7 +447,7 @@ impl Expression {
match &self.0 {
ExprKind::Variable(var_ref) => Some(var_ref),
ExprKind::Indexed(lhs, _, _) => lhs.backing_var(),
ExprKind::Accessed(lhs, _, _) => lhs.backing_var(),
ExprKind::Accessed(lhs, ..) => lhs.backing_var(),
ExprKind::Borrow(expr, _) => expr.backing_var(),
ExprKind::Deref(expr) => expr.backing_var(),
ExprKind::Block(block) => block.backing_var(),
@ -448,6 +459,7 @@ impl Expression {
ExprKind::If(_) => None,
ExprKind::CastTo(expression, _) => expression.backing_var(),
ExprKind::AssociatedFunctionCall(..) => None,
ExprKind::GlobalRef(..) => None,
}
}
@ -493,6 +505,7 @@ impl Expression {
ExprKind::Deref(_) => None,
ExprKind::CastTo(expression, _) => expression.num_value()?,
ExprKind::AssociatedFunctionCall(..) => None,
ExprKind::GlobalRef(..) => None,
})
}
}

View File

@ -11,8 +11,8 @@ use crate::{
compile_module,
error_raporting::{ErrorModules, ReidError},
mir::{
pass::BinopKey, BinopDefinition, CustomTypeKey, FunctionDefinitionKind, SourceModuleId, TypeDefinition,
TypeKind,
pass::BinopKey, BinopDefinition, CustomTypeKey, FunctionDefinitionKind, FunctionParam, SourceModuleId,
TypeDefinition, TypeKind,
},
parse_module,
};
@ -186,6 +186,7 @@ impl<'map> Pass for LinkerPass<'map> {
.borrow_mut();
let import_name = unsafe { path.get_unchecked(1) };
let import_id = imported.module_id;
let mut imported_types = Vec::new();
@ -220,10 +221,10 @@ impl<'map> Pass for LinkerPass<'map> {
imported_types.extend(types);
let mut param_tys = Vec::new();
for (param_name, param_ty) in &func.parameters {
let types = import_type(&param_ty, false);
for param in &func.parameters {
let types = import_type(&param.ty, false);
imported_types.extend(types);
param_tys.push((param_name.clone(), param_ty.clone()));
param_tys.push(param.clone());
}
importer_module.functions.push(FunctionDefinition {
@ -234,6 +235,7 @@ impl<'map> Pass for LinkerPass<'map> {
return_type,
parameters: param_tys,
kind: super::FunctionDefinitionKind::Extern(true),
source: Some(imported.module_id),
});
} else if let Some(ty) = imported.typedefs.iter_mut().find(|f| f.name == *import_name) {
let external_key = CustomTypeKey(ty.name.clone(), ty.source_module);
@ -241,11 +243,11 @@ impl<'map> Pass for LinkerPass<'map> {
imported_types.push((external_key, true));
for binop in &mut imported.binop_defs {
if binop.lhs.1 != imported_ty && binop.rhs.1 != imported_ty {
if binop.lhs.ty != imported_ty && binop.rhs.ty != imported_ty {
continue;
}
let binop_key = BinopKey {
params: (binop.lhs.1.clone(), binop.rhs.1.clone()),
params: (binop.lhs.ty.clone(), binop.rhs.ty.clone()),
operator: binop.op,
};
if already_imported_binops.contains(&binop_key) {
@ -309,10 +311,10 @@ impl<'map> Pass for LinkerPass<'map> {
imported_types.extend(types);
let mut param_tys = Vec::new();
for (param_name, param_ty) in &func.parameters {
let types = import_type(&param_ty, false);
for param in &func.parameters {
let types = import_type(&param.ty, false);
imported_types.extend(types);
param_tys.push((param_name.clone(), param_ty.clone()));
param_tys.push(param.clone());
}
importer_module.associated_functions.push((
@ -325,6 +327,7 @@ impl<'map> Pass for LinkerPass<'map> {
return_type,
parameters: param_tys,
kind: super::FunctionDefinitionKind::Extern(true),
source: Some(import_id),
},
));
}
@ -424,7 +427,7 @@ impl<'map> Pass for LinkerPass<'map> {
if let Some(extern_types) = extern_types {
function.return_type = function.return_type.update_imported(*extern_types, mod_id);
for param in function.parameters.iter_mut() {
param.1 = param.1.update_imported(extern_types, mod_id);
param.ty = param.ty.update_imported(extern_types, mod_id);
}
}
}
@ -456,7 +459,7 @@ impl<'map> Pass for LinkerPass<'map> {
super::ExprKind::Indexed(.., type_kind, _) => {
*type_kind = type_kind.update_imported(extern_types, mod_id)
}
super::ExprKind::Accessed(.., type_kind, _) => {
super::ExprKind::Accessed(.., type_kind, _, _) => {
*type_kind = type_kind.update_imported(extern_types, mod_id)
}
super::ExprKind::BinOp(.., type_kind) => *type_kind = type_kind.update_imported(extern_types, mod_id),

276
reid/src/mir/macros.rs Normal file
View File

@ -0,0 +1,276 @@
use std::{collections::HashMap, path::PathBuf};
use crate::mir::{
self, FunctionCall, GlobalKind, GlobalValue, IfExpression, Literal, Module, SourceModuleId, TypeKind,
WhileStatement,
};
use super::pass::{Pass, PassResult, PassState};
pub trait MacroFunction: std::fmt::Debug {
fn generate<'ctx, 'a>(
&self,
module: &MacroModule,
params: &[mir::Literal],
prefix: String,
) -> Result<(Vec<GlobalValue>, mir::ExprKind), ErrorKind>;
}
#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum ErrorKind {
#[error("Should never be encountered!")]
Null,
#[error("No such macro {0} defined")]
NoSuchMacro(String),
#[error("Macro arguments may only be literals")]
InvalidMacroArgs,
#[error("Got {0} parameters, expected {1}")]
InvalidAmountOfParams(u32, u32),
#[error("Expected argument type of {0}, got {1}")]
InvalidArgumentType(TypeKind, TypeKind),
#[error("Error executing macro: {0}")]
MacroExecutionError(String),
}
type MacroModuleMap = HashMap<SourceModuleId, MacroModule>;
/// Struct used to implement a type-checking pass that can be performed on the
/// MIR.
pub struct MacroPass {
pub(crate) macros: HashMap<String, Box<dyn MacroFunction>>,
pub module_map: MacroModuleMap,
}
pub struct MacroModule {
path: Option<PathBuf>,
}
impl From<&Module> for MacroModule {
fn from(value: &Module) -> Self {
MacroModule {
path: value.path.clone(),
}
}
}
type MacroPassState<'map, 'st, 'sc> = PassState<'st, 'sc, (), ErrorKind>;
impl Pass for MacroPass {
type Data = ();
type TError = ErrorKind;
fn context(&mut self, _context: &mut mir::Context, mut _state: PassState<Self::Data, Self::TError>) -> PassResult {
Ok(())
}
fn module(&mut self, module: &mut mir::Module, mut state: PassState<Self::Data, Self::TError>) -> PassResult {
for function in &mut module.functions {
let globals = match &mut function.kind {
mir::FunctionDefinitionKind::Local(block, _) => block.gen_macros(self, &mut state, &self.module_map),
_ => Vec::new(),
};
module.globals.extend(globals);
}
Ok(())
}
}
impl mir::Block {
fn gen_macros(&mut self, data: &MacroPass, state: &mut MacroPassState, map: &MacroModuleMap) -> Vec<GlobalValue> {
let mut globals = Vec::new();
for statement in &mut self.statements {
globals.extend(statement.gen_macros(data, state, map));
}
if let Some((_, Some(return_expr))) = &mut self.return_expression {
globals.extend(return_expr.gen_macros(data, state, map));
}
globals
}
}
impl mir::Statement {
fn gen_macros(&mut self, data: &MacroPass, state: &mut MacroPassState, map: &MacroModuleMap) -> Vec<GlobalValue> {
let mut globals = Vec::new();
match &mut self.0 {
mir::StmtKind::Let(.., expr) => {
globals.extend(expr.gen_macros(data, state, map));
}
mir::StmtKind::Set(lhs, rhs) => {
globals.extend(lhs.gen_macros(data, state, map));
globals.extend(rhs.gen_macros(data, state, map));
}
mir::StmtKind::Import(_) => {}
mir::StmtKind::Expression(expr) => {
globals.extend(expr.gen_macros(data, state, map));
}
mir::StmtKind::While(WhileStatement { condition, block, .. }) => {
globals.extend(condition.gen_macros(data, state, map));
globals.extend(block.gen_macros(data, state, map));
}
};
globals
}
}
impl mir::Expression {
fn gen_macros(&mut self, data: &MacroPass, state: &mut MacroPassState, map: &MacroModuleMap) -> Vec<GlobalValue> {
let mut globals = Vec::new();
match &mut self.0 {
mir::ExprKind::FunctionCall(function_call) => {
for param in &mut function_call.parameters {
globals.extend(param.gen_macros(data, state, map));
}
if function_call.is_macro {
if let Some(existing_macro) = data.macros.get(&function_call.name) {
let mut literals = Vec::new();
for param in &mut function_call.parameters {
match &param.0 {
super::ExprKind::Literal(literal) => literals.push(literal.clone()),
_ => state.note_errors(&vec![ErrorKind::InvalidMacroArgs], param.1),
}
}
let (generated_globals, expr) = state.or_else(
existing_macro
.generate(
map.get(&state.scope.module_id.unwrap()).unwrap(),
&literals,
format!(
"macro.{}.{}.{}",
function_call.name, self.1.range.start, self.1.range.end
),
)
.map(|(globals, kind)| (globals, mir::Expression(kind, self.1))),
(Vec::new(), self.clone()),
self.1,
);
globals.extend(generated_globals);
*self = expr;
} else {
state.note_errors(
&vec![ErrorKind::NoSuchMacro(function_call.name.clone())],
function_call.meta,
);
}
}
}
mir::ExprKind::Variable(_) => {}
mir::ExprKind::Indexed(expression, _, expression1) => {
globals.extend(expression.gen_macros(data, state, map));
globals.extend(expression1.gen_macros(data, state, map));
}
mir::ExprKind::Accessed(expression, ..) => {
globals.extend(expression.gen_macros(data, state, map));
}
mir::ExprKind::Array(expressions) => {
for expression in expressions {
globals.extend(expression.gen_macros(data, state, map));
}
}
mir::ExprKind::Struct(_, items) => {
for item in items {
globals.extend(item.1.gen_macros(data, state, map));
}
}
mir::ExprKind::Literal(_) => {}
mir::ExprKind::BinOp(_, expression, expression1, _) => {
globals.extend(expression.gen_macros(data, state, map));
globals.extend(expression1.gen_macros(data, state, map));
}
mir::ExprKind::AssociatedFunctionCall(
_,
FunctionCall {
parameters, is_macro, ..
},
) => {
for expression in parameters {
globals.extend(expression.gen_macros(data, state, map));
}
}
mir::ExprKind::If(IfExpression(cond, lhs, rhs)) => {
globals.extend(cond.gen_macros(data, state, map));
globals.extend(lhs.gen_macros(data, state, map));
if let Some(rhs) = rhs.as_mut() {
globals.extend(rhs.gen_macros(data, state, map));
}
}
mir::ExprKind::Block(block) => {
globals.extend(block.gen_macros(data, state, map));
}
mir::ExprKind::Borrow(expression, _) => {
globals.extend(expression.gen_macros(data, state, map));
}
mir::ExprKind::Deref(expression) => {
globals.extend(expression.gen_macros(data, state, map));
}
mir::ExprKind::CastTo(expression, _) => {
globals.extend(expression.gen_macros(data, state, map));
}
mir::ExprKind::GlobalRef(..) => {}
}
globals
}
}
pub fn form_macros() -> HashMap<String, Box<dyn MacroFunction>> {
let mut macros: HashMap<String, Box<dyn MacroFunction>> = HashMap::new();
macros.insert("include_bytes".to_owned(), Box::new(IncludeBytes));
macros
}
#[derive(Debug)]
pub struct IncludeBytes;
impl MacroFunction for IncludeBytes {
fn generate<'ctx, 'a>(
&self,
module: &MacroModule,
literals: &[mir::Literal],
global_name: String,
) -> Result<(Vec<GlobalValue>, mir::ExprKind), ErrorKind> {
if literals.len() != 1 {
return Err(ErrorKind::InvalidAmountOfParams(literals.len() as u32, 1));
}
let literal = literals.get(0).unwrap();
let Literal::String(path) = literal else {
return Err(ErrorKind::InvalidArgumentType(
literal.as_type(),
TypeKind::UserPtr(Box::new(TypeKind::Char)),
));
};
let path = module
.path
.as_ref()
.expect("Module has no path!")
.parent()
.expect("Module path has no parent!")
.join(path);
let contents = match std::fs::read(path) {
Ok(content) => content,
Err(e) => return Err(ErrorKind::MacroExecutionError(format!("{}", e))),
};
let literals = contents
.iter()
.map(|c| GlobalKind::Literal(Literal::U8(*c)))
.collect::<Vec<_>>();
let len = literals.len();
let global = GlobalValue {
name: global_name.clone(),
kind: GlobalKind::Array(literals),
};
Ok((
vec![global.clone()],
mir::ExprKind::GlobalRef(
global_name,
TypeKind::Borrow(Box::new(TypeKind::Array(Box::new(TypeKind::U8), len as u64)), false),
),
))
}
}

View File

@ -13,6 +13,7 @@ use crate::{
mod fmt;
pub mod implement;
pub mod linker;
pub mod macros;
pub mod pass;
pub mod typecheck;
@ -40,16 +41,19 @@ impl Metadata {
}
pub fn into_positions(&self, tokens: &Vec<FullToken>) -> Option<(Position, Position)> {
let mut iter = tokens
.iter()
.skip(self.range.start)
.take(self.range.end - self.range.start);
if let Some(first) = iter.next() {
let last = iter.last().unwrap_or(first);
Some((first.position, last.position.add(last.token.len() as u32)))
} else {
None
}
self.range.into_position(tokens)
}
pub fn is_after(&self, token_idx: usize) -> bool {
return token_idx < self.range.start;
}
pub fn is_before(&self, token_idx: usize) -> bool {
return token_idx > self.range.end;
}
pub fn contains(&self, token_idx: usize) -> bool {
return token_idx >= self.range.start && token_idx <= self.range.end;
}
}
@ -258,9 +262,9 @@ pub struct Import(pub Vec<String>, pub Metadata);
pub enum ExprKind {
Variable(NamedVariableRef),
Indexed(Box<Expression>, TypeKind, Box<Expression>),
Accessed(Box<Expression>, TypeKind, String),
Accessed(Box<Expression>, TypeKind, String, Metadata),
Array(Vec<Expression>),
Struct(String, Vec<(String, Expression)>),
Struct(String, Vec<(String, Expression, Metadata)>),
Literal(Literal),
BinOp(BinaryOperator, Box<Expression>, Box<Expression>, TypeKind),
FunctionCall(FunctionCall),
@ -270,6 +274,7 @@ pub enum ExprKind {
Borrow(Box<Expression>, bool),
Deref(Box<Expression>),
CastTo(Box<Expression>, TypeKind),
GlobalRef(String, TypeKind),
}
#[derive(Debug, Clone)]
@ -284,6 +289,7 @@ pub struct FunctionCall {
pub name: String,
pub return_type: TypeKind,
pub parameters: Vec<Expression>,
pub is_macro: bool,
pub meta: Metadata,
}
@ -296,8 +302,16 @@ pub struct FunctionDefinition {
/// Whether this module is from an external module, and has been imported
pub is_imported: bool,
pub return_type: TypeKind,
pub parameters: Vec<(String, TypeKind)>,
pub parameters: Vec<FunctionParam>,
pub kind: FunctionDefinitionKind,
pub source: Option<SourceModuleId>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct FunctionParam {
pub name: String,
pub ty: TypeKind,
pub meta: Metadata,
}
pub enum SelfKind {
@ -378,9 +392,9 @@ pub enum TypeDefinitionKind {
#[derive(Debug)]
pub struct BinopDefinition {
pub lhs: (String, TypeKind),
pub lhs: FunctionParam,
pub op: BinaryOperator,
pub rhs: (String, TypeKind),
pub rhs: FunctionParam,
pub return_type: TypeKind,
pub fn_kind: FunctionDefinitionKind,
pub meta: Metadata,
@ -411,11 +425,24 @@ pub struct Module {
pub functions: Vec<FunctionDefinition>,
pub typedefs: Vec<TypeDefinition>,
pub binop_defs: Vec<BinopDefinition>,
pub globals: Vec<GlobalValue>,
pub path: Option<PathBuf>,
pub tokens: Vec<FullToken>,
pub is_main: bool,
}
#[derive(Debug, Clone)]
pub struct GlobalValue {
pub name: String,
pub kind: GlobalKind,
}
#[derive(Debug, Clone)]
pub enum GlobalKind {
Literal(Literal),
Array(Vec<GlobalKind>),
}
pub type ModuleMap = HashMap<SourceModuleId, Module>;
#[derive(Debug)]

View File

@ -187,7 +187,7 @@ impl<Data: Clone + Default> Scope<Data> {
key.clone(),
ScopeFunction {
ret: func.return_type,
params: func.parameters.iter().map(|(_, p)| p.clone()).collect(),
params: func.parameters.iter().map(|p| p.ty.clone()).collect(),
},
)
.unwrap();
@ -369,11 +369,11 @@ impl Context {
.binops
.set(
BinopKey {
params: (intrinsic.lhs.1.clone(), intrinsic.rhs.1.clone()),
params: (intrinsic.lhs.ty.clone(), intrinsic.rhs.ty.clone()),
operator: intrinsic.op,
},
ScopeBinopDef {
hands: (intrinsic.lhs.1.clone(), intrinsic.rhs.1.clone()),
hands: (intrinsic.lhs.ty.clone(), intrinsic.rhs.ty.clone()),
operator: intrinsic.op,
return_ty: intrinsic.return_type.clone(),
},
@ -407,11 +407,11 @@ impl Module {
.binops
.set(
BinopKey {
params: (binop.lhs.1.clone(), binop.rhs.1.clone()),
params: (binop.lhs.ty.clone(), binop.rhs.ty.clone()),
operator: binop.op,
},
ScopeBinopDef {
hands: (binop.lhs.1.clone(), binop.rhs.1.clone()),
hands: (binop.lhs.ty.clone(), binop.rhs.ty.clone()),
operator: binop.op,
return_ty: binop.return_type.clone(),
},
@ -426,7 +426,7 @@ impl Module {
function.name.clone(),
ScopeFunction {
ret: function.return_type.clone(),
params: function.parameters.iter().cloned().map(|v| v.1).collect(),
params: function.parameters.iter().cloned().map(|v| v.ty).collect(),
},
)
.ok();
@ -439,7 +439,7 @@ impl Module {
AssociatedFunctionKey(ty.clone(), function.name.clone()),
ScopeFunction {
ret: function.return_type.clone(),
params: function.parameters.iter().cloned().map(|v| v.1).collect(),
params: function.parameters.iter().cloned().map(|v| v.ty).collect(),
},
)
.ok();
@ -466,9 +466,9 @@ impl FunctionDefinition {
scope
.variables
.set(
param.0.clone(),
param.name.clone(),
ScopeVariable {
ty: param.1.clone(),
ty: param.ty.clone(),
mutable: false,
},
)
@ -503,6 +503,10 @@ impl Block {
statement.pass(pass, state, &mut scope, mod_id)?;
}
if let Some((_, Some(return_expr))) = &mut self.return_expression {
return_expr.pass(pass, state, &mut scope, mod_id)?;
}
pass.block(self, PassState::from(state, &mut scope, Some(mod_id)))
}
}
@ -581,7 +585,7 @@ impl Expression {
}
}
ExprKind::Struct(_, items) => {
for (_, expr) in items {
for (_, expr, _) in items {
expr.pass(pass, state, scope, mod_id)?;
}
}
@ -611,6 +615,7 @@ impl Expression {
ExprKind::Borrow(expression, _) => expression.pass(pass, state, scope, mod_id)?,
ExprKind::Deref(expression) => expression.pass(pass, state, scope, mod_id)?,
ExprKind::CastTo(expression, _) => expression.pass(pass, state, scope, mod_id)?,
ExprKind::GlobalRef(..) => {}
}
Ok(())
}

View File

@ -88,6 +88,8 @@ pub enum ErrorKind {
InvalidBinop(BinaryOperator, TypeKind, TypeKind),
#[error("Could not infer type for {0:?}. Try adding type annotations.")]
CouldNotInferType(String),
#[error("Arguments for a macro-function may only contain literals")]
MacroMustBeLiterals,
}
#[derive(Clone, Debug, PartialEq, Eq)]
@ -254,6 +256,16 @@ impl TypeKind {
let other_cat = other.category();
match (self, other) {
(TypeKind::UserPtr(_), TypeKind::UserPtr(_)) => Ok(other.clone()),
(TypeKind::Borrow(ty1, _), TypeKind::UserPtr(ty2)) => match *ty1.clone() {
TypeKind::Array(ty1, _) => {
if ty1 == *ty2 {
Ok(other.clone())
} else {
Err(ErrorKind::NotCastableTo(self.clone(), other.clone()))
}
}
_ => Err(ErrorKind::NotCastableTo(self.clone(), other.clone())),
},
(TypeKind::Char, TypeKind::U8) => Ok(other.clone()),
(TypeKind::U8, TypeKind::Char) => Ok(other.clone()),
_ => match (&self_cat, &other_cat) {

View File

@ -97,9 +97,10 @@ fn check_typedefs_for_recursion<'a, 'b>(
typedef.meta,
);
} else {
seen.insert(name.clone());
if let Some(inner_typedef) = defmap.get(name) {
check_typedefs_for_recursion(defmap, inner_typedef, seen.clone(), state)
let mut inner_seen = seen.clone();
inner_seen.insert(name.clone());
check_typedefs_for_recursion(defmap, inner_typedef, inner_seen.clone(), state)
}
}
}
@ -112,7 +113,7 @@ impl BinopDefinition {
fn typecheck(&mut self, typerefs: &TypeRefs, state: &mut TypecheckPassState) -> Result<TypeKind, ErrorKind> {
for param in vec![&self.lhs, &self.rhs] {
let param_t = state.or_else(
param.1.assert_known(state),
param.ty.assert_known(state),
TypeKind::Vague(Vague::Unknown),
self.signature(),
);
@ -120,13 +121,13 @@ impl BinopDefinition {
.scope
.variables
.set(
param.0.clone(),
param.name.clone(),
ScopeVariable {
ty: param_t.clone(),
mutable: param_t.is_mutable(),
},
)
.or(Err(ErrorKind::VariableAlreadyDefined(param.0.clone())));
.or(Err(ErrorKind::VariableAlreadyDefined(param.name.clone())));
state.ok(res, self.signature());
}
@ -150,7 +151,7 @@ impl FunctionDefinition {
fn typecheck(&mut self, typerefs: &TypeRefs, state: &mut TypecheckPassState) -> Result<TypeKind, ErrorKind> {
for param in &self.parameters {
let param_t = state.or_else(
param.1.assert_known(state),
param.ty.assert_known(state),
TypeKind::Vague(Vague::Unknown),
self.signature(),
);
@ -158,13 +159,13 @@ impl FunctionDefinition {
.scope
.variables
.set(
param.0.clone(),
param.name.clone(),
ScopeVariable {
ty: param_t.clone(),
mutable: param_t.is_mutable(),
},
)
.or(Err(ErrorKind::VariableAlreadyDefined(param.0.clone())));
.or(Err(ErrorKind::VariableAlreadyDefined(param.name.clone())));
state.ok(res, self.signature());
}
@ -193,7 +194,7 @@ impl FunctionDefinitionKind {
block.typecheck(&mut state.inner(), &typerefs, hint.into())
}
FunctionDefinitionKind::Extern(_) => Ok((ReturnKind::Soft, TypeKind::Vague(Vague::Unknown))),
FunctionDefinitionKind::Intrinsic(..) => Ok((ReturnKind::Soft, TypeKind::Vague(Vague::Unknown))),
FunctionDefinitionKind::Intrinsic(intrinsic) => Ok((ReturnKind::Soft, TypeKind::Vague(Vague::Unknown))),
}
}
}
@ -596,7 +597,7 @@ impl Expression {
}
}
}
ExprKind::Accessed(expression, type_kind, field_name) => {
ExprKind::Accessed(expression, type_kind, field_name, _) => {
// Resolve expected type
let expected_ty = type_kind.resolve_ref(typerefs);
@ -640,7 +641,7 @@ impl Expression {
HashSet::new()
};
for (field_name, field_expr) in items {
for (field_name, field_expr, _) in items {
// Get expected type, or error if field does not exist
let expected_ty = state.or_else(
struct_def
@ -767,6 +768,10 @@ impl Expression {
Ok(function_call.return_type.clone().resolve_ref(typerefs))
}
}
ExprKind::GlobalRef(global_value, type_kind) => Ok(self
.return_type(typerefs, state.scope.module_id.unwrap())
.map(|r| r.1)
.unwrap()),
}
}
}

View File

@ -87,16 +87,16 @@ impl<'t> Pass for TypeInference<'t> {
let mut seen_binops = HashSet::new();
for binop in &module.binop_defs {
let binop_key = BinopKey {
params: (binop.lhs.1.clone(), binop.rhs.1.clone()),
params: (binop.lhs.ty.clone(), binop.rhs.ty.clone()),
operator: binop.op,
};
if seen_binops.contains(&binop_key) || (binop.lhs == binop.rhs && binop.lhs.1.category().is_simple_maths())
if seen_binops.contains(&binop_key) || (binop.lhs == binop.rhs && binop.lhs.ty.category().is_simple_maths())
{
state.note_errors(
&vec![ErrorKind::BinaryOpAlreadyDefined(
binop.op,
binop.lhs.1.clone(),
binop.rhs.1.clone(),
binop.lhs.ty.clone(),
binop.rhs.ty.clone(),
)],
binop.signature(),
);
@ -107,7 +107,7 @@ impl<'t> Pass for TypeInference<'t> {
.set(
binop_key,
crate::mir::pass::ScopeBinopDef {
hands: (binop.lhs.1.clone(), binop.rhs.1.clone()),
hands: (binop.lhs.ty.clone(), binop.rhs.ty.clone()),
operator: binop.op,
return_ty: binop.return_type.clone(),
},
@ -138,20 +138,20 @@ impl BinopDefinition {
fn infer_types(&mut self, type_refs: &TypeRefs, state: &mut TypecheckPassState) -> Result<(), ErrorKind> {
let scope_hints = ScopeTypeRefs::from(type_refs);
let lhs_ty = state.or_else(self.lhs.1.assert_unvague(), Vague(Unknown), self.signature());
let lhs_ty = state.or_else(self.lhs.ty.assert_unvague(), Vague(Unknown), self.signature());
state.ok(
scope_hints
.new_var(self.lhs.0.clone(), false, &lhs_ty)
.or(Err(ErrorKind::VariableAlreadyDefined(self.lhs.0.clone()))),
.new_var(self.lhs.name.clone(), false, &lhs_ty)
.or(Err(ErrorKind::VariableAlreadyDefined(self.lhs.name.clone()))),
self.signature(),
);
let rhs_ty = state.or_else(self.rhs.1.assert_unvague(), Vague(Unknown), self.signature());
let rhs_ty = state.or_else(self.rhs.ty.assert_unvague(), Vague(Unknown), self.signature());
state.ok(
scope_hints
.new_var(self.rhs.0.clone(), false, &rhs_ty)
.or(Err(ErrorKind::VariableAlreadyDefined(self.rhs.0.clone()))),
.new_var(self.rhs.name.clone(), false, &rhs_ty)
.or(Err(ErrorKind::VariableAlreadyDefined(self.rhs.name.clone()))),
self.signature(),
);
@ -170,10 +170,11 @@ impl FunctionDefinition {
fn infer_types(&mut self, type_refs: &TypeRefs, state: &mut TypecheckPassState) -> Result<(), ErrorKind> {
let scope_refs = ScopeTypeRefs::from(type_refs);
for param in &self.parameters {
let param_t = state.or_else(param.1.assert_unvague(), Vague(Unknown), self.signature());
let param_t = state.or_else(param.ty.assert_unvague(), Vague(Unknown), self.signature());
let mutable = matches!(param_t, TypeKind::Borrow(_, true));
let res = scope_refs
.new_var(param.0.clone(), false, &param_t)
.or(Err(ErrorKind::VariableAlreadyDefined(param.0.clone())));
.new_var(param.name.clone(), mutable, &param_t)
.or(Err(ErrorKind::VariableAlreadyDefined(param.name.clone())));
state.ok(res, self.signature());
}
@ -526,7 +527,7 @@ impl Expression {
}
}
}
ExprKind::Accessed(expression, type_kind, field_name) => {
ExprKind::Accessed(expression, type_kind, field_name, _) => {
let expr_ty = expression.infer_types(state, type_refs)?;
// Check that the resolved type is at least a struct, no
@ -605,7 +606,7 @@ impl Expression {
.parameters
.get_mut(0)
.expect("Unknown-type associated function NEEDS to always have at least one parameter!");
let param_ty = first_param.infer_types(state, type_refs).unwrap().resolve_deep();
let param_ty = first_param.infer_types(state, type_refs)?.resolve_deep();
*type_kind = state
.or_else(
param_ty.ok_or(ErrorKind::CouldNotInferType(format!("{}", first_param))),
@ -613,24 +614,36 @@ impl Expression {
first_param.1,
)
.resolve_ref(type_refs.types);
let backing_var = first_param.backing_var().expect("todo").1.clone();
if let TypeKind::Borrow(inner, _) = type_kind {
if let TypeKind::Borrow(..) = *inner.clone() {
*type_kind = type_kind.unroll_borrow();
let ExprKind::Borrow(val, _) = &first_param.0 else {
panic!()
};
*first_param = *val.clone();
}
}
if let Some((mutable, _)) = type_refs.find_var(&backing_var) {
if !mutable {
first_param.remove_borrow_mutability();
let backing_var = first_param.backing_var();
let is_mutable = if let Some(backing_var) = first_param.backing_var() {
if let Some((mutable, _)) = type_refs.find_var(&backing_var.1) {
mutable
} else {
return Err(ErrorKind::VariableNotDefined(backing_var.1.clone()));
}
} else {
return Err(ErrorKind::VariableNotDefined(backing_var));
false
};
if backing_var.is_some() {
if let TypeKind::Borrow(inner, _) = type_kind {
if let TypeKind::Borrow(..) = *inner.clone() {
*type_kind = type_kind.unroll_borrow();
let ExprKind::Borrow(val, _) = &first_param.0 else {
panic!()
};
*first_param = *val.clone();
}
}
} else {
let ExprKind::Borrow(val, _) = &first_param.0 else {
panic!()
};
*first_param = *val.clone();
}
if !is_mutable {
first_param.remove_borrow_mutability();
}
}
}
@ -660,6 +673,10 @@ impl Expression {
// Provide function return type
Ok(type_refs.from_type(&fn_call.ret).unwrap())
}
ExprKind::GlobalRef(global_value, type_kind) => Ok(self
.return_type(type_refs.types, state.scope.module_id.unwrap())
.map(|r| type_refs.from_type(&r.1).unwrap())
.unwrap()),
}
}

View File

@ -6,12 +6,12 @@ use reid::{
mir::{self},
parse_module, perform_all_passes,
};
use reid_lib::Context;
use reid_lib::{compile::CompileOutput, Context};
use util::assert_err;
mod util;
fn test(source: &str, name: &str, expected_exit_code: Option<i32>) {
fn test_compile(source: &str, name: &str) -> CompileOutput {
assert_err(assert_err(std::panic::catch_unwind(|| {
let mut map = Default::default();
let (id, tokens) = assert_err(parse_module(source, name, &mut map));
@ -24,7 +24,14 @@ fn test(source: &str, name: &str, expected_exit_code: Option<i32>) {
let codegen = assert_err(mir_context.codegen(&context));
let output = codegen.compile(None, Vec::new()).output();
Ok::<_, ()>(codegen.compile(None, Vec::new()).output())
})))
}
fn test(source: &str, name: &str, expected_exit_code: Option<i32>) {
assert_err(assert_err(std::panic::catch_unwind(|| {
let output = test_compile(source, name);
let time = SystemTime::now();
let in_path = PathBuf::from(format!(
"/tmp/temp-{}.o",
@ -149,6 +156,16 @@ fn associated_functions() {
test(
include_str!("../../examples/associated_functions.reid"),
"test",
Some(32),
Some(4),
);
}
#[test]
fn mutable_inner_functions() {
test(include_str!("../../examples/mutable_inner.reid"), "test", Some(0));
}
#[test]
fn cpu_raytracer_compiles() {
test_compile(include_str!("../../examples/cpu_raytracer.reid"), "test");
}