Compare commits

...

65 Commits

Author SHA1 Message Date
84233c8f49 Add reid.png 2025-08-17 18:13:11 +03:00
07b0403e9b Make exit code propagate 2025-08-17 17:48:40 +03:00
28934ea6fc Add run-subcommand 2025-08-17 17:44:44 +03:00
7dd06567bb Make some logs better 2025-08-17 17:33:11 +03:00
77107ef95e Fix a bunch of warnings 2025-08-17 17:19:03 +03:00
1d0fd9dd0a Change all previous weird logs to log:: logs 2025-08-17 17:16:58 +03:00
3a5766186a Add main.rs and make usage a little nicer 2025-08-17 17:07:19 +03:00
8d8d6ac336 Clean up grammar by a lot 2025-08-17 16:22:06 +03:00
fa95da97ae Remove unused debug-logs 2025-08-16 17:38:29 +03:00
407c681cb6 Add ordering for how types are chosen for binops 2025-08-16 17:36:37 +03:00
2dd482c9c2 Fix for-loop generation 2025-08-16 16:25:08 +03:00
1a5823c59c Add failing loop edge case example 2025-08-16 16:14:24 +03:00
3ebe16b80b Fix docs for associated function calls 2025-08-14 17:59:17 +03:00
7a6f99479a Add documentation to function autocompletes 2025-08-14 17:48:49 +03:00
f4bce14299 Add documentation for intrinsics 2025-08-14 17:45:07 +03:00
aa1de16f4c Add documentation to std 2025-08-14 17:34:08 +03:00
89b6fc1a71 Improve invoked ty hover 2025-08-14 17:10:56 +03:00
ceee2f286a Improve function hover texts 2025-08-14 17:05:32 +03:00
dcb4e76a40 Add documentation to hovers 2025-08-14 16:52:45 +03:00
6dccab8b12 Add hover kind 2025-08-14 16:39:31 +03:00
7b4f38406d Add some initial documentation support for functions 2025-08-14 16:28:33 +03:00
9e37ae7aac Re-add equals for floats 2025-08-14 14:40:14 +03:00
3e0367fb1e Disallow == between floats 2025-08-13 12:54:41 +03:00
685520a6cf Fix floating-point modulo 2025-08-13 12:47:50 +03:00
0203213b28 Fix a bunch of errors in reid-llvm-lib 2025-08-12 21:21:10 +03:00
c23160bc32 Fix inconsitent multiple typedefs 2025-08-12 21:02:32 +03:00
fe6fe6c435 Fix linker setting types as dependencies when they are also dependencies 2025-08-12 20:47:27 +03:00
3e85ed2705 Remove unnecessary dbg! 2025-08-12 20:35:28 +03:00
d50a748884 Add contributes breakpoints 2025-08-08 16:20:22 +03:00
c44d588b30 Fix a1.reid 2025-08-08 16:01:26 +03:00
0e23ab4636 Add back a type in linker that I thought unnecessary 2025-08-07 21:40:57 +03:00
ce2473a715 Add is_null to documentation 2025-08-07 21:33:50 +03:00
1a8535516c Add nullptr comparison 2025-08-07 21:32:30 +03:00
e1ac019ecd Fix codegen typedef sorting, again, oops 2025-08-07 18:43:21 +03:00
82b67dfaaa Fix typedef ordering in codegen 2025-08-06 22:01:28 +03:00
a5c7823a29 Resolve types recursively instead of just on-surface 2025-08-06 21:36:33 +03:00
3dba4a79ff Move one type resolution to a better place 2025-08-06 21:04:31 +03:00
2a8842658d Fix linker type importing 2025-08-06 21:01:05 +03:00
8d32f2bbad Add a lot more references to types in places 2025-08-05 23:57:45 +03:00
0ee9d3bf7d Fix linker type-replacement for imported functions 2025-08-05 22:23:57 +03:00
1dd4bbbb05 Fix lsp compilation problem 2025-08-05 22:04:02 +03:00
8cbc65422e Fix more warnings 2025-08-05 22:03:14 +03:00
3cd4121951 Fix linker cyclical fields checker 2025-08-05 22:02:19 +03:00
c84954480b Fix loads of warnings 2025-08-05 22:00:11 +03:00
67106ea17b Fix struct gep naming in LLVM 2025-08-05 21:57:07 +03:00
015c111b29 Fix modules needing to be evaluated in order in codegen 2025-08-05 21:55:04 +03:00
aad3c93068 Fix imports not setting their importer-value correctly 2025-08-05 21:45:40 +03:00
1ba0de442a Fix linker working with recursive imports 2025-08-05 21:03:53 +03:00
1c3386bc9a Fix triple-importing 2025-08-04 23:46:46 +03:00
8a178387ca Allow initializing foreign structs as well 2025-08-04 22:33:06 +03:00
b93b7aa52b Fix struct fields not being linked correctly for foreign types 2025-08-04 22:24:03 +03:00
1275063dc2 Pass associated functions as well as functions 2025-08-04 22:09:26 +03:00
02522dd36d Fix typechecker crashing unexpectedly with vague type 2025-08-04 21:18:14 +03:00
5b7c3d5b3a Cleanup 2025-08-04 21:14:32 +03:00
70a968d7a0 Add sqrt 2025-08-04 19:48:14 +03:00
a9d5a4d03b Fix token ranges for for-loops 2025-08-04 18:37:19 +03:00
11d93e4adf Fix length-intrinsic 2025-08-04 18:12:01 +03:00
c2f6cfb8e6 Update intrinsics documentation 2025-08-04 18:02:28 +03:00
34612e98ce Add documentation about memcpy-intrinsic 2025-08-04 17:52:03 +03:00
3b4835cff8 Fix and add memcpy-intrinsic 2025-08-04 17:51:32 +03:00
627d1bcfa5 Add a large amount of new intrinsics 2025-08-04 17:51:32 +03:00
ae6796acfc Fix calling convention for integers/real-numbers 2025-08-04 17:51:32 +03:00
5d19d38682 Add intrinsic min/max to integers and floats 2025-08-04 17:51:32 +03:00
4a33e7d123 Fix binop parsing from last commit 2025-08-04 17:51:32 +03:00
79ecb3b9ba Fix token-ranges for derefs and binop rhs 2025-08-04 14:30:36 +03:00
55 changed files with 3093 additions and 1117 deletions

304
Cargo.lock generated
View File

