Compare commits
196 Commits
1.0.0-beta
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| fbbaf09f73 | |||
| 4cb0ce9d69 | |||
| 5a65f7912f | |||
| c04dc01258 | |||
| d5a4168b0d | |||
| 663f327ccf | |||
| 84233c8f49 | |||
| 07b0403e9b | |||
| 28934ea6fc | |||
| 7dd06567bb | |||
| 77107ef95e | |||
| 1d0fd9dd0a | |||
| 3a5766186a | |||
| 8d8d6ac336 | |||
| fa95da97ae | |||
| 407c681cb6 | |||
| 2dd482c9c2 | |||
| 1a5823c59c | |||
| 3ebe16b80b | |||
| 7a6f99479a | |||
| f4bce14299 | |||
| aa1de16f4c | |||
| 89b6fc1a71 | |||
| ceee2f286a | |||
| dcb4e76a40 | |||
| 6dccab8b12 | |||
| 7b4f38406d | |||
| 9e37ae7aac | |||
| 3e0367fb1e | |||
| 685520a6cf | |||
| 0203213b28 | |||
| c23160bc32 | |||
| fe6fe6c435 | |||
| 3e85ed2705 | |||
| d50a748884 | |||
| c44d588b30 | |||
| 0e23ab4636 | |||
| ce2473a715 | |||
| 1a8535516c | |||
| e1ac019ecd | |||
| 82b67dfaaa | |||
| a5c7823a29 | |||
| 3dba4a79ff | |||
| 2a8842658d | |||
| 8d32f2bbad | |||
| 0ee9d3bf7d | |||
| 1dd4bbbb05 | |||
| 8cbc65422e | |||
| 3cd4121951 | |||
| c84954480b | |||
| 67106ea17b | |||
| 015c111b29 | |||
| aad3c93068 | |||
| 1ba0de442a | |||
| 1c3386bc9a | |||
| 8a178387ca | |||
| b93b7aa52b | |||
| 1275063dc2 | |||
| 02522dd36d | |||
| 5b7c3d5b3a | |||
| 70a968d7a0 | |||
| a9d5a4d03b | |||
| 11d93e4adf | |||
| c2f6cfb8e6 | |||
| 34612e98ce | |||
| 3b4835cff8 | |||
| 627d1bcfa5 | |||
| ae6796acfc | |||
| 5d19d38682 | |||
| 4a33e7d123 | |||
| 79ecb3b9ba | |||
| 28157ae4b8 | |||
| 8d27a6e7bd | |||
| 136e9f9d1f | |||
| 8e41deb653 | |||
| 766a853b48 | |||
| 109fedb624 | |||
| d27ec2bb70 | |||
| 5706fd99e3 | |||
| 3b3b21d4dc | |||
| 7809aeb2b5 | |||
| cd2ebb5224 | |||
| 8b0d09c08d | |||
| a215a2116a | |||
| 0abeb0c4cd | |||
| ac0d79f816 | |||
| 909728a564 | |||
| 82537224e7 | |||
| 79b3c6b3ef | |||
| 1ae164b1d6 | |||
| 9b9baabc81 | |||
| 13c462cb9b | |||
| 8739fe16d1 | |||
| 1438ba7bd1 | |||
| d9911a8ff5 | |||
| 48dd17b320 | |||
| dcc53498e7 | |||
| 018f3e2561 | |||
| 6a9133baff | |||
| 3f3de9e2c0 | |||
| b965ca11b9 | |||
| 3537318466 | |||
| dbc43f51ee | |||
| c7f1b81c9d | |||
| a51a2c8f56 | |||
| 101ee2d8e5 | |||
| a6844b919b | |||
| 4ea0913842 | |||
| bb9f69ee53 | |||
| 97a5c3a65e | |||
| 8595da0c30 | |||
| dae39bc9d2 | |||
| 658450993a | |||
| 3f6d26679d | |||
| 16082752e2 | |||
| 8a71ce3629 | |||
| 81d418c6d8 | |||
| 8d0e3d03d5 | |||
| 34e31549b3 | |||
| 0ba25db4c8 | |||
| 314f44304a | |||
| 08f7725ce7 | |||
| f89b26bf74 | |||
| 4fada0036c | |||
| 4f0ee72c83 | |||
| deed96bbfd | |||
| 1e094eeea0 | |||
| 3adb745576 | |||
| 8f7b785664 | |||
| c7aacfe756 | |||
| b71c253942 | |||
| 7d3aaa143a | |||
| 6619f1f0a9 | |||
| bc59b6f575 | |||
| c262418f88 | |||
| 2dd3a5904b | |||
| ff1da716e9 | |||
| 7c6d634287 | |||
| b0442e5685 | |||
| 2303bf757a | |||
| 7234cad5f0 | |||
| baa068a371 | |||
| 8b1d1084a6 | |||
| f5f55079a9 | |||
| baa7bafafc | |||
| f700c577f1 | |||
| ebe7fc8d75 | |||
| 140d963d9b | |||
| 480ba5155a | |||
| 2207c3df83 | |||
| 735c3231b1 | |||
| 50a875ad21 | |||
| 30257e1a2b | |||
| a7ac974f46 | |||
| 3d8f4bbd24 | |||
| 33ed1fd813 | |||
| 67a5fcd002 | |||
| 80bdf4eba8 | |||
| bd8994bb37 | |||
| 2e153922f1 | |||
| ea6458dddc | |||
| 014ba2f638 | |||
| 89850d7b4f | |||
| 13be3e9c02 | |||
| 5026013df3 | |||
| beaba4e7de | |||
| e14efa2ea7 | |||
| ccee457cf4 | |||
| 3f81104c99 | |||
| b643c13582 | |||
| e412a2e1d7 | |||
| 1b1a5934f5 | |||
| 726251e39c | |||
| 61d3ea61ee | |||
| d0e1082029 | |||
| 60860498df | |||
| 7ca8949e8c | |||
| a60d35c0b0 | |||
| bb7347c97b | |||
| a680064b0f | |||
| 469ce3ce77 | |||
| 2709eb8749 | |||
| 97948d8c38 | |||
| a4e18af983 | |||
| d06eff9347 | |||
| 49084ea0af | |||
| 516833d26f | |||
| 63c54ae4da | |||
| 3d38b7db8d | |||
| 1c7a600b96 | |||
| 8e71c6a47d | |||
| 9c2f47534a | |||
| c9909dc651 | |||
| cea756b2ad | |||
| 57b5a5cce4 | |||
| 2a6aceb052 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -10,4 +10,5 @@ main
|
||||
*.asm
|
||||
*.out
|
||||
*.llir
|
||||
*.mir
|
||||
*.mir
|
||||
foo.reid
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "reid-llvm-lib"]
|
||||
path = reid-llvm-lib
|
||||
url = gitea@git.teascade.net:teascade/reid-llvm-lib.git
|
||||
1267
Cargo.lock
generated
1267
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,6 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"reid",
|
||||
"reid-llvm-lib"
|
||||
]
|
||||
"reid-llvm-lib",
|
||||
"reid-lsp"
|
||||
]
|
||||
|
||||
35
README.md
35
README.md
@ -12,6 +12,9 @@ by simplicity.
|
||||
Specifications and a bunch of [documentation for the language can be found
|
||||
here](./documentation/).
|
||||
|
||||
An example of a real whole program (a CPU pathtracer) can be found [in
|
||||
examples/cpu_raytracer.reid](./examples/cpu_raytracer.reid), go have a look!
|
||||
|
||||
Reid is currently able to (non-exhaustively):
|
||||
- Do basic algebra binary and unary-operations (e.g. Add, Sub, Div, Mult, And,
|
||||
Not)
|
||||
@ -68,17 +71,22 @@ Currently missing big features (TODOs) are:
|
||||
Big features that I want later but are not necessary:
|
||||
- ~~User-defined binary operations~~ (DONE)
|
||||
- ~~Asymmetric binary operations (e.g. string + u32)~~ (DONE)
|
||||
- Error handling
|
||||
- Lexing & parsing of whitespace and comments as well
|
||||
- LSP implementation
|
||||
- ~~Error handling~~ (Not Doing It)
|
||||
- ~~Lexing & parsing of whitespace and comments as well~~ (DONE)
|
||||
- ~~LSP implementation~~ (DONE)
|
||||
- ~~Syntax Highlighting~~ (DONE)
|
||||
- ~~Semantic Highlighting~~ (DONE)
|
||||
- ~~Go-To-Definition~~ (DONE)
|
||||
- ~~Find-All-References~~ (DONE)
|
||||
- ~~Refactoring~~ (DONE)
|
||||
|
||||
Smaller features:
|
||||
- ~~Hex-numbers~~
|
||||
- Bitwise operations
|
||||
- ~~Easier way to initialize arrays with a single value~~
|
||||
- ~~Void-returns (`return;` for void-returning functions)~~
|
||||
- ~~Only include standard library at all if it is imported~~
|
||||
- Lexical scopes for Debug Information
|
||||
- ~~Hex-numbers~~ (DONE)
|
||||
- ~~Bitwise operations~~ (DONE)
|
||||
- ~~Easier way to initialize arrays with a single value~~ (DONE)
|
||||
- ~~Void-returns (`return;` for void-returning functions)~~ (DONE)
|
||||
- ~~Only include standard library at all if it is imported~~ (DONE)
|
||||
- ~~Lexical scopes for Debug Information~~ (DONE)
|
||||
|
||||
### Why "Reid"
|
||||
|
||||
@ -154,10 +162,6 @@ cmake llvm -B build -DCMAKE_BUILD_TYPE=MinSizeRel -DLLVM_ENABLE_ASSERTIONS=ON -D
|
||||
ninja -j23
|
||||
```
|
||||
|
||||
*Also Note:* Building LLVM with `Ninja` was not successful for me, but this
|
||||
method was. Ninja may be successful with you, to try it, add `-G Ninja` to the
|
||||
`cmake`-command, and instead of `make` run `ninja install`.
|
||||
|
||||
### Building this crate itself
|
||||
|
||||
Assuming `llvm-project` from the previous step was at
|
||||
@ -167,6 +171,5 @@ Assuming `llvm-project` from the previous step was at
|
||||
LLVM_SYS_201_PREFIX=/path/llvm-project/build cargo build
|
||||
```
|
||||
|
||||
## In conclusion
|
||||
Good luck! It took me a good 10 hours to figure this out for myself, I sure hope
|
||||
these instructions help both myself and someone else in the future!
|
||||
Alternatively assuming you have LLVM 20.1 or newer installed you may use omit
|
||||
the environment variable entirely and use dynamic linking instead
|
||||
@ -27,7 +27,9 @@ in-depth, but when you're feeling up to it, you can read about them
|
||||
## Syntax and general information
|
||||
|
||||
Syntax for Reid is very much inspired by rust, and examples of the language can
|
||||
be found in the [examples](../examples/)-folder.
|
||||
be found in the [examples](../examples/)-folder. A larger example of a whole
|
||||
program written in Reid (a CPU Pathtracer) can be found [in
|
||||
examples/cpu_raytracer.reid](../examples/cpu_raytracer.reid).
|
||||
|
||||
In Reid **modules** (or files) on the top-level are comprised of imports, type
|
||||
definitions, binop-definitions, functions and type-associated function blocks.
|
||||
@ -81,7 +83,11 @@ Common token used throughout this document to express parts of grammar include:
|
||||
"i8" | "i16" | "i32" | "i64" | "i128" |
|
||||
"f16" | "f32" | "f32b" | "f64" | "f80" | "f128" | "f128ppc"
|
||||
|
||||
<binop> :: "+" | "-" | "*" | "/" | "%" | "&&" | <cmp>
|
||||
<binop> :: "+" | "-" | "*"
|
||||
| "/" | "%" | "&&" | "||"
|
||||
| "&" | "|" | "^" | ">>"
|
||||
| "<<" | <cmp>
|
||||
|
||||
<cmp> :: "<" | "<=" | "==" | "!=" | ">=" | >"
|
||||
<unary> :: "+" | "-" | "!"
|
||||
```
|
||||
@ -255,6 +261,9 @@ calls, literals, or if-expressions. Types of supported expressions include:
|
||||
*associated type* with given parameters.
|
||||
- **Accessing function calls**, a shorthand to call associated function calls
|
||||
which have `&self` or `&mut self` as their first parameter.
|
||||
- **Macro invocations** for invoking **macros** which are evaluated at
|
||||
compile-time rather than runtime. Currently it is not possible to define
|
||||
your own macros, but there are some pre-defined in the intrinsics.
|
||||
- **Block-expressions**, which can return a value to the higher-level expression
|
||||
if they have a statement with a soft-return. Otherwise they return void.
|
||||
- **If-expressions**, which can execute one of two expressions depending on the
|
||||
@ -272,7 +281,7 @@ In formal grammar:
|
||||
<indexing> | <accessing> |
|
||||
<binary-exp> | <unary-exp> |
|
||||
<function-call> | <accessing-function-call> | <assoc-function-call>
|
||||
<block> | <if-expr> | <cast> |
|
||||
<macro-invocation> | <block> | <if-expr> | <cast> |
|
||||
( "(" <expression> ")" )
|
||||
|
||||
<variable> :: <ident>
|
||||
@ -288,6 +297,7 @@ In formal grammar:
|
||||
<function-call> :: <expression> "(" [ <expression> ( "," <expression> )* ] ")"
|
||||
<accessing-function-call> :: <accessing> "(" [ <expression> ( "," <expression> )* ] ")"
|
||||
<assoc-function-call> :: <type> "::" <function-call>
|
||||
<macro-invocation> :: <expression> "!(" [ <expression> ( "," <expression> )* ] ")"
|
||||
<if-expr> :: "if" <expression> <expression> [ "else" <expression> ]
|
||||
<cast> :: <expression> "as" <type>
|
||||
```
|
||||
@ -306,6 +316,7 @@ test.first // Accessing
|
||||
func(value, 14) // Function call
|
||||
Test::get_field(&test); // Associated function call
|
||||
test.get_field(); // Same, but using a the dot-form shorthand
|
||||
include_bytes!("./test"); // Macro invocation
|
||||
if varname {} else {} // If-expression
|
||||
value as u32 // cast
|
||||
(value + 2) // Binop within parenthesis
|
||||
|
||||
@ -7,22 +7,181 @@ 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).
|
||||
|
||||
### Macro Intrinsics
|
||||
|
||||
#### `include_bytes!(path: *char) -> &[u8; _]`
|
||||
|
||||
Attempts to load file from `path` (relative to module) into memory and includes
|
||||
it into the compiled binary directly. Returns a borrow to an array containing
|
||||
bytes from the file. Array length varies depending on the file contents.
|
||||
|
||||
### Associated Intrinsics
|
||||
|
||||
#### `<T>::sizeof() -> u64`
|
||||
|
||||
Simply returns the size of type `T` in bits.
|
||||
Simply returns the size of type `T` in bytes.
|
||||
|
||||
```rust
|
||||
i32::sizeof(); // Returns 32
|
||||
i32::sizeof(); // Returns 4
|
||||
```
|
||||
|
||||
#### `<T>::alloca(size: u64) -> *T`
|
||||
#### `<T>::null() -> *T`
|
||||
|
||||
Allocates `T::sizeof() * size` bits and returns a pointer to `T`.
|
||||
|
||||
**Note:** This does not seem to work correctly currently.
|
||||
Returns a null-pointer of type `T`.
|
||||
|
||||
```rust
|
||||
i32::alloca(30); // Returns *i32
|
||||
```
|
||||
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`.
|
||||
|
||||
```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.
|
||||
@ -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`
|
||||
|
||||
@ -20,7 +20,7 @@ fn main() -> u32 {
|
||||
let boop: f32 = 3;
|
||||
|
||||
let mut a = &mut value;
|
||||
|
||||
|
||||
*a.second[2] = 5;
|
||||
|
||||
return *a.second[2];
|
||||
|
||||
@ -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,14 +21,20 @@ 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::alloca(15);
|
||||
let mut list = u64::malloc(15);
|
||||
list[4] = 17;
|
||||
|
||||
print(from_str("value: ") + list[4]);
|
||||
|
||||
@ -22,6 +22,8 @@ fn main() -> u32 {
|
||||
let otus = Otus { field: 17 };
|
||||
print(from_str("otus: ") + otus.test() as u64);
|
||||
|
||||
otus.field;
|
||||
|
||||
|
||||
return otus.test();
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
// Arithmetic, function calls and imports!
|
||||
|
||||
/// Test stuff
|
||||
fn changer(param: &mut u32) {
|
||||
*param = 17;
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
// Arithmetic, function calls and imports!
|
||||
|
||||
import std::allocate;
|
||||
import std::print;
|
||||
|
||||
fn other() -> i16 {
|
||||
@ -9,7 +8,7 @@ fn other() -> i16 {
|
||||
|
||||
fn main() -> u32 {
|
||||
|
||||
let mut v = (allocate(4) as *u32);
|
||||
let mut v = (malloc(4) as *u32);
|
||||
v[0] = other() as u32;
|
||||
|
||||
return v[0];
|
||||
|
||||
10
examples/compilcated_main.reid
Normal file
10
examples/compilcated_main.reid
Normal 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;
|
||||
}
|
||||
12
examples/complicated_imported.reid
Normal file
12
examples/complicated_imported.reid
Normal 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) {}
|
||||
}
|
||||
466
examples/cpu_raytracer.reid
Normal file
466
examples/cpu_raytracer.reid
Normal file
@ -0,0 +1,466 @@
|
||||
// First half of Ray Tracing in One Weekend, rendered to a SDL3 window rather
|
||||
// than an image file. Needs to be linked against SDL3, i.e.
|
||||
// `./cli cpu_raytracer.reid SDL3`
|
||||
|
||||
import std::print;
|
||||
import std::String;
|
||||
|
||||
///////////////////
|
||||
/// SDL externs ///
|
||||
///////////////////
|
||||
|
||||
// Helper struct for stack allocated const sized strings, because structs are
|
||||
// easier to create uninit than arrays.
|
||||
struct SDL_Window {}
|
||||
struct SDL_Renderer {}
|
||||
struct SDL_Texture {}
|
||||
struct SDL_Event { type: u32, reserved: [u8; 124] }
|
||||
struct SDL_FRect { x: f32, y: f32, w: f32, h: f32 }
|
||||
struct SDL_Rect { x: i32, y: i32, w: i32, h: i32 }
|
||||
extern fn SDL_malloc(size: u64) -> *u8;
|
||||
extern fn SDL_Init(flags: u32) -> bool;
|
||||
extern fn SDL_Quit();
|
||||
extern fn SDL_CreateWindowAndRenderer(title: *char, width: i32, height: i32, flags: i32,
|
||||
window_out: &mut *SDL_Window, renderer_out: &mut *SDL_Renderer) -> bool;
|
||||
extern fn SDL_Delay(ms: u32);
|
||||
extern fn SDL_SetRenderDrawColor(renderer: *SDL_Renderer, r: u8, g: u8, b: u8, a: u8);
|
||||
extern fn SDL_RenderClear(renderer: *SDL_Renderer);
|
||||
extern fn SDL_RenderPresent(renderer: *SDL_Renderer);
|
||||
extern fn SDL_HasEvent(event_type: u32) -> bool;
|
||||
extern fn SDL_PollEvent(event: &mut SDL_Event) -> bool;
|
||||
extern fn SDL_PumpEvents();
|
||||
extern fn SDL_FlushEvents(min_type: u32, max_type: u32);
|
||||
extern fn SDL_GetTicks() -> u64;
|
||||
extern fn SDL_SetWindowTitle(window: *SDL_Window, title: *char) -> bool;
|
||||
extern fn SDL_CreateTexture(renderer: *SDL_Renderer,
|
||||
pixel_format: u32, texture_access: u32, width: u32, height: u32) -> *SDL_Texture;
|
||||
extern fn SDL_RenderTexture(renderer: *SDL_Renderer,
|
||||
texture: *SDL_Texture, srcfrect: &SDL_FRect, dstfrect: &SDL_FRect) -> bool;
|
||||
extern fn SDL_UpdateTexture(texture: *SDL_Texture, rect: &SDL_Rect, pixels: *u8, pitch: u32) -> bool;
|
||||
extern fn SDL_GetError() -> *char;
|
||||
extern fn SDL_GetWindowSize(window: *SDL_Window, w: &mut i32, h: &mut i32) -> bool;
|
||||
extern fn SDL_rand(max_exclusive: u32) -> u32;
|
||||
extern fn SDL_SetTextureScaleMode(texture: *SDL_Texture, scale_mode: i32) -> bool;
|
||||
extern fn SDL_sqrtf(value: f32) -> f32;
|
||||
extern fn SDL_randf() -> f32;
|
||||
extern fn SDL_powf(value: f32, power: f32) -> f32;
|
||||
|
||||
// SDL error reporting helper
|
||||
fn print_sdl_error(context: *char) {
|
||||
let mut message = String::new();
|
||||
message = message + context + ": " + SDL_GetError();
|
||||
print(message);
|
||||
message.free();
|
||||
}
|
||||
|
||||
/////////////////////////////////
|
||||
/// Main setup and frame loop ///
|
||||
/////////////////////////////////
|
||||
|
||||
struct GameState {
|
||||
renderer: *SDL_Renderer,
|
||||
window: *SDL_Window,
|
||||
render_texture: *SDL_Texture,
|
||||
frame_counter: u32,
|
||||
last_fps_reset: u64,
|
||||
pixels: *u8,
|
||||
pixels_w: u32,
|
||||
pixels_h: u32,
|
||||
pixels_bpp: u32,
|
||||
}
|
||||
|
||||
fn main() -> i32 {
|
||||
let SDL_INIT_VIDEO = 32;
|
||||
let SDL_WINDOW_RESIZABLE = 32;
|
||||
let SDL_PIXELFORMAT_RGBA8888 = 373694468;
|
||||
let SDL_PIXELFORMAT_ABGR8888 = 376840196;
|
||||
let SDL_PIXELFORMAT_RGB24 = 386930691;
|
||||
let SDL_PIXELFORMAT_BGR24 = 390076419;
|
||||
let SDL_PIXELFORMAT_RGB96_FLOAT = 454057996;
|
||||
let SDL_PIXELFORMAT_BGR96_FLOAT = 457203724;
|
||||
let SDL_TEXTUREACCESS_STREAMING = 1;
|
||||
let SDL_SCALEMODE_NEAREST = 0;
|
||||
let SDL_SCALEMODE_LINEAR = 1;
|
||||
let SDL_SCALEMODE_PIXELART = 2;
|
||||
|
||||
let init_success = SDL_Init(SDL_INIT_VIDEO);
|
||||
if init_success == false {
|
||||
print_sdl_error("SDL init failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
let mut window = SDL_Window::null();
|
||||
let mut renderer = SDL_Renderer::null();
|
||||
let gfx_init_success = SDL_CreateWindowAndRenderer(
|
||||
"cpu raytracer", 640, 480, SDL_WINDOW_RESIZABLE,
|
||||
&mut window, &mut renderer
|
||||
);
|
||||
if gfx_init_success == false {
|
||||
print_sdl_error("SDL renderer and window creation failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
let width = 128;
|
||||
let height = 64;
|
||||
let bpp = 4;
|
||||
let render_texture = SDL_CreateTexture(renderer,
|
||||
SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, width, height);
|
||||
SDL_SetTextureScaleMode(render_texture, SDL_SCALEMODE_NEAREST);
|
||||
|
||||
let pixels_len = (width * height * bpp) as u64;
|
||||
let pixels = SDL_malloc(pixels_len);
|
||||
let mut game_state = GameState {
|
||||
renderer: renderer,
|
||||
window: window,
|
||||
render_texture: render_texture,
|
||||
frame_counter: 0,
|
||||
last_fps_reset: 0,
|
||||
pixels: pixels,
|
||||
pixels_w: width,
|
||||
pixels_h: height,
|
||||
pixels_bpp: bpp,
|
||||
};
|
||||
|
||||
while frame_loop(&mut game_state) {}
|
||||
|
||||
SDL_Quit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn frame_loop(game_state: &mut GameState) -> bool {
|
||||
let mut event = SDL_Event { type: 0, reserved: [0; 124] };
|
||||
while (SDL_PollEvent(&mut event)) {
|
||||
if event.type == 256 { // SDL_EVENT_QUIT
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
let mut screen_width = 0;
|
||||
let mut screen_height = 0;
|
||||
SDL_GetWindowSize(*game_state.window, &mut screen_width, &mut screen_height);
|
||||
|
||||
let renderer = *game_state.renderer;
|
||||
SDL_SetRenderDrawColor(renderer, 0, 50, 90, 255);
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
let w = *game_state.pixels_w;
|
||||
let h = *game_state.pixels_h;
|
||||
let bpp = *game_state.pixels_bpp;
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
render_pixel(x, y, game_state);
|
||||
}
|
||||
}
|
||||
|
||||
let texture_area = SDL_Rect { x: 0, y: 0, w: w as i32, h: h as i32 };
|
||||
if SDL_UpdateTexture(*game_state.render_texture, &texture_area, *game_state.pixels as *u8, bpp * w) == false {
|
||||
print_sdl_error("UpdateTexture error");
|
||||
}
|
||||
let src = SDL_FRect { x: 0.0, y: 0.0, w: w as f32, h: h as f32 };
|
||||
let aspect_ratio = src.w / src.h;
|
||||
let scaled_width = screen_height as f32 * aspect_ratio;
|
||||
let dst = SDL_FRect { x: (screen_width as f32 - scaled_width) / 2.0, y: 0.0, w: scaled_width, h: screen_height as f32 };
|
||||
if SDL_RenderTexture(renderer, *game_state.render_texture, &src, &dst) == false {
|
||||
print_sdl_error("RenderTexture error");
|
||||
}
|
||||
|
||||
SDL_RenderPresent(renderer);
|
||||
SDL_Delay(1);
|
||||
|
||||
*game_state.frame_counter = *game_state.frame_counter + 1;
|
||||
let t = SDL_GetTicks();
|
||||
if (t - *game_state.last_fps_reset) >= 1000 {
|
||||
let mut title = String::new();
|
||||
title = title + "cpu raytracer (" + *game_state.frame_counter as u64 + " fps)";
|
||||
SDL_SetWindowTitle(*game_state.window, title.inner);
|
||||
title.free();
|
||||
*game_state.frame_counter = 0;
|
||||
*game_state.last_fps_reset = t;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fn render_pixel(x: u32, y: u32, game_state: &mut GameState) {
|
||||
let w = *game_state.pixels_w;
|
||||
let h = *game_state.pixels_h;
|
||||
let bpp = *game_state.pixels_bpp;
|
||||
|
||||
let samples = 8;
|
||||
let old_sample_weight = 0.9;
|
||||
let new_sample_weight = 0.1 / samples as f32;
|
||||
let mut rgb = vec_mul_scalar(old_sample_weight, [
|
||||
srgb_to_linear(*game_state.pixels[(x + y * w) * bpp + 0]),
|
||||
srgb_to_linear(*game_state.pixels[(x + y * w) * bpp + 1]),
|
||||
srgb_to_linear(*game_state.pixels[(x + y * w) * bpp + 2])
|
||||
]);
|
||||
for sample in 0..samples {
|
||||
rgb = vec_add(rgb, vec_mul_scalar(new_sample_weight, shade(x, y, *game_state.frame_counter, w, h)));
|
||||
}
|
||||
*game_state.pixels[(x + y * w) * bpp + 0] = linear_to_srgb(rgb[0]);
|
||||
*game_state.pixels[(x + y * w) * bpp + 1] = linear_to_srgb(rgb[1]);
|
||||
*game_state.pixels[(x + y * w) * bpp + 2] = linear_to_srgb(rgb[2]);
|
||||
*game_state.pixels[(x + y * w) * bpp + 3] = 255;
|
||||
}
|
||||
|
||||
|
||||
/////////////////
|
||||
/// Rendering ///
|
||||
/////////////////
|
||||
|
||||
struct Ray {
|
||||
origin: [f32; 3],
|
||||
direction: [f32; 3],
|
||||
}
|
||||
|
||||
struct Material {
|
||||
// 0 = lambertian diffuse
|
||||
// 1 = mirror
|
||||
type: u32,
|
||||
// Generally the "color" of the surface (linear factors of how much of each
|
||||
// color channel this surface does not absorb), but the idea is that the
|
||||
// type governs what this means.
|
||||
linear_color: [f32; 3],
|
||||
}
|
||||
|
||||
struct Hit {
|
||||
hit: bool,
|
||||
front_face: bool,
|
||||
distance: f32,
|
||||
normal: [f32; 3],
|
||||
position: [f32; 3],
|
||||
material: Material,
|
||||
}
|
||||
impl Hit {
|
||||
fn none() -> Hit {
|
||||
Hit {
|
||||
hit: false, front_face: true, distance: 0.0, normal: [0.0; 3], position: [0.0; 3],
|
||||
material: Material { type: 0, linear_color: [0.0; 3] },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Sphere {
|
||||
center: [f32; 3],
|
||||
radius: f32,
|
||||
material: Material,
|
||||
}
|
||||
|
||||
fn shade(x: u32, y: u32, t: u32, w: u32, h: u32) -> [f32; 3] {
|
||||
let jitter_x = SDL_randf() - 0.5;
|
||||
let jitter_y = SDL_randf() - 0.5;
|
||||
|
||||
let pixel_scale = 1.0 / h as f32;
|
||||
let pixel_pos = [
|
||||
(x as f32 + jitter_x) * pixel_scale,
|
||||
1.0 - (y as f32 + jitter_y) * pixel_scale,
|
||||
-1.0
|
||||
];
|
||||
let camera_pos = [w as f32 * 0.5f32 * pixel_scale, h as f32 * 0.5f32 * pixel_scale, 0.0f32];
|
||||
let dir = vec_normalize(vec_sub(pixel_pos, camera_pos));
|
||||
let ray = Ray { origin: camera_pos, direction: dir };
|
||||
let beige_lambertian = Material { type: 0, linear_color: [0.3, 0.2, 0.1] };
|
||||
let green_lambertian = Material { type: 0, linear_color: [0.1, 0.5, 0.06] };
|
||||
let greenish_mirror = Material { type: 1, linear_color: [0.9, 1.0, 0.95] };
|
||||
let spheres = [
|
||||
// Ground
|
||||
Sphere { center: vec_sub(camera_pos, [0.0, 100001.0, 0.0]), radius: 100000.0, material: beige_lambertian },
|
||||
// Centered unit sphere
|
||||
Sphere { center: vec_add(camera_pos, [0.0, 0.0, 0.0 - 5.0]), radius: 1.0, material: green_lambertian },
|
||||
// The unit sphere on the right
|
||||
Sphere { center: vec_add(camera_pos, [2.0, 0.0, 0.0 - 6.0]), radius: 1.0, material: greenish_mirror }
|
||||
];
|
||||
return shade_world(ray, &spheres, 3);
|
||||
}
|
||||
|
||||
fn shade_world(ray: Ray, spheres: &[Sphere; 3], bounces_left: u8) -> [f32; 3] {
|
||||
if bounces_left == 0 {
|
||||
return [0.0, 0.0, 0.0];
|
||||
}
|
||||
|
||||
let mut closest_hit = Hit::none();
|
||||
closest_hit.distance = 100.0;
|
||||
for i in 0..3 {
|
||||
let sphere_hit = ray_sphere_closest_hit(ray, *spheres[i], [0.001, closest_hit.distance]);
|
||||
if sphere_hit.hit {
|
||||
closest_hit = sphere_hit;
|
||||
}
|
||||
}
|
||||
|
||||
if closest_hit.hit {
|
||||
//return vec_mul_scalar(0.5, vec_add(closest_hit.normal, [1.0, 1.0, 1.0])); // normal
|
||||
//return vec_mul_scalar(closest_hit.distance / 10.0, [1.0, 1.0, 1.0]); // depth
|
||||
if closest_hit.material.type == 0 {
|
||||
let bounce_dir = vec_normalize(vec_add(closest_hit.normal, random_unit_vec()));
|
||||
let bounce_ray = Ray { origin: closest_hit.position, direction: bounce_dir };
|
||||
return vec_mul_componentwise(
|
||||
closest_hit.material.linear_color,
|
||||
shade_world(bounce_ray, spheres, bounces_left - 1)
|
||||
);
|
||||
} else if closest_hit.material.type == 1 {
|
||||
let bounce_dir = vec_reflect(ray.direction, closest_hit.normal);
|
||||
let bounce_ray = Ray { origin: closest_hit.position, direction: bounce_dir };
|
||||
return vec_mul_componentwise(
|
||||
closest_hit.material.linear_color,
|
||||
shade_world(bounce_ray, spheres, bounces_left - 1)
|
||||
);
|
||||
} else {
|
||||
return [1.0, 0.0, 1.0];
|
||||
}
|
||||
}
|
||||
|
||||
return shade_sky(ray);
|
||||
}
|
||||
|
||||
fn shade_sky(ray: Ray) -> [f32; 3] {
|
||||
let a = 0.5 * (ray.direction[1] + 1.0);
|
||||
return vec_add(
|
||||
vec_mul_scalar(1.0 - a, [1.0, 1.0, 1.0]),
|
||||
vec_mul_scalar(a, [0.5, 0.7, 1.0])
|
||||
);
|
||||
}
|
||||
|
||||
// Returns the distance from the ray origin to the sphere, or -1.0 if the ray doesn't hit.
|
||||
fn ray_sphere_closest_hit(ray: Ray, sphere: Sphere, interval: [f32; 2]) -> Hit {
|
||||
let to_sphere = vec_sub(sphere.center, ray.origin);
|
||||
let h = vec_dot(ray.direction, to_sphere);
|
||||
let c = vec_length_squared(to_sphere) - sphere.radius * sphere.radius;
|
||||
let discriminant = h * h - c;
|
||||
if discriminant < 0.0 {
|
||||
return Hit::none();
|
||||
}
|
||||
|
||||
let discriminant_sqrt = SDL_sqrtf(discriminant);
|
||||
let mut distance = h - discriminant_sqrt;
|
||||
if interval_surrounds(interval, distance) == false {
|
||||
distance = h + discriminant_sqrt;
|
||||
if interval_surrounds(interval, distance) == false {
|
||||
return Hit::none();
|
||||
}
|
||||
}
|
||||
let hit_position = vec_add(ray.origin, vec_mul_scalar(distance, ray.direction));
|
||||
let mut front_face = true;
|
||||
let mut normal = vec_normalize(vec_sub(hit_position, sphere.center));
|
||||
if vec_dot(normal, ray.direction) > 0.0 {
|
||||
normal = vec_mul_scalar(-1.0, normal);
|
||||
front_face = false;
|
||||
}
|
||||
|
||||
return Hit {
|
||||
hit: true,
|
||||
front_face: front_face,
|
||||
distance: distance,
|
||||
normal: normal,
|
||||
position: hit_position,
|
||||
material: sphere.material,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
//////////////////
|
||||
/// Other math ///
|
||||
//////////////////
|
||||
|
||||
fn clamp(min: f32, max: f32, value: f32) -> f32 {
|
||||
if value > max {
|
||||
return max;
|
||||
}
|
||||
if value < min {
|
||||
return min;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
fn abs(f: f32) -> f32 {
|
||||
if f < 0.0 {
|
||||
return f * -1.0;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
fn vec_add(lhs: [f32; 3], rhs: [f32; 3]) -> [f32; 3] {
|
||||
return [lhs[0] + rhs[0], lhs[1] + rhs[1], lhs[2] + rhs[2]];
|
||||
}
|
||||
|
||||
fn vec_sub(lhs: [f32; 3], rhs: [f32; 3]) -> [f32; 3] {
|
||||
return [lhs[0] - rhs[0], lhs[1] - rhs[1], lhs[2] - rhs[2]];
|
||||
}
|
||||
|
||||
fn vec_dot(lhs: [f32; 3], rhs: [f32; 3]) -> f32 {
|
||||
return lhs[0] * rhs[0] + lhs[1] * rhs[1] + lhs[2] * rhs[2];
|
||||
}
|
||||
|
||||
fn vec_mul_componentwise(lhs: [f32; 3], rhs: [f32; 3]) -> [f32; 3] {
|
||||
return [lhs[0] * rhs[0], lhs[1] * rhs[1], lhs[2] * rhs[2]];
|
||||
}
|
||||
|
||||
fn vec_mul_scalar(lhs: f32, rhs: [f32; 3]) -> [f32; 3] {
|
||||
return [lhs * rhs[0], lhs * rhs[1], lhs * rhs[2]];
|
||||
}
|
||||
|
||||
fn vec_normalize(v: [f32; 3]) -> [f32; 3] {
|
||||
let len_reciprocal = 1.0f32 / SDL_sqrtf(vec_length_squared(v));
|
||||
return vec_mul_scalar(len_reciprocal, v);
|
||||
}
|
||||
|
||||
fn vec_length_squared(v: [f32; 3]) -> f32 {
|
||||
return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
|
||||
}
|
||||
|
||||
fn vec_abs(v: [f32; 3]) -> [f32; 3] {
|
||||
return [abs(v[0]), abs(v[1]), abs(v[2])];
|
||||
}
|
||||
|
||||
fn vec_reflect(direction: [f32; 3], normal: [f32; 3]) -> [f32; 3] {
|
||||
return vec_sub(direction, vec_mul_scalar(2.0f32 * vec_dot(direction, normal), normal));
|
||||
}
|
||||
|
||||
fn interval_surrounds(interval: [f32; 2], value: f32) -> bool {
|
||||
return (interval[0] < value) && (value < interval[1]);
|
||||
}
|
||||
|
||||
fn random_unit_vec() -> [f32; 3] {
|
||||
let mut point = [
|
||||
SDL_randf() * 2.0f32 - 1.0f32,
|
||||
SDL_randf() * 2.0f32 - 1.0f32,
|
||||
SDL_randf() * 2.0f32 - 1.0f32
|
||||
];
|
||||
let mut lensq = vec_length_squared(point);
|
||||
while lensq > 1.0 {
|
||||
point = [
|
||||
SDL_randf() * 2.0f32 - 1.0f32,
|
||||
SDL_randf() * 2.0f32 - 1.0f32,
|
||||
SDL_randf() * 2.0f32 - 1.0f32
|
||||
];
|
||||
lensq = vec_length_squared(point);
|
||||
}
|
||||
let len_reciprocal = 1.0f32 / SDL_sqrtf(lensq);
|
||||
return vec_mul_scalar(len_reciprocal, point);
|
||||
}
|
||||
|
||||
fn random_unit_vec_on_hemi(normal: [f32; 3]) -> [f32; 3] {
|
||||
let rand_vec = random_unit_vec();
|
||||
if vec_dot(rand_vec, normal) < 0.0f32 {
|
||||
return vec_mul_scalar(0.0f32 - 1.0f32, rand_vec);
|
||||
}
|
||||
return rand_vec;
|
||||
}
|
||||
|
||||
fn linear_to_srgb(linear: f32) -> u8 {
|
||||
let mut floating_srgb = 0.0;
|
||||
if linear <= 0.0031308f32 {
|
||||
floating_srgb = 12.92f32 * linear;
|
||||
} else {
|
||||
floating_srgb = SDL_powf(linear as f32, 1.0 / 2.4) * 1.055f32 - 0.055f32;
|
||||
}
|
||||
let clamped = clamp(0.0, 1.0, floating_srgb);
|
||||
return (clamped * 255.999) as u8;
|
||||
}
|
||||
|
||||
fn srgb_to_linear(srgb: u8) -> f32 {
|
||||
let floating_srgb = srgb as f32 / 255.0;
|
||||
if floating_srgb <= 0.04045f32 {
|
||||
return floating_srgb / 12.92f32;
|
||||
}
|
||||
return SDL_powf((floating_srgb as f32 + 0.055) / 1.055, 2.4);
|
||||
}
|
||||
@ -9,4 +9,4 @@ fn fibonacci(value: u16) -> u16 {
|
||||
return 1;
|
||||
}
|
||||
fibonacci(value - 1) + fibonacci(value - 2)
|
||||
}
|
||||
}
|
||||
6
examples/foreign_init.reid
Normal file
6
examples/foreign_init.reid
Normal file
@ -0,0 +1,6 @@
|
||||
import foreign_struct::Vec2;
|
||||
|
||||
fn main() -> u32 {
|
||||
let a = Vec2 {x: 16, y: 32};
|
||||
return a.x;
|
||||
}
|
||||
1
examples/foreign_struct.reid
Normal file
1
examples/foreign_struct.reid
Normal file
@ -0,0 +1 @@
|
||||
struct Vec2 { x: u32, y: u32 }
|
||||
9
examples/loop_edge_case.reid
Normal file
9
examples/loop_edge_case.reid
Normal file
@ -0,0 +1,9 @@
|
||||
fn main() -> i32 {
|
||||
for i in 0..1 {
|
||||
let j = i;
|
||||
if i != j {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1,17 +1,6 @@
|
||||
// Arithmetic, function calls and imports!
|
||||
|
||||
import std::print;
|
||||
import std::from_str;
|
||||
import std::add_num_to_str;
|
||||
import std::free_string;
|
||||
|
||||
fn main() -> u32 {
|
||||
for i in 0 .. 15 {
|
||||
let mut text = from_str("num: ");
|
||||
add_num_to_str(&mut text, i);
|
||||
print(text);
|
||||
free_string(&text);
|
||||
}
|
||||
|
||||
let mut num = 0;
|
||||
while num < 10 {
|
||||
|
||||
9
examples/macro_easy.reid
Normal file
9
examples/macro_easy.reid
Normal file
@ -0,0 +1,9 @@
|
||||
import std::String;
|
||||
import std::print;
|
||||
|
||||
fn main() -> u8 {
|
||||
let bytes = include_bytes!("./macro_easy_file.txt");
|
||||
print(String::new() + bytes.length());
|
||||
print(String::new() + (include_bytes!("./macro_easy_file.txt") as *u8)[1] as u64);
|
||||
return (include_bytes!("./macro_easy_file.txt") as *u8)[0];
|
||||
}
|
||||
1
examples/macro_easy_file.txt
Normal file
1
examples/macro_easy_file.txt
Normal file
@ -0,0 +1 @@
|
||||
hello
|
||||
15
examples/math_intrinsics.reid
Normal file
15
examples/math_intrinsics.reid
Normal 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];
|
||||
}
|
||||
8
examples/module_importee.reid
Normal file
8
examples/module_importee.reid
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
struct Otus {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
pub fn test() -> Otus {
|
||||
Otus {field: 4}
|
||||
}
|
||||
11
examples/module_impoter.reid
Normal file
11
examples/module_impoter.reid
Normal file
@ -0,0 +1,11 @@
|
||||
// Arithmetic, function calls and imports!
|
||||
|
||||
import module_importee::Otus;
|
||||
import module_importee::test;
|
||||
|
||||
fn main() -> u32 {
|
||||
let value = 0b110;
|
||||
let other = 0o17;
|
||||
|
||||
return value * other + test().field * -value;
|
||||
}
|
||||
25
examples/mutable_inner.reid
Normal file
25
examples/mutable_inner.reid
Normal file
@ -0,0 +1,25 @@
|
||||
struct Game {}
|
||||
|
||||
impl Game {
|
||||
pub fn run_frame(&mut self) {}
|
||||
}
|
||||
|
||||
struct Platform {
|
||||
game: Game,
|
||||
}
|
||||
|
||||
impl Platform {
|
||||
pub fn new() -> Platform {
|
||||
return Platform { game: Game {} };
|
||||
}
|
||||
|
||||
pub fn run_frame(&mut self) {
|
||||
*self.game.run_frame();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> i32 {
|
||||
let mut platform = Platform::new();
|
||||
platform.run_frame();
|
||||
return 0;
|
||||
}
|
||||
4
examples/nullptr_comparison.reid
Normal file
4
examples/nullptr_comparison.reid
Normal file
@ -0,0 +1,4 @@
|
||||
fn main() -> bool {
|
||||
let ptr = i32::null();
|
||||
return i32::is_null(ptr);
|
||||
}
|
||||
10
examples/or_bitwise.reid
Normal file
10
examples/or_bitwise.reid
Normal file
@ -0,0 +1,10 @@
|
||||
// Arithmetic, function calls and imports!
|
||||
|
||||
|
||||
fn main() -> bool {
|
||||
let bwand = (0xff & 0xf0) >> 4;
|
||||
let bwor = (0x0fu32 | 0x00) << 4;
|
||||
let bwxor = (0xf0 | 0x0f);
|
||||
|
||||
return (bwxor == 255) && ((bwand == 15) || false) && (bwor == 240);
|
||||
}
|
||||
@ -1,9 +1,7 @@
|
||||
// Arithmetic, function calls and imports!
|
||||
|
||||
import std::allocate;
|
||||
|
||||
fn main() -> u8 {
|
||||
let mut ptr = allocate(4);
|
||||
let mut ptr = u8::malloc(4);
|
||||
|
||||
ptr[0] = 5;
|
||||
|
||||
|
||||
7
examples/test.reid
Normal file
7
examples/test.reid
Normal file
@ -0,0 +1,7 @@
|
||||
// Arithmetic, function calls and imports!
|
||||
|
||||
|
||||
fn main() -> bool {
|
||||
|
||||
return 5.0 > -1;
|
||||
}
|
||||
@ -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
|
||||
|
||||
1
reid-llvm-lib
Submodule
1
reid-llvm-lib
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 100cd96a6dc3bb882ce60e78c4eab47e77fdd8c4
|
||||
@ -1,12 +0,0 @@
|
||||
[package]
|
||||
name = "reid-lib"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
## LLVM Bindings
|
||||
llvm-sys = {version ="201.0.1", features=["prefer-dynamic"] }
|
||||
## Make it easier to generate errors
|
||||
thiserror = "1.0.44"
|
||||
@ -1,65 +0,0 @@
|
||||
use reid_lib::{CmpPredicate, ConstValue, Context, FunctionFlags, Instr, TerminatorKind, Type};
|
||||
|
||||
fn main() {
|
||||
use ConstValue::*;
|
||||
use Instr::*;
|
||||
|
||||
let context = Context::new("libtest");
|
||||
|
||||
let module = context.module("test", true);
|
||||
|
||||
let main = module.function("main", Type::I32, Vec::new(), FunctionFlags::default());
|
||||
let mut m_entry = main.block("entry");
|
||||
|
||||
let fibonacci = module.function(
|
||||
"fibonacci",
|
||||
Type::I32,
|
||||
vec![Type::I32],
|
||||
FunctionFlags::default(),
|
||||
);
|
||||
|
||||
let arg = m_entry.build_named("const", Constant(I32(5))).unwrap();
|
||||
let fibonacci_call = m_entry
|
||||
.build_named("const", FunctionCall(fibonacci.value(), vec![arg]))
|
||||
.unwrap();
|
||||
m_entry
|
||||
.terminate(TerminatorKind::Ret(fibonacci_call))
|
||||
.unwrap();
|
||||
|
||||
let mut f_entry = fibonacci.block("entry");
|
||||
|
||||
let num_3 = f_entry.build_named("const", Constant(I32(3))).unwrap();
|
||||
let param_n = f_entry.build_named("param", Param(0)).unwrap();
|
||||
let cond = f_entry
|
||||
.build_named("cmp", ICmp(CmpPredicate::LT, param_n, num_3))
|
||||
.unwrap();
|
||||
|
||||
let mut then_b = fibonacci.block("then");
|
||||
let mut else_b = fibonacci.block("else");
|
||||
|
||||
f_entry
|
||||
.terminate(TerminatorKind::CondBr(cond, then_b.value(), else_b.value()))
|
||||
.unwrap();
|
||||
|
||||
let ret_const = then_b.build_named("const", Constant(I32(1))).unwrap();
|
||||
then_b.terminate(TerminatorKind::Ret(ret_const)).unwrap();
|
||||
|
||||
let const_1 = else_b.build_named("const", Constant(I32(1))).unwrap();
|
||||
let const_2 = else_b.build_named("const", Constant(I32(2))).unwrap();
|
||||
let param_1 = else_b.build_named("sub", Sub(param_n, const_1)).unwrap();
|
||||
let param_2 = else_b.build_named("sub", Sub(param_n, const_2)).unwrap();
|
||||
let call_1 = else_b
|
||||
.build_named("fibonacci", FunctionCall(fibonacci.value(), vec![param_1]))
|
||||
.unwrap();
|
||||
let call_2 = else_b
|
||||
.build_named("fibonacci", FunctionCall(fibonacci.value(), vec![param_2]))
|
||||
.unwrap();
|
||||
|
||||
let add = else_b.build_named("add", Add(call_1, call_2)).unwrap();
|
||||
|
||||
else_b.terminate(TerminatorKind::Ret(add)).unwrap();
|
||||
|
||||
dbg!(&context);
|
||||
|
||||
context.compile(None, Vec::new());
|
||||
}
|
||||
@ -1,686 +0,0 @@
|
||||
//! This module contains simply [`Builder`] and it's related utility Values.
|
||||
//! Builder is the actual struct being modified when building the LLIR.
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
Block, BlockData, CompileResult, CustomTypeKind, ErrorKind, FunctionData, Instr, InstructionData, ModuleData,
|
||||
NamedStruct, TerminatorKind, Type, TypeCategory, TypeData,
|
||||
debug_information::{
|
||||
DebugInformation, DebugLocationValue, DebugMetadataValue, DebugProgramValue, InstructionDebugRecordData,
|
||||
},
|
||||
util::match_types,
|
||||
};
|
||||
|
||||
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
|
||||
pub struct ModuleValue(pub(crate) usize);
|
||||
|
||||
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
|
||||
pub struct TypeValue(pub(crate) ModuleValue, pub(crate) usize);
|
||||
|
||||
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
|
||||
pub struct FunctionValue(pub(crate) ModuleValue, pub(crate) usize);
|
||||
|
||||
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
|
||||
pub struct BlockValue(pub(crate) FunctionValue, pub(crate) usize);
|
||||
|
||||
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
|
||||
pub struct InstructionValue(pub(crate) BlockValue, pub(crate) usize);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ModuleHolder {
|
||||
pub(crate) value: ModuleValue,
|
||||
pub(crate) data: ModuleData,
|
||||
pub(crate) functions: Vec<FunctionHolder>,
|
||||
pub(crate) types: Vec<TypeHolder>,
|
||||
pub(crate) debug_information: Option<DebugInformation>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TypeHolder {
|
||||
pub(crate) value: TypeValue,
|
||||
pub(crate) data: TypeData,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FunctionHolder {
|
||||
pub(crate) value: FunctionValue,
|
||||
pub(crate) data: FunctionData,
|
||||
pub(crate) blocks: Vec<BlockHolder>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BlockHolder {
|
||||
pub(crate) value: BlockValue,
|
||||
pub(crate) data: BlockData,
|
||||
pub(crate) instructions: Vec<InstructionHolder>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InstructionHolder {
|
||||
pub(crate) value: InstructionValue,
|
||||
pub(crate) data: InstructionData,
|
||||
pub(crate) name: String,
|
||||
pub(crate) record: Option<InstructionDebugRecordData>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct Builder {
|
||||
pub(crate) modules: Rc<RefCell<Vec<ModuleHolder>>>,
|
||||
pub(crate) producer: String,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
pub fn new(producer: String) -> Builder {
|
||||
Builder {
|
||||
modules: Rc::new(RefCell::new(Vec::new())),
|
||||
producer,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_module(&self, data: ModuleData) -> ModuleValue {
|
||||
let value = ModuleValue(self.modules.borrow().len());
|
||||
self.modules.borrow_mut().push(ModuleHolder {
|
||||
value,
|
||||
data,
|
||||
functions: Vec::new(),
|
||||
types: Vec::new(),
|
||||
debug_information: None,
|
||||
});
|
||||
value
|
||||
}
|
||||
|
||||
pub(crate) fn set_debug_information(&self, mod_val: &ModuleValue, debug_info: DebugInformation) {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(mod_val.0);
|
||||
module.debug_information = Some(debug_info);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn add_type(&self, mod_val: &ModuleValue, data: TypeData) -> TypeValue {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(mod_val.0);
|
||||
let value = TypeValue(module.value, module.types.len());
|
||||
module.types.push(TypeHolder { value, data });
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn add_function(&self, mod_val: &ModuleValue, data: FunctionData) -> FunctionValue {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(mod_val.0);
|
||||
let value = FunctionValue(module.value, module.functions.len());
|
||||
module.functions.push(FunctionHolder {
|
||||
value,
|
||||
data,
|
||||
blocks: Vec::new(),
|
||||
});
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn add_block(&self, fun_val: &FunctionValue, data: BlockData) -> BlockValue {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(fun_val.0.0);
|
||||
let function = module.functions.get_unchecked_mut(fun_val.1);
|
||||
let value = BlockValue(function.value, function.blocks.len());
|
||||
function.blocks.push(BlockHolder {
|
||||
value,
|
||||
data,
|
||||
instructions: Vec::new(),
|
||||
});
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn add_instruction(
|
||||
&self,
|
||||
block_val: &BlockValue,
|
||||
data: InstructionData,
|
||||
name: String,
|
||||
) -> CompileResult<InstructionValue> {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(block_val.0.0.0);
|
||||
let function = module.functions.get_unchecked_mut(block_val.0.1);
|
||||
let block = function.blocks.get_unchecked_mut(block_val.1);
|
||||
let value = InstructionValue(block.value, block.instructions.len());
|
||||
block.instructions.push(InstructionHolder {
|
||||
value,
|
||||
data,
|
||||
name,
|
||||
record: None,
|
||||
});
|
||||
|
||||
// Drop modules so that it is no longer mutable borrowed
|
||||
// (check_instruction requires an immutable borrow).
|
||||
drop(modules);
|
||||
|
||||
self.check_instruction(&value)?;
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn add_instruction_location(&self, value: &InstructionValue, location: DebugLocationValue) {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(value.0.0.0.0);
|
||||
let function = module.functions.get_unchecked_mut(value.0.0.1);
|
||||
let block = function.blocks.get_unchecked_mut(value.0.1);
|
||||
let instr = block.instructions.get_unchecked_mut(value.1);
|
||||
instr.data.location = Some(location)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn add_instruction_metadata(&self, value: &InstructionValue, metadata: DebugMetadataValue) {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(value.0.0.0.0);
|
||||
let function = module.functions.get_unchecked_mut(value.0.0.1);
|
||||
let block = function.blocks.get_unchecked_mut(value.0.1);
|
||||
let instr = block.instructions.get_unchecked_mut(value.1);
|
||||
instr.data.meta = Some(metadata)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn add_instruction_record(&self, value: &InstructionValue, record: InstructionDebugRecordData) {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(value.0.0.0.0);
|
||||
let function = module.functions.get_unchecked_mut(value.0.0.1);
|
||||
let block = function.blocks.get_unchecked_mut(value.0.1);
|
||||
let instr = block.instructions.get_unchecked_mut(value.1);
|
||||
instr.record = Some(record)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn set_debug_subprogram(&self, value: &FunctionValue, subprogram: DebugProgramValue) {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(value.0.0);
|
||||
let function = module.functions.get_unchecked_mut(value.1);
|
||||
function.data.debug = Some(subprogram)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn terminate(&self, block: &BlockValue, value: TerminatorKind) -> CompileResult<()> {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(block.0.0.0);
|
||||
let function = module.functions.get_unchecked_mut(block.0.1);
|
||||
let block = function.blocks.get_unchecked_mut(block.1);
|
||||
if let Some(_) = &block.data.terminator {
|
||||
Err(ErrorKind::Null)
|
||||
} else {
|
||||
block.data.terminator = Some(value);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn set_terminator_location(
|
||||
&self,
|
||||
block: &BlockValue,
|
||||
location: DebugLocationValue,
|
||||
) -> CompileResult<()> {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(block.0.0.0);
|
||||
let function = module.functions.get_unchecked_mut(block.0.1);
|
||||
let block = function.blocks.get_unchecked_mut(block.1);
|
||||
if let Some(_) = &block.data.terminator_location {
|
||||
Err(ErrorKind::Null)
|
||||
} else {
|
||||
block.data.terminator_location = Some(location);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn delete_block(&self, block: &BlockValue) -> CompileResult<()> {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(block.0.0.0);
|
||||
let function = module.functions.get_unchecked_mut(block.0.1);
|
||||
let block = function.blocks.get_unchecked_mut(block.1);
|
||||
block.data.deleted = true;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) unsafe fn module_data(&self, value: &ModuleValue) -> ModuleData {
|
||||
unsafe { self.modules.borrow().get_unchecked(value.0).data.clone() }
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn function_data(&self, value: &FunctionValue) -> FunctionData {
|
||||
unsafe {
|
||||
self.modules
|
||||
.borrow()
|
||||
.get_unchecked(value.0.0)
|
||||
.functions
|
||||
.get_unchecked(value.1)
|
||||
.data
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) unsafe fn block_data(&self, value: &BlockValue) -> BlockData {
|
||||
unsafe {
|
||||
self.modules
|
||||
.borrow()
|
||||
.get_unchecked(value.0.0.0)
|
||||
.functions
|
||||
.get_unchecked(value.0.1)
|
||||
.blocks
|
||||
.get_unchecked(value.1)
|
||||
.data
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn instr_data(&self, value: &InstructionValue) -> InstructionData {
|
||||
unsafe {
|
||||
self.modules
|
||||
.borrow()
|
||||
.get_unchecked(value.0.0.0.0)
|
||||
.functions
|
||||
.get_unchecked(value.0.0.1)
|
||||
.blocks
|
||||
.get_unchecked(value.0.1)
|
||||
.instructions
|
||||
.get_unchecked(value.1)
|
||||
.data
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn type_data(&self, value: &TypeValue) -> TypeData {
|
||||
unsafe {
|
||||
self.modules
|
||||
.borrow()
|
||||
.get_unchecked(value.0.0)
|
||||
.types
|
||||
.get_unchecked(value.1)
|
||||
.data
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn find_module<'ctx>(&'ctx self, value: ModuleValue) -> ModuleHolder {
|
||||
unsafe { self.modules.borrow().get_unchecked(value.0).clone() }
|
||||
}
|
||||
|
||||
pub(crate) fn get_modules(&self) -> Rc<RefCell<Vec<ModuleHolder>>> {
|
||||
self.modules.clone()
|
||||
}
|
||||
|
||||
pub fn check_instruction(&self, instruction: &InstructionValue) -> CompileResult<()> {
|
||||
unsafe {
|
||||
match self.instr_data(&instruction).kind {
|
||||
Instr::Param(_) => Ok(()),
|
||||
Instr::Constant(_) => Ok(()),
|
||||
Instr::Add(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category().integer() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Null)
|
||||
}
|
||||
}
|
||||
Instr::FAdd(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Null)
|
||||
}
|
||||
}
|
||||
Instr::Sub(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category().integer() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Null)
|
||||
}
|
||||
}
|
||||
Instr::FSub(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Null)
|
||||
}
|
||||
}
|
||||
Instr::Mul(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category().integer() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Null)
|
||||
}
|
||||
}
|
||||
Instr::FMul(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Null)
|
||||
}
|
||||
}
|
||||
Instr::UDiv(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::UnsignedInteger {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Null)
|
||||
}
|
||||
}
|
||||
Instr::SDiv(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::SignedInteger {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Null)
|
||||
}
|
||||
}
|
||||
Instr::FDiv(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Null)
|
||||
}
|
||||
}
|
||||
Instr::URem(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::UnsignedInteger {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Null)
|
||||
}
|
||||
}
|
||||
Instr::SRem(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::SignedInteger {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Null)
|
||||
}
|
||||
}
|
||||
Instr::FRem(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Null)
|
||||
}
|
||||
}
|
||||
Instr::And(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
|
||||
Instr::ICmp(_, lhs, rhs) => {
|
||||
let t = match_types(&lhs, &rhs, self)?;
|
||||
if t.category().comparable() || !t.category().integer() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Null) // TODO error: Types not comparable
|
||||
}
|
||||
}
|
||||
Instr::FCmp(_, lhs, rhs) => {
|
||||
let t = match_types(&lhs, &rhs, self)?;
|
||||
if t.category().comparable() || t.category() != TypeCategory::Real {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Null) // TODO error: Types not comparable
|
||||
}
|
||||
}
|
||||
Instr::FunctionCall(fun, params) => {
|
||||
let param_types = self.function_data(&fun).params;
|
||||
if param_types.len() != params.len() {
|
||||
return Err(ErrorKind::Null); // TODO error: invalid amount of params
|
||||
}
|
||||
for (a, b) in param_types.iter().zip(params) {
|
||||
if *a != b.get_type(&self)? {
|
||||
return Err(ErrorKind::Null); // TODO error: params do not match
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Instr::Phi(vals) => {
|
||||
let mut iter = vals.iter();
|
||||
// TODO error: Phi must contain at least one item
|
||||
|
||||
// TODO error: compile can actually crash here if any of the
|
||||
// incoming values come from blocks that are added later
|
||||
// than the one where this one exists.
|
||||
|
||||
let first = iter.next().ok_or(ErrorKind::Null)?;
|
||||
for item in iter {
|
||||
match_types(first, item, &self)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Instr::Alloca(_) => Ok(()),
|
||||
Instr::Load(ptr, load_ty) => {
|
||||
let ptr_ty = ptr.get_type(&self)?;
|
||||
if let Type::Ptr(ptr_ty_inner) = ptr_ty {
|
||||
if *ptr_ty_inner == load_ty {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Null) // TODO error: inner type mismatch
|
||||
}
|
||||
} else {
|
||||
Err(ErrorKind::Null) // TODO error: not a pointer
|
||||
}
|
||||
}
|
||||
Instr::Store(ptr, _) => {
|
||||
let ty = ptr.get_type(&self)?;
|
||||
if let Type::Ptr(_) = ty {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Null) // TODO error: not a pointer
|
||||
}
|
||||
}
|
||||
Instr::ArrayAlloca(_, val) => {
|
||||
if val.get_type(self)?.category() == TypeCategory::UnsignedInteger {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Null) // TODO error: not a pointer
|
||||
}
|
||||
}
|
||||
Instr::GetElemPtr(ptr_val, _) => {
|
||||
let ptr_ty = ptr_val.get_type(&self)?;
|
||||
match ptr_ty {
|
||||
Type::Ptr(_) => Ok(()),
|
||||
_ => Err(ErrorKind::Null),
|
||||
}
|
||||
}
|
||||
Instr::GetStructElemPtr(ptr_val, idx) => {
|
||||
let ptr_ty = ptr_val.get_type(&self)?;
|
||||
if let Type::Ptr(ty) = ptr_ty {
|
||||
if let Type::CustomType(val) = *ty {
|
||||
match self.type_data(&val).kind {
|
||||
CustomTypeKind::NamedStruct(NamedStruct(_, fields)) => {
|
||||
if fields.len() <= idx as usize {
|
||||
return Err(ErrorKind::Null); // TODO error: no such field
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Null) // TODO error: not a struct
|
||||
}
|
||||
} else {
|
||||
Err(ErrorKind::Null) // TODO error: not a pointer
|
||||
}
|
||||
}
|
||||
Instr::ExtractValue(val, _) => {
|
||||
let val_ty = val.get_type(&self)?;
|
||||
match val_ty {
|
||||
Type::CustomType(custom) => match self.type_data(&custom).kind {
|
||||
CustomTypeKind::NamedStruct(_) => Ok(()),
|
||||
},
|
||||
Type::Array(_, _) => Ok(()),
|
||||
_ => Err(ErrorKind::Null),
|
||||
}
|
||||
}
|
||||
Instr::Trunc(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::ZExt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::SExt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::FPTrunc(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::FPExt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::FPToUI(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::FPToSI(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::UIToFP(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::SIToFP(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::PtrToInt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::IntToPtr(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::BitCast(..) => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_block_used(&self, block_v: BlockValue) -> bool {
|
||||
unsafe {
|
||||
let modules = self.modules.borrow();
|
||||
let module = modules.get_unchecked(block_v.0.0.0);
|
||||
let function = module.functions.get_unchecked(block_v.0.1);
|
||||
let block = function.blocks.get_unchecked(block_v.1);
|
||||
|
||||
if block.instructions.len() > 0 || block.data.terminator.is_some() {
|
||||
return true;
|
||||
}
|
||||
|
||||
for other in &function.blocks {
|
||||
if let Some(term) = &other.data.terminator {
|
||||
match term {
|
||||
TerminatorKind::Ret(_) => {}
|
||||
TerminatorKind::RetVoid => {}
|
||||
TerminatorKind::Br(other_val) => {
|
||||
if other_val == &block_v {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
TerminatorKind::CondBr(_, then_other_v, else_other_v) => {
|
||||
if then_other_v == &block_v || else_other_v == &block_v {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionValue {
|
||||
pub fn with_location(self, block: &Block, location: DebugLocationValue) -> InstructionValue {
|
||||
unsafe {
|
||||
block.builder.add_instruction_location(&self, location);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn maybe_location(self, block: &mut Block, location: Option<DebugLocationValue>) -> InstructionValue {
|
||||
unsafe {
|
||||
if let Some(location) = location {
|
||||
block.builder.add_instruction_location(&self, location);
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_record(&self, block: &mut Block, record: InstructionDebugRecordData) {
|
||||
unsafe {
|
||||
block.builder.add_instruction_record(self, record);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_type(&self, builder: &Builder) -> CompileResult<Type> {
|
||||
use Instr::*;
|
||||
unsafe {
|
||||
match &builder.instr_data(self).kind {
|
||||
Param(nth) => builder
|
||||
.function_data(&self.0.0)
|
||||
.params
|
||||
.get(*nth)
|
||||
.cloned()
|
||||
.ok_or(ErrorKind::Null),
|
||||
Constant(c) => Ok(c.get_type()),
|
||||
Add(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
FAdd(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
Sub(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
FSub(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
Mul(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
FMul(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
UDiv(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
SDiv(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
FDiv(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
URem(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
SRem(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
FRem(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
And(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
ICmp(_, _, _) => Ok(Type::Bool),
|
||||
FCmp(_, _, _) => Ok(Type::Bool),
|
||||
FunctionCall(function_value, _) => Ok(builder.function_data(function_value).ret),
|
||||
Phi(values) => values.first().ok_or(ErrorKind::Null).and_then(|v| v.get_type(&builder)),
|
||||
Alloca(ty) => Ok(Type::Ptr(Box::new(ty.clone()))),
|
||||
Load(_, ty) => Ok(ty.clone()),
|
||||
Store(_, value) => value.get_type(builder),
|
||||
ArrayAlloca(ty, _) => Ok(Type::Ptr(Box::new(ty.clone()))),
|
||||
GetElemPtr(instr, _) => {
|
||||
let instr_ty = instr.get_type(builder)?;
|
||||
let Type::Ptr(inner_ty) = &instr_ty else {
|
||||
panic!("GetStructElemPtr on non-pointer! ({:?})", &instr_ty)
|
||||
};
|
||||
match *inner_ty.clone() {
|
||||
Type::Array(elem_ty, _) => Ok(Type::Ptr(Box::new(*elem_ty.clone()))),
|
||||
_ => Ok(instr_ty),
|
||||
}
|
||||
}
|
||||
GetStructElemPtr(instr, idx) => {
|
||||
let instr_ty = instr.get_type(builder)?;
|
||||
let Type::Ptr(inner_ty) = instr_ty else {
|
||||
panic!("GetStructElemPtr on non-pointer! ({:?})", &instr_ty)
|
||||
};
|
||||
let Type::CustomType(ty_value) = *inner_ty else {
|
||||
panic!("GetStructElemPtr on non-struct! ({:?})", &inner_ty)
|
||||
};
|
||||
let field_ty = match builder.type_data(&ty_value).kind {
|
||||
CustomTypeKind::NamedStruct(NamedStruct(_, fields)) => {
|
||||
fields.get_unchecked(*idx as usize).clone()
|
||||
}
|
||||
};
|
||||
Ok(Type::Ptr(Box::new(field_ty)))
|
||||
}
|
||||
ExtractValue(instr, idx) => {
|
||||
let instr_ty = instr.get_type(builder)?;
|
||||
Ok(match instr_ty {
|
||||
Type::CustomType(struct_ty) => {
|
||||
let data = builder.type_data(&struct_ty);
|
||||
match data.kind {
|
||||
CustomTypeKind::NamedStruct(named_struct) => {
|
||||
named_struct.1.get(*idx as usize).unwrap().clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
Type::Array(elem_ty, _) => *elem_ty.clone(),
|
||||
_ => return Err(ErrorKind::Null),
|
||||
})
|
||||
}
|
||||
Trunc(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
ZExt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
SExt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
FPTrunc(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
FPExt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
FPToUI(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
FPToSI(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
UIToFP(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
SIToFP(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
PtrToInt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
IntToPtr(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
BitCast(_, ty) => Ok(ty.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cast_to(&self, builder: &Builder, ty: &Type) -> CompileResult<Instr> {
|
||||
self.get_type(builder)?
|
||||
.cast_instruction(*self, &ty)
|
||||
.ok_or(ErrorKind::Null)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,389 +0,0 @@
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::builder::InstructionValue;
|
||||
|
||||
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||
pub struct DebugScopeValue(pub Vec<usize>);
|
||||
|
||||
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
||||
pub struct DebugLocationValue(pub DebugProgramValue, pub usize);
|
||||
|
||||
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
||||
pub struct DebugTypeValue(pub usize);
|
||||
|
||||
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
||||
pub struct DebugMetadataValue(pub usize);
|
||||
|
||||
/// Represents either a subprogram, or the compilation context
|
||||
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
||||
pub struct DebugProgramValue(pub usize);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DebugFileData {
|
||||
pub name: String,
|
||||
pub directory: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct DebugScopeHolder {
|
||||
pub(crate) value: DebugScopeValue,
|
||||
pub(crate) location: Option<DebugLocation>,
|
||||
pub(crate) inner_scopes: Vec<DebugScopeHolder>,
|
||||
pub(crate) locations: Vec<DebugLocationHolder>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DebugMetadataHolder {
|
||||
pub(crate) location: DebugLocation,
|
||||
pub(crate) value: DebugMetadataValue,
|
||||
pub(crate) data: DebugMetadata,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DebugTypeHolder {
|
||||
pub(crate) value: DebugTypeValue,
|
||||
pub(crate) data: DebugTypeData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DebugSubprogramHolder {
|
||||
pub(crate) _value: DebugProgramValue,
|
||||
pub(crate) data: DebugSubprogramData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct DebugLocationHolder {
|
||||
pub(crate) program: DebugProgramValue,
|
||||
pub(crate) value: DebugLocationValue,
|
||||
pub(crate) location: DebugLocation,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DebugInformation {
|
||||
pub file: DebugFileData,
|
||||
// scope: Rc<RefCell<DebugScopeHolder>>,
|
||||
locations: Rc<RefCell<Vec<DebugLocationHolder>>>,
|
||||
programs: Rc<RefCell<Vec<DebugSubprogramHolder>>>,
|
||||
metadata: Rc<RefCell<Vec<DebugMetadataHolder>>>,
|
||||
types: Rc<RefCell<Vec<DebugTypeHolder>>>,
|
||||
}
|
||||
|
||||
impl DebugInformation {
|
||||
pub fn from_file(file: DebugFileData) -> (DebugInformation, DebugProgramValue) {
|
||||
(
|
||||
DebugInformation {
|
||||
file,
|
||||
// scope: Rc::new(RefCell::new(DebugScopeHolder {
|
||||
// value: scope_value.clone(),
|
||||
// inner_scopes: Vec::new(),
|
||||
// locations: Vec::new(),
|
||||
// location: None,
|
||||
// })),
|
||||
locations: Rc::new(RefCell::new(Vec::new())),
|
||||
metadata: Rc::new(RefCell::new(Vec::new())),
|
||||
programs: Rc::new(RefCell::new(Vec::new())),
|
||||
types: Rc::new(RefCell::new(Vec::new())),
|
||||
},
|
||||
DebugProgramValue(0),
|
||||
)
|
||||
}
|
||||
|
||||
// pub fn inner_scope(
|
||||
// &self,
|
||||
// parent: &DebugScopeValue,
|
||||
// location: DebugLocation,
|
||||
// ) -> DebugScopeValue {
|
||||
// unsafe {
|
||||
// let mut outer_scope = RefMut::map(self.scope.borrow_mut(), |mut v| {
|
||||
// for i in &parent.0 {
|
||||
// v = v.inner_scopes.get_unchecked_mut(*i);
|
||||
// }
|
||||
// v
|
||||
// });
|
||||
|
||||
// let mut arr = parent.0.clone();
|
||||
// arr.push(parent.0.len());
|
||||
// let value = DebugScopeValue(arr);
|
||||
|
||||
// outer_scope.inner_scopes.push(DebugScopeHolder {
|
||||
// value: value.clone(),
|
||||
// inner_scopes: Vec::new(),
|
||||
// locations: Vec::new(),
|
||||
// location: Some(location),
|
||||
// });
|
||||
// value
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn location(
|
||||
&self,
|
||||
program_value: &DebugProgramValue,
|
||||
location: DebugLocation,
|
||||
) -> DebugLocationValue {
|
||||
let value = DebugLocationValue(program_value.clone(), self.locations.borrow().len());
|
||||
let location = DebugLocationHolder {
|
||||
program: *program_value,
|
||||
value: value.clone(),
|
||||
location,
|
||||
};
|
||||
self.locations.borrow_mut().push(location);
|
||||
value
|
||||
}
|
||||
|
||||
pub fn debug_type(&self, kind: DebugTypeData) -> DebugTypeValue {
|
||||
let mut types = self.types.borrow_mut();
|
||||
let value = DebugTypeValue(types.len());
|
||||
types.push(DebugTypeHolder {
|
||||
value: value.clone(),
|
||||
data: kind,
|
||||
});
|
||||
value
|
||||
}
|
||||
|
||||
pub fn metadata(&self, location: &DebugLocation, kind: DebugMetadata) -> DebugMetadataValue {
|
||||
let mut metadata = self.metadata.borrow_mut();
|
||||
let value = DebugMetadataValue(metadata.len());
|
||||
metadata.push(DebugMetadataHolder {
|
||||
location: *location,
|
||||
value: value.clone(),
|
||||
data: kind,
|
||||
});
|
||||
value
|
||||
}
|
||||
|
||||
pub fn subprogram(&self, kind: DebugSubprogramData) -> DebugProgramValue {
|
||||
let mut subprogram = self.programs.borrow_mut();
|
||||
let value = DebugProgramValue(subprogram.len() + 1);
|
||||
subprogram.push(DebugSubprogramHolder {
|
||||
_value: value.clone(),
|
||||
data: kind,
|
||||
});
|
||||
value
|
||||
}
|
||||
|
||||
pub fn get_metadata(&self, value: DebugMetadataValue) -> DebugMetadata {
|
||||
unsafe { self.metadata.borrow().get_unchecked(value.0).data.clone() }
|
||||
}
|
||||
|
||||
pub fn get_metadata_location(&self, value: DebugMetadataValue) -> DebugLocation {
|
||||
unsafe { self.metadata.borrow().get_unchecked(value.0).location }
|
||||
}
|
||||
|
||||
pub fn get_subprogram_data(&self, value: DebugProgramValue) -> Option<DebugSubprogramData> {
|
||||
if value.0 == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(self.get_subprogram_data_unchecked(&value))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_type_data(&self, value: DebugTypeValue) -> DebugTypeData {
|
||||
unsafe { self.types.borrow().get_unchecked(value.0).data.clone() }
|
||||
}
|
||||
|
||||
pub fn get_location(&self, value: DebugLocationValue) -> DebugLocation {
|
||||
unsafe {
|
||||
self.locations
|
||||
.borrow()
|
||||
.get_unchecked(value.1)
|
||||
.location
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_metadatas(&self) -> Rc<RefCell<Vec<DebugMetadataHolder>>> {
|
||||
self.metadata.clone()
|
||||
}
|
||||
|
||||
// pub fn get_scopes(&self) -> Rc<RefCell<DebugScopeHolder>> {
|
||||
// self.scope.clone()
|
||||
// }
|
||||
|
||||
pub fn get_subprograms(&self) -> Rc<RefCell<Vec<DebugSubprogramHolder>>> {
|
||||
self.programs.clone()
|
||||
}
|
||||
|
||||
pub fn get_types(&self) -> Rc<RefCell<Vec<DebugTypeHolder>>> {
|
||||
self.types.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn get_locations(&self) -> Rc<RefCell<Vec<DebugLocationHolder>>> {
|
||||
self.locations.clone()
|
||||
}
|
||||
|
||||
pub fn get_subprogram_data_unchecked(&self, value: &DebugProgramValue) -> DebugSubprogramData {
|
||||
unsafe {
|
||||
self.programs
|
||||
.borrow()
|
||||
.get_unchecked(value.0 - 1)
|
||||
.data
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct DebugLocation {
|
||||
pub scope: DebugProgramValue,
|
||||
pub pos: DebugPosition,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct DebugPosition {
|
||||
pub line: u32,
|
||||
pub column: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum DebugMetadata {
|
||||
ParamVar(DebugParamVariable),
|
||||
LocalVar(DebugLocalVariable),
|
||||
VarAssignment,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DebugParamVariable {
|
||||
pub name: String,
|
||||
/// the index (starting from 1) of this variable in the subprogram
|
||||
/// parameters. arg_idx should not conflict with other parameters of the
|
||||
/// same subprogram.
|
||||
pub arg_idx: u32,
|
||||
pub ty: DebugTypeValue,
|
||||
/// If this variable will be referenced from its containing subprogram, and
|
||||
/// will survive some optimizations.
|
||||
pub always_preserve: bool,
|
||||
pub flags: DwarfFlags,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DebugLocalVariable {
|
||||
pub name: String,
|
||||
pub ty: DebugTypeValue,
|
||||
pub always_preserve: bool,
|
||||
pub flags: DwarfFlags,
|
||||
}
|
||||
|
||||
impl Default for DebugSubprogramOptionals {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
scope_line: 0,
|
||||
is_local: false,
|
||||
is_definition: true,
|
||||
is_optimized: false,
|
||||
flags: DwarfFlags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DwarfFlags;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum DebugTypeData {
|
||||
Basic(DebugBasicType),
|
||||
Subprogram(DebugSubprogramType),
|
||||
Pointer(DebugPointerType),
|
||||
Array(DebugArrayType),
|
||||
Struct(DebugStructType),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DebugBasicType {
|
||||
pub name: String,
|
||||
/// Size of the type.
|
||||
pub size_bits: u64,
|
||||
/// DWARF encoding code, e.g., dwarf::DW_ATE_float.
|
||||
pub encoding: DwarfEncoding,
|
||||
/// Optional DWARF attributes, e.g., DW_AT_endianity.
|
||||
pub flags: DwarfFlags,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DebugArrayType {
|
||||
pub size_bits: u64,
|
||||
pub align_bits: u32,
|
||||
pub element_type: DebugTypeValue,
|
||||
pub length: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DebugPointerType {
|
||||
pub name: String,
|
||||
pub pointee: DebugTypeValue,
|
||||
pub size_bits: u64,
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct DebugStructType {
|
||||
pub name: String,
|
||||
pub scope: DebugProgramValue,
|
||||
pub pos: Option<DebugPosition>,
|
||||
pub size_bits: u64,
|
||||
pub flags: DwarfFlags,
|
||||
pub fields: Vec<DebugFieldType>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DebugFieldType {
|
||||
pub name: String,
|
||||
pub scope: DebugProgramValue,
|
||||
pub pos: Option<DebugPosition>,
|
||||
pub size_bits: u64,
|
||||
pub offset: u64,
|
||||
pub flags: DwarfFlags,
|
||||
pub ty: DebugTypeValue,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DebugSubprogramType {
|
||||
pub parameters: Vec<DebugTypeValue>,
|
||||
pub flags: DwarfFlags,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum DwarfEncoding {
|
||||
Address = 1,
|
||||
Boolean = 2,
|
||||
Float = 4,
|
||||
Signed = 5,
|
||||
SignedChar = 6,
|
||||
Unsigned = 7,
|
||||
UnsignedChar = 8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DebugSubprogramData {
|
||||
/// Function name.
|
||||
pub name: String,
|
||||
pub outer_scope: DebugProgramValue,
|
||||
/// Used for line number.
|
||||
pub location: DebugLocation,
|
||||
/// Function type.
|
||||
pub ty: DebugTypeValue,
|
||||
pub opts: DebugSubprogramOptionals,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DebugSubprogramOptionals {
|
||||
/// Set to the beginning of the scope this starts
|
||||
pub scope_line: u32,
|
||||
pub is_local: bool,
|
||||
pub is_definition: bool,
|
||||
pub is_optimized: bool,
|
||||
/// These flags are used to emit dwarf attributes. e.g. is this function
|
||||
/// prototyped or not.
|
||||
pub flags: DwarfFlags,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InstructionDebugRecordData {
|
||||
pub scope: DebugProgramValue,
|
||||
pub variable: DebugMetadataValue,
|
||||
pub location: DebugLocation,
|
||||
pub kind: DebugRecordKind,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum DebugRecordKind {
|
||||
Declare(InstructionValue),
|
||||
Value(InstructionValue),
|
||||
}
|
||||
@ -1,619 +0,0 @@
|
||||
//! Debug implementations for relevant types
|
||||
|
||||
use std::{
|
||||
fmt::{Debug, Display, Write},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
CmpPredicate, Context, Instr, InstructionData, TerminatorKind,
|
||||
builder::*,
|
||||
debug_information::{
|
||||
DebugArrayType, DebugBasicType, DebugFieldType, DebugInformation, DebugLocalVariable,
|
||||
DebugLocation, DebugLocationValue, DebugMetadata, DebugMetadataValue, DebugParamVariable,
|
||||
DebugPointerType, DebugPosition, DebugProgramValue, DebugRecordKind, DebugScopeValue,
|
||||
DebugStructType, DebugSubprogramType, DebugTypeData, DebugTypeHolder, DebugTypeValue,
|
||||
},
|
||||
pad_adapter::PadAdapter,
|
||||
};
|
||||
|
||||
impl Display for Context {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Display::fmt(&self.builder, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Builder {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "Producer: {}", self.producer)?;
|
||||
for module in self.modules.borrow().iter() {
|
||||
if module.data.is_main {
|
||||
write!(f, "main ")?;
|
||||
}
|
||||
writeln!(f, "{} ({:?}) {{", module.data.name, module.value)?;
|
||||
for function in &module.functions {
|
||||
let mut state = Default::default();
|
||||
let mut inner = PadAdapter::wrap(f, &mut state);
|
||||
function.builder_fmt(&mut inner, self, &module.debug_information)?;
|
||||
}
|
||||
writeln!(f, "}}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FunctionHolder {
|
||||
fn builder_fmt(
|
||||
&self,
|
||||
f: &mut impl std::fmt::Write,
|
||||
builder: &Builder,
|
||||
debug: &Option<DebugInformation>,
|
||||
) -> std::fmt::Result {
|
||||
if self.data.flags.is_imported {
|
||||
write!(f, "imported ")?;
|
||||
}
|
||||
if self.data.flags.is_extern {
|
||||
write!(f, "extern ")?;
|
||||
}
|
||||
if self.data.flags.is_pub {
|
||||
write!(f, "pub ")?;
|
||||
}
|
||||
if self.data.flags.is_main {
|
||||
write!(f, "main ")?;
|
||||
}
|
||||
let params = self
|
||||
.data
|
||||
.params
|
||||
.iter()
|
||||
.map(|p| format!("{:?}", p))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
write!(
|
||||
f,
|
||||
"fn {}({}) -> {:?} ",
|
||||
self.data.name, params, self.data.ret
|
||||
)?;
|
||||
|
||||
writeln!(f, "{{")?;
|
||||
let mut state = Default::default();
|
||||
let mut inner = PadAdapter::wrap(f, &mut state);
|
||||
writeln!(inner, "(Value = {:?}) ", self.value)?;
|
||||
if let Some(debug) = self.data.debug {
|
||||
writeln!(inner, "(Debug = {:?})", debug)?;
|
||||
}
|
||||
|
||||
for block in &self.blocks {
|
||||
let mut state = Default::default();
|
||||
let mut inner = PadAdapter::wrap(&mut inner, &mut state);
|
||||
block.builder_fmt(&mut inner, builder, debug)?;
|
||||
}
|
||||
|
||||
writeln!(f, "}}")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockHolder {
|
||||
fn builder_fmt(
|
||||
&self,
|
||||
f: &mut impl std::fmt::Write,
|
||||
builder: &Builder,
|
||||
debug: &Option<DebugInformation>,
|
||||
) -> std::fmt::Result {
|
||||
if self.data.deleted {
|
||||
write!(f, "deleted ")?;
|
||||
}
|
||||
writeln!(f, "{} ({:?}):", self.data.name, self.value)?;
|
||||
|
||||
let mut state = Default::default();
|
||||
let mut inner = PadAdapter::wrap(f, &mut state);
|
||||
|
||||
for instr in &self.instructions {
|
||||
instr.builder_fmt(&mut inner, builder, debug)?;
|
||||
}
|
||||
|
||||
if let Some(terminator) = &self.data.terminator {
|
||||
terminator.builder_fmt(&mut inner, builder, debug)?;
|
||||
}
|
||||
if let Some(location) = self.data.terminator_location {
|
||||
writeln!(
|
||||
inner,
|
||||
" ^ (At {}) ",
|
||||
debug.as_ref().unwrap().get_location(location)
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionHolder {
|
||||
fn builder_fmt(
|
||||
&self,
|
||||
f: &mut impl std::fmt::Write,
|
||||
_builder: &Builder,
|
||||
debug: &Option<DebugInformation>,
|
||||
) -> std::fmt::Result {
|
||||
if let Some(record) = &self.record {
|
||||
let kind = match record.kind {
|
||||
DebugRecordKind::Declare(instruction_value) => {
|
||||
format!("= {:?} (Assign)", instruction_value)
|
||||
}
|
||||
DebugRecordKind::Value(instruction_value) => {
|
||||
format!("= {:?} (Value)", instruction_value)
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(debug) = debug {
|
||||
writeln!(f, " (Debug {} {})", record.variable.hr(debug), kind)?;
|
||||
}
|
||||
}
|
||||
writeln!(
|
||||
f,
|
||||
"{:?} ({}) = {:?} ",
|
||||
self.value, self.name, self.data.kind
|
||||
)?;
|
||||
if let Some(debug) = debug {
|
||||
if let Some(location) = self.data.location {
|
||||
writeln!(f, " ^ (At {}) ", debug.get_location(location))?;
|
||||
}
|
||||
if let Some(meta) = self.data.meta {
|
||||
writeln!(f, " ^ (Meta {}) ", meta.hr(debug))?;
|
||||
}
|
||||
}
|
||||
writeln!(f)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl TerminatorKind {
|
||||
fn builder_fmt(
|
||||
&self,
|
||||
f: &mut impl std::fmt::Write,
|
||||
_builder: &Builder,
|
||||
_debug: &Option<DebugInformation>,
|
||||
) -> std::fmt::Result {
|
||||
match self {
|
||||
TerminatorKind::Ret(instr) => writeln!(f, "ret {:?}", instr),
|
||||
TerminatorKind::RetVoid => writeln!(f, "ret void"),
|
||||
TerminatorKind::Br(block) => writeln!(f, "br {:?}", block),
|
||||
TerminatorKind::CondBr(instr, lhs, rhs) => {
|
||||
writeln!(f, "condbr {:?}, {:?} or {:?}", instr, lhs, rhs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DebugMetadataValue {
|
||||
fn hr(&self, debug: &DebugInformation) -> String {
|
||||
let kind = match debug.get_metadata(*self) {
|
||||
DebugMetadata::ParamVar(DebugParamVariable {
|
||||
name, arg_idx, ty, ..
|
||||
}) => format!("param {} (idx {}) (type {:?}) ", name, arg_idx, ty),
|
||||
DebugMetadata::LocalVar(DebugLocalVariable { name, ty, .. }) => {
|
||||
format!("var {} (type {:?}) ", name, ty)
|
||||
}
|
||||
DebugMetadata::VarAssignment => todo!(),
|
||||
};
|
||||
format!("{} at {}", kind, debug.get_metadata_location(*self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DebugLocation {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?} on scope {:?}", self.pos, self.scope)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DebugPosition {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "line {}, col {}", self.line, self.column)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Builder {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_list().entries(self.get_modules().borrow().iter());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PrintableModule<'ctx> {
|
||||
pub phantom: PhantomData<&'ctx ()>,
|
||||
pub module: ModuleHolder,
|
||||
}
|
||||
|
||||
impl<'ctx> Debug for PrintableModule<'ctx> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Debug::fmt(&self.module, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ModuleHolder {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple(&format!("{}({:#?}) ", self.data.name, self.value))
|
||||
.field(&self.functions)
|
||||
// .field(&self.debug_information)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for FunctionHolder {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple(&format!(
|
||||
"{}({:?}) -> {:?} ",
|
||||
self.data.name, self.data.params, self.data.ret
|
||||
))
|
||||
.field(&self.blocks)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for BlockHolder {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let deleted = if self.data.deleted { " (deleted)" } else { "" };
|
||||
f.debug_tuple(&format!(
|
||||
"{}[{:?}]{} ",
|
||||
&self.data.name, &self.value, deleted
|
||||
))
|
||||
.field(&self.instructions)
|
||||
.field(&self.data.terminator)
|
||||
.field(&self.data.terminator_location)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for InstructionHolder {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.value.fmt(f)?;
|
||||
write!(f, " ({})", self.name)?;
|
||||
f.write_str(" = ")?;
|
||||
self.data.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for InstructionData {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.kind.fmt(f)?;
|
||||
if let Some(location) = self.location {
|
||||
write!(f, " ({:?})", location)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ModuleValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "M[{:0>2}]", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for FunctionValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "F[{:0>2}-{:0>2}]", &self.0.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for BlockValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "B[{:0>2}-{:0>2}-{:0>2}]", &self.0.0.0, &self.0.1, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for InstructionValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"%{}.{}.{}.{}",
|
||||
self.0.0.0.0, self.0.0.1, self.0.1, self.1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// impl Debug for InstructionValue {
|
||||
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// write!(
|
||||
// f,
|
||||
// "I[{:0>2}-{:0>2}-{:0>2}-{:0>2}]",
|
||||
// &self.0.0.0.0, &self.0.0.1, &self.0.1, self.1
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Debug for TypeValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Ty[{:0>2}-{:0>2}]", &self.0.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Instr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Instr::Param(nth) => fmt_call(f, &"Param", &nth),
|
||||
Instr::Constant(c) => c.fmt(f),
|
||||
Instr::Add(lhs, rhs) => fmt_binop(f, lhs, &"+", rhs),
|
||||
Instr::FAdd(lhs, rhs) => fmt_binop(f, lhs, &"+", rhs),
|
||||
Instr::Sub(lhs, rhs) => fmt_binop(f, lhs, &"-", rhs),
|
||||
Instr::FSub(lhs, rhs) => fmt_binop(f, lhs, &"-", rhs),
|
||||
Instr::Mul(lhs, rhs) => fmt_binop(f, lhs, &"*", rhs),
|
||||
Instr::FMul(lhs, rhs) => fmt_binop(f, lhs, &"*", rhs),
|
||||
Instr::UDiv(lhs, rhs) => fmt_binop(f, lhs, &"/", rhs),
|
||||
Instr::SDiv(lhs, rhs) => fmt_binop(f, lhs, &"/", rhs),
|
||||
Instr::FDiv(lhs, rhs) => fmt_binop(f, lhs, &"/", rhs),
|
||||
Instr::URem(lhs, rhs) => fmt_binop(f, lhs, &"%", rhs),
|
||||
Instr::SRem(lhs, rhs) => fmt_binop(f, lhs, &"%", rhs),
|
||||
Instr::FRem(lhs, rhs) => fmt_binop(f, lhs, &"%", rhs),
|
||||
Instr::And(lhs, rhs) => fmt_binop(f, lhs, &"&&", rhs),
|
||||
Instr::Phi(val) => fmt_call(f, &"Phi", &val),
|
||||
Instr::ICmp(cmp, lhs, rhs) => fmt_binop(f, lhs, cmp, rhs),
|
||||
Instr::FCmp(cmp, lhs, rhs) => fmt_binop(f, lhs, cmp, rhs),
|
||||
Instr::FunctionCall(fun, params) => fmt_call(f, fun, params),
|
||||
Instr::Alloca(ty) => write!(f, "alloca<{:?}>", ty),
|
||||
Instr::Load(val, ty) => write!(f, "load<{:?}>({:?})", ty, val),
|
||||
Instr::Store(ptr, val) => write!(f, "store({:?} = {:?})", ptr, val),
|
||||
Instr::ArrayAlloca(ty, instruction_value) => {
|
||||
write!(f, "array_alloca<{:?}>({:?})", ty, instruction_value)
|
||||
}
|
||||
Instr::GetElemPtr(instruction_value, items) => fmt_index(
|
||||
f,
|
||||
instruction_value,
|
||||
&items
|
||||
.iter()
|
||||
.map(|expr| format!("{:?}", expr))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
),
|
||||
Instr::GetStructElemPtr(instruction_value, index) => {
|
||||
write!(f, "GEP(")?;
|
||||
fmt_index(f, instruction_value, &index.to_string())?;
|
||||
write!(f, ")")
|
||||
}
|
||||
Instr::ExtractValue(instruction_value, index) => {
|
||||
fmt_index(f, instruction_value, &index.to_string())
|
||||
}
|
||||
Instr::Trunc(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::ZExt(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::SExt(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::FPTrunc(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::FPExt(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::FPToUI(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::FPToSI(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::UIToFP(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::SIToFP(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::PtrToInt(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::IntToPtr(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::BitCast(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_binop(
|
||||
f: &mut std::fmt::Formatter<'_>,
|
||||
lhs: &impl std::fmt::Debug,
|
||||
op: &impl std::fmt::Debug,
|
||||
rhs: &impl std::fmt::Debug,
|
||||
) -> std::fmt::Result {
|
||||
lhs.fmt(f)?;
|
||||
f.write_char(' ')?;
|
||||
op.fmt(f)?;
|
||||
f.write_char(' ')?;
|
||||
rhs.fmt(f)
|
||||
}
|
||||
|
||||
fn fmt_call(
|
||||
f: &mut std::fmt::Formatter<'_>,
|
||||
fun: &impl std::fmt::Debug,
|
||||
params: &impl std::fmt::Debug,
|
||||
) -> std::fmt::Result {
|
||||
fun.fmt(f)?;
|
||||
f.write_char('(')?;
|
||||
params.fmt(f)?;
|
||||
f.write_char(')')
|
||||
}
|
||||
|
||||
fn fmt_index(
|
||||
f: &mut std::fmt::Formatter<'_>,
|
||||
fun: &impl std::fmt::Debug,
|
||||
params: &impl std::fmt::Debug,
|
||||
) -> std::fmt::Result {
|
||||
fun.fmt(f)?;
|
||||
f.write_char('[')?;
|
||||
params.fmt(f)?;
|
||||
f.write_char(']')
|
||||
}
|
||||
|
||||
impl Debug for CmpPredicate {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::LT => write!(f, "<"),
|
||||
Self::GT => write!(f, ">"),
|
||||
Self::LE => write!(f, "<="),
|
||||
Self::GE => write!(f, ">="),
|
||||
Self::EQ => write!(f, "=="),
|
||||
Self::NE => write!(f, "!="),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for TerminatorKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Ret(val) => {
|
||||
write!(f, "Ret ")?;
|
||||
val.fmt(f)
|
||||
}
|
||||
Self::RetVoid => write!(f, "Void Ret"),
|
||||
Self::Br(val) => {
|
||||
write!(f, "Br ")?;
|
||||
val.fmt(f)
|
||||
}
|
||||
Self::CondBr(cond, b1, b2) => {
|
||||
write!(f, "CondBr ")?;
|
||||
cond.fmt(f)?;
|
||||
write!(f, " ? ")?;
|
||||
b1.fmt(f)?;
|
||||
write!(f, " : ")?;
|
||||
b2.fmt(f)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugTypeHolder {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple(&format!("DebugTypeHolder {:?}", self.value))
|
||||
.field(&self.data)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugTypeData {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
DebugTypeData::Basic(ty) => Debug::fmt(ty, f),
|
||||
DebugTypeData::Subprogram(ty) => Debug::fmt(ty, f),
|
||||
DebugTypeData::Pointer(ty) => Debug::fmt(ty, f),
|
||||
DebugTypeData::Array(ty) => Debug::fmt(ty, f),
|
||||
DebugTypeData::Struct(ty) => Debug::fmt(ty, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugBasicType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("BasicType")
|
||||
.field(&self.name)
|
||||
.field(&self.size_bits)
|
||||
.field(&self.encoding)
|
||||
.field(&self.flags)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugStructType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Struct")
|
||||
.field("name", &self.name)
|
||||
.field("scope", &self.scope)
|
||||
.field("pos", &self.pos)
|
||||
.field("size_bit", &self.size_bits)
|
||||
.field("flags", &self.flags)
|
||||
.field("elements", &self.fields)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugFieldType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct(&format!("Field({})", self.name))
|
||||
.field("scope", &self.scope)
|
||||
.field("pos", &self.pos)
|
||||
.field("size_bits", &self.size_bits)
|
||||
.field("offset", &self.offset)
|
||||
.field("flags", &self.flags)
|
||||
.field("ty", &self.ty)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugSubprogramType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("Subprogram")
|
||||
.field(&self.parameters)
|
||||
.field(&self.flags)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugPointerType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple(&format!("Pointer<{:?}>({})", self.pointee, self.name))
|
||||
.field(&self.size_bits)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugArrayType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct(&format!("Array<{:?}>[{}]", self.element_type, self.length))
|
||||
.field("size_bits", &self.size_bits)
|
||||
.field("align_bits", &self.align_bits)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugMetadataValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Meta[{}]", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugScopeValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Scope[{}]",
|
||||
self.0
|
||||
.iter()
|
||||
.map(|v| v.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugTypeValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Type[{}]", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugProgramValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Subprogram[{}]", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugLocationValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Value[{:?}][{}]", self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugLocation {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?} on scope {:?}", self.pos, self.scope)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugPosition {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "ln {}, col {}", self.line, self.column)
|
||||
}
|
||||
}
|
||||
@ -1,648 +0,0 @@
|
||||
//! Reid LLVM Lib is an ergonomic Rust'y API which is used to produce a
|
||||
//! Low-Level IR (LLIR) using [`Context`] and [`Builder`]. This Builder can then
|
||||
//! be used at the end to compile said LLIR into LLVM IR.
|
||||
|
||||
use std::{fmt::Debug, marker::PhantomData};
|
||||
|
||||
use builder::{BlockValue, Builder, FunctionValue, InstructionValue, ModuleValue, TypeValue};
|
||||
use debug_information::{DebugFileData, DebugInformation, DebugLocationValue, DebugMetadataValue, DebugProgramValue};
|
||||
use fmt::PrintableModule;
|
||||
|
||||
pub mod builder;
|
||||
pub mod compile;
|
||||
pub mod debug_information;
|
||||
mod fmt;
|
||||
mod pad_adapter;
|
||||
mod util;
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, PartialEq, PartialOrd)]
|
||||
pub enum ErrorKind {
|
||||
#[error("NULL error, should never occur!")]
|
||||
Null,
|
||||
}
|
||||
|
||||
pub type CompileResult<T> = Result<T, ErrorKind>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Context {
|
||||
builder: Builder,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new<T: Into<String>>(producer: T) -> Context {
|
||||
Context {
|
||||
builder: Builder::new(producer.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn module<'ctx>(&'ctx self, name: &str, main: bool) -> Module<'ctx> {
|
||||
let value = self.builder.add_module(ModuleData {
|
||||
name: name.to_owned(),
|
||||
is_main: main,
|
||||
});
|
||||
Module {
|
||||
phantom: PhantomData,
|
||||
builder: self.builder.clone(),
|
||||
value,
|
||||
debug_info: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub struct ModuleData {
|
||||
name: String,
|
||||
is_main: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Module<'ctx> {
|
||||
phantom: PhantomData<&'ctx ()>,
|
||||
builder: Builder,
|
||||
value: ModuleValue,
|
||||
debug_info: Option<DebugInformation>,
|
||||
}
|
||||
|
||||
impl<'ctx> Module<'ctx> {
|
||||
pub fn function(&self, name: &str, ret: Type, params: Vec<Type>, flags: FunctionFlags) -> Function<'ctx> {
|
||||
unsafe {
|
||||
Function {
|
||||
phantom: PhantomData,
|
||||
builder: self.builder.clone(),
|
||||
value: self.builder.add_function(
|
||||
&self.value,
|
||||
FunctionData {
|
||||
name: name.to_owned(),
|
||||
ret,
|
||||
params,
|
||||
flags,
|
||||
debug: None,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn custom_type(&self, ty: CustomTypeKind) -> TypeValue {
|
||||
unsafe {
|
||||
let (name, kind) = match &ty {
|
||||
CustomTypeKind::NamedStruct(NamedStruct(name, _)) => (name.clone(), ty),
|
||||
};
|
||||
self.builder.add_type(&self.value, TypeData { name, kind })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(&self) -> ModuleValue {
|
||||
self.value
|
||||
}
|
||||
|
||||
pub fn as_printable(&self) -> PrintableModule<'ctx> {
|
||||
PrintableModule {
|
||||
phantom: PhantomData,
|
||||
module: self.builder.find_module(self.value),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_debug_info(&mut self, file: DebugFileData) -> (DebugInformation, DebugProgramValue) {
|
||||
let (debug_info, program_value) = DebugInformation::from_file(file);
|
||||
self.debug_info = Some(debug_info.clone());
|
||||
(debug_info, program_value)
|
||||
}
|
||||
|
||||
pub fn get_debug_info(&self) -> &Option<DebugInformation> {
|
||||
&self.debug_info
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> Drop for Module<'ctx> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(debug_info) = self.debug_info.take() {
|
||||
self.builder.set_debug_information(&self.value, debug_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub struct FunctionData {
|
||||
name: String,
|
||||
ret: Type,
|
||||
params: Vec<Type>,
|
||||
flags: FunctionFlags,
|
||||
debug: Option<DebugProgramValue>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash)]
|
||||
pub struct FunctionFlags {
|
||||
/// True in the destination module of the import, false in the source module.
|
||||
pub is_extern: bool,
|
||||
/// Whether this function is the main function of the module, that should be
|
||||
/// executed (and linked externally also).
|
||||
pub is_main: bool,
|
||||
/// Whether this function should be available externally always.
|
||||
pub is_pub: bool,
|
||||
/// If this function is an imported function (either in the source or
|
||||
/// destination module)
|
||||
pub is_imported: bool,
|
||||
/// Whether this function should add "alwaysinline"-attribute.
|
||||
pub inline: bool,
|
||||
}
|
||||
|
||||
impl Default for FunctionFlags {
|
||||
fn default() -> FunctionFlags {
|
||||
FunctionFlags {
|
||||
is_extern: false,
|
||||
is_main: false,
|
||||
is_pub: false,
|
||||
is_imported: false,
|
||||
inline: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Function<'ctx> {
|
||||
phantom: PhantomData<&'ctx ()>,
|
||||
builder: Builder,
|
||||
value: FunctionValue,
|
||||
}
|
||||
|
||||
impl<'ctx> Function<'ctx> {
|
||||
pub fn block(&self, name: &str) -> Block<'ctx> {
|
||||
unsafe {
|
||||
Block {
|
||||
phantom: PhantomData,
|
||||
builder: self.builder.clone(),
|
||||
value: self.builder.add_block(
|
||||
&self.value,
|
||||
BlockData {
|
||||
name: name.to_owned(),
|
||||
terminator: None,
|
||||
terminator_location: None,
|
||||
deleted: false,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_debug(&self, subprogram: DebugProgramValue) {
|
||||
unsafe {
|
||||
self.builder.set_debug_subprogram(&self.value, subprogram);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(&self) -> FunctionValue {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub struct BlockData {
|
||||
name: String,
|
||||
terminator: Option<TerminatorKind>,
|
||||
terminator_location: Option<DebugLocationValue>,
|
||||
deleted: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Block<'builder> {
|
||||
phantom: PhantomData<&'builder ()>,
|
||||
builder: Builder,
|
||||
value: BlockValue,
|
||||
}
|
||||
|
||||
impl Instr {
|
||||
pub fn default_name(&self) -> &str {
|
||||
match self {
|
||||
Instr::Param(_) => "param",
|
||||
Instr::Constant(_) => "const1",
|
||||
Instr::Add(..) => "add",
|
||||
Instr::FAdd(..) => "fadd",
|
||||
Instr::Sub(..) => "sub",
|
||||
Instr::FSub(..) => "fsub",
|
||||
Instr::Mul(..) => "mul",
|
||||
Instr::FMul(..) => "fmul",
|
||||
Instr::UDiv(..) => "udiv",
|
||||
Instr::SDiv(..) => "sdiv",
|
||||
Instr::FDiv(..) => "fdiv",
|
||||
Instr::URem(..) => "urem",
|
||||
Instr::SRem(..) => "srem",
|
||||
Instr::FRem(..) => "frem",
|
||||
Instr::And(..) => "and",
|
||||
Instr::Phi(_) => "phi",
|
||||
Instr::Alloca(_) => "alloca",
|
||||
Instr::Load(_, _) => "load",
|
||||
Instr::Store(..) => "store",
|
||||
Instr::ArrayAlloca(_, _) => "arrayalloca",
|
||||
Instr::GetElemPtr(..) => "getelemptr",
|
||||
Instr::GetStructElemPtr(..) => "getstructelemptr",
|
||||
Instr::ExtractValue(..) => "extractvalue",
|
||||
Instr::ICmp(..) => "icmp",
|
||||
Instr::FunctionCall(..) => "call",
|
||||
Instr::FCmp(_, _, _) => "fcmp",
|
||||
Instr::Trunc(_, _) => "trunc",
|
||||
Instr::ZExt(_, _) => "zext",
|
||||
Instr::SExt(_, _) => "sext",
|
||||
Instr::FPTrunc(_, _) => "fptrunc",
|
||||
Instr::FPExt(_, _) => "pfext",
|
||||
Instr::FPToUI(_, _) => "fptoui",
|
||||
Instr::FPToSI(_, _) => "fptosi",
|
||||
Instr::UIToFP(_, _) => "uitofp",
|
||||
Instr::SIToFP(_, _) => "sitofp",
|
||||
Instr::PtrToInt(_, _) => "ptrtoint",
|
||||
Instr::IntToPtr(_, _) => "inttoptr",
|
||||
Instr::BitCast(_, _) => "bitcast",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'builder> Block<'builder> {
|
||||
pub fn build_named<T: Into<String>>(&mut self, name: T, instruction: Instr) -> CompileResult<InstructionValue> {
|
||||
unsafe {
|
||||
self.builder.add_instruction(
|
||||
&self.value,
|
||||
InstructionData {
|
||||
kind: instruction,
|
||||
location: None,
|
||||
meta: None,
|
||||
},
|
||||
name.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(&mut self, instruction: Instr) -> CompileResult<InstructionValue> {
|
||||
unsafe {
|
||||
let name = instruction.default_name().to_owned();
|
||||
self.builder.add_instruction(
|
||||
&self.value,
|
||||
InstructionData {
|
||||
kind: instruction,
|
||||
location: None,
|
||||
meta: None,
|
||||
},
|
||||
name,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_instr_location(&self, instruction: InstructionValue, location: DebugLocationValue) {
|
||||
unsafe {
|
||||
self.builder.add_instruction_location(&instruction, location);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_instr_metadata(&self, instruction: InstructionValue, location: DebugMetadataValue) {
|
||||
unsafe {
|
||||
self.builder.add_instruction_metadata(&instruction, location);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn terminate(&mut self, instruction: TerminatorKind) -> CompileResult<()> {
|
||||
unsafe { self.builder.terminate(&self.value, instruction) }
|
||||
}
|
||||
|
||||
pub fn set_terminator_location(&mut self, location: DebugLocationValue) -> CompileResult<()> {
|
||||
unsafe { self.builder.set_terminator_location(&self.value, location) }
|
||||
}
|
||||
|
||||
/// Delete block if it is unused. Return true if deleted, false if not.
|
||||
pub fn delete_if_unused(&mut self) -> CompileResult<bool> {
|
||||
unsafe {
|
||||
if !self.builder.is_block_used(self.value()) {
|
||||
self.builder.delete_block(&self.value)?;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(&self) -> BlockValue {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InstructionData {
|
||||
kind: Instr,
|
||||
location: Option<DebugLocationValue>,
|
||||
meta: Option<DebugMetadataValue>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Hash)]
|
||||
pub enum CmpPredicate {
|
||||
LT,
|
||||
LE,
|
||||
GT,
|
||||
GE,
|
||||
EQ,
|
||||
NE,
|
||||
}
|
||||
|
||||
/// https://llvm.org/docs/LangRef.html#instruction-reference
|
||||
#[derive(Clone)]
|
||||
pub enum Instr {
|
||||
Param(usize),
|
||||
Constant(ConstValue),
|
||||
|
||||
/// Add two integers
|
||||
Add(InstructionValue, InstructionValue),
|
||||
/// Add two floats
|
||||
FAdd(InstructionValue, InstructionValue),
|
||||
/// Subtract two integers
|
||||
Sub(InstructionValue, InstructionValue),
|
||||
/// Subtract two floats
|
||||
FSub(InstructionValue, InstructionValue),
|
||||
/// Multiply two integers
|
||||
Mul(InstructionValue, InstructionValue),
|
||||
/// Multiply two floats
|
||||
FMul(InstructionValue, InstructionValue),
|
||||
/// Divide two unsigned integers
|
||||
UDiv(InstructionValue, InstructionValue),
|
||||
/// Divide two signed integers
|
||||
SDiv(InstructionValue, InstructionValue),
|
||||
/// Divide two floats
|
||||
FDiv(InstructionValue, InstructionValue),
|
||||
/// Get the remainder from two unsigned integers
|
||||
URem(InstructionValue, InstructionValue),
|
||||
/// Get the remainder from two signed integers
|
||||
SRem(InstructionValue, InstructionValue),
|
||||
/// Get the remainder from two floats
|
||||
FRem(InstructionValue, InstructionValue),
|
||||
And(InstructionValue, InstructionValue),
|
||||
Phi(Vec<InstructionValue>),
|
||||
|
||||
Alloca(Type),
|
||||
Load(InstructionValue, Type),
|
||||
Store(InstructionValue, InstructionValue),
|
||||
ArrayAlloca(Type, InstructionValue),
|
||||
GetElemPtr(InstructionValue, Vec<InstructionValue>),
|
||||
GetStructElemPtr(InstructionValue, u32),
|
||||
ExtractValue(InstructionValue, u32),
|
||||
|
||||
/// Integer Comparison
|
||||
ICmp(CmpPredicate, InstructionValue, InstructionValue),
|
||||
/// FLoat Comparison
|
||||
FCmp(CmpPredicate, InstructionValue, InstructionValue),
|
||||
|
||||
/// The `trunc` instruction truncates the high order bits in value and
|
||||
/// converts the remaining bits to ty2. Since the source size must be larger
|
||||
/// than the destination size, `trunc` cannot be a no-op cast. It will
|
||||
/// always truncate bits.
|
||||
Trunc(InstructionValue, Type),
|
||||
/// The `zext` fills the high order bits of the value with zero bits until
|
||||
/// it reaches the size of the destination type, ty2.
|
||||
ZExt(InstructionValue, Type),
|
||||
/// The `sext` instruction performs a sign extension by copying the sign bit
|
||||
/// (highest order bit) of the value until it reaches the bit size of the
|
||||
/// type ty2.
|
||||
SExt(InstructionValue, Type),
|
||||
/// The `fptrunc` instruction casts a value from a larger floating-point
|
||||
/// type to a smaller floating-point type.
|
||||
FPTrunc(InstructionValue, Type),
|
||||
/// The `fpext` instruction extends the value from a smaller floating-point
|
||||
/// type to a larger floating-point type.
|
||||
FPExt(InstructionValue, Type),
|
||||
/// The `fptoui` instruction takes a value to cast, which must be a scalar
|
||||
/// or vector floating-point value, and a type to cast it to ty2, which must
|
||||
/// be an integer type.
|
||||
FPToUI(InstructionValue, Type),
|
||||
/// The `fptosi` instruction takes a value to cast, which must be a scalar
|
||||
/// or vector floating-point value, and a type to cast it to ty2, which must
|
||||
/// be an integer type.
|
||||
FPToSI(InstructionValue, Type),
|
||||
/// The `uitofp` instruction takes a value to cast, which must be a scalar
|
||||
/// or vector integer value, and a type to cast it to ty2, which must be an
|
||||
/// floating-point type.
|
||||
UIToFP(InstructionValue, Type),
|
||||
/// The `sitofp` instruction takes a value to cast, which must be a scalar
|
||||
/// or vector integer value, and a type to cast it to ty2, which must be an
|
||||
/// floating-point type
|
||||
SIToFP(InstructionValue, Type),
|
||||
/// The `ptrtoint` instruction converts value to integer type ty2 by
|
||||
/// interpreting the all pointer representation bits as an integer
|
||||
/// (equivalent to a bitcast) and either truncating or zero extending that
|
||||
/// value to the size of the integer type.
|
||||
PtrToInt(InstructionValue, Type),
|
||||
/// The `inttoptr` instruction converts value to type ty2 by applying either
|
||||
/// a zero extension or a truncation depending on the size of the integer
|
||||
/// value.
|
||||
IntToPtr(InstructionValue, Type),
|
||||
/// The `bitcast` instruction converts value to type ty2. It is always a
|
||||
/// no-op cast because no bits change with this conversion.
|
||||
BitCast(InstructionValue, Type),
|
||||
|
||||
FunctionCall(FunctionValue, Vec<InstructionValue>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||
pub enum Type {
|
||||
I8,
|
||||
I16,
|
||||
I32,
|
||||
I64,
|
||||
I128,
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
U64,
|
||||
U128,
|
||||
F16,
|
||||
F32B,
|
||||
F32,
|
||||
F64,
|
||||
F80,
|
||||
F128,
|
||||
F128PPC,
|
||||
Bool,
|
||||
Void,
|
||||
CustomType(TypeValue),
|
||||
Array(Box<Type>, u64),
|
||||
Ptr(Box<Type>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ConstValue {
|
||||
I8(i8),
|
||||
I16(i16),
|
||||
I32(i32),
|
||||
I64(i64),
|
||||
I128(i128),
|
||||
U8(u8),
|
||||
U16(u16),
|
||||
U32(u32),
|
||||
U64(u64),
|
||||
U128(u128),
|
||||
Bool(bool),
|
||||
Str(String),
|
||||
F16(f32),
|
||||
F32B(f32),
|
||||
F32(f32),
|
||||
F64(f64),
|
||||
F80(f64),
|
||||
F128(f64),
|
||||
F128PPC(f64),
|
||||
}
|
||||
|
||||
#[derive(Clone, Hash)]
|
||||
pub enum TerminatorKind {
|
||||
Ret(InstructionValue),
|
||||
RetVoid,
|
||||
Br(BlockValue),
|
||||
CondBr(InstructionValue, BlockValue, BlockValue),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||
pub struct TypeData {
|
||||
name: String,
|
||||
kind: CustomTypeKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||
pub enum CustomTypeKind {
|
||||
NamedStruct(NamedStruct),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||
pub struct NamedStruct(pub String, pub Vec<Type>);
|
||||
|
||||
impl ConstValue {
|
||||
pub fn get_type(&self) -> Type {
|
||||
use Type::*;
|
||||
match self {
|
||||
ConstValue::I8(_) => I8,
|
||||
ConstValue::I16(_) => I16,
|
||||
ConstValue::I32(_) => I32,
|
||||
ConstValue::I64(_) => I64,
|
||||
ConstValue::I128(_) => I128,
|
||||
ConstValue::U8(_) => U8,
|
||||
ConstValue::U16(_) => U16,
|
||||
ConstValue::U32(_) => U32,
|
||||
ConstValue::U64(_) => U64,
|
||||
ConstValue::U128(_) => U128,
|
||||
ConstValue::Str(_) => Type::Ptr(Box::new(U8)),
|
||||
ConstValue::Bool(_) => Bool,
|
||||
ConstValue::F16(_) => F16,
|
||||
ConstValue::F32B(_) => F32B,
|
||||
ConstValue::F32(_) => F32,
|
||||
ConstValue::F64(_) => F64,
|
||||
ConstValue::F80(_) => F80,
|
||||
ConstValue::F128(_) => F128,
|
||||
ConstValue::F128PPC(_) => F128PPC,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
enum TypeCategory {
|
||||
SignedInteger,
|
||||
UnsignedInteger,
|
||||
Void,
|
||||
Real,
|
||||
Ptr,
|
||||
CustomType,
|
||||
Array,
|
||||
}
|
||||
|
||||
impl TypeCategory {
|
||||
pub fn comparable(&self) -> bool {
|
||||
match self {
|
||||
TypeCategory::SignedInteger => true,
|
||||
TypeCategory::UnsignedInteger => true,
|
||||
TypeCategory::Real => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn signed(&self) -> bool {
|
||||
match self {
|
||||
TypeCategory::SignedInteger => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn integer(&self) -> bool {
|
||||
match self {
|
||||
TypeCategory::SignedInteger => true,
|
||||
TypeCategory::UnsignedInteger => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn category(&self) -> TypeCategory {
|
||||
match self {
|
||||
Type::I8 | Type::I16 | Type::I32 | Type::I64 | Type::I128 => TypeCategory::SignedInteger,
|
||||
Type::U8 | Type::U16 | Type::U32 | Type::U64 | Type::U128 => TypeCategory::UnsignedInteger,
|
||||
Type::F16 | Type::F32B | Type::F32 | Type::F64 | Type::F80 | Type::F128 | Type::F128PPC => {
|
||||
TypeCategory::Real
|
||||
}
|
||||
Type::Bool => TypeCategory::UnsignedInteger,
|
||||
Type::Void => TypeCategory::Void,
|
||||
Type::CustomType(_) => TypeCategory::CustomType,
|
||||
Type::Array(_, _) => TypeCategory::Array,
|
||||
Type::Ptr(_) => TypeCategory::Ptr,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cast_instruction(&self, value: InstructionValue, other: &Type) -> Option<Instr> {
|
||||
use Type::*;
|
||||
match (self, other) {
|
||||
(I8, I16 | I32 | I64 | I128) => Some(Instr::SExt(value, other.clone())),
|
||||
(I16, I32 | I64 | I128) => Some(Instr::SExt(value, other.clone())),
|
||||
(I32, I64 | I128) => Some(Instr::SExt(value, other.clone())),
|
||||
(I64, I128) => Some(Instr::SExt(value, other.clone())),
|
||||
(I128 | U128, I64 | U64 | I32 | U32 | I16 | U16 | I8 | U8) => Some(Instr::Trunc(value, other.clone())),
|
||||
(I64 | U64, I32 | U32 | I16 | U16 | I8 | U8) => Some(Instr::Trunc(value, other.clone())),
|
||||
(I32 | U32, I16 | U16 | I8 | U8) => Some(Instr::Trunc(value, other.clone())),
|
||||
(I16 | U16, I8 | U8) => Some(Instr::Trunc(value, other.clone())),
|
||||
(U8 | I8, U8 | I8 | U16 | I16 | U32 | I32 | U64 | I64 | U128 | I128) => {
|
||||
Some(Instr::ZExt(value, other.clone()))
|
||||
}
|
||||
(U16 | I16, U16 | I16 | U32 | I32 | U64 | I64 | U128 | I128) => Some(Instr::ZExt(value, other.clone())),
|
||||
(U32 | I32, U32 | I32 | U64 | I64 | U128 | I128) => Some(Instr::ZExt(value, other.clone())),
|
||||
(U64 | I64, U64 | I64 | U128 | I128) => Some(Instr::ZExt(value, other.clone())),
|
||||
(U128 | I128, U128 | I128) => Some(Instr::ZExt(value, other.clone())),
|
||||
(U8 | U16 | U32 | U64 | U128, F16 | F32 | F32B | F64 | F80 | F128 | F128PPC) => {
|
||||
Some(Instr::UIToFP(value, other.clone()))
|
||||
}
|
||||
(I8 | I16 | I32 | I64 | I128, F16 | F32 | F32B | F64 | F80 | F128 | F128PPC) => {
|
||||
Some(Instr::SIToFP(value, other.clone()))
|
||||
}
|
||||
(F16 | F32 | F32B | F64 | F80 | F128 | F128PPC, U8 | U16 | U32 | U64 | U128) => {
|
||||
Some(Instr::FPToUI(value, other.clone()))
|
||||
}
|
||||
(F16 | F32 | F32B | F64 | F80 | F128 | F128PPC, I8 | I16 | I32 | I64 | I128) => {
|
||||
Some(Instr::FPToSI(value, other.clone()))
|
||||
}
|
||||
(I128 | U128 | I64 | U64 | I32 | U32 | I16 | U16 | I8 | U8, Ptr(_)) => {
|
||||
Some(Instr::IntToPtr(value, other.clone()))
|
||||
}
|
||||
(Ptr(_), I128 | U128 | I64 | U64 | I32 | U32 | I16 | U16 | I8 | U8) => {
|
||||
Some(Instr::PtrToInt(value, other.clone()))
|
||||
}
|
||||
(F16, F32 | F32B | F64 | F80 | F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())),
|
||||
(F32 | F32B, F64 | F80 | F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())),
|
||||
(F64, F80 | F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())),
|
||||
(F80, F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())),
|
||||
(F128PPC | F128, F80 | F64 | F32B | F32 | F16) => Some(Instr::FPTrunc(value, other.clone())),
|
||||
(F80, F64 | F32B | F32 | F16) => Some(Instr::FPTrunc(value, other.clone())),
|
||||
(F64, F32B | F32 | F16) => Some(Instr::FPTrunc(value, other.clone())),
|
||||
(F32B | F32, F16) => Some(Instr::FPTrunc(value, other.clone())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TerminatorKind {
|
||||
pub(crate) fn get_type(&self, builder: &Builder) -> CompileResult<Type> {
|
||||
use TerminatorKind::*;
|
||||
match self {
|
||||
Ret(instr_val) => instr_val.get_type(builder),
|
||||
RetVoid => Ok(Type::Void),
|
||||
Br(_) => Ok(Type::Void),
|
||||
CondBr(_, _, _) => Ok(Type::Void),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,69 +0,0 @@
|
||||
//! Copied from
|
||||
//! https://github.com/rust-lang/rust/blob/6b3ae3f6e45a33c2d95fa0362c9b2593e567fd34/library/core/src/fmt/builders.rs#L102
|
||||
|
||||
// Copyright (c) The Rust Project Contributors
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
pub struct PadAdapter<'buf, 'state> {
|
||||
buf: &'buf mut (dyn fmt::Write + 'buf),
|
||||
state: &'state mut PadAdapterState,
|
||||
}
|
||||
|
||||
pub struct PadAdapterState {
|
||||
on_newline: bool,
|
||||
}
|
||||
|
||||
impl Default for PadAdapterState {
|
||||
fn default() -> Self {
|
||||
PadAdapterState { on_newline: true }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'buf, 'state> PadAdapter<'buf, 'state> {
|
||||
pub fn wrap<'slot, 'fmt: 'buf + 'slot>(
|
||||
fmt: &'buf mut (dyn fmt::Write + 'buf),
|
||||
state: &'state mut PadAdapterState,
|
||||
) -> Self {
|
||||
PadAdapter { buf: fmt, state }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for PadAdapter<'_, '_> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for s in s.split_inclusive('\n') {
|
||||
if self.state.on_newline {
|
||||
self.buf.write_str(" ")?;
|
||||
}
|
||||
self.state.on_newline = s.ends_with('\n');
|
||||
self.buf.write_str(s)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_char(&mut self, c: char) -> fmt::Result {
|
||||
if self.state.on_newline {
|
||||
self.buf.write_str(" ")?;
|
||||
}
|
||||
self.state.on_newline = c == '\n';
|
||||
self.buf.write_char(c)
|
||||
}
|
||||
}
|
||||
@ -1,132 +0,0 @@
|
||||
use std::{
|
||||
ffi::{CStr, CString, c_char},
|
||||
ptr::null_mut,
|
||||
string::FromUtf8Error,
|
||||
};
|
||||
|
||||
use llvm_sys::{
|
||||
core::{
|
||||
LLVMCreateMemoryBufferWithMemoryRange, LLVMDisposeMemoryBuffer, LLVMGetBufferSize,
|
||||
LLVMGetBufferStart,
|
||||
},
|
||||
error::LLVMDisposeErrorMessage,
|
||||
prelude::LLVMMemoryBufferRef,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
CompileResult, ErrorKind, Type,
|
||||
builder::{Builder, InstructionValue},
|
||||
};
|
||||
|
||||
pub fn into_cstring<T: Into<String>>(value: T) -> CString {
|
||||
let string = value.into();
|
||||
unsafe { CString::from_vec_with_nul_unchecked((string + "\0").into_bytes()) }
|
||||
}
|
||||
|
||||
pub fn from_cstring(pointer: *mut c_char) -> Option<String> {
|
||||
if pointer.is_null() {
|
||||
None
|
||||
} else {
|
||||
unsafe { CStr::from_ptr(pointer).to_str().ok().map(|s| s.to_owned()) }
|
||||
}
|
||||
}
|
||||
|
||||
fn cstring_to_err(value: *mut c_char) -> Result<(), String> {
|
||||
from_cstring(value)
|
||||
.filter(|s| !s.is_empty())
|
||||
.map_or(Ok(()), |s| Err(s))
|
||||
}
|
||||
|
||||
/// Utility struct for LLVM's Error Messages, which need to be disposed
|
||||
/// manually.
|
||||
pub struct ErrorMessageHolder(*mut c_char);
|
||||
|
||||
impl ErrorMessageHolder {
|
||||
pub fn null() -> Self {
|
||||
ErrorMessageHolder(null_mut())
|
||||
}
|
||||
|
||||
pub fn borrow_mut(&mut self) -> *mut *mut c_char {
|
||||
&mut self.0
|
||||
}
|
||||
|
||||
pub fn into_result(&self) -> Result<(), String> {
|
||||
cstring_to_err(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ErrorMessageHolder {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if !self.0.is_null() {
|
||||
LLVMDisposeErrorMessage(self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Utility for creating and handling LLVM MemoryBuffers, needed for printing
|
||||
/// out ASM and .o -files without relying on LLVM's own API.
|
||||
pub struct MemoryBufferHolder {
|
||||
pub buffer: LLVMMemoryBufferRef,
|
||||
}
|
||||
|
||||
impl MemoryBufferHolder {
|
||||
pub fn empty(name: &str) -> MemoryBufferHolder {
|
||||
let array = [0i8; 0];
|
||||
unsafe {
|
||||
let buffer = LLVMCreateMemoryBufferWithMemoryRange(
|
||||
array.as_ptr(),
|
||||
array.len(),
|
||||
into_cstring(name).as_ptr(),
|
||||
0,
|
||||
);
|
||||
MemoryBufferHolder { buffer }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_buffer(&self) -> Vec<u8> {
|
||||
unsafe {
|
||||
let start = LLVMGetBufferStart(self.buffer);
|
||||
let size = LLVMGetBufferSize(self.buffer);
|
||||
|
||||
let mut buff = Vec::with_capacity(size);
|
||||
for i in 0..size {
|
||||
buff.push(*start.add(i) as u8);
|
||||
}
|
||||
buff
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_string(&self) -> Result<String, FromUtf8Error> {
|
||||
String::from_utf8(self.as_buffer())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MemoryBufferHolder {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
LLVMDisposeMemoryBuffer(self.buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Make sure types for given instructions match. Return Ok(type) if they do,
|
||||
/// and error otherwise.
|
||||
pub fn match_types(
|
||||
lhs: &InstructionValue,
|
||||
rhs: &InstructionValue,
|
||||
builder: &Builder,
|
||||
) -> CompileResult<Type> {
|
||||
let lhs_type = lhs.get_type(&builder);
|
||||
let rhs_type = rhs.get_type(&builder);
|
||||
if let (Ok(lhs_t), Ok(rhs_t)) = (lhs_type, rhs_type) {
|
||||
if lhs_t == rhs_t {
|
||||
Ok(lhs_t)
|
||||
} else {
|
||||
Err(ErrorKind::Null)
|
||||
}
|
||||
} else {
|
||||
Err(ErrorKind::Null)
|
||||
}
|
||||
}
|
||||
7
reid-lsp/.gitignore
vendored
Normal file
7
reid-lsp/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
.vscode
|
||||
node_modules
|
||||
dist
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
tsconfig.tsbuildinfo
|
||||
*.vsix
|
||||
1
reid-lsp/.npmrc
Normal file
1
reid-lsp/.npmrc
Normal file
@ -0,0 +1 @@
|
||||
enable-pre-post-scripts = true
|
||||
5
reid-lsp/.vscode-test.mjs
Normal file
5
reid-lsp/.vscode-test.mjs
Normal file
@ -0,0 +1,5 @@
|
||||
import { defineConfig } from '@vscode/test-cli';
|
||||
|
||||
export default defineConfig({
|
||||
files: 'out/test/**/*.test.js',
|
||||
});
|
||||
15
reid-lsp/.vscodeignore
Normal file
15
reid-lsp/.vscodeignore
Normal file
@ -0,0 +1,15 @@
|
||||
.vscode/**
|
||||
.vscode-test/**
|
||||
out/**
|
||||
node_modules/**
|
||||
src/**
|
||||
client/**
|
||||
.gitignore
|
||||
.yarnrc
|
||||
webpack.config.js
|
||||
vsc-extension-quickstart.md
|
||||
**/tsconfig.json
|
||||
**/eslint.config.mjs
|
||||
**/*.map
|
||||
**/*.ts
|
||||
**/.vscode-test.*
|
||||
9
reid-lsp/CHANGELOG.md
Normal file
9
reid-lsp/CHANGELOG.md
Normal file
@ -0,0 +1,9 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to the "reid-language-server" extension will be documented in this file.
|
||||
|
||||
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- Initial release
|
||||
13
reid-lsp/Cargo.toml
Normal file
13
reid-lsp/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "reid-language-server"
|
||||
version = "1.0.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
socket = "0.0.7"
|
||||
tokio = { version = "1.47.0", features = ["full"] }
|
||||
tower-lsp = "0.20.0"
|
||||
reid = { path = "../reid", version = "1.0.0", registry="gitea-teascade", features=[] }
|
||||
dashmap = "6.1.0"
|
||||
serde = "*"
|
||||
serde_json = "*"
|
||||
1
reid-lsp/README.md
Normal file
1
reid-lsp/README.md
Normal file
@ -0,0 +1 @@
|
||||
# Reid Language Server
|
||||
27
reid-lsp/client/package.json
Normal file
27
reid-lsp/client/package.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "reid-lsp",
|
||||
"displayName": "Reid Language Server",
|
||||
"description": "Language Server Extension for Reid",
|
||||
"version": "0.0.1",
|
||||
"engines": {
|
||||
"vscode": "^1.102.0"
|
||||
},
|
||||
"main": "../out/extension.js",
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^10.0.10",
|
||||
"@types/node": "20.x",
|
||||
"@types/vscode": "^1.102.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.31.1",
|
||||
"@typescript-eslint/parser": "^8.31.1",
|
||||
"@vscode/test-cli": "^0.0.11",
|
||||
"@vscode/test-electron": "^2.5.2",
|
||||
"eslint": "^9.25.1",
|
||||
"ts-loader": "^9.5.2",
|
||||
"typescript": "^5.8.3",
|
||||
"webpack": "^5.99.7",
|
||||
"webpack-cli": "^6.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-languageclient": "^9.0.1"
|
||||
}
|
||||
}
|
||||
96
reid-lsp/client/src/extension.ts
Normal file
96
reid-lsp/client/src/extension.ts
Normal file
@ -0,0 +1,96 @@
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as path from 'path';
|
||||
import { workspace, ExtensionContext, window, languages, SemanticTokensBuilder } from 'vscode';
|
||||
|
||||
import {
|
||||
Executable,
|
||||
LanguageClient,
|
||||
LanguageClientOptions,
|
||||
ServerOptions,
|
||||
TransportKind
|
||||
} from 'vscode-languageclient/node';
|
||||
|
||||
|
||||
let client: LanguageClient;
|
||||
|
||||
export function activate(context: ExtensionContext) {
|
||||
const configuration = workspace.getConfiguration('reid-language-server');
|
||||
let server_path: string = process.env.SERVER_PATH ?? configuration.get("language-server-path") ?? 'reid-language-server';
|
||||
const regex = /\$(\w+)/;
|
||||
while (regex.test(server_path)) {
|
||||
let envVar = regex.exec(server_path)?.[1];
|
||||
const envVal = envVar ? process.env[envVar] : undefined;
|
||||
if (envVar === undefined || envVal === undefined) {
|
||||
console.error(`No such environment variables as ${envVar}`);
|
||||
}
|
||||
server_path = server_path.replaceAll(`$${envVar}`, envVal ?? '');
|
||||
}
|
||||
|
||||
const run: Executable = {
|
||||
command: server_path,
|
||||
options: {
|
||||
env: {
|
||||
...process.env,
|
||||
RUST_LOG: "debug",
|
||||
RUST_BACKTRACE: 1,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const serverOptions: ServerOptions = {
|
||||
run,
|
||||
debug: run,
|
||||
};
|
||||
|
||||
// Options to control the language client
|
||||
const clientOptions: LanguageClientOptions = {
|
||||
// Register the server for plain text documents
|
||||
documentSelector: [{ scheme: 'file', language: 'reid' }],
|
||||
synchronize: {
|
||||
// Notify the server about file changes to '.clientrc files contained in the workspace
|
||||
fileEvents: workspace.createFileSystemWatcher('**/.clientrc')
|
||||
},
|
||||
};
|
||||
|
||||
// Create the language client and start the client.
|
||||
client = new LanguageClient(
|
||||
'reid-language-server',
|
||||
'Reid Language Server',
|
||||
serverOptions,
|
||||
clientOptions
|
||||
);
|
||||
client.info(JSON.stringify(server_path));
|
||||
|
||||
client.info(`Loaded Reid Language Server from ${server_path}`);
|
||||
|
||||
|
||||
workspace.onDidOpenTextDocument((e) => { });
|
||||
|
||||
client.info("Registering semantic tokens provide");
|
||||
context.subscriptions.push(languages.registerDocumentSemanticTokensProvider({
|
||||
language: 'reid',
|
||||
scheme: 'file'
|
||||
}, {
|
||||
provideDocumentSemanticTokens: () => {
|
||||
const builder = new SemanticTokensBuilder();
|
||||
return builder.build();
|
||||
}
|
||||
}, {
|
||||
tokenTypes: [],
|
||||
tokenModifiers: [],
|
||||
}));
|
||||
|
||||
// Start the client. This will also launch the server
|
||||
client.start();
|
||||
}
|
||||
|
||||
export function deactivate(): Thenable<void> | undefined {
|
||||
if (!client) {
|
||||
return undefined;
|
||||
}
|
||||
return client.stop();
|
||||
}
|
||||
15
reid-lsp/client/src/test/extension.test.ts
Normal file
15
reid-lsp/client/src/test/extension.test.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import * as assert from 'assert';
|
||||
|
||||
// You can import and use all API from the 'vscode' module
|
||||
// as well as import your extension to test it
|
||||
import * as vscode from 'vscode';
|
||||
// import * as myExtension from '../../extension';
|
||||
|
||||
suite('Extension Test Suite', () => {
|
||||
vscode.window.showInformationMessage('Start all tests.');
|
||||
|
||||
test('Sample test', () => {
|
||||
assert.strictEqual(-1, [1, 2, 3].indexOf(5));
|
||||
assert.strictEqual(-1, [1, 2, 3].indexOf(0));
|
||||
});
|
||||
});
|
||||
24
reid-lsp/client/tsconfig.json
Normal file
24
reid-lsp/client/tsconfig.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "Node16",
|
||||
"target": "ES2022",
|
||||
"lib": [
|
||||
"ES2022"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"rootDir": "src",
|
||||
"outDir": "../dist",
|
||||
"strict": true /* enable all strict type-checking options */
|
||||
/* Additional Checks */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
".vscode-test"
|
||||
]
|
||||
}
|
||||
28
reid-lsp/eslint.config.mjs
Normal file
28
reid-lsp/eslint.config.mjs
Normal file
@ -0,0 +1,28 @@
|
||||
import typescriptEslint from "@typescript-eslint/eslint-plugin";
|
||||
import tsParser from "@typescript-eslint/parser";
|
||||
|
||||
export default [{
|
||||
files: ["**/*.ts"],
|
||||
}, {
|
||||
plugins: {
|
||||
"@typescript-eslint": typescriptEslint,
|
||||
},
|
||||
|
||||
languageOptions: {
|
||||
parser: tsParser,
|
||||
ecmaVersion: 2022,
|
||||
sourceType: "module",
|
||||
},
|
||||
|
||||
rules: {
|
||||
"@typescript-eslint/naming-convention": ["warn", {
|
||||
selector: "import",
|
||||
format: ["camelCase", "PascalCase"],
|
||||
}],
|
||||
|
||||
curly: "warn",
|
||||
eqeqeq: "warn",
|
||||
"no-throw-literal": "warn",
|
||||
semi: "warn",
|
||||
},
|
||||
}];
|
||||
92
reid-lsp/language-configuration.json
Normal file
92
reid-lsp/language-configuration.json
Normal file
@ -0,0 +1,92 @@
|
||||
{
|
||||
"comments": {
|
||||
"lineComment": "//",
|
||||
},
|
||||
"brackets": [
|
||||
[
|
||||
"{",
|
||||
"}"
|
||||
],
|
||||
[
|
||||
"[",
|
||||
"]"
|
||||
],
|
||||
[
|
||||
"(",
|
||||
")"
|
||||
]
|
||||
],
|
||||
"autoClosingPairs": [
|
||||
{
|
||||
"open": "{",
|
||||
"close": "}"
|
||||
},
|
||||
{
|
||||
"open": "[",
|
||||
"close": "]"
|
||||
},
|
||||
{
|
||||
"open": "(",
|
||||
"close": ")"
|
||||
},
|
||||
{
|
||||
"open": "'",
|
||||
"close": "'",
|
||||
"notIn": [
|
||||
"string",
|
||||
"comment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"open": "\"",
|
||||
"close": "\"",
|
||||
"notIn": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
],
|
||||
"autoCloseBefore": ";:.,=}])>` \n\t",
|
||||
"surroundingPairs": [
|
||||
[
|
||||
"{",
|
||||
"}"
|
||||
],
|
||||
[
|
||||
"[",
|
||||
"]"
|
||||
],
|
||||
[
|
||||
"(",
|
||||
")"
|
||||
],
|
||||
[
|
||||
"\"",
|
||||
"\""
|
||||
],
|
||||
],
|
||||
"folding": {
|
||||
"markers": {
|
||||
"start": "^\\s*//\\s*#?region\\b",
|
||||
"end": "^\\s*//\\s*#?endregion\\b"
|
||||
}
|
||||
},
|
||||
"wordPattern": "[a-Z](\\w*)",
|
||||
"indentationRules": {
|
||||
"increaseIndentPattern": "^((?!\\/\\/).)*(\\{[^}\"'`]*|\\([^)\"'`]*|\\[[^\\]\"'`]*)$",
|
||||
"decreaseIndentPattern": "^((?!.*?\\/\\*).*\\*/)?\\s*[\\)\\}\\]].*$"
|
||||
},
|
||||
"colorizedBracketPairs": [
|
||||
[
|
||||
"(",
|
||||
")"
|
||||
],
|
||||
[
|
||||
"[",
|
||||
"]"
|
||||
],
|
||||
[
|
||||
"{",
|
||||
"}"
|
||||
]
|
||||
]
|
||||
}
|
||||
94
reid-lsp/package.json
Normal file
94
reid-lsp/package.json
Normal file
@ -0,0 +1,94 @@
|
||||
{
|
||||
"name": "reid-language-server",
|
||||
"displayName": "Reid Language Server",
|
||||
"description": "Language Server Extension for Reid",
|
||||
"version": "1.0.0",
|
||||
"repository": {
|
||||
"url": "https://git.teascade.net/teascade"
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "^1.102.0"
|
||||
},
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
"main": "./dist/extension.js",
|
||||
"icon": "./reid.png",
|
||||
"publisher": "Teascade",
|
||||
"author": {
|
||||
"email": "teascade@teascade.net",
|
||||
"name": "Teascade",
|
||||
"url": "https://teascade.net"
|
||||
},
|
||||
"contributes": {
|
||||
"languages": [
|
||||
{
|
||||
"id": "reid",
|
||||
"extensions": [
|
||||
".reid"
|
||||
],
|
||||
"aliases": [
|
||||
"Reid"
|
||||
],
|
||||
"configuration": "./language-configuration.json",
|
||||
"icon": {
|
||||
"dark": "./reid.png",
|
||||
"light": "./reid.png"
|
||||
}
|
||||
}
|
||||
],
|
||||
"breakpoints": [
|
||||
{
|
||||
"language": "reid"
|
||||
}
|
||||
],
|
||||
"configuration": {
|
||||
"type": "object",
|
||||
"title": "reid-language-server",
|
||||
"properties": {
|
||||
"reid-language-server.language-server-path": {
|
||||
"type": "string",
|
||||
"scope": "window",
|
||||
"default": "$HOME/.cargo/bin/reid-language-server",
|
||||
"description": "Path to the Reid Language Server executable"
|
||||
}
|
||||
}
|
||||
},
|
||||
"grammars": [
|
||||
{
|
||||
"language": "reid",
|
||||
"scopeName": "source.reid",
|
||||
"path": "./syntaxes/grammar.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "pnpm run package",
|
||||
"compile": "npx js-yaml syntaxes/grammar.yaml > syntaxes/grammar.json && webpack",
|
||||
"watch": "webpack --watch",
|
||||
"package": "webpack --mode production --devtool hidden-source-map",
|
||||
"compile-tests": "tsc -p . --outDir out",
|
||||
"watch-tests": "tsc -p . -w --outDir out",
|
||||
"pretest": "pnpm run compile-tests && pnpm run compile && pnpm run lint",
|
||||
"lint": "eslint src",
|
||||
"test": "vscode-test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^10.0.10",
|
||||
"@types/node": "20.x",
|
||||
"@types/vscode": "^1.102.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.31.1",
|
||||
"@typescript-eslint/parser": "^8.31.1",
|
||||
"@vscode/test-cli": "^0.0.11",
|
||||
"@vscode/test-electron": "^2.5.2",
|
||||
"eslint": "^9.25.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"ts-loader": "^9.5.2",
|
||||
"typescript": "^5.8.3",
|
||||
"webpack": "^5.99.7",
|
||||
"webpack-cli": "^6.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-languageclient": "^9.0.1"
|
||||
}
|
||||
}
|
||||
BIN
reid-lsp/reid.png
Normal file
BIN
reid-lsp/reid.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
1255
reid-lsp/src/analysis.rs
Normal file
1255
reid-lsp/src/analysis.rs
Normal file
File diff suppressed because it is too large
Load Diff
605
reid-lsp/src/main.rs
Normal file
605
reid-lsp/src/main.rs
Normal file
@ -0,0 +1,605 @@
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use dashmap::DashMap;
|
||||
use reid::ast::lexer::{FullToken, Position};
|
||||
use reid::error_raporting::{self, ErrorModules, ReidError};
|
||||
use reid::mir::SourceModuleId;
|
||||
use reid::parse_module;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::Mutex;
|
||||
use tower_lsp::lsp_types::{
|
||||
self, CompletionItem, CompletionOptions, CompletionParams, CompletionResponse, Diagnostic, DiagnosticSeverity,
|
||||
DidChangeTextDocumentParams, DidOpenTextDocumentParams, DidSaveTextDocumentParams, DocumentFilter,
|
||||
GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverContents, HoverParams, HoverProviderCapability,
|
||||
InitializeParams, InitializeResult, InitializedParams, Location, MarkedString, MarkupContent, MarkupKind,
|
||||
MessageType, OneOf, Range, ReferenceParams, RenameParams, SemanticToken, SemanticTokensLegend,
|
||||
SemanticTokensOptions, SemanticTokensParams, SemanticTokensResult, SemanticTokensServerCapabilities,
|
||||
ServerCapabilities, TextDocumentItem, TextDocumentRegistrationOptions, TextDocumentSyncCapability,
|
||||
TextDocumentSyncKind, TextDocumentSyncOptions, TextDocumentSyncSaveOptions, TextEdit, Url, WorkspaceEdit,
|
||||
WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities,
|
||||
};
|
||||
use tower_lsp::{Client, LanguageServer, LspService, Server, jsonrpc};
|
||||
|
||||
use crate::analysis::{MODIFIER_LEGEND, StateMap, StaticAnalysis, TOKEN_LEGEND, analyze};
|
||||
|
||||
mod analysis;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Backend {
|
||||
client: Client,
|
||||
analysis: DashMap<PathBuf, StaticAnalysis>,
|
||||
module_to_path: DashMap<SourceModuleId, PathBuf>,
|
||||
path_to_module: DashMap<PathBuf, SourceModuleId>,
|
||||
module_id_counter: Mutex<SourceModuleId>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct CompletionData {
|
||||
token_idx: usize,
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
#[tower_lsp::async_trait]
|
||||
impl LanguageServer for Backend {
|
||||
async fn initialize(&self, _: InitializeParams) -> jsonrpc::Result<InitializeResult> {
|
||||
self.client
|
||||
.log_message(MessageType::INFO, "Initializing Reid Language Server")
|
||||
.await;
|
||||
|
||||
let sync = TextDocumentSyncOptions {
|
||||
open_close: Some(true),
|
||||
change: Some(TextDocumentSyncKind::FULL),
|
||||
will_save: None,
|
||||
will_save_wait_until: None,
|
||||
save: Some(TextDocumentSyncSaveOptions::SaveOptions(lsp_types::SaveOptions {
|
||||
include_text: Some(true),
|
||||
})),
|
||||
};
|
||||
|
||||
let capabilities = ServerCapabilities {
|
||||
hover_provider: Some(HoverProviderCapability::Simple(true)),
|
||||
completion_provider: Some(CompletionOptions {
|
||||
trigger_characters: None,
|
||||
all_commit_characters: None,
|
||||
completion_item: Some(lsp_types::CompletionOptionsCompletionItem {
|
||||
label_details_support: Some(true),
|
||||
}),
|
||||
resolve_provider: Some(false),
|
||||
work_done_progress_options: lsp_types::WorkDoneProgressOptions {
|
||||
work_done_progress: Some(true),
|
||||
},
|
||||
}),
|
||||
text_document_sync: Some(TextDocumentSyncCapability::Options(sync)),
|
||||
workspace: Some(WorkspaceServerCapabilities {
|
||||
workspace_folders: Some(WorkspaceFoldersServerCapabilities {
|
||||
supported: Some(true),
|
||||
change_notifications: Some(OneOf::Left(true)),
|
||||
}),
|
||||
file_operations: None,
|
||||
}),
|
||||
semantic_tokens_provider: Some(SemanticTokensServerCapabilities::SemanticTokensRegistrationOptions(
|
||||
lsp_types::SemanticTokensRegistrationOptions {
|
||||
text_document_registration_options: TextDocumentRegistrationOptions {
|
||||
document_selector: Some(vec![DocumentFilter {
|
||||
language: Some("reid".to_owned()),
|
||||
scheme: Some("file".to_owned()),
|
||||
pattern: None,
|
||||
}]),
|
||||
},
|
||||
semantic_tokens_options: SemanticTokensOptions {
|
||||
work_done_progress_options: Default::default(),
|
||||
legend: SemanticTokensLegend {
|
||||
token_types: TOKEN_LEGEND.into(),
|
||||
token_modifiers: MODIFIER_LEGEND.into(),
|
||||
},
|
||||
range: None,
|
||||
full: Some(lsp_types::SemanticTokensFullOptions::Bool(true)),
|
||||
},
|
||||
static_registration_options: Default::default(),
|
||||
},
|
||||
)),
|
||||
references_provider: Some(OneOf::Left(true)),
|
||||
definition_provider: Some(OneOf::Left(true)),
|
||||
rename_provider: Some(OneOf::Left(true)),
|
||||
..Default::default()
|
||||
};
|
||||
Ok(InitializeResult {
|
||||
capabilities,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
async fn initialized(&self, _: InitializedParams) {
|
||||
self.client
|
||||
.log_message(MessageType::INFO, "Reid Language Server initialized!")
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn shutdown(&self) -> jsonrpc::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn completion(&self, params: CompletionParams) -> jsonrpc::Result<Option<CompletionResponse>> {
|
||||
let path = PathBuf::from(params.text_document_position.text_document.uri.path());
|
||||
let analysis = self.analysis.get(&path);
|
||||
let position = params.text_document_position.position;
|
||||
|
||||
let token = if let Some(analysis) = &analysis {
|
||||
analysis.tokens.iter().enumerate().find(|(_, tok)| {
|
||||
tok.position.1 == position.line + 1
|
||||
&& (tok.position.0 <= position.character
|
||||
&& (tok.position.0 + tok.token.len() as u32) > position.character)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// dbg!(position, token);
|
||||
|
||||
let list = if let Some((idx, _)) = token {
|
||||
if let Some(token_analysis) = self.analysis.get(&path).unwrap().state.map.get(&idx) {
|
||||
token_analysis
|
||||
.autocomplete
|
||||
.iter()
|
||||
.map(|autocomplete| {
|
||||
let mut item =
|
||||
CompletionItem::new_simple(autocomplete.text.to_string(), autocomplete.kind.to_string());
|
||||
item.data = Some(
|
||||
serde_json::to_value(CompletionData {
|
||||
token_idx: idx,
|
||||
path: path.clone(),
|
||||
})
|
||||
.unwrap(),
|
||||
);
|
||||
item.documentation = autocomplete.documentation.as_ref().and_then(|d| {
|
||||
Some(lsp_types::Documentation::MarkupContent(MarkupContent {
|
||||
kind: MarkupKind::Markdown,
|
||||
value: d.clone(),
|
||||
}))
|
||||
});
|
||||
item
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
// dbg!(&list);
|
||||
Ok(Some(CompletionResponse::Array(list)))
|
||||
}
|
||||
|
||||
async fn hover(&self, params: HoverParams) -> jsonrpc::Result<Option<Hover>> {
|
||||
let path = PathBuf::from(params.text_document_position_params.text_document.uri.path());
|
||||
let analysis = self.analysis.get(&path);
|
||||
let position = params.text_document_position_params.position;
|
||||
|
||||
let token = if let Some(analysis) = &analysis {
|
||||
analysis.tokens.iter().enumerate().find(|(_, tok)| {
|
||||
tok.position.1 == position.line + 1
|
||||
&& (tok.position.0 <= position.character + 1
|
||||
&& (tok.position.0 + tok.token.len() as u32) > position.character + 1)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let (range, ty, documentation) = if let Some((idx, token)) = token {
|
||||
if let Some(analysis) = self.analysis.get(&path).unwrap().state.map.get(&idx) {
|
||||
let start = token.position;
|
||||
let end = token.position.add(token.token.len() as u32);
|
||||
let range = Range {
|
||||
start: lsp_types::Position {
|
||||
line: (start.1 as i32 - 1).max(0) as u32,
|
||||
character: (start.0 as i32 - 1).max(0) as u32,
|
||||
},
|
||||
end: lsp_types::Position {
|
||||
line: (end.1 as i32 - 1).max(0) as u32,
|
||||
character: (end.0 as i32 - 1).max(0) as u32,
|
||||
},
|
||||
};
|
||||
if let Some(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("No type"),
|
||||
analysis.hover.documentation.clone(),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
(None, String::from("no type"), None)
|
||||
}
|
||||
} else {
|
||||
(None, String::from("no token"), None)
|
||||
};
|
||||
|
||||
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 }))
|
||||
}
|
||||
|
||||
async fn did_open(&self, params: DidOpenTextDocumentParams) {
|
||||
self.recompile(TextDocumentItem {
|
||||
uri: params.text_document.uri,
|
||||
language_id: params.text_document.language_id,
|
||||
version: params.text_document.version,
|
||||
text: params.text_document.text,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn did_change(&self, params: DidChangeTextDocumentParams) {
|
||||
self.recompile(TextDocumentItem {
|
||||
text: params.content_changes[0].text.clone(),
|
||||
uri: params.text_document.uri,
|
||||
version: params.text_document.version,
|
||||
language_id: String::new(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn did_save(&self, params: DidSaveTextDocumentParams) {
|
||||
self.recompile(TextDocumentItem {
|
||||
text: params.text.unwrap(),
|
||||
uri: params.text_document.uri,
|
||||
version: 0,
|
||||
language_id: String::new(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn semantic_tokens_full(
|
||||
&self,
|
||||
params: SemanticTokensParams,
|
||||
) -> jsonrpc::Result<Option<SemanticTokensResult>> {
|
||||
let path = PathBuf::from(params.text_document.uri.path());
|
||||
let analysis = self.analysis.get(&path);
|
||||
|
||||
let mut semantic_tokens = Vec::new();
|
||||
if let Some(analysis) = analysis {
|
||||
let mut prev_line = 0;
|
||||
let mut prev_start = 0;
|
||||
for (i, token) in analysis.tokens.iter().enumerate() {
|
||||
let vscode_line = token.position.1.max(1) - 1;
|
||||
let vscode_col = token.position.0.max(1) - 1;
|
||||
|
||||
let delta_line = vscode_line - prev_line;
|
||||
let delta_start = if delta_line == 0 {
|
||||
vscode_col - prev_start
|
||||
} else {
|
||||
vscode_col
|
||||
};
|
||||
|
||||
if let Some(token_analysis) = analysis.state.map.get(&i) {
|
||||
if let Some(symbol_id) = token_analysis.symbol {
|
||||
let symbol = analysis.state.get_local_symbol(symbol_id);
|
||||
if let Some(idx) = symbol.kind.into_token_idx(&self.state_map()) {
|
||||
let semantic_token = SemanticToken {
|
||||
delta_line,
|
||||
delta_start,
|
||||
length: token.token.len() as u32,
|
||||
token_type: idx,
|
||||
token_modifiers_bitset: symbol.kind.get_modifier().unwrap_or(0),
|
||||
};
|
||||
semantic_tokens.push(semantic_token);
|
||||
prev_line = vscode_line;
|
||||
prev_start = vscode_col;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(SemanticTokensResult::Tokens(lsp_types::SemanticTokens {
|
||||
result_id: None,
|
||||
data: semantic_tokens,
|
||||
})))
|
||||
}
|
||||
|
||||
async fn goto_definition(&self, params: GotoDefinitionParams) -> jsonrpc::Result<Option<GotoDefinitionResponse>> {
|
||||
let path = PathBuf::from(params.text_document_position_params.text_document.uri.path());
|
||||
let analysis = self.analysis.get(&path);
|
||||
let position = params.text_document_position_params.position;
|
||||
|
||||
if let Some(analysis) = &analysis {
|
||||
let token = analysis.tokens.iter().enumerate().find(|(_, tok)| {
|
||||
tok.position.1 == position.line + 1
|
||||
&& (tok.position.0 <= position.character + 1
|
||||
&& (tok.position.0 + tok.token.len() as u32) > position.character + 1)
|
||||
});
|
||||
|
||||
if let Some(token) = token {
|
||||
if let Some((module_id, def_token)) = analysis.find_definition(token.0, &self.state_map()) {
|
||||
return if let Some(path) = self.module_to_path.get(&module_id) {
|
||||
Ok(Some(GotoDefinitionResponse::Scalar(lsp_types::Location {
|
||||
uri: Url::from_file_path(path.value()).unwrap(),
|
||||
range: token_to_range(def_token),
|
||||
})))
|
||||
} else {
|
||||
Ok(None)
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn references(&self, params: ReferenceParams) -> jsonrpc::Result<Option<Vec<Location>>> {
|
||||
let path = PathBuf::from(params.text_document_position.text_document.uri.path());
|
||||
let analysis = self.analysis.get(&path);
|
||||
let position = params.text_document_position.position;
|
||||
|
||||
if let Some(analysis) = &analysis {
|
||||
let token = analysis.tokens.iter().enumerate().find(|(_, tok)| {
|
||||
tok.position.1 == position.line + 1
|
||||
&& (tok.position.0 <= position.character + 1
|
||||
&& (tok.position.0 + tok.token.len() as u32) > position.character + 1)
|
||||
});
|
||||
if let Some(token) = token {
|
||||
let reference_tokens = analysis.find_references(token.0, &self.state_map());
|
||||
let mut locations = Vec::new();
|
||||
if let Some(reference_tokens) = reference_tokens {
|
||||
for (module_id, symbol_idx) in reference_tokens {
|
||||
if let Some(path) = self.module_to_path.get(&module_id) {
|
||||
let url = Url::from_file_path(path.value()).unwrap();
|
||||
if let Some(inner_analysis) = self.analysis.get(path.value()) {
|
||||
if let Some(token_idx) = inner_analysis.state.symbol_to_token.get(&symbol_idx) {
|
||||
let token = inner_analysis.tokens.get(*token_idx).unwrap();
|
||||
|
||||
locations.push(lsp_types::Location {
|
||||
uri: url,
|
||||
range: token_to_range(token),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Some(locations))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
async fn rename(&self, params: RenameParams) -> jsonrpc::Result<Option<WorkspaceEdit>> {
|
||||
let path = PathBuf::from(params.text_document_position.text_document.uri.path());
|
||||
let analysis = self.analysis.get(&path);
|
||||
let position = params.text_document_position.position;
|
||||
|
||||
if let Some(analysis) = &analysis {
|
||||
let token = analysis.tokens.iter().enumerate().find(|(_, tok)| {
|
||||
tok.position.1 == position.line + 1
|
||||
&& (tok.position.0 <= position.character + 1
|
||||
&& (tok.position.0 + tok.token.len() as u32) > position.character + 1)
|
||||
});
|
||||
if let Some(token) = token {
|
||||
let symbols = analysis.find_references(token.0, &self.state_map());
|
||||
let mut changes: HashMap<Url, Vec<TextEdit>> = HashMap::new();
|
||||
if let Some(symbols) = symbols {
|
||||
for (module_id, symbol_id) in symbols {
|
||||
let path = self.module_to_path.get(&module_id);
|
||||
if let Some(path) = path {
|
||||
let url = Url::from_file_path(path.value()).unwrap();
|
||||
let analysis = self.analysis.get(&path.clone());
|
||||
|
||||
if let Some(analysis) = analysis {
|
||||
if let Some(token_idx) = analysis.state.symbol_to_token.get(&symbol_id) {
|
||||
let token = analysis.tokens.get(*token_idx).unwrap();
|
||||
|
||||
// edits = changes.get(k)
|
||||
let edit = TextEdit {
|
||||
range: token_to_range(token),
|
||||
new_text: params.new_name.clone(),
|
||||
};
|
||||
if let Some(edits) = changes.get_mut(&url) {
|
||||
edits.push(edit);
|
||||
} else {
|
||||
changes.insert(url, vec![edit]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(WorkspaceEdit {
|
||||
changes: Some(changes),
|
||||
document_changes: None,
|
||||
change_annotations: None,
|
||||
}))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn token_to_range(token: &FullToken) -> lsp_types::Range {
|
||||
Range {
|
||||
start: lsp_types::Position {
|
||||
line: token.position.1.max(1) - 1,
|
||||
character: token.position.0.max(1) - 1,
|
||||
},
|
||||
end: lsp_types::Position {
|
||||
line: token.position.1.max(1) - 1,
|
||||
character: token.position.0.max(1) - 1 + token.token.len() as u32,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
impl Backend {
|
||||
fn state_map(&self) -> StateMap {
|
||||
let mut state_map = HashMap::new();
|
||||
for path_state in self.analysis.iter() {
|
||||
let (path, state) = path_state.pair();
|
||||
if let Some(module_id) = self.path_to_module.get(path) {
|
||||
state_map.insert(*module_id, state.state.clone());
|
||||
}
|
||||
}
|
||||
state_map
|
||||
}
|
||||
|
||||
async fn recompile(&self, params: TextDocumentItem) {
|
||||
let file_path = PathBuf::from(params.uri.clone().path());
|
||||
|
||||
let mut map: ErrorModules = Default::default();
|
||||
for url_module in self.path_to_module.iter() {
|
||||
let (url, module) = url_module.pair();
|
||||
map.add_module(
|
||||
url.file_name().unwrap().to_str().unwrap().to_owned(),
|
||||
Some(url.clone()),
|
||||
Some(*module),
|
||||
);
|
||||
}
|
||||
|
||||
let module_id = if let Some(module_id) = self.path_to_module.get(&file_path) {
|
||||
*module_id
|
||||
} else {
|
||||
let mut lock = self.module_id_counter.lock().await;
|
||||
let module_id = lock.increment();
|
||||
drop(lock);
|
||||
self.path_to_module.insert(file_path.clone(), module_id);
|
||||
self.module_to_path.insert(module_id, file_path.clone());
|
||||
module_id
|
||||
};
|
||||
|
||||
let parse_res = parse(¶ms.text, file_path.clone(), &mut map, module_id);
|
||||
let (tokens, result) = match parse_res {
|
||||
Ok((module_id, tokens)) => (
|
||||
tokens.clone(),
|
||||
analyze(module_id, tokens, file_path.clone(), &mut map, &self.state_map()),
|
||||
),
|
||||
Err(e) => (Vec::new(), Err(e)),
|
||||
};
|
||||
|
||||
let mut diagnostics = Vec::new();
|
||||
match result {
|
||||
Ok(Some(mut analysis)) => {
|
||||
if let Some(reid_error) = &mut analysis.error {
|
||||
self.client
|
||||
.log_message(
|
||||
MessageType::INFO,
|
||||
format!("Successfully compiled despite parsing errors!"),
|
||||
)
|
||||
.await;
|
||||
reid_error.errors.dedup();
|
||||
for error in &reid_error.errors {
|
||||
diagnostics.push(reid_error_into_diagnostic(error, &tokens));
|
||||
self.client.log_message(MessageType::INFO, format!("{}", error)).await;
|
||||
}
|
||||
}
|
||||
self.analysis.insert(file_path, analysis);
|
||||
}
|
||||
Ok(_) => {}
|
||||
Err(mut reid_error) => {
|
||||
reid_error.errors.dedup();
|
||||
for error in &reid_error.errors {
|
||||
diagnostics.push(reid_error_into_diagnostic(error, &tokens));
|
||||
self.client.log_message(MessageType::INFO, format!("{}", error)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.client
|
||||
.publish_diagnostics(params.uri.clone(), diagnostics, Some(params.version))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
fn reid_error_into_diagnostic(error: &error_raporting::ErrorKind, tokens: &Vec<FullToken>) -> Diagnostic {
|
||||
let meta = error.get_meta();
|
||||
let positions = meta
|
||||
.range
|
||||
.into_position(&tokens)
|
||||
.unwrap_or((Position(0, 0), Position(0, 0)));
|
||||
|
||||
Diagnostic {
|
||||
range: Range {
|
||||
start: lsp_types::Position {
|
||||
line: ((positions.0.1 as i32) - 1).max(0) as u32,
|
||||
character: ((positions.0.0 as i32) - 1).max(0) as u32,
|
||||
},
|
||||
end: lsp_types::Position {
|
||||
line: ((positions.1.1 as i32) - 1).max(0) as u32,
|
||||
character: ((positions.1.0 as i32) - 1).max(0) as u32,
|
||||
},
|
||||
},
|
||||
severity: Some(DiagnosticSeverity::ERROR),
|
||||
code: None,
|
||||
code_description: None,
|
||||
source: Some(error.get_type_str().to_owned()),
|
||||
message: format!("{}", error),
|
||||
related_information: None,
|
||||
tags: None,
|
||||
data: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse(
|
||||
source: &str,
|
||||
path: PathBuf,
|
||||
map: &mut ErrorModules,
|
||||
module_id: SourceModuleId,
|
||||
) -> Result<(SourceModuleId, Vec<FullToken>), ReidError> {
|
||||
let file_name = path.file_name().unwrap().to_str().unwrap().to_owned();
|
||||
|
||||
Ok(parse_module(
|
||||
source,
|
||||
file_name.clone(),
|
||||
Some(path),
|
||||
map,
|
||||
Some(module_id),
|
||||
)?)
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let stdin = tokio::io::stdin();
|
||||
let stdout = tokio::io::stdout();
|
||||
|
||||
let (service, socket) = LspService::new(|client| Backend {
|
||||
client,
|
||||
analysis: DashMap::new(),
|
||||
module_to_path: DashMap::new(),
|
||||
path_to_module: DashMap::new(),
|
||||
module_id_counter: Mutex::new(SourceModuleId(0)),
|
||||
});
|
||||
Server::new(stdin, stdout, socket).serve(service).await;
|
||||
}
|
||||
281
reid-lsp/syntaxes/grammar.json
Normal file
281
reid-lsp/syntaxes/grammar.json
Normal file
@ -0,0 +1,281 @@
|
||||
{
|
||||
"scopeName": "source.reid",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#expression"
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"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": "#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"
|
||||
},
|
||||
{
|
||||
"match": "\\{|\\}",
|
||||
"name": "punctuation.brackets.curly.reid"
|
||||
},
|
||||
{
|
||||
"match": "\\(|\\)",
|
||||
"name": "punctuation.parenthesis.reid"
|
||||
}
|
||||
]
|
||||
},
|
||||
"comment": {
|
||||
"match": "\\/\\/(.|\\/)*",
|
||||
"name": "comment.line.double-slash.reid"
|
||||
},
|
||||
"struct-definition": {
|
||||
"match": "(struct)\\s*(\\w+)",
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "keyword.struct.reid"
|
||||
},
|
||||
"2": {
|
||||
"name": "entity.name.type"
|
||||
}
|
||||
}
|
||||
},
|
||||
"struct-expression": {
|
||||
"begin": "\\b([A-Z]\\w*)\\s*\\{",
|
||||
"end": "\\}",
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "entity.name.type.struct.reid"
|
||||
}
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#expression"
|
||||
}
|
||||
]
|
||||
},
|
||||
"number-literal": {
|
||||
"patterns": [
|
||||
{
|
||||
"match": "\\b0x[0-9a-fA-F]+(\\.[0-9a-fA-F]+)?\\b",
|
||||
"name": "constant.hexadecimal"
|
||||
},
|
||||
{
|
||||
"match": "\\b0o[0-7]+(\\.[0-7]+)?\\b",
|
||||
"name": "constant.octal"
|
||||
},
|
||||
{
|
||||
"match": "\\b0b[01]+(\\.[01]+)?\\b",
|
||||
"name": "constant.binary"
|
||||
},
|
||||
{
|
||||
"match": "\\b[0-9]+(\\.[0-9]+)?\\b",
|
||||
"name": "constant.numeric"
|
||||
}
|
||||
]
|
||||
},
|
||||
"string-literal": {
|
||||
"begin": "\"",
|
||||
"end": "\"",
|
||||
"name": "string.quoted.double",
|
||||
"patterns": [
|
||||
{
|
||||
"match": "\\\\\\w",
|
||||
"name": "constant.character.escape"
|
||||
}
|
||||
]
|
||||
},
|
||||
"namespace": {
|
||||
"match": "(\\w+)(\\:\\:)",
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "entity.name.namespace.reid"
|
||||
},
|
||||
"2": {
|
||||
"name": "keyword.operator.namespace.reid"
|
||||
}
|
||||
}
|
||||
},
|
||||
"cast": {
|
||||
"match": "(as)\\s+(\\w+)",
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "keyword.cast.reid"
|
||||
},
|
||||
"2": {
|
||||
"name": "entity.name.type.reid"
|
||||
}
|
||||
}
|
||||
},
|
||||
"function-call": {
|
||||
"begin": "(\\w+)?(\\()",
|
||||
"end": "(\\))",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "entity.name.function.reid"
|
||||
},
|
||||
"2": {
|
||||
"name": "punctuation.parenthesis.reid"
|
||||
}
|
||||
},
|
||||
"endCaptures": {
|
||||
"1": {
|
||||
"name": "punctuation.parenthesis.reid"
|
||||
}
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#expression"
|
||||
}
|
||||
]
|
||||
},
|
||||
"parenthesis": {
|
||||
"begin": "\\(",
|
||||
"end": "\\)",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "keyword.operator.parenthesis.reid"
|
||||
}
|
||||
},
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "keyword.operator.parenthesis.reid"
|
||||
}
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#expression"
|
||||
}
|
||||
]
|
||||
},
|
||||
"identifier": {
|
||||
"patterns": [
|
||||
{
|
||||
"match": "\\b(?:\\w+)\\b",
|
||||
"name": "variable.language.reid"
|
||||
}
|
||||
]
|
||||
},
|
||||
"keywords": {
|
||||
"patterns": [
|
||||
{
|
||||
"match": "\\b(?:let|mut|pub|extern)\\b",
|
||||
"name": "storage.type.reid"
|
||||
},
|
||||
{
|
||||
"match": "\\bimport\\b",
|
||||
"name": "keyword.import.reid"
|
||||
},
|
||||
{
|
||||
"match": "\\bbinop\\b",
|
||||
"name": "keyword.binop.reid"
|
||||
},
|
||||
{
|
||||
"match": "\\bimpl\\b",
|
||||
"name": "keyword.impl.reid"
|
||||
},
|
||||
{
|
||||
"match": "\\b(?:if|return|for|in)\\b",
|
||||
"name": "keyword.control"
|
||||
},
|
||||
{
|
||||
"match": "\\bself\\b",
|
||||
"name": "variable.language.self.reid"
|
||||
},
|
||||
{
|
||||
"match": "\\bfn\\b",
|
||||
"name": "keyword.fn.reid"
|
||||
}
|
||||
]
|
||||
},
|
||||
"binop": {
|
||||
"match": "\\<\\=|\\>\\=|\\=\\=|\\<|\\>|\\*|\\+|\\-|\\^|\\&\\&|\\&",
|
||||
"name": "keyword.operator.math.reid"
|
||||
},
|
||||
"array": {
|
||||
"begin": "\\[",
|
||||
"end": "\\]",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "entity.name.type.array.reid"
|
||||
}
|
||||
},
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "entity.name.type.array.reid"
|
||||
}
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#expression"
|
||||
}
|
||||
]
|
||||
},
|
||||
"common-type": {
|
||||
"match": "\\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"
|
||||
}
|
||||
}
|
||||
}
|
||||
149
reid-lsp/syntaxes/grammar.yaml
Normal file
149
reid-lsp/syntaxes/grammar.yaml
Normal file
@ -0,0 +1,149 @@
|
||||
scopeName: source.reid
|
||||
patterns:
|
||||
- include: "#expression"
|
||||
repository:
|
||||
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: "#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
|
||||
- match: "\\{|\\}"
|
||||
name: punctuation.brackets.curly.reid
|
||||
- match: "\\(|\\)"
|
||||
name: punctuation.parenthesis.reid
|
||||
comment:
|
||||
match: "\\/\\/(.|\\/)*"
|
||||
name: comment.line.double-slash.reid
|
||||
struct-definition:
|
||||
match: "(struct)\\s*(\\w+)"
|
||||
captures:
|
||||
1:
|
||||
name: keyword.struct.reid
|
||||
2:
|
||||
name: entity.name.type
|
||||
struct-expression:
|
||||
begin: "\\b([A-Z]\\w*)\\s*\\{"
|
||||
end: "\\}"
|
||||
captures:
|
||||
1:
|
||||
name: entity.name.type.struct.reid
|
||||
patterns:
|
||||
- include: "#expression"
|
||||
number-literal:
|
||||
patterns:
|
||||
- match: "\\b0x[0-9a-fA-F]+(\\.[0-9a-fA-F]+)?\\b"
|
||||
name: "constant.hexadecimal"
|
||||
- match: "\\b0o[0-7]+(\\.[0-7]+)?\\b"
|
||||
name: "constant.octal"
|
||||
- match: "\\b0b[01]+(\\.[01]+)?\\b"
|
||||
name: "constant.binary"
|
||||
- match: "\\b[0-9]+(\\.[0-9]+)?\\b"
|
||||
name: "constant.numeric"
|
||||
string-literal:
|
||||
begin: '"'
|
||||
end: '"'
|
||||
name: string.quoted.double
|
||||
patterns:
|
||||
- match: "\\\\\\w"
|
||||
name: constant.character.escape
|
||||
namespace:
|
||||
match: "(\\w+)(\\:\\:)"
|
||||
captures:
|
||||
1:
|
||||
name: entity.name.namespace.reid
|
||||
2:
|
||||
name: keyword.operator.namespace.reid
|
||||
cast:
|
||||
match: "(as)\\s+(\\w+)"
|
||||
captures:
|
||||
1:
|
||||
name: keyword.cast.reid
|
||||
2:
|
||||
name: entity.name.type.reid
|
||||
function-call:
|
||||
begin: "(\\w+)?(\\()"
|
||||
end: "(\\))"
|
||||
beginCaptures:
|
||||
1:
|
||||
name: entity.name.function.reid
|
||||
2:
|
||||
name: punctuation.parenthesis.reid
|
||||
endCaptures:
|
||||
1:
|
||||
name: punctuation.parenthesis.reid
|
||||
patterns:
|
||||
- include: "#expression"
|
||||
parenthesis:
|
||||
begin: "\\("
|
||||
end: "\\)"
|
||||
beginCaptures:
|
||||
0:
|
||||
name: keyword.operator.parenthesis.reid
|
||||
endCaptures:
|
||||
0:
|
||||
name: keyword.operator.parenthesis.reid
|
||||
patterns:
|
||||
- include: "#expression"
|
||||
identifier:
|
||||
patterns:
|
||||
- match: "\\b(?:\\w+)\\b"
|
||||
name: variable.language.reid
|
||||
keywords:
|
||||
patterns:
|
||||
- match: "\\b(?:let|mut|pub|extern)\\b"
|
||||
name: storage.type.reid
|
||||
- match: "\\bimport\\b"
|
||||
name: keyword.import.reid
|
||||
- match: "\\bbinop\\b"
|
||||
name: keyword.binop.reid
|
||||
- match: "\\bimpl\\b"
|
||||
name: keyword.impl.reid
|
||||
- match: "\\b(?:if|return|for|in)\\b"
|
||||
name: keyword.control
|
||||
- match: "\\bself\\b"
|
||||
name: variable.language.self.reid
|
||||
- match: "\\bfn\\b"
|
||||
name: keyword.fn.reid
|
||||
binop:
|
||||
match: "\\<\\=|\\>\\=|\\=\\=|\\<|\\>|\\*|\\+|\\-|\\^|\\&\\&|\\&"
|
||||
name: keyword.operator.math.reid
|
||||
array:
|
||||
begin: "\\["
|
||||
end: "\\]"
|
||||
beginCaptures:
|
||||
0:
|
||||
name: entity.name.type.array.reid
|
||||
endCaptures:
|
||||
0:
|
||||
name: entity.name.type.array.reid
|
||||
patterns:
|
||||
- include: "#expression"
|
||||
common-type:
|
||||
match: "\\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
|
||||
|
||||
|
||||
29
reid-lsp/tsconfig.json
Normal file
29
reid-lsp/tsconfig.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "Node16",
|
||||
"target": "ES2022",
|
||||
"lib": [
|
||||
"ES2022"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"rootDir": "src",
|
||||
"outDir": "out",
|
||||
"strict": true /* enable all strict type-checking options */
|
||||
/* Additional Checks */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
".vscode-test"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "./client/"
|
||||
},
|
||||
]
|
||||
}
|
||||
48
reid-lsp/vsc-extension-quickstart.md
Normal file
48
reid-lsp/vsc-extension-quickstart.md
Normal file
@ -0,0 +1,48 @@
|
||||
# Welcome to your VS Code Extension
|
||||
|
||||
## What's in the folder
|
||||
|
||||
* This folder contains all of the files necessary for your extension.
|
||||
* `package.json` - this is the manifest file in which you declare your extension and command.
|
||||
* The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesn’t yet need to load the plugin.
|
||||
* `src/extension.ts` - this is the main file where you will provide the implementation of your command.
|
||||
* The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
|
||||
* We pass the function containing the implementation of the command as the second parameter to `registerCommand`.
|
||||
|
||||
## Setup
|
||||
|
||||
* install the recommended extensions (amodio.tsl-problem-matcher, ms-vscode.extension-test-runner, and dbaeumer.vscode-eslint)
|
||||
|
||||
|
||||
## Get up and running straight away
|
||||
|
||||
* Press `F5` to open a new window with your extension loaded.
|
||||
* Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`.
|
||||
* Set breakpoints in your code inside `src/extension.ts` to debug your extension.
|
||||
* Find output from your extension in the debug console.
|
||||
|
||||
## Make changes
|
||||
|
||||
* You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`.
|
||||
* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
|
||||
|
||||
|
||||
## Explore the API
|
||||
|
||||
* You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`.
|
||||
|
||||
## Run tests
|
||||
|
||||
* Install the [Extension Test Runner](https://marketplace.visualstudio.com/items?itemName=ms-vscode.extension-test-runner)
|
||||
* Run the "watch" task via the **Tasks: Run Task** command. Make sure this is running, or tests might not be discovered.
|
||||
* Open the Testing view from the activity bar and click the Run Test" button, or use the hotkey `Ctrl/Cmd + ; A`
|
||||
* See the output of the test result in the Test Results view.
|
||||
* Make changes to `src/test/extension.test.ts` or create new test files inside the `test` folder.
|
||||
* The provided test runner will only consider files matching the name pattern `**.test.ts`.
|
||||
* You can create folders inside the `test` folder to structure your tests any way you want.
|
||||
|
||||
## Go further
|
||||
|
||||
* Reduce the extension size and improve the startup time by [bundling your extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension).
|
||||
* [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VS Code extension marketplace.
|
||||
* Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration).
|
||||
48
reid-lsp/webpack.config.js
Normal file
48
reid-lsp/webpack.config.js
Normal file
@ -0,0 +1,48 @@
|
||||
//@ts-check
|
||||
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
|
||||
//@ts-check
|
||||
/** @typedef {import('webpack').Configuration} WebpackConfig **/
|
||||
|
||||
/** @type WebpackConfig */
|
||||
const extensionConfig = {
|
||||
target: 'node', // VS Code extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/
|
||||
mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production')
|
||||
|
||||
entry: './client/src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/
|
||||
output: {
|
||||
// the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'extension.js',
|
||||
libraryTarget: 'commonjs2'
|
||||
},
|
||||
externals: {
|
||||
vscode: 'commonjs vscode' // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/
|
||||
// modules added here also need to be added in the .vscodeignore file
|
||||
},
|
||||
resolve: {
|
||||
// support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader
|
||||
extensions: ['.ts', '.js']
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts$/,
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
{
|
||||
loader: 'ts-loader'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
devtool: 'nosources-source-map',
|
||||
infrastructureLogging: {
|
||||
level: "log", // enables logging required for problem matchers
|
||||
},
|
||||
};
|
||||
module.exports = [extensionConfig];
|
||||
@ -1,19 +1,23 @@
|
||||
[package]
|
||||
name = "reid"
|
||||
version = "0.1.0"
|
||||
version = "1.0.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
[features]
|
||||
default = ["color"]
|
||||
default = ["color", "cli"]
|
||||
|
||||
color = ["colored"]
|
||||
cli = ["argh", "stderrlog"]
|
||||
|
||||
[dependencies]
|
||||
## Make it easier to generate errors
|
||||
thiserror = "1.0.44"
|
||||
reid-lib = { path = "../reid-llvm-lib", version = "0.1.0" }
|
||||
reid-lib = { path = "../reid-llvm-lib", version = "1.0.0", 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 }
|
||||
@ -12,7 +12,6 @@ fn main() -> Result<(), std::io::Error> {
|
||||
libraries.push(libname);
|
||||
}
|
||||
|
||||
dbg!(&filename);
|
||||
let path = PathBuf::from(filename).canonicalize().unwrap();
|
||||
let parent = path.with_extension("");
|
||||
let llvm_ir_path = parent.with_extension("ll");
|
||||
@ -31,27 +30,27 @@ fn main() -> Result<(), std::io::Error> {
|
||||
match compile_simple(&text, PathBuf::from(&path), Some(cpu), vec![features]) {
|
||||
Ok((
|
||||
CompileOutput {
|
||||
triple,
|
||||
triple: _triple,
|
||||
assembly,
|
||||
obj_buffer,
|
||||
llvm_ir,
|
||||
llvm_ir: _llvm_ir,
|
||||
},
|
||||
CustomIRs { llir, mir },
|
||||
)) => {
|
||||
println!("{}", llvm_ir);
|
||||
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);
|
||||
|
||||
let after = std::time::SystemTime::now();
|
||||
println!("Compiled with triple: {}\n", &triple);
|
||||
fs::write(&llvm_ir_path, &llvm_ir).expect("Could not write LLVM IR -file!");
|
||||
println!("Output LLVM IR to {:?}", llvm_ir_path);
|
||||
fs::write(&llvm_ir_path, &_llvm_ir).expect("Could not write LLVM IR -file!");
|
||||
fs::write(&asm_path, &assembly).expect("Could not write Assembly-file!");
|
||||
println!("Output Assembly to {:?}", asm_path);
|
||||
fs::write(&object_path, &obj_buffer).expect("Could not write Object-file!");
|
||||
println!("Output Object-file to {:?}\n", object_path);
|
||||
fs::write(&llir_path, &llir).expect("Could not write LLIR-file!");
|
||||
println!("Output LLIR-file to {:?}\n", llir_path);
|
||||
fs::write(&mir_path, &mir).expect("Could not write MIR-file!");
|
||||
println!("Output MIR-file to {:?}\n", mir_path);
|
||||
let after = std::time::SystemTime::now();
|
||||
println!(
|
||||
"Compilation took: {:.2}ms\n",
|
||||
(after.duration_since(before).unwrap().as_micros() as f32) / 1000.
|
||||
@ -60,7 +59,7 @@ fn main() -> Result<(), std::io::Error> {
|
||||
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);
|
||||
}
|
||||
@ -69,7 +68,7 @@ fn main() -> Result<(), std::io::Error> {
|
||||
Err(e) => panic!("{}", e),
|
||||
};
|
||||
} else {
|
||||
println!("Please input compiled file path!")
|
||||
log::error!("Please input compiled file path!")
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
|
||||
extern fn puts(message: *char) -> i32;
|
||||
extern fn malloc(size: u64) -> *u8;
|
||||
extern fn free(ptr: *u8);
|
||||
extern fn div(numerator: i32, denominator: i32) -> div_t;
|
||||
|
||||
/// Editable string value that can be printed and extended
|
||||
struct String {
|
||||
inner: *char,
|
||||
length: u64,
|
||||
@ -12,15 +12,18 @@ struct String {
|
||||
}
|
||||
|
||||
impl String {
|
||||
/// Returns a new empty `String`-object, which must later be manually freed.
|
||||
pub fn new() -> String {
|
||||
String {
|
||||
inner: allocate(0),
|
||||
inner: char::malloc(0),
|
||||
length: 0,
|
||||
max_length: 0,
|
||||
must_be_freed: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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();
|
||||
@ -34,15 +37,17 @@ 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 = allocate((*self).max_length + 4) as *char;
|
||||
let new = char::malloc((*self).max_length + 4);
|
||||
copy_bits((*self).inner, new, (*self).max_length);
|
||||
|
||||
if (*self).must_be_freed == true {
|
||||
@ -58,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)
|
||||
@ -76,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);
|
||||
}
|
||||
@ -112,27 +122,28 @@ 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);
|
||||
}
|
||||
|
||||
pub fn allocate(size: u64) -> *u8 {
|
||||
malloc(size)
|
||||
}
|
||||
|
||||
/// (deprecated) creates a new editable string
|
||||
pub fn new_string() -> String {
|
||||
String {
|
||||
inner: allocate(0),
|
||||
inner: char::malloc(0),
|
||||
length: 0,
|
||||
max_length: 0,
|
||||
must_be_freed: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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();
|
||||
@ -146,9 +157,10 @@ 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 = allocate((*string).max_length + 4) as *char;
|
||||
let new = char::malloc((*string).max_length + 4);
|
||||
copy_bits((*string).inner, new, (*string).max_length);
|
||||
|
||||
if (*string).must_be_freed == true {
|
||||
@ -164,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);
|
||||
}
|
||||
@ -188,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)
|
||||
@ -206,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;
|
||||
@ -222,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);
|
||||
|
||||
@ -7,7 +7,7 @@ static HEXADECIMAL_NUMERICS: &[char] = &[
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
];
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, PartialOrd, Ord)]
|
||||
#[derive(Eq, PartialEq, Clone, PartialOrd, Ord, Hash)]
|
||||
pub enum Token {
|
||||
/// Values
|
||||
Identifier(String),
|
||||
@ -88,6 +88,10 @@ pub enum Token {
|
||||
LessThan,
|
||||
/// `&`
|
||||
Et,
|
||||
/// `|`
|
||||
Pipe,
|
||||
/// `^`
|
||||
Hat,
|
||||
/// `!`
|
||||
Exclamation,
|
||||
|
||||
@ -110,6 +114,9 @@ pub enum Token {
|
||||
|
||||
Unknown(char),
|
||||
|
||||
Whitespace(String),
|
||||
Comment(String),
|
||||
Doc(String),
|
||||
Eof,
|
||||
}
|
||||
|
||||
@ -174,6 +181,8 @@ impl ToString for Token {
|
||||
Token::GreaterThan => String::from('>'),
|
||||
Token::LessThan => String::from('<'),
|
||||
Token::Et => String::from('&'),
|
||||
Token::Pipe => String::from('|'),
|
||||
Token::Hat => String::from('^'),
|
||||
Token::Exclamation => String::from('!'),
|
||||
Token::ParenOpen => String::from('('),
|
||||
Token::ParenClose => String::from(')'),
|
||||
@ -186,6 +195,9 @@ impl ToString for Token {
|
||||
Token::Eof => String::new(),
|
||||
Token::Slash => String::from('/'),
|
||||
Token::Percent => String::from('%'),
|
||||
Token::Whitespace(val) => val.clone(),
|
||||
Token::Comment(val) => format!("//{}", val.clone()),
|
||||
Token::Doc(val) => format!("///{}", val.clone()),
|
||||
Token::Unknown(val) => val.to_string(),
|
||||
}
|
||||
}
|
||||
@ -201,7 +213,7 @@ impl std::fmt::Debug for Token {
|
||||
}
|
||||
|
||||
/// A token with a position
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct FullToken {
|
||||
pub token: Token,
|
||||
pub position: Position,
|
||||
@ -287,13 +299,37 @@ pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error
|
||||
|
||||
let variant = match character {
|
||||
// Whitespace
|
||||
w if w.is_whitespace() => continue,
|
||||
w if w.is_whitespace() => {
|
||||
let mut whitespace = String::from(*w);
|
||||
while let Some(w) = cursor.first() {
|
||||
if !w.is_whitespace() {
|
||||
break;
|
||||
}
|
||||
whitespace.push(cursor.next().unwrap());
|
||||
}
|
||||
Token::Whitespace(whitespace)
|
||||
}
|
||||
// Comments
|
||||
'/' if cursor.first() == Some('/') => {
|
||||
while !matches!(cursor.first(), Some('\n') | None) {
|
||||
cursor.next();
|
||||
let doc = if cursor.first() == Some('/') {
|
||||
cursor.next();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let mut comment = String::new();
|
||||
while !matches!(cursor.first(), Some('\n') | None) {
|
||||
if let Some(c) = cursor.next() {
|
||||
comment.push(c);
|
||||
}
|
||||
}
|
||||
if doc {
|
||||
Token::Doc(comment)
|
||||
} else {
|
||||
Token::Comment(comment)
|
||||
}
|
||||
continue;
|
||||
}
|
||||
'\"' | '\'' => {
|
||||
let mut value = String::new();
|
||||
@ -411,6 +447,8 @@ pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error
|
||||
'>' => Token::GreaterThan,
|
||||
'<' => Token::LessThan,
|
||||
'&' => Token::Et,
|
||||
'|' => Token::Pipe,
|
||||
'^' => Token::Hat,
|
||||
'!' => Token::Exclamation,
|
||||
'(' => Token::ParenOpen,
|
||||
')' => Token::ParenClose,
|
||||
|
||||
@ -88,7 +88,7 @@ pub enum ExpressionKind {
|
||||
/// Array-indexed, e.g. <expr>[<expr>]
|
||||
Indexed(Box<Expression>, Box<Expression>),
|
||||
/// Struct-accessed, e.g. <expr>.<expr>
|
||||
Accessed(Box<Expression>, String),
|
||||
Accessed(Box<Expression>, String, TokenRange),
|
||||
/// Associated function call, but with a shorthand
|
||||
AccessCall(Box<Expression>, Box<FunctionCallExpression>),
|
||||
Binop(BinaryOperator, Box<Expression>, Box<Expression>),
|
||||
@ -116,7 +116,14 @@ pub enum BinaryOperator {
|
||||
Div,
|
||||
Mod,
|
||||
|
||||
BitshiftRight,
|
||||
BitshiftLeft,
|
||||
|
||||
And,
|
||||
Or,
|
||||
Xor,
|
||||
BitAnd,
|
||||
BitOr,
|
||||
LT,
|
||||
LE,
|
||||
GT,
|
||||
@ -126,7 +133,7 @@ pub enum BinaryOperator {
|
||||
}
|
||||
|
||||
impl BinaryOperator {
|
||||
pub fn get_precedence(&self) -> i8 {
|
||||
pub fn get_precedence(&self) -> u8 {
|
||||
use BinaryOperator::*;
|
||||
match &self {
|
||||
Minus => 5,
|
||||
@ -134,19 +141,30 @@ impl BinaryOperator {
|
||||
Mult => 15,
|
||||
Div => 20,
|
||||
Mod => 20,
|
||||
And => 100,
|
||||
LT => 100,
|
||||
LE => 100,
|
||||
GT => 100,
|
||||
GE => 100,
|
||||
EQ => 100,
|
||||
NE => 100,
|
||||
BitAnd => 90,
|
||||
BitOr => 90,
|
||||
BitshiftLeft => 100,
|
||||
BitshiftRight => 100,
|
||||
And => 150,
|
||||
Or => 150,
|
||||
Xor => 150,
|
||||
LT => 150,
|
||||
LE => 150,
|
||||
GT => 150,
|
||||
GE => 150,
|
||||
EQ => 150,
|
||||
NE => 150,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FunctionCallExpression(pub String, pub Vec<Expression>, pub TokenRange);
|
||||
pub struct FunctionCallExpression {
|
||||
pub name: String,
|
||||
pub params: Vec<Expression>,
|
||||
pub range: TokenRange,
|
||||
pub is_macro: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IfExpression(
|
||||
@ -166,7 +184,7 @@ pub struct LetStatement {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ImportStatement(pub Vec<String>, pub TokenRange);
|
||||
pub struct ImportStatement(pub Vec<(String, TokenRange)>, pub TokenRange);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FunctionDefinition(pub FunctionSignature, pub bool, pub Block, pub TokenRange);
|
||||
@ -174,8 +192,9 @@ 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)>,
|
||||
pub params: Vec<(String, Type, TokenRange)>,
|
||||
pub return_type: Option<Type>,
|
||||
#[allow(dead_code)]
|
||||
pub range: TokenRange,
|
||||
@ -183,13 +202,13 @@ pub struct FunctionSignature {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SelfKind {
|
||||
Owned(TypeKind),
|
||||
Borrow(TypeKind),
|
||||
MutBorrow(TypeKind),
|
||||
Owned(Type),
|
||||
Borrow(Type),
|
||||
MutBorrow(Type),
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum ReturnType {
|
||||
Soft,
|
||||
Hard,
|
||||
@ -198,7 +217,7 @@ pub enum ReturnType {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StructExpression {
|
||||
name: String,
|
||||
fields: Vec<(String, Expression)>,
|
||||
fields: Vec<(String, Expression, TokenRange)>,
|
||||
range: TokenRange,
|
||||
}
|
||||
|
||||
@ -254,9 +273,9 @@ pub enum TopLevelStatement {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BinopDefinition {
|
||||
pub lhs: (String, Type),
|
||||
pub lhs: (String, Type, TokenRange),
|
||||
pub op: BinaryOperator,
|
||||
pub rhs: (String, Type),
|
||||
pub rhs: (String, Type, TokenRange),
|
||||
pub return_ty: Type,
|
||||
pub block: Block,
|
||||
pub signature_range: TokenRange,
|
||||
|
||||
@ -175,7 +175,50 @@ impl Parse for AssociatedFunctionCall {
|
||||
let ty = stream.parse()?;
|
||||
stream.expect(Token::Colon)?;
|
||||
stream.expect(Token::Colon)?;
|
||||
Ok(AssociatedFunctionCall(ty, stream.parse()?))
|
||||
|
||||
if stream.next_is_whitespace() {
|
||||
stream.expecting_err_nonfatal("associated function name");
|
||||
return Ok(AssociatedFunctionCall(
|
||||
ty,
|
||||
FunctionCallExpression {
|
||||
name: String::new(),
|
||||
params: Vec::new(),
|
||||
range: stream.get_range_prev_curr().unwrap(),
|
||||
is_macro: false,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
match stream.parse() {
|
||||
Ok(fn_call) => Ok(AssociatedFunctionCall(ty, fn_call)),
|
||||
_ => {
|
||||
if let Some(Token::Identifier(fn_name)) = stream.peek() {
|
||||
stream.next();
|
||||
stream.expected_err_nonfatal("associated function call");
|
||||
|
||||
Ok(AssociatedFunctionCall(
|
||||
ty,
|
||||
FunctionCallExpression {
|
||||
name: fn_name,
|
||||
params: Vec::new(),
|
||||
range: stream.get_range_prev_curr().unwrap(),
|
||||
is_macro: false,
|
||||
},
|
||||
))
|
||||
} else {
|
||||
stream.expected_err_nonfatal("associated function name");
|
||||
Ok(AssociatedFunctionCall(
|
||||
ty,
|
||||
FunctionCallExpression {
|
||||
name: String::new(),
|
||||
params: Vec::new(),
|
||||
range: stream.get_range_prev_curr().unwrap(),
|
||||
is_macro: false,
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,10 +234,11 @@ where
|
||||
),
|
||||
expr.0 .1,
|
||||
),
|
||||
ExpressionKind::Accessed(value_expr, index_name) => Expression(
|
||||
ExpressionKind::Accessed(value_expr, index_name, range) => Expression(
|
||||
ExpressionKind::Accessed(
|
||||
Box::new(apply_inner(PrimaryExpression(*value_expr.clone()), fun)),
|
||||
index_name.clone(),
|
||||
*range,
|
||||
),
|
||||
expr.0 .1,
|
||||
),
|
||||
@ -205,6 +249,13 @@ where
|
||||
),
|
||||
expr.0 .1,
|
||||
),
|
||||
ExpressionKind::CastTo(value_expr, ty) => Expression(
|
||||
ExpressionKind::CastTo(
|
||||
Box::new(apply_inner(PrimaryExpression(*value_expr.clone()), fun)),
|
||||
ty.clone(),
|
||||
),
|
||||
expr.0 .1,
|
||||
),
|
||||
_ => fun(expr),
|
||||
}
|
||||
}
|
||||
@ -236,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(
|
||||
@ -392,9 +443,9 @@ impl Parse for PrimaryExpression {
|
||||
);
|
||||
}
|
||||
ValueIndex::Dot(val) => match val {
|
||||
DotIndexKind::StructValueIndex(name) => {
|
||||
DotIndexKind::StructValueIndex(name, range) => {
|
||||
expr = Expression(
|
||||
ExpressionKind::Accessed(Box::new(expr), name),
|
||||
ExpressionKind::Accessed(Box::new(expr), name, range),
|
||||
stream.get_range().unwrap(),
|
||||
);
|
||||
}
|
||||
@ -459,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);
|
||||
}
|
||||
@ -481,6 +532,10 @@ impl Parse for BinaryOperator {
|
||||
stream.next();
|
||||
BinaryOperator::And
|
||||
}
|
||||
(Some(Token::Pipe), Some(Token::Pipe)) => {
|
||||
stream.next();
|
||||
BinaryOperator::Or
|
||||
}
|
||||
(Some(Token::LessThan), Some(Token::Equals)) => {
|
||||
stream.next();
|
||||
BinaryOperator::LE
|
||||
@ -497,6 +552,18 @@ impl Parse for BinaryOperator {
|
||||
stream.next();
|
||||
BinaryOperator::NE
|
||||
}
|
||||
(Some(Token::GreaterThan), Some(Token::GreaterThan)) => {
|
||||
stream.next();
|
||||
BinaryOperator::BitshiftRight
|
||||
}
|
||||
(Some(Token::LessThan), Some(Token::LessThan)) => {
|
||||
stream.next();
|
||||
BinaryOperator::BitshiftLeft
|
||||
}
|
||||
|
||||
(Some(Token::Hat), _) => BinaryOperator::Xor,
|
||||
(Some(Token::Et), _) => BinaryOperator::BitAnd,
|
||||
(Some(Token::Pipe), _) => BinaryOperator::BitOr,
|
||||
|
||||
(Some(Token::LessThan), _) => BinaryOperator::LT,
|
||||
(Some(Token::GreaterThan), _) => BinaryOperator::GT,
|
||||
@ -514,8 +581,20 @@ impl Parse for BinaryOperator {
|
||||
impl Parse for FunctionCallExpression {
|
||||
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
|
||||
if let Some(Token::Identifier(name)) = stream.next() {
|
||||
let is_macro = if let Some(Token::Exclamation) = stream.peek() {
|
||||
stream.next(); // Consume !
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let args = stream.parse::<FunctionArgs>()?;
|
||||
Ok(FunctionCallExpression(name, args.0, stream.get_range().unwrap()))
|
||||
Ok(FunctionCallExpression {
|
||||
name,
|
||||
params: args.0,
|
||||
range: stream.get_range().unwrap(),
|
||||
is_macro,
|
||||
})
|
||||
} else {
|
||||
Err(stream.expected_err("identifier")?)
|
||||
}
|
||||
@ -574,7 +653,7 @@ impl Parse for LetStatement {
|
||||
stream.expect(Token::Equals)?;
|
||||
|
||||
let expression = stream.parse()?;
|
||||
stream.expect(Token::Semi)?;
|
||||
stream.expect_nonfatal(Token::Semi).ok();
|
||||
Ok(LetStatement {
|
||||
name: variable,
|
||||
ty,
|
||||
@ -595,19 +674,21 @@ impl Parse for ImportStatement {
|
||||
let mut import_list = Vec::new();
|
||||
|
||||
if let Some(Token::Identifier(name)) = stream.next() {
|
||||
import_list.push(name);
|
||||
import_list.push((name, stream.get_range_prev_curr().unwrap()));
|
||||
while stream.expect(Token::Colon).is_ok() && stream.expect(Token::Colon).is_ok() {
|
||||
if let Some(Token::Identifier(name)) = stream.next() {
|
||||
import_list.push(name);
|
||||
if let Some(Token::Identifier(name)) = stream.peek() {
|
||||
stream.next(); // Consume identifier
|
||||
import_list.push((name, stream.get_range_prev_curr().unwrap()));
|
||||
} else {
|
||||
Err(stream.expected_err("identifier")?)?
|
||||
stream.expected_err_nonfatal("identifier");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(stream.expected_err("identifier")?)?
|
||||
}
|
||||
|
||||
stream.expect(Token::Semi)?;
|
||||
stream.expect_nonfatal(Token::Semi).ok();
|
||||
|
||||
Ok(ImportStatement(import_list, stream.get_range().unwrap()))
|
||||
}
|
||||
@ -615,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
|
||||
@ -623,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(),
|
||||
@ -633,7 +718,7 @@ impl Parse for FunctionDefinition {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FunctionParam(String, Type);
|
||||
struct FunctionParam(String, Type, TokenRange);
|
||||
|
||||
impl Parse for FunctionParam {
|
||||
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
|
||||
@ -641,7 +726,7 @@ impl Parse for FunctionParam {
|
||||
return Err(stream.expected_err("parameter name")?);
|
||||
};
|
||||
stream.expect(Token::Colon)?;
|
||||
Ok(FunctionParam(arg_name, stream.parse()?))
|
||||
Ok(FunctionParam(arg_name, stream.parse()?, stream.get_range().unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -673,9 +758,18 @@ impl Parse for SelfParam {
|
||||
};
|
||||
if name == "self" {
|
||||
match kind {
|
||||
SelfParamKind::BorrowMut => Ok(SelfParam(SelfKind::MutBorrow(TypeKind::Unknown))),
|
||||
SelfParamKind::Borrow => Ok(SelfParam(SelfKind::Borrow(TypeKind::Unknown))),
|
||||
SelfParamKind::Owned => Ok(SelfParam(SelfKind::Owned(TypeKind::Unknown))),
|
||||
SelfParamKind::BorrowMut => Ok(SelfParam(SelfKind::MutBorrow(Type(
|
||||
TypeKind::Unknown,
|
||||
stream.get_range_prev().unwrap(),
|
||||
)))),
|
||||
SelfParamKind::Borrow => Ok(SelfParam(SelfKind::Borrow(Type(
|
||||
TypeKind::Unknown,
|
||||
stream.get_range_prev().unwrap(),
|
||||
)))),
|
||||
SelfParamKind::Owned => Ok(SelfParam(SelfKind::Owned(Type(
|
||||
TypeKind::Unknown,
|
||||
stream.get_range_prev().unwrap(),
|
||||
)))),
|
||||
}
|
||||
} else {
|
||||
Err(stream.expected_err("self parameter")?)
|
||||
@ -694,11 +788,11 @@ impl Parse for FunctionSignature {
|
||||
match &self_kind {
|
||||
SelfKind::None => {
|
||||
if let Ok(param) = stream.parse::<FunctionParam>() {
|
||||
params.push((param.0, param.1));
|
||||
params.push((param.0, param.1, param.2));
|
||||
while let Some(Token::Comma) = stream.peek() {
|
||||
stream.next();
|
||||
let param = stream.parse::<FunctionParam>()?;
|
||||
params.push((param.0, param.1));
|
||||
params.push((param.0, param.1, param.2));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -706,7 +800,7 @@ impl Parse for FunctionSignature {
|
||||
while let Some(Token::Comma) = stream.peek() {
|
||||
stream.next();
|
||||
let param = stream.parse::<FunctionParam>()?;
|
||||
params.push((param.0, param.1));
|
||||
params.push((param.0, param.1, param.2));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -720,6 +814,7 @@ impl Parse for FunctionSignature {
|
||||
|
||||
Ok(FunctionSignature {
|
||||
name,
|
||||
documentation: None,
|
||||
params,
|
||||
self_kind,
|
||||
return_type,
|
||||
@ -743,7 +838,7 @@ impl Parse for Block {
|
||||
// if semicolon is missing.
|
||||
if !matches!(e, Expression(ExpressionKind::IfExpr(_), _)) {
|
||||
// In theory could ignore the missing semicolon..
|
||||
return Err(stream.expected_err("semicolon to complete statement")?);
|
||||
stream.expected_err_nonfatal("semicolon to complete statement");
|
||||
}
|
||||
|
||||
statements.push(BlockLevelStatement::Expression(e));
|
||||
@ -774,9 +869,10 @@ impl Parse for StructExpression {
|
||||
let Some(Token::Identifier(name)) = stream.next() else {
|
||||
return Err(stream.expected_err("struct identifier")?);
|
||||
};
|
||||
|
||||
stream.expect(Token::BraceOpen)?;
|
||||
let named_list = stream.parse::<NamedFieldList<Expression>>()?;
|
||||
let fields = named_list.0.into_iter().map(|f| (f.0, f.1)).collect();
|
||||
let fields = named_list.0.into_iter().map(|f| (f.0, f.1, f.2)).collect();
|
||||
|
||||
stream.expect(Token::BraceClose)?;
|
||||
|
||||
@ -853,25 +949,35 @@ impl Parse for ArrayValueIndex {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum DotIndexKind {
|
||||
StructValueIndex(String),
|
||||
StructValueIndex(String, TokenRange),
|
||||
FunctionCall(FunctionCallExpression),
|
||||
}
|
||||
|
||||
impl Parse for DotIndexKind {
|
||||
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
|
||||
stream.expect(Token::Dot)?;
|
||||
if let Some(Token::Identifier(name)) = stream.next() {
|
||||
if let Some(Token::Identifier(name)) = stream.peek() {
|
||||
stream.next(); // Consume identifer
|
||||
if let Ok(args) = stream.parse::<FunctionArgs>() {
|
||||
Ok(Self::FunctionCall(FunctionCallExpression(
|
||||
Ok(Self::FunctionCall(FunctionCallExpression {
|
||||
name,
|
||||
args.0,
|
||||
stream.get_range_prev().unwrap(),
|
||||
)))
|
||||
params: args.0,
|
||||
range: stream.get_range_prev().unwrap(),
|
||||
is_macro: false,
|
||||
}))
|
||||
} else {
|
||||
Ok(Self::StructValueIndex(name))
|
||||
Ok(Self::StructValueIndex(name, stream.get_range_prev().unwrap()))
|
||||
}
|
||||
} else {
|
||||
return Err(stream.expected_err("struct index (number)")?);
|
||||
if stream.next_is_whitespace() {
|
||||
stream.expecting_err_nonfatal("struct index");
|
||||
Ok(Self::StructValueIndex(
|
||||
String::new(),
|
||||
stream.get_range_prev_curr().unwrap(),
|
||||
))
|
||||
} else {
|
||||
Err(stream.expecting_err("struct index")?)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -885,7 +991,7 @@ impl Parse for BlockLevelStatement {
|
||||
Some(Token::ReturnKeyword) => {
|
||||
stream.next();
|
||||
let exp = stream.parse().ok();
|
||||
stream.expect(Token::Semi)?;
|
||||
stream.expect_nonfatal(Token::Semi).ok();
|
||||
Stmt::Return(ReturnType::Hard, exp)
|
||||
}
|
||||
Some(Token::For) => {
|
||||
@ -950,7 +1056,7 @@ impl Parse for SetStatement {
|
||||
let var_ref = stream.parse()?;
|
||||
stream.expect(Token::Equals)?;
|
||||
let expr = stream.parse()?;
|
||||
stream.expect(Token::Semi)?;
|
||||
stream.expect_nonfatal(Token::Semi).ok();
|
||||
Ok(SetStatement(var_ref, expr, stream.get_range().unwrap()))
|
||||
}
|
||||
}
|
||||
@ -988,10 +1094,13 @@ 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()?);
|
||||
stream.expect(Token::Semi)?;
|
||||
let mut signature: FunctionSignature = stream.parse()?;
|
||||
signature.documentation = documentation;
|
||||
let extern_fn = Stmt::ExternFunction(signature);
|
||||
stream.expect_nonfatal(Token::Semi).ok();
|
||||
extern_fn
|
||||
}
|
||||
Some(Token::FnKeyword) | Some(Token::PubKeyword) => Stmt::FunctionDefinition(stream.parse()?),
|
||||