Compare commits
55 Commits
lexical-sc
...
main
Author | SHA1 | Date | |
---|---|---|---|
8d0e3d03d5 | |||
34e31549b3 | |||
0ba25db4c8 | |||
314f44304a | |||
08f7725ce7 | |||
f89b26bf74 | |||
4fada0036c | |||
4f0ee72c83 | |||
deed96bbfd | |||
1e094eeea0 | |||
3adb745576 | |||
8f7b785664 | |||
c7aacfe756 | |||
b71c253942 | |||
7d3aaa143a | |||
6619f1f0a9 | |||
bc59b6f575 | |||
c262418f88 | |||
2dd3a5904b | |||
ff1da716e9 | |||
7c6d634287 | |||
b0442e5685 | |||
2303bf757a | |||
7234cad5f0 | |||
baa068a371 | |||
8b1d1084a6 | |||
f5f55079a9 | |||
baa7bafafc | |||
f700c577f1 | |||
ebe7fc8d75 | |||
140d963d9b | |||
480ba5155a | |||
2207c3df83 | |||
735c3231b1 | |||
50a875ad21 | |||
30257e1a2b | |||
a7ac974f46 | |||
3d8f4bbd24 | |||
33ed1fd813 | |||
67a5fcd002 | |||
80bdf4eba8 | |||
bd8994bb37 | |||
2e153922f1 | |||
ea6458dddc | |||
014ba2f638 | |||
89850d7b4f | |||
13be3e9c02 | |||
5026013df3 | |||
beaba4e7de | |||
e14efa2ea7 | |||
ccee457cf4 | |||
3f81104c99 | |||
b643c13582 | |||
e412a2e1d7 | |||
1b1a5934f5 |
959
Cargo.lock
generated
959
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,6 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"reid",
|
||||
"reid-llvm-lib"
|
||||
]
|
||||
"reid-llvm-lib",
|
||||
"reid-lsp"
|
||||
]
|
||||
|
28
README.md
28
README.md
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
```
|
@ -1,6 +0,0 @@
|
||||
|
||||
pub fn main() -> u32 {
|
||||
let b = 4;
|
||||
let c = b + 4;
|
||||
return c;
|
||||
}
|
@ -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]);
|
||||
|
@ -1,6 +0,0 @@
|
||||
import std::String;
|
||||
import std::print;
|
||||
|
||||
fn main() {
|
||||
print(String::new() + "hello")
|
||||
}
|
@ -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
466
examples/cpu_raytracer.reid
Normal 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);
|
||||
}
|
@ -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
9
examples/macro_easy.reid
Normal 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];
|
||||
}
|
1
examples/macro_easy_file.txt
Normal file
1
examples/macro_easy_file.txt
Normal file
@ -0,0 +1 @@
|
||||
hello
|
25
examples/mutable_inner.reid
Normal file
25
examples/mutable_inner.reid
Normal 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;
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
6
reid-lsp/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
.vscode
|
||||
node_modules
|
||||
dist
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
tsconfig.tsbuildinfo
|
1
reid-lsp/.npmrc
Normal file
1
reid-lsp/.npmrc
Normal file
@ -0,0 +1 @@
|
||||
enable-pre-post-scripts = true
|
5
reid-lsp/.vscode-test.mjs
Normal file
5
reid-lsp/.vscode-test.mjs
Normal 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
15
reid-lsp/.vscodeignore
Normal 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
9
reid-lsp/CHANGELOG.md
Normal 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
11
reid-lsp/Cargo.toml
Normal 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
71
reid-lsp/README.md
Normal 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!**
|
27
reid-lsp/client/package.json
Normal file
27
reid-lsp/client/package.json
Normal 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"
|
||||
}
|
||||
}
|
72
reid-lsp/client/src/extension.ts
Normal file
72
reid-lsp/client/src/extension.ts
Normal 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();
|
||||
}
|
15
reid-lsp/client/src/test/extension.test.ts
Normal file
15
reid-lsp/client/src/test/extension.test.ts
Normal 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));
|
||||
});
|
||||
});
|
24
reid-lsp/client/tsconfig.json
Normal file
24
reid-lsp/client/tsconfig.json
Normal 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"
|
||||
]
|
||||
}
|
28
reid-lsp/eslint.config.mjs
Normal file
28
reid-lsp/eslint.config.mjs
Normal 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
84
reid-lsp/package.json
Normal 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
490
reid-lsp/src/main.rs
Normal 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(¶ms.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()),
|
||||
}
|
||||
}
|
395
reid-lsp/syntaxes/grammar.json
Normal file
395
reid-lsp/syntaxes/grammar.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
232
reid-lsp/syntaxes/grammar.yaml
Normal file
232
reid-lsp/syntaxes/grammar.yaml
Normal 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
29
reid-lsp/tsconfig.json
Normal 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/"
|
||||
},
|
||||
]
|
||||
}
|
48
reid-lsp/vsc-extension-quickstart.md
Normal file
48
reid-lsp/vsc-extension-quickstart.md
Normal 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 doesn’t 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).
|
48
reid-lsp/webpack.config.js
Normal file
48
reid-lsp/webpack.config.js
Normal 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];
|
@ -10,6 +10,8 @@ edition = "2021"
|
||||
default = ["color"]
|
||||
|
||||
color = ["colored"]
|
||||
log_output = []
|
||||
context_debug = []
|
||||
|
||||
[dependencies]
|
||||
## Make it easier to generate errors
|
||||
|
@ -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(())
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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>(
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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)> {
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
144
reid/src/lib.rs
144
reid/src/lib.rs
@ -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);
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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(¶m_ty, false);
|
||||
for param in &func.parameters {
|
||||
let types = import_type(¶m.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(¶m_ty, false);
|
||||
for param in &func.parameters {
|
||||
let types = import_type(¶m.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
276
reid/src/mir/macros.rs
Normal 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 ¶m.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),
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
@ -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)]
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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, ¶m_t)
|
||||
.or(Err(ErrorKind::VariableAlreadyDefined(param.0.clone())));
|
||||
.new_var(param.name.clone(), mutable, ¶m_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()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user