Compare commits

..

No commits in common. "main" and "1.0.0-beta.3" have entirely different histories.

70 changed files with 5383 additions and 4428 deletions

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "reid-llvm-lib"]
path = reid-llvm-lib
url = gitea@git.teascade.net:teascade/reid-llvm-lib.git

326
Cargo.lock generated
View File

@ -17,59 +17,12 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "anyhow"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
[[package]]
name = "argh"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34ff18325c8a36b82f992e533ece1ec9f9a9db446bd1c14d4f936bac88fcd240"
dependencies = [
"argh_derive",
"argh_shared",
"rust-fuzzy-search",
]
[[package]]
name = "argh_derive"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb7b2b83a50d329d5d8ccc620f5c7064028828538bdf5646acd60dc1f767803"
dependencies = [
"argh_shared",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "argh_shared"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a464143cc82dedcdc3928737445362466b7674b5db4e2eb8e869846d6d84f4f6"
dependencies = [
"serde",
]
[[package]]
name = "async-trait"
version = "0.1.88"
@ -125,12 +78,6 @@ version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "bumpalo"
version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "bytes"
version = "1.10.1"
@ -152,20 +99,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "chrono"
version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-link",
]
[[package]]
name = "colored"
version = "3.0.0"
@ -175,12 +108,6 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
@ -323,42 +250,12 @@ version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
name = "hermit-abi"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]]
name = "httparse"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "iana-time-zone"
version = "0.1.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"log",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "icu_collections"
version = "2.0.0"
@ -477,33 +374,12 @@ dependencies = [
"libc",
]
[[package]]
name = "is-terminal"
version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
dependencies = [
"hermit-abi",
"libc",
"windows-sys",
]
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "js-sys"
version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
@ -546,12 +422,6 @@ dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "lsp-types"
version = "0.94.1"
@ -784,42 +654,31 @@ checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a"
[[package]]
name = "reid"
version = "1.0.0"
version = "1.0.0-beta.2"
dependencies = [
"argh",
"colored",
"log",
"reid-lib",
"stderrlog",
"thiserror",
]
[[package]]
name = "reid-language-server"
version = "1.0.0"
dependencies = [
"dashmap 6.1.0",
"reid",
"serde",
"serde_json",
"socket",
"tokio",
"tower-lsp",
]
[[package]]
name = "reid-lib"
version = "1.0.0"
version = "1.0.0-beta.2"
dependencies = [
"llvm-sys",
"thiserror",
]
[[package]]
name = "rust-fuzzy-search"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a157657054ffe556d8858504af8a672a054a6e0bd9e8ee531059100c0fa11bb2"
name = "reid-lsp"
version = "0.1.0"
dependencies = [
"dashmap 6.1.0",
"reid",
"socket",
"tokio",
"tower-lsp",
]
[[package]]
name = "rustc-demangle"
@ -827,12 +686,6 @@ version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
version = "1.0.20"
@ -947,19 +800,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "stderrlog"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c910772f992ab17d32d6760e167d2353f4130ed50e796752689556af07dc6b"
dependencies = [
"chrono",
"is-terminal",
"log",
"termcolor",
"thread_local",
]
[[package]]
name = "syn"
version = "2.0.104"
@ -982,15 +822,6 @@ dependencies = [
"syn",
]
[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
[[package]]
name = "thiserror"
version = "1.0.69"
@ -1011,15 +842,6 @@ dependencies = [
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
dependencies = [
"cfg-if",
]
[[package]]
name = "tinystr"
version = "0.8.1"
@ -1195,132 +1017,6 @@ version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasm-bindgen"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
dependencies = [
"bumpalo",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
dependencies = [
"unicode-ident",
]
[[package]]
name = "winapi-util"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys",
]
[[package]]
name = "windows-core"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-link"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
[[package]]
name = "windows-result"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.59.0"

View File

@ -73,12 +73,7 @@ Big features that I want later but are not necessary:
- ~~Asymmetric binary operations (e.g. string + u32)~~ (DONE)
- ~~Error handling~~ (Not Doing It)
- ~~Lexing & parsing of whitespace and comments as well~~ (DONE)
- ~~LSP implementation~~ (DONE)
- ~~Syntax Highlighting~~ (DONE)
- ~~Semantic Highlighting~~ (DONE)
- ~~Go-To-Definition~~ (DONE)
- ~~Find-All-References~~ (DONE)
- ~~Refactoring~~ (DONE)
- LSP implementation
Smaller features:
- ~~Hex-numbers~~ (DONE)

View File

@ -7,6 +7,16 @@ 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; _]`
@ -33,155 +43,15 @@ Returns a null-pointer of type `T`.
i32::null(); // Returns *i32 (null-ptr)
```
#### `<T>::is_null(val: T) -> bool`
Returns a boolean representing if `val` is a nullptr or not.
```rust
i32::is_null(i32::null()); // Returns true
```
#### `<T>::malloc(size: u64) -> *T`
Allocates `T::sizeof() * size` bytes and returns a pointer to `T`.
**Note:** This does not seem to work correctly currently.
```rust
i32::malloc(30); // Returns *i32
// Equivalent to
malloc(i32::sizeof() * 30) as *i32
```
#### `<T>::memcpy(destination: *T, source: *T, size: u64)`
Copies `T::sizeof() * size` bytes from pointer `source` to pointer
`destination`.
```rust
let a = i32::malloc(30);
let b = i32::malloc(30);
// Copies the contents from b to a
i32::memcpy(a, b, 30);
```
#### `<T>::min(a: T, b: T) -> T`
*Note: (only on integer- and floating-point values)*
Returns the smaller of `a` and `b`.
#### `<T>::max(a: T, b: T) -> T`
*Note: (only on integer- and floating-point values)*
Returns the larger of `a` and `b`.
#### `<T>::abs(value: T) -> T`
*Note: (only on signed integer and floating-point values)*
Returns the absolute value of `value`.
#### `<T>::sqrt(value: T) -> T`
*Note: (only on floating-point numbers)*
Calculates the square-root of `value`
#### `<T>::pow(value: T, exponent: T) -> T`
*Note: (only on floating-point numbers)*
Returns `value` raised to the exponent of `exponent`.
#### `<T>::powi(value: T, exponent: u64) -> T`
*Note: (only on floating-point numbers)*
Returns `value` raised to the exponent of `exponent`.
#### `<T>::sin(value: T) -> T`
*Note: (only on floating-point numbers)*
Calculates sine of `value`
#### `<T>::cos(value: T) -> T`
*Note: (only on floating-point numbers)*
Calculates cosine of `value`
#### `<T>::tan(value: T) -> T`
*Note: (only on floating-point numbers)*
Calculates tangent of `value`
#### `<T>::asin(value: T) -> T`
*Note: (only on floating-point numbers)*
Calculates arcsine of `value`
#### `<T>::acos(value: T) -> T`
*Note: (only on floating-point numbers)*
Calculates arccosine of `value`
#### `<T>::atan(value: T) -> T`
*Note: (only on floating-point numbers)*
Calculates arctangent of `value`
#### `<T>::atan2(value: T) -> T`
*Note: (only on floating-point numbers)*
Calculates 2-argument arctangent of `value`
#### `<T>::sinh(value: T) -> T`
*Note: (only on floating-point numbers)*
Calculates hyperbolic sine of `value`
#### `<T>::cosh(value: T) -> T`
*Note: (only on floating-point numbers)*
Calculates hyperbolic cosine of `value`
#### `<T>::tanh(value: T) -> T`
*Note: (only on floating-point numbers)*
Calculates hyperbolic tangent of `value`
#### `<T>::log(value: T) -> T`
*Note: (only on floating-point numbers)*
Returns logₑ of `value`
#### `<T>::log2(value: T) -> T`
*Note: (only on floating-point numbers)*
Returns log₂ of `value`
#### `<T>::log10(value: T) -> T`
*Note: (only on floating-point numbers)*
Returns log₁₀ of `value`
#### `<T>::round(value: T) -> T`
*Note: (only on floating-point numbers)*
Rounds `value` to the nearest integer
#### `<T>::trunc(value: T) -> T`
*Note: (only on floating-point numbers)*
Truncates `value` to the integer nearest to `0`.
#### `<T>::ceil(value: T) -> T`
*Note: (only on floating-point numbers)*
Rounds `value` towards positive infinity.
#### `<T>::floor(value: T) -> T`
*Note: (only on floating-point numbers)*
Rounds `value` towards negative infinity.
#### `<T>::even(value: T) -> T`
*Note: (only on floating-point numbers)*
Rounds `value` to the closest even integer.
```

View File

@ -64,6 +64,10 @@ _deprecated: Use `String::concat()`_
## General
#### `pub fn allocate(size: u64) -> *u8`
Unsafely allocates `size` bytes of memory from the stack, and returns a pointer to it, which must be manually freed.
## Maths
#### `pub fn clamp(min: f32, max: f32, value: f32) -> f32`

View File

@ -20,7 +20,7 @@ fn main() -> u32 {
let boop: f32 = 3;
let mut a = &mut value;
*a.second[2] = 5;
return *a.second[2];

View File

@ -2,14 +2,11 @@ import std::print;
import std::from_str;
import std::String;
/// Asd
struct Otus {
field: u32,
}
impl Otus {
/// Some test documentation here.
/// On a second line
fn test(&self) -> u32 {
*self.field
}
@ -21,18 +18,12 @@ impl i32 {
}
}
/// Hello there!
fn test() {
}
fn main() -> u32 {
let otus = Otus { field: 17 };
print(from_str("otus: ") + Otus::test(&otus) as u64);
print(from_str("i32: ") + i32::test(54) as u64);
print(from_str("sizeof i32: ") + i32::sizeof());
let nullptr = i32::null();
let mut list = u64::malloc(15);
list[4] = 17;

View File

@ -22,8 +22,6 @@ fn main() -> u32 {
let otus = Otus { field: 17 };
print(from_str("otus: ") + otus.test() as u64);
otus.field;
return otus.test();
}

View File

@ -1,6 +1,5 @@
// Arithmetic, function calls and imports!
/// Test stuff
fn changer(param: &mut u32) {
*param = 17;
}

View File

@ -1,10 +0,0 @@
import complicated_imported::Foo;
import complicated_imported::A;
import complicated_imported::AResult;
fn main() -> i32 {
let foo = Foo {};
let a = A::new();
foo.foo(&a.a);
return 0;
}

View File

@ -1,12 +0,0 @@
struct A {}
struct AResult { a: A }
impl A {
pub fn new() -> AResult {
AResult { a: A {} }
}
}
struct Foo {}
impl Foo {
pub fn foo(&self, a: &A) {}
}

View File

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

View File

@ -1,6 +0,0 @@
import foreign_struct::Vec2;
fn main() -> u32 {
let a = Vec2 {x: 16, y: 32};
return a.x;
}

View File

@ -1 +0,0 @@
struct Vec2 { x: u32, y: u32 }

View File

@ -1,9 +0,0 @@
fn main() -> i32 {
for i in 0..1 {
let j = i;
if i != j {
return 1;
}
}
return 0;
}

View File

@ -1,15 +0,0 @@
extern fn printf(message: *char, num: f64);
fn main() -> i32 {
let b = 5;
let mut otus = i32::malloc(1);
otus[0] = 10500300;
let potus = i32::malloc(1);
i32::memcpy(potus, otus, 1);
printf("log10 %f\n", f64::round(123.3) as f64);
printf("sqrt %f\n", f64::sqrt(2) as f64);
printf("log10 %f\n", potus[0] as f64);
return potus[0];
}

View File

@ -1,8 +0,0 @@
struct Otus {
field: u32,
}
pub fn test() -> Otus {
Otus {field: 4}
}

View File

@ -1,11 +0,0 @@
// Arithmetic, function calls and imports!
import module_importee::Otus;
import module_importee::test;
fn main() -> u32 {
let value = 0b110;
let other = 0o17;
return value * other + test().field * -value;
}

View File

@ -1,4 +0,0 @@
fn main() -> bool {
let ptr = i32::null();
return i32::is_null(ptr);
}

View File

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

@ -1 +0,0 @@
Subproject commit 100cd96a6dc3bb882ce60e78c4eab47e77fdd8c4

12
reid-llvm-lib/Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "reid-lib"
version = "1.0.0-beta.2"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
## LLVM Bindings
llvm-sys = {version ="201.0.1" }
## Make it easier to generate errors
thiserror = "1.0.44"

View File

@ -0,0 +1,56 @@
use reid_lib::{CmpPredicate, ConstValueKind, Context, FunctionFlags, Instr, TerminatorKind, Type};
fn main() {
use ConstValueKind::*;
use Instr::*;
let context = Context::new("libtest");
let module = context.module("test", true);
let main = module.function("main", None, Type::I32, Vec::new(), FunctionFlags::default());
let mut m_entry = main.block("entry");
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();
let mut f_entry = fibonacci.block("entry");
let num_3 = f_entry.build_named("const", Constant(I32(3))).unwrap();
let param_n = f_entry.build_named("param", Param(0)).unwrap();
let cond = f_entry
.build_named("cmp", ICmp(CmpPredicate::LT, param_n, num_3))
.unwrap();
let mut then_b = fibonacci.block("then");
let mut else_b = fibonacci.block("else");
f_entry
.terminate(TerminatorKind::CondBr(cond, then_b.value(), else_b.value()))
.unwrap();
let ret_const = then_b.build_named("const", Constant(I32(1))).unwrap();
then_b.terminate(TerminatorKind::Ret(ret_const)).unwrap();
let const_1 = else_b.build_named("const", Constant(I32(1))).unwrap();
let const_2 = else_b.build_named("const", Constant(I32(2))).unwrap();
let param_1 = else_b.build_named("sub", Sub(param_n, const_1)).unwrap();
let param_2 = else_b.build_named("sub", Sub(param_n, const_2)).unwrap();
let call_1 = else_b
.build_named("fibonacci", FunctionCall(fibonacci.value(), vec![param_1]))
.unwrap();
let call_2 = else_b
.build_named("fibonacci", FunctionCall(fibonacci.value(), vec![param_2]))
.unwrap();
let add = else_b.build_named("add", Add(call_1, call_2)).unwrap();
else_b.terminate(TerminatorKind::Ret(add)).unwrap();
context.compile(None, Vec::new());
}

View File

@ -0,0 +1,782 @@
//! This module contains simply [`Builder`] and it's related utility Values.
//! Builder is the actual struct being modified when building the LLIR.
use std::{cell::RefCell, rc::Rc};
use crate::{
Block, BlockData, CompileResult, ConstValueKind, CustomTypeKind, ErrorKind, FunctionData, Instr, InstructionData,
ModuleData, NamedStruct, TerminatorKind, Type, TypeCategory, TypeData,
debug_information::{
DebugInformation, DebugLocationValue, DebugMetadataValue, DebugScopeValue, InstructionDebugRecordData,
},
util::match_types,
};
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
pub struct ModuleValue(pub(crate) usize);
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
pub struct TypeValue(pub(crate) ModuleValue, pub(crate) usize);
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
pub struct FunctionValue(pub(crate) ModuleValue, pub(crate) usize);
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
pub struct BlockValue(pub(crate) FunctionValue, pub(crate) usize);
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
pub struct InstructionValue(pub(crate) BlockValue, pub(crate) usize);
#[derive(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,
pub(crate) data: ModuleData,
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)]
pub struct TypeHolder {
pub(crate) value: TypeValue,
pub(crate) data: TypeData,
}
#[derive(Clone)]
pub struct FunctionHolder {
pub(crate) value: FunctionValue,
pub(crate) data: FunctionData,
pub(crate) blocks: Vec<BlockHolder>,
/// Debug scope value of this current function
pub(crate) debug_info: Option<DebugScopeValue>,
}
#[derive(Clone)]
pub struct BlockHolder {
pub(crate) value: BlockValue,
pub(crate) data: BlockData,
pub(crate) instructions: Vec<InstructionHolder>,
}
#[derive(Clone)]
pub struct InstructionHolder {
pub(crate) value: InstructionValue,
pub(crate) data: InstructionData,
pub(crate) name: String,
pub(crate) record: Option<InstructionDebugRecordData>,
}
#[derive(Clone)]
pub(crate) struct Builder {
pub(crate) modules: Rc<RefCell<Vec<ModuleHolder>>>,
pub(crate) producer: String,
}
impl Builder {
pub fn new(producer: String) -> Builder {
Builder {
modules: Rc::new(RefCell::new(Vec::new())),
producer,
}
}
pub(crate) fn add_module(&self, data: ModuleData) -> ModuleValue {
let value = ModuleValue(self.modules.borrow().len());
self.modules.borrow_mut().push(ModuleHolder {
value,
data,
functions: Vec::new(),
types: Vec::new(),
debug_information: None,
constants: Vec::new(),
globals: Vec::new(),
});
value
}
pub(crate) fn set_debug_information(&self, mod_val: &ModuleValue, debug_info: DebugInformation) {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(mod_val.0);
module.debug_information = Some(debug_info);
}
}
pub(crate) unsafe fn add_type(&self, mod_val: &ModuleValue, data: TypeData) -> TypeValue {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(mod_val.0);
let value = TypeValue(module.value, module.types.len());
module.types.push(TypeHolder { value, data });
value
}
}
pub(crate) unsafe fn add_function(&self, mod_val: &ModuleValue, data: FunctionData) -> FunctionValue {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(mod_val.0);
let value = FunctionValue(module.value, module.functions.len());
module.functions.push(FunctionHolder {
value,
data,
blocks: Vec::new(),
debug_info: None,
});
value
}
}
pub(crate) unsafe fn add_block(&self, fun_val: &FunctionValue, data: BlockData) -> BlockValue {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(fun_val.0.0);
let function = module.functions.get_unchecked_mut(fun_val.1);
let value = BlockValue(function.value, function.blocks.len());
function.blocks.push(BlockHolder {
value,
data,
instructions: Vec::new(),
});
value
}
}
pub(crate) unsafe fn add_instruction(
&self,
block_val: &BlockValue,
data: InstructionData,
name: String,
) -> CompileResult<InstructionValue> {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(block_val.0.0.0);
let function = module.functions.get_unchecked_mut(block_val.0.1);
let block = function.blocks.get_unchecked_mut(block_val.1);
let value = InstructionValue(block.value, block.instructions.len());
block.instructions.push(InstructionHolder {
value,
data,
name,
record: None,
});
// Drop modules so that it is no longer mutable borrowed
// (check_instruction requires an immutable borrow).
drop(modules);
self.check_instruction(&value)?;
Ok(value)
}
}
pub(crate) unsafe fn 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();
let module = modules.get_unchecked_mut(value.0.0.0.0);
let function = module.functions.get_unchecked_mut(value.0.0.1);
let block = function.blocks.get_unchecked_mut(value.0.1);
let instr = block.instructions.get_unchecked_mut(value.1);
instr.data.location = Some(location)
}
}
pub(crate) unsafe fn add_instruction_metadata(&self, value: &InstructionValue, metadata: DebugMetadataValue) {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(value.0.0.0.0);
let function = module.functions.get_unchecked_mut(value.0.0.1);
let block = function.blocks.get_unchecked_mut(value.0.1);
let instr = block.instructions.get_unchecked_mut(value.1);
instr.data.meta = Some(metadata)
}
}
pub(crate) unsafe fn add_instruction_record(&self, value: &InstructionValue, record: InstructionDebugRecordData) {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(value.0.0.0.0);
let function = module.functions.get_unchecked_mut(value.0.0.1);
let block = function.blocks.get_unchecked_mut(value.0.1);
let instr = block.instructions.get_unchecked_mut(value.1);
instr.record = Some(record)
}
}
pub(crate) unsafe fn set_debug_subprogram(&self, value: &FunctionValue, subprogram: DebugScopeValue) {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(value.0.0);
let function = module.functions.get_unchecked_mut(value.1);
function.debug_info = Some(subprogram)
}
}
pub(crate) unsafe fn terminate(&self, block: &BlockValue, value: TerminatorKind) -> CompileResult<()> {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(block.0.0.0);
let function = module.functions.get_unchecked_mut(block.0.1);
let block = function.blocks.get_unchecked_mut(block.1);
if let Some(_) = &block.data.terminator {
Err(ErrorKind::Null)
} else {
block.data.terminator = Some(value);
Ok(())
}
}
}
pub(crate) unsafe fn set_terminator_location(
&self,
block: &BlockValue,
location: DebugLocationValue,
) -> CompileResult<()> {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(block.0.0.0);
let function = module.functions.get_unchecked_mut(block.0.1);
let block = function.blocks.get_unchecked_mut(block.1);
if let Some(_) = &block.data.terminator_location {
Err(ErrorKind::Null)
} else {
block.data.terminator_location = Some(location);
Ok(())
}
}
}
pub(crate) unsafe fn delete_block(&self, block: &BlockValue) -> CompileResult<()> {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(block.0.0.0);
let function = module.functions.get_unchecked_mut(block.0.1);
let block = function.blocks.get_unchecked_mut(block.1);
block.data.deleted = true;
Ok(())
}
}
#[allow(dead_code)]
pub(crate) unsafe fn module_data(&self, value: &ModuleValue) -> ModuleData {
unsafe { self.modules.borrow().get_unchecked(value.0).data.clone() }
}
pub(crate) unsafe fn function_data(&self, value: &FunctionValue) -> FunctionData {
unsafe {
self.modules
.borrow()
.get_unchecked(value.0.0)
.functions
.get_unchecked(value.1)
.data
.clone()
}
}
#[allow(dead_code)]
pub(crate) unsafe fn block_data(&self, value: &BlockValue) -> BlockData {
unsafe {
self.modules
.borrow()
.get_unchecked(value.0.0.0)
.functions
.get_unchecked(value.0.1)
.blocks
.get_unchecked(value.1)
.data
.clone()
}
}
pub(crate) unsafe fn instr_data(&self, value: &InstructionValue) -> InstructionData {
unsafe {
self.modules
.borrow()
.get_unchecked(value.0.0.0.0)
.functions
.get_unchecked(value.0.0.1)
.blocks
.get_unchecked(value.0.1)
.instructions
.get_unchecked(value.1)
.data
.clone()
}
}
pub(crate) unsafe fn type_data(&self, value: &TypeValue) -> TypeData {
unsafe {
self.modules
.borrow()
.get_unchecked(value.0.0)
.types
.get_unchecked(value.1)
.data
.clone()
}
}
pub(crate) fn find_module<'ctx>(&'ctx self, value: ModuleValue) -> ModuleHolder {
unsafe { self.modules.borrow().get_unchecked(value.0).clone() }
}
pub(crate) fn get_modules(&self) -> Rc<RefCell<Vec<ModuleHolder>>> {
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 {
Instr::Param(_) => Ok(()),
Instr::Constant(_) => Ok(()),
Instr::Add(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category().integer() {
Ok(())
} else {
Err(ErrorKind::Null)
}
}
Instr::FAdd(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
Ok(())
} else {
Err(ErrorKind::Null)
}
}
Instr::Sub(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category().integer() {
Ok(())
} else {
Err(ErrorKind::Null)
}
}
Instr::FSub(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
Ok(())
} else {
Err(ErrorKind::Null)
}
}
Instr::Mul(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category().integer() {
Ok(())
} else {
Err(ErrorKind::Null)
}
}
Instr::FMul(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
Ok(())
} else {
Err(ErrorKind::Null)
}
}
Instr::UDiv(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::UnsignedInteger {
Ok(())
} else {
Err(ErrorKind::Null)
}
}
Instr::SDiv(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::SignedInteger {
Ok(())
} else {
Err(ErrorKind::Null)
}
}
Instr::FDiv(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
Ok(())
} else {
Err(ErrorKind::Null)
}
}
Instr::URem(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::UnsignedInteger {
Ok(())
} else {
Err(ErrorKind::Null)
}
}
Instr::SRem(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::SignedInteger {
Ok(())
} else {
Err(ErrorKind::Null)
}
}
Instr::FRem(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
Ok(())
} else {
Err(ErrorKind::Null)
}
}
Instr::And(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::Or(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::XOr(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::ICmp(_, lhs, rhs) => {
let t = match_types(&lhs, &rhs, self)?;
if t.category().comparable() || !t.category().integer() {
Ok(())
} else {
Err(ErrorKind::Null) // TODO error: Types not comparable
}
}
Instr::FCmp(_, lhs, rhs) => {
let t = match_types(&lhs, &rhs, self)?;
if t.category().comparable() || t.category() != TypeCategory::Real {
Ok(())
} else {
Err(ErrorKind::Null) // TODO error: Types not comparable
}
}
Instr::FunctionCall(fun, params) => {
let param_types = self.function_data(&fun).params;
if param_types.len() != params.len() {
return Err(ErrorKind::Null); // TODO error: invalid amount of params
}
for (a, b) in param_types.iter().zip(params) {
if *a != b.get_type(&self)? {
return Err(ErrorKind::Null); // TODO error: params do not match
}
}
Ok(())
}
Instr::Phi(vals) => {
let mut iter = vals.iter();
// TODO error: Phi must contain at least one item
// TODO error: compile can actually crash here if any of the
// incoming values come from blocks that are added later
// than the one where this one exists.
let first = iter.next().ok_or(ErrorKind::Null)?;
for item in iter {
match_types(first, item, &self)?;
}
Ok(())
}
Instr::Alloca(_) => Ok(()),
Instr::Load(ptr, load_ty) => {
let ptr_ty = ptr.get_type(&self)?;
if let Type::Ptr(ptr_ty_inner) = ptr_ty {
if *ptr_ty_inner == load_ty {
Ok(())
} else {
Err(ErrorKind::Null) // TODO error: inner type mismatch
}
} else {
Err(ErrorKind::Null) // TODO error: not a pointer
}
}
Instr::Store(ptr, _) => {
let ty = ptr.get_type(&self)?;
if let Type::Ptr(_) = ty {
Ok(())
} else {
Err(ErrorKind::Null) // TODO error: not a pointer
}
}
Instr::ArrayAlloca(_, val) => {
if val.get_type(self)?.category() == TypeCategory::UnsignedInteger {
Ok(())
} else {
Err(ErrorKind::Null) // TODO error: not a pointer
}
}
Instr::GetElemPtr(ptr_val, _) => {
let ptr_ty = ptr_val.get_type(&self)?;
match ptr_ty {
Type::Ptr(_) => Ok(()),
_ => Err(ErrorKind::Null),
}
}
Instr::GetStructElemPtr(ptr_val, idx) => {
let ptr_ty = ptr_val.get_type(&self)?;
if let Type::Ptr(ty) = ptr_ty {
if let Type::CustomType(val) = *ty {
match self.type_data(&val).kind {
CustomTypeKind::NamedStruct(NamedStruct(_, fields)) => {
if fields.len() <= idx as usize {
return Err(ErrorKind::Null); // TODO error: no such field
}
}
}
Ok(())
} else {
Err(ErrorKind::Null) // TODO error: not a struct
}
} else {
Err(ErrorKind::Null) // TODO error: not a pointer
}
}
Instr::ExtractValue(val, _) => {
let val_ty = val.get_type(&self)?;
match val_ty {
Type::CustomType(custom) => match self.type_data(&custom).kind {
CustomTypeKind::NamedStruct(_) => Ok(()),
},
Type::Array(_, _) => Ok(()),
_ => Err(ErrorKind::Null),
}
}
Instr::Trunc(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::ZExt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::SExt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::FPTrunc(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::FPExt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::FPToUI(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::FPToSI(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::UIToFP(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::SIToFP(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
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(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(()),
}
}
}
pub fn is_block_used(&self, block_v: BlockValue) -> bool {
unsafe {
let modules = self.modules.borrow();
let module = modules.get_unchecked(block_v.0.0.0);
let function = module.functions.get_unchecked(block_v.0.1);
let block = function.blocks.get_unchecked(block_v.1);
if block.instructions.len() > 0 || block.data.terminator.is_some() {
return true;
}
for other in &function.blocks {
if let Some(term) = &other.data.terminator {
match term {
TerminatorKind::Ret(_) => {}
TerminatorKind::RetVoid => {}
TerminatorKind::Br(other_val) => {
if other_val == &block_v {
return true;
}
}
TerminatorKind::CondBr(_, then_other_v, else_other_v) => {
if then_other_v == &block_v || else_other_v == &block_v {
return true;
}
}
}
}
}
false
}
}
}
impl InstructionValue {
pub fn with_location(self, block: &Block, location: DebugLocationValue) -> InstructionValue {
unsafe {
block.builder.add_instruction_location(&self, location);
}
self
}
pub fn maybe_location(self, block: &mut Block, location: Option<DebugLocationValue>) -> InstructionValue {
unsafe {
if let Some(location) = location {
block.builder.add_instruction_location(&self, location);
}
}
self
}
pub fn add_record(&self, block: &mut Block, record: InstructionDebugRecordData) {
unsafe {
block.builder.add_instruction_record(self, record);
}
}
pub(crate) fn get_type(&self, builder: &Builder) -> CompileResult<Type> {
use Instr::*;
unsafe {
match &builder.instr_data(self).kind {
Param(nth) => builder
.function_data(&self.0.0)
.params
.get(*nth)
.cloned()
.ok_or(ErrorKind::Null),
Constant(c) => Ok(c.get_type()),
Add(lhs, rhs) => match_types(lhs, rhs, &builder),
FAdd(lhs, rhs) => match_types(lhs, rhs, &builder),
Sub(lhs, rhs) => match_types(lhs, rhs, &builder),
FSub(lhs, rhs) => match_types(lhs, rhs, &builder),
Mul(lhs, rhs) => match_types(lhs, rhs, &builder),
FMul(lhs, rhs) => match_types(lhs, rhs, &builder),
UDiv(lhs, rhs) => match_types(lhs, rhs, &builder),
SDiv(lhs, rhs) => match_types(lhs, rhs, &builder),
FDiv(lhs, rhs) => match_types(lhs, rhs, &builder),
URem(lhs, rhs) => match_types(lhs, rhs, &builder),
SRem(lhs, rhs) => match_types(lhs, rhs, &builder),
FRem(lhs, rhs) => match_types(lhs, rhs, &builder),
And(lhs, rhs) => match_types(lhs, rhs, &builder),
Or(lhs, rhs) => match_types(lhs, rhs, &builder),
XOr(lhs, rhs) => match_types(lhs, rhs, &builder),
ICmp(_, _, _) => Ok(Type::Bool),
FCmp(_, _, _) => Ok(Type::Bool),
FunctionCall(function_value, _) => Ok(builder.function_data(function_value).ret),
Phi(values) => values.first().ok_or(ErrorKind::Null).and_then(|v| v.get_type(&builder)),
Alloca(ty) => Ok(Type::Ptr(Box::new(ty.clone()))),
Load(_, ty) => Ok(ty.clone()),
Store(_, value) => value.get_type(builder),
ArrayAlloca(ty, _) => Ok(Type::Ptr(Box::new(ty.clone()))),
GetElemPtr(instr, _) => {
let instr_ty = instr.get_type(builder)?;
let Type::Ptr(inner_ty) = &instr_ty else {
panic!("GetStructElemPtr on non-pointer! ({:?})", &instr_ty)
};
match *inner_ty.clone() {
Type::Array(elem_ty, _) => Ok(Type::Ptr(Box::new(*elem_ty.clone()))),
_ => Ok(instr_ty),
}
}
GetStructElemPtr(instr, idx) => {
let instr_ty = instr.get_type(builder)?;
let Type::Ptr(inner_ty) = instr_ty else {
panic!("GetStructElemPtr on non-pointer! ({:?})", &instr_ty)
};
let Type::CustomType(ty_value) = *inner_ty else {
panic!("GetStructElemPtr on non-struct! ({:?})", &inner_ty)
};
let field_ty = match builder.type_data(&ty_value).kind {
CustomTypeKind::NamedStruct(NamedStruct(_, fields)) => {
fields.get_unchecked(*idx as usize).clone()
}
};
Ok(Type::Ptr(Box::new(field_ty)))
}
ExtractValue(instr, idx) => {
let instr_ty = instr.get_type(builder)?;
Ok(match instr_ty {
Type::CustomType(struct_ty) => {
let data = builder.type_data(&struct_ty);
match data.kind {
CustomTypeKind::NamedStruct(named_struct) => {
named_struct.1.get(*idx as usize).unwrap().clone()
}
}
}
Type::Array(elem_ty, _) => *elem_ty.clone(),
_ => return Err(ErrorKind::Null),
})
}
Trunc(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
ZExt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
SExt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
FPTrunc(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
FPExt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
FPToUI(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
FPToSI(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
UIToFP(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
SIToFP(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
PtrToInt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
IntToPtr(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
BitCast(_, ty) => Ok(ty.clone()),
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())
}
}
}
}
fn cast_to(&self, builder: &Builder, ty: &Type) -> CompileResult<Instr> {
self.get_type(builder)?
.cast_instruction(*self, &ty)
.ok_or(ErrorKind::Null)
}
}

1253
reid-llvm-lib/src/compile.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,399 @@
use std::{
cell::{Ref, RefCell, RefMut},
rc::Rc,
};
use crate::builder::InstructionValue;
/// Represents 1. the compilation context, 2. subprogram or 3. a lexical scope
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct DebugScopeValue(pub Vec<usize>);
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct DebugLocationValue(pub DebugScopeValue, pub usize);
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct DebugTypeValue(pub usize);
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct DebugMetadataValue(pub usize);
#[derive(Debug, Clone)]
pub struct DebugFileData {
pub name: String,
pub directory: String,
}
#[derive(Debug, Clone)]
pub(crate) struct DebugScopeHolder {
pub(crate) value: DebugScopeValue,
pub(crate) data: DebugScopeData,
pub(crate) inner_scopes: Vec<DebugScopeHolder>,
}
#[derive(Debug, Clone)]
pub struct DebugMetadataHolder {
pub(crate) location: DebugLocation,
pub(crate) value: DebugMetadataValue,
pub(crate) data: DebugMetadata,
}
#[derive(Clone)]
pub struct DebugTypeHolder {
pub(crate) value: DebugTypeValue,
pub(crate) data: DebugTypeData,
}
#[derive(Debug, Clone)]
pub(crate) struct DebugLocationHolder {
pub(crate) scope: DebugScopeValue,
pub(crate) value: DebugLocationValue,
pub(crate) location: DebugLocation,
}
#[derive(Debug, Clone)]
pub struct DebugInformation {
pub file: DebugFileData,
scope: Rc<RefCell<DebugScopeHolder>>,
locations: Rc<RefCell<Vec<DebugLocationHolder>>>,
metadata: Rc<RefCell<Vec<DebugMetadataHolder>>>,
types: Rc<RefCell<Vec<DebugTypeHolder>>>,
}
impl DebugInformation {
pub fn from_file(file: DebugFileData) -> (DebugInformation, DebugScopeValue) {
let scope_value = DebugScopeValue(Vec::new());
(
DebugInformation {
file,
scope: Rc::new(RefCell::new(DebugScopeHolder {
value: scope_value.clone(),
inner_scopes: Vec::new(),
data: DebugScopeData {
parent: None,
kind: DebugScopeKind::CodegenContext,
},
})),
locations: Rc::new(RefCell::new(Vec::new())),
metadata: Rc::new(RefCell::new(Vec::new())),
types: Rc::new(RefCell::new(Vec::new())),
},
scope_value,
)
}
pub fn location(&self, scope_value: &DebugScopeValue, location: DebugLocation) -> DebugLocationValue {
let value = DebugLocationValue(scope_value.clone(), self.locations.borrow().len());
let location = DebugLocationHolder {
scope: scope_value.clone(),
value: value.clone(),
location,
};
self.locations.borrow_mut().push(location);
value
}
pub fn debug_type(&self, kind: DebugTypeData) -> DebugTypeValue {
let mut types = self.types.borrow_mut();
let value = DebugTypeValue(types.len());
types.push(DebugTypeHolder {
value: value.clone(),
data: kind,
});
value
}
pub fn metadata(&self, location: &DebugLocation, kind: DebugMetadata) -> DebugMetadataValue {
let mut metadata = self.metadata.borrow_mut();
let value = DebugMetadataValue(metadata.len());
metadata.push(DebugMetadataHolder {
location: location.clone(),
value: value.clone(),
data: kind,
});
value
}
pub fn subprogram(&self, parent: DebugScopeValue, kind: DebugSubprogramData) -> 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::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() }
}
pub fn get_metadata_location(&self, value: DebugMetadataValue) -> DebugLocation {
unsafe { self.metadata.borrow().get_unchecked(value.0).location.clone() }
}
pub fn get_scope_data(&self, value: &DebugScopeValue) -> Option<DebugScopeData> {
let scope = Ref::filter_map(self.scope.borrow(), |v: &DebugScopeHolder| {
let mut opt = Some(v);
for i in &value.0 {
if let Some(inner) = opt {
opt = inner.inner_scopes.get(*i);
}
}
opt
});
if let Ok(scope) = scope {
Some(scope.data.clone())
} else {
None
}
}
pub fn get_type_data(&self, value: DebugTypeValue) -> DebugTypeData {
unsafe { self.types.borrow().get_unchecked(value.0).data.clone() }
}
pub fn get_location(&self, value: &DebugLocationValue) -> DebugLocation {
unsafe { self.locations.borrow().get_unchecked(value.1).location.clone() }
}
pub fn get_metadatas(&self) -> Rc<RefCell<Vec<DebugMetadataHolder>>> {
self.metadata.clone()
}
pub(crate) fn get_scope(&self) -> Rc<RefCell<DebugScopeHolder>> {
self.scope.clone()
}
pub fn get_types(&self) -> Rc<RefCell<Vec<DebugTypeHolder>>> {
self.types.clone()
}
pub(crate) fn get_locations(&self) -> Rc<RefCell<Vec<DebugLocationHolder>>> {
self.locations.clone()
}
}
#[derive(Clone)]
pub struct DebugLocation {
pub scope: DebugScopeValue,
pub pos: DebugPosition,
}
#[derive(Clone, Copy)]
pub struct DebugPosition {
pub line: u32,
pub column: u32,
}
#[derive(Debug, Clone)]
pub enum DebugMetadata {
ParamVar(DebugParamVariable),
LocalVar(DebugLocalVariable),
VarAssignment,
}
#[derive(Debug, Clone)]
pub struct DebugParamVariable {
pub name: String,
/// the index (starting from 1) of this variable in the subprogram
/// parameters. arg_idx should not conflict with other parameters of the
/// same subprogram.
pub arg_idx: u32,
pub ty: DebugTypeValue,
/// If this variable will be referenced from its containing subprogram, and
/// will survive some optimizations.
pub always_preserve: bool,
pub flags: DwarfFlags,
}
#[derive(Debug, Clone)]
pub struct DebugLocalVariable {
pub name: String,
pub ty: DebugTypeValue,
pub always_preserve: bool,
pub flags: DwarfFlags,
}
impl Default for DebugSubprogramOptionals {
fn default() -> Self {
Self {
scope_line: 0,
is_local: false,
is_definition: true,
is_optimized: false,
flags: DwarfFlags,
}
}
}
#[derive(Debug, Clone)]
pub struct DwarfFlags;
#[derive(Clone)]
pub enum DebugTypeData {
Basic(DebugBasicType),
Subprogram(DebugSubprogramType),
Pointer(DebugPointerType),
Array(DebugArrayType),
Struct(DebugStructType),
}
#[derive(Clone)]
pub struct DebugBasicType {
pub name: String,
/// Size of the type.
pub size_bits: u64,
/// DWARF encoding code, e.g., dwarf::DW_ATE_float.
pub encoding: DwarfEncoding,
/// Optional DWARF attributes, e.g., DW_AT_endianity.
pub flags: DwarfFlags,
}
#[derive(Clone)]
pub struct DebugArrayType {
pub size_bits: u64,
pub align_bits: u32,
pub element_type: DebugTypeValue,
pub length: u64,
}
#[derive(Clone)]
pub struct DebugPointerType {
pub name: String,
pub pointee: DebugTypeValue,
pub size_bits: u64,
}
#[derive(Clone)]
pub struct DebugStructType {
pub name: String,
pub scope: DebugScopeValue,
pub pos: Option<DebugPosition>,
pub size_bits: u64,
pub flags: DwarfFlags,
pub fields: Vec<DebugFieldType>,
}
#[derive(Clone)]
pub struct DebugFieldType {
pub name: String,
pub scope: DebugScopeValue,
pub pos: Option<DebugPosition>,
pub size_bits: u64,
pub offset: u64,
pub flags: DwarfFlags,
pub ty: DebugTypeValue,
}
#[derive(Clone)]
pub struct DebugSubprogramType {
pub parameters: Vec<DebugTypeValue>,
pub flags: DwarfFlags,
}
#[derive(Debug, Clone, Copy)]
pub enum DwarfEncoding {
Address = 1,
Boolean = 2,
Float = 4,
Signed = 5,
SignedChar = 6,
Unsigned = 7,
UnsignedChar = 8,
}
#[derive(Debug, Clone)]
pub struct DebugScopeData {
pub parent: Option<DebugScopeValue>,
pub kind: DebugScopeKind,
}
#[derive(Debug, Clone)]
pub enum DebugScopeKind {
CodegenContext,
LexicalScope(DebugLexicalScope),
Subprogram(DebugSubprogramData),
}
#[derive(Debug, Clone)]
pub struct DebugLexicalScope {
pub location: DebugLocation,
}
#[derive(Debug, Clone)]
pub struct DebugSubprogramData {
/// Function name.
pub name: String,
pub outer_scope: DebugScopeValue,
/// Used for line number.
pub location: DebugLocation,
/// Function type.
pub ty: DebugTypeValue,
pub opts: DebugSubprogramOptionals,
}
#[derive(Debug, Clone)]
pub struct DebugSubprogramOptionals {
/// Set to the beginning of the scope this starts
pub scope_line: u32,
pub is_local: bool,
pub is_definition: bool,
pub is_optimized: bool,
/// These flags are used to emit dwarf attributes. e.g. is this function
/// prototyped or not.
pub flags: DwarfFlags,
}
#[derive(Clone)]
pub struct InstructionDebugRecordData {
pub scope: DebugScopeValue,
pub variable: DebugMetadataValue,
pub location: DebugLocation,
pub kind: DebugRecordKind,
}
#[derive(Clone, Copy)]
pub enum DebugRecordKind {
Declare(InstructionValue),
Value(InstructionValue),
}

594
reid-llvm-lib/src/fmt.rs Normal file
View File

@ -0,0 +1,594 @@
//! Debug implementations for relevant types
use std::{
fmt::{Debug, Display, Write},
marker::PhantomData,
};
use crate::{
CmpPredicate, Context, Instr, InstructionData, TerminatorKind,
builder::*,
debug_information::{
DebugArrayType, DebugBasicType, DebugFieldType, DebugInformation, DebugLocalVariable, DebugLocation,
DebugLocationValue, DebugMetadata, DebugMetadataValue, DebugParamVariable, DebugPointerType, DebugPosition,
DebugRecordKind, DebugScopeValue, DebugStructType, DebugSubprogramType, DebugTypeData, DebugTypeHolder,
DebugTypeValue,
},
pad_adapter::PadAdapter,
};
impl Display for Context {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self.builder, f)
}
}
impl Display for Builder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Producer: {}", self.producer)?;
for module in self.modules.borrow().iter() {
if module.data.is_main {
write!(f, "main ")?;
}
writeln!(f, "{} ({:?}) {{", module.data.name, module.value)?;
for function in &module.functions {
let mut state = Default::default();
let mut inner = PadAdapter::wrap(f, &mut state);
function.builder_fmt(&mut inner, self, &module.debug_information)?;
}
writeln!(f, "}}")?;
}
Ok(())
}
}
impl FunctionHolder {
fn builder_fmt(
&self,
f: &mut impl std::fmt::Write,
builder: &Builder,
debug: &Option<DebugInformation>,
) -> std::fmt::Result {
if self.data.flags.is_imported {
write!(f, "imported ")?;
}
if self.data.flags.is_extern {
write!(f, "extern ")?;
}
if self.data.flags.is_pub {
write!(f, "pub ")?;
}
if self.data.flags.is_main {
write!(f, "main ")?;
}
let params = self
.data
.params
.iter()
.map(|p| format!("{:?}", p))
.collect::<Vec<_>>()
.join(", ");
write!(f, "fn {}({}) -> {:?} ", self.data.name, params, self.data.ret)?;
writeln!(f, "{{")?;
let mut state = Default::default();
let mut inner = PadAdapter::wrap(f, &mut state);
writeln!(inner, "(Value = {:?}) ", self.value)?;
if let Some(debug) = &self.debug_info {
writeln!(inner, "(Debug = {:?})", debug)?;
}
for block in &self.blocks {
let mut state = Default::default();
let mut inner = PadAdapter::wrap(&mut inner, &mut state);
block.builder_fmt(&mut inner, builder, debug)?;
}
writeln!(f, "}}")?;
Ok(())
}
}
impl BlockHolder {
fn builder_fmt(
&self,
f: &mut impl std::fmt::Write,
builder: &Builder,
debug: &Option<DebugInformation>,
) -> std::fmt::Result {
if self.data.deleted {
write!(f, "deleted ")?;
}
writeln!(f, "{} ({:?}):", self.data.name, self.value)?;
let mut state = Default::default();
let mut inner = PadAdapter::wrap(f, &mut state);
for instr in &self.instructions {
instr.builder_fmt(&mut inner, builder, debug)?;
}
if let Some(terminator) = &self.data.terminator {
terminator.builder_fmt(&mut inner, builder, debug)?;
}
if let Some(location) = &self.data.terminator_location {
writeln!(inner, " ^ (At {}) ", debug.as_ref().unwrap().get_location(location))?;
}
Ok(())
}
}
impl InstructionHolder {
fn builder_fmt(
&self,
f: &mut impl std::fmt::Write,
_builder: &Builder,
debug: &Option<DebugInformation>,
) -> std::fmt::Result {
if let Some(record) = &self.record {
let kind = match record.kind {
DebugRecordKind::Declare(instruction_value) => {
format!("= {:?} (Assign)", instruction_value)
}
DebugRecordKind::Value(instruction_value) => {
format!("= {:?} (Value)", instruction_value)
}
};
if let Some(debug) = debug {
writeln!(f, " (Debug {} {})", record.variable.hr(debug), kind)?;
}
}
writeln!(f, "{:?} ({}) = {:?} ", self.value, self.name, self.data.kind)?;
if let Some(debug) = debug {
if let Some(location) = &self.data.location {
writeln!(f, " ^ (At {}) ", debug.get_location(location))?;
}
if let Some(meta) = self.data.meta {
writeln!(f, " ^ (Meta {}) ", meta.hr(debug))?;
}
}
writeln!(f)?;
Ok(())
}
}
impl TerminatorKind {
fn builder_fmt(
&self,
f: &mut impl std::fmt::Write,
_builder: &Builder,
_debug: &Option<DebugInformation>,
) -> std::fmt::Result {
match self {
TerminatorKind::Ret(instr) => writeln!(f, "ret {:?}", instr),
TerminatorKind::RetVoid => writeln!(f, "ret void"),
TerminatorKind::Br(block) => writeln!(f, "br {:?}", block),
TerminatorKind::CondBr(instr, lhs, rhs) => {
writeln!(f, "condbr {:?}, {:?} or {:?}", instr, lhs, rhs)
}
}
}
}
impl DebugMetadataValue {
fn hr(&self, debug: &DebugInformation) -> String {
let kind = match debug.get_metadata(*self) {
DebugMetadata::ParamVar(DebugParamVariable { name, arg_idx, ty, .. }) => {
format!("param {} (idx {}) (type {:?}) ", name, arg_idx, ty)
}
DebugMetadata::LocalVar(DebugLocalVariable { name, ty, .. }) => {
format!("var {} (type {:?}) ", name, ty)
}
DebugMetadata::VarAssignment => todo!(),
};
format!("{} at {}", kind, debug.get_metadata_location(*self))
}
}
impl Display for DebugLocation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?} on scope {:?}", self.pos, self.scope)
}
}
impl Display for DebugPosition {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "line {}, col {}", self.line, self.column)
}
}
impl Debug for Builder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(self.get_modules().borrow().iter());
Ok(())
}
}
pub struct PrintableModule<'ctx> {
pub phantom: PhantomData<&'ctx ()>,
pub module: ModuleHolder,
}
impl<'ctx> Debug for PrintableModule<'ctx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Debug::fmt(&self.module, f)
}
}
impl Debug for ModuleHolder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple(&format!("{}({:#?}) ", self.data.name, self.value))
.field(&self.functions)
// .field(&self.debug_information)
.finish()
}
}
impl Debug for FunctionHolder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple(&format!(
"{}({:?}) -> {:?} ",
self.data.name, self.data.params, self.data.ret
))
.field(&self.blocks)
.finish()
}
}
impl Debug for BlockHolder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let deleted = if self.data.deleted { " (deleted)" } else { "" };
f.debug_tuple(&format!("{}[{:?}]{} ", &self.data.name, &self.value, deleted))
.field(&self.instructions)
.field(&self.data.terminator)
.field(&self.data.terminator_location)
.finish()
}
}
impl Debug for InstructionHolder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.value.fmt(f)?;
write!(f, " ({})", self.name)?;
f.write_str(" = ")?;
self.data.fmt(f)
}
}
impl Debug for InstructionData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.kind.fmt(f)?;
if let Some(location) = &self.location {
write!(f, " ({:?})", location)?;
}
Ok(())
}
}
impl Debug for ModuleValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "M[{:0>2}]", self.0)
}
}
impl Debug for FunctionValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "F[{:0>2}-{:0>2}]", &self.0.0, self.1)
}
}
impl Debug for BlockValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "B[{:0>2}-{:0>2}-{:0>2}]", &self.0.0.0, &self.0.1, self.1)
}
}
impl Debug for InstructionValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "%{}.{}.{}.{}", self.0.0.0.0, self.0.0.1, self.0.1, self.1)
}
}
// impl Debug for InstructionValue {
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// write!(
// f,
// "I[{:0>2}-{:0>2}-{:0>2}-{:0>2}]",
// &self.0.0.0.0, &self.0.0.1, &self.0.1, self.1
// )
// }
// }
impl Debug for TypeValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Ty[{:0>2}-{:0>2}]", &self.0.0, self.1)
}
}
impl Debug for Instr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Instr::Param(nth) => fmt_call(f, &"Param", &nth),
Instr::Constant(c) => c.fmt(f),
Instr::Add(lhs, rhs) => fmt_binop(f, lhs, &"+", rhs),
Instr::FAdd(lhs, rhs) => fmt_binop(f, lhs, &"+", rhs),
Instr::Sub(lhs, rhs) => fmt_binop(f, lhs, &"-", rhs),
Instr::FSub(lhs, rhs) => fmt_binop(f, lhs, &"-", rhs),
Instr::Mul(lhs, rhs) => fmt_binop(f, lhs, &"*", rhs),
Instr::FMul(lhs, rhs) => fmt_binop(f, lhs, &"*", rhs),
Instr::UDiv(lhs, rhs) => fmt_binop(f, lhs, &"/", rhs),
Instr::SDiv(lhs, rhs) => fmt_binop(f, lhs, &"/", rhs),
Instr::FDiv(lhs, rhs) => fmt_binop(f, lhs, &"/", rhs),
Instr::URem(lhs, rhs) => fmt_binop(f, lhs, &"%", rhs),
Instr::SRem(lhs, rhs) => fmt_binop(f, lhs, &"%", rhs),
Instr::FRem(lhs, rhs) => fmt_binop(f, lhs, &"%", rhs),
Instr::And(lhs, rhs) => fmt_binop(f, lhs, &"&&", rhs),
Instr::Phi(val) => fmt_call(f, &"Phi", &val),
Instr::ICmp(cmp, lhs, rhs) => fmt_binop(f, lhs, cmp, rhs),
Instr::FCmp(cmp, lhs, rhs) => fmt_binop(f, lhs, cmp, rhs),
Instr::FunctionCall(fun, params) => fmt_call(f, fun, params),
Instr::Alloca(ty) => write!(f, "alloca<{:?}>", ty),
Instr::Load(val, ty) => write!(f, "load<{:?}>({:?})", ty, val),
Instr::Store(ptr, val) => write!(f, "store({:?} = {:?})", ptr, val),
Instr::ArrayAlloca(ty, instruction_value) => {
write!(f, "array_alloca<{:?}>({:?})", ty, instruction_value)
}
Instr::GetElemPtr(instruction_value, items) => fmt_index(
f,
instruction_value,
&items
.iter()
.map(|expr| format!("{:?}", expr))
.collect::<Vec<_>>()
.join(", "),
),
Instr::GetStructElemPtr(instruction_value, index) => {
write!(f, "GEP(")?;
fmt_index(f, instruction_value, &index.to_string())?;
write!(f, ")")
}
Instr::ExtractValue(instruction_value, index) => fmt_index(f, instruction_value, &index.to_string()),
Instr::Trunc(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::ZExt(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::SExt(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::FPTrunc(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::FPExt(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::FPToUI(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::FPToSI(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::UIToFP(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::SIToFP(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::PtrToInt(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::IntToPtr(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::BitCast(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::Or(lhs, rhs) => fmt_binop(f, lhs, &"||", rhs),
Instr::XOr(lhs, rhs) => fmt_binop(f, lhs, &"^", rhs),
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),
}
}
}
fn fmt_binop(
f: &mut std::fmt::Formatter<'_>,
lhs: &impl std::fmt::Debug,
op: &impl std::fmt::Debug,
rhs: &impl std::fmt::Debug,
) -> std::fmt::Result {
lhs.fmt(f)?;
f.write_char(' ')?;
op.fmt(f)?;
f.write_char(' ')?;
rhs.fmt(f)
}
fn fmt_call(
f: &mut std::fmt::Formatter<'_>,
fun: &impl std::fmt::Debug,
params: &impl std::fmt::Debug,
) -> std::fmt::Result {
fun.fmt(f)?;
f.write_char('(')?;
params.fmt(f)?;
f.write_char(')')
}
fn fmt_index(
f: &mut std::fmt::Formatter<'_>,
fun: &impl std::fmt::Debug,
params: &impl std::fmt::Debug,
) -> std::fmt::Result {
fun.fmt(f)?;
f.write_char('[')?;
params.fmt(f)?;
f.write_char(']')
}
impl Debug for CmpPredicate {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::LT => write!(f, "<"),
Self::GT => write!(f, ">"),
Self::LE => write!(f, "<="),
Self::GE => write!(f, ">="),
Self::EQ => write!(f, "=="),
Self::NE => write!(f, "!="),
}
}
}
impl Debug for TerminatorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Ret(val) => {
write!(f, "Ret ")?;
val.fmt(f)
}
Self::RetVoid => write!(f, "Void Ret"),
Self::Br(val) => {
write!(f, "Br ")?;
val.fmt(f)
}
Self::CondBr(cond, b1, b2) => {
write!(f, "CondBr ")?;
cond.fmt(f)?;
write!(f, " ? ")?;
b1.fmt(f)?;
write!(f, " : ")?;
b2.fmt(f)?;
Ok(())
}
}
}
}
impl Debug for DebugTypeHolder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple(&format!("DebugTypeHolder {:?}", self.value))
.field(&self.data)
.finish()
}
}
impl Debug for DebugTypeData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DebugTypeData::Basic(ty) => Debug::fmt(ty, f),
DebugTypeData::Subprogram(ty) => Debug::fmt(ty, f),
DebugTypeData::Pointer(ty) => Debug::fmt(ty, f),
DebugTypeData::Array(ty) => Debug::fmt(ty, f),
DebugTypeData::Struct(ty) => Debug::fmt(ty, f),
}
}
}
impl Debug for DebugBasicType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("BasicType")
.field(&self.name)
.field(&self.size_bits)
.field(&self.encoding)
.field(&self.flags)
.finish()
}
}
impl Debug for DebugStructType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Struct")
.field("name", &self.name)
.field("scope", &self.scope)
.field("pos", &self.pos)
.field("size_bit", &self.size_bits)
.field("flags", &self.flags)
.field("elements", &self.fields)
.finish()
}
}
impl Debug for DebugFieldType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(&format!("Field({})", self.name))
.field("scope", &self.scope)
.field("pos", &self.pos)
.field("size_bits", &self.size_bits)
.field("offset", &self.offset)
.field("flags", &self.flags)
.field("ty", &self.ty)
.finish()
}
}
impl Debug for DebugSubprogramType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Subprogram")
.field(&self.parameters)
.field(&self.flags)
.finish()
}
}
impl Debug for DebugPointerType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple(&format!("Pointer<{:?}>({})", self.pointee, self.name))
.field(&self.size_bits)
.finish()
}
}
impl Debug for DebugArrayType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(&format!("Array<{:?}>[{}]", self.element_type, self.length))
.field("size_bits", &self.size_bits)
.field("align_bits", &self.align_bits)
.finish()
}
}
impl Debug for DebugMetadataValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Meta[{}]", self.0)
}
}
impl Debug for DebugScopeValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Scope[{}]",
self.0.iter().map(|v| v.to_string()).collect::<Vec<_>>().join(", ")
)
}
}
impl Debug for DebugTypeValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Type[{}]", self.0)
}
}
impl Debug for DebugLocationValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Value[{:?}][{}]", self.0, self.1)
}
}
impl Debug for DebugLocation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?} on scope {:?}", self.pos, self.scope)
}
}
impl Debug for DebugPosition {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ln {}, col {}", self.line, self.column)
}
}

688
reid-llvm-lib/src/lib.rs Normal file
View File

@ -0,0 +1,688 @@
//! Reid LLVM Lib is an ergonomic Rust'y API which is used to produce a
//! Low-Level IR (LLIR) using [`Context`] and [`Builder`]. This Builder can then
//! be used at the end to compile said LLIR into LLVM IR.
use std::{fmt::Debug, marker::PhantomData};
use builder::{BlockValue, Builder, FunctionValue, InstructionValue, ModuleValue, TypeValue};
use debug_information::{DebugFileData, DebugInformation, DebugLocationValue, DebugMetadataValue};
use fmt::PrintableModule;
use crate::{
builder::{ConstantValue, GlobalValue},
debug_information::DebugScopeValue,
};
pub mod builder;
pub mod compile;
pub mod debug_information;
mod fmt;
mod pad_adapter;
mod util;
#[derive(thiserror::Error, Debug, Clone, PartialEq, PartialOrd)]
pub enum ErrorKind {
#[error("NULL error, should never occur!")]
Null,
}
pub type CompileResult<T> = Result<T, ErrorKind>;
#[derive(Debug)]
pub struct Context {
builder: Builder,
}
impl Context {
pub fn new<T: Into<String>>(producer: T) -> Context {
Context {
builder: Builder::new(producer.into()),
}
}
pub fn module<'ctx>(&'ctx self, name: &str, main: bool) -> Module<'ctx> {
let value = self.builder.add_module(ModuleData {
name: name.to_owned(),
is_main: main,
});
Module {
phantom: PhantomData,
builder: self.builder.clone(),
value,
debug_info: None,
}
}
}
#[derive(Debug, Clone, Hash)]
pub struct ModuleData {
name: String,
is_main: bool,
}
#[derive(Clone)]
pub struct Module<'ctx> {
phantom: PhantomData<&'ctx ()>,
builder: Builder,
value: ModuleValue,
debug_info: Option<DebugInformation>,
}
impl<'ctx> Module<'ctx> {
pub fn function(
&self,
name: &str,
linkage: Option<String>,
ret: Type,
params: Vec<Type>,
flags: FunctionFlags,
) -> Function<'ctx> {
unsafe {
Function {
phantom: PhantomData,
builder: self.builder.clone(),
value: self.builder.add_function(
&self.value,
FunctionData {
name: name.to_owned(),
linkage_name: linkage,
ret,
params,
flags,
},
),
}
}
}
pub fn custom_type(&self, ty: CustomTypeKind) -> TypeValue {
unsafe {
let (name, kind) = match &ty {
CustomTypeKind::NamedStruct(NamedStruct(name, _)) => (name.clone(), ty),
};
self.builder.add_type(&self.value, TypeData { name, kind })
}
}
pub fn value(&self) -> ModuleValue {
self.value
}
pub fn as_printable(&self) -> PrintableModule<'ctx> {
PrintableModule {
phantom: PhantomData,
module: self.builder.find_module(self.value),
}
}
pub fn create_debug_info(&mut self, file: DebugFileData) -> (DebugInformation, DebugScopeValue) {
let (debug_info, scope_value) = DebugInformation::from_file(file);
self.debug_info = Some(debug_info.clone());
(debug_info, scope_value)
}
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> {
fn drop(&mut self) {
if let Some(debug_info) = self.debug_info.take() {
self.builder.set_debug_information(&self.value, debug_info);
}
}
}
#[derive(Debug, Clone, Hash)]
pub struct FunctionData {
name: String,
linkage_name: Option<String>,
ret: Type,
params: Vec<Type>,
flags: FunctionFlags,
}
#[derive(Debug, Clone, Copy, Hash)]
pub struct FunctionFlags {
/// True in the destination module of the import, false in the source module.
pub is_extern: bool,
/// Whether this function is the main function of the module, that should be
/// executed (and linked externally also).
pub is_main: bool,
/// Whether this function should be available externally always.
pub is_pub: bool,
/// If this function is an imported function (either in the source or
/// destination module)
pub is_imported: bool,
/// Whether this function should add "alwaysinline"-attribute.
pub inline: bool,
}
impl Default for FunctionFlags {
fn default() -> FunctionFlags {
FunctionFlags {
is_extern: false,
is_main: false,
is_pub: false,
is_imported: false,
inline: false,
}
}
}
pub struct Function<'ctx> {
phantom: PhantomData<&'ctx ()>,
builder: Builder,
value: FunctionValue,
}
impl<'ctx> Function<'ctx> {
pub fn block(&self, name: &str) -> Block<'ctx> {
unsafe {
Block {
phantom: PhantomData,
builder: self.builder.clone(),
value: self.builder.add_block(
&self.value,
BlockData {
name: name.to_owned(),
terminator: None,
terminator_location: None,
deleted: false,
},
),
}
}
}
pub fn set_debug(&self, subprogram: DebugScopeValue) {
unsafe {
self.builder.set_debug_subprogram(&self.value, subprogram);
}
}
pub fn value(&self) -> FunctionValue {
self.value
}
}
#[derive(Debug, Clone, Hash)]
pub struct BlockData {
name: String,
terminator: Option<TerminatorKind>,
terminator_location: Option<DebugLocationValue>,
deleted: bool,
}
#[derive(Clone)]
pub struct Block<'builder> {
phantom: PhantomData<&'builder ()>,
builder: Builder,
value: BlockValue,
}
impl Instr {
pub fn default_name(&self) -> &str {
match self {
Instr::Param(_) => "param",
Instr::Constant(_) => "const1",
Instr::Add(..) => "add",
Instr::FAdd(..) => "fadd",
Instr::Sub(..) => "sub",
Instr::FSub(..) => "fsub",
Instr::Mul(..) => "mul",
Instr::FMul(..) => "fmul",
Instr::UDiv(..) => "udiv",
Instr::SDiv(..) => "sdiv",
Instr::FDiv(..) => "fdiv",
Instr::URem(..) => "urem",
Instr::SRem(..) => "srem",
Instr::FRem(..) => "frem",
Instr::And(..) => "and",
Instr::Phi(_) => "phi",
Instr::Alloca(_) => "alloca",
Instr::Load(_, _) => "load",
Instr::Store(..) => "store",
Instr::ArrayAlloca(_, _) => "arrayalloca",
Instr::GetElemPtr(..) => "getelemptr",
Instr::GetStructElemPtr(..) => "getstructelemptr",
Instr::ExtractValue(..) => "extractvalue",
Instr::ICmp(..) => "icmp",
Instr::FunctionCall(..) => "call",
Instr::FCmp(_, _, _) => "fcmp",
Instr::Trunc(_, _) => "trunc",
Instr::ZExt(_, _) => "zext",
Instr::SExt(_, _) => "sext",
Instr::FPTrunc(_, _) => "fptrunc",
Instr::FPExt(_, _) => "pfext",
Instr::FPToUI(_, _) => "fptoui",
Instr::FPToSI(_, _) => "fptosi",
Instr::UIToFP(_, _) => "uitofp",
Instr::SIToFP(_, _) => "sitofp",
Instr::PtrToInt(_, _) => "ptrtoint",
Instr::IntToPtr(_, _) => "inttoptr",
Instr::BitCast(_, _) => "bitcast",
Instr::Or(..) => "or",
Instr::XOr(..) => "xor",
Instr::ShiftRightLogical(..) => "lshr",
Instr::ShiftRightArithmetic(..) => "ashr",
Instr::ShiftLeft(..) => "shl",
Instr::GetGlobal(..) => "global",
}
}
}
impl<'builder> Block<'builder> {
pub fn build_named<T: Into<String>>(&mut self, name: T, instruction: Instr) -> CompileResult<InstructionValue> {
unsafe {
self.builder.add_instruction(
&self.value,
InstructionData {
kind: instruction,
location: None,
meta: None,
},
name.into(),
)
}
}
pub fn build(&mut self, instruction: Instr) -> CompileResult<InstructionValue> {
unsafe {
let name = instruction.default_name().to_owned();
self.builder.add_instruction(
&self.value,
InstructionData {
kind: instruction,
location: None,
meta: None,
},
name,
)
}
}
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);
}
}
pub fn set_instr_metadata(&self, instruction: InstructionValue, location: DebugMetadataValue) {
unsafe {
self.builder.add_instruction_metadata(&instruction, location);
}
}
pub fn terminate(&mut self, instruction: TerminatorKind) -> CompileResult<()> {
unsafe { self.builder.terminate(&self.value, instruction) }
}
pub fn set_terminator_location(&mut self, location: DebugLocationValue) -> CompileResult<()> {
unsafe { self.builder.set_terminator_location(&self.value, location) }
}
/// Delete block if it is unused. Return true if deleted, false if not.
pub fn delete_if_unused(&mut self) -> CompileResult<bool> {
unsafe {
if !self.builder.is_block_used(self.value()) {
self.builder.delete_block(&self.value)?;
Ok(true)
} else {
Ok(false)
}
}
}
pub fn value(&self) -> BlockValue {
self.value
}
}
#[derive(Clone)]
pub struct InstructionData {
kind: Instr,
location: Option<DebugLocationValue>,
meta: Option<DebugMetadataValue>,
}
#[derive(Clone, Copy, Hash)]
pub enum CmpPredicate {
LT,
LE,
GT,
GE,
EQ,
NE,
}
/// https://llvm.org/docs/LangRef.html#instruction-reference
#[derive(Clone)]
pub enum Instr {
Param(usize),
Constant(ConstValueKind),
GetGlobal(GlobalValue),
/// Add two integers
Add(InstructionValue, InstructionValue),
/// Add two floats
FAdd(InstructionValue, InstructionValue),
/// Subtract two integers
Sub(InstructionValue, InstructionValue),
/// Subtract two floats
FSub(InstructionValue, InstructionValue),
/// Multiply two integers
Mul(InstructionValue, InstructionValue),
/// Multiply two floats
FMul(InstructionValue, InstructionValue),
/// Divide two unsigned integers
UDiv(InstructionValue, InstructionValue),
/// Divide two signed integers
SDiv(InstructionValue, InstructionValue),
/// Divide two floats
FDiv(InstructionValue, InstructionValue),
/// Get the remainder from two unsigned integers
URem(InstructionValue, InstructionValue),
/// Get the remainder from two signed integers
SRem(InstructionValue, InstructionValue),
/// Get the remainder from two floats
FRem(InstructionValue, InstructionValue),
And(InstructionValue, InstructionValue),
Or(InstructionValue, InstructionValue),
XOr(InstructionValue, InstructionValue),
ShiftRightLogical(InstructionValue, InstructionValue),
ShiftRightArithmetic(InstructionValue, InstructionValue),
ShiftLeft(InstructionValue, InstructionValue),
Phi(Vec<InstructionValue>),
Alloca(Type),
Load(InstructionValue, Type),
Store(InstructionValue, InstructionValue),
ArrayAlloca(Type, InstructionValue),
GetElemPtr(InstructionValue, Vec<InstructionValue>),
GetStructElemPtr(InstructionValue, u32),
ExtractValue(InstructionValue, u32),
/// Integer Comparison
ICmp(CmpPredicate, InstructionValue, InstructionValue),
/// FLoat Comparison
FCmp(CmpPredicate, InstructionValue, InstructionValue),
/// The `trunc` instruction truncates the high order bits in value and
/// converts the remaining bits to ty2. Since the source size must be larger
/// than the destination size, `trunc` cannot be a no-op cast. It will
/// always truncate bits.
Trunc(InstructionValue, Type),
/// The `zext` fills the high order bits of the value with zero bits until
/// it reaches the size of the destination type, ty2.
ZExt(InstructionValue, Type),
/// The `sext` instruction performs a sign extension by copying the sign bit
/// (highest order bit) of the value until it reaches the bit size of the
/// type ty2.
SExt(InstructionValue, Type),
/// The `fptrunc` instruction casts a value from a larger floating-point
/// type to a smaller floating-point type.
FPTrunc(InstructionValue, Type),
/// The `fpext` instruction extends the value from a smaller floating-point
/// type to a larger floating-point type.
FPExt(InstructionValue, Type),
/// The `fptoui` instruction takes a value to cast, which must be a scalar
/// or vector floating-point value, and a type to cast it to ty2, which must
/// be an integer type.
FPToUI(InstructionValue, Type),
/// The `fptosi` instruction takes a value to cast, which must be a scalar
/// or vector floating-point value, and a type to cast it to ty2, which must
/// be an integer type.
FPToSI(InstructionValue, Type),
/// The `uitofp` instruction takes a value to cast, which must be a scalar
/// or vector integer value, and a type to cast it to ty2, which must be an
/// floating-point type.
UIToFP(InstructionValue, Type),
/// The `sitofp` instruction takes a value to cast, which must be a scalar
/// or vector integer value, and a type to cast it to ty2, which must be an
/// floating-point type
SIToFP(InstructionValue, Type),
/// The `ptrtoint` instruction converts value to integer type ty2 by
/// interpreting the all pointer representation bits as an integer
/// (equivalent to a bitcast) and either truncating or zero extending that
/// value to the size of the integer type.
PtrToInt(InstructionValue, Type),
/// The `inttoptr` instruction converts value to type ty2 by applying either
/// a zero extension or a truncation depending on the size of the integer
/// value.
IntToPtr(InstructionValue, Type),
/// The `bitcast` instruction converts value to type ty2. It is always a
/// no-op cast because no bits change with this conversion.
BitCast(InstructionValue, Type),
FunctionCall(FunctionValue, Vec<InstructionValue>),
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum Type {
I8,
I16,
I32,
I64,
I128,
U8,
U16,
U32,
U64,
U128,
F16,
F32B,
F32,
F64,
F80,
F128,
F128PPC,
Bool,
Void,
CustomType(TypeValue),
Array(Box<Type>, u64),
Ptr(Box<Type>),
}
#[derive(Debug, Clone)]
pub enum ConstValueKind {
I8(i8),
I16(i16),
I32(i32),
I64(i64),
I128(i128),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
U128(u128),
Bool(bool),
Str(String),
F16(f32),
F32B(f32),
F32(f32),
F64(f64),
F80(f64),
F128(f64),
F128PPC(f64),
Array(Vec<ConstantValue>, Type),
}
#[derive(Clone, Hash)]
pub enum TerminatorKind {
Ret(InstructionValue),
RetVoid,
Br(BlockValue),
CondBr(InstructionValue, BlockValue, BlockValue),
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct TypeData {
name: String,
kind: CustomTypeKind,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum CustomTypeKind {
NamedStruct(NamedStruct),
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct NamedStruct(pub String, pub Vec<Type>);
impl ConstValueKind {
pub fn get_type(&self) -> Type {
use Type::*;
match self {
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),
}
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum TypeCategory {
SignedInteger,
UnsignedInteger,
Void,
Real,
Ptr,
CustomType,
Array,
}
impl TypeCategory {
pub fn comparable(&self) -> bool {
match self {
TypeCategory::SignedInteger => true,
TypeCategory::UnsignedInteger => true,
TypeCategory::Real => true,
_ => false,
}
}
pub fn signed(&self) -> bool {
match self {
TypeCategory::SignedInteger => true,
_ => false,
}
}
pub fn integer(&self) -> bool {
match self {
TypeCategory::SignedInteger => true,
TypeCategory::UnsignedInteger => true,
_ => false,
}
}
}
impl Type {
pub fn category(&self) -> TypeCategory {
match self {
Type::I8 | Type::I16 | Type::I32 | Type::I64 | Type::I128 => TypeCategory::SignedInteger,
Type::U8 | Type::U16 | Type::U32 | Type::U64 | Type::U128 => TypeCategory::UnsignedInteger,
Type::F16 | Type::F32B | Type::F32 | Type::F64 | Type::F80 | Type::F128 | Type::F128PPC => {
TypeCategory::Real
}
Type::Bool => TypeCategory::UnsignedInteger,
Type::Void => TypeCategory::Void,
Type::CustomType(_) => TypeCategory::CustomType,
Type::Array(_, _) => TypeCategory::Array,
Type::Ptr(_) => TypeCategory::Ptr,
}
}
pub fn cast_instruction(&self, value: InstructionValue, other: &Type) -> Option<Instr> {
use Type::*;
match (self, other) {
(I8, I16 | I32 | I64 | I128) => Some(Instr::SExt(value, other.clone())),
(I16, I32 | I64 | I128) => Some(Instr::SExt(value, other.clone())),
(I32, I64 | I128) => Some(Instr::SExt(value, other.clone())),
(I64, I128) => Some(Instr::SExt(value, other.clone())),
(I128 | U128, I64 | U64 | I32 | U32 | I16 | U16 | I8 | U8) => Some(Instr::Trunc(value, other.clone())),
(I64 | U64, I32 | U32 | I16 | U16 | I8 | U8) => Some(Instr::Trunc(value, other.clone())),
(I32 | U32, I16 | U16 | I8 | U8) => Some(Instr::Trunc(value, other.clone())),
(I16 | U16, I8 | U8) => Some(Instr::Trunc(value, other.clone())),
(U8 | I8, U8 | I8 | U16 | I16 | U32 | I32 | U64 | I64 | U128 | I128) => {
Some(Instr::ZExt(value, other.clone()))
}
(U16 | I16, U16 | I16 | U32 | I32 | U64 | I64 | U128 | I128) => Some(Instr::ZExt(value, other.clone())),
(U32 | I32, U32 | I32 | U64 | I64 | U128 | I128) => Some(Instr::ZExt(value, other.clone())),
(U64 | I64, U64 | I64 | U128 | I128) => Some(Instr::ZExt(value, other.clone())),
(U128 | I128, U128 | I128) => Some(Instr::ZExt(value, other.clone())),
(U8 | U16 | U32 | U64 | U128, F16 | F32 | F32B | F64 | F80 | F128 | F128PPC) => {
Some(Instr::UIToFP(value, other.clone()))
}
(I8 | I16 | I32 | I64 | I128, F16 | F32 | F32B | F64 | F80 | F128 | F128PPC) => {
Some(Instr::SIToFP(value, other.clone()))
}
(F16 | F32 | F32B | F64 | F80 | F128 | F128PPC, U8 | U16 | U32 | U64 | U128) => {
Some(Instr::FPToUI(value, other.clone()))
}
(F16 | F32 | F32B | F64 | F80 | F128 | F128PPC, I8 | I16 | I32 | I64 | I128) => {
Some(Instr::FPToSI(value, other.clone()))
}
(I128 | U128 | I64 | U64 | I32 | U32 | I16 | U16 | I8 | U8, Ptr(_)) => {
Some(Instr::IntToPtr(value, other.clone()))
}
(Ptr(_), I128 | U128 | I64 | U64 | I32 | U32 | I16 | U16 | I8 | U8) => {
Some(Instr::PtrToInt(value, other.clone()))
}
(F16, F32 | F32B | F64 | F80 | F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())),
(F32 | F32B, F64 | F80 | F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())),
(F64, F80 | F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())),
(F80, F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())),
(F128PPC | F128, F80 | F64 | F32B | F32 | F16) => Some(Instr::FPTrunc(value, other.clone())),
(F80, F64 | F32B | F32 | F16) => Some(Instr::FPTrunc(value, other.clone())),
(F64, F32B | F32 | F16) => Some(Instr::FPTrunc(value, other.clone())),
(F32B | F32, F16) => Some(Instr::FPTrunc(value, other.clone())),
_ => None,
}
}
}
impl TerminatorKind {
pub(crate) fn get_type(&self, builder: &Builder) -> CompileResult<Type> {
use TerminatorKind::*;
match self {
Ret(instr_val) => instr_val.get_type(builder),
RetVoid => Ok(Type::Void),
Br(_) => Ok(Type::Void),
CondBr(_, _, _) => Ok(Type::Void),
}
}
}

View File

@ -0,0 +1,69 @@
//! Copied from
//! https://github.com/rust-lang/rust/blob/6b3ae3f6e45a33c2d95fa0362c9b2593e567fd34/library/core/src/fmt/builders.rs#L102
// Copyright (c) The Rust Project Contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
use std::fmt;
pub struct PadAdapter<'buf, 'state> {
buf: &'buf mut (dyn fmt::Write + 'buf),
state: &'state mut PadAdapterState,
}
pub struct PadAdapterState {
on_newline: bool,
}
impl Default for PadAdapterState {
fn default() -> Self {
PadAdapterState { on_newline: true }
}
}
impl<'buf, 'state> PadAdapter<'buf, 'state> {
pub fn wrap<'slot, 'fmt: 'buf + 'slot>(
fmt: &'buf mut (dyn fmt::Write + 'buf),
state: &'state mut PadAdapterState,
) -> Self {
PadAdapter { buf: fmt, state }
}
}
impl fmt::Write for PadAdapter<'_, '_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
for s in s.split_inclusive('\n') {
if self.state.on_newline {
self.buf.write_str(" ")?;
}
self.state.on_newline = s.ends_with('\n');
self.buf.write_str(s)?;
}
Ok(())
}
fn write_char(&mut self, c: char) -> fmt::Result {
if self.state.on_newline {
self.buf.write_str(" ")?;
}
self.state.on_newline = c == '\n';
self.buf.write_char(c)
}
}

132
reid-llvm-lib/src/util.rs Normal file
View File

@ -0,0 +1,132 @@
use std::{
ffi::{CStr, CString, c_char},
ptr::null_mut,
string::FromUtf8Error,
};
use llvm_sys::{
core::{
LLVMCreateMemoryBufferWithMemoryRange, LLVMDisposeMemoryBuffer, LLVMGetBufferSize,
LLVMGetBufferStart,
},
error::LLVMDisposeErrorMessage,
prelude::LLVMMemoryBufferRef,
};
use crate::{
CompileResult, ErrorKind, Type,
builder::{Builder, InstructionValue},
};
pub fn into_cstring<T: Into<String>>(value: T) -> CString {
let string = value.into();
unsafe { CString::from_vec_with_nul_unchecked((string + "\0").into_bytes()) }
}
pub fn from_cstring(pointer: *mut c_char) -> Option<String> {
if pointer.is_null() {
None
} else {
unsafe { CStr::from_ptr(pointer).to_str().ok().map(|s| s.to_owned()) }
}
}
fn cstring_to_err(value: *mut c_char) -> Result<(), String> {
from_cstring(value)
.filter(|s| !s.is_empty())
.map_or(Ok(()), |s| Err(s))
}
/// Utility struct for LLVM's Error Messages, which need to be disposed
/// manually.
pub struct ErrorMessageHolder(*mut c_char);
impl ErrorMessageHolder {
pub fn null() -> Self {
ErrorMessageHolder(null_mut())
}
pub fn borrow_mut(&mut self) -> *mut *mut c_char {
&mut self.0
}
pub fn into_result(&self) -> Result<(), String> {
cstring_to_err(self.0)
}
}
impl Drop for ErrorMessageHolder {
fn drop(&mut self) {
unsafe {
if !self.0.is_null() {
LLVMDisposeErrorMessage(self.0);
}
}
}
}
/// Utility for creating and handling LLVM MemoryBuffers, needed for printing
/// out ASM and .o -files without relying on LLVM's own API.
pub struct MemoryBufferHolder {
pub buffer: LLVMMemoryBufferRef,
}
impl MemoryBufferHolder {
pub fn empty(name: &str) -> MemoryBufferHolder {
let array = [0i8; 0];
unsafe {
let buffer = LLVMCreateMemoryBufferWithMemoryRange(
array.as_ptr(),
array.len(),
into_cstring(name).as_ptr(),
0,
);
MemoryBufferHolder { buffer }
}
}
pub fn as_buffer(&self) -> Vec<u8> {
unsafe {
let start = LLVMGetBufferStart(self.buffer);
let size = LLVMGetBufferSize(self.buffer);
let mut buff = Vec::with_capacity(size);
for i in 0..size {
buff.push(*start.add(i) as u8);
}
buff
}
}
pub fn as_string(&self) -> Result<String, FromUtf8Error> {
String::from_utf8(self.as_buffer())
}
}
impl Drop for MemoryBufferHolder {
fn drop(&mut self) {
unsafe {
LLVMDisposeMemoryBuffer(self.buffer);
}
}
}
/// Make sure types for given instructions match. Return Ok(type) if they do,
/// and error otherwise.
pub fn match_types(
lhs: &InstructionValue,
rhs: &InstructionValue,
builder: &Builder,
) -> CompileResult<Type> {
let lhs_type = lhs.get_type(&builder);
let rhs_type = rhs.get_type(&builder);
if let (Ok(lhs_t), Ok(rhs_t)) = (lhs_type, rhs_type) {
if lhs_t == rhs_t {
Ok(lhs_t)
} else {
Err(ErrorKind::Null)
}
} else {
Err(ErrorKind::Null)
}
}

3
reid-lsp/.gitignore vendored
View File

@ -3,5 +3,4 @@ node_modules
dist
package-lock.json
pnpm-lock.yaml
tsconfig.tsbuildinfo
*.vsix
tsconfig.tsbuildinfo

View File

@ -1,6 +1,6 @@
# Change Log
All notable changes to the "reid-language-server" extension will be documented in this file.
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.

View File

@ -1,13 +1,11 @@
[package]
name = "reid-language-server"
version = "1.0.0"
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", registry="gitea-teascade", features=[] }
reid = { path = "../reid", version = "1.0.0-beta.2", registry="gitea-teascade", features=[] }
dashmap = "6.1.0"
serde = "*"
serde_json = "*"

View File

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

View File

@ -4,7 +4,7 @@
* ------------------------------------------------------------------------------------------ */
import * as path from 'path';
import { workspace, ExtensionContext, window, languages, SemanticTokensBuilder } from 'vscode';
import { workspace, ExtensionContext, window } from 'vscode';
import {
Executable,
@ -18,25 +18,16 @@ import {
let client: LanguageClient;
export function activate(context: ExtensionContext) {
const configuration = workspace.getConfiguration('reid-language-server');
let server_path: string = process.env.SERVER_PATH ?? configuration.get("language-server-path") ?? 'reid-language-server';
const regex = /\$(\w+)/;
while (regex.test(server_path)) {
let envVar = regex.exec(server_path)?.[1];
const envVal = envVar ? process.env[envVar] : undefined;
if (envVar === undefined || envVal === undefined) {
console.error(`No such environment variables as ${envVar}`);
}
server_path = server_path.replaceAll(`$${envVar}`, envVal ?? '');
}
const traceOutputChannel = window.createOutputChannel("Reid Language Server trace");
const command = process.env.SERVER_PATH || "reid-language-server";
const run: Executable = {
command: server_path,
command,
options: {
env: {
...process.env,
RUST_LOG: "debug",
RUST_BACKTRACE: 1,
}
}
};
@ -53,36 +44,21 @@ export function activate(context: ExtensionContext) {
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-language-server',
'reid-lsp',
'Reid Language Server',
serverOptions,
clientOptions
);
client.info(JSON.stringify(server_path));
client.info(`Loaded Reid Language Server from ${server_path}`);
client.info("hello");
workspace.onDidOpenTextDocument((e) => { });
client.info("Registering semantic tokens provide");
context.subscriptions.push(languages.registerDocumentSemanticTokensProvider({
language: 'reid',
scheme: 'file'
}, {
provideDocumentSemanticTokens: () => {
const builder = new SemanticTokensBuilder();
return builder.build();
}
}, {
tokenTypes: [],
tokenModifiers: [],
}));
workspace.onDidOpenTextDocument((e) => {
});
// Start the client. This will also launch the server
client.start();

View File

@ -1,92 +0,0 @@
{
"comments": {
"lineComment": "//",
},
"brackets": [
[
"{",
"}"
],
[
"[",
"]"
],
[
"(",
")"
]
],
"autoClosingPairs": [
{
"open": "{",
"close": "}"
},
{
"open": "[",
"close": "]"
},
{
"open": "(",
"close": ")"
},
{
"open": "'",
"close": "'",
"notIn": [
"string",
"comment"
]
},
{
"open": "\"",
"close": "\"",
"notIn": [
"string"
]
},
],
"autoCloseBefore": ";:.,=}])>` \n\t",
"surroundingPairs": [
[
"{",
"}"
],
[
"[",
"]"
],
[
"(",
")"
],
[
"\"",
"\""
],
],
"folding": {
"markers": {
"start": "^\\s*//\\s*#?region\\b",
"end": "^\\s*//\\s*#?endregion\\b"
}
},
"wordPattern": "[a-Z](\\w*)",
"indentationRules": {
"increaseIndentPattern": "^((?!\\/\\/).)*(\\{[^}\"'`]*|\\([^)\"'`]*|\\[[^\\]\"'`]*)$",
"decreaseIndentPattern": "^((?!.*?\\/\\*).*\\*/)?\\s*[\\)\\}\\]].*$"
},
"colorizedBracketPairs": [
[
"(",
")"
],
[
"[",
"]"
],
[
"{",
"}"
]
]
}