@ -17,12 +17,59 @@ 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"
@ -78,6 +125,12 @@ 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"
@ -99,6 +152,20 @@ 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"
@ -108,6 +175,12 @@ 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"
@ -250,12 +323,42 @@ 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"
@ -374,12 +477,33 @@ 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"
@ -422,6 +546,12 @@ 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"
@ -656,8 +786,11 @@ checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a"
name = "reid"
version = "1.0.0-beta.4"
dependencies = [
"argh",
"colored",
"log",
"reid-lib",
"stderrlog",
"thiserror",
]
@ -667,6 +800,8 @@ version = "1.0.0-beta.1"
dependencies = [
"dashmap 6.1.0",
"reid",
"serde",
"serde_json",
"socket",
"tokio",
"tower-lsp",
@ -680,12 +815,24 @@ dependencies = [
"thiserror",
]
[[package]]
name = "rust-fuzzy-search"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a157657054ffe556d8858504af8a672a054a6e0bd9e8ee531059100c0fa11bb2"
[[package]]
name = "rustc-demangle"
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"
@ -800,6 +947,19 @@ 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"
@ -822,6 +982,15 @@ 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"
@ -842,6 +1011,15 @@ 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"
@ -1017,6 +1195,132 @@ 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

@ -7,16 +7,6 @@ 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; _]`
@ -43,15 +33,155 @@ 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,10 +64,6 @@ _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

@ -2,11 +2,14 @@ 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
}
@ -18,17 +21,22 @@ 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;
print(from_str("value: ") + list[4]);
return i32::sizeof() as u32;

View File

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

View File

@ -0,0 +1,10 @@
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

@ -0,0 +1,12 @@
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

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

View File

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

View File

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

View File

@ -0,0 +1,15 @@
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

@ -0,0 +1,4 @@
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 --example cli $@ && \
cargo run -p reid -- $@ && \
./$BINARY ; echo "Return value: ""$?"
## Command from: clang -v hello.o -o test

View File

@ -12,19 +12,19 @@ use crate::{
util::match_types,
};
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
#[derive(Clone, Hash, Copy, PartialEq, Eq, PartialOrd)]
pub struct ModuleValue(pub(crate) usize);
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
#[derive(Clone, Hash, Copy, PartialEq, Eq, PartialOrd)]
pub struct TypeValue(pub(crate) ModuleValue, pub(crate) usize);
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
#[derive(Clone, Hash, Copy, PartialEq, Eq, PartialOrd)]
pub struct FunctionValue(pub(crate) ModuleValue, pub(crate) usize);
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
#[derive(Clone, Hash, Copy, PartialEq, Eq, PartialOrd)]
pub struct BlockValue(pub(crate) FunctionValue, pub(crate) usize);
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
#[derive(Clone, Hash, Copy, PartialEq, Eq, PartialOrd)]
pub struct InstructionValue(pub(crate) BlockValue, pub(crate) usize);
#[derive(Debug, Clone, Hash, Copy, PartialEq, Eq)]
@ -276,7 +276,7 @@ impl Builder {
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)
Err(ErrorKind::BlockAlreadyTerminated)
} else {
block.data.terminator = Some(value);
Ok(())
@ -295,7 +295,7 @@ impl Builder {
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)
Err(ErrorKind::BlockTerminatorLocated)
} else {
block.data.terminator_location = Some(location);
Ok(())
@ -409,84 +409,120 @@ impl Builder {
if match_types(&lhs, &rhs, &self)?.category().integer() {
Ok(())
} else {
Err(ErrorKind::Null)
Err(ErrorKind::TypeNotInteger(lhs, lhs.get_type(&self)?))
}
}
Instr::FAdd(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
Ok(())
} else {
Err(ErrorKind::Null)
Err(ErrorKind::TypeWrongCategory(
lhs,
lhs.get_type(&self)?,
TypeCategory::Real,
))
}
}
Instr::Sub(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category().integer() {
Ok(())
} else {
Err(ErrorKind::Null)
Err(ErrorKind::TypeNotInteger(lhs, lhs.get_type(&self)?))
}
}
Instr::FSub(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
Ok(())
} else {
Err(ErrorKind::Null)
Err(ErrorKind::TypeWrongCategory(
lhs,
lhs.get_type(&self)?,
TypeCategory::Real,
))
}
}
Instr::Mul(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category().integer() {
Ok(())
} else {
Err(ErrorKind::Null)
Err(ErrorKind::TypeNotInteger(lhs, lhs.get_type(&self)?))
}
}
Instr::FMul(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
Ok(())
} else {
Err(ErrorKind::Null)
Err(ErrorKind::TypeWrongCategory(
lhs,
lhs.get_type(&self)?,
TypeCategory::Real,
))
}
}
Instr::UDiv(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::UnsignedInteger {
Ok(())
} else {
Err(ErrorKind::Null)
Err(ErrorKind::TypeWrongCategory(
lhs,
lhs.get_type(&self)?,
TypeCategory::UnsignedInteger,
))
}
}
Instr::SDiv(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::SignedInteger {
Ok(())
} else {
Err(ErrorKind::Null)
Err(ErrorKind::TypeWrongCategory(
lhs,
lhs.get_type(&self)?,
TypeCategory::SignedInteger,
))
}
}
Instr::FDiv(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
Ok(())
} else {
Err(ErrorKind::Null)
Err(ErrorKind::TypeWrongCategory(
lhs,
lhs.get_type(&self)?,
TypeCategory::Real,
))
}
}
Instr::URem(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::UnsignedInteger {
Ok(())
} else {
Err(ErrorKind::Null)
Err(ErrorKind::TypeWrongCategory(
lhs,
lhs.get_type(&self)?,
TypeCategory::UnsignedInteger,
))
}
}
Instr::SRem(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::SignedInteger {
Ok(())
} else {
Err(ErrorKind::Null)
Err(ErrorKind::TypeWrongCategory(
lhs,
lhs.get_type(&self)?,
TypeCategory::SignedInteger,
))
}
}
Instr::FRem(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
Ok(())
} else {
Err(ErrorKind::Null)
Err(ErrorKind::TypeWrongCategory(
lhs,
lhs.get_type(&self)?,
TypeCategory::Real,
))
}
}
Instr::And(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
@ -497,7 +533,7 @@ impl Builder {
if t.category().comparable() || !t.category().integer() {
Ok(())
} else {
Err(ErrorKind::Null) // TODO error: Types not comparable
Err(ErrorKind::TypeNotComparable(lhs, t))
}
}
Instr::FCmp(_, lhs, rhs) => {
@ -505,17 +541,17 @@ impl Builder {
if t.category().comparable() || t.category() != TypeCategory::Real {
Ok(())
} else {
Err(ErrorKind::Null) // TODO error: Types not comparable
Err(ErrorKind::TypeNotComparable(lhs, t))
}
}
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
return Err(ErrorKind::InvalidLenParams(params.len(), param_types.len()));
}
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
return Err(ErrorKind::TypesIncompatible(a.clone(), b.get_type(&self)?));
}
}
Ok(())
@ -528,7 +564,7 @@ impl Builder {
// incoming values come from blocks that are added later
// than the one where this one exists.
let first = iter.next().ok_or(ErrorKind::Null)?;
let first = iter.next().ok_or(ErrorKind::EmptyPhiList)?;
for item in iter {
match_types(first, item, &self)?;
}
@ -541,10 +577,10 @@ impl Builder {
if *ptr_ty_inner == load_ty {
Ok(())
} else {
Err(ErrorKind::Null) // TODO error: inner type mismatch
Err(ErrorKind::TypesIncompatible(*ptr_ty_inner, load_ty))
}
} else {
Err(ErrorKind::Null) // TODO error: not a pointer
Err(ErrorKind::NotPointer(ptr, ptr_ty))
}
}
Instr::Store(ptr, _) => {
@ -552,21 +588,25 @@ impl Builder {
if let Type::Ptr(_) = ty {
Ok(())
} else {
Err(ErrorKind::Null) // TODO error: not a pointer
Err(ErrorKind::NotPointer(ptr, ty))
}
}
Instr::ArrayAlloca(_, val) => {
if val.get_type(self)?.category() == TypeCategory::UnsignedInteger {
Ok(())
} else {
Err(ErrorKind::Null) // TODO error: not a pointer
Err(ErrorKind::TypeWrongCategory(
val,
val.get_type(self)?,
TypeCategory::UnsignedInteger,
))
}
}
Instr::GetElemPtr(ptr_val, _) => {
let ptr_ty = ptr_val.get_type(&self)?;
match ptr_ty {
Type::Ptr(_) => Ok(()),
_ => Err(ErrorKind::Null),
_ => Err(ErrorKind::NotPointer(ptr_val, ptr_ty)),
}
}
Instr::GetStructElemPtr(ptr_val, idx) => {
@ -576,16 +616,16 @@ impl Builder {
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
return Err(ErrorKind::NoSuchField(*ty, idx));
}
}
}
Ok(())
} else {
Err(ErrorKind::Null) // TODO error: not a struct
Err(ErrorKind::NotStruct(ptr_val, *ty))
}
} else {
Err(ErrorKind::Null) // TODO error: not a pointer
Err(ErrorKind::NotPointer(ptr_val, ptr_ty))
}
}
Instr::ExtractValue(val, _) => {
@ -595,7 +635,7 @@ impl Builder {
CustomTypeKind::NamedStruct(_) => Ok(()),
},
Type::Array(_, _) => Ok(()),
_ => Err(ErrorKind::Null),
_ => Err(ErrorKind::NotExtractable(val, val_ty)),
}
}
Instr::Trunc(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
@ -614,6 +654,14 @@ impl Builder {
Instr::ShiftRightArithmetic(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::ShiftLeft(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::GetGlobal(_) => Ok(()),
Instr::IsNull(val) => {
let val_ty = val.get_type(&self)?;
if let Type::Ptr(_) = val_ty {
Ok(())
} else {
Err(ErrorKind::NotPointer(val, val_ty))
}
}
}
}
}
@ -685,7 +733,7 @@ impl InstructionValue {
.params
.get(*nth)
.cloned()
.ok_or(ErrorKind::Null),
.ok_or(ErrorKind::NoSuchParam(self.0.0, *nth)),
Constant(c) => Ok(c.get_type()),
Add(lhs, rhs) => match_types(lhs, rhs, &builder),
FAdd(lhs, rhs) => match_types(lhs, rhs, &builder),
@ -705,7 +753,10 @@ impl InstructionValue {
ICmp(_, _, _) => Ok(Type::Bool),
FCmp(_, _, _) => Ok(Type::Bool),
FunctionCall(function_value, _) => Ok(builder.function_data(function_value).ret),
Phi(values) => values.first().ok_or(ErrorKind::Null).and_then(|v| v.get_type(&builder)),
Phi(values) => values
.first()
.ok_or(ErrorKind::EmptyPhiList)
.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),
@ -747,7 +798,7 @@ impl InstructionValue {
}
}
Type::Array(elem_ty, _) => *elem_ty.clone(),
_ => return Err(ErrorKind::Null),
_ => return Err(ErrorKind::NotExtractable(*instr, instr_ty)),
})
}
Trunc(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
@ -770,13 +821,15 @@ impl InstructionValue {
let kind = builder.get_const_kind(constant);
Ok(kind.get_type())
}
IsNull(_) => Ok(Type::Bool),
}
}
}
fn cast_to(&self, builder: &Builder, ty: &Type) -> CompileResult<Instr> {
self.get_type(builder)?
let own_type = self.get_type(builder)?;
own_type
.cast_instruction(*self, &ty)
.ok_or(ErrorKind::Null)
.ok_or(ErrorKind::ImpossibleCast(own_type, ty.clone()))
}
}

View File

@ -1051,6 +1051,10 @@ impl InstructionHolder {
LLVMBuildShl(module.builder_ref, lhs_val, rhs_val, name.as_ptr())
}
GetGlobal(global_value) => module.globals.get(global_value).unwrap().clone(),
IsNull(instruction_value) => {
let val = module.values.get(&*instruction_value).unwrap().value_ref;
LLVMBuildIsNull(module.builder_ref, val, name.as_ptr())
}
}
};
if let Some(record) = &self.record {

View File

@ -393,6 +393,7 @@ impl Debug for Instr {
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),
Instr::IsNull(_) => write!(f, "is_null"),
}
}
}

View File

@ -0,0 +1,263 @@
use crate::{CompileResult, Type, TypeCategory, builder::Builder};
#[derive(Clone, Debug)]
pub enum LLVMIntrinsic {
Abs(Type),
Max(Type),
Min(Type),
Memcpy(Type),
Sqrt(Type),
PowI(Type, Type),
Pow(Type),
Sin(Type),
Cos(Type),
Tan(Type),
ASin(Type),
ACos(Type),
ATan(Type),
ATan2(Type),
SinH(Type),
CosH(Type),
TanH(Type),
Log(Type),
Log2(Type),
Log10(Type),
Copysign(Type),
Floor(Type),
Ceil(Type),
Trunc(Type),
RoundEven(Type),
Round(Type),
}
impl LLVMIntrinsic {
pub(crate) fn signature(&self, builder: &Builder) -> CompileResult<(String, Vec<Type>, Type)> {
match self {
LLVMIntrinsic::Max(ty) => {
let name = match ty.category() {
TypeCategory::SignedInteger => format!("llvm.smax.{}", ty.llvm_ty_str(builder)),
TypeCategory::UnsignedInteger => format!("llvm.umax.{}", ty.llvm_ty_str(builder)),
TypeCategory::Real => format!("llvm.maximum.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone(), ty.clone()], ty.clone()))
}
LLVMIntrinsic::Min(ty) => {
let name = match ty.category() {
TypeCategory::SignedInteger => format!("llvm.smin.{}", ty.llvm_ty_str(builder)),
TypeCategory::UnsignedInteger => format!("llvm.umin.{}", ty.llvm_ty_str(builder)),
TypeCategory::Real => format!("llvm.minimum.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone(), ty.clone()], ty.clone()))
}
LLVMIntrinsic::Abs(ty) => {
let name = match ty.category() {
TypeCategory::SignedInteger => format!("llvm.abs.{}", ty.llvm_ty_str(builder)),
TypeCategory::UnsignedInteger => format!("llvm.abs.{}", ty.llvm_ty_str(builder)),
TypeCategory::Real => format!("llvm.fabs.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone(), Type::Bool], ty.clone()))
}
LLVMIntrinsic::Memcpy(ty) => {
let name = match ty.category() {
TypeCategory::Ptr => String::from("llvm.memcpy"),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone(), ty.clone(), Type::U64, Type::Bool], Type::Void))
}
LLVMIntrinsic::Sqrt(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.sqrt.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::PowI(ty1, ty2) => {
let name = match (ty1.category(), ty2.category()) {
(TypeCategory::Real, TypeCategory::SignedInteger) => {
format!("llvm.powi.{}.{}", ty1.llvm_ty_str(builder), ty2.llvm_ty_str(builder))
}
(TypeCategory::Real, TypeCategory::UnsignedInteger) => {
format!("llvm.powi.{}.{}", ty1.llvm_ty_str(builder), ty2.llvm_ty_str(builder))
}
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty1.clone(), ty2.clone()], ty1.clone()))
}
LLVMIntrinsic::Pow(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.pow.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone(), ty.clone()], ty.clone()))
}
LLVMIntrinsic::Sin(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.sin.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::Cos(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.cos.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::Tan(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.tan.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::ASin(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.asin.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::ACos(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.acos.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::ATan(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.atan.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::ATan2(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.atan2.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone(), ty.clone()], ty.clone()))
}
LLVMIntrinsic::SinH(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.sinh.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::CosH(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.cosh.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::TanH(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.tanh.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::Log(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.log.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::Log2(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.log2.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::Log10(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.log10.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::Copysign(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.copysign.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::Floor(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.floor.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::Ceil(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.ceil.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::Trunc(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.trunc.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::RoundEven(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.roundeven.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::Round(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.rint.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
}
}
}
impl Type {
fn llvm_ty_str(&self, builder: &Builder) -> String {
match self {
Type::I8 => String::from("i8"),
Type::I16 => String::from("u16"),
Type::I32 => String::from("i32"),
Type::I64 => String::from("i64"),
Type::I128 => String::from("i128"),
Type::U8 => String::from("i8"),
Type::U16 => String::from("i16"),
Type::U32 => String::from("i32"),
Type::U64 => String::from("i64"),
Type::U128 => String::from("i128"),
Type::F16 => String::from("f16"),
Type::F32B => String::from("f32b"),
Type::F32 => String::from("f32"),
Type::F64 => String::from("f64"),
Type::F80 => String::from("x86_fp80"),
Type::F128 => String::from("fp128"),
Type::F128PPC => String::from("ppc_fp128"),
Type::Bool => String::from("i1"),
Type::Void => String::from("void"),
Type::CustomType(type_value) => {
let ty = unsafe { builder.type_data(type_value) };
ty.name.clone()
}
Type::Array(ty, len) => format!("[{} x {}]", len, ty.llvm_ty_str(builder)),
Type::Ptr(_) => String::from("ptr"),
}
}
}

View File

@ -11,12 +11,14 @@ use fmt::PrintableModule;
use crate::{
builder::{ConstantValue, GlobalValue},
debug_information::DebugScopeValue,
intrinsics::LLVMIntrinsic,
};
pub mod builder;
pub mod compile;
pub mod debug_information;
mod fmt;
pub mod intrinsics;
mod pad_adapter;
mod util;
@ -24,6 +26,34 @@ mod util;
pub enum ErrorKind {
#[error("NULL error, should never occur!")]
Null,
#[error("Types {0:?} and {1:?} incompatible")]
TypesIncompatible(Type, Type),
#[error("Phi list of values is empty")]
EmptyPhiList,
#[error("Type {1:?} of value {0:?} is not extractable")]
NotExtractable(InstructionValue, Type),
#[error("Type {0:?} is not castable to {1:?}")]
ImpossibleCast(Type, Type),
#[error("Block is already terminated")]
BlockAlreadyTerminated,
#[error("Block terminator already has a location")]
BlockTerminatorLocated,
#[error("Value {0:?} must be an integer type. Is {1:?}")]
TypeNotInteger(InstructionValue, Type),
#[error("Value {0:?} must be a {2:?} type. Is {1:?}")]
TypeWrongCategory(InstructionValue, Type, TypeCategory),
#[error("Value {0:?} must be comparable, was {1:?}")]
TypeNotComparable(InstructionValue, Type),
#[error("Got {0:?} parameters, expected {1:?}")]
InvalidLenParams(usize, usize),
#[error("Value {0:?} is not a pointer, is {1:?}")]
NotPointer(InstructionValue, Type),
#[error("Value {0:?} is not a struct, is {1:?}")]
NotStruct(InstructionValue, Type),
#[error("Struct {0:?} has no such field as {1:?}")]
NoSuchField(Type, u32),
#[error("Function {0:?} has no such parameter as {1:?}")]
NoSuchParam(FunctionValue, usize),
}
pub type CompileResult<T> = Result<T, ErrorKind>;
@ -95,6 +125,25 @@ impl<'ctx> Module<'ctx> {
}
}
pub fn intrinsic(&self, intrinsic: LLVMIntrinsic) -> CompileResult<FunctionValue> {
unsafe {
let (name, params, ret) = intrinsic.signature(&self.builder)?;
Ok(self.builder.add_function(
&self.value,
FunctionData {
name: name.to_owned(),
linkage_name: Some(name.to_owned()),
ret,
params,
flags: FunctionFlags {
is_extern: true,
..Default::default()
},
},
))
}
}
pub fn custom_type(&self, ty: CustomTypeKind) -> TypeValue {
unsafe {
let (name, kind) = match &ty {
@ -277,6 +326,7 @@ impl Instr {
Instr::ShiftRightArithmetic(..) => "ashr",
Instr::ShiftLeft(..) => "shl",
Instr::GetGlobal(..) => "global",
Instr::IsNull(..) => "is_null",
}
}
}
@ -470,10 +520,13 @@ pub enum Instr {
/// no-op cast because no bits change with this conversion.
BitCast(InstructionValue, Type),
/// Check if the given instruction value is a null pointer
IsNull(InstructionValue),
FunctionCall(FunctionValue, Vec<InstructionValue>),
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd)]
pub enum Type {
I8,
I16,
@ -573,7 +626,7 @@ impl ConstValueKind {
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone)]
pub enum TypeCategory {
SignedInteger,
UnsignedInteger,

View File

@ -5,10 +5,7 @@ use std::{
};
use llvm_sys::{
core::{
LLVMCreateMemoryBufferWithMemoryRange, LLVMDisposeMemoryBuffer, LLVMGetBufferSize,
LLVMGetBufferStart,
},
core::{LLVMCreateMemoryBufferWithMemoryRange, LLVMDisposeMemoryBuffer, LLVMGetBufferSize, LLVMGetBufferStart},
error::LLVMDisposeErrorMessage,
prelude::LLVMMemoryBufferRef,
};
@ -32,9 +29,7 @@ pub fn from_cstring(pointer: *mut c_char) -> Option<String> {
}
fn cstring_to_err(value: *mut c_char) -> Result<(), String> {
from_cstring(value)
.filter(|s| !s.is_empty())
.map_or(Ok(()), |s| Err(s))
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
@ -75,12 +70,8 @@ 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,
);
let buffer =
LLVMCreateMemoryBufferWithMemoryRange(array.as_ptr(), array.len(), into_cstring(name).as_ptr(), 0);
MemoryBufferHolder { buffer }
}
}
@ -113,20 +104,12 @@ impl Drop for MemoryBufferHolder {
/// 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)
}
pub fn match_types(lhs: &InstructionValue, rhs: &InstructionValue, builder: &Builder) -> CompileResult<Type> {
let lhs_t = lhs.get_type(&builder)?;
let rhs_t = rhs.get_type(&builder)?;
if lhs_t == rhs_t {
Ok(lhs_t)
} else {
Err(ErrorKind::Null)
Err(ErrorKind::TypesIncompatible(lhs_t, rhs_t))
}
}

View File

@ -9,3 +9,5 @@ tokio = { version = "1.47.0", features = ["full"] }
tower-lsp = "0.20.0"
reid = { path = "../reid", version = "1.0.0-beta.4", registry="gitea-teascade", features=[] }
dashmap = "6.1.0"
serde = "*"
serde_json = "*"

View File

@ -76,7 +76,6 @@ export function activate(context: ExtensionContext) {
scheme: 'file'
}, {
provideDocumentSemanticTokens: () => {
client.info("hello!");
const builder = new SemanticTokensBuilder();
return builder.build();
}

View File

@ -74,5 +74,19 @@
"indentationRules": {
"increaseIndentPattern": "^((?!\\/\\/).)*(\\{[^}\"'`]*|\\([^)\"'`]*|\\[[^\\]\"'`]*)$",
"decreaseIndentPattern": "^((?!.*?\\/\\*).*\\*/)?\\s*[\\)\\}\\]].*$"
}
},
"colorizedBracketPairs": [
[
"(",
")"
],
[
"[",
"]"
],
[
"{",
"}"
]
]
}