View File

@ -1,56 +1,46 @@
{
"name": "reid-language-server",
"name": "reid-lsp",
"displayName": "Reid Language Server",
"description": "Language Server Extension for Reid",
"version": "1.0.0",
"repository": {
"url": "https://git.teascade.net/teascade"
},
"version": "0.0.1",
"engines": {
"vscode": "^1.102.0"
},
"categories": [
"Other"
],
"activationEvents": [
"onLanguage:reid"
],
"main": "./dist/extension.js",
"icon": "./reid.png",
"publisher": "Teascade",
"author": {
"email": "teascade@teascade.net",
"name": "Teascade",
"url": "https://teascade.net"
},
"contributes": {
"languages": [
{
"id": "reid",
"extensions": [
".reid"
],
"aliases": [
"Reid"
],
"configuration": "./language-configuration.json",
"icon": {
"dark": "./reid.png",
"light": "./reid.png"
}
}
],
"breakpoints": [
{
"language": "reid"
]
}
],
"configuration": {
"type": "object",
"title": "reid-language-server",
"properties": {
"reid-language-server.language-server-path": {
"nrs-language-server.trace.server": {
"type": "string",
"scope": "window",
"default": "$HOME/.cargo/bin/reid-language-server",
"description": "Path to the Reid Language Server executable"
"enum": [
"off",
"messages",
"verbose"
],
"enumDescriptions": [
"No traces",
"Error only",
"Full log"
],
"default": "off",
"description": "Traces the communication between VS Code and the language server."
}
}
},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

File diff suppressed because it is too large Load Diff

View File

@ -3,41 +3,27 @@ use std::path::PathBuf;
use dashmap::DashMap;
use reid::ast::lexer::{FullToken, Position};
use reid::error_raporting::{self, ErrorModules, ReidError};
use reid::mir::SourceModuleId;
use reid::parse_module;
use serde::{Deserialize, Serialize};
use tokio::sync::Mutex;
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, DidSaveTextDocumentParams, DocumentFilter,
GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverContents, HoverParams, HoverProviderCapability,
InitializeParams, InitializeResult, InitializedParams, Location, MarkedString, MarkupContent, MarkupKind,
MessageType, OneOf, Range, ReferenceParams, RenameParams, SemanticToken, SemanticTokensLegend,
SemanticTokensOptions, SemanticTokensParams, SemanticTokensResult, SemanticTokensServerCapabilities,
ServerCapabilities, TextDocumentItem, TextDocumentRegistrationOptions, TextDocumentSyncCapability,
TextDocumentSyncKind, TextDocumentSyncOptions, TextDocumentSyncSaveOptions, TextEdit, Url, WorkspaceEdit,
WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities,
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};
use crate::analysis::{MODIFIER_LEGEND, StateMap, StaticAnalysis, TOKEN_LEGEND, analyze};
mod analysis;
#[derive(Debug)]
struct Backend {
client: Client,
analysis: DashMap<PathBuf, StaticAnalysis>,
module_to_path: DashMap<SourceModuleId, PathBuf>,
path_to_module: DashMap<PathBuf, SourceModuleId>,
module_id_counter: Mutex<SourceModuleId>,
}
#[derive(Serialize, Deserialize, Debug)]
struct CompletionData {
token_idx: usize,
path: PathBuf,
tokens: DashMap<String, Vec<FullToken>>,
ast: DashMap<String, reid::ast::Module>,
types: DashMap<String, DashMap<FullToken, Option<TypeKind>>>,
}
#[tower_lsp::async_trait]
@ -52,67 +38,29 @@ impl LanguageServer for Backend {
change: Some(TextDocumentSyncKind::FULL),
will_save: None,
will_save_wait_until: None,
save: Some(TextDocumentSyncSaveOptions::SaveOptions(lsp_types::SaveOptions {
include_text: Some(true),
})),
};
let capabilities = ServerCapabilities {
hover_provider: Some(HoverProviderCapability::Simple(true)),
completion_provider: Some(CompletionOptions {
trigger_characters: None,
all_commit_characters: None,
completion_item: Some(lsp_types::CompletionOptionsCompletionItem {
label_details_support: Some(true),
}),
resolve_provider: Some(false),
work_done_progress_options: lsp_types::WorkDoneProgressOptions {
work_done_progress: Some(true),
},
}),
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,
}),
semantic_tokens_provider: Some(SemanticTokensServerCapabilities::SemanticTokensRegistrationOptions(
lsp_types::SemanticTokensRegistrationOptions {
text_document_registration_options: TextDocumentRegistrationOptions {
document_selector: Some(vec![DocumentFilter {
language: Some("reid".to_owned()),
scheme: Some("file".to_owned()),
pattern: None,
}]),
},
semantic_tokens_options: SemanticTokensOptions {
work_done_progress_options: Default::default(),
legend: SemanticTokensLegend {
token_types: TOKEN_LEGEND.into(),
token_modifiers: MODIFIER_LEGEND.into(),
},
range: None,
full: Some(lsp_types::SemanticTokensFullOptions::Bool(true)),
},
static_registration_options: Default::default(),
},
)),
references_provider: Some(OneOf::Left(true)),
definition_provider: Some(OneOf::Left(true)),
rename_provider: Some(OneOf::Left(true)),
..Default::default()
save: None,
};
Ok(InitializeResult {
capabilities,
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!")
.log_message(MessageType::INFO, "Reid Language Server initialized hello!")
.await;
}
@ -121,64 +69,20 @@ impl LanguageServer for Backend {
}
async fn completion(&self, params: CompletionParams) -> jsonrpc::Result<Option<CompletionResponse>> {
let path = PathBuf::from(params.text_document_position.text_document.uri.path());
let analysis = self.analysis.get(&path);
let position = params.text_document_position.position;
let token = if let Some(analysis) = &analysis {
analysis.tokens.iter().enumerate().find(|(_, tok)| {
tok.position.1 == position.line + 1
&& (tok.position.0 <= position.character
&& (tok.position.0 + tok.token.len() as u32) > position.character)
})
} else {
None
};
// dbg!(position, token);
let list = if let Some((idx, _)) = token {
if let Some(token_analysis) = self.analysis.get(&path).unwrap().state.map.get(&idx) {
token_analysis
.autocomplete
.iter()
.map(|autocomplete| {
let mut item =
CompletionItem::new_simple(autocomplete.text.to_string(), autocomplete.kind.to_string());
item.data = Some(
serde_json::to_value(CompletionData {
token_idx: idx,
path: path.clone(),
})
.unwrap(),
);
item.documentation = autocomplete.documentation.as_ref().and_then(|d| {
Some(lsp_types::Documentation::MarkupContent(MarkupContent {
kind: MarkupKind::Markdown,
value: d.clone(),
}))
});
item
})
.collect()
} else {
Vec::new()
}
} else {
Vec::new()
};
// dbg!(&list);
Ok(Some(CompletionResponse::Array(list)))
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 analysis = self.analysis.get(&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(analysis) = &analysis {
analysis.tokens.iter().enumerate().find(|(_, tok)| {
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)
@ -187,8 +91,8 @@ impl LanguageServer for Backend {
None
};
let (range, ty, documentation) = if let Some((idx, token)) = token {
if let Some(analysis) = self.analysis.get(&path).unwrap().state.map.get(&idx) {
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 {
@ -201,50 +105,22 @@ impl LanguageServer for Backend {
character: (end.0 as i32 - 1).max(0) as u32,
},
};
if let Some(kind) = &analysis.hover.kind {
match kind {
analysis::HoverKind::Type(type_kind) => (
Some(range),
format!("{}", type_kind),
analysis.hover.documentation.clone(),
),
analysis::HoverKind::Function(name, function_params, return_type) => (
Some(range),
format!(
"{}({}) -> {}",
name,
function_params
.iter()
.map(|p| format!("{}: {}", p.name, p.ty))
.collect::<Vec<_>>()
.join(", "),
return_type
),
analysis.hover.documentation.clone(),
),
}
if let Some(ty) = possible_ty.clone() {
(Some(range), format!("{}", ty))
} else {
(
Some(range),
String::from("No type"),
analysis.hover.documentation.clone(),
)
(Some(range), String::from("no type"))
}
} else {
(None, String::from("no type"), None)
(None, String::from("no token"))
}
} else {
(None, String::from("no token"), None)
(None, String::from("no token"))
};
let contents = if let Some(doc) = documentation {
HoverContents::Array(vec![MarkedString::String(doc), MarkedString::String(format!("`{ty}`"))])
} else {
HoverContents::Markup(MarkupContent {
kind: MarkupKind::Markdown,
value: format!("`{ty}`"),
})
};
let contents = HoverContents::Markup(MarkupContent {
kind: MarkupKind::Markdown,
value: format!("`{ty}`"),
});
Ok(Some(Hover { contents, range }))
}
@ -268,270 +144,60 @@ impl LanguageServer for Backend {
})
.await
}
async fn did_save(&self, params: DidSaveTextDocumentParams) {
self.recompile(TextDocumentItem {
text: params.text.unwrap(),
uri: params.text_document.uri,
version: 0,
language_id: String::new(),
})
.await
}
async fn semantic_tokens_full(
&self,
params: SemanticTokensParams,
) -> jsonrpc::Result<Option<SemanticTokensResult>> {
let path = PathBuf::from(params.text_document.uri.path());
let analysis = self.analysis.get(&path);
let mut semantic_tokens = Vec::new();
if let Some(analysis) = analysis {
let mut prev_line = 0;
let mut prev_start = 0;
for (i, token) in analysis.tokens.iter().enumerate() {
let vscode_line = token.position.1.max(1) - 1;
let vscode_col = token.position.0.max(1) - 1;
let delta_line = vscode_line - prev_line;
let delta_start = if delta_line == 0 {
vscode_col - prev_start
} else {
vscode_col
};
if let Some(token_analysis) = analysis.state.map.get(&i) {
if let Some(symbol_id) = token_analysis.symbol {
let symbol = analysis.state.get_local_symbol(symbol_id);
if let Some(idx) = symbol.kind.into_token_idx(&self.state_map()) {
let semantic_token = SemanticToken {
delta_line,
delta_start,
length: token.token.len() as u32,
token_type: idx,
token_modifiers_bitset: symbol.kind.get_modifier().unwrap_or(0),
};
semantic_tokens.push(semantic_token);
prev_line = vscode_line;
prev_start = vscode_col;
}
}
}
}
}
Ok(Some(SemanticTokensResult::Tokens(lsp_types::SemanticTokens {
result_id: None,
data: semantic_tokens,
})))
}
async fn goto_definition(&self, params: GotoDefinitionParams) -> jsonrpc::Result<Option<GotoDefinitionResponse>> {
let path = PathBuf::from(params.text_document_position_params.text_document.uri.path());
let analysis = self.analysis.get(&path);
let position = params.text_document_position_params.position;
if let Some(analysis) = &analysis {
let token = analysis.tokens.iter().enumerate().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)
});
if let Some(token) = token {
if let Some((module_id, def_token)) = analysis.find_definition(token.0, &self.state_map()) {
return if let Some(path) = self.module_to_path.get(&module_id) {
Ok(Some(GotoDefinitionResponse::Scalar(lsp_types::Location {
uri: Url::from_file_path(path.value()).unwrap(),
range: token_to_range(def_token),
})))
} else {
Ok(None)
};
}
}
};
Ok(None)
}
async fn references(&self, params: ReferenceParams) -> jsonrpc::Result<Option<Vec<Location>>> {
let path = PathBuf::from(params.text_document_position.text_document.uri.path());
let analysis = self.analysis.get(&path);
let position = params.text_document_position.position;
if let Some(analysis) = &analysis {
let token = analysis.tokens.iter().enumerate().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)
});
if let Some(token) = token {
let reference_tokens = analysis.find_references(token.0, &self.state_map());
let mut locations = Vec::new();
if let Some(reference_tokens) = reference_tokens {
for (module_id, symbol_idx) in reference_tokens {
if let Some(path) = self.module_to_path.get(&module_id) {
let url = Url::from_file_path(path.value()).unwrap();
if let Some(inner_analysis) = self.analysis.get(path.value()) {
if let Some(token_idx) = inner_analysis.state.symbol_to_token.get(&symbol_idx) {
let token = inner_analysis.tokens.get(*token_idx).unwrap();
locations.push(lsp_types::Location {
uri: url,
range: token_to_range(token),
});
}
}
}
}
}
Ok(Some(locations))
} else {
Ok(None)
}
} else {
Ok(None)
}
}
async fn rename(&self, params: RenameParams) -> jsonrpc::Result<Option<WorkspaceEdit>> {
let path = PathBuf::from(params.text_document_position.text_document.uri.path());
let analysis = self.analysis.get(&path);
let position = params.text_document_position.position;
if let Some(analysis) = &analysis {
let token = analysis.tokens.iter().enumerate().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)
});
if let Some(token) = token {
let symbols = analysis.find_references(token.0, &self.state_map());
let mut changes: HashMap<Url, Vec<TextEdit>> = HashMap::new();
if let Some(symbols) = symbols {
for (module_id, symbol_id) in symbols {
let path = self.module_to_path.get(&module_id);
if let Some(path) = path {
let url = Url::from_file_path(path.value()).unwrap();
let analysis = self.analysis.get(&path.clone());
if let Some(analysis) = analysis {
if let Some(token_idx) = analysis.state.symbol_to_token.get(&symbol_id) {
let token = analysis.tokens.get(*token_idx).unwrap();
// edits = changes.get(k)
let edit = TextEdit {
range: token_to_range(token),
new_text: params.new_name.clone(),
};
if let Some(edits) = changes.get_mut(&url) {
edits.push(edit);
} else {
changes.insert(url, vec![edit]);
}
}
}
}
}
}
Ok(Some(WorkspaceEdit {
changes: Some(changes),
document_changes: None,
change_annotations: None,
}))
} else {
Ok(None)
}
} else {
Ok(None)
}
}
}
fn token_to_range(token: &FullToken) -> lsp_types::Range {
Range {
start: lsp_types::Position {
line: token.position.1.max(1) - 1,
character: token.position.0.max(1) - 1,
},
end: lsp_types::Position {
line: token.position.1.max(1) - 1,
character: token.position.0.max(1) - 1 + token.token.len() as u32,
},
}
}
impl Backend {
fn state_map(&self) -> StateMap {
let mut state_map = HashMap::new();
for path_state in self.analysis.iter() {
let (path, state) = path_state.pair();
if let Some(module_id) = self.path_to_module.get(path) {
state_map.insert(*module_id, state.state.clone());
}
}
state_map
}
async fn recompile(&self, params: TextDocumentItem) {
let file_path = PathBuf::from(params.uri.clone().path());
let path = PathBuf::from(params.uri.clone().path());
let file_name = path.file_name().unwrap().to_str().unwrap().to_owned();
let mut map: ErrorModules = Default::default();
for url_module in self.path_to_module.iter() {
let (url, module) = url_module.pair();
map.add_module(
url.file_name().unwrap().to_str().unwrap().to_owned(),
Some(url.clone()),
Some(*module),
);
}
let module_id = if let Some(module_id) = self.path_to_module.get(&file_path) {
*module_id
} else {
let mut lock = self.module_id_counter.lock().await;
let module_id = lock.increment();
drop(lock);
self.path_to_module.insert(file_path.clone(), module_id);
self.module_to_path.insert(module_id, file_path.clone());
module_id
};
let parse_res = parse(&params.text, file_path.clone(), &mut map, module_id);
let mut map = Default::default();
let parse_res = parse(&params.text, path.clone(), &mut map);
let (tokens, result) = match parse_res {
Ok((module_id, tokens)) => (
tokens.clone(),
analyze(module_id, tokens, file_path.clone(), &mut map, &self.state_map()),
),
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(mut analysis)) => {
if let Some(reid_error) = &mut analysis.error {
self.client
.log_message(
MessageType::INFO,
format!("Successfully compiled despite parsing errors!"),
)
.await;
reid_error.errors.dedup();
for error in &reid_error.errors {
diagnostics.push(reid_error_into_diagnostic(error, &tokens));
self.client.log_message(MessageType::INFO, format!("{}", error)).await;
}
}
self.analysis.insert(file_path, analysis);
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 {
diagnostics.push(reid_error_into_diagnostic(error, &tokens));
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;
}
}
@ -543,50 +209,45 @@ impl Backend {
}
}
fn reid_error_into_diagnostic(error: &error_raporting::ErrorKind, tokens: &Vec<FullToken>) -> Diagnostic {
let meta = error.get_meta();
let positions = meta
.range
.into_position(&tokens)
.unwrap_or((Position(0, 0), Position(0, 0)));
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,
}
struct CompileResult {
tokens: Vec<FullToken>,
types: DashMap<FullToken, Option<TypeKind>>,
}
fn parse(
source: &str,
path: PathBuf,
map: &mut ErrorModules,
module_id: SourceModuleId,
) -> Result<(SourceModuleId, Vec<FullToken>), ReidError> {
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(),
Some(path),
map,
Some(module_id),
)?)
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]
@ -596,10 +257,234 @@ async fn main() {
let (service, socket) = LspService::new(|client| Backend {
client,
analysis: DashMap::new(),
module_to_path: DashMap::new(),
path_to_module: DashMap::new(),
module_id_counter: Mutex::new(SourceModuleId(0)),
ast: DashMap::new(),
tokens: DashMap::new(),
types: DashMap::new(),
});
Server::new(stdin, stdout, socket).serve(service).await;
}
pub fn find_type_in_context(module: &mir::Module, token_idx: usize) -> Option<TypeKind> {
for import in &module.imports {
if import.1.contains(token_idx) {
return None;
}
}
for typedef in &module.typedefs {
if !typedef.meta.contains(token_idx) {
continue;
}
match &typedef.kind {
mir::TypeDefinitionKind::Struct(StructType(fields)) => {
for field in fields {
if field.2.contains(token_idx) {
return Some(field.1.clone());
}
}
}
}
}
for binop in &module.binop_defs {
if let Some(meta) = binop.block_meta() {
if !meta.contains(token_idx) {
continue;
}
} else {
continue;
}
return match &binop.fn_kind {
mir::FunctionDefinitionKind::Local(block, _) => find_type_in_block(&block, module.module_id, token_idx),
mir::FunctionDefinitionKind::Extern(_) => None,
mir::FunctionDefinitionKind::Intrinsic(_) => None,
};
}
for (_, function) in &module.associated_functions {
if !(function.signature() + function.block_meta()).contains(token_idx) {
continue;
}
for param in &function.parameters {
if param.meta.contains(token_idx) {
return Some(param.ty.clone());
}
}
return match &function.kind {
mir::FunctionDefinitionKind::Local(block, _) => find_type_in_block(&block, module.module_id, token_idx),
mir::FunctionDefinitionKind::Extern(_) => None,
mir::FunctionDefinitionKind::Intrinsic(_) => None,
};
}
for function in &module.functions {
if !(function.signature() + function.block_meta()).contains(token_idx) {
continue;
}
for param in &function.parameters {
if param.meta.contains(token_idx) {
return Some(param.ty.clone());
}
}
return match &function.kind {
mir::FunctionDefinitionKind::Local(block, _) => find_type_in_block(&block, module.module_id, token_idx),
mir::FunctionDefinitionKind::Extern(_) => None,
mir::FunctionDefinitionKind::Intrinsic(_) => None,
};
}
None
}
pub fn find_type_in_block(block: &mir::Block, module_id: SourceModuleId, token_idx: usize) -> Option<TypeKind> {
if !block.meta.contains(token_idx) {
return None;
}
for statement in &block.statements {
if !statement.1.contains(token_idx) {
continue;
}
match &statement.0 {
mir::StmtKind::Let(named_variable_ref, _, expression) => {
if named_variable_ref.2.contains(token_idx) {
return expression
.return_type(&Default::default(), module_id)
.ok()
.map(|(_, ty)| ty);
} else {
return find_type_in_expr(&expression, module_id, token_idx);
}
}
mir::StmtKind::Set(lhs, rhs) => {
return find_type_in_expr(lhs, module_id, token_idx).or(find_type_in_expr(rhs, module_id, token_idx));
}
mir::StmtKind::Import(_) => {}
mir::StmtKind::Expression(expression) => return find_type_in_expr(expression, module_id, token_idx),
mir::StmtKind::While(WhileStatement { condition, block, .. }) => {
return find_type_in_expr(condition, module_id, token_idx)
.or(find_type_in_block(block, module_id, token_idx));
}
}
}
if let Some((_, Some(return_exp))) = &block.return_expression {
if let Some(ty) = find_type_in_expr(return_exp, module_id, token_idx) {
return Some(ty);
}
}
None
}
pub fn find_type_in_expr(expr: &mir::Expression, module_id: SourceModuleId, token_idx: usize) -> Option<TypeKind> {
if !expr.1.contains(token_idx) {
return None;
}
match &expr.0 {
mir::ExprKind::Variable(named_variable_ref) => Some(named_variable_ref.0.clone()),
mir::ExprKind::Indexed(value, type_kind, index_expr) => Some(
find_type_in_expr(&value, module_id, token_idx)
.or(find_type_in_expr(&index_expr, module_id, token_idx))
.unwrap_or(type_kind.clone()),
),
mir::ExprKind::Accessed(expression, type_kind, _, meta) => {
if meta.contains(token_idx) {
Some(type_kind.clone())
} else {
find_type_in_expr(&expression, module_id, token_idx)
}
}
mir::ExprKind::Array(expressions) => {
for expr in expressions {
if let Some(ty) = find_type_in_expr(expr, module_id, token_idx) {
return Some(ty);
}
}
None
}
mir::ExprKind::Struct(name, items) => {
for (_, expr, meta) in items {
if meta.contains(token_idx) {
return expr.return_type(&Default::default(), module_id).map(|(_, t)| t).ok();
}
if let Some(ty) = find_type_in_expr(expr, module_id, token_idx) {
return Some(ty);
}
}
Some(TypeKind::CustomType(mir::CustomTypeKey(name.clone(), module_id)))
}
mir::ExprKind::Literal(literal) => Some(literal.as_type()),
mir::ExprKind::BinOp(_, lhs, rhs, type_kind) => {
if let Some(ty) = find_type_in_expr(lhs, module_id, token_idx) {
return Some(ty);
}
if let Some(ty) = find_type_in_expr(rhs, module_id, token_idx) {
return Some(ty);
}
Some(type_kind.clone())
}
mir::ExprKind::FunctionCall(FunctionCall {
return_type,
parameters,
..
}) => {
for expr in parameters {
if let Some(ty) = find_type_in_expr(expr, module_id, token_idx) {
return Some(ty);
}
}
Some(return_type.clone())
}
mir::ExprKind::AssociatedFunctionCall(
_,
FunctionCall {
return_type,
parameters,
..
},
) => {
for expr in parameters {
if let Some(ty) = find_type_in_expr(expr, module_id, token_idx) {
return Some(ty);
}
}
Some(return_type.clone())
}
mir::ExprKind::If(IfExpression(cond, then_e, else_e)) => find_type_in_expr(&cond, module_id, token_idx)
.or(find_type_in_expr(&then_e, module_id, token_idx))
.or(else_e.clone().and_then(|e| find_type_in_expr(&e, module_id, token_idx))),
mir::ExprKind::Block(block) => find_type_in_block(block, module_id, token_idx),
mir::ExprKind::Borrow(expression, mutable) => {
if let Some(ty) = find_type_in_expr(&expression, module_id, token_idx) {
return Some(ty);
}
if let Ok(inner) = expression.return_type(&Default::default(), module_id).map(|(_, ty)| ty) {
Some(TypeKind::Borrow(Box::new(inner.clone()), *mutable))
} else {
None
}
}
mir::ExprKind::Deref(expression) => {
if let Some(ty) = find_type_in_expr(&expression, module_id, token_idx) {
return Some(ty);
}
if let Ok(TypeKind::Borrow(inner, _)) =
expression.return_type(&Default::default(), module_id).map(|(_, ty)| ty)
{
Some(*inner.clone())
} else {
None
}
}
mir::ExprKind::CastTo(expression, type_kind) => {
Some(find_type_in_expr(&expression, module_id, token_idx).unwrap_or(type_kind.clone()))
}
mir::ExprKind::GlobalRef(_, type_kind) => Some(type_kind.clone()),
}
}

View File

@ -1,11 +1,56 @@
{
"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": [
{
@ -15,17 +60,26 @@
"include": "#fn-signature"
},
{
"include": "#namespace"
"include": "#common-type"
},
{
"include": "#common-type"
"include": "#binop-impl"
},
{
"include": "#type-impl"
},
{
"include": "#struct-definition"
},
{
"include": "#block"
},
{
"include": "#binop"
},
{
"include": "#namespace"
},
{
"include": "#cast"
},
@ -41,6 +95,9 @@
{
"include": "#keywords"
},
{
"include": "#struct-expression"
},
{
"include": "#number-literal"
},
@ -55,44 +112,93 @@
}
]
},
"punctuation": {
"patterns": [
{
"match": "::",
"name": "keyword.operator.namespace.reid"
},
{
"match": ":",
"name": "keyword.operator.colon.reid"
},
{
"match": ";",
"name": "punctuation.semi.reid"
},
{
"match": ".",
"name": "punctuation.dot.reid"
},
{
"match": ",",
"name": "punctuation.comma.reid"
},
{
"match": "\\{|\\}",
"name": "punctuation.brackets.curly.reid"
},
{
"match": "\\(|\\)",
"name": "punctuation.parenthesis.reid"
}
]
},
"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": {
"match": "(struct)\\s*(\\w+)",
"begin": "(struct)\\s*(\\w+)\\s*\\{",
"end": "\\}",
"captures": {
"1": {
"name": "keyword.struct.reid"
@ -100,10 +206,15 @@
"2": {
"name": "entity.name.type"
}
}
},
"patterns": [
{
"include": "#annotated-identifier"
}
]
},
"struct-expression": {
"begin": "\\b([A-Z]\\w*)\\s*\\{",
"begin": "([A-Z]\\w*)\\s*\\{",
"end": "\\}",
"captures": {
"1": {
@ -119,19 +230,19 @@
"number-literal": {
"patterns": [
{
"match": "\\b0x[0-9a-fA-F]+(\\.[0-9a-fA-F]+)?\\b",
"match": "0x[0-9a-fA-F]+(\\.[0-9a-fA-F]+)?",
"name": "constant.hexadecimal"
},
{
"match": "\\b0o[0-7]+(\\.[0-7]+)?\\b",
"match": "0o[0-7]+(\\.[0-7]+)?",
"name": "constant.octal"
},
{
"match": "\\b0b[01]+(\\.[01]+)?\\b",
"match": "0b[01]+(\\.[01]+)?",
"name": "constant.binary"
},
{
"match": "\\b[0-9]+(\\.[0-9]+)?\\b",
"match": "[0-9]+(\\.[0-9]+)?",
"name": "constant.numeric"
}
]
@ -142,16 +253,25 @@
"name": "string.quoted.double",
"patterns": [
{
"match": "\\\\\\w",
"match": "\\.",
"name": "constant.character.escape"
}
]
},
"block": {
"begin": "\\{",
"end": "\\}",
"patterns": [
{
"include": "#expression"
}
]
},
"namespace": {
"match": "(\\w+)(\\:\\:)",
"captures": {
"1": {
"name": "entity.name.namespace.reid"
"name": "entity.name.function.reid"
},
"2": {
"name": "keyword.operator.namespace.reid"
@ -170,19 +290,11 @@
}
},
"function-call": {
"begin": "(\\w+)?(\\()",
"end": "(\\))",
"begin": "(\\w+)?\\(",
"end": "\\)",
"beginCaptures": {
"1": {
"name": "entity.name.function.reid"
},
"2": {
"name": "punctuation.parenthesis.reid"
}
},
"endCaptures": {
"1": {
"name": "punctuation.parenthesis.reid"
}
},
"patterns": [
@ -210,10 +322,28 @@
}
]
},
"annotated-identifier": {
"begin": "(\\w+)\\:",
"end": ",",
"beginCaptures": {
"1": {
"name": "variable.language.reid"
}
},
"patterns": [
{
"include": "#expression"
}
]
},
"identifier": {
"patterns": [
{
"match": "\\b(?:\\w+)\\b",
"match": "[A-Z]\\w*",
"name": "entity.name.type.reid"
},
{
"match": "\\w+",
"name": "variable.language.reid"
}
]
@ -221,32 +351,16 @@
"keywords": {
"patterns": [
{
"match": "\\b(?:let|mut|pub|extern)\\b",
"match": "let|mut|pub|extern",
"name": "storage.type.reid"
},
{
"match": "\\bimport\\b",
"name": "keyword.import.reid"
},
{
"match": "\\bbinop\\b",
"name": "keyword.binop.reid"
},
{
"match": "\\bimpl\\b",
"name": "keyword.impl.reid"
},
{
"match": "\\b(?:if|return|for|in)\\b",
"match": "if|return",
"name": "keyword.control"
},
{
"match": "\\bself\\b",
"match": "self",
"name": "variable.language.self.reid"
},
{
"match": "\\bfn\\b",
"name": "keyword.fn.reid"
}
]
},
@ -274,7 +388,7 @@
]
},
"common-type": {
"match": "\\b(?:u8|u16|u32|u64|u128|i8|i16|i32|i64|i128|f16|f16b|f32|f64|f80|f128|f128ppc|bool|char|([A-Z]\\w*))\\b",
"match": "u8|u16|u32|u64|u128|i8|i16|i32|i64|i128|bool",
"name": "entity.name.type.common.reid"
}
}

View File

@ -1,52 +1,133 @@
scopeName: source.reid
patterns:
- include: "#import"
- include: "#expression"
repository:
expression:
# 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: "#comment"
- include: "#fn-signature"
- include: "#namespace"
- include: "#common-type"
- include: "#struct-definition"
- include: "#binop"
- include: "#cast"
- include: "#function-call"
- include: "#parenthesis"
- include: "#array"
- include: "#keywords"
- include: "#number-literal"
- include: "#string-literal"
- include: "#identifier"
- include: "#punctuation"
- include: "#punctiation"
punctuation:
patterns:
- match: "::"
name: keyword.operator.namespace.reid
- match: ":"
name: keyword.operator.colon.reid
- match: ";"
name: punctuation.semi.reid
- match: "."
name: punctuation.dot.reid
- match: ","
name: punctuation.comma.reid
- match: "\\{|\\}"
name: punctuation.brackets.curly.reid
- match: "\\(|\\)"
name: punctuation.parenthesis.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:
match: "(struct)\\s*(\\w+)"
begin: "(struct)\\s*(\\w+)\\s*\\{"
end: "\\}"
captures:
1:
name: keyword.struct.reid
2:
name: entity.name.type
patterns:
- include: "#annotated-identifier"
struct-expression:
begin: "\\b([A-Z]\\w*)\\s*\\{"
begin: "([A-Z]\\w*)\\s*\\{"
end: "\\}"
captures:
1:
@ -55,26 +136,31 @@ repository:
- include: "#expression"
number-literal:
patterns:
- match: "\\b0x[0-9a-fA-F]+(\\.[0-9a-fA-F]+)?\\b"
- match: "0x[0-9a-fA-F]+(\\.[0-9a-fA-F]+)?"
name: "constant.hexadecimal"
- match: "\\b0o[0-7]+(\\.[0-7]+)?\\b"
- match: "0o[0-7]+(\\.[0-7]+)?"
name: "constant.octal"
- match: "\\b0b[01]+(\\.[01]+)?\\b"
- match: "0b[01]+(\\.[01]+)?"
name: "constant.binary"
- match: "\\b[0-9]+(\\.[0-9]+)?\\b"
- match: "[0-9]+(\\.[0-9]+)?"
name: "constant.numeric"
string-literal:
begin: '"'
end: '"'
name: string.quoted.double
patterns:
- match: "\\\\\\w"
- match: "\\."
name: constant.character.escape
block:
begin: "\\{"
end: "\\}"
patterns:
- include: "#expression"
namespace:
match: "(\\w+)(\\:\\:)"
captures:
1:
name: entity.name.namespace.reid
name: entity.name.function.reid
2:
name: keyword.operator.namespace.reid
cast:
@ -85,16 +171,11 @@ repository:
2:
name: entity.name.type.reid
function-call:
begin: "(\\w+)?(\\()"
end: "(\\))"
begin: "(\\w+)?\\("
end: "\\)"
beginCaptures:
1:
name: entity.name.function.reid
2:
name: punctuation.parenthesis.reid
endCaptures:
1:
name: punctuation.parenthesis.reid
patterns:
- include: "#expression"
parenthesis:
@ -108,26 +189,28 @@ repository:
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: "\\b(?:\\w+)\\b"
- match: "[A-Z]\\w*"
name: entity.name.type.reid
- match: "\\w+"
name: variable.language.reid
keywords:
patterns:
- match: "\\b(?:let|mut|pub|extern)\\b"
name: storage.type.reid
- match: "\\bimport\\b"
name: keyword.import.reid
- match: "\\bbinop\\b"
name: keyword.binop.reid
- match: "\\bimpl\\b"
name: keyword.impl.reid
- match: "\\b(?:if|return|for|in)\\b"
name: keyword.control
- match: "\\bself\\b"
name: variable.language.self.reid
- match: "\\bfn\\b"
name: keyword.fn.reid
- 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
@ -143,7 +226,7 @@ repository:
patterns:
- include: "#expression"
common-type:
match: "\\b(?:u8|u16|u32|u64|u128|i8|i16|i32|i64|i128|f16|f16b|f32|f64|f80|f128|f128ppc|bool|char|([A-Z]\\w*))\\b"
match: "u8|u16|u32|u64|u128|i8|i16|i32|i64|i128|bool"
name: entity.name.type.common.reid

View File

@ -1,23 +1,21 @@
[package]
name = "reid"
version = "1.0.0"
version = "1.0.0-beta.2"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = ["color", "cli"]
default = ["color"]
color = ["colored"]
cli = ["argh", "stderrlog"]
log_output = []
context_debug = []
[dependencies]
## Make it easier to generate errors
thiserror = "1.0.44"
reid-lib = { path = "../reid-llvm-lib", version = "1.0.0", registry="gitea-teascade" }
reid-lib = { path = "../reid-llvm-lib", version = "1.0.0-beta.1", registry="gitea-teascade" }
argh = { version = "0.1.13", optional = true }
stderrlog = { version = "0.6.0", optional = true }
log = "*"
colored = { version = "3.0.0", optional = true }
colored = {version = "3.0.0", optional = true}

View File

@ -20,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)?;
@ -37,29 +38,35 @@ fn main() -> Result<(), std::io::Error> {
},
CustomIRs { llir, mir },
)) => {
log::trace!("{}", _llvm_ir);
log::debug!("Compiled with triple: {}\n", &_triple);
log::debug!("Output LLVM IR to {:?}", llvm_ir_path);
log::debug!("Output Assembly to {:?}", asm_path);
log::debug!("Output Object-file to {:?}\n", object_path);
log::debug!("Output LLIR-file to {:?}\n", llir_path);
log::debug!("Output MIR-file to {:?}\n", mir_path);
#[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);
}
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!");
fs::write(&object_path, &obj_buffer).expect("Could not write Object-file!");
fs::write(&llir_path, &llir).expect("Could not write LLIR-file!");
fs::write(&mir_path, &mir).expect("Could not write MIR-file!");
let after = std::time::SystemTime::now();
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").with_library("m");
let mut linker = LDRunner::from_command(&linker).with_library("c");
for library in libraries {
linker = linker.with_library(&library);
}
@ -68,7 +75,8 @@ fn main() -> Result<(), std::io::Error> {
Err(e) => panic!("{}", e),
};
} else {
log::error!("Please input compiled file path!")
#[cfg(feature = "log_output")]
println!("Please input compiled file path!")
}
Ok(())
}