View File

@ -23,7 +23,16 @@
"aliases": [
"Reid"
],
"configuration": "./language-configuration.json"
"configuration": "./language-configuration.json",
"icon": {
"dark": "./reid.png",
"light": "./reid.png"
}
}
],
"breakpoints": [
{
"language": "reid"
}
],
"configuration": {

BIN
reid-lsp/reid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1,8 +1,7 @@
use std::{collections::HashMap, fmt::format, hash::Hash, path::PathBuf};
use std::{collections::HashMap, hash::Hash, path::PathBuf};
use reid::{
ast::{
self, FunctionDefinition,
lexer::{FullToken, Token},
token_stream::TokenRange,
},
@ -85,7 +84,7 @@ impl StaticAnalysis {
#[derive(Debug, Clone)]
pub struct SemanticToken {
pub ty: Option<TypeKind>,
pub hover: Hover,
pub autocomplete: Vec<Autocomplete>,
pub symbol: Option<SymbolId>,
}
@ -93,6 +92,7 @@ pub struct SemanticToken {
#[derive(Debug, Clone)]
pub struct Autocomplete {
pub text: String,
pub documentation: Option<String>,
pub kind: AutocompleteKind,
}
@ -103,6 +103,18 @@ pub enum AutocompleteKind {
Function(Vec<FunctionParam>, TypeKind),
}
#[derive(Debug, Clone)]
pub struct Hover {
pub documentation: Option<String>,
pub kind: Option<HoverKind>,
}
#[derive(Debug, Clone)]
pub enum HoverKind {
Type(TypeKind),
Function(String, Vec<FunctionParam>, TypeKind),
}
impl ToString for AutocompleteKind {
fn to_string(&self) -> String {
match self {
@ -132,6 +144,7 @@ pub struct AnalysisState {
pub symbol_to_token: HashMap<SymbolId, usize>,
module_id: SourceModuleId,
module_name: String,
functions: HashMap<String, SymbolId>,
associated_functions: HashMap<(TypeKind, String), SymbolId>,
@ -148,12 +161,19 @@ impl AnalysisState {
}
impl AnalysisState {
pub fn init_types(&mut self, meta: &mir::Metadata, ty: Option<TypeKind>) {
pub fn set_hover_meta(&mut self, meta: &mir::Metadata, hover: Hover) {
for token in meta.range.start..=meta.range.end {
self.set_hover(token, hover.clone());
}
}
pub fn set_hover(&mut self, token_idx: usize, hover: Hover) {
if let Some(token) = self.map.get_mut(&token_idx) {
token.hover = hover.clone();
} else {
self.map.insert(
token,
token_idx,
SemanticToken {
ty: ty.clone(),
hover: hover.clone(),
autocomplete: Vec::new(),
symbol: Default::default(),
},
@ -168,7 +188,10 @@ impl AnalysisState {
self.map.insert(
token_idx,
SemanticToken {
ty: None,
hover: Hover {
documentation: None,
kind: None,
},
autocomplete: autocomplete.clone(),
symbol: Default::default(),
},
@ -184,7 +207,10 @@ impl AnalysisState {
self.map.insert(
idx,
SemanticToken {
ty: None,
hover: Hover {
documentation: None,
kind: None,
},
autocomplete: Vec::new(),
symbol: Some(symbol),
},
@ -192,12 +218,11 @@ impl AnalysisState {
}
}
pub fn new_symbol(&mut self, definition: usize, kind: SemanticKind, module_id: SourceModuleId) -> SymbolId {
pub fn new_symbol(&mut self, definition: usize, kind: SemanticKind) -> SymbolId {
let id = SymbolId(self.symbol_table.len());
self.symbol_table.push(Symbol {
kind,
definition,
module_id,
_definition: definition,
});
id
}
@ -222,8 +247,7 @@ impl AnalysisState {
#[derive(Debug, Clone)]
pub struct Symbol {
pub kind: SemanticKind,
pub definition: usize,
pub module_id: SourceModuleId,
pub _definition: usize,
}
pub struct AnalysisScope<'a> {
@ -233,6 +257,8 @@ pub struct AnalysisScope<'a> {
types: HashMap<TypeKind, (SourceModuleId, SymbolId)>,
functions: HashMap<String, (SourceModuleId, SymbolId)>,
associated_functions: HashMap<(TypeKind, String), (SourceModuleId, SymbolId)>,
function_hovers: HashMap<String, Hover>,
assoc_function_hovers: HashMap<(TypeKind, String), Hover>,
map: &'a StateMap,
}
@ -245,7 +271,9 @@ impl<'a> AnalysisScope<'a> {
variables: self.variables.clone(),
types: self.types.clone(),
functions: self.functions.clone(),
function_hovers: self.function_hovers.clone(),
associated_functions: self.associated_functions.clone(),
assoc_function_hovers: self.assoc_function_hovers.clone(),
}
}
@ -306,7 +334,6 @@ pub enum SemanticKind {
Type,
Struct,
Comment,
Operator,
Keyword,
Reference(SourceModuleId, SymbolId),
}
@ -328,7 +355,6 @@ impl SemanticKind {
SemanticKind::Property => SemanticTokenType::PROPERTY,
SemanticKind::Struct => SemanticTokenType::STRUCT,
SemanticKind::Comment => SemanticTokenType::COMMENT,
SemanticKind::Operator => SemanticTokenType::OPERATOR,
SemanticKind::Keyword => SemanticTokenType::KEYWORD,
SemanticKind::Default => return None,
SemanticKind::Reference(module_id, symbol_id) => {
@ -360,7 +386,6 @@ impl SemanticKind {
SemanticKind::Property => SemanticTokenModifier::DECLARATION,
SemanticKind::Struct => SemanticTokenModifier::DEFINITION,
SemanticKind::Comment => return None,
SemanticKind::Operator => return None,
SemanticKind::Keyword => return None,
SemanticKind::Reference(..) => SEMANTIC_REFERENCE,
};
@ -421,6 +446,7 @@ pub fn analyze_context(
properties: HashMap::new(),
types: HashMap::new(),
module_id: module.module_id,
module_name: module.name.clone().replace(".reid", ""),
};
let mut scope = AnalysisScope {
@ -430,7 +456,9 @@ pub fn analyze_context(
map,
types: HashMap::new(),
functions: HashMap::new(),
function_hovers: HashMap::new(),
associated_functions: HashMap::new(),
assoc_function_hovers: HashMap::new(),
};
for (i, token) in module.tokens.iter().enumerate() {
@ -475,52 +503,12 @@ pub fn analyze_context(
_ => None,
};
if let Some(semantic) = semantic_token {
let symbol = scope.state.new_symbol(i, semantic, module.module_id);
let symbol = scope.state.new_symbol(i, semantic);
scope.state.set_symbol(i, symbol);
}
}
for import in &module.imports {
scope.state.init_types(&import.1, None);
if let Some((module_name, _)) = import.0.get(0) {
let (import_name, import_meta) = import.0.get(1).cloned().unwrap_or((
String::new(),
mir::Metadata {
source_module_id: module.module_id,
range: reid::ast::token_stream::TokenRange {
start: import.1.range.end - 1,
end: import.1.range.end - 1,
},
position: None,
},
));
let mut autocompletes = Vec::new();
if let Some((_, module)) = context.modules.iter().find(|m| m.1.name == *module_name) {
for function in &module.functions {
if !function.is_pub {
continue;
}
if function.name.starts_with(&import_name) {
autocompletes.push(Autocomplete {
text: function.name.clone(),
kind: AutocompleteKind::Function(function.parameters.clone(), function.return_type.clone()),
});
}
}
for typedef in &module.typedefs {
if typedef.name.starts_with(&import_name) {
autocompletes.push(Autocomplete {
text: typedef.name.clone(),
kind: AutocompleteKind::Type,
});
}
}
}
scope.state.set_autocomplete(import_meta.range.end, autocompletes);
}
}
// First we need to initialize symbols for all types themselves correctly
for typedef in &module.typedefs {
if typedef.source_module != module.module_id {
if let Some(state) = map.get(&typedef.source_module) {
@ -537,9 +525,7 @@ pub fn analyze_context(
let struct_idx = scope
.token_idx(&typedef.meta, |t| matches!(t, Token::Identifier(_)))
.unwrap_or(typedef.meta.range.end);
let struct_symbol = scope
.state
.new_symbol(struct_idx, SemanticKind::Struct, module.module_id);
let struct_symbol = scope.state.new_symbol(struct_idx, SemanticKind::Struct);
scope.state.set_symbol(struct_idx, struct_symbol);
let ty = TypeKind::CustomType(CustomTypeKey(typedef.name.clone(), typedef.source_module));
@ -551,7 +537,7 @@ pub fn analyze_context(
.token_idx(&field.2, |t| matches!(t, Token::Identifier(_)))
.unwrap_or(field.2.range.end);
scope.state.init_types(
scope.state.set_hover_meta(
&Metadata {
source_module_id: field.2.source_module_id,
range: TokenRange {
@ -560,12 +546,13 @@ pub fn analyze_context(
},
position: None,
},
Some(field.1.clone()),
Hover {
kind: Some(HoverKind::Type(field.1.clone())),
documentation: None,
},
);
let field_symbol = scope
.state
.new_symbol(field_idx, SemanticKind::Property, module.module_id);
let field_symbol = scope.state.new_symbol(field_idx, SemanticKind::Property);
scope.state.set_symbol(field_idx, field_symbol);
scope.state.properties.insert(
@ -580,14 +567,64 @@ pub fn analyze_context(
}
}
// Afterwards we can try to assign types correctly to the fields of structs
for typedef in &module.typedefs {
if typedef.source_module != module.module_id {
continue;
}
match &typedef.kind {
mir::TypeDefinitionKind::Struct(StructType(fields)) => {
for field in fields {
let field_idx = scope
.token_idx(&field.2, |t| matches!(t, Token::Identifier(_)))
.unwrap_or(field.2.range.end);
let field_ty_idx = scope
.token_idx(&field.2.after(field_idx + 1), |t| matches!(t, Token::Identifier(_)))
.unwrap_or(field.2.range.end);
let field_ty_symbol = if let Some((source_id, symbol_id)) = scope.types.get(&field.1) {
scope
.state
.new_symbol(field_ty_idx, SemanticKind::Reference(*source_id, *symbol_id))
} else {
scope.state.new_symbol(field_ty_idx, SemanticKind::Type)
};
scope.state.set_hover_meta(
&Metadata {
source_module_id: field.2.source_module_id,
range: TokenRange {
start: field_ty_idx,
end: field_ty_idx,
},
position: None,
},
Hover {
kind: Some(HoverKind::Type(field.1.clone())),
documentation: None,
},
);
scope.state.set_symbol(field_ty_idx, field_ty_symbol);
}
}
}
}
for binop in &module.binop_defs {
if binop.meta.source_module_id == module.module_id {
for param in [&binop.lhs, &binop.rhs] {
scope.state.init_types(&param.meta, Some(param.ty.clone()));
scope.state.set_hover_meta(
&param.meta,
Hover {
kind: Some(HoverKind::Type(param.ty.clone())),
documentation: None,
},
);
let idx = scope
.token_idx(&param.meta, |t| matches!(t, Token::Identifier(_)))
.unwrap_or(param.meta.range.end);
let symbol = scope.state.new_symbol(idx, SemanticKind::Variable, module.module_id);
let symbol = scope.state.new_symbol(idx, SemanticKind::Variable);
scope.state.set_symbol(idx, symbol);
scope.variables.insert(param.name.clone(), symbol);
}
@ -608,6 +645,17 @@ pub fn analyze_context(
scope
.associated_functions
.insert((ty.clone(), function.name.clone()), (source_id, *symbol));
scope.assoc_function_hovers.insert(
(ty.clone(), function.name.clone()),
Hover {
documentation: function.documentation.clone(),
kind: Some(HoverKind::Function(
function.name.clone(),
function.parameters.clone(),
function.return_type.clone(),
)),
},
);
}
}
continue;
@ -617,7 +665,7 @@ pub fn analyze_context(
let idx = scope
.token_idx(&function.signature(), |t| matches!(t, Token::Identifier(_)))
.unwrap_or(function.signature().range.end);
let symbol = scope.state.new_symbol(idx, SemanticKind::Function, module.module_id);
let symbol = scope.state.new_symbol(idx, SemanticKind::Function);
scope.state.set_symbol(idx, symbol);
scope
.state
@ -626,49 +674,74 @@ pub fn analyze_context(
scope
.associated_functions
.insert((ty.clone(), function.name.clone()), (module.module_id, symbol));
scope.assoc_function_hovers.insert(
(ty.clone(), function.name.clone()),
Hover {
documentation: function.documentation.clone(),
kind: Some(HoverKind::Function(
function.name.clone(),
function.parameters.clone(),
function.return_type.clone(),
)),
},
);
}
for param in &function.parameters {
scope.state.init_types(&param.meta, Some(param.ty.clone()));
if param.meta.source_module_id == module.module_id {
let param_idx = scope
.token_idx(&param.meta, |t| matches!(t, Token::Identifier(_)))
.unwrap_or(function.signature().range.end);
let param_symbol = scope
.state
.new_symbol(param_idx, SemanticKind::Variable, module.module_id);
scope.state.set_symbol(param_idx, param_symbol);
scope.variables.insert(param.name.clone(), param_symbol);
for (_, function) in &module.associated_functions {
if let Some(source_id) = function.source {
if source_id != module.module_id {
continue;
}
}
let mut inner_scope = scope.inner();
analyze_function_parameters(module, function, &mut inner_scope);
match &function.kind {
mir::FunctionDefinitionKind::Local(block, _) => analyze_block(context, module, block, &mut scope),
mir::FunctionDefinitionKind::Local(block, _) => analyze_block(context, module, block, &mut inner_scope),
mir::FunctionDefinitionKind::Extern(_) => {}
mir::FunctionDefinitionKind::Intrinsic(_) => {}
};
}
for function in &module.functions {
dbg!(&function.name);
scope.function_hovers.insert(
function.name.clone(),
Hover {
documentation: function.documentation.clone(),
kind: Some(HoverKind::Function(
function.name.clone(),
function.parameters.clone(),
function.return_type.clone(),
)),
},
);
if let Some(source_id) = function.source {
if source_id != module.module_id {
if let Some(state) = map.get(&source_id) {
if let Some(symbol) = state.functions.get(&function.name) {
scope.functions.insert(function.name.clone(), (source_id, *symbol));
}
if let Some(state) = map.get(&source_id) {
if let Some(symbol) = state.functions.get(&function.name) {
scope.functions.insert(function.name.clone(), (source_id, *symbol));
}
}
if source_id != module.module_id {
continue;
}
}
scope
.state
.init_types(&function.signature(), Some(function.return_type.clone()));
scope.state.set_hover_meta(
&function.signature(),
Hover {
kind: Some(HoverKind::Type(function.return_type.clone())),
documentation: None,
},
);
let idx = scope
.token_idx(&function.signature(), |t| matches!(t, Token::Identifier(_)))
.unwrap_or(function.signature().range.end);
let function_symbol = scope.state.new_symbol(idx, SemanticKind::Function, module.module_id);
let function_symbol = scope.state.new_symbol(idx, SemanticKind::Function);
scope.state.set_symbol(idx, function_symbol);
scope.state.functions.insert(function.name.clone(), function_symbol);
scope
@ -677,25 +750,99 @@ pub fn analyze_context(
}
for function in &module.functions {
for param in &function.parameters {
scope.state.init_types(&param.meta, Some(param.ty.clone()));
if param.meta.source_module_id == module.module_id {
let idx = scope
.token_idx(&param.meta, |t| matches!(t, Token::Identifier(_)))
.unwrap_or(function.signature().range.end);
let symbol = scope.state.new_symbol(idx, SemanticKind::Variable, module.module_id);
scope.state.set_symbol(idx, symbol);
scope.variables.insert(param.name.clone(), symbol);
if let Some(source_id) = function.source {
if source_id != module.module_id {
continue;
}
}
let mut inner_scope = scope.inner();
analyze_function_parameters(module, function, &mut inner_scope);
match &function.kind {
mir::FunctionDefinitionKind::Local(block, _) => analyze_block(context, module, block, &mut scope),
mir::FunctionDefinitionKind::Local(block, _) => analyze_block(context, module, block, &mut inner_scope),
mir::FunctionDefinitionKind::Extern(_) => {}
mir::FunctionDefinitionKind::Intrinsic(_) => {}
};
}
for import in &module.imports {
scope.state.set_hover_meta(
&import.1,
Hover {
kind: None,
documentation: None,
},
);
if let Some((module_name, _)) = import.0.get(0) {
let module_idx = scope
.token_idx(&import.1, |t| matches!(t, Token::Identifier(_)))
.unwrap_or(import.1.range.end - 1);
let import_idx = scope
.token_idx(&import.1.after(module_idx + 1), |t| matches!(t, Token::Identifier(_)))
.unwrap_or(import.1.range.end - 1);
let (import_name, import_meta) = import.0.get(1).cloned().unwrap_or((
String::new(),
mir::Metadata {
source_module_id: module.module_id,
range: reid::ast::token_stream::TokenRange {
start: import_idx,
end: import_idx,
},
position: None,
},
));
let mut autocompletes = Vec::new();
if let Some((_, module)) = context.modules.iter().find(|m| m.1.name == *module_name) {
for function in &module.functions {
if !function.is_pub {
continue;
}
autocompletes.push(Autocomplete {
text: function.name.clone(),
documentation: function.documentation.clone(),
kind: AutocompleteKind::Function(function.parameters.clone(), function.return_type.clone()),
});
}
for typedef in &module.typedefs {
autocompletes.push(Autocomplete {
text: typedef.name.clone(),
documentation: None,
kind: AutocompleteKind::Type,
});
}
}
let symbol = if let Some((source_id, symbol_id)) = scope.functions.get(&import_name) {
scope
.state
.new_symbol(import_idx, SemanticKind::Reference(*source_id, *symbol_id))
} else if let Some(module_source) = scope.map.values().find(|s| s.module_name == *module_name) {
if let Some((source_id, symbol_id)) = scope.types.get(&TypeKind::CustomType(CustomTypeKey(
import_name.clone(),
module_source.module_id,
))) {
scope
.state
.new_symbol(import_idx, SemanticKind::Reference(*source_id, *symbol_id))
} else {
scope.state.new_symbol(import_idx, SemanticKind::Default)
}
} else {
scope.state.new_symbol(import_idx, SemanticKind::Default)
};
scope.state.set_symbol(import_idx, symbol);
if let Some(hover) = scope.function_hovers.get(&import_name) {
scope.state.set_hover(import_idx, hover.clone());
}
scope.state.set_autocomplete(import_meta.range.end, autocompletes);
}
}
StaticAnalysis {
tokens: module.tokens.clone(),
state,
@ -703,6 +850,46 @@ pub fn analyze_context(
}
}
pub fn analyze_function_parameters(
module: &mir::Module,
function: &mir::FunctionDefinition,
scope: &mut AnalysisScope,
) {
for param in &function.parameters {
scope.state.set_hover_meta(
&param.meta,
Hover {
kind: Some(HoverKind::Type(param.ty.clone())),
documentation: None,
},
);
if param.meta.source_module_id == module.module_id {
let param_var_idx = scope
.token_idx(&param.meta, |t| matches!(t, Token::Identifier(_)))
.unwrap_or(function.signature().range.end);
let param_var_symbol = scope.state.new_symbol(param_var_idx, SemanticKind::Variable);
scope.state.set_symbol(param_var_idx, param_var_symbol);
scope.variables.insert(param.name.clone(), param_var_symbol);
let param_ty_idx = scope
.token_idx(&param.meta.after(param_var_idx + 1), |t| {
matches!(t, Token::Identifier(_))
})
.unwrap_or(function.signature().range.end);
let param_ty_symbol = scope.state.new_symbol(
param_ty_idx,
match scope.types.get(&param.ty) {
Some((source_id, symbol_id)) => SemanticKind::Reference(*source_id, *symbol_id),
None => SemanticKind::Type,
},
);
scope.state.set_symbol(param_ty_idx, param_ty_symbol);
}
}
}
pub fn analyze_block(
context: &mir::Context,
source_module: &mir::Module,
@ -714,22 +901,38 @@ pub fn analyze_block(
for statement in &block.statements {
match &statement.0 {
mir::StmtKind::Let(named_variable_ref, _, expression) => {
scope.state.init_types(
scope.state.set_hover_meta(
&named_variable_ref.2,
expression
.return_type(&TypeRefs::unknown(), source_module.module_id)
.ok()
.map(|(_, ty)| ty),
Hover {
documentation: None,
kind: expression
.return_type(&TypeRefs::unknown(), source_module.module_id)
.ok()
.map(|(_, ty)| ty)
.map(|t| HoverKind::Type(t)),
},
);
let idx = scope
.token_idx(&named_variable_ref.2, |t| matches!(t, Token::Identifier(_)))
.unwrap_or(named_variable_ref.2.range.end);
let symbol = scope
.state
.new_symbol(idx, SemanticKind::Variable, source_module.module_id);
let symbol = scope.state.new_symbol(idx, SemanticKind::Variable);
scope.state.set_symbol(idx, symbol);
scope.variables.insert(named_variable_ref.1.clone(), symbol);
let ty_idx = scope.token_idx(&named_variable_ref.2.after(idx + 1), |t| {
matches!(t, Token::Identifier(_))
});
if let Some(ty_idx) = ty_idx {
let ty_symbol = if let Some((source_id, symbol_id)) = scope.types.get(&named_variable_ref.0) {
scope
.state
.new_symbol(ty_idx, SemanticKind::Reference(*source_id, *symbol_id))
} else {
scope.state.new_symbol(ty_idx, SemanticKind::Type)
};
scope.state.set_symbol(ty_idx, ty_symbol);
}
analyze_expr(context, source_module, expression, scope);
}
mir::StmtKind::Set(lhs, rhs) => {
@ -758,28 +961,36 @@ pub fn analyze_expr(
expr: &mir::Expression,
scope: &mut AnalysisScope,
) {
scope.state.init_types(
scope.state.set_hover_meta(
&expr.1,
expr.return_type(&TypeRefs::unknown(), source_module.module_id)
.ok()
.map(|(_, t)| t),
Hover {
documentation: None,
kind: expr
.return_type(&TypeRefs::unknown(), source_module.module_id)
.ok()
.map(|(_, t)| HoverKind::Type(t)),
},
);
match &expr.0 {
mir::ExprKind::Variable(var_ref) => {
scope.state.init_types(&var_ref.2, Some(var_ref.0.clone()));
scope.state.set_hover_meta(
&var_ref.2,
Hover {
documentation: None,
kind: Some(HoverKind::Type(var_ref.0.clone())),
},
);
let idx = scope
.token_idx(&var_ref.2, |t| matches!(t, Token::Identifier(_)))
.unwrap_or(var_ref.2.range.end);
let symbol = if let Some(symbol_id) = scope.variables.get(&var_ref.1) {
scope.state.new_symbol(
idx,
SemanticKind::Reference(source_module.module_id, *symbol_id),
source_module.module_id,
)
scope
.state
.new_symbol(idx, SemanticKind::Reference(source_module.module_id, *symbol_id))
} else {
scope.state.new_symbol(idx, SemanticKind::Type, source_module.module_id)
scope.state.new_symbol(idx, SemanticKind::Type)
};
scope.state.set_symbol(idx, symbol);
}
@ -799,9 +1010,10 @@ pub fn analyze_expr(
source_module
.associated_functions
.iter()
.filter(|(t, fun)| *t == accessed_type && fun.name.starts_with(name))
.filter(|(t, _)| *t == accessed_type)
.map(|(_, fun)| Autocomplete {
text: fun.name.clone(),
documentation: fun.documentation.clone(),
kind: AutocompleteKind::Function(fun.parameters.clone(), fun.return_type.clone()),
}),
);
@ -819,23 +1031,20 @@ pub fn analyze_expr(
let field_symbol = if let Some((module_id, symbol_id)) =
scope.find_property(accessed_type.clone(), name.clone())
{
scope.state.new_symbol(
field_idx,
SemanticKind::Reference(module_id, symbol_id),
source_module.module_id,
)
} else {
scope
.state
.new_symbol(field_idx, SemanticKind::Property, source_module.module_id)
.new_symbol(field_idx, SemanticKind::Reference(module_id, symbol_id))
} else {
scope.state.new_symbol(field_idx, SemanticKind::Property)
};
scope.state.set_symbol(field_idx, field_symbol);
if let Some(typedef) = typedef {
autocompletes.extend(match &typedef.kind {
mir::TypeDefinitionKind::Struct(StructType(fields)) => {
fields.iter().filter(|f| f.0.starts_with(name)).map(|f| Autocomplete {
fields.iter().map(|f| Autocomplete {
text: f.0.clone(),
documentation: None,
kind: AutocompleteKind::Field(f.1.clone()),
})
}
@ -855,22 +1064,18 @@ pub fn analyze_expr(
analyze_expr(context, source_module, expr, scope);
}
}
mir::ExprKind::Struct(struct_name, items) => {
let struct_type = TypeKind::CustomType(CustomTypeKey(struct_name.clone(), source_module.module_id));
mir::ExprKind::Struct(key, items) => {
let struct_type = TypeKind::CustomType(key.clone());
let struct_idx = scope
.token_idx(&expr.1, |t| matches!(t, Token::Identifier(_)))
.unwrap_or(expr.1.range.end);
let struct_symbol = if let Some(symbol_id) = scope.state.types.get(&struct_type) {
scope.state.new_symbol(
struct_idx,
SemanticKind::Reference(source_module.module_id, *symbol_id),
source_module.module_id,
)
} else {
let struct_symbol = if let Some((source_id, symbol_id)) = scope.types.get(&struct_type) {
scope
.state
.new_symbol(struct_idx, SemanticKind::Struct, source_module.module_id)
.new_symbol(struct_idx, SemanticKind::Reference(*source_id, *symbol_id))
} else {
scope.state.new_symbol(struct_idx, SemanticKind::Struct)
};
scope.state.set_symbol(struct_idx, struct_symbol);
@ -881,15 +1086,11 @@ pub fn analyze_expr(
let field_symbol =
if let Some(symbol_id) = scope.state.properties.get(&(struct_type.clone(), field_name.clone())) {
scope.state.new_symbol(
field_idx,
SemanticKind::Reference(source_module.module_id, *symbol_id),
source_module.module_id,
)
} else {
scope
.state
.new_symbol(field_idx, SemanticKind::Property, source_module.module_id)
.new_symbol(field_idx, SemanticKind::Reference(source_module.module_id, *symbol_id))
} else {
scope.state.new_symbol(field_idx, SemanticKind::Property)
};
scope.state.set_symbol(field_idx, field_symbol);
@ -899,18 +1100,14 @@ pub fn analyze_expr(
}
mir::ExprKind::Literal(_) => {
if let Some(idx) = scope.token_idx(&expr.1, |t| matches!(t, Token::StringLit(_) | Token::CharLit(_))) {
scope
.state
.new_symbol(idx, SemanticKind::String, source_module.module_id);
scope.state.new_symbol(idx, SemanticKind::String);
} else if let Some(idx) = scope.token_idx(&expr.1, |t| {
matches!(
t,
Token::DecimalValue(_) | Token::HexadecimalValue(_) | Token::OctalValue(_) | Token::BinaryValue(_)
)
}) {
scope
.state
.new_symbol(idx, SemanticKind::Number, source_module.module_id);
scope.state.new_symbol(idx, SemanticKind::Number);
}
}
mir::ExprKind::BinOp(_, lhs, rhs, _) => {
@ -928,17 +1125,17 @@ pub fn analyze_expr(
.token_idx(&meta, |t| matches!(t, Token::Identifier(_)))
.unwrap_or(meta.range.end);
let symbol = if let Some((module_id, symbol_id)) = scope.functions.get(name) {
scope.state.new_symbol(
idx,
SemanticKind::Reference(*module_id, *symbol_id),
source_module.module_id,
)
} else {
scope
.state
.new_symbol(idx, SemanticKind::Function, source_module.module_id)
.new_symbol(idx, SemanticKind::Reference(*module_id, *symbol_id))
} else {
scope.state.new_symbol(idx, SemanticKind::Function)
};
scope.state.set_symbol(idx, symbol);
if let Some(hover) = scope.function_hovers.get(name) {
scope.state.set_hover(idx, hover.clone());
}
}
mir::ExprKind::AssociatedFunctionCall(
ty,
@ -956,35 +1153,55 @@ pub fn analyze_expr(
};
let type_symbol = if let Some((module_id, symbol_id)) = scope.types.get(&invoked_ty) {
scope.state.new_symbol(
type_idx,
SemanticKind::Reference(*module_id, *symbol_id),
source_module.module_id,
)
} else {
scope
.state
.new_symbol(type_idx, SemanticKind::Type, source_module.module_id)
.new_symbol(type_idx, SemanticKind::Reference(*module_id, *symbol_id))
} else {
scope.state.new_symbol(type_idx, SemanticKind::Type)
};
scope.state.set_symbol(type_idx, type_symbol);
scope.state.set_hover(
type_idx,
Hover {
documentation: None,
kind: Some(HoverKind::Type(invoked_ty.clone())),
},
);
let fn_idx = scope
.token_idx(&meta, |t| matches!(t, Token::Identifier(_)))
.unwrap_or(meta.range.end);
let intrinsics = get_intrinsic_assoc_functions(&invoked_ty);
let intrinsic_fn = intrinsics.iter().find(|i| i.name == *name);
let fn_symbol = if let Some((module_id, symbol_id)) =
scope.associated_functions.get(&(invoked_ty.clone(), name.clone()))
{
scope.state.new_symbol(
fn_idx,
SemanticKind::Reference(*module_id, *symbol_id),
source_module.module_id,
)
} else {
scope
.state
.new_symbol(fn_idx, SemanticKind::Function, source_module.module_id)
.new_symbol(fn_idx, SemanticKind::Reference(*module_id, *symbol_id))
} else if let Some(intrinsic) = intrinsic_fn {
let symbol = scope.state.new_symbol(fn_idx, SemanticKind::Function);
scope.state.set_hover(
fn_idx,
Hover {
documentation: intrinsic.documentation.clone(),
kind: Some(HoverKind::Function(
intrinsic.name.clone(),
intrinsic.parameters.clone(),
intrinsic.return_type.clone(),
)),
},
);
symbol
} else {
scope.state.new_symbol(fn_idx, SemanticKind::Function)
};
scope.state.set_symbol(fn_idx, fn_symbol);
if let Some(hover) = scope.assoc_function_hovers.get(&(invoked_ty.clone(), name.clone())) {
scope.state.set_hover(fn_idx, hover.clone());
}
for expr in parameters {
analyze_expr(context, source_module, expr, scope);
@ -992,19 +1209,19 @@ pub fn analyze_expr(
let mut function_autocomplete = source_module
.associated_functions
.iter()
.filter(|(t, fun)| *t == invoked_ty && fun.name.starts_with(name))
.filter(|(t, _)| *t == invoked_ty)
.map(|(_, fun)| Autocomplete {
text: fun.name.clone(),
documentation: fun.documentation.clone(),
kind: AutocompleteKind::Function(fun.parameters.clone(), fun.return_type.clone()),
})
.collect::<Vec<_>>();
function_autocomplete.extend(
get_intrinsic_assoc_functions(&invoked_ty)
.iter()
.filter_map(|(s, f)| f.as_ref().map(|f| (s, f)))
.filter(|(_, fun)| fun.name.starts_with(name))
.map(|(_, fun)| Autocomplete {
.map(|fun| Autocomplete {
text: fun.name.clone(),
documentation: fun.documentation.clone(),
kind: AutocompleteKind::Function(fun.parameters.clone(), fun.return_type.clone()),
})
.collect::<Vec<_>>(),

View File

@ -6,17 +6,18 @@ 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 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, 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,
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,
};
use tower_lsp::{Client, LanguageServer, LspService, Server, jsonrpc};
@ -33,6 +34,12 @@ struct Backend {
module_id_counter: Mutex<SourceModuleId>,
}
#[derive(Serialize, Deserialize, Debug)]
struct CompletionData {
token_idx: usize,
path: PathBuf,
}
#[tower_lsp::async_trait]
impl LanguageServer for Backend {
async fn initialize(&self, _: InitializeParams) -> jsonrpc::Result<InitializeResult> {
@ -52,7 +59,17 @@ impl LanguageServer for Backend {
let capabilities = ServerCapabilities {
hover_provider: Some(HoverProviderCapability::Simple(true)),
completion_provider: Some(CompletionOptions { ..Default::default() }),
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 {
@ -95,7 +112,7 @@ impl LanguageServer for Backend {
async fn initialized(&self, _: InitializedParams) {
self.client
.log_message(MessageType::INFO, "Reid Language Server initialized hello!")
.log_message(MessageType::INFO, "Reid Language Server initialized!")
.await;
}
@ -121,11 +138,28 @@ impl LanguageServer for Backend {
// dbg!(position, token);
let list = if let Some((idx, _)) = token {
if let Some(analysis) = self.analysis.get(&path).unwrap().state.map.get(&idx) {
analysis
if let Some(token_analysis) = self.analysis.get(&path).unwrap().state.map.get(&idx) {
token_analysis
.autocomplete
.iter()
.map(|s| CompletionItem::new_simple(s.text.to_string(), s.kind.to_string()))
.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()
@ -153,7 +187,7 @@ impl LanguageServer for Backend {
None
};
let (range, ty) = if let Some((idx, token)) = token {
let (range, ty, documentation) = if let Some((idx, token)) = token {
if let Some(analysis) = self.analysis.get(&path).unwrap().state.map.get(&idx) {
let start = token.position;
let end = token.position.add(token.token.len() as u32);
@ -167,22 +201,50 @@ impl LanguageServer for Backend {
character: (end.0 as i32 - 1).max(0) as u32,
},
};
if let Some(ty) = analysis.ty.clone() {
(Some(range), format!("{}", ty))
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(),
),
}
} else {
(Some(range), String::from("None type"))
(
Some(range),
String::from("No type"),
analysis.hover.documentation.clone(),
)
}
} else {
(None, String::from("no type"))
(None, String::from("no type"), None)
}
} else {
(None, String::from("no token"))
(None, String::from("no token"), None)
};
let contents = HoverContents::Markup(MarkupContent {
kind: MarkupKind::Markdown,
value: format!("`{ty}`"),
});
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}`"),
})
};
Ok(Some(Hover { contents, range }))
}

View File

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

View File

@ -1,133 +1,52 @@
scopeName: source.reid
patterns:
- include: "#import"
- include: "#expression"
repository:
# function-definition:
# begin: "(fn)\\s*(\\w+)\\(((\\w+)\\s*\\:\\s*(\\w+),?)*\\)\\s*->\\s*(\\w+)\\s*\\{"
# end: "\\}"
# beginCaptures:
# 1:
# name: "keyword.other"
# 2:
# name: "entity.name.function"
# 4:
# name: "entity.name.parameter"
# 5:
# name: "entity.name.type"
# 6:
# name: "entity.name.type"
# patterns:
# - include: "#type"
# - include: "#expression"
import:
begin: "(import)\\s*"
end: ";"
beginCaptures:
1:
name: keyword
endCaptures:
0:
name: punctuation.semi.reid
expression:
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: "#punctiation"
- include: "#punctuation"
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
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"
- 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*
\{
end: "\\}"
captures:
1:
name: keyword.impl.reid
2:
name: entity.name.type
patterns:
- include: "#expression"
binop-impl:
begin: "(impl)\\s+(binop)\\s+\\(((.*)\\s*:\\s*(.*))\\)(.*)\\(((.*)\\s*:\\s*(.*))\\)\\s*->\\s*(\\w+)\\s*\\{"
end: "\\}"
beginCaptures:
1:
name: keyword.impl.reid
2:
name: keyword.impl.reid
4:
name: variable.parameter.binop.reid
5:
name: entity.name.type.parameter.binop.reid
6:
name: keyword.operator.math.reid
8:
name: variable.parameter.binop.reid
9:
name: entity.name.type.parameter.binop.reid
10:
name: entity.name.type.return.binop.reid
patterns:
- include: "#expression"
struct-definition:
begin: "(struct)\\s*(\\w+)\\s*\\{"
end: "\\}"
match: "(struct)\\s*(\\w+)"
captures:
1:
name: keyword.struct.reid
2:
name: entity.name.type
patterns:
- include: "#annotated-identifier"
struct-expression:
begin: "([A-Z]\\w*)\\s*\\{"
begin: "\\b([A-Z]\\w*)\\s*\\{"
end: "\\}"
captures:
1:
@ -136,31 +55,26 @@ repository:
- include: "#expression"
number-literal:
patterns:
- match: "0x[0-9a-fA-F]+(\\.[0-9a-fA-F]+)?"
- match: "\\b0x[0-9a-fA-F]+(\\.[0-9a-fA-F]+)?\\b"
name: "constant.hexadecimal"
- match: "0o[0-7]+(\\.[0-7]+)?"
- match: "\\b0o[0-7]+(\\.[0-7]+)?\\b"
name: "constant.octal"
- match: "0b[01]+(\\.[01]+)?"
- match: "\\b0b[01]+(\\.[01]+)?\\b"
name: "constant.binary"
- match: "[0-9]+(\\.[0-9]+)?"
- match: "\\b[0-9]+(\\.[0-9]+)?\\b"
name: "constant.numeric"
string-literal:
begin: '"'
end: '"'
name: string.quoted.double
patterns:
- match: "\\."
- match: "\\\\\\w"
name: constant.character.escape
block:
begin: "\\{"
end: "\\}"
patterns:
- include: "#expression"
namespace:
match: "(\\w+)(\\:\\:)"
captures:
1:
name: entity.name.function.reid
name: entity.name.namespace.reid
2:
name: keyword.operator.namespace.reid
cast:
@ -171,11 +85,16 @@ 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:
@ -189,28 +108,26 @@ 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: "[A-Z]\\w*"
name: entity.name.type.reid
- match: "\\w+"
- match: "\\b(?:\\w+)\\b"
name: variable.language.reid
keywords:
patterns:
- match: "let|mut|pub|extern"
name: "storage.type.reid"
- match: "if|return"
name: "keyword.control"
- match: "self"
name: "variable.language.self.reid"
- 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
binop:
match: "\\<\\=|\\>\\=|\\=\\=|\\<|\\>|\\*|\\+|\\-|\\^|\\&\\&|\\&"
name: keyword.operator.math.reid
@ -226,7 +143,7 @@ repository:
patterns:
- include: "#expression"
common-type:
match: "u8|u16|u32|u64|u128|i8|i16|i32|i64|i128|bool"
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"
name: entity.name.type.common.reid

View File

@ -7,15 +7,17 @@ edition = "2021"
[features]
default = ["color"]
default = ["color", "cli"]
color = ["colored"]
log_output = []
context_debug = []
cli = ["argh", "stderrlog"]
[dependencies]
## Make it easier to generate errors
thiserror = "1.0.44"
reid-lib = { path = "../reid-llvm-lib", version = "1.0.0-beta.4", registry="gitea-teascade" }
colored = {version = "3.0.0", optional = true}
argh = { version = "0.1.13", optional = true }
stderrlog = { version = "0.6.0", optional = true }
log = "*"
colored = { version = "3.0.0", optional = true }

View File

@ -20,7 +20,6 @@ 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)?;
@ -38,35 +37,29 @@ fn main() -> Result<(), std::io::Error> {
},
CustomIRs { llir, mir },
)) => {
#[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);
}
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!");
#[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.
);
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");
let mut linker = LDRunner::from_command(&linker).with_library("c").with_library("m");
for library in libraries {
linker = linker.with_library(&library);
}
@ -75,8 +68,7 @@ fn main() -> Result<(), std::io::Error> {
Err(e) => panic!("{}", e),
};
} else {
#[cfg(feature = "log_output")]
println!("Please input compiled file path!")
log::error!("Please input compiled file path!")
}
Ok(())
}

View File

@ -3,6 +3,7 @@ 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,
@ -11,6 +12,7 @@ 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),
@ -20,6 +22,8 @@ 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();
@ -33,12 +37,14 @@ 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);
@ -57,6 +63,7 @@ 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)
@ -75,18 +82,22 @@ 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);
}
@ -111,14 +122,17 @@ 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),
@ -128,6 +142,8 @@ 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();
@ -141,6 +157,7 @@ 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);
@ -159,12 +176,14 @@ 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);
}
@ -183,6 +202,7 @@ 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)
@ -201,12 +221,15 @@ 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;
@ -217,6 +240,7 @@ 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,6 +116,7 @@ pub enum Token {
Whitespace(String),
Comment(String),
Doc(String),
Eof,
}
@ -196,6 +197,7 @@ 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(),
}
}
@ -309,13 +311,25 @@ 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);
}
}
Token::Comment(comment)
if doc {
Token::Doc(comment)
} else {
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::{fs::Metadata, path::PathBuf};
use std::path::PathBuf;
use token_stream::TokenRange;
@ -192,6 +192,7 @@ 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>,
@ -207,7 +208,7 @@ pub enum SelfKind {
None,
}
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ReturnType {
Soft,
Hard,

View File

@ -287,9 +287,9 @@ impl Parse for PrimaryExpression {
stream.get_range().unwrap(),
)
} else if let Some(Token::Star) = stream.peek() {
stream.next(); // Consume Et
stream.next(); // Consume Star
apply_inner(stream.parse()?, |e| {
Expression(Kind::Deref(Box::new(e.0)), stream.get_range().unwrap())
Expression(Kind::Deref(Box::new(e.0.clone())), e.0 .1)
})
} else if let Ok(unary) = stream.parse() {
Expression(
@ -510,7 +510,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 = parse_binop_rhs(stream, rhs, Some(op))?;
rhs = stream.parse_with(|mut st| parse_binop_rhs(&mut st, rhs, Some(op)))?;
} else {
let _ = prev_operator.insert(next_op);
}
@ -696,6 +696,8 @@ 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
@ -704,8 +706,10 @@ impl Parse for FunctionDefinition {
};
stream.expect(Token::FnKeyword)?;
let mut signature: FunctionSignature = stream.parse()?;
signature.documentation = documentation;
Ok(FunctionDefinition(
stream.parse()?,
signature,
is_pub,
stream.parse()?,
stream.get_range().unwrap(),
@ -810,6 +814,7 @@ impl Parse for FunctionSignature {
Ok(FunctionSignature {
name,
documentation: None,
params,
self_kind,
return_type,
@ -1089,9 +1094,12 @@ 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 extern_fn = Stmt::ExternFunction(stream.parse()?);
let mut signature: FunctionSignature = stream.parse()?;
signature.documentation = documentation;
let extern_fn = Stmt::ExternFunction(signature);
stream.expect_nonfatal(Token::Semi).ok();
extern_fn
}

View File

@ -1,7 +1,7 @@
use std::path::PathBuf;
use crate::{
ast::{self},
ast::{self, ReturnType},
mir::{
self, CustomTypeKey, FunctionParam, ModuleMap, NamedVariableRef, ReturnKind, SourceModuleId, StmtKind,
StructField, StructType, WhileStatement,
@ -43,6 +43,7 @@ impl ast::Module {
ExternFunction(signature) => {
let def = mir::FunctionDefinition {
name: signature.name.clone(),
documentation: signature.documentation.clone(),
linkage_name: None,
is_pub: false,
is_imported: false,
@ -176,6 +177,7 @@ impl ast::FunctionDefinition {
}));
mir::FunctionDefinition {
name: signature.name.clone(),
documentation: signature.documentation.clone(),
linkage_name: None,
is_pub: *is_pub,
is_imported: false,
@ -235,7 +237,7 @@ impl ast::Block {
);
let let_statement = mir::Statement(
StmtKind::Let(counter_var.clone(), true, start.process(module_id)),
counter_range.as_meta(module_id),
start.1.as_meta(module_id),
);
let statement_range = counter_range.clone() + start.1 + end.1 + block.2;
@ -243,28 +245,53 @@ impl ast::Block {
StmtKind::Set(
mir::Expression(
mir::ExprKind::Variable(counter_var.clone()),
(start.1 + end.1).as_meta(module_id),
counter_range.as_meta(module_id),
),
mir::Expression(
mir::ExprKind::BinOp(
mir::BinaryOperator::Add,
Box::new(mir::Expression(
mir::ExprKind::Variable(counter_var.clone()),
(start.1 + end.1).as_meta(module_id),
counter_range.as_meta(module_id),
)),
Box::new(mir::Expression(
mir::ExprKind::Literal(mir::Literal::Vague(mir::VagueLiteral::Number(1))),
(start.1 + end.1).as_meta(module_id),
counter_range.as_meta(module_id),
)),
mir::TypeKind::Vague(mir::VagueType::Unknown),
),
(start.1 + end.1).as_meta(module_id),
counter_range.as_meta(module_id),
),
),
(start.1 + end.1).as_meta(module_id),
counter_range.as_meta(module_id),
);
let mut mir_block = block.into_mir(module_id);
mir_block.statements.push(set_new);
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 while_statement = mir::Statement(
StmtKind::While(WhileStatement {
condition: mir::Expression(
@ -272,17 +299,17 @@ impl ast::Block {
mir::BinaryOperator::Cmp(mir::CmpOperator::LT),
Box::new(mir::Expression(
mir::ExprKind::Variable(counter_var),
(start.1 + end.1).as_meta(module_id),
counter_range.as_meta(module_id),
)),
Box::new(end.process(module_id)),
mir::TypeKind::Vague(mir::VagueType::Unknown),
),
(start.1 + end.1).as_meta(module_id),
end.1.as_meta(module_id),
),
block: mir_block.clone(),
meta: (start.1 + end.1 + block.2).as_meta(module_id),
meta: (*counter_range + end.1 + block.2).as_meta(module_id),
}),
(start.1 + end.1 + block.2).as_meta(module_id),
(*counter_range + end.1 + block.2).as_meta(module_id),
);
let inner_scope = StmtKind::Expression(mir::Expression(
@ -381,7 +408,7 @@ impl ast::Expression {
Box::new(idx_expr.process(module_id)),
),
ast::ExpressionKind::StructExpression(struct_init) => mir::ExprKind::Struct(
struct_init.name.clone(),
CustomTypeKey(struct_init.name.clone(), module_id),
struct_init
.fields
.iter()
@ -556,7 +583,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::F32B,
ast::TypeKind::F32B => mir::TypeKind::F16B,
ast::TypeKind::F32 => mir::TypeKind::F32,
ast::TypeKind::F64 => mir::TypeKind::F64,
ast::TypeKind::F80 => mir::TypeKind::F80,

View File

@ -87,6 +87,27 @@ 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 {
@ -199,6 +220,29 @@ 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,
@ -226,7 +270,7 @@ impl<'a, 'b> TokenStream<'a, 'b> {
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(_)) {
if matches!(token.token, Token::Whitespace(_) | Token::Comment(_) | Token::Doc(_)) {
from -= 1;
} else {
break;
@ -237,7 +281,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(_)) {
if matches!(token.token, Token::Whitespace(_) | Token::Comment(_) | Token::Doc(_)) {
from += 1;
} else {
break;

View File

@ -5,7 +5,7 @@ use reid_lib::{
Block, Instr,
};
use mir::{CustomTypeKey, FunctionCall, FunctionDefinitionKind, IfExpression, TypeKind, WhileStatement};
use mir::{CustomTypeKey, 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(name, items) => {
mir::ExprKind::Struct(key, items) => {
let (_, ty) = self.return_type(&Default::default(), scope.mod_id).unwrap();
let allocation = scope
.block
.build_named(name, Instr::Alloca(ty.get_type(scope.type_values)))
.build_named(key.0.clone(), Instr::Alloca(ty.get_type(scope.type_values)))
.unwrap();
allocated.push(Allocation(self.1, ty, allocation));

View File

@ -1,12 +1,10 @@
use std::{collections::HashMap, hash::Hash};
use reid_lib::{builder::InstructionValue, CmpPredicate, ConstValueKind, Instr, Type};
use crate::{
codegen::{ErrorKind, StackValueKind},
mir::{
BinaryOperator, BinopDefinition, CmpOperator, FunctionDefinition, FunctionDefinitionKind, FunctionParam,
TypeKind,
implement::TypeCategory, BinaryOperator, BinopDefinition, CmpOperator, FunctionDefinition,
FunctionDefinitionKind, FunctionParam, TypeKind,
},
};
@ -28,21 +26,58 @@ const INTEGERS: [TypeKind; 10] = [
const FLOATS: [TypeKind; 7] = [
TypeKind::F16,
TypeKind::F32,
TypeKind::F32B,
TypeKind::F16B,
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,
@ -60,79 +95,362 @@ pub fn form_intrinsics() -> Vec<FunctionDefinition> {
intrinsics
}
pub fn get_intrinsic_assoc_functions(ty: &TypeKind) -> HashMap<String, Option<FunctionDefinition>> {
let mut map = HashMap::new();
map.insert("length".to_owned(), get_intrinsic_assoc_func(ty, "length"));
map.insert("sizeof".to_owned(), get_intrinsic_assoc_func(ty, "sizeof"));
map.insert("malloc".to_owned(), get_intrinsic_assoc_func(ty, "malloc"));
map.insert("null".to_owned(), get_intrinsic_assoc_func(ty, "null"));
map
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_func(ty: &TypeKind, name: &str) -> Option<FunctionDefinition> {
pub fn get_intrinsic_assoc_functions(ty: &TypeKind) -> Vec<FunctionDefinition> {
let mut intrinsics = Vec::new();
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,
signature_meta: Default::default(),
});
}
_ => {}
}
}
match name {
"sizeof" => Some(FunctionDefinition {
name: "sizeof".to_owned(),
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::new(),
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSizeOf(ty.clone()))),
source: None,
signature_meta: Default::default(),
}),
"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,
name: String::from("self"),
ty: TypeKind::Borrow(Box::new(ty.clone()), false),
meta: Default::default(),
}],
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicMalloc(ty.clone()))),
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicConst(*len))),
source: None,
signature_meta: Default::default(),
}),
"null" => Some(FunctionDefinition {
name: "null".to_owned(),
});
}
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: TypeKind::UserPtr(Box::new(ty.clone())),
parameters: Vec::new(),
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicNullPtr(ty.clone()))),
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(),
}),
_ => None,
});
}
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)
}
fn simple_binop_def<T: Clone + 'static>(op: BinaryOperator, ty: &TypeKind, fun: T) -> BinopDefinition
@ -152,7 +470,7 @@ where
meta: Default::default(),
},
return_type: ty.clone(),
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSimpleInstr(fun))),
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSimpleBinaryInstr(fun))),
meta: Default::default(),
exported: false,
}
@ -175,7 +493,7 @@ where
meta: Default::default(),
},
return_type: lhs.clone(),
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSimpleInstr(fun))),
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSimpleBinaryInstr(fun))),
meta: Default::default(),
exported: false,
}
@ -296,17 +614,22 @@ 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, |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(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(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::GT), &ty, |scope, lhs, rhs| {
scope.block.build(Instr::FCmp(CmpPredicate::GT, lhs, rhs)).unwrap()
}));
@ -352,12 +675,37 @@ macro_rules! intrinsic_debug {
}
#[derive(Clone)]
pub struct IntrinsicSimpleInstr<T>(T)
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)
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue;
intrinsic_debug!(IntrinsicSimpleInstr<T>, "IntrinsicSimpleInstr");
intrinsic_debug!(IntrinsicSimpleBinaryInstr<T>, "IntrinsicSimpleBinaryInstr");
impl<T: Clone> IntrinsicFunction for IntrinsicSimpleInstr<T>
impl<T: Clone> IntrinsicFunction for IntrinsicSimpleBinaryInstr<T>
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
{
@ -401,6 +749,34 @@ 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 {
@ -441,6 +817,17 @@ 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 {
@ -450,6 +837,23 @@ 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,4 +1,8 @@
use std::{cell::RefCell, collections::HashMap, rc::Rc};
use std::{
cell::RefCell,
collections::{HashMap, HashSet},
rc::Rc,
};
use allocator::{Allocator, AllocatorScope};
use intrinsics::*;
@ -58,10 +62,12 @@ 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 {
let codegen = module.codegen(context, modules.clone())?;
modules.insert(module.module_id, codegen);
module.codegen(context, modules.clone())?;
}
Ok(CodegenContext { context })
}
@ -69,7 +75,6 @@ impl mir::Context {
#[derive(Clone)]
struct ModuleCodegen<'ctx> {
name: String,
module: Module<'ctx>,
}
@ -120,11 +125,22 @@ 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, ModuleCodegen<'ctx>>,
modules: HashMap<SourceModuleId, &mir::Module>,
) -> Result<ModuleCodegen<'ctx>, ErrorKind> {
let mut module = context.module(&self.name, self.is_main);
let tokens = &self.tokens;
@ -177,10 +193,40 @@ impl mir::Module {
insert_debug!(&TypeKind::Void);
insert_debug!(&TypeKind::Char);
let mut typedefs = self.typedefs.clone();
typedefs.sort_by(|a, b| b.source_module.cmp(&a.source_module));
// 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;
}
}
}
};
for typedef in typedefs {
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 {
let type_key = CustomTypeKey(typedef.name.clone(), typedef.source_module);
type_map.insert(type_key.clone(), typedef.clone());
@ -190,9 +236,6 @@ 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(),
)))
@ -287,11 +330,11 @@ impl mir::Module {
.collect();
let is_main = self.is_main && function.name == "main";
let module_prefix = if let Some(module) = function.source {
if module == self.module_id {
let module_prefix = if let Some(module_id) = function.source {
if module_id == self.module_id {
format!("reid.{}.", self.name)
} else {
format!("reid.{}.", modules.get(&module).unwrap().name)
format!("reid.{}.", modules.get(&module_id).unwrap().name)
}
} else {
format!("reid.intrinsic.")
@ -393,6 +436,7 @@ impl mir::Module {
}),
binops: &binops,
allocator: Rc::new(RefCell::new(allocator)),
llvm_intrinsics: Rc::new(RefCell::new(HashMap::new())),
};
binop
@ -471,6 +515,7 @@ impl mir::Module {
globals: &globals,
binops: &binops,
allocator: Rc::new(RefCell::new(allocator)),
llvm_intrinsics: Rc::new(RefCell::new(HashMap::new())),
};
mir_function
@ -533,6 +578,7 @@ impl mir::Module {
globals: &globals,
binops: &binops,
allocator: Rc::new(RefCell::new(allocator)),
llvm_intrinsics: Rc::new(RefCell::new(HashMap::new())),
};
mir_function
@ -556,10 +602,7 @@ impl mir::Module {
}
}
Ok(ModuleCodegen {
name: self.name.clone(),
module,
})
Ok(ModuleCodegen { module })
}
}
@ -1205,10 +1248,9 @@ impl mir::Expression {
))
}
}
mir::ExprKind::Struct(name, items) => {
let type_key = CustomTypeKey(name.clone(), scope.module_id);
mir::ExprKind::Struct(key, items) => {
let ty = Type::CustomType({
let Some(a) = scope.type_values.get(&type_key) else {
let Some(a) = scope.type_values.get(&key) else {
return Ok(None);
};
*a
@ -1217,20 +1259,20 @@ impl mir::Expression {
let TypeDefinition {
kind: TypeDefinitionKind::Struct(struct_ty),
..
} = scope.types.get(scope.type_values.get(&type_key).unwrap()).unwrap();
} = scope.types.get(scope.type_values.get(&key).unwrap()).unwrap();
let indices = struct_ty.0.iter().enumerate();
let load_n = format!("{}.load", name);
let load_n = format!("{}@{}.load", key.0, key.1);
let struct_ptr = scope
.allocate(&self.1, &TypeKind::CustomType(type_key.clone()))
.allocate(&self.1, &TypeKind::CustomType(key.clone()))
.unwrap()
.maybe_location(&mut scope.block, location.clone());
for (field_n, exp, _) in items {
let gep_n = format!("{}.{}.gep", name, field_n);
let store_n = format!("{}.{}.store", name, field_n);
let gep_n = format!("{}@{}.{}.gep", key.0, key.1, field_n);
let store_n = format!("{}@{}.{}.store", key.0, key.1, field_n);
let i = indices.clone().find(|(_, f)| f.0 == *field_n).unwrap().0;
let elem_ptr = scope
@ -1251,7 +1293,7 @@ impl mir::Expression {
Some(StackValue(
StackValueKind::Literal(struct_val),
TypeKind::CustomType(type_key),
TypeKind::CustomType(key.clone()),
))
}
mir::ExprKind::Borrow(expr, mutable) => {

View File

@ -1,24 +1,27 @@
use std::{cell::RefCell, collections::HashMap, mem, rc::Rc};
use reid_lib::{
builder::{GlobalValue, InstructionValue, TypeValue},
builder::{FunctionValue, 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, ModuleCodegen};
use super::{allocator::Allocator, ErrorKind, IntrinsicFunction};
pub struct Scope<'ctx, 'scope> {
pub(super) context: &'ctx Context,
pub(super) modules: &'scope HashMap<SourceModuleId, ModuleCodegen<'ctx>>,
pub(super) modules: &'scope HashMap<SourceModuleId, &'ctx mir::Module>,
pub(super) tokens: &'ctx Vec<FullToken>,
pub(super) module: &'ctx Module<'ctx>,
pub(super) module_id: SourceModuleId,
@ -34,6 +37,7 @@ 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> {
@ -56,6 +60,7 @@ impl<'ctx, 'a> Scope<'ctx, 'a> {
allocator: self.allocator.clone(),
globals: self.globals,
binops: self.binops,
llvm_intrinsics: self.llvm_intrinsics.clone(),
}
}
@ -74,6 +79,49 @@ 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,10 +14,7 @@ use crate::{
mir::{self, CustomTypeKey, Metadata, SourceModuleId, TypeDefinition, TypeDefinitionKind, TypeKind, VagueLiteral},
};
use super::{
scope::{Debug, Scope},
ModuleCodegen,
};
use super::scope::{Debug, Scope};
impl mir::CmpOperator {
pub(super) fn predicate(&self) -> CmpPredicate {
@ -82,7 +79,7 @@ impl TypeKind {
TypeKind::U128 => Type::U128,
TypeKind::Bool => Type::Bool,
TypeKind::F16 => Type::F16,
TypeKind::F32B => Type::F32B,
TypeKind::F16B => Type::F32B,
TypeKind::F32 => Type::F32,
TypeKind::F64 => Type::F64,
TypeKind::F128 => Type::F128,
@ -124,7 +121,7 @@ impl TypeKind {
type_map: &HashMap<CustomTypeKey, TypeDefinition>,
local_mod: SourceModuleId,
tokens: &Vec<FullToken>,
modules: &HashMap<SourceModuleId, ModuleCodegen>,
modules: &HashMap<SourceModuleId, &mir::Module>,
) -> DebugTypeValue {
if let Some(ty) = debug_types.get(self) {
return *ty;
@ -226,7 +223,7 @@ impl TypeKind {
TypeKind::U16 | TypeKind::U32 | TypeKind::U64 | TypeKind::U128 => DwarfEncoding::Unsigned,
TypeKind::F16
| TypeKind::F32
| TypeKind::F32B
| TypeKind::F16B
| TypeKind::F64
| TypeKind::F80
| TypeKind::F128

View File

@ -25,36 +25,58 @@ 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");
#[cfg(feature = "log_output")]
println!("LDRunner: Using dynamic linker at: {:?}", dyn_linker_path);
log::debug!("LDRunner: Using dynamic linker at: {:?}", dyn_linker_path);
let mut ld = Command::new(&self.command);
ld.arg("-dynamic-linker").arg(dyn_linker_path).arg(crt1_path);
ld.arg("-dynamic-linker")
.arg(dyn_linker_path)
.arg(crt1_path)
.arg(crti_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());
#[cfg(feature = "log_output")]
println!(
"LDRunner: Executing linker to objfile at {:?} => {:?}",
input_path, out_path
);
#[cfg(feature = "log_output")]
dbg!(&ld);
log::debug!("{:#?}", ld);
ld.spawn().expect("Unable to execute ld!");
log::debug!(
"LDRunner: Executing linker to objfile at {:?} => {:?}",
input_path,
out_path
);
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;
}
thread::sleep(Duration::from_millis(100));
#[cfg(feature = "log_output")]
println!("Setting executable bit to {:?}..", out_path);
Command::new("chmod").arg("+x").arg(out_path).spawn().unwrap();
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;
}
thread::sleep(Duration::from_millis(100));
}
}
@ -64,6 +86,12 @@ 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
@ -76,3 +104,15 @@ 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

@ -98,8 +98,7 @@ pub fn parse_module<'map, T: Into<String>>(
map.set_tokens(id, tokens.clone());
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:#?}", &tokens);
log::trace!("{:#?}", &tokens);
Ok((id, tokens))
}
@ -158,8 +157,7 @@ pub fn compile_module<'map>(
}
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
dbg!(&ast_module);
log::trace!("{:#?}", &ast_module);
Ok(Ok(ast_module.process(module_id)))
}
@ -169,12 +167,10 @@ pub fn perform_all_passes<'map>(
module_map: &'map mut ErrorModules,
) -> Result<(), ReidError> {
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
dbg!(&context);
log::trace!("{:#?}", &context);
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:#}", &context);
log::trace!("{:#}", &context);
let state = context.pass(&mut LinkerPass {
module_map,
@ -188,14 +184,11 @@ pub fn perform_all_passes<'map>(
}
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:-^100}", "LINKER OUTPUT");
log::trace!("{:-^100}", "LINKER OUTPUT");
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:#}", &context);
log::trace!("{:#}", &context);
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
dbg!(&state);
log::trace!("{:#?}", &state);
if !state.errors.is_empty() {
return Err(ReidError::from_kind(
@ -216,14 +209,11 @@ pub fn perform_all_passes<'map>(
let state = context.pass(&mut macro_pass)?;
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:-^100}", "MACRO OUTPUT");
log::trace!("{:-^100}", "MACRO OUTPUT");
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:#}", &context);
log::trace!("{:#}", &context);
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
dbg!(&state);
log::trace!("{:#?}", &state);
if !state.errors.is_empty() {
return Err(ReidError::from_kind(
@ -257,17 +247,13 @@ pub fn perform_all_passes<'map>(
let state = context.pass(&mut TypeInference { refs: &mut refs })?;
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:-^100}", "TYPE INFERRER OUTPUT");
log::trace!("{:-^70}", "TYPE INFERRER OUTPUT");
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{}", &refs);
log::trace!("{}", &refs);
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:#}", &context);
log::trace!("{:#}", &context);
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
dbg!(&state);
log::trace!("{:#?}", &state);
if !state.errors.is_empty() {
return Err(ReidError::from_kind(
@ -283,14 +269,11 @@ pub fn perform_all_passes<'map>(
let state = context.pass(&mut TypeCheck { refs: &refs })?;
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:-^100}", "TYPECHECKER OUTPUT");
log::trace!("{:-^100}", "TYPECHECKER OUTPUT");
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:#}", &context);
log::trace!("{:#}", &context);
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
dbg!(&state);
log::trace!("{:#?}", &state);
if !state.errors.is_empty() {
return Err(ReidError::from_kind(
@ -303,9 +286,6 @@ pub fn perform_all_passes<'map>(
));
}
#[cfg(feature = "context_debug")]
dbg!(&context);
Ok(())
}
@ -330,11 +310,9 @@ pub fn compile_and_pass<'map>(
perform_all_passes(&mut mir_context, module_map)?;
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:-^100}", "FINAL OUTPUT");
log::trace!("{:-^100}", "FINAL OUTPUT");
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{:#}", &mir_context);
log::trace!("{:#}", &mir_context);
let mut context = Context::new(format!("Reid ({})", env!("CARGO_PKG_VERSION")));
let codegen_modules = match mir_context.codegen(&mut context) {
@ -343,8 +321,7 @@ pub fn compile_and_pass<'map>(
};
#[cfg(debug_assertions)]
#[cfg(feature = "log_output")]
println!("{}", &codegen_modules.context);
log::trace!("{}", &codegen_modules.context);
let compiled = codegen_modules.compile(cpu, features);
Ok((

149
reid/src/main.rs Normal file
View File

@ -0,0 +1,149 @@
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

@ -117,7 +117,7 @@ impl Display for TypeDefinition {
self.name,
self.source_module,
if let Some(mod_id) = self.importer {
format!("; imported to {}", mod_id)
format!("; imported by {}", mod_id)
} else {
String::new()
}
@ -152,6 +152,9 @@ 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 {}({}) -> {:#} ",
@ -276,8 +279,8 @@ impl Display for ExprKind {
}
f.write_char(']')
}
ExprKind::Struct(name, items) => {
write!(f, "{} ", name)?;
ExprKind::Struct(key, items) => {
write!(f, "{:?} ", key)?;
f.write_char('{')?;
let mut state = Default::default();
@ -468,7 +471,7 @@ impl Display for TypeKind {
}
TypeKind::Vague(vague_type) => Display::fmt(vague_type, f),
TypeKind::F16 => write!(f, "f16"),
TypeKind::F32B => write!(f, "f16b"),
TypeKind::F16B => write!(f, "f16b"),
TypeKind::F32 => write!(f, "f32"),
TypeKind::F64 => write!(f, "f64"),
TypeKind::F128 => write!(f, "f128"),

View File

@ -1,5 +1,3 @@
use reid_lib::builder::TypeValue;
use crate::util::maybe;
use super::{typecheck::typerefs::TypeRefs, *};
@ -50,7 +48,7 @@ impl TypeKind {
TypeKind::Borrow(..) => false,
TypeKind::UserPtr(..) => false,
TypeKind::F16 => true,
TypeKind::F32B => true,
TypeKind::F16B => true,
TypeKind::F32 => true,
TypeKind::F64 => true,
TypeKind::F128 => true,
@ -75,21 +73,26 @@ 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).unwrap().kind {
TypeDefinitionKind::Struct(struct_type) => {
let mut size = 0;
for field in &struct_type.0 {
size += field.1.size_of(map)
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
}
size
}
},
// Easy to recognize default number. Used e.g. when sorting
// types by size
None => 404,
},
TypeKind::CodegenPtr(_) => 64,
TypeKind::Vague(_) => panic!("Tried to sizeof a vague type!"),
TypeKind::Borrow(..) => 64,
TypeKind::UserPtr(_) => 64,
TypeKind::F16 => 16,
TypeKind::F32B => 16,
TypeKind::F16B => 16,
TypeKind::F32 => 32,
TypeKind::F64 => 64,
TypeKind::F128 => 128,
@ -120,7 +123,7 @@ impl TypeKind {
TypeKind::Borrow(_, _) => 64,
TypeKind::UserPtr(_) => 64,
TypeKind::F16 => 16,
TypeKind::F32B => 16,
TypeKind::F16B => 16,
TypeKind::F32 => 32,
TypeKind::F64 => 64,
TypeKind::F128 => 128,
@ -150,7 +153,7 @@ impl TypeKind {
| TypeKind::U128
| TypeKind::Char => TypeCategory::Integer,
TypeKind::F16
| TypeKind::F32B
| TypeKind::F16B
| TypeKind::F32
| TypeKind::F64
| TypeKind::F128
@ -199,6 +202,36 @@ 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 {
@ -226,7 +259,28 @@ impl BinaryOperator {
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord)]
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)]
pub enum TypeCategory {
Integer,
Real,
@ -416,10 +470,7 @@ impl Expression {
))
}
Accessed(_, type_kind, ..) => Ok((ReturnKind::Soft, type_kind.clone())),
Struct(name, _) => Ok((
ReturnKind::Soft,
TypeKind::CustomType(CustomTypeKey(name.clone(), mod_id)),
)),
Struct(key, _) => Ok((ReturnKind::Soft, TypeKind::CustomType(key.clone()))),
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, FunctionParam, SourceModuleId,
TypeDefinition, TypeKind,
pass::BinopKey, BinopDefinition, CustomTypeKey, FunctionDefinitionKind, SourceModuleId, StructType,
TypeDefinition, TypeDefinitionKind, TypeKind,
},
parse_module,
};
@ -46,6 +46,12 @@ 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),
}
@ -70,11 +76,21 @@ pub struct LinkerPass<'map> {
#[derive(Default, Clone)]
pub struct LinkerState {
extern_imported_types: HashMap<SourceModuleId, HashMap<String, SourceModuleId>>,
foreign_types: HashMap<SourceModuleId, HashMap<CustomTypeKey, 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;
@ -102,39 +118,56 @@ impl<'map> Pass for LinkerPass<'map> {
}
};
let mut modules = HashMap::<SourceModuleId, Rc<RefCell<_>>>::new();
let mut modules = HashMap::<SourceModuleId, LinkerModule>::new();
let mut module_ids = HashMap::<String, SourceModuleId>::new();
for (mod_id, module) in context.modules.drain() {
modules.insert(mod_id, Rc::new(RefCell::new(module)));
modules.insert(
mod_id,
LinkerModule {
module: Rc::new(RefCell::new(module)),
function_imports: HashMap::new(),
type_imports: HashMap::new(),
},
);
}
let mut modules_to_process: Vec<Rc<RefCell<_>>> = modules.values().cloned().collect();
let mut module_queue: Vec<LinkerModule> = modules.values().cloned().collect();
let mut already_imported_types = HashSet::<CustomTypeKey>::new();
let mut already_imported_binops = HashSet::<BinopKey>::new();
while let Some(mut importer) = module_queue.pop() {
let importer_mod = importer.module.borrow_mut();
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() {
// Gp go through all imports in this specific modulee
for import in importer_mod.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 mut imported = if let Some(mod_id) = module_ids.get(module_name) {
// Actually compile or fetch the imported module
let 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)?;
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()
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()
} else {
let file_path = PathBuf::from(&context.base.clone()).join(module_name.to_owned() + ".reid");
@ -175,9 +208,16 @@ impl<'map> Pass for LinkerPass<'map> {
}
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)));
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();
modules_to_process.push(imported.clone());
module_queue.push(imported.clone());
imported
}
Err((_, err)) => {
@ -202,70 +242,151 @@ impl<'map> Pass for LinkerPass<'map> {
continue;
}
}
}
.borrow_mut();
let Some((import_name, _)) = path.get(1) else {
continue;
};
let import_id = imported.module_id;
let mut imported_types = Vec::new();
let imported_module = imported.module.borrow();
if let Some(func) = imported.functions.iter_mut().find(|f| f.name == *import_name) {
let func_name = func.name.clone();
let func_signature = func.signature();
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));
}
}
if !func.is_pub {
let module_id = importer_mod.module_id;
drop(importer_mod);
modules.insert(module_id, importer);
}
for (_, linker_module) in &modules {
let mut importer_module = linker_module.module.borrow_mut();
let mut unresolved_types = HashMap::new();
// 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) {
state.ok::<_, Infallible>(
Err(ErrorKind::FunctionIsPrivate(module_name.clone(), func_name.clone())),
import.1,
Err(ErrorKind::FunctionImportIssue(func_module_name, name.clone(), e)),
import_meta.clone(),
);
}
}
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;
}
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,
);
for ty in import_type(&func.return_type) {
if unresolved_types.contains_key(&ty) {
continue;
}
unresolved_types.insert(ty, (meta.clone(), true));
}
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(&func.return_type, false);
let return_type = func.return_type.clone();
imported_types.extend(types);
unresolved_types.insert(imported_ty_key.clone(), (meta.clone(), false));
}
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());
}
// 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();
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),
signature_meta: func_signature,
});
} 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));
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;
@ -296,6 +417,7 @@ 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;
@ -305,8 +427,11 @@ impl<'map> Pass for LinkerPass<'map> {
if !func.is_pub {
state.ok::<_, Infallible>(
Err(ErrorKind::FunctionIsPrivate(module_name.clone(), func_name.clone())),
import.1,
Err(ErrorKind::FunctionIsPrivate(
imported_module_name.clone(),
func_name.clone(),
)),
meta.clone(),
);
continue;
}
@ -321,117 +446,81 @@ impl<'map> Pass for LinkerPass<'map> {
if let Err(e) = existing.equals_as_imported(func) {
state.ok::<_, Infallible>(
Err(ErrorKind::FunctionImportIssue(
module_name.clone(),
imported_module_name.clone(),
func_name.clone(),
e,
)),
import.1,
meta.clone(),
);
}
}
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,
parameters: param_tys,
return_type: func.return_type.clone(),
parameters: func.parameters.clone(),
kind: super::FunctionDefinitionKind::Extern(true),
source: Some(import_id),
source: Some(imported_module_id),
signature_meta: func.signature_meta,
},
));
}
} else {
state.ok::<_, Infallible>(
Err(ErrorKind::ImportDoesNotExist(module_name.clone(), import_name.clone())),
import.1,
);
continue;
}
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()
.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);
}
match resolve_types_recursively(&TypeKind::CustomType(ty.clone()), &modules, HashSet::new()) {
Ok(resolved) => {
imported_types.extend(resolved);
}
}
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
};
Err(e) => {
state.note_errors(&vec![e], meta);
return Ok(());
}
importer_module.typedefs.push(typedef);
}
}
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);
}
}
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)
.cloned()
{
importer_module.typedefs.push(TypeDefinition {
importer: Some(*importer_module_id),
..typedef
});
}
}
state
.scope
.data
.extern_imported_types
.insert(importer_module.module_id, extern_types);
.foreign_types
.insert(importer_module.module_id, imported_types);
}
let mut modules: Vec<Module> = modules
.into_values()
.map(|v| Rc::into_inner(v).unwrap().into_inner())
.map(|v| Rc::into_inner(v.module).unwrap().into_inner())
.collect();
for module in modules.drain(..) {
@ -441,19 +530,33 @@ 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 {
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);
}
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);
}
}
Ok(())
@ -461,11 +564,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 extern_types = &state.scope.data.extern_imported_types.get(&mod_id);
if let Some(extern_types) = extern_types {
let foreign_types = &state.scope.data.foreign_types.get(&mod_id);
if let Some(foreign_types) = foreign_types {
match &mut stmt.0 {
super::StmtKind::Let(var_ref, _, _) => {
var_ref.0 = var_ref.0.update_imported(extern_types, mod_id);
var_ref.0 = var_ref.0.update_imported(foreign_types);
}
_ => {}
}
@ -475,25 +578,29 @@ 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 extern_types = &state.scope.data.extern_imported_types.get(&mod_id);
if let Some(extern_types) = extern_types {
let foreign_types = &state.scope.data.foreign_types.get(&mod_id);
if let Some(foreign_types) = foreign_types {
match &mut expr.0 {
super::ExprKind::Variable(var_ref) => {
var_ref.0 = var_ref.0.update_imported(extern_types, mod_id);
var_ref.0 = var_ref.0.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::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::Borrow(..) => {}
super::ExprKind::Deref(..) => {}
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)
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()
}
}
_ => {}
}
@ -503,78 +610,103 @@ impl<'map> Pass for LinkerPass<'map> {
}
impl TypeKind {
fn update_imported(
&self,
extern_types: &HashMap<String, SourceModuleId>,
importer_mod_id: SourceModuleId,
) -> TypeKind {
fn update_imported(&self, foreign_types: &HashMap<CustomTypeKey, SourceModuleId>) -> TypeKind {
match &self {
TypeKind::Array(type_kind, len) => {
TypeKind::Array(Box::new(type_kind.update_imported(extern_types, importer_mod_id)), *len)
TypeKind::Array(Box::new(type_kind.update_imported(foreign_types)), *len)
}
TypeKind::CustomType(custom_type_key) => {
if let Some(mod_id) = extern_types.get(&custom_type_key.0) {
if let Some(mod_id) = foreign_types.get(&custom_type_key) {
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(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::Borrow(type_kind, mutable) => {
TypeKind::Borrow(Box::new(type_kind.update_imported(foreign_types)), *mutable)
}
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, usable_import: bool) -> Vec<(CustomTypeKey, bool)> {
fn import_type(ty: &TypeKind) -> Vec<CustomTypeKey> {
let mut imported_types = Vec::new();
match &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)),
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)),
_ => {}
};
imported_types
}
fn find_inner_types(
typedef: &TypeDefinition,
mut seen: HashSet<CustomTypeKey>,
mod_id: SourceModuleId,
) -> Vec<CustomTypeKey> {
match &typedef.kind {
crate::mir::TypeDefinitionKind::Struct(struct_type) => {
let typenames = struct_type
.0
.iter()
.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);
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()));
}
seen.into_iter().collect()
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>,
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
.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())?);
}
}
}
}
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,12 +177,7 @@ impl mir::Expression {
globals.extend(expression.gen_macros(data, state, map));
globals.extend(expression1.gen_macros(data, state, map));
}
mir::ExprKind::AssociatedFunctionCall(
_,
FunctionCall {
parameters, is_macro, ..
},
) => {
mir::ExprKind::AssociatedFunctionCall(_, FunctionCall { parameters, .. }) => {
for expression in parameters {
globals.extend(expression.gen_macros(data, state, map));
}

View File

@ -55,6 +55,28 @@ 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 {
@ -83,7 +105,7 @@ impl TokenRange {
#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct CustomTypeKey(pub String, pub SourceModuleId);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
pub enum TypeKind {
Bool,
I8,
@ -98,7 +120,7 @@ pub enum TypeKind {
U128,
Void,
F16,
F32B,
F16B,
F32,
F64,
F128,
@ -198,7 +220,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::F32B,
Literal::F32B(_) => TypeKind::F16B,
Literal::F32(_) => TypeKind::F32,
Literal::F64(_) => TypeKind::F64,
Literal::F80(_) => TypeKind::F80,
@ -264,7 +286,7 @@ pub enum ExprKind {
Indexed(Box<Expression>, TypeKind, Box<Expression>),
Accessed(Box<Expression>, TypeKind, String, Metadata),
Array(Vec<Expression>),
Struct(String, Vec<(String, Expression, Metadata)>),
Struct(CustomTypeKey, Vec<(String, Expression, Metadata)>),
Literal(Literal),
BinOp(BinaryOperator, Box<Expression>, Box<Expression>, TypeKind),
FunctionCall(FunctionCall),
@ -296,6 +318,7 @@ 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,
@ -315,6 +338,16 @@ 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,

View File

@ -450,6 +450,10 @@ 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::F32B
| TypeKind::F16B
| 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::F32B
| TypeKind::F16B
| 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::F32B
| TypeKind::F16B
| TypeKind::F32
| TypeKind::F64
| TypeKind::F80
@ -333,7 +333,7 @@ impl TypeKind {
.map(|_| ())
.ok_or(ErrorKind::NoSuchType(
custom_type_key.0.clone(),
state.module_id.unwrap(),
custom_type_key.1.clone(),
))
}
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,
mut seen: HashSet<String>,
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(intrinsic) => Ok((ReturnKind::Soft, TypeKind::Vague(Vague::Unknown))),
FunctionDefinitionKind::Intrinsic(_) => Ok((ReturnKind::Soft, TypeKind::Vague(Vague::Unknown))),
}
}
}
@ -627,15 +627,14 @@ impl Expression {
Err(ErrorKind::TriedAccessingNonStruct(expr_ty))
}
}
ExprKind::Struct(struct_name, items) => {
let type_key = CustomTypeKey(struct_name.clone(), state.module_id.unwrap());
ExprKind::Struct(struct_key, items) => {
let struct_def = state
.scope
.get_struct_type(&type_key)
.ok_or(ErrorKind::NoSuchType(struct_name.clone(), type_key.1))?
.get_struct_type(&struct_key)
.ok_or(ErrorKind::NoSuchType(struct_key.0.clone(), struct_key.1))?
.clone();
let mut expected_fields = if let Some(struct_ty) = state.scope.get_struct_type(&type_key) {
let mut expected_fields = if let Some(struct_ty) = state.scope.get_struct_type(&struct_key) {
struct_ty.0.iter().map(|f| f.0.clone()).collect()
} else {
HashSet::new()
@ -646,7 +645,7 @@ impl Expression {
let expected_ty = state.or_else(
struct_def
.get_field_ty(field_name)
.ok_or(ErrorKind::NoSuchField(format!("{}.{}", struct_name, field_name))),
.ok_or(ErrorKind::NoSuchField(format!("{:?}.{}", struct_key, field_name))),
&TypeKind::Vague(VagueType::Unknown),
field_expr.1,
);
@ -668,7 +667,7 @@ impl Expression {
self.1,
);
Ok(TypeKind::CustomType(type_key))
Ok(TypeKind::CustomType(struct_key.clone()))
}
ExprKind::Borrow(expr, mutable) => {
let hint_t = if let HintKind::Coerce(hint_t) = hint_t {
@ -721,7 +720,7 @@ impl Expression {
expr.resolve_ref(typerefs).cast_into(type_kind)
}
ExprKind::AssociatedFunctionCall(type_kind, function_call) => {
*type_kind = type_kind.or_default().unwrap();
*type_kind = type_kind.or_default()?;
let true_function = state
.scope
.get_associated_function(&pass::AssociatedFunctionKey(
@ -772,7 +771,7 @@ impl Expression {
Ok(function_call.return_type.clone().resolve_ref(typerefs))
}
}
ExprKind::GlobalRef(global_value, type_kind) => Ok(self
ExprKind::GlobalRef(..) => Ok(self
.return_type(typerefs, state.scope.module_id.unwrap())
.map(|r| r.1)
.unwrap()),
@ -807,14 +806,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::F32B) => L::F32B(v as f32),
(L::Vague(VagueL::Number(v)), TypeKind::F16B) => 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::F32B) => L::F32B(v as f32),
(L::Vague(VagueL::Decimal(v)), TypeKind::F16B) => 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,9 +12,10 @@ use std::{
use crate::{
mir::{
implement::TypeCategory,
pass::{AssociatedFunctionKey, ScopeVariable},
BinopDefinition, Block, CustomTypeKey, ExprKind, Expression, FunctionDefinition, FunctionDefinitionKind,
IfExpression, Module, ReturnKind, StmtKind, TypeKind, VagueType, WhileStatement,
BinopDefinition, Block, ExprKind, Expression, FunctionDefinition, FunctionDefinitionKind, IfExpression, Module,
ReturnKind, StmtKind, TypeKind, VagueType, WhileStatement,
},
util::try_all,
};
@ -382,6 +383,7 @@ impl Expression {
} else {
Vec::new()
};
if binops.len() > 0 {
let binop = unsafe { binops.get_unchecked(0) };
let mut widened_lhs = binop.hands.0.clone();
@ -391,9 +393,6 @@ 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();
@ -552,12 +551,11 @@ impl Expression {
_ => Ok(type_refs.from_type(&TypeKind::Vague(VagueType::Unknown)).unwrap()),
}
}
ExprKind::Struct(struct_name, fields) => {
let type_key = CustomTypeKey(struct_name.clone(), state.module_id.unwrap());
ExprKind::Struct(struct_key, fields) => {
let expected_struct_ty = state
.scope
.get_struct_type(&type_key)
.ok_or(ErrorKind::NoSuchType(struct_name.clone(), state.module_id.unwrap()))?
.get_struct_type(&struct_key)
.ok_or(ErrorKind::NoSuchType(struct_key.0.clone(), state.module_id.unwrap()))?
.clone();
for field in fields {
if let Some(expected_field_ty) = expected_struct_ty.get_field_ty(&field.0) {
@ -567,12 +565,12 @@ impl Expression {
}
} else {
state.ok::<_, Infallible>(
Err(ErrorKind::NoSuchField(format!("{}.{}", struct_name, field.0))),
Err(ErrorKind::NoSuchField(format!("{:?}.{}", struct_key, field.0))),
field.1 .1,
);
}
}
Ok(type_refs.from_type(&TypeKind::CustomType(type_key.clone())).unwrap())
Ok(type_refs.from_type(&TypeKind::CustomType(struct_key.clone())).unwrap())
}
ExprKind::Borrow(expr, mutable) => {
// Find variable type
@ -627,12 +625,18 @@ 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 {
@ -679,7 +683,7 @@ impl Expression {
// Provide function return type
Ok(type_refs.from_type(&fn_call.ret).unwrap())
}
ExprKind::GlobalRef(global_value, type_kind) => Ok(self
ExprKind::GlobalRef(..) => Ok(self
.return_type(type_refs.types, state.scope.module_id.unwrap())
.map(|r| type_refs.from_type(&r.1).unwrap())
.unwrap()),

View File

@ -308,12 +308,23 @@ impl<'outer> ScopeTypeRefs<'outer> {
let lhs_resolved = lhs.resolve_ref(self.types);
let rhs_resolved = rhs.resolve_ref(self.types);
let binops = self
let mut 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),
@ -487,6 +498,17 @@ 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

@ -169,3 +169,8 @@ 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));
}