View File

@ -3,7 +3,6 @@ extern fn puts(message: *char) -> i32;
extern fn free(ptr: *u8);
extern fn div(numerator: i32, denominator: i32) -> div_t;
/// Editable string value that can be printed and extended
struct String {
inner: *char,
length: u64,
@ -12,7 +11,6 @@ struct String {
}
impl String {
/// Returns a new empty `String`-object, which must later be manually freed.
pub fn new() -> String {
String {
inner: char::malloc(0),
@ -22,8 +20,6 @@ impl String {
}
}
/// Creates a new `String`-object containing initially data from the given
/// string-literal which must be later freed.
pub fn from(str: *char) -> String {
let length = str_length(str) as u64;
let mut new = String::new();
@ -37,14 +33,12 @@ impl String {
return new;
}
/// Same as `concat`
pub fn push(&mut self, other: String) {
for i in 0 .. (str_length(other.inner) - 1) {
add_char(self, other.inner[i]);
}
}
/// Adds a character to the end of this string
pub fn add_char(&mut self, c: char) {
if ((*self).length + 1) >= (*self).max_length {
let new = char::malloc((*self).max_length + 4);
@ -63,7 +57,6 @@ impl String {
(*self).length = (*self).length + 1;
}
/// Formats the given number into the end of the string.
pub fn push_num(&mut self, num: u64) {
if num >= 10 {
self.push_num(num / 10)
@ -82,22 +75,18 @@ impl String {
else if rem == 9 { self.add_char('9'); }
}
/// Concatenates `source` to the end of `destination`.
pub fn concat(&mut self, other: &String) {
for i in 0 .. *other.length {
self.add_char(*other.inner[i]);
}
}
/// Edits given `string` by setting the character at index `position` to be
/// `c`.
pub fn set(&mut self, c: char, position: u64) {
if position <= (*self).length {
(*self).inner[position] = c;
}
}
/// Frees this given string
pub fn free(&self) {
free((*self).inner as *u8);
}
@ -122,17 +111,14 @@ struct div_t {
remainder: i32,
}
/// Print given string to stdout
pub fn print(message: String) {
puts(message.inner);
}
/// Divide an integer, returning the quotient and remainder.
pub fn int_div(numerator: i32, denominator: i32) -> div_t {
return div(numerator, denominator);
}
/// (deprecated) creates a new editable string
pub fn new_string() -> String {
String {
inner: char::malloc(0),
@ -142,8 +128,6 @@ pub fn new_string() -> String {
}
}
/// Creates a new `String`-object containing initially data from the given
/// string-literal which must be later freed.
pub fn from_str(str: *char) -> String {
let length = str_length(str) as u64;
let mut new = new_string();
@ -157,7 +141,6 @@ pub fn from_str(str: *char) -> String {
return new;
}
/// (deprecated) Adds a character to the end of a given string
pub fn add_char(string: &mut String, c: char) {
if ((*string).length + 1) >= (*string).max_length {
let new = char::malloc((*string).max_length + 4);
@ -176,14 +159,12 @@ pub fn add_char(string: &mut String, c: char) {
(*string).length = (*string).length + 1;
}
/// (deprecated) sets a character in a string
pub fn set_char(string: &mut String, c: char, position: u64) {
if position <= (*string).length {
(*string).inner[position] = c;
}
}
/// (deprecated) frees given string
pub fn free_string(string: &String) {
free((*string).inner as *u8);
}
@ -202,7 +183,6 @@ fn str_length(string: *char) -> u32 {
return pos + 1;
}
/// (deprecated) concatenates number to the end of this string.
pub fn add_num_to_str(string: &mut String, num: u64) {
if num >= 10 {
add_num_to_str(string, num / 10)
@ -221,15 +201,12 @@ pub fn add_num_to_str(string: &mut String, num: u64) {
else if rem == 9 { add_char(string, '9'); }
}
/// (deprecated) concatenates two strings to the destination
pub fn concat_strings(destination: &mut String, source: String) {
for i in 0 .. (str_length(source.inner) - 1) {
add_char(destination, source.inner[i]);
}
}
/// Returns `value` as clamped between `min` and `max`. Equivalent to
/// `max(min(value, max), min)`
pub fn clamp(min: f32, max: f32, value: f32) -> f32 {
if value > max {
return max;
@ -240,7 +217,6 @@ pub fn clamp(min: f32, max: f32, value: f32) -> f32 {
return value;
}
/// Returns the absolute value of `value`.
pub fn abs(f: f32) -> f32 {
if f < 0.0 {
return f * (0.0 - 1.0);

View File

@ -116,7 +116,6 @@ pub enum Token {
Whitespace(String),
Comment(String),
Doc(String),
Eof,
}
@ -197,7 +196,6 @@ impl ToString for Token {
Token::Percent => String::from('%'),
Token::Whitespace(val) => val.clone(),
Token::Comment(val) => format!("//{}", val.clone()),
Token::Doc(val) => format!("///{}", val.clone()),
Token::Unknown(val) => val.to_string(),
}
}
@ -311,25 +309,13 @@ pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error
}
// Comments
'/' if cursor.first() == Some('/') => {
cursor.next();
let doc = if cursor.first() == Some('/') {
cursor.next();
true
} else {
false
};
let mut comment = String::new();
while !matches!(cursor.first(), Some('\n') | None) {
if let Some(c) = cursor.next() {
comment.push(c);
}
}
if doc {
Token::Doc(comment)
} else {
Token::Comment(comment)
}
Token::Comment(comment)
}
'\"' | '\'' => {
let mut value = String::new();

View File

@ -1,7 +1,7 @@
//! This is the module that contains relevant code to parsing Reid, that is to
//! say transforming a Vec of FullTokens into a loose parsed AST that can be
//! used for unwrapping syntax sugar, and then be transformed into Reid MIR.
use std::path::PathBuf;
use std::{fs::Metadata, path::PathBuf};
use token_stream::TokenRange;
@ -184,7 +184,7 @@ pub struct LetStatement {
}
#[derive(Debug, Clone)]
pub struct ImportStatement(pub Vec<(String, TokenRange)>, pub TokenRange);
pub struct ImportStatement(pub Vec<String>, pub TokenRange);
#[derive(Debug)]
pub struct FunctionDefinition(pub FunctionSignature, pub bool, pub Block, pub TokenRange);
@ -192,7 +192,6 @@ pub struct FunctionDefinition(pub FunctionSignature, pub bool, pub Block, pub To
#[derive(Debug, Clone)]
pub struct FunctionSignature {
pub name: String,
pub documentation: Option<String>,
pub self_kind: SelfKind,
pub params: Vec<(String, Type, TokenRange)>,
pub return_type: Option<Type>,
@ -208,7 +207,7 @@ pub enum SelfKind {
None,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy)]
pub enum ReturnType {
Soft,
Hard,
@ -273,9 +272,9 @@ pub enum TopLevelStatement {
#[derive(Debug)]
pub struct BinopDefinition {
pub lhs: (String, Type, TokenRange),
pub lhs: (String, Type),
pub op: BinaryOperator,
pub rhs: (String, Type, TokenRange),
pub rhs: (String, Type),
pub return_ty: Type,
pub block: Block,
pub signature_range: TokenRange,

View File

@ -175,50 +175,7 @@ impl Parse for AssociatedFunctionCall {
let ty = stream.parse()?;
stream.expect(Token::Colon)?;
stream.expect(Token::Colon)?;
if stream.next_is_whitespace() {
stream.expecting_err_nonfatal("associated function name");
return Ok(AssociatedFunctionCall(
ty,
FunctionCallExpression {
name: String::new(),
params: Vec::new(),
range: stream.get_range_prev_curr().unwrap(),
is_macro: false,
},
));
}
match stream.parse() {
Ok(fn_call) => Ok(AssociatedFunctionCall(ty, fn_call)),
_ => {
if let Some(Token::Identifier(fn_name)) = stream.peek() {
stream.next();
stream.expected_err_nonfatal("associated function call");
Ok(AssociatedFunctionCall(
ty,
FunctionCallExpression {
name: fn_name,
params: Vec::new(),
range: stream.get_range_prev_curr().unwrap(),
is_macro: false,
},
))
} else {
stream.expected_err_nonfatal("associated function name");
Ok(AssociatedFunctionCall(
ty,
FunctionCallExpression {
name: String::new(),
params: Vec::new(),
range: stream.get_range_prev_curr().unwrap(),
is_macro: false,
},
))
}
}
}
Ok(AssociatedFunctionCall(ty, stream.parse()?))
}
}
@ -287,9 +244,9 @@ impl Parse for PrimaryExpression {
stream.get_range().unwrap(),
)
} else if let Some(Token::Star) = stream.peek() {
stream.next(); // Consume Star
stream.next(); // Consume Et
apply_inner(stream.parse()?, |e| {
Expression(Kind::Deref(Box::new(e.0.clone())), e.0 .1)
Expression(Kind::Deref(Box::new(e.0)), stream.get_range().unwrap())
})
} else if let Ok(unary) = stream.parse() {
Expression(
@ -510,7 +467,7 @@ fn parse_binop_rhs(
if curr_token_prec < next_prec {
// Operator on the right of rhs has more precedence, turn
// rhs into lhs for new binop
rhs = stream.parse_with(|mut st| parse_binop_rhs(&mut st, rhs, Some(op)))?;
rhs = parse_binop_rhs(stream, rhs, Some(op))?;
} else {
let _ = prev_operator.insert(next_op);
}
@ -653,7 +610,7 @@ impl Parse for LetStatement {
stream.expect(Token::Equals)?;
let expression = stream.parse()?;
stream.expect_nonfatal(Token::Semi).ok();
stream.expect(Token::Semi)?;
Ok(LetStatement {
name: variable,
ty,
@ -674,21 +631,19 @@ impl Parse for ImportStatement {
let mut import_list = Vec::new();
if let Some(Token::Identifier(name)) = stream.next() {
import_list.push((name, stream.get_range_prev_curr().unwrap()));
import_list.push(name);
while stream.expect(Token::Colon).is_ok() && stream.expect(Token::Colon).is_ok() {
if let Some(Token::Identifier(name)) = stream.peek() {
stream.next(); // Consume identifier
import_list.push((name, stream.get_range_prev_curr().unwrap()));
if let Some(Token::Identifier(name)) = stream.next() {
import_list.push(name);
} else {
stream.expected_err_nonfatal("identifier");
break;
Err(stream.expected_err("identifier")?)?
}
}
} else {
Err(stream.expected_err("identifier")?)?
}
stream.expect_nonfatal(Token::Semi).ok();
stream.expect(Token::Semi)?;
Ok(ImportStatement(import_list, stream.get_range().unwrap()))
}
@ -696,8 +651,6 @@ impl Parse for ImportStatement {
impl Parse for FunctionDefinition {
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
let documentation = stream.find_documentation();
let is_pub = if let Some(Token::PubKeyword) = stream.peek() {
stream.next(); // Consume pub
true
@ -706,10 +659,8 @@ impl Parse for FunctionDefinition {
};
stream.expect(Token::FnKeyword)?;
let mut signature: FunctionSignature = stream.parse()?;
signature.documentation = documentation;
Ok(FunctionDefinition(
signature,
stream.parse()?,
is_pub,
stream.parse()?,
stream.get_range().unwrap(),
@ -814,7 +765,6 @@ impl Parse for FunctionSignature {
Ok(FunctionSignature {
name,
documentation: None,
params,
self_kind,
return_type,
@ -838,7 +788,7 @@ impl Parse for Block {
// if semicolon is missing.
if !matches!(e, Expression(ExpressionKind::IfExpr(_), _)) {
// In theory could ignore the missing semicolon..
stream.expected_err_nonfatal("semicolon to complete statement");
return Err(stream.expected_err("semicolon to complete statement")?);
}
statements.push(BlockLevelStatement::Expression(e));
@ -956,8 +906,7 @@ pub enum DotIndexKind {
impl Parse for DotIndexKind {
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
stream.expect(Token::Dot)?;
if let Some(Token::Identifier(name)) = stream.peek() {
stream.next(); // Consume identifer
if let Some(Token::Identifier(name)) = stream.next() {
if let Ok(args) = stream.parse::<FunctionArgs>() {
Ok(Self::FunctionCall(FunctionCallExpression {
name,
@ -966,18 +915,10 @@ impl Parse for DotIndexKind {
is_macro: false,
}))
} else {
Ok(Self::StructValueIndex(name, stream.get_range_prev().unwrap()))
Ok(Self::StructValueIndex(name, stream.get_range().unwrap()))
}
} else {
if stream.next_is_whitespace() {
stream.expecting_err_nonfatal("struct index");
Ok(Self::StructValueIndex(
String::new(),
stream.get_range_prev_curr().unwrap(),
))
} else {
Err(stream.expecting_err("struct index")?)
}
return Err(stream.expected_err("struct index (number)")?);
}
}
}
@ -991,7 +932,7 @@ impl Parse for BlockLevelStatement {
Some(Token::ReturnKeyword) => {
stream.next();
let exp = stream.parse().ok();
stream.expect_nonfatal(Token::Semi).ok();
stream.expect(Token::Semi)?;
Stmt::Return(ReturnType::Hard, exp)
}
Some(Token::For) => {
@ -1056,7 +997,7 @@ impl Parse for SetStatement {
let var_ref = stream.parse()?;
stream.expect(Token::Equals)?;
let expr = stream.parse()?;
stream.expect_nonfatal(Token::Semi).ok();
stream.expect(Token::Semi)?;
Ok(SetStatement(var_ref, expr, stream.get_range().unwrap()))
}
}
@ -1094,13 +1035,10 @@ impl Parse for TopLevelStatement {
Ok(match stream.peek() {
Some(Token::ImportKeyword) => Stmt::Import(stream.parse()?),
Some(Token::Extern) => {
let documentation = stream.find_documentation();
stream.next(); // Consume Extern
stream.expect(Token::FnKeyword)?;
let mut signature: FunctionSignature = stream.parse()?;
signature.documentation = documentation;
let extern_fn = Stmt::ExternFunction(signature);
stream.expect_nonfatal(Token::Semi).ok();
let extern_fn = Stmt::ExternFunction(stream.parse()?);
stream.expect(Token::Semi)?;
extern_fn
}
Some(Token::FnKeyword) | Some(Token::PubKeyword) => Stmt::FunctionDefinition(stream.parse()?),
@ -1134,7 +1072,6 @@ impl Parse for BinopDefinition {
let Some(Token::Identifier(lhs_name)) = stream.next() else {
return Err(stream.expected_err("lhs name")?);
};
let lhs_range = stream.get_range_prev_curr().unwrap();
stream.expect(Token::Colon)?;
let lhs_type = stream.parse()?;
stream.expect(Token::ParenClose)?;
@ -1145,7 +1082,6 @@ impl Parse for BinopDefinition {
let Some(Token::Identifier(rhs_name)) = stream.next() else {
return Err(stream.expected_err("rhs name")?);
};
let rhs_range = stream.get_range_prev_curr().unwrap();
stream.expect(Token::Colon)?;
let rhs_type = stream.parse()?;
stream.expect(Token::ParenClose)?;
@ -1155,9 +1091,9 @@ impl Parse for BinopDefinition {
stream.expect(Token::Arrow)?;
Ok(BinopDefinition {
lhs: (lhs_name, lhs_type, lhs_range),
lhs: (lhs_name, lhs_type),
op: operator,
rhs: (rhs_name, rhs_type, rhs_range),
rhs: (rhs_name, rhs_type),
return_ty: stream.parse()?,
block: stream.parse()?,
signature_range,
@ -1178,11 +1114,11 @@ impl Parse for AssociatedFunctionBlock {
match stream.peek() {
Some(Token::FnKeyword) | Some(Token::PubKeyword) => {
let mut fun: FunctionDefinition = stream.parse()?;
match &mut fun.0.self_kind {
SelfKind::Owned(inner_ty) => inner_ty.0 = ty.0.clone(),
SelfKind::Borrow(inner_ty) => inner_ty.0 = ty.0.clone(),
SelfKind::MutBorrow(inner_ty) => inner_ty.0 = ty.0.clone(),
SelfKind::None => {}
fun.0.self_kind = match fun.0.self_kind {
SelfKind::Owned(_) => SelfKind::Owned(ty.clone()),
SelfKind::Borrow(_) => SelfKind::Borrow(ty.clone()),
SelfKind::MutBorrow(_) => SelfKind::MutBorrow(ty.clone()),
SelfKind::None => SelfKind::None,
};
functions.push(fun);
}

View File

@ -1,7 +1,7 @@
use std::path::PathBuf;
use crate::{
ast::{self, ReturnType},
ast::{self},
mir::{
self, CustomTypeKey, FunctionParam, ModuleMap, NamedVariableRef, ReturnKind, SourceModuleId, StmtKind,
StructField, StructType, WhileStatement,
@ -30,20 +30,12 @@ impl ast::Module {
for stmt in &self.top_level_statements {
match stmt {
Import(import) => {
imports.push(mir::Import(
import
.0
.iter()
.map(|(s, range)| (s.clone(), range.as_meta(module_id)))
.collect(),
import.1.as_meta(module_id),
));
imports.push(mir::Import(import.0.clone(), import.1.as_meta(module_id)));
}
FunctionDefinition(function_def) => functions.push(function_def.into_mir(module_id)),
ExternFunction(signature) => {
let def = mir::FunctionDefinition {
name: signature.name.clone(),
documentation: signature.documentation.clone(),
linkage_name: None,
is_pub: false,
is_imported: false,
@ -64,7 +56,6 @@ impl ast::Module {
.collect(),
kind: mir::FunctionDefinitionKind::Extern(false),
source: Some(module_id),
signature_meta: signature.range.as_meta(module_id),
};
functions.push(def);
}
@ -105,13 +96,13 @@ impl ast::Module {
lhs: mir::FunctionParam {
name: lhs.0.clone(),
ty: lhs.1 .0.into_mir(module_id),
meta: lhs.2.as_meta(module_id),
meta: lhs.1 .1.as_meta(module_id),
},
op: op.mir(),
rhs: mir::FunctionParam {
name: rhs.0.clone(),
ty: rhs.1 .0.into_mir(module_id),
meta: rhs.2.as_meta(module_id),
meta: rhs.1 .1.as_meta(module_id),
},
return_type: return_ty.0.into_mir(module_id),
fn_kind: mir::FunctionDefinitionKind::Local(
@ -177,7 +168,6 @@ impl ast::FunctionDefinition {
}));
mir::FunctionDefinition {
name: signature.name.clone(),
documentation: signature.documentation.clone(),
linkage_name: None,
is_pub: *is_pub,
is_imported: false,
@ -189,7 +179,6 @@ impl ast::FunctionDefinition {
parameters: params,
kind: mir::FunctionDefinitionKind::Local(block.into_mir(module_id), (range).as_meta(module_id)),
source: Some(module_id),
signature_meta: signature.range.as_meta(module_id),
}
}
}
@ -237,9 +226,8 @@ impl ast::Block {
);
let let_statement = mir::Statement(
StmtKind::Let(counter_var.clone(), true, start.process(module_id)),
start.1.as_meta(module_id),
counter_range.as_meta(module_id),
);
let statement_range = counter_range.clone() + start.1 + end.1 + block.2;
let set_new = mir::Statement(
StmtKind::Set(
@ -265,33 +253,8 @@ impl ast::Block {
),
counter_range.as_meta(module_id),
);
let mir_block = if let Some((ret_kind, ret_expr)) = &block.1 {
if *ret_kind == ReturnType::Soft {
if let Some(ret_expr) = ret_expr {
let mir_ret = ret_expr.process(module_id);
let mut clone = block.clone();
clone.1 = None;
let mut mir_block = clone.into_mir(module_id);
mir_block
.statements
.push(mir::Statement(StmtKind::Expression(mir_ret.clone()), mir_ret.1));
mir_block.statements.push(set_new);
mir_block
} else {
let mut mir_block = block.into_mir(module_id);
mir_block.statements.push(set_new);
mir_block
}
} else {
block.into_mir(module_id)
}
} else {
let mut mir_block = block.into_mir(module_id);
mir_block.statements.push(set_new);
mir_block
};
let mut block = block.into_mir(module_id);
block.statements.push(set_new);
let while_statement = mir::Statement(
StmtKind::While(WhileStatement {
condition: mir::Expression(
@ -304,23 +267,23 @@ impl ast::Block {
Box::new(end.process(module_id)),
mir::TypeKind::Vague(mir::VagueType::Unknown),
),
end.1.as_meta(module_id),
counter_range.as_meta(module_id),
),
block: mir_block.clone(),
meta: (*counter_range + end.1 + block.2).as_meta(module_id),
block,
meta: self.2.as_meta(module_id),
}),
(*counter_range + end.1 + block.2).as_meta(module_id),
self.2.as_meta(module_id),
);
let inner_scope = StmtKind::Expression(mir::Expression(
mir::ExprKind::Block(mir::Block {
statements: vec![let_statement, while_statement],
return_expression: None,
meta: statement_range.as_meta(module_id),
meta: counter_range.as_meta(module_id) + end.1.as_meta(module_id),
}),
statement_range.as_meta(module_id),
counter_range.as_meta(module_id) + end.1.as_meta(module_id),
));
(inner_scope, statement_range)
(inner_scope, self.2)
}
ast::BlockLevelStatement::WhileLoop(expression, block) => (
StmtKind::While(WhileStatement {
@ -328,7 +291,7 @@ impl ast::Block {
block: block.into_mir(module_id),
meta: self.2.as_meta(module_id),
}),
expression.1 + block.2,
self.2,
),
};
@ -408,7 +371,7 @@ impl ast::Expression {
Box::new(idx_expr.process(module_id)),
),
ast::ExpressionKind::StructExpression(struct_init) => mir::ExprKind::Struct(
CustomTypeKey(struct_init.name.clone(), module_id),
struct_init.name.clone(),
struct_init
.fields
.iter()
@ -583,7 +546,7 @@ impl ast::TypeKind {
}
ast::TypeKind::Ptr(type_kind) => mir::TypeKind::UserPtr(Box::new(type_kind.clone().into_mir(source_mod))),
ast::TypeKind::F16 => mir::TypeKind::F16,
ast::TypeKind::F32B => mir::TypeKind::F16B,
ast::TypeKind::F32B => mir::TypeKind::F32B,
ast::TypeKind::F32 => mir::TypeKind::F32,
ast::TypeKind::F64 => mir::TypeKind::F64,
ast::TypeKind::F80 => mir::TypeKind::F80,

View File

@ -1,8 +1,6 @@
//! Contains relevant code for parsing tokens received from
//! Lexing/Tokenizing-stage.
use std::{cell::RefCell, rc::Rc};
use crate::{
ast::parse::Parse,
lexer::{FullToken, Token},
@ -14,7 +12,6 @@ use crate::{
pub struct TokenStream<'a, 'b> {
ref_position: Option<&'b mut usize>,
tokens: &'a [FullToken],
errors: Rc<RefCell<Vec<Error>>>,
pub position: usize,
}
@ -23,7 +20,6 @@ impl<'a, 'b> TokenStream<'a, 'b> {
TokenStream {
ref_position: None,
tokens,
errors: Rc::new(RefCell::new(Vec::new())),
position: 0,
}
}
@ -42,16 +38,6 @@ impl<'a, 'b> TokenStream<'a, 'b> {
))
}
/// Returns expected-error for the next token in-line. Useful in conjunction
/// with [`TokenStream::peek`]
pub fn expected_err_nonfatal<T: Into<String>>(&mut self, expected: T) {
let err = match self.expected_err(expected) {
Ok(e) => e,
Err(e) => e,
};
self.errors.borrow_mut().push(err);
}
/// Returns expected-error for the previous token that was already consumed.
/// Useful in conjunction with [`TokenStream::next`]
pub fn expecting_err<T: Into<String>>(&mut self, expected: T) -> Result<Error, Error> {
@ -64,16 +50,6 @@ impl<'a, 'b> TokenStream<'a, 'b> {
))
}
/// Returns expected-error for the previous token that was already consumed.
/// Useful in conjunction with [`TokenStream::next`]
pub fn expecting_err_nonfatal<T: Into<String>>(&mut self, expected: T) {
let err = match self.expecting_err(expected) {
Ok(e) => e,
Err(e) => e,
};
self.errors.borrow_mut().push(err);
}
pub fn expect(&mut self, token: Token) -> Result<(), Error> {
if let (pos, Some(peeked)) = self.next_token(self.position) {
if token == peeked.token {
@ -87,42 +63,6 @@ impl<'a, 'b> TokenStream<'a, 'b> {
}
}
pub fn find_documentation(&mut self) -> Option<String> {
let mut from = self.position;
let mut documentation = None;
while let Some(token) = self.tokens.get(from) {
if matches!(token.token, Token::Whitespace(_) | Token::Comment(_) | Token::Doc(_)) {
from += 1;
if let Token::Doc(doctext) = &token.token {
documentation = Some(
match documentation {
Some(t) => t + " ",
None => String::new(),
} + doctext.trim(),
);
}
} else {
break;
}
}
documentation
}
pub fn expect_nonfatal(&mut self, token: Token) -> Result<(), ()> {
if let (pos, Some(peeked)) = self.next_token(self.position) {
if token == peeked.token {
self.position = pos + 1;
Ok(())
} else {
self.expecting_err_nonfatal(token);
Err(())
}
} else {
self.expecting_err_nonfatal(token);
Err(())
}
}
pub fn next(&mut self) -> Option<Token> {
let (position, token) = self.next_token(self.position);
self.position = position + 1;
@ -207,7 +147,6 @@ impl<'a, 'b> TokenStream<'a, 'b> {
let clone = TokenStream {
ref_position: Some(&mut ref_pos),
tokens: self.tokens,
errors: self.errors.clone(),
position,
};
@ -220,29 +159,6 @@ impl<'a, 'b> TokenStream<'a, 'b> {
}
}
pub fn parse_with<T, U>(&mut self, fun: T) -> Result<U, Error>
where
T: FnOnce(TokenStream) -> Result<U, Error>,
{
let mut ref_pos = self.position;
let position = self.position;
let clone = TokenStream {
ref_position: Some(&mut ref_pos),
tokens: self.tokens,
errors: self.errors.clone(),
position,
};
match fun(clone) {
Ok(res) => {
self.position = ref_pos.max(self.position);
Ok(res)
}
Err(e) => Err(e),
}
}
pub fn get_range(&self) -> Option<TokenRange> {
self.ref_position.as_ref().map(|ref_pos| TokenRange {
start: **ref_pos,
@ -259,18 +175,10 @@ impl<'a, 'b> TokenStream<'a, 'b> {
})
}
/// Gets range of the previous token only.
pub fn get_range_prev_curr(&self) -> Option<TokenRange> {
Some(TokenRange {
start: self.previous_token(self.position).0,
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(_) | Token::Doc(_)) {
if matches!(token.token, Token::Whitespace(_) | Token::Comment(_)) {
from -= 1;
} else {
break;
@ -281,7 +189,7 @@ impl<'a, 'b> TokenStream<'a, 'b> {
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(_) | Token::Doc(_)) {
if matches!(token.token, Token::Whitespace(_) | Token::Comment(_)) {
from += 1;
} else {
break;
@ -289,22 +197,6 @@ impl<'a, 'b> TokenStream<'a, 'b> {
}
(from, self.tokens.get(from))
}
pub fn errors(&self) -> Vec<Error> {
self.errors.borrow().clone().clone()
}
pub fn next_is_whitespace(&self) -> bool {
if let Some(token) = self.tokens.get(self.position) {
if let Token::Whitespace(_) = token.token {
true
} else {
false
}
} else {
true
}
}
}
impl Drop for TokenStream<'_, '_> {

View File

@ -5,7 +5,7 @@ use reid_lib::{
Block, Instr,
};
use mir::{CustomTypeKey, FunctionDefinitionKind, IfExpression, TypeKind, WhileStatement};
use mir::{CustomTypeKey, FunctionCall, FunctionDefinitionKind, IfExpression, TypeKind, WhileStatement};
use crate::mir::{self, FunctionParam, Metadata, SourceModuleId};
@ -151,11 +151,11 @@ impl mir::Expression {
allocated.extend(expression.allocate(scope));
}
}
mir::ExprKind::Struct(key, items) => {
mir::ExprKind::Struct(name, items) => {
let (_, ty) = self.return_type(&Default::default(), scope.mod_id).unwrap();
let allocation = scope
.block
.build_named(key.0.clone(), Instr::Alloca(ty.get_type(scope.type_values)))
.build_named(name, Instr::Alloca(ty.get_type(scope.type_values)))
.unwrap();
allocated.push(Allocation(self.1, ty, allocation));

View File

@ -3,8 +3,8 @@ use reid_lib::{builder::InstructionValue, CmpPredicate, ConstValueKind, Instr, T
use crate::{
codegen::{ErrorKind, StackValueKind},
mir::{
implement::TypeCategory, BinaryOperator, BinopDefinition, CmpOperator, FunctionDefinition,
FunctionDefinitionKind, FunctionParam, TypeKind,
BinaryOperator, BinopDefinition, CmpOperator, FunctionDefinition, FunctionDefinitionKind, FunctionParam,
TypeKind,
},
};
@ -26,58 +26,21 @@ const INTEGERS: [TypeKind; 10] = [
const FLOATS: [TypeKind; 7] = [
TypeKind::F16,
TypeKind::F32,
TypeKind::F16B,
TypeKind::F32B,
TypeKind::F64,
TypeKind::F80,
TypeKind::F128,
TypeKind::F128PPC,
];
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub enum LLVMIntrinsicKind {
Max(TypeKind),
Min(TypeKind),
Abs(TypeKind),
Memcpy(TypeKind),
Sqrt(TypeKind),
PowI(TypeKind, TypeKind),
Pow(TypeKind),
Sin(TypeKind),
Cos(TypeKind),
Tan(TypeKind),
ASin(TypeKind),
ACos(TypeKind),
ATan(TypeKind),
ATan2(TypeKind),
SinH(TypeKind),
CosH(TypeKind),
TanH(TypeKind),
Log(TypeKind),
Log2(TypeKind),
Log10(TypeKind),
Copysign(TypeKind),
Floor(TypeKind),
Ceil(TypeKind),
Trunc(TypeKind),
RoundEven(TypeKind),
Round(TypeKind),
}
const INTRINSIC_IDENT: &str = "reid.intrinsic";
const MALLOC_IDENT: &str = "malloc";
macro_rules! doc {
($str:expr) => {
Some($str.to_string())
};
}
pub fn form_intrinsics() -> Vec<FunctionDefinition> {
let mut intrinsics = Vec::new();
intrinsics.push(FunctionDefinition {
name: MALLOC_IDENT.to_owned(),
documentation: doc!("Allocates `size` bytes and returns a `u8`-pointer."),
linkage_name: Some("malloc".to_owned()),
is_pub: false,
is_imported: true,
@ -89,368 +52,71 @@ pub fn form_intrinsics() -> Vec<FunctionDefinition> {
}],
kind: FunctionDefinitionKind::Extern(false),
source: None,
signature_meta: Default::default(),
});
intrinsics
}
pub fn simple_intrinsic<T: Into<String> + Clone>(
name: T,
doc: T,
params: Vec<T>,
ret: TypeKind,
intrisic: LLVMIntrinsicKind,
) -> FunctionDefinition {
FunctionDefinition {
name: name.into(),
documentation: Some(doc.into()),
linkage_name: None,
is_pub: true,
is_imported: false,
return_type: ret.clone(),
parameters: params
.iter()
.map(|p| FunctionParam::from(p.clone(), ret.clone()))
.collect(),
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicLLVM(intrisic, ret.clone()))),
source: None,
signature_meta: Default::default(),
}
}
pub fn get_intrinsic_assoc_functions(ty: &TypeKind) -> Vec<FunctionDefinition> {
let mut intrinsics = Vec::new();
if let TypeKind::Array(_, len) = ty {
intrinsics.push(FunctionDefinition {
name: "length".to_owned(),
documentation: doc!("Returns the length of this given array"),
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,
signature_meta: Default::default(),
});
}
if ty.category() == TypeCategory::Real {
intrinsics.push(simple_intrinsic(
"sqrt",
"Calculates the square-root of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Sqrt(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"sin",
"Calculates sine of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Sin(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"cos",
"Calculates cosine of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Cos(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"tan",
"Calculates tangent of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Tan(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"sinh",
"Calculates hyperbolic sine of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::SinH(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"cosh",
"Calculates hyperbolic cosine of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::CosH(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"tanh",
"Calculates hyperbolic tangent of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::TanH(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"asin",
"Calculates arcsine of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::ASin(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"acos",
"Calculates arccosine of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::ACos(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"atan",
"Calculates arctangent of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::ATan(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"atan2",
"Calculates 2-argument arctangent of `value`",
vec!["self", "other"],
ty.clone(),
LLVMIntrinsicKind::ATan2(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"log",
"Returns logₑ of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Log(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"log2",
"Returns log₂ of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Log2(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"log10",
"Returns log₁₀ of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Log10(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"floor",
"Rounds `value` towards negative infinity.",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Floor(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"ceil",
"Rounds `value` towards positive infinity.",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Ceil(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"trunc",
"Truncates `value` to the integer nearest to `0`.",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Trunc(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"round",
"Rounds `value` to the closest even integer.",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Round(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"even",
"Rounds `value` to the closest even integer.",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::RoundEven(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"pow",
"Returns `value` raised to the exponent of `exponent`.",
vec!["self", "exponent"],
ty.clone(),
LLVMIntrinsicKind::Pow(ty.clone()),
));
intrinsics.push(FunctionDefinition {
name: "powi".to_owned(),
documentation: doc!("Returns `value` raised to the exponent of `exponent`."),
linkage_name: None,
is_pub: true,
is_imported: false,
return_type: ty.clone(),
parameters: vec![
FunctionParam {
name: String::from("self"),
ty: ty.clone(),
meta: Default::default(),
},
FunctionParam {
name: String::from("exponent"),
ty: TypeKind::U32,
meta: Default::default(),
},
],
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicLLVM(
LLVMIntrinsicKind::PowI(ty.clone(), TypeKind::U32),
ty.clone(),
))),
source: None,
signature_meta: Default::default(),
});
}
match ty.category() {
TypeCategory::Integer | TypeCategory::Real | TypeCategory::Bool => {
intrinsics.push(simple_intrinsic(
"max",
"Returns the larger of `a` and `b`.",
vec!["self", "other"],
ty.clone(),
LLVMIntrinsicKind::Max(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"min",
"Returns the smaller of `a` and `b`.",
vec!["self", "other"],
ty.clone(),
LLVMIntrinsicKind::Min(ty.clone()),
));
if ty.signed() {
intrinsics.push(FunctionDefinition {
name: "abs".to_owned(),
documentation: doc!("Returns the absolute value of `value`."),
linkage_name: None,
is_pub: true,
is_imported: false,
return_type: ty.clone(),
parameters: vec![FunctionParam {
name: String::from("self"),
ty: ty.clone(),
meta: Default::default(),
}],
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSimpleUnaryInstr({
let ty = ty.clone();
|scope, param| {
let intrinsic = scope.get_intrinsic(LLVMIntrinsicKind::Abs(ty));
let constant = scope.block.build(Instr::Constant(ConstValueKind::Bool(false))).unwrap();
let value = scope
.block
.build(Instr::FunctionCall(intrinsic, vec![param, constant]))
.unwrap();
value
}
}))),
source: None,
signature_meta: Default::default(),
});
}
}
_ => {}
}
intrinsics.push(FunctionDefinition {
name: "sizeof".to_owned(),
documentation: doc!("Simply returns the size of type `T` in bytes."),
linkage_name: None,
is_pub: true,
is_imported: false,
return_type: TypeKind::U64,
parameters: Vec::new(),
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSizeOf(ty.clone()))),
source: None,
signature_meta: Default::default(),
});
intrinsics.push(FunctionDefinition {
name: "malloc".to_owned(),
documentation: doc!("Allocates `T::sizeof() * size` bytes and returns a pointer to `T`."),
linkage_name: None,
is_pub: true,
is_imported: false,
return_type: TypeKind::UserPtr(Box::new(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,
signature_meta: Default::default(),
});
intrinsics.push(FunctionDefinition {
name: "memcpy".to_owned(),
documentation: doc!(
"Copies `T::sizeof() * size` bytes from pointer `source` to pointer
`destination`."
),
linkage_name: None,
is_pub: true,
is_imported: false,
return_type: TypeKind::Void,
parameters: vec![
FunctionParam {
name: String::from("destination"),
ty: TypeKind::UserPtr(Box::new(ty.clone())),
meta: Default::default(),
},
FunctionParam {
name: String::from("source"),
ty: TypeKind::UserPtr(Box::new(ty.clone())),
meta: Default::default(),
},
FunctionParam {
name: String::from("length"),
ty: TypeKind::U64,
meta: Default::default(),
},
],
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicMemcpy(ty.clone()))),
source: None,
signature_meta: Default::default(),
});
intrinsics.push(FunctionDefinition {
name: "null".to_owned(),
documentation: doc!("Returns a null-pointer of type `T`."),
linkage_name: None,
is_pub: true,
is_imported: false,
return_type: TypeKind::UserPtr(Box::new(ty.clone())),
parameters: Vec::new(),
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicNullPtr(ty.clone()))),
source: None,
signature_meta: Default::default(),
});
intrinsics.push(FunctionDefinition {
name: "is_null".to_owned(),
documentation: doc!("Returns a boolean representing if `val` is a nullptr or not."),
linkage_name: None,
is_pub: true,
is_imported: false,
return_type: TypeKind::Bool,
parameters: vec![FunctionParam {
name: "value".to_string(),
ty: TypeKind::UserPtr(Box::new(ty.clone())),
meta: Default::default(),
}],
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicIsNull)),
source: None,
signature_meta: Default::default(),
});
intrinsics
}
pub fn get_intrinsic_assoc_func(ty: &TypeKind, name: &str) -> Option<FunctionDefinition> {
get_intrinsic_assoc_functions(ty).into_iter().find(|f| f.name == name)
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(),
linkage_name: None,
is_pub: true,
is_imported: false,
return_type: TypeKind::U64,
parameters: Vec::new(),
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSizeOf(ty.clone()))),
source: None,
}),
"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![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(),
linkage_name: None,
is_pub: true,
is_imported: false,
return_type: TypeKind::UserPtr(Box::new(ty.clone())),
parameters: Vec::new(),
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicNullPtr(ty.clone()))),
source: None,
}),
_ => None,
}
}
fn simple_binop_def<T: Clone + 'static>(op: BinaryOperator, ty: &TypeKind, fun: T) -> BinopDefinition
@ -470,7 +136,7 @@ where
meta: Default::default(),
},
return_type: ty.clone(),
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSimpleBinaryInstr(fun))),
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSimpleInstr(fun))),
meta: Default::default(),
exported: false,
}
@ -493,7 +159,7 @@ where
meta: Default::default(),
},
return_type: lhs.clone(),
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSimpleBinaryInstr(fun))),
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSimpleInstr(fun))),
meta: Default::default(),
exported: false,
}
@ -614,22 +280,17 @@ pub fn form_intrinsic_binops() -> Vec<BinopDefinition> {
intrinsics.push(simple_binop_def(Div, &ty, |scope, lhs, rhs| {
scope.block.build(Instr::FDiv(lhs, rhs)).unwrap()
}));
intrinsics.push(simple_binop_def(Mod, &ty, {
let ty = ty.clone();
|scope, lhs, rhs| {
let div = scope.block.build(Instr::FDiv(lhs, rhs)).unwrap();
let fun = scope.get_intrinsic(LLVMIntrinsicKind::Trunc(ty));
let div_truncated = scope.block.build(Instr::FunctionCall(fun, vec![div])).unwrap();
let mul = scope.block.build(Instr::FMul(rhs, div_truncated)).unwrap();
scope.block.build(Instr::FSub(lhs, mul)).unwrap()
}
}));
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::NE), &ty, |scope, lhs, rhs| {
scope.block.build(Instr::FCmp(CmpPredicate::NE, lhs, rhs)).unwrap()
intrinsics.push(simple_binop_def(Mod, &ty, |scope, lhs, rhs| {
let div = scope.block.build(Instr::FDiv(lhs, rhs)).unwrap();
let mul = scope.block.build(Instr::Mul(rhs, div)).unwrap();
scope.block.build(Instr::Sub(lhs, mul)).unwrap()
}));
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::EQ), &ty, |scope, lhs, rhs| {
scope.block.build(Instr::FCmp(CmpPredicate::EQ, lhs, rhs)).unwrap()
}));
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::NE), &ty, |scope, lhs, rhs| {
scope.block.build(Instr::FCmp(CmpPredicate::NE, lhs, rhs)).unwrap()
}));
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::GT), &ty, |scope, lhs, rhs| {
scope.block.build(Instr::FCmp(CmpPredicate::GT, lhs, rhs)).unwrap()
}));
@ -675,37 +336,12 @@ macro_rules! intrinsic_debug {
}
#[derive(Clone)]
pub struct IntrinsicSimpleUnaryInstr<T>(T)
where
T: FnOnce(&mut Scope, InstructionValue) -> InstructionValue;
impl<T> std::fmt::Debug for IntrinsicSimpleUnaryInstr<T>
where
T: FnOnce(&mut Scope, InstructionValue) -> InstructionValue,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("IntrinsicSimpleUnaryInstr").finish()
}
}
impl<T: Clone> IntrinsicFunction for IntrinsicSimpleUnaryInstr<T>
where
T: FnOnce(&mut Scope, InstructionValue) -> InstructionValue,
{
fn codegen<'b, 'c>(&self, scope: &mut Scope<'b, 'c>, params: &[StackValue]) -> Result<StackValue, ErrorKind> {
let param = params.get(0).unwrap();
let instr = self.clone().0(scope, param.instr());
Ok(StackValue(StackValueKind::Literal(instr), param.1.clone()))
}
}
#[derive(Clone)]
pub struct IntrinsicSimpleBinaryInstr<T>(T)
pub struct IntrinsicSimpleInstr<T>(T)
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue;
intrinsic_debug!(IntrinsicSimpleBinaryInstr<T>, "IntrinsicSimpleBinaryInstr");
intrinsic_debug!(IntrinsicSimpleInstr<T>, "IntrinsicSimpleInstr");
impl<T: Clone> IntrinsicFunction for IntrinsicSimpleBinaryInstr<T>
impl<T: Clone> IntrinsicFunction for IntrinsicSimpleInstr<T>
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
{
@ -749,34 +385,6 @@ impl IntrinsicFunction for IntrinsicSizeOf {
}
}
#[derive(Clone, Debug)]
pub struct IntrinsicMemcpy(TypeKind);
impl IntrinsicFunction for IntrinsicMemcpy {
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, params: &[StackValue]) -> Result<StackValue, ErrorKind> {
let dest = params.get(0).unwrap();
let src = params.get(1).unwrap();
let length = params.get(2).unwrap();
let intrinsic = scope.get_intrinsic(LLVMIntrinsicKind::Memcpy(TypeKind::UserPtr(Box::new(self.0.clone()))));
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, length.instr())).unwrap();
let params = vec![
dest.instr(),
src.instr(),
bytes,
scope.block.build(Instr::Constant(ConstValueKind::Bool(false))).unwrap(),
];
let value = scope.block.build(Instr::FunctionCall(intrinsic, params)).unwrap();
Ok(StackValue(StackValueKind::Literal(value), TypeKind::Void))
}
}
#[derive(Clone, Debug)]
pub struct IntrinsicMalloc(TypeKind);
impl IntrinsicFunction for IntrinsicMalloc {
@ -817,17 +425,6 @@ impl IntrinsicFunction for IntrinsicNullPtr {
))
}
}
#[derive(Clone, Debug)]
pub struct IntrinsicIsNull;
impl IntrinsicFunction for IntrinsicIsNull {
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, params: &[StackValue]) -> Result<StackValue, ErrorKind> {
let val = params.get(0).unwrap().instr();
let instr = scope.block.build(Instr::IsNull(val)).unwrap();
Ok(StackValue(StackValueKind::Literal(instr), TypeKind::Bool))
}
}
#[derive(Clone, Debug)]
pub struct IntrinsicConst(u64);
impl IntrinsicFunction for IntrinsicConst {
@ -837,23 +434,6 @@ impl IntrinsicFunction for IntrinsicConst {
}
}
#[derive(Clone, Debug)]
pub struct IntrinsicLLVM(LLVMIntrinsicKind, TypeKind);
impl IntrinsicFunction for IntrinsicLLVM {
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, params: &[StackValue]) -> Result<StackValue, ErrorKind> {
let intrinsic = scope.get_intrinsic(self.0.clone());
let value = scope
.block
.build(Instr::FunctionCall(
intrinsic,
params.iter().map(|p| p.instr()).collect(),
))
.unwrap();
Ok(StackValue(StackValueKind::Literal(value), self.1.clone()))
}
}
// impl IntrinsicFunction for IntrinsicIAdd {
// fn codegen<'ctx, 'a>(
// &self,

View File

@ -1,8 +1,4 @@
use std::{
cell::RefCell,
collections::{HashMap, HashSet},
rc::Rc,
};
use std::{cell::RefCell, collections::HashMap, rc::Rc};
use allocator::{Allocator, AllocatorScope};
use intrinsics::*;
@ -62,12 +58,10 @@ impl mir::Context {
let mut modules = HashMap::new();
let mut modules_sorted = self.modules.iter().map(|(_, m)| m).collect::<Vec<_>>();
modules_sorted.sort_by(|m1, m2| m2.module_id.cmp(&m1.module_id));
for module in &modules_sorted {
modules.insert(module.module_id, *module);
}
for module in &modules_sorted {
module.codegen(context, modules.clone())?;
let codegen = module.codegen(context, modules.clone())?;
modules.insert(module.module_id, codegen);
}
Ok(CodegenContext { context })
}
@ -75,6 +69,7 @@ impl mir::Context {
#[derive(Clone)]
struct ModuleCodegen<'ctx> {
name: String,
module: Module<'ctx>,
}
@ -125,22 +120,11 @@ impl mir::GlobalKind {
}
}
fn get_typekey(ty: &TypeKind) -> Option<CustomTypeKey> {
match ty {
TypeKind::Array(type_kind, _) => get_typekey(type_kind.as_ref()),
TypeKind::CustomType(custom_type_key) => Some(custom_type_key.clone()),
TypeKind::Borrow(type_kind, _) => get_typekey(type_kind.as_ref()),
TypeKind::UserPtr(type_kind) => get_typekey(type_kind.as_ref()),
TypeKind::CodegenPtr(type_kind) => get_typekey(type_kind.as_ref()),
_ => None,
}
}
impl mir::Module {
fn codegen<'ctx>(
&'ctx self,
context: &'ctx Context,
modules: HashMap<SourceModuleId, &mir::Module>,
modules: HashMap<SourceModuleId, ModuleCodegen<'ctx>>,
) -> Result<ModuleCodegen<'ctx>, ErrorKind> {
let mut module = context.module(&self.name, self.is_main);
let tokens = &self.tokens;
@ -193,40 +177,10 @@ impl mir::Module {
insert_debug!(&TypeKind::Void);
insert_debug!(&TypeKind::Char);
// Since we know by this point that no types are recursive, we can
// somewhat easily sort the type-definitions such that we can process
// the ones with no depencencies first, and later the ones that depend
// on the earlier ones.
let mut typekeys_seen: HashSet<CustomTypeKey> = HashSet::new();
let mut typedefs_sorted: Vec<TypeDefinition> = Vec::new();
let mut typedefs_left = self.typedefs.clone();
while let Some(typedef) = typedefs_left.pop() {
let is_ok = match &typedef.kind {
TypeDefinitionKind::Struct(StructType(fields)) => {
let mut field_iter = fields.iter();
loop {
if let Some(field) = field_iter.next() {
if let Some(key) = get_typekey(&field.1) {
if !typekeys_seen.contains(&key) {
break false;
}
}
} else {
break true;
}
}
}
};
let mut typedefs = self.typedefs.clone();
typedefs.sort_by(|a, b| b.source_module.cmp(&a.source_module));
if is_ok {
typekeys_seen.insert(CustomTypeKey(typedef.name.clone(), typedef.source_module));
typedefs_sorted.push(typedef);
} else {
typedefs_left.insert(0, typedef.clone());
}
}
for typedef in typedefs_sorted {
for typedef in typedefs {
let type_key = CustomTypeKey(typedef.name.clone(), typedef.source_module);
type_map.insert(type_key.clone(), typedef.clone());
@ -236,6 +190,9 @@ impl mir::Module {
typedef.name.clone(),
fields
.iter()
// TODO: Reorder custom-type definitions such that
// inner types get evaluated first. Otherwise this
// will cause a panic!
.map(|StructField(_, t, _)| t.get_type(&type_values))
.collect(),
)))
@ -330,11 +287,11 @@ impl mir::Module {
.collect();
let is_main = self.is_main && function.name == "main";
let module_prefix = if let Some(module_id) = function.source {
if module_id == self.module_id {
let module_prefix = if let Some(module) = function.source {
if module == self.module_id {
format!("reid.{}.", self.name)
} else {
format!("reid.{}.", modules.get(&module_id).unwrap().name)
format!("reid.{}.", modules.get(&module).unwrap().name)
}
} else {
format!("reid.intrinsic.")
@ -436,7 +393,6 @@ impl mir::Module {
}),
binops: &binops,
allocator: Rc::new(RefCell::new(allocator)),
llvm_intrinsics: Rc::new(RefCell::new(HashMap::new())),
};
binop
@ -515,7 +471,6 @@ impl mir::Module {
globals: &globals,
binops: &binops,
allocator: Rc::new(RefCell::new(allocator)),
llvm_intrinsics: Rc::new(RefCell::new(HashMap::new())),
};
mir_function
@ -578,7 +533,6 @@ impl mir::Module {
globals: &globals,
binops: &binops,
allocator: Rc::new(RefCell::new(allocator)),
llvm_intrinsics: Rc::new(RefCell::new(HashMap::new())),
};
mir_function
@ -602,7 +556,10 @@ impl mir::Module {
}
}
Ok(ModuleCodegen { module })
Ok(ModuleCodegen {
name: self.name.clone(),
module,
})
}
}
@ -1248,9 +1205,10 @@ impl mir::Expression {
))
}
}
mir::ExprKind::Struct(key, items) => {
mir::ExprKind::Struct(name, items) => {
let type_key = CustomTypeKey(name.clone(), scope.module_id);
let ty = Type::CustomType({
let Some(a) = scope.type_values.get(&key) else {
let Some(a) = scope.type_values.get(&type_key) else {
return Ok(None);
};
*a
@ -1259,20 +1217,20 @@ impl mir::Expression {
let TypeDefinition {
kind: TypeDefinitionKind::Struct(struct_ty),
..
} = scope.types.get(scope.type_values.get(&key).unwrap()).unwrap();
} = scope.types.get(scope.type_values.get(&type_key).unwrap()).unwrap();
let indices = struct_ty.0.iter().enumerate();
let load_n = format!("{}@{}.load", key.0, key.1);
let load_n = format!("{}.load", name);
let struct_ptr = scope
.allocate(&self.1, &TypeKind::CustomType(key.clone()))
.allocate(&self.1, &TypeKind::CustomType(type_key.clone()))
.unwrap()
.maybe_location(&mut scope.block, location.clone());
for (field_n, exp, _) in items {
let gep_n = format!("{}@{}.{}.gep", key.0, key.1, field_n);
let store_n = format!("{}@{}.{}.store", key.0, key.1, field_n);
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;
let elem_ptr = scope
@ -1293,7 +1251,7 @@ impl mir::Expression {
Some(StackValue(
StackValueKind::Literal(struct_val),
TypeKind::CustomType(key.clone()),
TypeKind::CustomType(type_key),
))
}
mir::ExprKind::Borrow(expr, mutable) => {
@ -1368,6 +1326,8 @@ impl mir::Expression {
(val.1.clone(), type_kind.clone())
};
dbg!(&ty, type_kind);
match (&ty, type_kind) {
(TypeKind::UserPtr(_), TypeKind::UserPtr(_)) => Some(StackValue(
val.0.derive(

View File

@ -1,27 +1,24 @@
use std::{cell::RefCell, collections::HashMap, mem, rc::Rc};
use reid_lib::{
builder::{FunctionValue, GlobalValue, InstructionValue, TypeValue},
builder::{GlobalValue, InstructionValue, TypeValue},
debug_information::{DebugInformation, DebugLocation, DebugScopeValue, DebugTypeValue},
intrinsics::LLVMIntrinsic,
Block, Context, Function, Instr, Module,
};
use crate::{
codegen::intrinsics::LLVMIntrinsicKind,
lexer::FullToken,
mir::{
self,
pass::{AssociatedFunctionKey, BinopKey},
CustomTypeKey, FunctionParam, Metadata, SourceModuleId, TypeDefinition, TypeKind,
},
};
use super::{allocator::Allocator, ErrorKind, IntrinsicFunction};
use super::{allocator::Allocator, ErrorKind, IntrinsicFunction, ModuleCodegen};
pub struct Scope<'ctx, 'scope> {
pub(super) context: &'ctx Context,
pub(super) modules: &'scope HashMap<SourceModuleId, &'ctx mir::Module>,
pub(super) modules: &'scope HashMap<SourceModuleId, ModuleCodegen<'ctx>>,
pub(super) tokens: &'ctx Vec<FullToken>,
pub(super) module: &'ctx Module<'ctx>,
pub(super) module_id: SourceModuleId,
@ -37,7 +34,6 @@ pub struct Scope<'ctx, 'scope> {
pub(super) globals: &'scope HashMap<String, GlobalValue>,
pub(super) debug: Option<Debug<'ctx>>,
pub(super) allocator: Rc<RefCell<Allocator>>,
pub(super) llvm_intrinsics: Rc<RefCell<HashMap<LLVMIntrinsicKind, FunctionValue>>>,
}
impl<'ctx, 'a> Scope<'ctx, 'a> {
@ -60,7 +56,6 @@ impl<'ctx, 'a> Scope<'ctx, 'a> {
allocator: self.allocator.clone(),
globals: self.globals,
binops: self.binops,
llvm_intrinsics: self.llvm_intrinsics.clone(),
}
}
@ -79,49 +74,6 @@ impl<'ctx, 'a> Scope<'ctx, 'a> {
pub fn allocate(&self, meta: &Metadata, ty: &TypeKind) -> Option<InstructionValue> {
self.allocator.borrow_mut().allocate(meta, ty)
}
pub fn get_intrinsic(&self, kind: LLVMIntrinsicKind) -> FunctionValue {
let mut intrinsics = self.llvm_intrinsics.borrow_mut();
if let Some(fun) = intrinsics.get(&kind) {
*fun
} else {
let intrinsic = self
.module
.intrinsic(match &kind {
LLVMIntrinsicKind::Max(ty) => LLVMIntrinsic::Max(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Min(ty) => LLVMIntrinsic::Min(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Abs(ty) => LLVMIntrinsic::Abs(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Memcpy(ty) => LLVMIntrinsic::Memcpy(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Sqrt(ty) => LLVMIntrinsic::Sqrt(ty.get_type(self.type_values)),
LLVMIntrinsicKind::PowI(lhs, rhs) => {
LLVMIntrinsic::PowI(lhs.get_type(self.type_values), rhs.get_type(self.type_values))
}
LLVMIntrinsicKind::Pow(ty) => LLVMIntrinsic::Pow(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Sin(ty) => LLVMIntrinsic::Sin(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Cos(ty) => LLVMIntrinsic::Cos(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Tan(ty) => LLVMIntrinsic::Tan(ty.get_type(self.type_values)),
LLVMIntrinsicKind::ASin(ty) => LLVMIntrinsic::ASin(ty.get_type(self.type_values)),
LLVMIntrinsicKind::ACos(ty) => LLVMIntrinsic::ACos(ty.get_type(self.type_values)),
LLVMIntrinsicKind::ATan(ty) => LLVMIntrinsic::ATan(ty.get_type(self.type_values)),
LLVMIntrinsicKind::ATan2(ty) => LLVMIntrinsic::ATan2(ty.get_type(self.type_values)),
LLVMIntrinsicKind::SinH(ty) => LLVMIntrinsic::SinH(ty.get_type(self.type_values)),
LLVMIntrinsicKind::CosH(ty) => LLVMIntrinsic::CosH(ty.get_type(self.type_values)),
LLVMIntrinsicKind::TanH(ty) => LLVMIntrinsic::TanH(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Log(ty) => LLVMIntrinsic::Log(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Log2(ty) => LLVMIntrinsic::Log2(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Log10(ty) => LLVMIntrinsic::Log10(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Copysign(ty) => LLVMIntrinsic::Copysign(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Floor(ty) => LLVMIntrinsic::Floor(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Ceil(ty) => LLVMIntrinsic::Ceil(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Trunc(ty) => LLVMIntrinsic::Trunc(ty.get_type(self.type_values)),
LLVMIntrinsicKind::RoundEven(ty) => LLVMIntrinsic::RoundEven(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Round(ty) => LLVMIntrinsic::Round(ty.get_type(self.type_values)),
})
.unwrap();
intrinsics.insert(kind, intrinsic.clone());
intrinsic
}
}
}
#[derive(Debug, Clone)]

View File

@ -14,7 +14,10 @@ use crate::{
mir::{self, CustomTypeKey, Metadata, SourceModuleId, TypeDefinition, TypeDefinitionKind, TypeKind, VagueLiteral},
};
use super::scope::{Debug, Scope};
use super::{
scope::{Debug, Scope},
ModuleCodegen,
};
impl mir::CmpOperator {
pub(super) fn predicate(&self) -> CmpPredicate {
@ -79,7 +82,7 @@ impl TypeKind {
TypeKind::U128 => Type::U128,
TypeKind::Bool => Type::Bool,
TypeKind::F16 => Type::F16,
TypeKind::F16B => Type::F32B,
TypeKind::F32B => Type::F32B,
TypeKind::F32 => Type::F32,
TypeKind::F64 => Type::F64,
TypeKind::F128 => Type::F128,
@ -121,7 +124,7 @@ impl TypeKind {
type_map: &HashMap<CustomTypeKey, TypeDefinition>,
local_mod: SourceModuleId,
tokens: &Vec<FullToken>,
modules: &HashMap<SourceModuleId, &mir::Module>,
modules: &HashMap<SourceModuleId, ModuleCodegen>,
) -> DebugTypeValue {
if let Some(ty) = debug_types.get(self) {
return *ty;
@ -223,7 +226,7 @@ impl TypeKind {
TypeKind::U16 | TypeKind::U32 | TypeKind::U64 | TypeKind::U128 => DwarfEncoding::Unsigned,
TypeKind::F16
| TypeKind::F32
| TypeKind::F16B
| TypeKind::F32B
| TypeKind::F64
| TypeKind::F80
| TypeKind::F128

View File

@ -1,7 +1,6 @@
use std::{
collections::HashMap,
fmt::{Debug, Write},
path::PathBuf,
};
use crate::{
@ -96,46 +95,21 @@ pub struct ErrorModule {
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ErrorModules {
pub(super) module_map: HashMap<mir::SourceModuleId, ErrorModule>,
pub(super) source_id_map: HashMap<PathBuf, mir::SourceModuleId>,
module_counter: mir::SourceModuleId,
}
impl ErrorModules {
pub fn add_module<T: Into<String>>(
&mut self,
name: T,
path: Option<PathBuf>,
external_module_id: Option<SourceModuleId>,
) -> Option<mir::SourceModuleId> {
let module_id = path.as_ref().and_then(|p| self.source_id_map.get(p));
if let Some(module_id) = module_id {
Some(*module_id)
} else {
let id = if let Some(module_id) = external_module_id {
self.module_counter = SourceModuleId(module_id.0.max(self.module_counter.0));
if let Some(_) = self.module_map.get(&module_id) {
panic!("Can not use external module id: Module already exists!")
}
module_id
} else {
self.module_counter.increment()
};
if let Some(path) = path {
self.source_id_map.insert(path, id);
}
self.module_map.insert(
id,
ErrorModule {
name: name.into(),
tokens: None,
source: None,
},
);
Some(id)
}
pub fn add_module<T: Into<String>>(&mut self, name: T) -> Option<mir::SourceModuleId> {
let id = self.module_counter.increment();
self.module_map.insert(
id,
ErrorModule {
name: name.into(),
tokens: None,
source: None,
},
);
Some(id)
}
pub fn set_tokens(&mut self, id: mir::SourceModuleId, tokens: Vec<FullToken>) {
@ -207,10 +181,6 @@ impl ReidError {
pub fn from_kind(errors: Vec<ErrorKind>, map: ErrorModules) -> ReidError {
ReidError { map, errors }
}
pub fn extend(&mut self, other: ReidError) {
self.errors.extend(other.errors);
}
}
impl std::error::Error for ReidError {}

View File

@ -25,58 +25,36 @@ impl LDRunner {
let dyn_linker_path = find_objectfile(&self.dynamic_linker);
let crt1_path = find_objectfile("crt1.o");
let crti_path = find_objectfile("crti.o");
let crtn_path = find_objectfile("crtn.o");
log::debug!("LDRunner: Using dynamic linker at: {:?}", dyn_linker_path);
#[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)
.arg(crti_path);
ld.arg("-dynamic-linker").arg(dyn_linker_path).arg(crt1_path);
for library in &self.libraries {
ld.arg(format!("-l{}", library));
}
ld.arg(crtn_path);
ld.arg(input_path.to_str().unwrap())
.arg("-o")
.arg(out_path.to_str().unwrap());
log::debug!("{:#?}", ld);
log::debug!(
#[cfg(feature = "log_output")]
println!(
"LDRunner: Executing linker to objfile at {:?} => {:?}",
input_path,
out_path
input_path, out_path
);
#[cfg(feature = "log_output")]
dbg!(&ld);
let ld_output = ld.output().expect("Unable to execute ld!");
if !ld_output.status.success() {
let code = ld_output.status.code().unwrap_or(255);
log::error!("LD exited with code {code}");
println!("{}", unsafe { String::from_utf8_unchecked(ld_output.stderr) });
return;
}
ld.spawn().expect("Unable to execute ld!");
thread::sleep(Duration::from_millis(100));
log::debug!("Setting executable bit to {:?}..", out_path);
let chmod_output = Command::new("chmod")
.arg("+x")
.arg(out_path)
.output()
.expect("Unable to execute ld!");
if !chmod_output.status.success() {
let code = chmod_output.status.code().unwrap_or(255);
log::error!("chmod exited with code {code}");
println!("{}", unsafe { String::from_utf8_unchecked(chmod_output.stderr) });
return;
}
#[cfg(feature = "log_output")]
println!("Setting executable bit to {:?}..", out_path);
Command::new("chmod").arg("+x").arg(out_path).spawn().unwrap();
thread::sleep(Duration::from_millis(100));
}
}
@ -86,12 +64,6 @@ fn find_objectfile(name: &str) -> String {
.arg(&name)
.output()
.expect("Unable to execute whereis");
if !whereis.status.success() {
let code = whereis.status.code().unwrap_or(255);
log::error!("whereis exited with code {code}");
println!("{}", unsafe { String::from_utf8_unchecked(whereis.stderr) });
panic!();
}
let whereis_output = String::from_utf8(whereis.stdout).unwrap();
whereis_output
@ -104,15 +76,3 @@ fn find_objectfile(name: &str) -> String {
.unwrap()
.to_owned()
}
pub fn execute(path: &PathBuf) -> Option<i32> {
let output = Command::new(path.clone()).output().expect("Unable to execute {path}");
if !output.status.success() {
let code = output.status.code().unwrap_or(255);
log::error!("{path:?} exited with code {code}");
println!("{}", unsafe { String::from_utf8_unchecked(output.stderr) });
}
output.status.code()
}

View File

@ -69,14 +69,11 @@ use reid_lib::{compile::CompileOutput, Context};
use crate::{
ast::TopLevelStatement,
mir::{
macros::{form_macros, MacroModule, MacroPass},
SourceModuleId,
},
mir::macros::{form_macros, MacroModule, MacroPass},
};
pub mod ast;
pub mod codegen;
mod codegen;
pub mod error_raporting;
pub mod ld;
pub mod mir;
@ -86,11 +83,9 @@ mod util;
pub fn parse_module<'map, T: Into<String>>(
source: &str,
name: T,
path: Option<PathBuf>,
map: &'map mut ErrorModules,
module_id: Option<SourceModuleId>,
) -> Result<(mir::SourceModuleId, Vec<FullToken>), ReidError> {
let id = map.add_module(name.into(), path, module_id).unwrap();
let id = map.add_module(name.into()).unwrap();
map.set_source(id, source.to_owned());
let tokens = ReidError::from_lexer(lexer::tokenize(source), map.clone(), id)?;
@ -98,7 +93,8 @@ pub fn parse_module<'map, T: Into<String>>(
map.set_tokens(id, tokens.clone());
#[cfg(debug_assertions)]
log::trace!("{:#?}", &tokens);
#[cfg(feature = "log_output")]
println!("{:#?}", &tokens);
Ok((id, tokens))
}
@ -109,7 +105,7 @@ pub fn compile_module<'map>(
map: &'map mut ErrorModules,
path: Option<PathBuf>,
is_main: bool,
) -> Result<Result<mir::Module, (ast::Module, ReidError)>, ReidError> {
) -> Result<mir::Module, ReidError> {
let module = map.module(&module_id).cloned().unwrap();
let mut token_stream = TokenStream::from(&tokens);
@ -121,8 +117,6 @@ pub fn compile_module<'map>(
statements.push(statement);
}
let errors = token_stream.errors();
drop(token_stream);
let ast_module = ast::Module {
@ -133,33 +127,11 @@ pub fn compile_module<'map>(
is_main,
};
if errors.len() > 0 {
// dbg!(&ast_module);
return Ok(Err((
ast_module,
ReidError::from_kind(
errors
.into_iter()
.map(|e| {
error_raporting::ErrorKind::from(mir::pass::Error {
metadata: mir::Metadata {
source_module_id: module_id,
range: *e.get_range().unwrap_or(&Default::default()),
position: None,
},
kind: e,
})
})
.collect(),
map.clone(),
),
)));
}
#[cfg(debug_assertions)]
log::trace!("{:#?}", &ast_module);
#[cfg(feature = "log_output")]
dbg!(&ast_module);
Ok(Ok(ast_module.process(module_id)))
Ok(ast_module.process(module_id))
}
pub fn perform_all_passes<'map>(
@ -167,10 +139,12 @@ pub fn perform_all_passes<'map>(
module_map: &'map mut ErrorModules,
) -> Result<(), ReidError> {
#[cfg(debug_assertions)]
log::trace!("{:#?}", &context);
#[cfg(feature = "log_output")]
dbg!(&context);
#[cfg(debug_assertions)]
log::trace!("{:#}", &context);
#[cfg(feature = "log_output")]
println!("{:#}", &context);
let state = context.pass(&mut LinkerPass {
module_map,
@ -184,11 +158,14 @@ pub fn perform_all_passes<'map>(
}
#[cfg(debug_assertions)]
log::trace!("{:-^100}", "LINKER OUTPUT");
#[cfg(feature = "log_output")]
println!("{:-^100}", "LINKER OUTPUT");
#[cfg(debug_assertions)]
log::trace!("{:#}", &context);
#[cfg(feature = "log_output")]
println!("{:#}", &context);
#[cfg(debug_assertions)]
log::trace!("{:#?}", &state);
#[cfg(feature = "log_output")]
dbg!(&state);
if !state.errors.is_empty() {
return Err(ReidError::from_kind(
@ -209,11 +186,14 @@ pub fn perform_all_passes<'map>(
let state = context.pass(&mut macro_pass)?;
#[cfg(debug_assertions)]
log::trace!("{:-^100}", "MACRO OUTPUT");
#[cfg(feature = "log_output")]
println!("{:-^100}", "MACRO OUTPUT");
#[cfg(debug_assertions)]
log::trace!("{:#}", &context);
#[cfg(feature = "log_output")]
println!("{:#}", &context);
#[cfg(debug_assertions)]
log::trace!("{:#?}", &state);
#[cfg(feature = "log_output")]
dbg!(&state);
if !state.errors.is_empty() {
return Err(ReidError::from_kind(
@ -247,13 +227,17 @@ pub fn perform_all_passes<'map>(
let state = context.pass(&mut TypeInference { refs: &mut refs })?;
#[cfg(debug_assertions)]
log::trace!("{:-^70}", "TYPE INFERRER OUTPUT");
#[cfg(feature = "log_output")]
println!("{:-^100}", "TYPE INFERRER OUTPUT");
#[cfg(debug_assertions)]
log::trace!("{}", &refs);
#[cfg(feature = "log_output")]
println!("{}", &refs);
#[cfg(debug_assertions)]
log::trace!("{:#}", &context);
#[cfg(feature = "log_output")]
println!("{:#}", &context);
#[cfg(debug_assertions)]
log::trace!("{:#?}", &state);
#[cfg(feature = "log_output")]
dbg!(&state);
if !state.errors.is_empty() {
return Err(ReidError::from_kind(
@ -269,11 +253,14 @@ pub fn perform_all_passes<'map>(
let state = context.pass(&mut TypeCheck { refs: &refs })?;
#[cfg(debug_assertions)]
log::trace!("{:-^100}", "TYPECHECKER OUTPUT");
#[cfg(feature = "log_output")]
println!("{:-^100}", "TYPECHECKER OUTPUT");
#[cfg(debug_assertions)]
log::trace!("{:#}", &context);
#[cfg(feature = "log_output")]
println!("{:#}", &context);
#[cfg(debug_assertions)]
log::trace!("{:#?}", &state);
#[cfg(feature = "log_output")]
dbg!(&state);
if !state.errors.is_empty() {
return Err(ReidError::from_kind(
@ -286,6 +273,9 @@ pub fn perform_all_passes<'map>(
));
}
#[cfg(feature = "context_debug")]
dbg!(&context);
Ok(())
}
@ -302,17 +292,19 @@ pub fn compile_and_pass<'map>(
let path = path.canonicalize().unwrap();
let name = path.file_name().unwrap().to_str().unwrap().to_owned();
let (id, tokens) = parse_module(source, name, Some(path.clone()), module_map, None)?;
let module = compile_module(id, tokens, module_map, Some(path.clone()), true)?.map_err(|(_, e)| e)?;
let (id, tokens) = parse_module(source, name, module_map)?;
let module = compile_module(id, tokens, module_map, Some(path.clone()), true)?;
let mut mir_context = mir::Context::from(vec![module], path.parent().unwrap().to_owned());
perform_all_passes(&mut mir_context, module_map)?;
#[cfg(debug_assertions)]
log::trace!("{:-^100}", "FINAL OUTPUT");
#[cfg(feature = "log_output")]
println!("{:-^100}", "FINAL OUTPUT");
#[cfg(debug_assertions)]
log::trace!("{:#}", &mir_context);
#[cfg(feature = "log_output")]
println!("{:#}", &mir_context);
let mut context = Context::new(format!("Reid ({})", env!("CARGO_PKG_VERSION")));
let codegen_modules = match mir_context.codegen(&mut context) {
@ -321,7 +313,8 @@ pub fn compile_and_pass<'map>(
};
#[cfg(debug_assertions)]
log::trace!("{}", &codegen_modules.context);
#[cfg(feature = "log_output")]
println!("{}", &codegen_modules.context);
let compiled = codegen_modules.compile(cpu, features);
Ok((

View File

@ -1,149 +0,0 @@
use std::{fs, path::PathBuf, process};
use argh::FromArgs;
use log::*;
use reid::{
compile_simple,
ld::{execute, LDRunner},
CustomIRs,
};
use reid_lib::compile::CompileOutput;
#[derive(FromArgs, PartialEq, Debug)]
/// Compile or run a Reid (.reid) file
#[argh(help_triggers("-h", "--help", "help"))]
struct Options {
#[argh(option, short = 'l', default = "log::Level::Info")]
/// log level
log_level: log::Level,
#[argh(switch, short = 't')]
/// should logs be timestamped
timestamps: bool,
#[argh(subcommand)]
command: Command,
}
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)]
enum Command {
Build(BuildOpts),
Run(RunOpts),
}
#[derive(FromArgs, PartialEq, Debug)]
/// Build an executable file without running it
#[argh(subcommand, name = "build")]
struct BuildOpts {
#[argh(option, long = "lib", short = 'l')]
/// additional libraries to link against (with ld)
libraries: Vec<String>,
#[argh(positional)]
path: PathBuf,
}
#[derive(FromArgs, PartialEq, Debug)]
/// Build and then execute the executable
#[argh(subcommand, name = "run")]
struct RunOpts {
#[argh(option, long = "lib", short = 'l')]
/// additional libraries to link against (with ld)
libraries: Vec<String>,
#[argh(positional)]
path: PathBuf,
}
fn main() {
let options: Options = argh::from_env();
let mut errlog = stderrlog::new();
errlog.module(module_path!()).verbosity(options.log_level);
if options.timestamps {
errlog.timestamp(stderrlog::Timestamp::Second);
}
errlog.init().unwrap();
match &options.command {
Command::Build(BuildOpts { libraries, path }) | Command::Run(RunOpts { libraries, path }) => {
let cpu = std::env::var("CPU").unwrap_or("generic".to_owned());
let features = std::env::var("REIDFLAGS").unwrap_or("".to_owned());
let path = match path.canonicalize() {
Ok(path) => path,
Err(e) => {
error!("{e}");
return;
}
};
let parent = path.with_extension("");
let llvm_ir_path = parent.with_extension("ll");
let object_path = parent.with_extension("o");
let llir_path = parent.with_extension("llir");
let mir_path = parent.with_extension("mir");
let asm_path = parent.with_extension("asm");
let out_path = object_path.with_extension("out");
let before = std::time::SystemTime::now();
let text = match fs::read_to_string(&path) {
Ok(text) => text,
Err(e) => {
error!("Could not read file: {e}");
return;
}
};
match compile_simple(&text, PathBuf::from(&path), Some(cpu), vec![features]) {
Ok((
CompileOutput {
triple: _triple,
assembly,
obj_buffer,
llvm_ir: _llvm_ir,
},
CustomIRs { llir, mir },
)) => {
log::trace!("{}", _llvm_ir);
log::debug!("Compiled with triple: {}\n", &_triple);
log::debug!("Output LLVM IR to {:?}", llvm_ir_path);
log::debug!("Output Assembly to {:?}", asm_path);
log::debug!("Output Object-file to {:?}\n", object_path);
log::debug!("Output LLIR-file to {:?}\n", llir_path);
log::debug!("Output MIR-file to {:?}\n", mir_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!");
fs::write(&object_path, &obj_buffer).expect("Could not write Object-file!");
fs::write(&llir_path, &llir).expect("Could not write LLIR-file!");
fs::write(&mir_path, &mir).expect("Could not write MIR-file!");
let after = std::time::SystemTime::now();
log::info!(
"Compilation took: {:.2}ms\n",
(after.duration_since(before).unwrap().as_micros() as f32) / 1000.
);
log::info!("Linking {:?}", &object_path);
let linker = std::env::var("LD").unwrap_or("ld".to_owned());
let mut linker = LDRunner::from_command(&linker).with_library("c").with_library("m");
for library in libraries {
linker = linker.with_library(&library);
}
linker.invoke(&object_path, &out_path);
}
Err(e) => {
log::error!("{e}");
return;
}
};
match &options.command {
Command::Build(_) => {}
Command::Run(_) => {
if let Some(code) = execute(&out_path) {
process::exit(code);
}
}
}
}
}
}

View File

@ -84,11 +84,7 @@ impl Display for GlobalKind {
impl Display for Import {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"import {}",
self.0.iter().map(|(s, _)| s.clone()).collect::<Vec<_>>().join("::")
)
write!(f, "import {}", self.0.join("::"))
}
}
@ -117,7 +113,7 @@ impl Display for TypeDefinition {
self.name,
self.source_module,
if let Some(mod_id) = self.importer {
format!("; imported by {}", mod_id)
format!("; imported to {}", mod_id)
} else {
String::new()
}
@ -152,9 +148,6 @@ impl Display for StructField {
impl Display for FunctionDefinition {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(documentation) = &self.documentation {
writeln!(f, "/// {}", documentation)?;
}
write!(
f,
"{}fn {}({}) -> {:#} ",
@ -279,8 +272,8 @@ impl Display for ExprKind {
}
f.write_char(']')
}
ExprKind::Struct(key, items) => {
write!(f, "{:?} ", key)?;
ExprKind::Struct(name, items) => {
write!(f, "{} ", name)?;
f.write_char('{')?;
let mut state = Default::default();
@ -471,7 +464,7 @@ impl Display for TypeKind {
}
TypeKind::Vague(vague_type) => Display::fmt(vague_type, f),
TypeKind::F16 => write!(f, "f16"),
TypeKind::F16B => write!(f, "f16b"),
TypeKind::F32B => write!(f, "f16b"),
TypeKind::F32 => write!(f, "f32"),
TypeKind::F64 => write!(f, "f64"),
TypeKind::F128 => write!(f, "f128"),

View File

@ -1,3 +1,5 @@
use reid_lib::builder::TypeValue;
use crate::util::maybe;
use super::{typecheck::typerefs::TypeRefs, *};
@ -48,7 +50,7 @@ impl TypeKind {
TypeKind::Borrow(..) => false,
TypeKind::UserPtr(..) => false,
TypeKind::F16 => true,
TypeKind::F16B => true,
TypeKind::F32B => true,
TypeKind::F32 => true,
TypeKind::F64 => true,
TypeKind::F128 => true,
@ -73,26 +75,21 @@ impl TypeKind {
TypeKind::Void => 0,
TypeKind::Char => 8,
TypeKind::Array(type_kind, len) => type_kind.size_of(map) * (*len as u64),
TypeKind::CustomType(key) => match &map.get(key) {
Some(def) => match &def.kind {
TypeDefinitionKind::Struct(struct_type) => {
let mut size = 0;
for field in &struct_type.0 {
size += field.1.size_of(map)
}
size
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)
}
},
// Easy to recognize default number. Used e.g. when sorting
// types by size
None => 404,
size
}
},
TypeKind::CodegenPtr(_) => 64,
TypeKind::Vague(_) => panic!("Tried to sizeof a vague type!"),
TypeKind::Borrow(..) => 64,
TypeKind::UserPtr(_) => 64,
TypeKind::F16 => 16,
TypeKind::F16B => 16,
TypeKind::F32B => 16,
TypeKind::F32 => 32,
TypeKind::F64 => 64,
TypeKind::F128 => 128,
@ -123,7 +120,7 @@ impl TypeKind {
TypeKind::Borrow(_, _) => 64,
TypeKind::UserPtr(_) => 64,
TypeKind::F16 => 16,
TypeKind::F16B => 16,
TypeKind::F32B => 16,
TypeKind::F32 => 32,
TypeKind::F64 => 64,
TypeKind::F128 => 128,
@ -153,7 +150,7 @@ impl TypeKind {
| TypeKind::U128
| TypeKind::Char => TypeCategory::Integer,
TypeKind::F16
| TypeKind::F16B
| TypeKind::F32B
| TypeKind::F32
| TypeKind::F64
| TypeKind::F128
@ -202,36 +199,6 @@ impl TypeKind {
}
}
impl Ord for TypeKind {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
use std::cmp::*;
let category_ord = self.category().partial_cmp(&other.category());
match category_ord {
Some(Ordering::Equal) | None => {
if !self.signed() && other.signed() {
return Ordering::Less;
}
if self.signed() && !other.signed() {
return Ordering::Greater;
}
let self_size = self.size_of(&HashMap::new());
let other_size = other.size_of(&HashMap::new());
if self_size == 32 && other_size != 32 {
return Ordering::Less;
} else if self_size != 32 && other_size == 32 {
return Ordering::Greater;
}
self_size.cmp(&self_size)
}
Some(ord) => ord,
}
}
}
impl BinaryOperator {
pub fn is_commutative(&self) -> bool {
match self {
@ -259,28 +226,7 @@ impl BinaryOperator {
}
}
const TYPE_CATEGORY_ORDER: [TypeCategory; 5] = [
TypeCategory::Integer,
TypeCategory::Bool,
TypeCategory::Real,
TypeCategory::Other,
TypeCategory::TypeRef,
];
impl PartialOrd for TypeCategory {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
use std::cmp::*;
let self_idx = TYPE_CATEGORY_ORDER.iter().enumerate().find(|s| s.1 == self);
let other_idx = TYPE_CATEGORY_ORDER.iter().enumerate().find(|s| s.1 == other);
if let (Some(self_idx), Some(other_idx)) = (self_idx, other_idx) {
Some(self_idx.cmp(&other_idx))
} else {
None
}
}
}
#[derive(PartialEq, Eq, Ord)]
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub enum TypeCategory {
Integer,
Real,
@ -470,7 +416,10 @@ impl Expression {
))
}
Accessed(_, type_kind, ..) => Ok((ReturnKind::Soft, type_kind.clone())),
Struct(key, _) => Ok((ReturnKind::Soft, TypeKind::CustomType(key.clone()))),
Struct(name, _) => Ok((
ReturnKind::Soft,
TypeKind::CustomType(CustomTypeKey(name.clone(), mod_id)),
)),
Borrow(expr, mutable) => {
let ret_type = expr.return_type(refs, mod_id)?;
Ok((ret_type.0, TypeKind::Borrow(Box::new(ret_type.1), *mutable)))

View File

@ -11,8 +11,8 @@ use crate::{
compile_module,
error_raporting::{ErrorModules, ReidError},
mir::{
pass::BinopKey, BinopDefinition, CustomTypeKey, FunctionDefinitionKind, SourceModuleId, StructType,
TypeDefinition, TypeDefinitionKind, TypeKind,
pass::BinopKey, BinopDefinition, CustomTypeKey, FunctionDefinitionKind, FunctionParam, SourceModuleId,
TypeDefinition, TypeKind,
},
parse_module,
};
@ -46,19 +46,13 @@ pub enum ErrorKind {
NoMainDefined,
#[error("Main module has no main-function!")]
NoMainFunction,
#[error("Type {0} has cyclical fields!")]
CyclicalType(String),
#[error("Type {0} is imported cyclically!")]
RecursiveTypeImport(String),
#[error("Type {} does not exist in module {}", 0.0, 0.1)]
NoSuchTypeInModule(CustomTypeKey),
#[error("Function {1} in module {0} is private!")]
FunctionIsPrivate(String, String),
}
pub fn compile_std(module_map: &mut ErrorModules) -> Result<Module, ReidError> {
let (id, tokens) = parse_module(STD_SOURCE, STD_NAME, None, module_map, None)?;
let module = compile_module(id, tokens, module_map, None, false)?.map_err(|(_, e)| e)?;
let (id, tokens) = parse_module(STD_SOURCE, STD_NAME, module_map)?;
let module = compile_module(id, tokens, module_map, None, false)?;
let module_id = module.module_id;
let mut mir_context = super::Context::from(vec![module], Default::default());
@ -76,21 +70,11 @@ pub struct LinkerPass<'map> {
#[derive(Default, Clone)]
pub struct LinkerState {
foreign_types: HashMap<SourceModuleId, HashMap<CustomTypeKey, SourceModuleId>>,
extern_imported_types: HashMap<SourceModuleId, HashMap<String, SourceModuleId>>,
}
type LinkerPassState<'st, 'sc> = PassState<'st, 'sc, LinkerState, ErrorKind>;
#[derive(Clone, Debug)]
struct LinkerModule {
module: Rc<RefCell<Module>>,
// Functions imported directly from a module
function_imports: HashMap<String, (SourceModuleId, Metadata)>,
// Types imported either directly by the user or indirectly via functions.
// May contain type-imports that are again recursively imported elsewhere.
type_imports: HashMap<String, (SourceModuleId, Metadata)>,
}
impl<'map> Pass for LinkerPass<'map> {
type Data = LinkerState;
type TError = ErrorKind;
@ -118,56 +102,37 @@ impl<'map> Pass for LinkerPass<'map> {
}
};
let mut modules = HashMap::<SourceModuleId, LinkerModule>::new();
let mut modules = HashMap::<SourceModuleId, Rc<RefCell<_>>>::new();
let mut module_ids = HashMap::<String, SourceModuleId>::new();
for (mod_id, module) in context.modules.drain() {
modules.insert(
mod_id,
LinkerModule {
module: Rc::new(RefCell::new(module)),
function_imports: HashMap::new(),
type_imports: HashMap::new(),
},
);
modules.insert(mod_id, Rc::new(RefCell::new(module)));
}
let mut module_queue: Vec<LinkerModule> = modules.values().cloned().collect();
let mut modules_to_process: Vec<Rc<RefCell<_>>> = modules.values().cloned().collect();
while let Some(mut importer) = module_queue.pop() {
let importer_mod = importer.module.borrow_mut();
let mut already_imported_types = HashSet::<CustomTypeKey>::new();
let mut already_imported_binops = HashSet::<BinopKey>::new();
// Gp go through all imports in this specific modulee
for import in importer_mod.imports.clone() {
while let Some(module) = modules_to_process.pop() {
let mut extern_types = HashMap::new();
let mut importer_module = module.borrow_mut();
for import in importer_module.imports.clone() {
let Import(path, _) = &import;
if path.len() != 2 {
state.ok::<_, Infallible>(Err(ErrorKind::InnerModulesNotYetSupported(import.clone())), import.1);
}
// Cut the import statement into parts
let Some((module_name, _)) = path.get(0) else {
continue;
};
let Some((import_name, _)) = path.get(1) else {
continue;
};
let module_name = unsafe { path.get_unchecked(0) };
// Actually compile or fetch the imported module
let imported = if let Some(mod_id) = module_ids.get(module_name) {
let mut imported = if let Some(mod_id) = module_ids.get(module_name) {
modules.get(mod_id).unwrap()
} else if module_name == STD_NAME {
let std = compile_std(&mut self.module_map)?;
let module_id = std.module_id;
modules.insert(
std.module_id,
LinkerModule {
module: Rc::new(RefCell::new(std)),
function_imports: HashMap::new(),
type_imports: HashMap::new(),
},
);
module_ids.insert(module_name.clone(), module_id);
modules.get(&module_id).unwrap()
modules.insert(std.module_id, Rc::new(RefCell::new(compile_std(&mut self.module_map)?)));
module_ids.insert(std.name, std.module_id);
modules.get(&std.module_id).unwrap()
} else {
let file_path = PathBuf::from(&context.base.clone()).join(module_name.to_owned() + ".reid");
@ -176,13 +141,7 @@ impl<'map> Pass for LinkerPass<'map> {
continue;
};
let (id, tokens) = match parse_module(
&source,
module_name.clone(),
Some(file_path.clone()),
&mut self.module_map,
None,
) {
let (id, tokens) = match parse_module(&source, module_name.clone(), &mut self.module_map) {
Ok(val) => val,
Err(err) => {
state.ok::<_, Infallible>(
@ -197,40 +156,21 @@ impl<'map> Pass for LinkerPass<'map> {
};
match compile_module(id, tokens, &mut self.module_map, Some(file_path), false) {
Ok(res) => match res {
Ok(imported_module) => {
if imported_module.is_main {
state.ok::<_, Infallible>(
Err(ErrorKind::TriedLinkingMain(module_name.clone())),
import.1,
);
continue;
}
let module_id = imported_module.module_id;
module_ids.insert(imported_module.name.clone(), imported_module.module_id);
modules.insert(
module_id,
LinkerModule {
module: Rc::new(RefCell::new(imported_module)),
function_imports: HashMap::new(),
type_imports: HashMap::new(),
},
);
let imported = modules.get_mut(&module_id).unwrap();
module_queue.push(imported.clone());
imported
}
Err((_, err)) => {
Ok(imported_module) => {
if imported_module.is_main {
state.ok::<_, Infallible>(
Err(ErrorKind::ModuleCompilationError(
module_name.clone(),
format!("{}", err),
)),
Err(ErrorKind::TriedLinkingMain(module_name.clone())),
import.1,
);
continue;
}
},
let module_id = imported_module.module_id;
module_ids.insert(imported_module.name.clone(), imported_module.module_id);
modules.insert(module_id, Rc::new(RefCell::new(imported_module)));
let imported = modules.get_mut(&module_id).unwrap();
modules_to_process.push(imported.clone());
imported
}
Err(err) => {
state.ok::<_, Infallible>(
Err(ErrorKind::ModuleCompilationError(
@ -242,151 +182,66 @@ impl<'map> Pass for LinkerPass<'map> {
continue;
}
}
};
let imported_module = imported.module.borrow();
if let Some(func) = imported_module.functions.iter().find(|f| f.name == *import_name) {
// If the imported item is a function, add it to the list of imported functions
importer
.function_imports
.insert(func.name.clone(), (imported_module.module_id, import.1));
} else if let Some(ty) = imported_module.typedefs.iter().find(|t| t.name == *import_name) {
// If the imported item is a type, add it to the list of imported types
// imported_types.insert((CustomTypeKey(ty.name.clone(), ty.source_module), true));
importer
.type_imports
.insert(ty.name.clone(), (imported_module.module_id, import.1));
}
}
.borrow_mut();
let module_id = importer_mod.module_id;
drop(importer_mod);
modules.insert(module_id, importer);
}
let import_name = unsafe { path.get_unchecked(1) };
let import_id = imported.module_id;
for (_, linker_module) in &modules {
let mut importer_module = linker_module.module.borrow_mut();
let mut imported_types = Vec::new();
let mut unresolved_types = HashMap::new();
if let Some(func) = imported.functions.iter_mut().find(|f| f.name == *import_name) {
let func_name = func.name.clone();
// 1. Import functions and find all types that are dependencies of
// functions
for (name, (function_source, import_meta)) in &linker_module.function_imports {
let mut function_module = modules.get(&function_source).unwrap().module.borrow_mut();
let func_module_name = function_module.name.clone();
let func_module_id = function_module.module_id;
let function = function_module.functions.iter_mut().find(|f| f.name == *name).unwrap();
// If function is not pub, error
if !function.is_pub {
state.ok::<_, Infallible>(
Err(ErrorKind::FunctionIsPrivate(func_module_name, function.name.clone())),
import_meta.clone(),
);
continue;
}
// If function already exists, error
if let Some(existing) = importer_module.functions.iter().find(|f| f.name == *name) {
if let Err(e) = existing.equals_as_imported(&function) {
if !func.is_pub {
state.ok::<_, Infallible>(
Err(ErrorKind::FunctionImportIssue(func_module_name, name.clone(), e)),
import_meta.clone(),
Err(ErrorKind::FunctionIsPrivate(module_name.clone(), func_name.clone())),
import.1,
);
}
}
function.is_imported = true;
for ty in import_type(&function.return_type) {
unresolved_types.insert(ty, (import_meta.clone(), true));
}
for param in &function.parameters {
for ty in import_type(&param.ty) {
unresolved_types.insert(ty, (import_meta.clone(), true));
}
}
importer_module.functions.push(FunctionDefinition {
name: function.name.clone(),
documentation: function.documentation.clone(),
linkage_name: None,
is_pub: false,
is_imported: false,
return_type: function.return_type.clone(),
parameters: function.parameters.clone(),
kind: super::FunctionDefinitionKind::Extern(true),
source: Some(func_module_id),
signature_meta: function.signature(),
});
}
// 2. Add all manually imported types to the list of types that need
// to be resolved and recursed
for (name, (source_module, meta)) in &linker_module.type_imports {
let imported_ty_key = CustomTypeKey(name.clone(), source_module.clone());
let imported_ty = TypeKind::CustomType(imported_ty_key.clone());
let imported = modules.get(&imported_ty_key.1).unwrap().module.borrow();
for (ty, func) in &imported.associated_functions {
if *ty != imported_ty {
continue;
}
for ty in import_type(&func.return_type) {
if unresolved_types.contains_key(&ty) {
continue;
func.is_imported = true;
if let Some(existing) = importer_module.functions.iter().find(|f| f.name == *func_name) {
if let Err(e) = existing.equals_as_imported(func) {
state.ok::<_, Infallible>(
Err(ErrorKind::FunctionImportIssue(
module_name.clone(),
func_name.clone(),
e,
)),
import.1,
);
}
unresolved_types.insert(ty, (meta.clone(), true));
}
let types = import_type(&func.return_type, false);
let return_type = func.return_type.clone();
imported_types.extend(types);
let mut param_tys = Vec::new();
for param in &func.parameters {
for ty in import_type(&param.ty) {
if unresolved_types.contains_key(&ty) {
continue;
}
unresolved_types.insert(ty, (meta.clone(), true));
}
let types = import_type(&param.ty, false);
imported_types.extend(types);
param_tys.push(param.clone());
}
}
unresolved_types.insert(imported_ty_key.clone(), (meta.clone(), false));
}
importer_module.functions.push(FunctionDefinition {
name: func_name.clone(),
linkage_name: None,
is_pub: false,
is_imported: false,
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);
let imported_ty = TypeKind::CustomType(external_key.clone());
imported_types.push((external_key, true));
// 3. Recurse these types to find their true sources, find their
// dependencies, and list them all. Store manually imported types
// in a separate mapping for later.
let mut imported_types = HashMap::new();
let mut foreign_keys = HashSet::new();
let mut already_imported_binops = HashSet::new();
for (ty, (meta, is_dependency)) in unresolved_types {
// First deal with manually imported types
if !is_dependency {
// Add them to the list of foreign types (types that are
// later replaced in-source by name)
let imported_ty_key = match resolve_type(&ty, &modules) {
Ok(ty) => {
foreign_keys.insert(CustomTypeKey(ty.0.clone(), importer_module.module_id));
ty
}
Err(e) => {
state.note_errors(&vec![e], meta);
return Ok(());
}
};
imported_types.insert(CustomTypeKey(ty.0.clone(), importer_module.module_id), ty.1);
let mut imported = modules.get(&imported_ty_key.1).unwrap().module.borrow_mut();
let imported_module_name = imported.name.clone();
let imported_module_id = imported.module_id.clone();
let imported_ty = TypeKind::CustomType(imported_ty_key);
// Add all binary operators that are defined for this type
for binop in &mut imported.binop_defs {
if binop.lhs.ty != imported_ty && binop.rhs.ty != imported_ty {
continue;
@ -417,7 +272,6 @@ impl<'map> Pass for LinkerPass<'map> {
}
}
// Import all functions that are associated with this type
for (ty, func) in &mut imported.associated_functions {
if *ty != imported_ty {
continue;
@ -427,11 +281,8 @@ impl<'map> Pass for LinkerPass<'map> {
if !func.is_pub {
state.ok::<_, Infallible>(
Err(ErrorKind::FunctionIsPrivate(
imported_module_name.clone(),
func_name.clone(),
)),
meta.clone(),
Err(ErrorKind::FunctionIsPrivate(module_name.clone(), func_name.clone())),
import.1,
);
continue;
}
@ -446,81 +297,116 @@ impl<'map> Pass for LinkerPass<'map> {
if let Err(e) = existing.equals_as_imported(func) {
state.ok::<_, Infallible>(
Err(ErrorKind::FunctionImportIssue(
imported_module_name.clone(),
module_name.clone(),
func_name.clone(),
e,
)),
meta.clone(),
import.1,
);
}
}
let types = import_type(&func.return_type, false);
let return_type = func.return_type.clone();
imported_types.extend(types);
let mut param_tys = Vec::new();
for param in &func.parameters {
let types = import_type(&param.ty, false);
imported_types.extend(types);
param_tys.push(param.clone());
}
importer_module.associated_functions.push((
ty.clone(),
FunctionDefinition {
name: func_name.clone(),
documentation: func.documentation.clone(),
linkage_name: Some(format!("{}::{}", ty, func_name)),
is_pub: false,
is_imported: false,
return_type: func.return_type.clone(),
parameters: func.parameters.clone(),
return_type,
parameters: param_tys,
kind: super::FunctionDefinitionKind::Extern(true),
source: Some(imported_module_id),
signature_meta: func.signature_meta,
source: Some(import_id),
},
));
}
}
match resolve_types_recursively(&TypeKind::CustomType(ty.clone()), &modules, HashSet::new()) {
Ok(resolved) => {
imported_types.extend(resolved);
}
Err(e) => {
state.note_errors(&vec![e], meta);
return Ok(());
}
}
}
let mut typedef_keys = HashMap::new();
// 4. Import all listed types.
for (importer_typekey, imported_module_id) in &imported_types {
let importee_typekey = CustomTypeKey(importer_typekey.0.clone(), *imported_module_id);
if let Some(module_id) = typedef_keys.get(&importee_typekey) {
if *module_id != importer_module.module_id {
typedef_keys.insert(importee_typekey.clone(), importer_typekey.1);
}
} else {
typedef_keys.insert(importee_typekey.clone(), importer_typekey.1);
state.ok::<_, Infallible>(
Err(ErrorKind::ImportDoesNotExist(module_name.clone(), import_name.clone())),
import.1,
);
continue;
}
}
for (typedef_key, importer_module_id) in &typedef_keys {
let imported_ty_module = modules.get(&typedef_key.1).unwrap().module.borrow();
if let Some(typedef) = imported_ty_module
.typedefs
.iter()
.find(|ty| CustomTypeKey(ty.name.clone(), ty.source_module) == *typedef_key)
let mut seen = HashSet::new();
let mut current_extern_types = HashSet::new();
seen.extend(imported_types.clone().iter().map(|t| t.0.clone()));
current_extern_types.extend(imported_types.clone().iter().filter(|t| t.1).map(|t| t.0.clone()));
for extern_type in &current_extern_types {
extern_types.insert(extern_type.0.clone(), extern_type.1);
}
let imported_mod_id = imported.module_id;
let imported_mod_typedefs = &mut imported.typedefs;
for typekey in imported_types.clone() {
let typedef = imported_mod_typedefs
.iter()
.find(|ty| CustomTypeKey(ty.name.clone(), imported_mod_id) == typekey.0)
.unwrap();
let inner = find_inner_types(typedef, seen.clone(), imported_mod_id);
seen.extend(inner.iter().cloned());
}
// TODO: Unable to import same-named type from multiple places..
let seen = seen
.difference(&already_imported_types)
.cloned()
{
importer_module.typedefs.push(TypeDefinition {
importer: Some(*importer_module_id),
..typedef
});
.collect::<HashSet<_>>();
already_imported_types.extend(seen.clone());
for typekey in &already_imported_types {
if current_extern_types.contains(typekey) {
let module_id = importer_module.module_id;
let typedef = importer_module
.typedefs
.iter_mut()
.find(|t| t.name == typekey.0 && t.source_module == typekey.1);
if let Some(typedef) = typedef {
typedef.importer = Some(module_id);
}
}
}
for typekey in seen.into_iter() {
let mut typedef = imported_mod_typedefs
.iter()
.find(|ty| CustomTypeKey(ty.name.clone(), imported_mod_id) == typekey)
.unwrap()
.clone();
if current_extern_types.contains(&typekey) {
typedef = TypeDefinition {
importer: Some(importer_module.module_id),
..typedef
};
}
importer_module.typedefs.push(typedef);
}
}
state
.scope
.data
.foreign_types
.insert(importer_module.module_id, imported_types);
.extern_imported_types
.insert(importer_module.module_id, extern_types);
}
let mut modules: Vec<Module> = modules
.into_values()
.map(|v| Rc::into_inner(v.module).unwrap().into_inner())
.map(|v| Rc::into_inner(v).unwrap().into_inner())
.collect();
for module in modules.drain(..) {
@ -530,33 +416,19 @@ impl<'map> Pass for LinkerPass<'map> {
Ok(())
}
fn module(&mut self, module: &mut Module, state: PassState<Self::Data, Self::TError>) -> PassResult {
let foreign_types = &state.scope.data.foreign_types.get(&module.module_id);
if let Some(foreign_types) = foreign_types {
for ty in &mut module.typedefs {
match &mut ty.kind {
TypeDefinitionKind::Struct(StructType(fields)) => {
for field in fields {
field.1 = field.1.update_imported(foreign_types);
}
}
}
}
}
Ok(())
}
fn function(
&mut self,
function: &mut FunctionDefinition,
state: PassState<Self::Data, Self::TError>,
) -> PassResult {
let mod_id = state.scope.module_id.unwrap();
let foreign_types = &state.scope.data.foreign_types.get(&mod_id);
if let Some(foreign_types) = foreign_types {
function.return_type = function.return_type.update_imported(*foreign_types);
for param in function.parameters.iter_mut() {
param.ty = param.ty.update_imported(foreign_types);
if matches!(function.kind, FunctionDefinitionKind::Local(_, _)) {
let mod_id = state.scope.module_id.unwrap();
let extern_types = &state.scope.data.extern_imported_types.get(&mod_id);
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.ty = param.ty.update_imported(extern_types, mod_id);
}
}
}
Ok(())
@ -564,11 +436,11 @@ impl<'map> Pass for LinkerPass<'map> {
fn stmt(&mut self, stmt: &mut super::Statement, state: PassState<Self::Data, Self::TError>) -> PassResult {
let mod_id = state.scope.module_id.unwrap();
let foreign_types = &state.scope.data.foreign_types.get(&mod_id);
if let Some(foreign_types) = foreign_types {
let extern_types = &state.scope.data.extern_imported_types.get(&mod_id);
if let Some(extern_types) = extern_types {
match &mut stmt.0 {
super::StmtKind::Let(var_ref, _, _) => {
var_ref.0 = var_ref.0.update_imported(foreign_types);
var_ref.0 = var_ref.0.update_imported(extern_types, mod_id);
}
_ => {}
}
@ -578,29 +450,25 @@ impl<'map> Pass for LinkerPass<'map> {
fn expr(&mut self, expr: &mut super::Expression, state: PassState<Self::Data, Self::TError>) -> PassResult {
let mod_id = state.scope.module_id.unwrap();
let foreign_types = &state.scope.data.foreign_types.get(&mod_id);
if let Some(foreign_types) = foreign_types {
let extern_types = &state.scope.data.extern_imported_types.get(&mod_id);
if let Some(extern_types) = extern_types {
match &mut expr.0 {
super::ExprKind::Variable(var_ref) => {
var_ref.0 = var_ref.0.update_imported(foreign_types);
var_ref.0 = var_ref.0.update_imported(extern_types, mod_id);
}
super::ExprKind::Indexed(.., type_kind, _) => *type_kind = type_kind.update_imported(foreign_types),
super::ExprKind::Accessed(.., type_kind, _, _) => *type_kind = type_kind.update_imported(foreign_types),
super::ExprKind::BinOp(.., type_kind) => *type_kind = type_kind.update_imported(foreign_types),
super::ExprKind::Indexed(.., type_kind, _) => {
*type_kind = type_kind.update_imported(extern_types, mod_id)
}
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),
super::ExprKind::Borrow(..) => {}
super::ExprKind::Deref(..) => {}
super::ExprKind::CastTo(_, type_kind) => *type_kind = type_kind.update_imported(foreign_types),
super::ExprKind::AssociatedFunctionCall(type_kind, fn_call) => {
*type_kind = type_kind.update_imported(foreign_types);
fn_call.return_type = fn_call.return_type.update_imported(foreign_types);
}
super::ExprKind::Struct(key, _) => {
*key = if let Some(mod_id) = foreign_types.get(&key) {
CustomTypeKey(key.0.clone(), *mod_id)
} else {
key.clone()
}
super::ExprKind::CastTo(_, type_kind) => *type_kind = type_kind.update_imported(extern_types, mod_id),
super::ExprKind::AssociatedFunctionCall(type_kind, _) => {
*type_kind = type_kind.update_imported(extern_types, mod_id)
}
_ => {}
}
@ -610,103 +478,78 @@ impl<'map> Pass for LinkerPass<'map> {
}
impl TypeKind {
fn update_imported(&self, foreign_types: &HashMap<CustomTypeKey, SourceModuleId>) -> TypeKind {
fn update_imported(
&self,
extern_types: &HashMap<String, SourceModuleId>,
importer_mod_id: SourceModuleId,
) -> TypeKind {
match &self {
TypeKind::Array(type_kind, len) => {
TypeKind::Array(Box::new(type_kind.update_imported(foreign_types)), *len)
TypeKind::Array(Box::new(type_kind.update_imported(extern_types, importer_mod_id)), *len)
}
TypeKind::CustomType(custom_type_key) => {
if let Some(mod_id) = foreign_types.get(&custom_type_key) {
if let Some(mod_id) = extern_types.get(&custom_type_key.0) {
TypeKind::CustomType(CustomTypeKey(custom_type_key.0.clone(), *mod_id))
} else {
self.clone()
}
}
TypeKind::Borrow(type_kind, mutable) => {
TypeKind::Borrow(Box::new(type_kind.update_imported(foreign_types)), *mutable)
TypeKind::Borrow(type_kind, mutable) => TypeKind::Borrow(
Box::new(type_kind.update_imported(extern_types, importer_mod_id)),
*mutable,
),
TypeKind::UserPtr(type_kind) => {
TypeKind::UserPtr(Box::new(type_kind.update_imported(extern_types, importer_mod_id)))
}
TypeKind::CodegenPtr(type_kind) => {
TypeKind::CodegenPtr(Box::new(type_kind.update_imported(extern_types, importer_mod_id)))
}
TypeKind::UserPtr(type_kind) => TypeKind::UserPtr(Box::new(type_kind.update_imported(foreign_types))),
TypeKind::CodegenPtr(type_kind) => TypeKind::CodegenPtr(Box::new(type_kind.update_imported(foreign_types))),
_ => self.clone(),
}
}
}
fn import_type(ty: &TypeKind) -> Vec<CustomTypeKey> {
fn import_type(ty: &TypeKind, usable_import: bool) -> Vec<(CustomTypeKey, bool)> {
let mut imported_types = Vec::new();
match &ty {
TypeKind::CustomType(key) => imported_types.push(key.clone()),
TypeKind::Borrow(ty, _) => imported_types.extend(import_type(ty)),
TypeKind::Array(ty, _) => imported_types.extend(import_type(ty)),
TypeKind::UserPtr(ty) => imported_types.extend(import_type(ty)),
TypeKind::CodegenPtr(ty) => imported_types.extend(import_type(ty)),
TypeKind::CustomType(key) => imported_types.push((key.clone(), usable_import)),
TypeKind::Borrow(ty, _) => imported_types.extend(import_type(ty, usable_import)),
TypeKind::Array(ty, _) => imported_types.extend(import_type(ty, usable_import)),
TypeKind::UserPtr(ty) => imported_types.extend(import_type(ty, usable_import)),
TypeKind::CodegenPtr(ty) => imported_types.extend(import_type(ty, usable_import)),
_ => {}
};
imported_types
}
fn resolve_type(
ty: &CustomTypeKey,
modules: &HashMap<SourceModuleId, LinkerModule>,
) -> Result<CustomTypeKey, ErrorKind> {
let mut source_module_id = ty.1;
let mut seen = HashSet::new();
loop {
seen.insert(source_module_id);
let source_module = modules.get(&source_module_id).unwrap();
if let Some((new_module_id, _)) = source_module.type_imports.get(&ty.0) {
if seen.contains(new_module_id) {
return Err(ErrorKind::RecursiveTypeImport(ty.0.clone()));
}
source_module_id = *new_module_id;
} else {
break;
}
}
Ok(CustomTypeKey(ty.0.clone(), source_module_id))
}
fn resolve_types_recursively(
ty: &TypeKind,
modules: &HashMap<SourceModuleId, LinkerModule>,
fn find_inner_types(
typedef: &TypeDefinition,
mut seen: HashSet<CustomTypeKey>,
) -> Result<HashMap<CustomTypeKey, SourceModuleId>, ErrorKind> {
let mut types = HashMap::new();
match ty {
TypeKind::CustomType(type_key) => {
let resolved_ty = resolve_type(type_key, modules)?;
if seen.contains(&resolved_ty) {
return Err(ErrorKind::CyclicalType(type_key.0.clone()));
}
types.insert(type_key.clone(), resolved_ty.1);
seen.insert(resolved_ty.clone());
let resolved = modules
.get(&resolved_ty.1)
.unwrap()
.module
.borrow()
.typedefs
mod_id: SourceModuleId,
) -> Vec<CustomTypeKey> {
match &typedef.kind {
crate::mir::TypeDefinitionKind::Struct(struct_type) => {
let typenames = struct_type
.0
.iter()
.find(|t| t.name == resolved_ty.0)
.ok_or(ErrorKind::NoSuchTypeInModule(type_key.clone()))
.cloned()?;
match resolved.kind {
TypeDefinitionKind::Struct(StructType(fields)) => {
for field in fields {
types.extend(resolve_types_recursively(&field.1, modules, seen.clone())?);
}
.filter(|t| matches!(t.1, TypeKind::CustomType(..)))
.map(|t| match &t.1 {
TypeKind::CustomType(CustomTypeKey(t, _)) => t,
_ => panic!(),
})
.cloned()
.collect::<Vec<_>>();
for typename in typenames {
if seen.contains(&CustomTypeKey(typename.clone(), mod_id)) {
continue;
}
let inner = find_inner_types(typedef, seen.clone(), mod_id);
seen.insert(CustomTypeKey(typename, mod_id));
seen.extend(inner);
}
seen.into_iter().collect()
}
TypeKind::Array(type_kind, _) => types.extend(resolve_types_recursively(&type_kind, modules, seen.clone())?),
TypeKind::Borrow(type_kind, _) => types.extend(resolve_types_recursively(&type_kind, modules, seen.clone())?),
TypeKind::UserPtr(type_kind) => types.extend(resolve_types_recursively(&type_kind, modules, seen.clone())?),
TypeKind::CodegenPtr(type_kind) => types.extend(resolve_types_recursively(&type_kind, modules, seen.clone())?),
_ => {}
}
Ok(types)
}

View File

@ -177,7 +177,12 @@ impl mir::Expression {
globals.extend(expression.gen_macros(data, state, map));
globals.extend(expression1.gen_macros(data, state, map));
}
mir::ExprKind::AssociatedFunctionCall(_, FunctionCall { parameters, .. }) => {
mir::ExprKind::AssociatedFunctionCall(
_,
FunctionCall {
parameters, is_macro, ..
},
) => {
for expression in parameters {
globals.extend(expression.gen_macros(data, state, map));
}

View File

@ -55,28 +55,6 @@ impl Metadata {
pub fn contains(&self, token_idx: usize) -> bool {
return token_idx >= self.range.start && token_idx <= self.range.end;
}
pub fn after(&self, cutoff: usize) -> Metadata {
Metadata {
source_module_id: self.source_module_id,
range: TokenRange {
start: cutoff.max(self.range.start),
end: cutoff.max(self.range.end),
},
position: None,
}
}
pub fn before(&self, cutoff: usize) -> Metadata {
Metadata {
source_module_id: self.source_module_id,
range: TokenRange {
start: cutoff.min(self.range.start),
end: cutoff.min(self.range.end),
},
position: None,
}
}
}
impl std::ops::Add for Metadata {
@ -105,7 +83,7 @@ impl TokenRange {
#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct CustomTypeKey(pub String, pub SourceModuleId);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum TypeKind {
Bool,
I8,
@ -120,7 +98,7 @@ pub enum TypeKind {
U128,
Void,
F16,
F16B,
F32B,
F32,
F64,
F128,
@ -220,7 +198,7 @@ impl Literal {
Literal::Vague(VagueLiteral::Number(_)) => TypeKind::Vague(VagueType::Integer),
Literal::Vague(VagueLiteral::Decimal(_)) => TypeKind::Vague(VagueType::Decimal),
Literal::F16(_) => TypeKind::F16,
Literal::F32B(_) => TypeKind::F16B,
Literal::F32B(_) => TypeKind::F32B,
Literal::F32(_) => TypeKind::F32,
Literal::F64(_) => TypeKind::F64,
Literal::F80(_) => TypeKind::F80,
@ -278,7 +256,7 @@ pub enum ReturnKind {
pub struct NamedVariableRef(pub TypeKind, pub String, pub Metadata);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Import(pub Vec<(String, Metadata)>, pub Metadata);
pub struct Import(pub Vec<String>, pub Metadata);
#[derive(Debug, Clone)]
pub enum ExprKind {
@ -286,7 +264,7 @@ pub enum ExprKind {
Indexed(Box<Expression>, TypeKind, Box<Expression>),
Accessed(Box<Expression>, TypeKind, String, Metadata),
Array(Vec<Expression>),
Struct(CustomTypeKey, Vec<(String, Expression, Metadata)>),
Struct(String, Vec<(String, Expression, Metadata)>),
Literal(Literal),
BinOp(BinaryOperator, Box<Expression>, Box<Expression>, TypeKind),
FunctionCall(FunctionCall),
@ -318,7 +296,6 @@ pub struct FunctionCall {
#[derive(Debug)]
pub struct FunctionDefinition {
pub name: String,
pub documentation: Option<String>,
pub linkage_name: Option<String>,
/// Whether this function is visible to outside modules
pub is_pub: bool,
@ -328,7 +305,6 @@ pub struct FunctionDefinition {
pub parameters: Vec<FunctionParam>,
pub kind: FunctionDefinitionKind,
pub source: Option<SourceModuleId>,
pub signature_meta: Metadata,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
@ -338,16 +314,6 @@ pub struct FunctionParam {
pub meta: Metadata,
}
impl FunctionParam {
pub fn from<T: Into<String>>(name: T, ty: TypeKind) -> FunctionParam {
FunctionParam {
name: name.into(),
ty: ty,
meta: Default::default(),
}
}
}
pub enum SelfKind {
Borrow,
MutBorrow,
@ -374,7 +340,11 @@ impl FunctionDefinition {
}
pub fn signature(&self) -> Metadata {
self.signature_meta
match &self.kind {
FunctionDefinitionKind::Local(_, metadata) => metadata.clone(),
FunctionDefinitionKind::Extern(_) => Metadata::default(),
FunctionDefinitionKind::Intrinsic(_) => Metadata::default(),
}
}
}

View File

@ -450,10 +450,6 @@ impl Module {
for function in &mut self.functions {
function.pass(pass, state, &mut scope.inner(), self.module_id)?;
}
for (_, function) in &mut self.associated_functions {
function.pass(pass, state, &mut scope.inner(), self.module_id)?;
}
Ok(())
}
}

View File

@ -141,7 +141,7 @@ impl TypeKind {
| TypeKind::U64
| TypeKind::U128
| TypeKind::F16
| TypeKind::F16B
| TypeKind::F32B
| TypeKind::F32
| TypeKind::F64
| TypeKind::F80
@ -153,7 +153,7 @@ impl TypeKind {
TypeKind::Vague(Vague::Unknown) => Ok(TypeKind::Vague(Vague::Decimal)),
TypeKind::Vague(Vague::Decimal) => Ok(TypeKind::Vague(Vague::Decimal)),
TypeKind::F16
| TypeKind::F16B
| TypeKind::F32B
| TypeKind::F32
| TypeKind::F64
| TypeKind::F80
@ -207,7 +207,7 @@ impl TypeKind {
},
(TypeKind::Vague(Vague::Decimal), other) | (other, TypeKind::Vague(Vague::Decimal)) => match other {
TypeKind::F16
| TypeKind::F16B
| TypeKind::F32B
| TypeKind::F32
| TypeKind::F64
| TypeKind::F80
@ -333,7 +333,7 @@ impl TypeKind {
.map(|_| ())
.ok_or(ErrorKind::NoSuchType(
custom_type_key.0.clone(),
custom_type_key.1.clone(),
state.module_id.unwrap(),
))
}
TypeKind::Borrow(type_kind, _) => type_kind.is_known(state),

View File

@ -84,7 +84,7 @@ impl<'t> Pass for TypeCheck<'t> {
fn check_typedefs_for_recursion<'a, 'b>(
defmap: &'b HashMap<&'a String, &'b TypeDefinition>,
typedef: &'b TypeDefinition,
seen: HashSet<String>,
mut seen: HashSet<String>,
state: &mut TypecheckPassState,
) {
match &typedef.kind {
@ -194,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))),
}
}
}
@ -621,20 +621,21 @@ impl Expression {
// Update possibly resolved type
Ok(true_ty)
} else {
Err(ErrorKind::NoSuchField(key.0.clone()))
Err(ErrorKind::NoSuchField(field_name.clone()))
}
} else {
Err(ErrorKind::TriedAccessingNonStruct(expr_ty))
}
}
ExprKind::Struct(struct_key, items) => {
ExprKind::Struct(struct_name, items) => {
let type_key = CustomTypeKey(struct_name.clone(), state.module_id.unwrap());
let struct_def = state
.scope
.get_struct_type(&struct_key)
.ok_or(ErrorKind::NoSuchType(struct_key.0.clone(), struct_key.1))?
.get_struct_type(&type_key)
.ok_or(ErrorKind::NoSuchType(struct_name.clone(), type_key.1))?
.clone();
let mut expected_fields = if let Some(struct_ty) = state.scope.get_struct_type(&struct_key) {
let mut expected_fields = if let Some(struct_ty) = state.scope.get_struct_type(&type_key) {
struct_ty.0.iter().map(|f| f.0.clone()).collect()
} else {
HashSet::new()
@ -645,7 +646,7 @@ impl Expression {
let expected_ty = state.or_else(
struct_def
.get_field_ty(field_name)
.ok_or(ErrorKind::NoSuchField(format!("{:?}.{}", struct_key, field_name))),
.ok_or(ErrorKind::NoSuchField(format!("{}.{}", struct_name, field_name))),
&TypeKind::Vague(VagueType::Unknown),
field_expr.1,
);
@ -667,7 +668,7 @@ impl Expression {
self.1,
);
Ok(TypeKind::CustomType(struct_key.clone()))
Ok(TypeKind::CustomType(type_key))
}
ExprKind::Borrow(expr, mutable) => {
let hint_t = if let HintKind::Coerce(hint_t) = hint_t {
@ -720,19 +721,15 @@ impl Expression {
expr.resolve_ref(typerefs).cast_into(type_kind)
}
ExprKind::AssociatedFunctionCall(type_kind, function_call) => {
*type_kind = type_kind.or_default()?;
let true_function = state
.scope
.get_associated_function(&pass::AssociatedFunctionKey(
type_kind.clone(),
function_call.name.clone(),
))
.ok_or(ErrorKind::AssocFunctionNotDefined(
function_call.name.clone(),
type_kind.clone(),
));
.ok_or(ErrorKind::FunctionNotDefined(function_call.name.clone()));
if let Some(f) = state.ok(true_function, function_call.meta) {
if let Some(f) = state.ok(true_function, self.1) {
let param_len_given = function_call.parameters.len();
let param_len_expected = f.params.len();
@ -771,7 +768,7 @@ impl Expression {
Ok(function_call.return_type.clone().resolve_ref(typerefs))
}
}
ExprKind::GlobalRef(..) => Ok(self
ExprKind::GlobalRef(global_value, type_kind) => Ok(self
.return_type(typerefs, state.scope.module_id.unwrap())
.map(|r| r.1)
.unwrap()),
@ -806,14 +803,14 @@ impl Literal {
(L::Vague(VagueL::Number(v)), TypeKind::U128) => L::U128(v as u128),
(L::Vague(VagueL::Number(v)), TypeKind::F16) => L::F16(v as f32),
(L::Vague(VagueL::Number(v)), TypeKind::F32) => L::F32(v as f32),
(L::Vague(VagueL::Number(v)), TypeKind::F16B) => L::F32B(v as f32),
(L::Vague(VagueL::Number(v)), TypeKind::F32B) => L::F32B(v as f32),
(L::Vague(VagueL::Number(v)), TypeKind::F64) => L::F64(v as f64),
(L::Vague(VagueL::Number(v)), TypeKind::F80) => L::F80(v as f64),
(L::Vague(VagueL::Number(v)), TypeKind::F128) => L::F128(v as f64),
(L::Vague(VagueL::Number(v)), TypeKind::F128PPC) => L::F128PPC(v as f64),
(L::Vague(VagueL::Decimal(v)), TypeKind::F16) => L::F16(v as f32),
(L::Vague(VagueL::Decimal(v)), TypeKind::F32) => L::F32(v as f32),
(L::Vague(VagueL::Decimal(v)), TypeKind::F16B) => L::F32B(v as f32),
(L::Vague(VagueL::Decimal(v)), TypeKind::F32B) => L::F32B(v as f32),
(L::Vague(VagueL::Decimal(v)), TypeKind::F64) => L::F64(v as f64),
(L::Vague(VagueL::Decimal(v)), TypeKind::F80) => L::F80(v as f64),
(L::Vague(VagueL::Decimal(v)), TypeKind::F128) => L::F128(v as f64),

View File

@ -12,10 +12,9 @@ use std::{
use crate::{
mir::{
implement::TypeCategory,
pass::{AssociatedFunctionKey, ScopeVariable},
BinopDefinition, Block, ExprKind, Expression, FunctionDefinition, FunctionDefinitionKind, IfExpression, Module,
ReturnKind, StmtKind, TypeKind, VagueType, WhileStatement,
BinopDefinition, Block, CustomTypeKey, ExprKind, Expression, FunctionDefinition, FunctionDefinitionKind,
IfExpression, Module, ReturnKind, StmtKind, TypeKind, WhileStatement,
},
util::try_all,
};
@ -383,7 +382,6 @@ impl Expression {
} else {
Vec::new()
};
if binops.len() > 0 {
let binop = unsafe { binops.get_unchecked(0) };
let mut widened_lhs = binop.hands.0.clone();
@ -393,6 +391,9 @@ impl Expression {
widened_rhs = widened_rhs.widen_into(&binop.hands.1);
}
let binop_res = type_refs.from_binop(*op, &lhs_ref, &rhs_ref);
// dbg!(&return_ty);
// dbg!(&binop_res);
// dbg!(&lhs_ref, &rhs_ref, &binops, &widened_lhs, &widened_rhs);
lhs_ref.narrow(&type_refs.from_type(&widened_lhs).unwrap());
rhs_ref.narrow(&type_refs.from_type(&widened_rhs).unwrap());
*return_ty = binop_res.as_type();
@ -545,17 +546,18 @@ impl Expression {
*type_kind = elem_ty.as_type().clone();
Ok(elem_ty)
}
None => Ok(type_refs.from_type(&TypeKind::Vague(VagueType::Unknown)).unwrap()),
None => Err(ErrorKind::NoSuchField(field_name.clone())),
}
}
_ => Ok(type_refs.from_type(&TypeKind::Vague(VagueType::Unknown)).unwrap()),
_ => Err(ErrorKind::TriedAccessingNonStruct(kind)),
}
}
ExprKind::Struct(struct_key, fields) => {
ExprKind::Struct(struct_name, fields) => {
let type_key = CustomTypeKey(struct_name.clone(), state.module_id.unwrap());
let expected_struct_ty = state
.scope
.get_struct_type(&struct_key)
.ok_or(ErrorKind::NoSuchType(struct_key.0.clone(), state.module_id.unwrap()))?
.get_struct_type(&type_key)
.ok_or(ErrorKind::NoSuchType(struct_name.clone(), state.module_id.unwrap()))?
.clone();
for field in fields {
if let Some(expected_field_ty) = expected_struct_ty.get_field_ty(&field.0) {
@ -565,12 +567,12 @@ impl Expression {
}
} else {
state.ok::<_, Infallible>(
Err(ErrorKind::NoSuchField(format!("{:?}.{}", struct_key, field.0))),
Err(ErrorKind::NoSuchField(format!("{}.{}", struct_name, field.0))),
field.1 .1,
);
}
}
Ok(type_refs.from_type(&TypeKind::CustomType(struct_key.clone())).unwrap())
Ok(type_refs.from_type(&TypeKind::CustomType(type_key.clone())).unwrap())
}
ExprKind::Borrow(expr, mutable) => {
// Find variable type
@ -625,27 +627,19 @@ impl Expression {
if backing_var.is_some() {
if let TypeKind::Borrow(inner, _) = type_kind {
let ty_cat = inner.category();
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 if ty_cat == TypeCategory::Integer || ty_cat == TypeCategory::Real {
if let ExprKind::Borrow(val, _) = &first_param.0 {
*first_param = *val.clone();
}
*type_kind = *inner.clone();
}
}
} else {
if let ExprKind::Borrow(val, _) = &first_param.0 {
*first_param = *val.clone();
}
if let TypeKind::Borrow(inner_ty, _) = type_kind {
*type_kind = *inner_ty.clone();
}
let ExprKind::Borrow(val, _) = &first_param.0 else {
panic!()
};
*first_param = *val.clone();
}
if !is_mutable {
@ -661,13 +655,9 @@ impl Expression {
.ok_or(ErrorKind::AssocFunctionNotDefined(
function_call.name.clone(),
type_kind.clone(),
))
))?
.clone();
let Ok(fn_call) = fn_call else {
return Ok(type_refs.from_type(&Vague(Unknown)).unwrap());
};
// Infer param expression types and narrow them to the
// expected function parameters (or Unknown types if too
// many were provided)
@ -683,7 +673,7 @@ impl Expression {
// Provide function return type
Ok(type_refs.from_type(&fn_call.ret).unwrap())
}
ExprKind::GlobalRef(..) => Ok(self
ExprKind::GlobalRef(global_value, type_kind) => Ok(self
.return_type(type_refs.types, state.scope.module_id.unwrap())
.map(|r| type_refs.from_type(&r.1).unwrap())
.unwrap()),

View File

@ -97,9 +97,6 @@ pub struct TypeRefs {
/// Indirect ID-references, referring to hints-vec
pub(super) type_refs: RefCell<Vec<TypeIdRef>>,
pub(super) binop_types: BinopMap,
/// Used when the real typerefs are not available, and any TypeRefs need to
/// be resolved as Unknown.
pub unknown_typerefs: bool,
}
impl std::fmt::Display for TypeRefs {
@ -125,14 +122,6 @@ impl TypeRefs {
hints: Default::default(),
type_refs: Default::default(),
binop_types: binops,
unknown_typerefs: false,
}
}
pub fn unknown() -> TypeRefs {
TypeRefs {
unknown_typerefs: true,
..Default::default()
}
}
@ -188,12 +177,8 @@ impl TypeRefs {
}
pub fn retrieve_typeref(&self, idx: usize) -> Option<TypeRefKind> {
if !self.unknown_typerefs {
let inner_idx = unsafe { *self.recurse_type_ref(idx).borrow() };
self.hints.borrow().get(inner_idx).cloned()
} else {
Some(TypeRefKind::Direct(TypeKind::Vague(VagueType::Unknown)))
}
let inner_idx = unsafe { *self.recurse_type_ref(idx).borrow() };
self.hints.borrow().get(inner_idx).cloned()
}
pub fn retrieve_wide_type(&self, idx: usize, seen: &mut HashSet<usize>) -> Option<TypeKind> {
@ -308,23 +293,12 @@ impl<'outer> ScopeTypeRefs<'outer> {
let lhs_resolved = lhs.resolve_ref(self.types);
let rhs_resolved = rhs.resolve_ref(self.types);
let mut binops = self
let binops = self
.types
.binop_types
.iter()
.filter(|b| b.1.operator == op && b.1.return_ty == *ty)
.collect::<Vec<_>>();
// Sort binops by lhs and then rhs
binops.sort_by(|a, b| {
let lhs = a.1.hands.0.cmp(&b.1.hands.0);
let rhs = a.1.hands.1.cmp(&b.1.hands.1);
match lhs {
std::cmp::Ordering::Equal => rhs,
_ => lhs,
}
});
for binop in binops {
if let (Ok(lhs_narrow), Ok(rhs_narrow)) = (
lhs_resolved.narrow_into(&binop.1.hands.0),
@ -498,17 +472,6 @@ impl<'outer> ScopeTypeRefs<'outer> {
}
}
}
// Sort binops by lhs and then rhs
applying_binops.sort_by(|a, b| {
let lhs = a.hands.0.cmp(&b.hands.0);
let rhs = a.hands.1.cmp(&b.hands.1);
match lhs {
std::cmp::Ordering::Equal => rhs,
_ => lhs,
}
});
applying_binops
}
}

View File

@ -14,9 +14,9 @@ mod util;
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, None, &mut map, None));
let (id, tokens) = assert_err(parse_module(source, name, &mut map));
let module = assert_err(assert_err(compile_module(id, tokens, &mut map, None, true)).map_err(|(_, e)| e));
let module = assert_err(compile_module(id, tokens, &mut map, None, true));
let mut mir_context = mir::Context::from(vec![module], Default::default());
assert_err(perform_all_passes(&mut mir_context, &mut map));
@ -169,8 +169,3 @@ fn mutable_inner_functions() {
fn cpu_raytracer_compiles() {
test_compile(include_str!("../../examples/cpu_raytracer.reid"), "test");
}
#[test]
fn loop_edge_case_functions() {
test(include_str!("../../examples/loop_edge_case.reid"), "test", Some(0));
}