Compare commits

..

1 Commits

Author SHA1 Message Date
7b11951209 Get borrow to work, somewhat 2025-07-21 00:53:30 +03:00
124 changed files with 6713 additions and 15998 deletions

11
.gitignore vendored
View File

@ -5,10 +5,7 @@ src/old_llvm
.env
hello.*
main
*.o
*.ll
*.asm
*.out
*.llir
*.mir
foo.reid
reid_src/*.o
reid_src/*.ll
reid_src/*.asm
reid_src/*.out

3
.gitmodules vendored
View File

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

View File

@ -1 +0,0 @@
max_width = 120

1267
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -3,7 +3,7 @@ BIN=$(SRC:.reid=.out)
REID=cargo run --example cli
LD=ld
LDFLAGS=-lSDL3
LDFLAGS=
all: $(BIN)
clean:

View File

@ -9,29 +9,14 @@ contains the relevant abstraction to produce a more Rust'y API from that.
Much of the syntax in Reid is directly inspired by rust, but mostly it is driven
by simplicity.
Specifications and a bunch of [documentation for the language can be found
here](./documentation/).
An example of a real whole program (a CPU pathtracer) can be found [in
examples/cpu_raytracer.reid](./examples/cpu_raytracer.reid), go have a look!
Reid is currently able to (non-exhaustively):
- Do basic algebra binary and unary-operations (e.g. Add, Sub, Div, Mult, And,
Not)
- Do basic algebra (e.g. Add, Sub, Mult)
- Resolve complex one-liners correctly using PEDMAS (e.g. `5 + 2 * 5 - 5 *
5` is calculated correctly)
- Handle borrows/derefs, pointers.
- Declare and call functions with varying parameters and return types
- Perform type-checking and type-inference such that return-types and
parameter types must always match.
- Do simple logic-operations (e.g. If/And/Or)
- Handle, access, define and initialize structs and arrays.
- Define and execute For/While loops
- Output detailed debug information
- Define extern functions that can be linked to outside modules such as `libc`.
- Define custom binary operations for any two types that hasn't been defined
previously (such as `u16 + u32`).
An example program of Reid, that calculates the 5th fibonacci number (and uses
Rust for highlighting) is:
@ -47,46 +32,18 @@ fn fibonacci(n: u16) -> u16 {
}
```
Currently missing big features (TODOs) are:
Currently missing relevant features (TODOs) are:
- ~~Arrays~~ (DONE)
- ~~Structs~~ (DONE)
- ~~Extern functions~~ (DONE)
- ~~Strings~~ (DONE)
- ~~Borrows~~ (DONE)
- ~~Pointers~~ (DONE)
- ~~Unary operators~~
- ~~Floats~~ (DONE)
- ~~Type casting~~ (DONE)
- ~~Built-in Int/Float division and modulo~~ (DONE)
- ~~Loops~~ (DONE)
- ~~Intrinsic functions~~ (DONE)
- ~~Ability to specify types in literals and variable definitions~~ (DONE)
- ~~Debug Information~~ (DONE)
- ~~Fix struct initialization (wrong order and missing fields allowed now)~~ (DONE)
- ~~Not-Unary~~ (DONE)
- ~~Importing types from other modules~~ (DONE)
- ~~Importable binops?~~ (DONE)
- ~~Associated functions (for e.g. sizeof)~~ (DONE)
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~~ (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)
- Loops
- Debug Information (PARTIALLY DONE)
- Borrows+Pointers
Smaller features:
- ~~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)
- Easier way to initialize arrays with a single value
- Lexical scopes for Debug Information
### Why "Reid"
@ -106,12 +63,12 @@ and not as representative of my skills as a programmer today as this one.
## What is currently being tested?
Currently when testing the compiler I run `./libtest.sh examples/{file}.reid`,
Currently when testing the compiler I run `./libtest.sh reid_src/{file}.reid`,
where the `{file}` is one of the various examples I've written to help me test
features of the compiler.
What `./libtest.sh $1` does, is it compiles and runs the rust example found at
path `$1`. Some pre-existing examples can be found in [`examples`](./examples)
path `$1`. Some pre-existing examples can be found in [`reid_src`](./reid_src)
All examples currently end up producing a `hello.o` and `hello.asm` file to the
root directory, which is then linked with `ldd` to produce a `main`, which is
@ -162,6 +119,10 @@ 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
@ -171,5 +132,6 @@ Assuming `llvm-project` from the previous step was at
LLVM_SYS_201_PREFIX=/path/llvm-project/build cargo build
```
Alternatively assuming you have LLVM 20.1 or newer installed you may use omit
the environment variable entirely and use dynamic linking instead
## 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!

View File

@ -1,323 +0,0 @@
# Reid Language
This is where the documentation for this language will go, describing the
features, syntax and standard library of the language as best as I have time and
motivation to write.
Documentation is presented in a formal grammar and an example,
syntax-highlighted with Rust, because it's close enough.
## Standard Library
Reid has a standard library that is referred to as `std` in code. Documentation
about importable types and functions can be found [here](./standard_library.md).
## Book
A collection of documentation and examples to get you going can be found at [the
Book of Reid](./book.md). It is recommended you read through the chapter about
Syntax first though to familiarize yourself with the basic concepts of Reid.
## Intrinsics
Reid also has intrinsic functions that are good to know about. These are more
in-depth, but when you're feeling up to it, you can read about them
[here](./intrinsics.md).
## 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. 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.
In formal grammar
```bnf
<module> :: (<import> | <type-definition> | <binop-definition> | <function> | <assoc-function-block>)*
```
Table of Contents:
- [Common tokens](#common-tokens)
- [Imports](#imports)
- [Type definitions](#type-definitions)
- [Struct types](#struct-types)
- [Binary operation Definitions](#binary-operation-definitions)
- [Function definitions](#function-definition)
- [Associated functions](#associated-functions)
- [Statement](#statement)
- [Expression](#expression)
### Common tokens
Common token used throughout this document to express parts of grammar include:
```bnf
<ident> :: [a-Z]([_a-Z0-9])*
<literal> :: <integer> | <real> | <char> | <string> | <bool>
<integer> :: <decimal> | <hexadecimal> | <octal> | <binary>
<real> :: [0-9]+ "." [0-9]+
<char> :: "'" <any-character> "'"
<string> :: "\"" <any-character>* "\""
<bool> :: "true" | "false"
// Any character (except "), or any character escaped
<any-character> :: [.] | "\" [.]
<decimal> :: [0-9]+
<hexadecimal> :: "0x" [0-9a-f]+
<octal> :: "0o" [0-7]+
<binary> :: "0b" [01]+
<type> :: <primitive-type>
| "[" <type> ";" <integer>] "]"
| "*" <type>
| "&" [ "mut" ] <type>
<primitive-type> ::
"char" | "bool" |
"u8" | "u16" | "u32" | "u64" | "u128" |
"i8" | "i16" | "i32" | "i64" | "i128" |
"f16" | "f32" | "f32b" | "f64" | "f80" | "f128" | "f128ppc"
<binop> :: "+" | "-" | "*"
| "/" | "%" | "&&" | "||"
| "&" | "|" | "^" | ">>"
| "<<" | <cmp>
<cmp> :: "<" | "<=" | "==" | "!=" | ">=" | >"
<unary> :: "+" | "-" | "!"
```
### Imports
Imports are used to import functions and types from other modules. In formal
grammar the syntax for imports is
```bnf
<import> :: "import" <ident> "::" <ident> ";"
```
An example importing the `print`-function from [`std`](./standard_library.md) would be
``` rust
import std::print;
```
### Type Definitions
Type definitions are used to define new types. Currently this only supports
struct-types. In formal grammar:
```bnf
<type-definition> :: <struct-definition>
```
#### Struct types
Struct (or Structure) types are aggregate types containing other types within
struct fields. In formal grammar:
```bnf
<struct-definition> :: "struct" <ident> "{" [ <field-def> ( "," <field-def> )* [ "," ] ] "}"
<field-def> :: <ident> ":" <type>
```
An example of a struct `Test` containing two fields `first` and `second` of
integer types.
```rust
struct Test {
first: u32,
second: u64,
}
```
### Binary Operation Definitions
Reid has a feature where custom binary operations can be defined with a
specialized syntas. Only pre-defined operators are allowed however. In formal
grammar:
```bnf
<binop-definition>: "impl" "binop" <param> <binop> <param> "-> " <type> <block>
<param> :: "(" <ident> ":" <type> ")"
```
(Block-syntax is defined formally with functions)
An example of a custom binary operator `+` between type `u16` and `u32` could be:
```rust
impl binop (lhs: u16) + (rhs: u32) -> u32 {
return (lhs as u32) + rhs;
}
```
### Function Definition
Reid syntax for defining functions is similar to rust. There are two types of functions:
1. `extern` functions which are defined in another module, used to define functions from outside modules such as `libc`.
2. `local` functions which are defined locally in the module in Reid. Their
definition is contained within a `block` which contains a list of
`statement`s.
In formal grammar:
```bnf
<function-definition> :: <extern-function> | <local-function>
<extern-function> :: "extern" "fn" <signature> ";"
<local-function> :: [ "pub" ] "fn" <signature> <block>
<signature> :: <ident> "(" [ <params> ] ")" [ "->" <type> ]
<params> :: <param-or-self> ( "," <param> )*
<param-or-self> = <param> | ( [ "&" [ "mut" ] ] "self")
<param> :: (<ident> ":" <type>)
<block> :: "{" <statement>* "}"
```
An example of a simple `extern` and `local` function definition would be:
```rust
extern fn puts(message: *char) -> i32;
fn main() -> u8 {
return 7;
}
```
#### Associated Functions
Reid also has a very similar syntax for defining associated functions as Rust
does. They are also the only types of functions where usage of initial
"self"-param is allowed, referring to a potential self-type. Associated
functions are functions that are defined within certain types such that you can
have multiple functions of the same name, as long as they are associated with a
different type. In formal grammar associated function blocks are:
```bnf
<assoc-function-block> :: "impl" <type> "{" <function-definition>* "}"
```
An example of such a block could be:
```rust
impl Test {
fn get_field(&self) -> u32 {
*self.field
}
}
```
### Statement
Statements in Reid is how you tell the program to do anything. Currently supported statements include:
- Let-statement to declare new variables
- Let-statements declare immutable variables by-default, but can declare
mutable variables with the `mut` keyword similar to Rust.
- Set-statement to re-set previously declared (mutable) variables to new values.
- Statements can also be simply expressions without intent to store the result.
- Return-statements to return values out of blocks or functions.
- For-loops to loop over a certain range of numbers
- While-loops to loop until a certain condition is no longer met.
In formal grammar:
```bnf
<statement> :: <let> | <set> | <return> | <for> | <while> | <expr-statement>
<let> :: "let" [ "mut" ] <ident> "=" <expression> ";"
<set> :: <ident> "=" <expression> ";"
<expr-statement> :: <expression> ";"
<for> :: "for" <ident> "in" <expression> ".." <expression> <block>
<while> :: "while" <expression> <block>
<return> :: ( "return" <expression> ";" ) | <expression>
```
An example of each statement type would be:
```rust
let mut value = 5;
value = 6;
for i in 0..5 { }
while value < 5 { }
// "hard" return
return value;
// "soft" return
value
```
### Expression
Expressions in Reid are anything that can return a value, such as function
calls, literals, or if-expressions. Types of supported expressions include:
- **Variable name reference**, to reference a value in a variable
- **Borrow**, which works similar to Rust but with a little less safety. In simple
terms it creates a safer pointer-type than a regular pointer.
- **Deref**, which extracts the value out of a borrow.
- **Literal**, which is just some direct value, such as a number.
- **Array-value**, to declare a new array with a static length
- **Struct-value**, to declare a new value for a struct that has been defined
earlier.
- Shorter way to declare arrays with a single initialized value.
- **Indexing** into an array-value
- **Accessing a field** in a struct-value
- **Binary operations** (such as add/sub/mult)
- **Unary operations** (such as !value or -value)
- **Function calls**, to invoke a predefined function with given parameters
- **Associated function calls**, to invoke a predefined function on a certain
*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
given condition.
- **Casts** to explicitly cast a value to another type.
Expressions can also always be surrounded by (paranthesis).
In formal grammar:
```bnf
<expression> ::
<variable> | <borrow> |
<deref> | <literal> |
<array> | <struct> |
<indexing> | <accessing> |
<binary-exp> | <unary-exp> |
<function-call> | <accessing-function-call> | <assoc-function-call>
<macro-invocation> | <block> | <if-expr> | <cast> |
( "(" <expression> ")" )
<variable> :: <ident>
<borrow> :: "&" [ "mut" ] <ident>
<deref> :: "*" <ident>
<array> :: "[" <expression>* "]"
<struct> :: <ident> "{" [ <field> ( "," <field> )* [ "," ] ]
<field> :: <ident> ":" <expression>
<indexing> :: <expression> "[" <integer> "]"
<accessing> :: <expression> "." <ident>
<binary-exp> :: <expression> <binop> <expression>
<unary-exp> :: <unary> <expression>
<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>
```
An example of each expression in-order is:
```rust
varname // Variable
&varname // Borrow
*borrowed_varname // Deref
[ varname, 5, 7 ] // Array
Test { first: 4, second: 15 } // Struct
array[0] // Indexing
test.first // Accessing
7 + value // Binop
!bool_value // Unary
func(value, 14) // Function call
Test::get_field(&test); // Associated function call
test.get_field(); // Same, but using a the dot-form shorthand
include_bytes!("./test"); // Macro invocation
if varname {} else {} // If-expression
value as u32 // cast
(value + 2) // Binop within parenthesis
```

View File

@ -1,193 +0,0 @@
# Book of Reid
Welcome to the Book of Reid, a learning resource for Reid programming language.
This is neither just documentation or a tutorial, but something in between,
trying to establish basic concepts and philosophies.
Before reading this book, it is recommended you familiarize yourself with [the
Syntax of Reid](./README.md#syntax-and-general-information). After you're
familiar with that, you can continue here.
The best way to think about Reid is to think about how a combination of Rust's
Syntax and C's closeness to hardware would manifest itself in a language. Reid
is a very grounded language with not many safety features in reality, while
still trying to have some. Reid also has error raporting vastly superior to that
of C, similar to Rust.
Reid also, similarly to Rust, has a powerful typechecker that can infer types
automatically quite well. When this does not pan out, you can often coerce types
either in literals by adding a type after it (e.g. `5u32`), similarly to rust,
or simply casting the value (e.g. `5 as u32`).
## Table of Contents:
- [Hello World](#hello-world)
- [Borrowing and Pointers](#borrowing-and-pointers)
- [Harder Hello World](#harder-hello-world)
### Hello World
A hello world in Reid looks something like this:
```rust
import std::print;
import std::from_str;
import std::free_string;
fn main() {
let message = from_str("hello world");
print(message);
free_string(&message);
}
```
Let's go through this example line-by-line:
```rust
import std::print;
import std::from_str;
import std::free_string;
```
Tthe first 3 lines are simply imports from the [Standard
Library](./standard_library.md) to functions `print`, `from_str` and
`free_string`, which are used later in this example.
```rust
fn main() {
...
}
```
Then we declare our `main`-function. The function that gets executed after
compilation is always called `main`, and it can return a value, although it does
not necessarily have to. The return code of the program ends up being the return
value of `main`, and without a return value it may be unpredictable. In this
example we don't declare a return value for `main`.
```rust
let message = from_str("hello world");
```
Then we create our printable message with `from_str` and store it in variable
`message`. While this value could be passed to `print` directly, it is necessary
to store the value first in order to free it. Let's come back to that.
```rust
print(message);
```
Here we actually print out the message we just created, very simple.
```rust
free_string(&message);
```
Finally we free the string. Like mentioned before, it is necessary to store the
value in a variable so that the memory allocated for the message can be free.
While freeing the memory is not strictly necessary, it is recommended,
especially if the program runs for longer than this example.
That's the Hello World of Reid! It is not a oneliner, but at least I'd say it is quite simple in the end!
### Borrowing and Pointers
In Reid, all **variables** can be borrowed, and borrows can be dereferenced.
Borrows act like pointers, except that borrows do not have the same implicit
safety-problem as pointers, because Borrows are not implicitly unsized. With
pointers, the size of the allocated memory is unknown at compile time, which
makes them unsafe in comparisons.
Note though how **variables** were bolded; You can not make borrows out of just any expressions, they must first be stored in variables. A simple example using borrows would be:
```rust
fn main() -> u32 {
// Create a value to be mutated
let mut value = [4, 3, 2];
// Pass a mutable borrow of the value
mutate(&mut value);
// Retrieve the now-mutated value
return value[1];
}
fn mutate(value: &mut [u32; 3]) {
// Dereference the borrow to mutate it
*value[1] = 17;
}
```
This example will always return `17`. Notice also, how a **mutable** borrow was
passed to `mutate`-function. While borrows do not always need to be mutable,
this example would not work without the `mut`-keyword. Try it out for yourself
to see why!
### Harder Hello World
A little bit harder example to the previous hello world would be
`hello_world_harder.reid` from [examples](../examples/hello_world_harder.reid)
```rust
import std::print;
import std::String;
fn main() {
let mut test = String::from("hello");
test.push(String::from(" world: "));
test.push_num(175);
print(test);
test.free();
return;
}
```
Let's go through this again line-by-line
```rust
import std::print;
import std::String;
```
At the start are the standard imports, like in the original Hello World, but
notice that instead of importing just functions we import the type `String` as
well. When importing types, not only are the type imported, but also any binary
functions or associated functions related to it with it.
```rust
let mut test = String::from("hello");
```
Next we create the initial string, just like in our original example, but this
time the message is not complete. We also use an associated function to create
the string instead one of the now-deprecated global functions.
```rust
test.push(String::from(" world: "));
test.push_num(175);
```
After that we edit the message by first pushing ` world: ` to it, and also
appending the number `175` at the end of it. For both of these operations we are
again using associated functions, but in a different short-hand syntax.
These two lines would be actually equivalent to the above ones:
```rust
String::push(&mut test, String::from(" world: "));
String::push_num(&mut test, 175);
```
----
```rust
print(test);
test.free();
return;
```
After that we simply print the string, and free up the string (again with an
associated function) before returning. Nothing special there!

View File

@ -1,187 +0,0 @@
## Intrinsics
Intrinsics are functions that are defined within the language compiler but not
in standard library, thus they do not require importing in order to use (and
trying to re-define these will end up causing issues). Intrinsics include all
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 bytes.
```rust
i32::sizeof(); // Returns 4
```
#### `<T>::null() -> *T`
Returns a null-pointer of type `T`.
```rust
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.

View File

@ -1,75 +0,0 @@
# Standard Library
## Strings
#### `pub struct String`
Editable string value that can be printed and extended
Has the following binops defined:
- `String` + `*char` = `String`
- `String` + `u64` = `String`
##### `String::new() -> String`
Returns a new empty `String`-object, which must later be manually freed.
##### `String::from(str: *char) -> String`
Creates a new `String`-object containing initially data from the given string-literal which must be later freed.
##### `String::set(&mut self, c: char, position: u64)`
Edits given `string` by setting the character at index `position` to be `c`.
##### `String::push_num(&mut self, num: u64)`
Formats the given number into the end of the string.
##### `String::concat(&mut self, source: String)`
Concatenates `source` to the end of `destination`.
### Deprecated functions
#### `pub fn new_string() -> String`
Returns a new empty `String`-object, which must later be manually freed.
_deprecated: Use `String::new()`_
#### `pub fn from_str(string: *char) -> String`
Creates a new `String`-object containing initially data from the given string-literal which must be later freed.
_deprecated: Use `String::from()`_
#### `pub fn set_char(string: &mut String, c: char, position: u64)`
Edits given `string` by setting the character at index `position` to be `c`.
_deprecated: Use `String::set()`_
#### `pub fn add_num_to_string(string: &mut String, num: u64)`
Formats the given number into the end of the string.
_deprecated: Use `String::push_num()`_
#### `pub fn concat_strings(destination: &mut String, source: String)`
Concatenates `source` to the end of `destination`.
_deprecated: Use `String::concat()`_
## General
## Maths
#### `pub fn clamp(min: f32, max: f32, value: f32) -> f32`
Returns `value` as clamped between `min` and `max`. Equivalent to `max(min(value, max), min)`
#### `pub fn abs(value: f32) -> f32`
Returns the absolute value of `value`.

View File

@ -1,9 +0,0 @@
// Arithmetic, function calls and imports!
fn main() -> u32 {
let value = 0b110;
let other = 0o17;
return value * other + 7 * -value;
}

View File

@ -1,7 +0,0 @@
// Arithmetic, function calls and imports!
fn main() -> u16 {
let mut a = [5; 20];
return a[15];
}

View File

@ -1,27 +0,0 @@
// Arithmetic, function calls and imports!
struct Test {
field: i32,
second: [u32; 4],
}
fn test() -> Test {
let value = Test {
field: 5,
second: [6, 3, 4, 8],
};
return value;
}
fn main() -> u32 {
let mut value = test();
let beep = [2, 3, 4];
let boop: f32 = 3;
let mut a = &mut value;
*a.second[2] = 5;
return *a.second[2];
}

View File

@ -1,43 +0,0 @@
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
}
}
impl i32 {
fn test(self) -> u32 {
43
}
}
/// Hello there!
fn test() {
}
fn main() -> u32 {
let otus = Otus { field: 17 };
print(from_str("otus: ") + Otus::test(&otus) as u64);
print(from_str("i32: ") + i32::test(54) as u64);
print(from_str("sizeof i32: ") + i32::sizeof());
let nullptr = i32::null();
let mut list = u64::malloc(15);
list[4] = 17;
print(from_str("value: ") + list[4]);
return i32::sizeof() as u32;
}

View File

@ -1,29 +0,0 @@
import std::print;
import std::from_str;
import std::String;
struct Otus {
field: u32,
}
impl Otus {
fn test(&self) -> u32 {
*self.field
}
}
impl i32 {
fn test(self) -> u32 {
43
}
}
fn main() -> u32 {
let otus = Otus { field: 17 };
print(from_str("otus: ") + otus.test() as u64);
otus.field;
return otus.test();
}

View File

@ -1,15 +0,0 @@
fn main() -> u32 {
// Create a value to be mutated
let mut value = [4, 3, 2];
// Pass a mutable borrow of the value
mutate(&mut value);
// Retrieve the now-mutated value
return value[1];
}
fn mutate(value: &mut [u32; 3]) {
// Dereference the borrow to mutate it
*value[1] = 17;
}

View File

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

View File

@ -1,5 +0,0 @@
// Arithmetic, function calls and imports!
pub fn main() -> char {
return 'b';
}

View File

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

View File

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

View File

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

View File

@ -1,12 +0,0 @@
// Arithmetic, function calls and imports!
impl binop (lhs: u16) + (rhs: u32) -> u32 {
return (lhs as u32) + rhs;
}
fn main() -> u32 {
let value = 6 as u16;
let other = 15 as u32;
return value + other;
}

View File

@ -1,6 +0,0 @@
// Arithmetic, function calls and imports!
fn main() -> u16 {
return (50 / 5 + 52 % 5);
}

View File

@ -1,6 +0,0 @@
// Arithmetic, function calls and imports!
pub fn main() -> bool {
return 7.5 > 5.001;
}

View File

@ -1,11 +0,0 @@
import std::print;
import std::from_str;
fn main() {
for i in 0..1 {
print(from_str("Hello!"));
}
for i in 0..1 {
print(from_str("Hello!"));
}
}

View File

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

View File

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

View File

@ -1,9 +0,0 @@
import std::print;
import std::from_str;
import std::free_string;
fn main() {
let message = from_str("hello world");
print(message);
free_string(&message);
}

View File

@ -1,16 +0,0 @@
import std::print;
import std::String;
fn main() {
let mut test = String::from("hello");
test.push(String::from(" world: "));
test.push_num(175);
print(test);
test.free();
return;
}

View File

@ -1,9 +0,0 @@
import std::print;
import std::from_str;
import std::String;
fn main() -> u8 {
print(from_str("hello") + " beep: " + 1234u64);
return 0;
}

View File

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

View File

@ -1,27 +0,0 @@
import std::print;
import std::new_string;
import std::add_num_to_str;
import std::add_char;
fn main() -> u8 {
for i in 0..5 {
for j in 0..5 {
for k in 0..5 {
let mut test = new_string();
add_num_to_str(&mut test, i);
add_char(&mut test, ',');
add_num_to_str(&mut test, j);
add_char(&mut test, ',');
add_num_to_str(&mut test, k);
print(test);
}
}
}
for i in 0..1000 {
for j in 0..1000 {
for k in 0..1000 {
}
}
}
return 0;
}

View File

@ -1,11 +0,0 @@
// Arithmetic, function calls and imports!
fn main() -> u32 {
let mut num = 0;
while num < 10 {
num = num + 1;
}
return num;
}

View File

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

View File

@ -1 +0,0 @@
hello

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,10 +0,0 @@
// 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);
}

View File

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

View File

@ -1,9 +0,0 @@
fn main() -> u8 {
let mut a = 5;
inner(&mut a);
return 0;
}
fn inner(a: &mut i32) {
*a = *a + 1;
}

View File

@ -1,12 +0,0 @@
import std::from_str;
import std::print;
import std::int_div;
fn main() -> i32 {
let hello = from_str("hello world");
print(hello);
return int_div(15, 5).quotient;
}

View File

@ -1,7 +0,0 @@
// Arithmetic, function calls and imports!
fn main() -> bool {
return 5.0 > -1;
}

View File

@ -1,10 +0,0 @@
struct Otus {}
impl binop (lhs: Otus) + (rhs: u64) -> Otus {
return lhs;
}
pub fn make_otus() -> Otus {
return Otus {};
}

View File

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

7
main.cpp Normal file
View File

@ -0,0 +1,7 @@
#include <iostream>
extern "C" {
int mainfunc();
}
int main() { std::cout << "Return value of test: " << mainfunc() << std::endl; }

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

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

@ -0,0 +1,12 @@
[package]
name = "reid-lib"
version = "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=["force-static"] }
## Make it easier to generate errors
thiserror = "1.0.44"

View File

@ -0,0 +1,65 @@
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("const", Constant(I32(5))).unwrap();
let fibonacci_call = m_entry
.build("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("const", Constant(I32(3))).unwrap();
let param_n = f_entry.build("param", Param(0)).unwrap();
let cond = f_entry
.build("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("const", Constant(I32(1))).unwrap();
then_b.terminate(TerminatorKind::Ret(ret_const)).unwrap();
let const_1 = else_b.build("const", Constant(I32(1))).unwrap();
let const_2 = else_b.build("const", Constant(I32(2))).unwrap();
let param_1 = else_b.build("sub", Sub(param_n, const_1)).unwrap();
let param_2 = else_b.build("sub", Sub(param_n, const_2)).unwrap();
let call_1 = else_b
.build("fibonacci", FunctionCall(fibonacci.value(), vec![param_1]))
.unwrap();
let call_2 = else_b
.build("fibonacci", FunctionCall(fibonacci.value(), vec![param_2]))
.unwrap();
let add = else_b.build("add", Add(call_1, call_2)).unwrap();
else_b.terminate(TerminatorKind::Ret(add)).unwrap();
dbg!(&context);
context.compile();
}

View File

@ -0,0 +1,491 @@
//! 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::{
BlockData, CustomTypeKind, FunctionData, Instr, InstructionData, ModuleData, NamedStruct,
TerminatorKind, Type, 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 {
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,
) -> Result<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,
) -> Result<(), ()> {
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(())
} else {
block.data.terminator = Some(value);
Ok(())
}
}
}
pub(crate) unsafe fn set_terminator_location(
&self,
block: &BlockValue,
location: DebugLocationValue,
) -> Result<(), ()> {
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(())
} else {
block.data.terminator_location = Some(location);
Ok(())
}
}
}
pub(crate) unsafe fn delete_block(&self, block: &BlockValue) -> Result<(), ()> {
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) -> Result<(), ()> {
unsafe {
match self.instr_data(&instruction).kind {
Instr::Param(_) => Ok(()),
Instr::Constant(_) => Ok(()),
Instr::Add(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::Sub(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::Mult(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::And(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::ICmp(_, lhs, rhs) => {
let t = match_types(&lhs, &rhs, self)?;
if t.comparable() {
Ok(())
} else {
Err(()) // TODO error: Types not comparable
}
}
Instr::FunctionCall(fun, params) => {
let param_types = self.function_data(&fun).params;
dbg!(&param_types, &params);
if param_types.len() != params.len() {
return Err(()); // TODO error: invalid amount of params
}
for (a, b) in param_types.iter().zip(params) {
if *a != b.get_type(&self)? {
return Err(()); // 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(())?;
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(()) // TODO error: inner type mismatch
}
} else {
Err(()) // TODO error: not a pointer
}
}
Instr::Store(ptr, _) => {
let ty = ptr.get_type(&self)?;
if let Type::Ptr(_) = ty {
Ok(())
} else {
Err(()) // TODO error: not a pointer
}
}
Instr::ArrayAlloca(_, _) => Ok(()),
Instr::GetElemPtr(ptr_val, _) => {
let ptr_ty = ptr_val.get_type(&self)?;
match ptr_ty {
Type::Ptr(_) => Ok(()),
_ => Err(()),
}
}
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(()); // TODO error: no such field
}
}
}
Ok(())
} else {
Err(()) // TODO error: not a struct
}
} else {
Err(()) // 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(()),
}
}
}
}
}
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
}
}
}

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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,355 @@
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) program: DebugProgramValue,
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, program: &DebugProgramValue, kind: DebugMetadata) -> DebugMetadataValue {
let mut metadata = self.metadata.borrow_mut();
let value = DebugMetadataValue(metadata.len());
metadata.push(DebugMetadataHolder {
program: *program,
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) -> 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(&self, value: &DebugProgramValue) -> DebugSubprogramData {
unsafe {
self.programs
.borrow()
.get_unchecked(value.0 - 1)
.data
.clone()
}
}
}
#[derive(Clone, Copy, Default)]
pub struct DebugLocation {
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,
/// Used for line number
pub location: DebugLocation,
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 location: DebugLocation,
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 location: DebugLocation,
pub size_bits: u64,
pub flags: DwarfFlags,
pub fields: Vec<DebugFieldType>,
}
#[derive(Clone)]
pub struct DebugFieldType {
pub name: String,
pub location: DebugLocation,
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),
}

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

@ -0,0 +1,359 @@
//! Debug implementations for relevant types
use std::{
fmt::{Debug, Write},
marker::PhantomData,
};
use crate::{
CmpPredicate, Instr, InstructionData, TerminatorKind,
builder::*,
debug_information::{
DebugArrayType, DebugBasicType, DebugFieldType, DebugLocation, DebugLocationValue,
DebugMetadataValue, DebugPointerType, DebugProgramValue, DebugScopeValue, DebugStructType,
DebugSubprogramType, DebugTypeData, DebugTypeHolder, DebugTypeValue,
},
};
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 {
self.module.fmt(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,
"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::Sub(lhs, rhs) => fmt_binop(f, lhs, &"-", rhs),
Instr::Mult(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::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())
}
}
}
}
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("location", &self.location)
.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("location", &self.location)
.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, "ln {}, col {}", self.line, self.column)
}
}

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

@ -0,0 +1,531 @@
//! 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,
InstructionDebugRecordData,
};
use fmt::PrintableModule;
use util::match_types;
pub mod builder;
pub mod compile;
pub mod debug_information;
mod fmt;
mod util;
#[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,
}
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 {
pub is_extern: bool,
pub is_main: bool,
pub is_pub: bool,
pub is_imported: bool,
}
impl Default for FunctionFlags {
fn default() -> FunctionFlags {
FunctionFlags {
is_extern: false,
is_main: false,
is_pub: false,
is_imported: 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,
}
pub struct Block<'builder> {
phantom: PhantomData<&'builder ()>,
builder: Builder,
value: BlockValue,
}
impl<'builder> Block<'builder> {
pub fn build<T: Into<String>>(
&mut self,
name: T,
instruction: Instr,
) -> Result<InstructionValue, ()> {
unsafe {
self.builder.add_instruction(
&self.value,
InstructionData {
kind: instruction,
location: None,
meta: None,
},
name.into(),
)
}
}
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) -> Result<(), ()> {
unsafe { self.builder.terminate(&self.value, instruction) }
}
pub fn set_terminator_location(&mut self, location: DebugLocationValue) -> Result<(), ()> {
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) -> Result<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
}
}
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);
}
}
}
#[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,
}
#[derive(Clone, Hash)]
pub enum Instr {
Param(usize),
Constant(ConstValue),
Add(InstructionValue, InstructionValue),
Sub(InstructionValue, InstructionValue),
Mult(InstructionValue, InstructionValue),
And(InstructionValue, InstructionValue),
Phi(Vec<InstructionValue>),
Alloca(Type),
Load(InstructionValue, Type),
Store(InstructionValue, InstructionValue),
ArrayAlloca(Type, u32),
GetElemPtr(InstructionValue, Vec<InstructionValue>),
GetStructElemPtr(InstructionValue, u32),
ExtractValue(InstructionValue, u32),
/// Integer Comparison
ICmp(CmpPredicate, InstructionValue, InstructionValue),
FunctionCall(FunctionValue, Vec<InstructionValue>),
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum Type {
I8,
I16,
I32,
I64,
I128,
U8,
U16,
U32,
U64,
U128,
Bool,
Void,
CustomType(TypeValue),
Array(Box<Type>, u64),
Ptr(Box<Type>),
}
#[derive(Debug, Clone, Hash)]
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),
StringPtr(String),
}
#[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 InstructionValue {
pub(crate) fn get_type(&self, builder: &Builder) -> Result<Type, ()> {
use Instr::*;
unsafe {
match &builder.instr_data(self).kind {
Param(nth) => builder
.function_data(&self.0.0)
.params
.get(*nth)
.cloned()
.ok_or(()),
Constant(c) => Ok(c.get_type()),
Add(lhs, rhs) => match_types(lhs, rhs, &builder),
Sub(lhs, rhs) => match_types(lhs, rhs, &builder),
Mult(lhs, rhs) => match_types(lhs, rhs, &builder),
And(lhs, rhs) => match_types(lhs, rhs, &builder),
ICmp(_, _, _) => Ok(Type::Bool),
FunctionCall(function_value, _) => Ok(builder.function_data(function_value).ret),
Phi(values) => values.first().ok_or(()).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 {
Type::Array(elem_ty, _) => Ok(Type::Ptr(Box::new(*elem_ty.clone()))),
_ => Ok(*inner_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(()),
})
}
}
}
}
}
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::StringPtr(_) => Ptr(Box::new(I8)),
ConstValue::Bool(_) => Bool,
}
}
}
impl Type {
pub fn comparable(&self) -> bool {
match self {
Type::I8 => true,
Type::I16 => true,
Type::I32 => true,
Type::I64 => true,
Type::I128 => true,
Type::U8 => true,
Type::U16 => true,
Type::U32 => true,
Type::U64 => true,
Type::U128 => true,
Type::Bool => true,
Type::Void => false,
Type::Ptr(_) => false,
Type::CustomType(_) => false,
Type::Array(_, _) => false,
}
}
pub fn signed(&self) -> bool {
match self {
Type::I8 => true,
Type::I16 => true,
Type::I32 => true,
Type::I64 => true,
Type::I128 => true,
Type::U8 => false,
Type::U16 => false,
Type::U32 => false,
Type::U64 => false,
Type::U128 => false,
Type::Bool => false,
Type::Void => false,
Type::Ptr(_) => false,
Type::CustomType(_) => false,
Type::Array(_, _) => false,
}
}
}
impl TerminatorKind {
pub(crate) fn get_type(&self, builder: &Builder) -> Result<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),
}
}
}

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

@ -0,0 +1,128 @@
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::{
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,
) -> Result<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(()) }
} else {
Err(())
}
}

7
reid-lsp/.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,13 +0,0 @@
[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 = "*"

View File

@ -1 +0,0 @@
# Reid Language Server

View File

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

View File

@ -1,96 +0,0 @@
/* --------------------------------------------------------------------------------------------
* 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();
}

View File

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

View File

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

View File

@ -1,28 +0,0 @@
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",
},
}];

View File

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

View File

@ -1,94 +0,0 @@
{
"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"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

File diff suppressed because it is too large Load Diff

View File

@ -1,605 +0,0 @@
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(&params.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;
}

View File

@ -1,281 +0,0 @@
{
"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"
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,74 +1,46 @@
use std::{env, fs, path::PathBuf};
use reid::{compile_simple, ld::LDRunner, CustomIRs};
use reid::compile_simple;
use reid_lib::compile::CompileOutput;
fn main() -> Result<(), std::io::Error> {
let args: Vec<String> = env::args().collect();
let mut iter = args.into_iter().skip(1);
if let Some(filename) = iter.next() {
let mut libraries = Vec::new();
while let Some(libname) = iter.next() {
libraries.push(libname);
}
if let Some(filename) = args.get(1) {
let path = PathBuf::from(filename).canonicalize().unwrap();
let parent = path.with_extension("");
let llvm_ir_path = parent.with_extension("ll");
let object_path = parent.with_extension("o");
let llir_path = parent.with_extension("llir");
let mir_path = parent.with_extension("mir");
let asm_path = parent.with_extension("asm");
let before = std::time::SystemTime::now();
let text = fs::read_to_string(&path)?;
match compile_simple(&text, PathBuf::from(&path)) {
Ok(CompileOutput {
triple,
assembly,
obj_buffer,
llvm_ir,
}) => {
println!("{}", llvm_ir);
let cpu = std::env::var("CPU").unwrap_or("generic".to_owned());
let features = std::env::var("REIDFLAGS").unwrap_or("".to_owned());
match compile_simple(&text, PathBuf::from(&path), Some(cpu), vec![features]) {
Ok((
CompileOutput {
triple: _triple,
assembly,
obj_buffer,
llvm_ir: _llvm_ir,
},
CustomIRs { llir, mir },
)) => {
log::trace!("{}", _llvm_ir);
log::debug!("Compiled with triple: {}\n", &_triple);
log::debug!("Output LLVM IR to {:?}", llvm_ir_path);
log::debug!("Output Assembly to {:?}", asm_path);
log::debug!("Output Object-file to {:?}\n", object_path);
log::debug!("Output LLIR-file to {:?}\n", llir_path);
log::debug!("Output MIR-file to {:?}\n", mir_path);
fs::write(&llvm_ir_path, &_llvm_ir).expect("Could not write LLVM IR -file!");
fs::write(&asm_path, &assembly).expect("Could not write Assembly-file!");
fs::write(&object_path, &obj_buffer).expect("Could not write Object-file!");
fs::write(&llir_path, &llir).expect("Could not write LLIR-file!");
fs::write(&mir_path, &mir).expect("Could not write MIR-file!");
let after = std::time::SystemTime::now();
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(&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);
println!(
"Compilation took: {:.2}ms\n",
(after.duration_since(before).unwrap().as_micros() as f32) / 1000.
);
println!("Linking {:?}", &object_path);
let linker = std::env::var("LD").unwrap_or("ld".to_owned());
let mut linker = LDRunner::from_command(&linker).with_library("c").with_library("m");
for library in libraries {
linker = linker.with_library(&library);
}
linker.invoke(&object_path, &object_path.with_extension("out"));
}
Err(e) => panic!("{}", e),
};
} else {
log::error!("Please input compiled file path!")
println!("Please input compiled file path!")
}
Ok(())
}

View File

@ -1,249 +1,21 @@
extern fn puts(message: *char) -> i32;
extern fn free(ptr: *u8);
extern fn div(numerator: i32, denominator: i32) -> div_t;
extern fn puts(message: string) -> i32;
/// Editable string value that can be printed and extended
struct String {
inner: *char,
length: u64,
max_length: u64,
must_be_freed: bool,
}
impl String {
/// Returns a new empty `String`-object, which must later be manually freed.
pub fn new() -> String {
String {
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();
let static = String {
inner: str,
length: length - 1,
max_length: length,
must_be_freed: false,
};
concat_strings(&mut new, static);
return new;
}
/// Same as `concat`
pub fn push(&mut self, other: String) {
for i in 0 .. (str_length(other.inner) - 1) {
add_char(self, other.inner[i]);
}
}
/// Adds a character to the end of this string
pub fn add_char(&mut self, c: char) {
if ((*self).length + 1) >= (*self).max_length {
let new = char::malloc((*self).max_length + 4);
copy_bits((*self).inner, new, (*self).max_length);
if (*self).must_be_freed == true {
free((*self).inner as *u8);
}
(*self).max_length = (*self).max_length + 4;
(*self).inner = new;
(*self).must_be_freed = true;
}
(*self).inner[(*self).length] = c;
(((*self).inner) as *u8)[(*self).length + 1] = 0;
(*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)
}
let rem = num % 10;
if rem == 0 { self.add_char('0'); }
else if rem == 1 { self.add_char('1'); }
else if rem == 2 { self.add_char('2'); }
else if rem == 3 { self.add_char('3'); }
else if rem == 4 { self.add_char('4'); }
else if rem == 5 { self.add_char('5'); }
else if rem == 6 { self.add_char('6'); }
else if rem == 7 { self.add_char('7'); }
else if rem == 8 { self.add_char('8'); }
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);
}
}
impl binop (lhs: String) + (rhs: *char) -> String {
let mut new = lhs;
let added = from_str(rhs);
concat_strings(&mut new, added);
free_string(&added);
return new;
}
impl binop (lhs: String) + (rhs: u64) -> String {
let mut new = lhs;
add_num_to_str(&mut new, rhs);
return new;
}
struct div_t {
struct DivT {
quotient: i32,
remainder: i32,
}
/// Print given string to stdout
pub fn print(message: String) {
puts(message.inner);
extern fn div(numerator: i32, denominator: i32) -> DivT;
pub fn print(message: string) {
puts(message);
}
/// Divide an integer, returning the quotient and remainder.
pub fn int_div(numerator: i32, denominator: i32) -> div_t {
pub fn intdiv(numerator: i32, denominator: i32) -> DivT {
return div(numerator, denominator);
}
/// (deprecated) creates a new editable string
pub fn new_string() -> String {
String {
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();
let static = String {
inner: str,
length: length - 1,
max_length: length,
must_be_freed: false,
};
concat_strings(&mut new, static);
return new;
}
/// (deprecated) Adds a character to the end of a given string
pub fn add_char(string: &mut String, c: char) {
if ((*string).length + 1) >= (*string).max_length {
let new = char::malloc((*string).max_length + 4);
copy_bits((*string).inner, new, (*string).max_length);
if (*string).must_be_freed == true {
free((*string).inner as *u8);
}
(*string).max_length = (*string).max_length + 4;
(*string).inner = new;
(*string).must_be_freed = true;
}
(*string).inner[(*string).length] = c;
(((*string).inner) as *u8)[(*string).length + 1] = 0;
(*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);
}
fn copy_bits(from: *char, to: *char, length: u64) {
for i in 0 .. length {
to[i] = from[i];
}
}
fn str_length(string: *char) -> u32 {
let mut pos = 0;
while ((string[pos] == '\0') == false) {
pos = pos + 1;
}
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)
}
let rem = num % 10;
if rem == 0 { add_char(string, '0'); }
else if rem == 1 { add_char(string, '1'); }
else if rem == 2 { add_char(string, '2'); }
else if rem == 3 { add_char(string, '3'); }
else if rem == 4 { add_char(string, '4'); }
else if rem == 5 { add_char(string, '5'); }
else if rem == 6 { add_char(string, '6'); }
else if rem == 7 { add_char(string, '7'); }
else if rem == 8 { add_char(string, '8'); }
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;
}
if value < min {
return min;
}
return value;
}
/// Returns the absolute value of `value`.
pub fn abs(f: f32) -> f32 {
if f < 0.0 {
return f * (0.0 - 1.0);
}
return f;
fn main() -> u16 {
return 0;
}

View File

@ -3,14 +3,10 @@
//! used for unwrapping syntax sugar, and then be transformed into Reid MIR.
use std::path::PathBuf;
use token_stream::TokenRange;
use crate::token_stream::TokenRange;
use crate::lexer::FullToken;
pub mod lexer;
pub mod parse;
pub mod process;
pub mod token_stream;
#[derive(Debug, Clone)]
pub struct Type(pub TypeKind, pub TokenRange);
@ -28,50 +24,16 @@ pub enum TypeKind {
U32,
U64,
U128,
F16,
F32B,
F32,
F64,
F128,
F80,
F128PPC,
Char,
String,
Array(Box<TypeKind>, u64),
Custom(String),
Borrow(Box<TypeKind>, bool),
Ptr(Box<TypeKind>),
Unknown,
}
#[derive(Debug, Clone)]
pub enum Literal {
Integer(u128),
Decimal(f64),
Number(u64),
Bool(bool),
String(String),
Char(char),
Specific(SpecificLiteral),
}
#[derive(Debug, Clone)]
pub enum SpecificLiteral {
I8(i8),
I16(i16),
I32(i32),
I64(i64),
I128(i128),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
U128(u128),
F16(f32),
F32(f32),
F32B(f32),
F64(f64),
F80(f64),
F128(f64),
F128PPC(f64),
}
#[derive(Debug, Clone)]
@ -80,32 +42,19 @@ pub struct Expression(pub ExpressionKind, pub TokenRange);
#[derive(Debug, Clone)]
pub enum ExpressionKind {
VariableName(String),
Borrow(Box<Expression>, bool),
Deref(Box<Expression>),
Borrow(String),
Deref(String),
Literal(Literal),
Array(Vec<Expression>),
ArrayShort(Box<Expression>, u64),
/// Array-indexed, e.g. <expr>[<expr>]
Indexed(Box<Expression>, Box<Expression>),
/// Struct-accessed, e.g. <expr>.<expr>
Accessed(Box<Expression>, String, TokenRange),
/// Associated function call, but with a shorthand
AccessCall(Box<Expression>, Box<FunctionCallExpression>),
Accessed(Box<Expression>, String),
Binop(BinaryOperator, Box<Expression>, Box<Expression>),
FunctionCall(Box<FunctionCallExpression>),
AssociatedFunctionCall(Type, Box<FunctionCallExpression>),
BlockExpr(Box<Block>),
IfExpr(Box<IfExpression>),
StructExpression(StructExpression),
UnaryOperation(UnaryOperator, Box<Expression>),
CastTo(Box<Expression>, Type),
}
#[derive(Debug, Clone)]
pub enum UnaryOperator {
Plus,
Minus,
Not,
}
#[derive(Debug, Clone, Copy)]
@ -113,17 +62,8 @@ pub enum BinaryOperator {
Add,
Minus,
Mult,
Div,
Mod,
BitshiftRight,
BitshiftLeft,
And,
Or,
Xor,
BitAnd,
BitOr,
LT,
LE,
GT,
@ -133,58 +73,46 @@ pub enum BinaryOperator {
}
impl BinaryOperator {
pub fn get_precedence(&self) -> u8 {
pub fn get_precedence(&self) -> i8 {
use BinaryOperator::*;
match &self {
Minus => 5,
Add => 10,
Mult => 15,
Div => 20,
Mod => 20,
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,
Minus => 10,
Mult => 20,
And => 100,
LT => 100,
LE => 100,
GT => 100,
GE => 100,
EQ => 100,
NE => 100,
}
}
}
#[derive(Debug, Clone)]
pub struct FunctionCallExpression {
pub name: String,
pub params: Vec<Expression>,
pub range: TokenRange,
pub is_macro: bool,
}
pub struct FunctionCallExpression(pub String, pub Vec<Expression>, pub TokenRange);
#[derive(Debug, Clone)]
pub struct IfExpression(
pub Expression,
pub Expression,
pub Option<Expression>,
pub Block,
pub Option<Block>,
#[allow(dead_code)] pub TokenRange,
);
#[derive(Debug, Clone)]
pub struct LetStatement {
pub name: String,
pub ty: Option<Type>,
pub mutable: bool,
pub value: Expression,
pub name_range: TokenRange,
}
pub struct LetStatement(
pub String,
pub Option<Type>,
/// Mutability
pub bool,
pub Expression,
pub TokenRange,
);
#[derive(Debug, Clone)]
pub struct ImportStatement(pub Vec<(String, TokenRange)>, pub TokenRange);
pub struct ImportStatement(pub Vec<String>, pub TokenRange);
#[derive(Debug)]
pub struct FunctionDefinition(pub FunctionSignature, pub bool, pub Block, pub TokenRange);
@ -192,23 +120,13 @@ pub struct FunctionDefinition(pub FunctionSignature, pub bool, pub Block, pub To
#[derive(Debug, Clone)]
pub struct FunctionSignature {
pub name: String,
pub documentation: Option<String>,
pub self_kind: SelfKind,
pub params: Vec<(String, Type, TokenRange)>,
pub args: Vec<(String, Type)>,
pub return_type: Option<Type>,
#[allow(dead_code)]
pub range: TokenRange,
}
#[derive(Debug, Clone)]
pub enum SelfKind {
Owned(Type),
Borrow(Type),
MutBorrow(Type),
None,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy)]
pub enum ReturnType {
Soft,
Hard,
@ -217,14 +135,13 @@ pub enum ReturnType {
#[derive(Debug, Clone)]
pub struct StructExpression {
name: String,
fields: Vec<(String, Expression, TokenRange)>,
range: TokenRange,
fields: Vec<(String, Expression)>,
}
#[derive(Debug, Clone)]
pub struct Block(
pub Vec<BlockLevelStatement>,
pub Option<(ReturnType, Option<Expression>)>,
pub Option<(ReturnType, Expression)>,
pub TokenRange,
);
@ -237,9 +154,7 @@ pub enum BlockLevelStatement {
_i: ImportStatement,
},
Expression(Expression),
Return(ReturnType, Option<Expression>),
ForLoop(String, TokenRange, Expression, Expression, Block),
WhileLoop(Expression, Block),
Return(ReturnType, Expression),
}
#[derive(Debug, Clone)]
@ -267,24 +182,11 @@ pub enum TopLevelStatement {
ExternFunction(FunctionSignature),
FunctionDefinition(FunctionDefinition),
TypeDefinition(TypeDefinition),
BinopDefinition(BinopDefinition),
AssociatedFunction(Type, Vec<FunctionDefinition>),
}
#[derive(Debug)]
pub struct BinopDefinition {
pub lhs: (String, Type, TokenRange),
pub op: BinaryOperator,
pub rhs: (String, Type, TokenRange),
pub return_ty: Type,
pub block: Block,
pub signature_range: TokenRange,
}
#[derive(Debug)]
pub struct Module {
pub name: String,
pub tokens: Vec<FullToken>,
pub top_level_statements: Vec<TopLevelStatement>,
pub path: Option<PathBuf>,
pub is_main: bool,

File diff suppressed because it is too large Load Diff

View File

@ -1,70 +1,68 @@
use std::path::PathBuf;
use crate::{
ast::{self, ReturnType},
mir::{
self, CustomTypeKey, FunctionParam, ModuleMap, NamedVariableRef, ReturnKind, SourceModuleId, StmtKind,
StructField, StructType, WhileStatement,
},
ast::{self},
mir::{self, NamedVariableRef, SourceModuleId, StmtKind, StructField, StructType},
};
impl mir::Context {
pub fn from(modules: Vec<mir::Module>, base: PathBuf) -> mir::Context {
let mut map = ModuleMap::new();
for module in modules {
map.insert(module.module_id, module);
}
mir::Context { modules: map, base }
mir::Context { modules, base }
}
}
impl ast::Module {
pub fn process(self, module_id: SourceModuleId) -> mir::Module {
pub fn process(&self, module_id: SourceModuleId) -> mir::Module {
let mut imports = Vec::new();
let mut associated_functions = Vec::new();
let mut functions = Vec::new();
let mut typedefs = Vec::new();
let mut binops = Vec::new();
use ast::TopLevelStatement::*;
for stmt in &self.top_level_statements {
match stmt {
Import(import) => {
imports.push(mir::Import(
import
.0
.iter()
.map(|(s, range)| (s.clone(), range.as_meta(module_id)))
.collect(),
import.1.as_meta(module_id),
));
imports.push(mir::Import(import.0.clone(), import.1.as_meta(module_id)));
}
FunctionDefinition(ast::FunctionDefinition(signature, is_pub, block, range)) => {
let def = mir::FunctionDefinition {
name: signature.name.clone(),
is_pub: *is_pub,
is_imported: false,
return_type: signature
.return_type
.clone()
.map(|r| r.0.into())
.unwrap_or(mir::TypeKind::Void),
parameters: signature
.args
.iter()
.cloned()
.map(|p| (p.0, p.1.into()))
.collect(),
kind: mir::FunctionDefinitionKind::Local(
block.into_mir(module_id),
(*range).as_meta(module_id),
),
};
functions.push(def);
}
FunctionDefinition(function_def) => functions.push(function_def.into_mir(module_id)),
ExternFunction(signature) => {
let def = mir::FunctionDefinition {
name: signature.name.clone(),
documentation: signature.documentation.clone(),
linkage_name: None,
is_pub: false,
is_imported: false,
return_type: signature
.return_type
.clone()
.map(|r| r.0.into_mir(module_id))
.map(|r| r.0.into())
.unwrap_or(mir::TypeKind::Void),
parameters: signature
.params
.args
.iter()
.cloned()
.map(|p| mir::FunctionParam {
name: p.0,
ty: p.1 .0.into_mir(module_id),
meta: p.2.as_meta(module_id),
})
.map(|p| (p.0, p.1.into()))
.collect(),
kind: mir::FunctionDefinitionKind::Extern(false),
source: Some(module_id),
signature_meta: signature.range.as_meta(module_id),
};
functions.push(def);
}
@ -79,7 +77,7 @@ impl ast::Module {
.map(|s| {
StructField(
s.name.clone(),
s.ty.clone().0.into_mir(module_id),
s.ty.clone().into(),
s.range.as_meta(module_id),
)
})
@ -88,112 +86,24 @@ impl ast::Module {
}
},
meta: (*range).as_meta(module_id),
source_module: module_id,
importer: None,
};
typedefs.push(def);
}
BinopDefinition(ast::BinopDefinition {
lhs,
op,
rhs,
return_ty,
block,
signature_range,
}) => {
binops.push(mir::BinopDefinition {
lhs: mir::FunctionParam {
name: lhs.0.clone(),
ty: lhs.1 .0.into_mir(module_id),
meta: lhs.2.as_meta(module_id),
},
op: op.mir(),
rhs: mir::FunctionParam {
name: rhs.0.clone(),
ty: rhs.1 .0.into_mir(module_id),
meta: rhs.2.as_meta(module_id),
},
return_type: return_ty.0.into_mir(module_id),
fn_kind: mir::FunctionDefinitionKind::Local(
block.into_mir(module_id),
block.2.as_meta(module_id),
),
meta: signature_range.as_meta(module_id),
exported: false,
});
}
AssociatedFunction(ty, function_definition) => {
for function_def in function_definition {
associated_functions.push((ty.0.into_mir(module_id), function_def.into_mir(module_id)));
}
}
}
}
mir::Module {
name: self.name.clone(),
module_id: module_id,
binop_defs: binops,
imports,
associated_functions,
functions,
globals: Vec::new(),
path: self.path.clone(),
is_main: self.is_main,
tokens: self.tokens,
typedefs,
}
}
}
impl ast::FunctionDefinition {
pub fn into_mir(&self, module_id: SourceModuleId) -> mir::FunctionDefinition {
let &ast::FunctionDefinition(signature, is_pub, block, range) = &self;
let mut params = Vec::new();
match &signature.self_kind {
ast::SelfKind::Borrow(ty) => params.push(mir::FunctionParam {
name: "self".to_owned(),
ty: mir::TypeKind::Borrow(Box::new(ty.0.into_mir(module_id)), false),
meta: ty.1.as_meta(module_id),
}),
ast::SelfKind::MutBorrow(ty) => params.push(mir::FunctionParam {
name: "self".to_owned(),
ty: mir::TypeKind::Borrow(Box::new(ty.0.into_mir(module_id)), true),
meta: ty.1.as_meta(module_id),
}),
ast::SelfKind::Owned(ty) => params.push(mir::FunctionParam {
name: "self".to_owned(),
ty: ty.0.into_mir(module_id),
meta: ty.1.as_meta(module_id),
}),
ast::SelfKind::None => {}
}
params.extend(signature.params.iter().cloned().map(|p| FunctionParam {
name: p.0,
ty: p.1 .0.into_mir(module_id),
meta: p.2.as_meta(module_id),
}));
mir::FunctionDefinition {
name: signature.name.clone(),
documentation: signature.documentation.clone(),
linkage_name: None,
is_pub: *is_pub,
is_imported: false,
return_type: signature
.return_type
.clone()
.map(|r| r.0.into_mir(module_id))
.unwrap_or(mir::TypeKind::Void),
parameters: params,
kind: mir::FunctionDefinitionKind::Local(block.into_mir(module_id), (range).as_meta(module_id)),
source: Some(module_id),
signature_meta: signature.range.as_meta(module_id),
}
}
}
impl ast::Block {
pub fn into_mir(&self, module_id: SourceModuleId) -> mir::Block {
let mut mir_statements = Vec::new();
@ -204,143 +114,36 @@ impl ast::Block {
mir::StmtKind::Let(
mir::NamedVariableRef(
s_let
.ty
.1
.clone()
.map(|t| t.0.into_mir(module_id))
.map(|t| t.0.into())
.unwrap_or(mir::TypeKind::Vague(mir::VagueType::Unknown)),
s_let.name.clone(),
s_let.name_range.as_meta(module_id),
s_let.0.clone(),
s_let.4.as_meta(module_id),
),
s_let.mutable,
s_let.value.process(module_id),
s_let.2,
s_let.3.process(module_id),
),
s_let.name_range + s_let.value.1,
s_let.4,
),
ast::BlockLevelStatement::Set(var_ref, expression, range) => (
StmtKind::Set(var_ref.process(module_id), expression.process(module_id)),
*range,
),
ast::BlockLevelStatement::Import { _i } => todo!(),
ast::BlockLevelStatement::Expression(e) => (StmtKind::Expression(e.process(module_id)), e.1),
ast::BlockLevelStatement::Expression(e) => {
(StmtKind::Expression(e.process(module_id)), e.1)
}
ast::BlockLevelStatement::Return(_, e) => {
if let Some(e) = e {
(StmtKind::Expression(e.process(module_id)), e.1)
} else {
panic!(); // Should never happen?
}
(StmtKind::Expression(e.process(module_id)), e.1)
}
ast::BlockLevelStatement::ForLoop(counter, counter_range, start, end, block) => {
let counter_var = NamedVariableRef(
mir::TypeKind::Vague(mir::VagueType::Unknown),
counter.clone(),
counter_range.as_meta(module_id),
);
let let_statement = mir::Statement(
StmtKind::Let(counter_var.clone(), true, start.process(module_id)),
start.1.as_meta(module_id),
);
let statement_range = counter_range.clone() + start.1 + end.1 + block.2;
let set_new = mir::Statement(
StmtKind::Set(
mir::Expression(
mir::ExprKind::Variable(counter_var.clone()),
counter_range.as_meta(module_id),
),
mir::Expression(
mir::ExprKind::BinOp(
mir::BinaryOperator::Add,
Box::new(mir::Expression(
mir::ExprKind::Variable(counter_var.clone()),
counter_range.as_meta(module_id),
)),
Box::new(mir::Expression(
mir::ExprKind::Literal(mir::Literal::Vague(mir::VagueLiteral::Number(1))),
counter_range.as_meta(module_id),
)),
mir::TypeKind::Vague(mir::VagueType::Unknown),
),
counter_range.as_meta(module_id),
),
),
counter_range.as_meta(module_id),
);
let mir_block = if let Some((ret_kind, ret_expr)) = &block.1 {
if *ret_kind == ReturnType::Soft {
if let Some(ret_expr) = ret_expr {
let mir_ret = ret_expr.process(module_id);
let mut clone = block.clone();
clone.1 = None;
let mut mir_block = clone.into_mir(module_id);
mir_block
.statements
.push(mir::Statement(StmtKind::Expression(mir_ret.clone()), mir_ret.1));
mir_block.statements.push(set_new);
mir_block
} else {
let mut mir_block = block.into_mir(module_id);
mir_block.statements.push(set_new);
mir_block
}
} else {
block.into_mir(module_id)
}
} else {
let mut mir_block = block.into_mir(module_id);
mir_block.statements.push(set_new);
mir_block
};
let while_statement = mir::Statement(
StmtKind::While(WhileStatement {
condition: mir::Expression(
mir::ExprKind::BinOp(
mir::BinaryOperator::Cmp(mir::CmpOperator::LT),
Box::new(mir::Expression(
mir::ExprKind::Variable(counter_var),
counter_range.as_meta(module_id),
)),
Box::new(end.process(module_id)),
mir::TypeKind::Vague(mir::VagueType::Unknown),
),
end.1.as_meta(module_id),
),
block: mir_block.clone(),
meta: (*counter_range + end.1 + block.2).as_meta(module_id),
}),
(*counter_range + end.1 + block.2).as_meta(module_id),
);
let inner_scope = StmtKind::Expression(mir::Expression(
mir::ExprKind::Block(mir::Block {
statements: vec![let_statement, while_statement],
return_expression: None,
meta: statement_range.as_meta(module_id),
}),
statement_range.as_meta(module_id),
));
(inner_scope, statement_range)
}
ast::BlockLevelStatement::WhileLoop(expression, block) => (
StmtKind::While(WhileStatement {
condition: expression.process(module_id),
block: block.into_mir(module_id),
meta: self.2.as_meta(module_id),
}),
expression.1 + block.2,
),
};
mir_statements.push(mir::Statement(kind, range.as_meta(module_id)));
}
let return_expression = if let Some(r) = &self.1 {
if let Some(expr) = &r.1 {
Some((r.0.into(), Some(Box::new(expr.process(module_id)))))
} else {
Some((ReturnKind::Hard, None))
}
Some((r.0.into(), Box::new(r.1.process(module_id))))
} else {
None
};
@ -375,29 +178,31 @@ impl ast::Expression {
binary_operator.mir(),
Box::new(lhs.process(module_id)),
Box::new(rhs.process(module_id)),
mir::TypeKind::Vague(mir::VagueType::Unknown),
),
ast::ExpressionKind::FunctionCall(fn_call_expr) => mir::ExprKind::FunctionCall(mir::FunctionCall {
name: fn_call_expr.name.clone(),
return_type: mir::TypeKind::Vague(mir::VagueType::Unknown),
parameters: fn_call_expr.params.iter().map(|e| e.process(module_id)).collect(),
meta: fn_call_expr.range.as_meta(module_id),
is_macro: fn_call_expr.is_macro,
}),
ast::ExpressionKind::BlockExpr(block) => mir::ExprKind::Block(block.into_mir(module_id)),
ast::ExpressionKind::FunctionCall(fn_call_expr) => {
mir::ExprKind::FunctionCall(mir::FunctionCall {
name: fn_call_expr.0.clone(),
return_type: mir::TypeKind::Vague(mir::VagueType::Unknown),
parameters: fn_call_expr
.1
.iter()
.map(|e| e.process(module_id))
.collect(),
meta: fn_call_expr.2.as_meta(module_id),
})
}
ast::ExpressionKind::BlockExpr(block) => {
mir::ExprKind::Block(block.into_mir(module_id))
}
ast::ExpressionKind::IfExpr(if_expression) => {
let cond = if_expression.0.process(module_id);
let then_block = if_expression.1.process(module_id);
let then_block = if_expression.1.into_mir(module_id);
let else_block = if let Some(el) = &if_expression.2 {
Some(el.process(module_id))
Some(el.into_mir(module_id))
} else {
None
};
mir::ExprKind::If(mir::IfExpression(
Box::new(cond),
Box::new(then_block),
Box::new(else_block),
))
mir::ExprKind::If(mir::IfExpression(Box::new(cond), then_block, else_block))
}
ast::ExpressionKind::Array(expressions) => {
mir::ExprKind::Array(expressions.iter().map(|e| e.process(module_id)).collect())
@ -408,96 +213,28 @@ impl ast::Expression {
Box::new(idx_expr.process(module_id)),
),
ast::ExpressionKind::StructExpression(struct_init) => mir::ExprKind::Struct(
CustomTypeKey(struct_init.name.clone(), module_id),
struct_init.name.clone(),
struct_init
.fields
.iter()
.map(|(n, e, r)| (n.clone(), e.process(module_id), r.as_meta(module_id)))
.map(|(n, e)| (n.clone(), e.process(module_id)))
.collect(),
),
ast::ExpressionKind::Accessed(expression, name, name_range) => mir::ExprKind::Accessed(
ast::ExpressionKind::Accessed(expression, name) => mir::ExprKind::Accessed(
Box::new(expression.process(module_id)),
mir::TypeKind::Vague(mir::VagueType::Unknown),
name.clone(),
name_range.as_meta(module_id),
),
ast::ExpressionKind::Borrow(expr, mutable) => {
mir::ExprKind::Borrow(Box::new(expr.process(module_id)), *mutable)
}
ast::ExpressionKind::Deref(expr) => mir::ExprKind::Deref(Box::new(expr.process(module_id))),
ast::ExpressionKind::UnaryOperation(unary_operator, expr) => match unary_operator {
ast::UnaryOperator::Plus => mir::ExprKind::BinOp(
mir::BinaryOperator::Add,
Box::new(mir::Expression(
mir::ExprKind::Literal(mir::Literal::Vague(mir::VagueLiteral::Number(0))),
expr.1.as_meta(module_id),
)),
Box::new(expr.process(module_id)),
mir::TypeKind::Vague(mir::VagueType::Unknown),
),
ast::UnaryOperator::Minus => mir::ExprKind::BinOp(
mir::BinaryOperator::Minus,
Box::new(mir::Expression(
mir::ExprKind::Literal(mir::Literal::Vague(mir::VagueLiteral::Number(0))),
expr.1.as_meta(module_id),
)),
Box::new(expr.process(module_id)),
mir::TypeKind::Vague(mir::VagueType::Unknown),
),
ast::UnaryOperator::Not => mir::ExprKind::BinOp(
mir::BinaryOperator::Cmp(mir::CmpOperator::EQ),
Box::new(expr.process(module_id)),
Box::new(mir::Expression(
mir::ExprKind::Literal(mir::Literal::Bool(false)),
expr.1.as_meta(module_id),
)),
mir::TypeKind::Bool,
),
},
ast::ExpressionKind::CastTo(expression, ty) => mir::ExprKind::CastTo(
Box::new(expression.process(module_id)),
ty.0.clone().into_mir(module_id),
),
ast::ExpressionKind::ArrayShort(expression, len) => mir::ExprKind::Array(
vec![*expression.clone(); *len as usize]
.iter()
.map(|e| e.process(module_id))
.collect(),
),
ast::ExpressionKind::AssociatedFunctionCall(ty, fn_call_expr) => mir::ExprKind::AssociatedFunctionCall(
ty.0.into_mir(module_id),
mir::FunctionCall {
name: fn_call_expr.name.clone(),
return_type: mir::TypeKind::Vague(mir::VagueType::Unknown),
parameters: fn_call_expr.params.iter().map(|e| e.process(module_id)).collect(),
meta: fn_call_expr.range.as_meta(module_id),
is_macro: fn_call_expr.is_macro,
},
),
ast::ExpressionKind::AccessCall(expression, fn_call_expr) => {
let mut params: Vec<_> = fn_call_expr.params.iter().map(|e| e.process(module_id)).collect();
params.insert(
0,
mir::Expression(
mir::ExprKind::Borrow(Box::new(expression.process(module_id)), true),
expression.1.as_meta(module_id),
),
);
if fn_call_expr.is_macro {
panic!("Macros aren't supported as access-calls!");
};
mir::ExprKind::AssociatedFunctionCall(
mir::TypeKind::Vague(mir::VagueType::Unknown),
mir::FunctionCall {
name: fn_call_expr.name.clone(),
return_type: mir::TypeKind::Vague(mir::VagueType::Unknown),
parameters: params,
meta: fn_call_expr.range.as_meta(module_id),
is_macro: fn_call_expr.is_macro,
},
)
}
ast::ExpressionKind::Borrow(name) => mir::ExprKind::Borrow(NamedVariableRef(
mir::TypeKind::Vague(mir::VagueType::Unknown),
name.clone(),
self.1.as_meta(module_id),
)),
ast::ExpressionKind::Deref(name) => mir::ExprKind::Deref(NamedVariableRef(
mir::TypeKind::Vague(mir::VagueType::Unknown),
name.clone(),
self.1.as_meta(module_id),
)),
};
mir::Expression(kind, self.1.as_meta(module_id))
@ -510,21 +247,13 @@ impl ast::BinaryOperator {
ast::BinaryOperator::Add => mir::BinaryOperator::Add,
ast::BinaryOperator::Minus => mir::BinaryOperator::Minus,
ast::BinaryOperator::Mult => mir::BinaryOperator::Mult,
ast::BinaryOperator::Div => mir::BinaryOperator::Div,
ast::BinaryOperator::Mod => mir::BinaryOperator::Mod,
ast::BinaryOperator::And => mir::BinaryOperator::And,
ast::BinaryOperator::Or => mir::BinaryOperator::Or,
ast::BinaryOperator::LT => mir::BinaryOperator::Cmp(mir::CmpOperator::LT),
ast::BinaryOperator::LE => mir::BinaryOperator::Cmp(mir::CmpOperator::LE),
ast::BinaryOperator::GT => mir::BinaryOperator::Cmp(mir::CmpOperator::GT),
ast::BinaryOperator::GE => mir::BinaryOperator::Cmp(mir::CmpOperator::GE),
ast::BinaryOperator::EQ => mir::BinaryOperator::Cmp(mir::CmpOperator::EQ),
ast::BinaryOperator::NE => mir::BinaryOperator::Cmp(mir::CmpOperator::NE),
ast::BinaryOperator::BitshiftRight => mir::BinaryOperator::BitshiftRight,
ast::BinaryOperator::BitshiftLeft => mir::BinaryOperator::BitshiftLeft,
ast::BinaryOperator::Xor => mir::BinaryOperator::Xor,
ast::BinaryOperator::BitAnd => mir::BinaryOperator::BitAnd,
ast::BinaryOperator::BitOr => mir::BinaryOperator::BitOr,
}
}
}
@ -532,37 +261,16 @@ impl ast::BinaryOperator {
impl ast::Literal {
fn mir(&self) -> mir::Literal {
match &self {
ast::Literal::Integer(v) => mir::Literal::Vague(mir::VagueLiteral::Number(*v)),
ast::Literal::Number(v) => mir::Literal::Vague(mir::VagueLiteral::Number(*v)),
ast::Literal::Bool(v) => mir::Literal::Bool(*v),
ast::Literal::String(val) => mir::Literal::String(val.clone()),
ast::Literal::Decimal(v) => mir::Literal::Vague(mir::VagueLiteral::Decimal(*v)),
ast::Literal::Char(inner) => mir::Literal::Char(*inner),
ast::Literal::Specific(specific_literal) => match specific_literal {
ast::SpecificLiteral::I8(val) => mir::Literal::I8(*val),
ast::SpecificLiteral::I16(val) => mir::Literal::I16(*val),
ast::SpecificLiteral::I32(val) => mir::Literal::I32(*val),
ast::SpecificLiteral::I64(val) => mir::Literal::I64(*val),
ast::SpecificLiteral::I128(val) => mir::Literal::I128(*val),
ast::SpecificLiteral::U8(val) => mir::Literal::U8(*val),
ast::SpecificLiteral::U16(val) => mir::Literal::U16(*val),
ast::SpecificLiteral::U32(val) => mir::Literal::U32(*val),
ast::SpecificLiteral::U64(val) => mir::Literal::U64(*val),
ast::SpecificLiteral::U128(val) => mir::Literal::U128(*val),
ast::SpecificLiteral::F16(val) => mir::Literal::F16(*val),
ast::SpecificLiteral::F32(val) => mir::Literal::F32(*val),
ast::SpecificLiteral::F32B(val) => mir::Literal::F32B(*val),
ast::SpecificLiteral::F64(val) => mir::Literal::F64(*val),
ast::SpecificLiteral::F80(val) => mir::Literal::F80(*val),
ast::SpecificLiteral::F128(val) => mir::Literal::F128(*val),
ast::SpecificLiteral::F128PPC(val) => mir::Literal::F128PPC(*val),
},
}
}
}
impl ast::TypeKind {
fn into_mir(&self, source_mod: SourceModuleId) -> mir::TypeKind {
match &self {
impl From<ast::TypeKind> for mir::TypeKind {
fn from(value: ast::TypeKind) -> Self {
match &value {
ast::TypeKind::Bool => mir::TypeKind::Bool,
ast::TypeKind::I8 => mir::TypeKind::I8,
ast::TypeKind::I16 => mir::TypeKind::I16,
@ -575,22 +283,16 @@ impl ast::TypeKind {
ast::TypeKind::U64 => mir::TypeKind::U64,
ast::TypeKind::U128 => mir::TypeKind::U128,
ast::TypeKind::Array(type_kind, length) => {
mir::TypeKind::Array(Box::new(type_kind.clone().into_mir(source_mod)), *length)
mir::TypeKind::Array(Box::new(mir::TypeKind::from(*type_kind.clone())), *length)
}
ast::TypeKind::Custom(name) => mir::TypeKind::CustomType(CustomTypeKey(name.clone(), source_mod)),
ast::TypeKind::Borrow(type_kind, mutable) => {
mir::TypeKind::Borrow(Box::new(type_kind.clone().into_mir(source_mod)), *mutable)
}
ast::TypeKind::Ptr(type_kind) => mir::TypeKind::UserPtr(Box::new(type_kind.clone().into_mir(source_mod))),
ast::TypeKind::F16 => mir::TypeKind::F16,
ast::TypeKind::F32B => mir::TypeKind::F16B,
ast::TypeKind::F32 => mir::TypeKind::F32,
ast::TypeKind::F64 => mir::TypeKind::F64,
ast::TypeKind::F80 => mir::TypeKind::F80,
ast::TypeKind::F128 => mir::TypeKind::F128,
ast::TypeKind::F128PPC => mir::TypeKind::F128PPC,
ast::TypeKind::Char => mir::TypeKind::Char,
ast::TypeKind::Unknown => mir::TypeKind::Vague(mir::VagueType::Unknown),
ast::TypeKind::String => mir::TypeKind::StringPtr,
ast::TypeKind::Custom(name) => mir::TypeKind::CustomType(name.clone()),
}
}
}
impl From<ast::Type> for mir::TypeKind {
fn from(value: ast::Type) -> Self {
value.0.into()
}
}

1316
reid/src/codegen.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,223 +0,0 @@
use std::collections::HashMap;
use reid_lib::{
builder::{InstructionValue, TypeValue},
Block, Instr,
};
use mir::{CustomTypeKey, FunctionDefinitionKind, IfExpression, TypeKind, WhileStatement};
use crate::mir::{self, FunctionParam, Metadata, SourceModuleId};
#[derive(Debug)]
pub struct Allocator {
allocations: Vec<Allocation>,
}
pub struct AllocatorScope<'ctx, 'a> {
pub(super) block: &'a mut Block<'ctx>,
pub(super) mod_id: SourceModuleId,
pub(super) type_values: &'a HashMap<CustomTypeKey, TypeValue>,
}
impl Allocator {
pub fn from(func: &FunctionDefinitionKind, params: &Vec<FunctionParam>, scope: &mut AllocatorScope) -> Allocator {
func.allocate(scope, params)
}
pub fn allocate(&mut self, meta: &Metadata, ty: &TypeKind) -> Option<InstructionValue> {
let mut allocs = self.allocations.iter().cloned().enumerate();
let val = allocs.find(|a| a.1 .0 == *meta && a.1 .1 == *ty);
if let Some((i, _)) = val {
self.allocations.remove(i);
}
val.map(|v| v.1 .2)
}
}
#[derive(Clone, Debug)]
pub struct Allocation(Metadata, TypeKind, InstructionValue);
impl mir::FunctionDefinitionKind {
fn allocate<'ctx, 'a>(
&self,
scope: &mut AllocatorScope<'ctx, 'a>,
parameters: &Vec<mir::FunctionParam>,
) -> Allocator {
let mut allocated = Vec::new();
match &self {
mir::FunctionDefinitionKind::Local(block, _) => {
for param in parameters {
let allocation = scope
.block
.build_named(
param.name.clone(),
reid_lib::Instr::Alloca(param.ty.get_type(scope.type_values)),
)
.unwrap();
allocated.push(Allocation(param.meta, param.ty.clone(), allocation));
}
allocated.extend(block.allocate(scope));
}
mir::FunctionDefinitionKind::Extern(_) => {}
mir::FunctionDefinitionKind::Intrinsic(_) => {}
}
Allocator { allocations: allocated }
}
}
impl mir::Block {
fn allocate<'ctx, 'a>(&self, scope: &mut AllocatorScope<'ctx, 'a>) -> Vec<Allocation> {
let mut allocated = Vec::new();
for statement in &self.statements {
allocated.extend(statement.allocate(scope));
}
if let Some((_, ret_expr)) = &self.return_expression {
if let Some(ret_expr) = ret_expr {
allocated.extend(ret_expr.allocate(scope));
}
}
allocated
}
}
impl mir::Statement {
fn allocate<'ctx, 'a>(&self, scope: &mut AllocatorScope<'ctx, 'a>) -> Vec<Allocation> {
let mut allocated = Vec::new();
match &self.0 {
mir::StmtKind::Let(named_variable_ref, _, expression) => {
allocated.extend(expression.allocate(scope));
let allocation = scope
.block
.build_named(
named_variable_ref.1.clone(),
reid_lib::Instr::Alloca(named_variable_ref.0.get_type(scope.type_values)),
)
.unwrap();
allocated.push(Allocation(
named_variable_ref.2,
named_variable_ref.0.clone(),
allocation,
));
}
mir::StmtKind::Set(lhs, rhs) => {
allocated.extend(lhs.allocate(scope));
allocated.extend(rhs.allocate(scope));
}
mir::StmtKind::Import(_) => {}
mir::StmtKind::Expression(expression) => {
allocated.extend(expression.allocate(scope));
}
mir::StmtKind::While(WhileStatement { condition, block, .. }) => {
allocated.extend(condition.allocate(scope));
allocated.extend(block.allocate(scope));
}
}
allocated
}
}
impl mir::Expression {
fn allocate<'ctx, 'a>(&self, scope: &mut AllocatorScope<'ctx, 'a>) -> Vec<Allocation> {
let mut allocated = Vec::new();
match &self.0 {
mir::ExprKind::Variable(_) => {}
mir::ExprKind::Indexed(expr, _, idx) => {
allocated.extend(expr.allocate(scope));
allocated.extend(idx.allocate(scope));
}
mir::ExprKind::Accessed(expression, ..) => {
allocated.extend(expression.allocate(scope));
}
mir::ExprKind::Array(expressions) => {
let (_, ty) = self.return_type(&Default::default(), scope.mod_id).unwrap();
let TypeKind::Array(elem_ty, _) = &ty else { panic!() };
let array_name = format!("{}.{}", elem_ty, expressions.len());
let allocation = scope
.block
.build_named(array_name, Instr::Alloca(ty.get_type(scope.type_values)))
.unwrap();
allocated.push(Allocation(self.1, ty, allocation));
for expression in expressions {
allocated.extend(expression.allocate(scope));
}
}
mir::ExprKind::Struct(key, items) => {
let (_, ty) = self.return_type(&Default::default(), scope.mod_id).unwrap();
let allocation = scope
.block
.build_named(key.0.clone(), Instr::Alloca(ty.get_type(scope.type_values)))
.unwrap();
allocated.push(Allocation(self.1, ty, allocation));
for (field_name, expression, _) in items {
allocated.extend(expression.allocate(scope));
let (_, ty) = expression.return_type(&Default::default(), scope.mod_id).unwrap();
let allocation = scope
.block
.build_named(field_name, Instr::Alloca(ty.get_type(scope.type_values)))
.unwrap();
allocated.push(Allocation(expression.1, ty, allocation));
}
}
mir::ExprKind::Literal(_) => {}
mir::ExprKind::BinOp(_, lhs, rhs, _) => {
allocated.extend(lhs.allocate(scope));
allocated.extend(rhs.allocate(scope));
}
mir::ExprKind::FunctionCall(fn_call) => allocated.extend(fn_call.allocate(&fn_call.name, scope)),
mir::ExprKind::If(IfExpression(cond, then_ex, else_ex)) => {
allocated.extend(cond.allocate(scope));
allocated.extend(then_ex.allocate(scope));
if let Some(else_ex) = else_ex.as_ref() {
allocated.extend(else_ex.allocate(scope));
}
}
mir::ExprKind::Block(block) => {
allocated.extend(block.allocate(scope));
}
mir::ExprKind::Borrow(_, _) => {}
mir::ExprKind::Deref(_) => {}
mir::ExprKind::CastTo(expression, _) => {
allocated.extend(expression.allocate(scope));
}
mir::ExprKind::AssociatedFunctionCall(ty, fn_call) => {
allocated.extend(fn_call.allocate(&format!("{}::{}", ty, fn_call.name), scope))
}
mir::ExprKind::GlobalRef(..) => {}
}
allocated
}
}
impl mir::FunctionCall {
fn allocate<'ctx, 'a>(&self, name: &String, scope: &mut AllocatorScope<'ctx, 'a>) -> Vec<Allocation> {
let mut allocated = Vec::new();
for param in &self.parameters {
allocated.extend(param.allocate(scope));
}
if self.return_type != TypeKind::Void {
let allocation = scope
.block
.build_named(name, Instr::Alloca(self.return_type.get_type(scope.type_values)))
.unwrap();
allocated.push(Allocation(self.meta, self.return_type.clone(), allocation));
}
allocated
}
}

View File

@ -1,918 +0,0 @@
use reid_lib::{builder::InstructionValue, CmpPredicate, ConstValueKind, Instr, Type};
use crate::{
codegen::{ErrorKind, StackValueKind},
mir::{
implement::TypeCategory, BinaryOperator, BinopDefinition, CmpOperator, FunctionDefinition,
FunctionDefinitionKind, FunctionParam, TypeKind,
},
};
use super::scope::{Scope, StackValue};
const INTEGERS: [TypeKind; 10] = [
TypeKind::U8,
TypeKind::U16,
TypeKind::U32,
TypeKind::U64,
TypeKind::U128,
TypeKind::I8,
TypeKind::I16,
TypeKind::I32,
TypeKind::I64,
TypeKind::I128,
];
const FLOATS: [TypeKind; 7] = [
TypeKind::F16,
TypeKind::F32,
TypeKind::F16B,
TypeKind::F64,
TypeKind::F80,
TypeKind::F128,
TypeKind::F128PPC,
];
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub enum LLVMIntrinsicKind {
Max(TypeKind),
Min(TypeKind),
Abs(TypeKind),
Memcpy(TypeKind),
Sqrt(TypeKind),
PowI(TypeKind, TypeKind),
Pow(TypeKind),
Sin(TypeKind),
Cos(TypeKind),
Tan(TypeKind),
ASin(TypeKind),
ACos(TypeKind),
ATan(TypeKind),
ATan2(TypeKind),
SinH(TypeKind),
CosH(TypeKind),
TanH(TypeKind),
Log(TypeKind),
Log2(TypeKind),
Log10(TypeKind),
Copysign(TypeKind),
Floor(TypeKind),
Ceil(TypeKind),
Trunc(TypeKind),
RoundEven(TypeKind),
Round(TypeKind),
}
const INTRINSIC_IDENT: &str = "reid.intrinsic";
const MALLOC_IDENT: &str = "malloc";
macro_rules! doc {
($str:expr) => {
Some($str.to_string())
};
}
pub fn form_intrinsics() -> Vec<FunctionDefinition> {
let mut intrinsics = Vec::new();
intrinsics.push(FunctionDefinition {
name: MALLOC_IDENT.to_owned(),
documentation: doc!("Allocates `size` bytes and returns a `u8`-pointer."),
linkage_name: Some("malloc".to_owned()),
is_pub: false,
is_imported: true,
return_type: TypeKind::UserPtr(Box::new(TypeKind::U8)),
parameters: vec![FunctionParam {
name: "size".to_owned(),
ty: TypeKind::U64,
meta: Default::default(),
}],
kind: FunctionDefinitionKind::Extern(false),
source: None,
signature_meta: Default::default(),
});
intrinsics
}
pub fn simple_intrinsic<T: Into<String> + Clone>(
name: T,
doc: T,
params: Vec<T>,
ret: TypeKind,
intrisic: LLVMIntrinsicKind,
) -> FunctionDefinition {
FunctionDefinition {
name: name.into(),
documentation: Some(doc.into()),
linkage_name: None,
is_pub: true,
is_imported: false,
return_type: ret.clone(),
parameters: params
.iter()
.map(|p| FunctionParam::from(p.clone(), ret.clone()))
.collect(),
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicLLVM(intrisic, ret.clone()))),
source: None,
signature_meta: Default::default(),
}
}
pub fn get_intrinsic_assoc_functions(ty: &TypeKind) -> Vec<FunctionDefinition> {
let mut intrinsics = Vec::new();
if let TypeKind::Array(_, len) = ty {
intrinsics.push(FunctionDefinition {
name: "length".to_owned(),
documentation: doc!("Returns the length of this given array"),
linkage_name: None,
is_pub: true,
is_imported: false,
return_type: TypeKind::U64,
parameters: vec![FunctionParam {
name: String::from("self"),
ty: TypeKind::Borrow(Box::new(ty.clone()), false),
meta: Default::default(),
}],
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicConst(*len))),
source: None,
signature_meta: Default::default(),
});
}
if ty.category() == TypeCategory::Real {
intrinsics.push(simple_intrinsic(
"sqrt",
"Calculates the square-root of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Sqrt(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"sin",
"Calculates sine of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Sin(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"cos",
"Calculates cosine of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Cos(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"tan",
"Calculates tangent of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Tan(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"sinh",
"Calculates hyperbolic sine of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::SinH(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"cosh",
"Calculates hyperbolic cosine of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::CosH(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"tanh",
"Calculates hyperbolic tangent of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::TanH(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"asin",
"Calculates arcsine of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::ASin(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"acos",
"Calculates arccosine of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::ACos(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"atan",
"Calculates arctangent of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::ATan(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"atan2",
"Calculates 2-argument arctangent of `value`",
vec!["self", "other"],
ty.clone(),
LLVMIntrinsicKind::ATan2(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"log",
"Returns logₑ of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Log(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"log2",
"Returns log₂ of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Log2(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"log10",
"Returns log₁₀ of `value`",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Log10(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"floor",
"Rounds `value` towards negative infinity.",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Floor(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"ceil",
"Rounds `value` towards positive infinity.",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Ceil(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"trunc",
"Truncates `value` to the integer nearest to `0`.",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Trunc(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"round",
"Rounds `value` to the closest even integer.",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::Round(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"even",
"Rounds `value` to the closest even integer.",
vec!["self"],
ty.clone(),
LLVMIntrinsicKind::RoundEven(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"pow",
"Returns `value` raised to the exponent of `exponent`.",
vec!["self", "exponent"],
ty.clone(),
LLVMIntrinsicKind::Pow(ty.clone()),
));
intrinsics.push(FunctionDefinition {
name: "powi".to_owned(),
documentation: doc!("Returns `value` raised to the exponent of `exponent`."),
linkage_name: None,
is_pub: true,
is_imported: false,
return_type: ty.clone(),
parameters: vec![
FunctionParam {
name: String::from("self"),
ty: ty.clone(),
meta: Default::default(),
},
FunctionParam {
name: String::from("exponent"),
ty: TypeKind::U32,
meta: Default::default(),
},
],
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicLLVM(
LLVMIntrinsicKind::PowI(ty.clone(), TypeKind::U32),
ty.clone(),
))),
source: None,
signature_meta: Default::default(),
});
}
match ty.category() {
TypeCategory::Integer | TypeCategory::Real | TypeCategory::Bool => {
intrinsics.push(simple_intrinsic(
"max",
"Returns the larger of `a` and `b`.",
vec!["self", "other"],
ty.clone(),
LLVMIntrinsicKind::Max(ty.clone()),
));
intrinsics.push(simple_intrinsic(
"min",
"Returns the smaller of `a` and `b`.",
vec!["self", "other"],
ty.clone(),
LLVMIntrinsicKind::Min(ty.clone()),
));
if ty.signed() {
intrinsics.push(FunctionDefinition {
name: "abs".to_owned(),
documentation: doc!("Returns the absolute value of `value`."),
linkage_name: None,
is_pub: true,
is_imported: false,
return_type: ty.clone(),
parameters: vec![FunctionParam {
name: String::from("self"),
ty: ty.clone(),
meta: Default::default(),
}],
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSimpleUnaryInstr({
let ty = ty.clone();
|scope, param| {
let intrinsic = scope.get_intrinsic(LLVMIntrinsicKind::Abs(ty));
let constant = scope.block.build(Instr::Constant(ConstValueKind::Bool(false))).unwrap();
let value = scope
.block
.build(Instr::FunctionCall(intrinsic, vec![param, constant]))
.unwrap();
value
}
}))),
source: None,
signature_meta: Default::default(),
});
}
}
_ => {}
}
intrinsics.push(FunctionDefinition {
name: "sizeof".to_owned(),
documentation: doc!("Simply returns the size of type `T` in bytes."),
linkage_name: None,
is_pub: true,
is_imported: false,
return_type: TypeKind::U64,
parameters: Vec::new(),
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSizeOf(ty.clone()))),
source: None,
signature_meta: Default::default(),
});
intrinsics.push(FunctionDefinition {
name: "malloc".to_owned(),
documentation: doc!("Allocates `T::sizeof() * size` bytes and returns a pointer to `T`."),
linkage_name: None,
is_pub: true,
is_imported: false,
return_type: TypeKind::UserPtr(Box::new(ty.clone())),
parameters: vec![FunctionParam {
name: String::from("size"),
ty: TypeKind::U64,
meta: Default::default(),
}],
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicMalloc(ty.clone()))),
source: None,
signature_meta: Default::default(),
});
intrinsics.push(FunctionDefinition {
name: "memcpy".to_owned(),
documentation: doc!(
"Copies `T::sizeof() * size` bytes from pointer `source` to pointer
`destination`."
),
linkage_name: None,
is_pub: true,
is_imported: false,
return_type: TypeKind::Void,
parameters: vec![
FunctionParam {
name: String::from("destination"),
ty: TypeKind::UserPtr(Box::new(ty.clone())),
meta: Default::default(),
},
FunctionParam {
name: String::from("source"),
ty: TypeKind::UserPtr(Box::new(ty.clone())),
meta: Default::default(),
},
FunctionParam {
name: String::from("length"),
ty: TypeKind::U64,
meta: Default::default(),
},
],
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicMemcpy(ty.clone()))),
source: None,
signature_meta: Default::default(),
});
intrinsics.push(FunctionDefinition {
name: "null".to_owned(),
documentation: doc!("Returns a null-pointer of type `T`."),
linkage_name: None,
is_pub: true,
is_imported: false,
return_type: TypeKind::UserPtr(Box::new(ty.clone())),
parameters: Vec::new(),
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicNullPtr(ty.clone()))),
source: None,
signature_meta: Default::default(),
});
intrinsics.push(FunctionDefinition {
name: "is_null".to_owned(),
documentation: doc!("Returns a boolean representing if `val` is a nullptr or not."),
linkage_name: None,
is_pub: true,
is_imported: false,
return_type: TypeKind::Bool,
parameters: vec![FunctionParam {
name: "value".to_string(),
ty: TypeKind::UserPtr(Box::new(ty.clone())),
meta: Default::default(),
}],
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicIsNull)),
source: None,
signature_meta: Default::default(),
});
intrinsics
}
pub fn get_intrinsic_assoc_func(ty: &TypeKind, name: &str) -> Option<FunctionDefinition> {
get_intrinsic_assoc_functions(ty).into_iter().find(|f| f.name == name)
}
fn simple_binop_def<T: Clone + 'static>(op: BinaryOperator, ty: &TypeKind, fun: T) -> BinopDefinition
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
{
BinopDefinition {
lhs: FunctionParam {
name: "lhs".to_owned(),
ty: ty.clone(),
meta: Default::default(),
},
op,
rhs: FunctionParam {
name: "rhs".to_owned(),
ty: ty.clone(),
meta: Default::default(),
},
return_type: ty.clone(),
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSimpleBinaryInstr(fun))),
meta: Default::default(),
exported: false,
}
}
fn complex_binop_def<T: Clone + 'static>(op: BinaryOperator, lhs: &TypeKind, rhs: &TypeKind, fun: T) -> BinopDefinition
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
{
BinopDefinition {
lhs: FunctionParam {
name: "lhs".to_owned(),
ty: lhs.clone(),
meta: Default::default(),
},
op,
rhs: FunctionParam {
name: "rhs".to_owned(),
ty: rhs.clone(),
meta: Default::default(),
},
return_type: lhs.clone(),
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSimpleBinaryInstr(fun))),
meta: Default::default(),
exported: false,
}
}
fn boolean_binop_def<T: Clone + 'static>(op: BinaryOperator, ty: &TypeKind, fun: T) -> BinopDefinition
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
{
BinopDefinition {
lhs: FunctionParam {
name: "lhs".to_owned(),
ty: ty.clone(),
meta: Default::default(),
},
op,
rhs: FunctionParam {
name: "rhs".to_owned(),
ty: ty.clone(),
meta: Default::default(),
},
return_type: TypeKind::Bool,
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicBooleanInstr(fun))),
meta: Default::default(),
exported: false,
}
}
pub fn form_intrinsic_binops() -> Vec<BinopDefinition> {
let mut intrinsics = Vec::new();
use BinaryOperator::*;
for ty in INTEGERS {
intrinsics.push(simple_binop_def(Add, &ty, |scope, lhs, rhs| {
scope.block.build(Instr::Add(lhs, rhs)).unwrap()
}));
intrinsics.push(simple_binop_def(Mult, &ty, |scope, lhs, rhs| {
scope.block.build(Instr::Mul(lhs, rhs)).unwrap()
}));
intrinsics.push(simple_binop_def(Minus, &ty, |scope, lhs, rhs| {
scope.block.build(Instr::Sub(lhs, rhs)).unwrap()
}));
if ty.signed() {
intrinsics.push(simple_binop_def(Div, &ty, |scope, lhs, rhs| {
scope.block.build(Instr::SDiv(lhs, rhs)).unwrap()
}));
intrinsics.push(simple_binop_def(Mod, &ty, |scope, lhs, rhs| {
let div = scope.block.build(Instr::SDiv(lhs, rhs)).unwrap();
let mul = scope.block.build(Instr::Mul(rhs, div)).unwrap();
scope.block.build(Instr::Sub(lhs, mul)).unwrap()
}));
} else {
intrinsics.push(simple_binop_def(Div, &ty, |scope, lhs, rhs| {
scope.block.build(Instr::UDiv(lhs, rhs)).unwrap()
}));
intrinsics.push(simple_binop_def(Mod, &ty, |scope, lhs, rhs| {
let div = scope.block.build(Instr::UDiv(lhs, rhs)).unwrap();
let mul = scope.block.build(Instr::Mul(rhs, div)).unwrap();
scope.block.build(Instr::Sub(lhs, mul)).unwrap()
}));
}
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::GT), &ty, |scope, lhs, rhs| {
scope.block.build(Instr::ICmp(CmpPredicate::GT, lhs, rhs)).unwrap()
}));
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::GE), &ty, |scope, lhs, rhs| {
scope.block.build(Instr::ICmp(CmpPredicate::GE, lhs, rhs)).unwrap()
}));
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::LT), &ty, |scope, lhs, rhs| {
scope.block.build(Instr::ICmp(CmpPredicate::LT, lhs, rhs)).unwrap()
}));
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::LE), &ty, |scope, lhs, rhs| {
scope.block.build(Instr::ICmp(CmpPredicate::LE, lhs, rhs)).unwrap()
}));
// Bitwise operations
intrinsics.push(simple_binop_def(BitOr, &ty, |scope, lhs, rhs| {
scope.block.build(Instr::Or(lhs, rhs)).unwrap()
}));
intrinsics.push(simple_binop_def(BitAnd, &ty, |scope, lhs, rhs| {
scope.block.build(Instr::And(lhs, rhs)).unwrap()
}));
intrinsics.push(complex_binop_def(Xor, &ty, &TypeKind::U64, |scope, lhs, rhs| {
scope.block.build(Instr::XOr(lhs, rhs)).unwrap()
}));
if ty.signed() {
intrinsics.push(complex_binop_def(BitshiftRight, &ty, &ty, |scope, lhs, rhs| {
scope.block.build(Instr::ShiftRightArithmetic(lhs, rhs)).unwrap()
}));
} else {
intrinsics.push(complex_binop_def(BitshiftRight, &ty, &ty, |scope, lhs, rhs| {
scope.block.build(Instr::ShiftRightLogical(lhs, rhs)).unwrap()
}));
}
intrinsics.push(complex_binop_def(BitshiftLeft, &ty, &ty, |scope, lhs, rhs| {
scope.block.build(Instr::ShiftLeft(lhs, rhs)).unwrap()
}));
}
for ty in INTEGERS.iter().chain(&[TypeKind::Bool, TypeKind::Char]) {
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::EQ), &ty, |scope, lhs, rhs| {
scope.block.build(Instr::ICmp(CmpPredicate::EQ, lhs, rhs)).unwrap()
}));
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::NE), &ty, |scope, lhs, rhs| {
scope.block.build(Instr::ICmp(CmpPredicate::NE, lhs, rhs)).unwrap()
}));
}
for ty in FLOATS {
intrinsics.push(simple_binop_def(BinaryOperator::Add, &ty, |scope, lhs, rhs| {
scope.block.build(Instr::FAdd(lhs, rhs)).unwrap()
}));
intrinsics.push(simple_binop_def(BinaryOperator::Mult, &ty, |scope, lhs, rhs| {
scope.block.build(Instr::FMul(lhs, rhs)).unwrap()
}));
intrinsics.push(simple_binop_def(BinaryOperator::Minus, &ty, |scope, lhs, rhs| {
scope.block.build(Instr::FSub(lhs, rhs)).unwrap()
}));
intrinsics.push(simple_binop_def(Div, &ty, |scope, lhs, rhs| {
scope.block.build(Instr::FDiv(lhs, rhs)).unwrap()
}));
intrinsics.push(simple_binop_def(Mod, &ty, {
let ty = ty.clone();
|scope, lhs, rhs| {
let div = scope.block.build(Instr::FDiv(lhs, rhs)).unwrap();
let fun = scope.get_intrinsic(LLVMIntrinsicKind::Trunc(ty));
let div_truncated = scope.block.build(Instr::FunctionCall(fun, vec![div])).unwrap();
let mul = scope.block.build(Instr::FMul(rhs, div_truncated)).unwrap();
scope.block.build(Instr::FSub(lhs, mul)).unwrap()
}
}));
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::NE), &ty, |scope, lhs, rhs| {
scope.block.build(Instr::FCmp(CmpPredicate::NE, lhs, rhs)).unwrap()
}));
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::EQ), &ty, |scope, lhs, rhs| {
scope.block.build(Instr::FCmp(CmpPredicate::EQ, lhs, rhs)).unwrap()
}));
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::GT), &ty, |scope, lhs, rhs| {
scope.block.build(Instr::FCmp(CmpPredicate::GT, lhs, rhs)).unwrap()
}));
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::GE), &ty, |scope, lhs, rhs| {
scope.block.build(Instr::FCmp(CmpPredicate::GE, lhs, rhs)).unwrap()
}));
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::LT), &ty, |scope, lhs, rhs| {
scope.block.build(Instr::FCmp(CmpPredicate::LT, lhs, rhs)).unwrap()
}));
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::LE), &ty, |scope, lhs, rhs| {
scope.block.build(Instr::FCmp(CmpPredicate::LE, lhs, rhs)).unwrap()
}));
}
intrinsics.push(boolean_binop_def(And, &TypeKind::Bool, |scope, lhs, rhs| {
scope.block.build(Instr::And(lhs, rhs)).unwrap()
}));
intrinsics.push(boolean_binop_def(Or, &TypeKind::Bool, |scope, lhs, rhs| {
scope.block.build(Instr::Or(lhs, rhs)).unwrap()
}));
intrinsics.push(boolean_binop_def(Xor, &TypeKind::Bool, |scope, lhs, rhs| {
scope.block.build(Instr::XOr(lhs, rhs)).unwrap()
}));
intrinsics
}
pub trait IntrinsicFunction: std::fmt::Debug {
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, params: &[StackValue]) -> Result<StackValue, ErrorKind>;
}
macro_rules! intrinsic_debug {
($kind:ty, $name:literal) => {
impl<T> std::fmt::Debug for $kind
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple($name).finish()
}
}
};
}
#[derive(Clone)]
pub struct IntrinsicSimpleUnaryInstr<T>(T)
where
T: FnOnce(&mut Scope, InstructionValue) -> InstructionValue;
impl<T> std::fmt::Debug for IntrinsicSimpleUnaryInstr<T>
where
T: FnOnce(&mut Scope, InstructionValue) -> InstructionValue,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("IntrinsicSimpleUnaryInstr").finish()
}
}
impl<T: Clone> IntrinsicFunction for IntrinsicSimpleUnaryInstr<T>
where
T: FnOnce(&mut Scope, InstructionValue) -> InstructionValue,
{
fn codegen<'b, 'c>(&self, scope: &mut Scope<'b, 'c>, params: &[StackValue]) -> Result<StackValue, ErrorKind> {
let param = params.get(0).unwrap();
let instr = self.clone().0(scope, param.instr());
Ok(StackValue(StackValueKind::Literal(instr), param.1.clone()))
}
}
#[derive(Clone)]
pub struct IntrinsicSimpleBinaryInstr<T>(T)
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue;
intrinsic_debug!(IntrinsicSimpleBinaryInstr<T>, "IntrinsicSimpleBinaryInstr");
impl<T: Clone> IntrinsicFunction for IntrinsicSimpleBinaryInstr<T>
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
{
fn codegen<'b, 'c>(&self, scope: &mut Scope<'b, 'c>, params: &[StackValue]) -> Result<StackValue, ErrorKind> {
let lhs = params.get(0).unwrap();
let rhs = params.get(1).unwrap();
let instr = self.clone().0(scope, lhs.instr(), rhs.instr());
Ok(StackValue(StackValueKind::Literal(instr), lhs.1.clone()))
}
}
#[derive(Clone)]
pub struct IntrinsicBooleanInstr<T>(T)
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue;
intrinsic_debug!(IntrinsicBooleanInstr<T>, "IntrinsicBooleanInstr");
impl<T: Clone> IntrinsicFunction for IntrinsicBooleanInstr<T>
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
{
fn codegen<'b, 'c>(&self, scope: &mut Scope<'b, 'c>, params: &[StackValue]) -> Result<StackValue, ErrorKind> {
let lhs = params.get(0).unwrap();
let rhs = params.get(1).unwrap();
let instr = self.clone().0(scope, lhs.instr(), rhs.instr());
Ok(StackValue(StackValueKind::Literal(instr), TypeKind::Bool))
}
}
#[derive(Clone, Debug)]
pub struct IntrinsicSizeOf(TypeKind);
impl IntrinsicFunction for IntrinsicSizeOf {
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, _: &[StackValue]) -> Result<StackValue, ErrorKind> {
let instr = scope
.block
.build(Instr::Constant(reid_lib::ConstValueKind::U64(
self.0.size_of(&scope.type_map) / 8,
)))
.unwrap();
Ok(StackValue(StackValueKind::Literal(instr), self.0.clone()))
}
}
#[derive(Clone, Debug)]
pub struct IntrinsicMemcpy(TypeKind);
impl IntrinsicFunction for IntrinsicMemcpy {
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, params: &[StackValue]) -> Result<StackValue, ErrorKind> {
let dest = params.get(0).unwrap();
let src = params.get(1).unwrap();
let length = params.get(2).unwrap();
let intrinsic = scope.get_intrinsic(LLVMIntrinsicKind::Memcpy(TypeKind::UserPtr(Box::new(self.0.clone()))));
let sizeof = scope
.block
.build(Instr::Constant(ConstValueKind::U64(
self.0.size_of(&scope.type_map) / 8,
)))
.unwrap();
let bytes = scope.block.build(Instr::Mul(sizeof, length.instr())).unwrap();
let params = vec![
dest.instr(),
src.instr(),
bytes,
scope.block.build(Instr::Constant(ConstValueKind::Bool(false))).unwrap(),
];
let value = scope.block.build(Instr::FunctionCall(intrinsic, params)).unwrap();
Ok(StackValue(StackValueKind::Literal(value), TypeKind::Void))
}
}
#[derive(Clone, Debug)]
pub struct IntrinsicMalloc(TypeKind);
impl IntrinsicFunction for IntrinsicMalloc {
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, params: &[StackValue]) -> Result<StackValue, ErrorKind> {
let amount = params.get(0).unwrap();
let function = scope
.block
.find_function(&format!("{}.{}", INTRINSIC_IDENT, MALLOC_IDENT))
.unwrap();
let sizeof = scope
.block
.build(Instr::Constant(ConstValueKind::U64(
self.0.size_of(&scope.type_map) / 8,
)))
.unwrap();
let bytes = scope.block.build(Instr::Mul(sizeof, amount.instr())).unwrap();
let instr = scope.block.build(Instr::FunctionCall(function, vec![bytes])).unwrap();
Ok(StackValue(StackValueKind::Literal(instr), self.0.clone()))
}
}
#[derive(Clone, Debug)]
pub struct IntrinsicNullPtr(TypeKind);
impl IntrinsicFunction for IntrinsicNullPtr {
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, _: &[StackValue]) -> Result<StackValue, ErrorKind> {
let zero = scope.block.build(Instr::Constant(ConstValueKind::I8(0))).unwrap();
let instr = scope
.block
.build(Instr::IntToPtr(
zero,
Type::Ptr(Box::new(self.0.get_type(scope.type_values))),
))
.unwrap();
Ok(StackValue(
StackValueKind::Literal(instr),
TypeKind::UserPtr(Box::new(self.0.clone())),
))
}
}
#[derive(Clone, Debug)]
pub struct IntrinsicIsNull;
impl IntrinsicFunction for IntrinsicIsNull {
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, params: &[StackValue]) -> Result<StackValue, ErrorKind> {
let val = params.get(0).unwrap().instr();
let instr = scope.block.build(Instr::IsNull(val)).unwrap();
Ok(StackValue(StackValueKind::Literal(instr), TypeKind::Bool))
}
}
#[derive(Clone, Debug)]
pub struct IntrinsicConst(u64);
impl IntrinsicFunction for IntrinsicConst {
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, _: &[StackValue]) -> Result<StackValue, ErrorKind> {
let zero = scope.block.build(Instr::Constant(ConstValueKind::U64(self.0))).unwrap();
Ok(StackValue(StackValueKind::Literal(zero), TypeKind::U64))
}
}
#[derive(Clone, Debug)]
pub struct IntrinsicLLVM(LLVMIntrinsicKind, TypeKind);
impl IntrinsicFunction for IntrinsicLLVM {
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, params: &[StackValue]) -> Result<StackValue, ErrorKind> {
let intrinsic = scope.get_intrinsic(self.0.clone());
let value = scope
.block
.build(Instr::FunctionCall(
intrinsic,
params.iter().map(|p| p.instr()).collect(),
))
.unwrap();
Ok(StackValue(StackValueKind::Literal(value), self.1.clone()))
}
}
// impl IntrinsicFunction for IntrinsicIAdd {
// fn codegen<'ctx, 'a>(
// &self,
// scope: &mut Scope<'ctx, 'a>,
// params: &[InstructionValue],
// ) -> Result<StackValue, ErrorKind> {
// let lhs = params.get(0).unwrap();
// let rhs = params.get(1).unwrap();
// let add = scope.block.build(Instr::Add(*lhs, *rhs)).unwrap();
// Ok(StackValue(StackValueKind::Literal(add), self.0.clone()))
// }
// }
// #[derive(Debug, Clone)]
// pub struct IntrinsicIAdd(TypeKind);
// impl IntrinsicFunction for IntrinsicIAdd {
// fn codegen<'ctx, 'a>(
// &self,
// scope: &mut Scope<'ctx, 'a>,
// params: &[InstructionValue],
// ) -> Result<StackValue, ErrorKind> {
// let lhs = params.get(0).unwrap();
// let rhs = params.get(1).unwrap();
// let add = scope.block.build(Instr::Add(*lhs, *rhs)).unwrap();
// Ok(StackValue(StackValueKind::Literal(add), self.0.clone()))
// }
// }
// #[derive(Debug, Clone)]
// pub struct IntrinsicUDiv(TypeKind);
// impl IntrinsicFunction for IntrinsicUDiv {
// fn codegen<'ctx, 'a>(
// &self,
// scope: &mut Scope<'ctx, 'a>,
// params: &[InstructionValue],
// ) -> Result<StackValue, ErrorKind> {
// let lhs = params.get(0).unwrap();
// let rhs = params.get(1).unwrap();
// let add = scope.block.build(Instr::UDiv(*lhs, *rhs)).unwrap();
// Ok(StackValue(StackValueKind::Literal(add), self.0.clone()))
// }
// }
// #[derive(Debug, Clone)]
// pub struct IntrinsicUMod(TypeKind);
// impl IntrinsicFunction for IntrinsicUMod {
// fn codegen<'ctx, 'a>(
// &self,
// scope: &mut Scope<'ctx, 'a>,
// params: &[InstructionValue],
// ) -> Result<StackValue, ErrorKind> {
// let lhs = params.get(0).unwrap();
// let rhs = params.get(1).unwrap();
// let div = scope.block.build(Instr::UDiv(*lhs, *rhs)).unwrap();
// let mul = scope.block.build(Instr::Mul(*rhs, div)).unwrap();
// let sub = scope.block.build(Instr::Sub(*lhs, mul)).unwrap();
// Ok(StackValue(StackValueKind::Literal(sub), self.0.clone()))
// }
// }

File diff suppressed because it is too large Load Diff

View File

@ -1,246 +0,0 @@
use std::{cell::RefCell, collections::HashMap, mem, rc::Rc};
use reid_lib::{
builder::{FunctionValue, GlobalValue, InstructionValue, TypeValue},
debug_information::{DebugInformation, DebugLocation, DebugScopeValue, DebugTypeValue},
intrinsics::LLVMIntrinsic,
Block, Context, Function, Instr, Module,
};
use crate::{
codegen::intrinsics::LLVMIntrinsicKind,
lexer::FullToken,
mir::{
self,
pass::{AssociatedFunctionKey, BinopKey},
CustomTypeKey, FunctionParam, Metadata, SourceModuleId, TypeDefinition, TypeKind,
},
};
use super::{allocator::Allocator, ErrorKind, IntrinsicFunction};
pub struct Scope<'ctx, 'scope> {
pub(super) context: &'ctx Context,
pub(super) modules: &'scope HashMap<SourceModuleId, &'ctx mir::Module>,
pub(super) tokens: &'ctx Vec<FullToken>,
pub(super) module: &'ctx Module<'ctx>,
pub(super) module_id: SourceModuleId,
pub(super) function: &'ctx Function<'ctx>,
pub(super) block: Block<'ctx>,
pub(super) types: &'scope HashMap<TypeValue, TypeDefinition>,
pub(super) type_values: &'scope HashMap<CustomTypeKey, TypeValue>,
pub(super) type_map: &'scope HashMap<CustomTypeKey, TypeDefinition>,
pub(super) assoc_functions: &'scope HashMap<AssociatedFunctionKey, ScopeFunctionKind<'ctx>>,
pub(super) functions: &'scope HashMap<String, ScopeFunctionKind<'ctx>>,
pub(super) binops: &'scope HashMap<BinopKey, StackBinopDefinition<'ctx>>,
pub(super) stack_values: HashMap<String, StackValue>,
pub(super) globals: &'scope HashMap<String, GlobalValue>,
pub(super) debug: Option<Debug<'ctx>>,
pub(super) allocator: Rc<RefCell<Allocator>>,
pub(super) llvm_intrinsics: Rc<RefCell<HashMap<LLVMIntrinsicKind, FunctionValue>>>,
}
impl<'ctx, 'a> Scope<'ctx, 'a> {
pub fn with_block(&self, block: Block<'ctx>) -> Scope<'ctx, 'a> {
Scope {
block,
modules: self.modules,
tokens: self.tokens,
function: self.function,
context: self.context,
module: self.module,
module_id: self.module_id,
assoc_functions: self.assoc_functions,
functions: self.functions,
types: self.types,
type_values: self.type_values,
type_map: self.type_map,
stack_values: self.stack_values.clone(),
debug: self.debug.clone(),
allocator: self.allocator.clone(),
globals: self.globals,
binops: self.binops,
llvm_intrinsics: self.llvm_intrinsics.clone(),
}
}
/// Takes the block out from this scope, swaps the given block in it's place
/// and returns the old block.
pub fn swap_block(&mut self, block: Block<'ctx>) -> Block<'ctx> {
let mut old_block = block;
mem::swap(&mut self.block, &mut old_block);
old_block
}
pub fn get_typedef(&self, key: &CustomTypeKey) -> Option<&TypeDefinition> {
self.type_values.get(key).and_then(|v| self.types.get(v))
}
pub fn allocate(&self, meta: &Metadata, ty: &TypeKind) -> Option<InstructionValue> {
self.allocator.borrow_mut().allocate(meta, ty)
}
pub fn get_intrinsic(&self, kind: LLVMIntrinsicKind) -> FunctionValue {
let mut intrinsics = self.llvm_intrinsics.borrow_mut();
if let Some(fun) = intrinsics.get(&kind) {
*fun
} else {
let intrinsic = self
.module
.intrinsic(match &kind {
LLVMIntrinsicKind::Max(ty) => LLVMIntrinsic::Max(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Min(ty) => LLVMIntrinsic::Min(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Abs(ty) => LLVMIntrinsic::Abs(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Memcpy(ty) => LLVMIntrinsic::Memcpy(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Sqrt(ty) => LLVMIntrinsic::Sqrt(ty.get_type(self.type_values)),
LLVMIntrinsicKind::PowI(lhs, rhs) => {
LLVMIntrinsic::PowI(lhs.get_type(self.type_values), rhs.get_type(self.type_values))
}
LLVMIntrinsicKind::Pow(ty) => LLVMIntrinsic::Pow(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Sin(ty) => LLVMIntrinsic::Sin(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Cos(ty) => LLVMIntrinsic::Cos(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Tan(ty) => LLVMIntrinsic::Tan(ty.get_type(self.type_values)),
LLVMIntrinsicKind::ASin(ty) => LLVMIntrinsic::ASin(ty.get_type(self.type_values)),
LLVMIntrinsicKind::ACos(ty) => LLVMIntrinsic::ACos(ty.get_type(self.type_values)),
LLVMIntrinsicKind::ATan(ty) => LLVMIntrinsic::ATan(ty.get_type(self.type_values)),
LLVMIntrinsicKind::ATan2(ty) => LLVMIntrinsic::ATan2(ty.get_type(self.type_values)),
LLVMIntrinsicKind::SinH(ty) => LLVMIntrinsic::SinH(ty.get_type(self.type_values)),
LLVMIntrinsicKind::CosH(ty) => LLVMIntrinsic::CosH(ty.get_type(self.type_values)),
LLVMIntrinsicKind::TanH(ty) => LLVMIntrinsic::TanH(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Log(ty) => LLVMIntrinsic::Log(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Log2(ty) => LLVMIntrinsic::Log2(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Log10(ty) => LLVMIntrinsic::Log10(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Copysign(ty) => LLVMIntrinsic::Copysign(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Floor(ty) => LLVMIntrinsic::Floor(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Ceil(ty) => LLVMIntrinsic::Ceil(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Trunc(ty) => LLVMIntrinsic::Trunc(ty.get_type(self.type_values)),
LLVMIntrinsicKind::RoundEven(ty) => LLVMIntrinsic::RoundEven(ty.get_type(self.type_values)),
LLVMIntrinsicKind::Round(ty) => LLVMIntrinsic::Round(ty.get_type(self.type_values)),
})
.unwrap();
intrinsics.insert(kind, intrinsic.clone());
intrinsic
}
}
}
#[derive(Debug, Clone)]
pub struct Debug<'ctx> {
pub(super) info: &'ctx DebugInformation,
pub(super) scope: DebugScopeValue,
pub(super) types: &'ctx HashMap<TypeKind, DebugTypeValue>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StackValue(pub(super) StackValueKind, pub(super) TypeKind);
impl StackValue {
pub fn instr(&self) -> InstructionValue {
self.0.instr()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StackValueKind {
Immutable(InstructionValue),
Mutable(InstructionValue),
Literal(InstructionValue),
}
impl StackValueKind {
pub fn mutable(mutable: bool, instr: InstructionValue) -> StackValueKind {
match mutable {
true => StackValueKind::Mutable(instr),
false => StackValueKind::Immutable(instr),
}
}
pub fn instr(&self) -> InstructionValue {
match &self {
StackValueKind::Immutable(val) => *val,
StackValueKind::Mutable(val) => *val,
StackValueKind::Literal(val) => *val,
}
}
pub fn derive(&self, instr: InstructionValue) -> StackValueKind {
match &self {
StackValueKind::Immutable(_) => StackValueKind::Immutable(instr),
StackValueKind::Mutable(_) => StackValueKind::Mutable(instr),
StackValueKind::Literal(_) => StackValueKind::Literal(instr),
}
}
#[allow(dead_code)]
fn map<F>(&self, lambda: F) -> StackValueKind
where
F: FnOnce(InstructionValue) -> InstructionValue,
{
self.derive(lambda(self.instr()))
}
}
pub struct StackBinopDefinition<'ctx> {
pub(super) parameters: (FunctionParam, FunctionParam),
pub(super) return_ty: TypeKind,
pub(super) kind: ScopeFunctionKind<'ctx>,
}
pub enum ScopeFunctionKind<'ctx> {
UserGenerated(Function<'ctx>),
Intrinsic(&'ctx Box<dyn IntrinsicFunction>),
IntrinsicOwned(Box<dyn IntrinsicFunction>),
}
impl<'ctx> StackBinopDefinition<'ctx> {
pub fn codegen<'a>(
&self,
lhs: StackValue,
rhs: StackValue,
scope: &mut Scope<'ctx, 'a>,
) -> Result<StackValue, ErrorKind> {
let (lhs, rhs) = if lhs.1 == self.parameters.0.ty && rhs.1 == self.parameters.1.ty {
(lhs, rhs)
} else {
(rhs, lhs)
};
let name = format!(
"binop.{}.{}.{}.call",
self.parameters.0.ty, self.parameters.1.ty, self.return_ty
);
self.kind.codegen(&name, &[lhs, rhs], &self.return_ty, None, scope)
}
}
impl<'ctx> ScopeFunctionKind<'ctx> {
pub fn codegen<'a>(
&self,
name: &str,
params: &[StackValue],
return_ty: &TypeKind,
location: Option<DebugLocation>,
scope: &mut Scope<'ctx, 'a>,
) -> Result<StackValue, ErrorKind> {
match self {
ScopeFunctionKind::UserGenerated(function) => {
let val = scope
.block
.build_named(
name,
Instr::FunctionCall(function.value(), params.iter().map(|p| p.instr()).collect()),
)
.unwrap();
if let Some(debug) = &scope.debug {
if let Some(location) = location {
let location_val = debug.info.location(&debug.scope, location);
val.with_location(&mut scope.block, location_val);
}
}
Ok(StackValue(StackValueKind::Immutable(val), return_ty.clone()))
}
ScopeFunctionKind::Intrinsic(fun) => fun.codegen(scope, params),
ScopeFunctionKind::IntrinsicOwned(fun) => fun.codegen(scope, params),
}
}
}

View File

@ -1,264 +0,0 @@
use std::collections::HashMap;
use reid_lib::{
builder::{InstructionValue, TypeValue},
debug_information::{
DebugArrayType, DebugBasicType, DebugFieldType, DebugInformation, DebugLocation, DebugPointerType,
DebugPosition, DebugScopeValue, DebugStructType, DebugTypeData, DebugTypeValue, DwarfEncoding, DwarfFlags,
},
Block, CmpPredicate, ConstValueKind, Instr, Type,
};
use crate::{
lexer::{FullToken, Position},
mir::{self, CustomTypeKey, Metadata, SourceModuleId, TypeDefinition, TypeDefinitionKind, TypeKind, VagueLiteral},
};
use super::scope::{Debug, Scope};
impl mir::CmpOperator {
pub(super) fn predicate(&self) -> CmpPredicate {
match self {
mir::CmpOperator::LT => CmpPredicate::LT,
mir::CmpOperator::GT => CmpPredicate::GT,
mir::CmpOperator::LE => CmpPredicate::LE,
mir::CmpOperator::GE => CmpPredicate::GE,
mir::CmpOperator::EQ => CmpPredicate::EQ,
mir::CmpOperator::NE => CmpPredicate::NE,
}
}
}
impl mir::Literal {
pub(super) fn as_const(&self, block: &mut Block) -> InstructionValue {
block
.build_named(format!("{}", self), Instr::Constant(self.as_const_kind()))
.unwrap()
}
pub(super) fn as_const_kind(&self) -> ConstValueKind {
match self.clone() {
mir::Literal::I8(val) => ConstValueKind::I8(val),
mir::Literal::I16(val) => ConstValueKind::I16(val),
mir::Literal::I32(val) => ConstValueKind::I32(val),
mir::Literal::I64(val) => ConstValueKind::I64(val),
mir::Literal::I128(val) => ConstValueKind::I128(val),
mir::Literal::U8(val) => ConstValueKind::U8(val),
mir::Literal::U16(val) => ConstValueKind::U16(val),
mir::Literal::U32(val) => ConstValueKind::U32(val),
mir::Literal::U64(val) => ConstValueKind::U64(val),
mir::Literal::U128(val) => ConstValueKind::U128(val),
mir::Literal::Bool(val) => ConstValueKind::Bool(val),
mir::Literal::String(val) => ConstValueKind::Str(val.clone()),
mir::Literal::Vague(VagueLiteral::Number(val)) => ConstValueKind::I32(val as i32),
mir::Literal::Vague(VagueLiteral::Decimal(val)) => ConstValueKind::F32(val as f32),
mir::Literal::F16(val) => ConstValueKind::F16(val),
mir::Literal::F32B(val) => ConstValueKind::F32B(val),
mir::Literal::F32(val) => ConstValueKind::F32(val),
mir::Literal::F64(val) => ConstValueKind::F64(val),
mir::Literal::F80(val) => ConstValueKind::F80(val),
mir::Literal::F128(val) => ConstValueKind::F128(val),
mir::Literal::F128PPC(val) => ConstValueKind::F128PPC(val),
mir::Literal::Char(c) => ConstValueKind::U8(c as u8),
}
}
}
impl TypeKind {
pub(super) fn get_type(&self, type_vals: &HashMap<CustomTypeKey, TypeValue>) -> Type {
match &self {
TypeKind::I8 => Type::I8,
TypeKind::I16 => Type::I16,
TypeKind::I32 => Type::I32,
TypeKind::I64 => Type::I64,
TypeKind::I128 => Type::I128,
TypeKind::U8 => Type::U8,
TypeKind::U16 => Type::U16,
TypeKind::U32 => Type::U32,
TypeKind::U64 => Type::U64,
TypeKind::U128 => Type::U128,
TypeKind::Bool => Type::Bool,
TypeKind::F16 => Type::F16,
TypeKind::F16B => Type::F32B,
TypeKind::F32 => Type::F32,
TypeKind::F64 => Type::F64,
TypeKind::F128 => Type::F128,
TypeKind::F80 => Type::F80,
TypeKind::F128PPC => Type::F128PPC,
TypeKind::Char => Type::U8,
TypeKind::Array(elem_t, len) => Type::Array(Box::new(elem_t.get_type(type_vals)), *len),
TypeKind::Void => Type::Void,
TypeKind::Vague(_) => panic!("Tried to compile a vague type!"),
TypeKind::CustomType(n) => {
let type_val = type_vals.get(n).unwrap().clone();
Type::CustomType(type_val)
}
TypeKind::UserPtr(type_kind) => Type::Ptr(Box::new(type_kind.get_type(type_vals))),
TypeKind::CodegenPtr(type_kind) => Type::Ptr(Box::new(type_kind.get_type(type_vals))),
TypeKind::Borrow(type_kind, _) => Type::Ptr(Box::new(type_kind.get_type(type_vals))),
}
}
}
impl TypeKind {
pub(super) fn get_debug_type(&self, debug: &Debug, scope: &Scope) -> DebugTypeValue {
self.get_debug_type_hard(
&debug.scope,
debug.info,
debug.types,
scope.type_map,
scope.module_id,
scope.tokens,
scope.modules,
)
}
pub(super) fn get_debug_type_hard(
&self,
scope: &DebugScopeValue,
debug_info: &DebugInformation,
debug_types: &HashMap<TypeKind, DebugTypeValue>,
type_map: &HashMap<CustomTypeKey, TypeDefinition>,
local_mod: SourceModuleId,
tokens: &Vec<FullToken>,
modules: &HashMap<SourceModuleId, &mir::Module>,
) -> DebugTypeValue {
if let Some(ty) = debug_types.get(self) {
return *ty;
}
let name = format!("{}", self);
let data = match self {
TypeKind::CodegenPtr(inner) | TypeKind::UserPtr(inner) | TypeKind::Borrow(inner, _) => {
DebugTypeData::Pointer(DebugPointerType {
name,
pointee: inner.get_debug_type_hard(
scope,
debug_info,
debug_types,
type_map,
local_mod,
tokens,
modules,
),
size_bits: self.size_of(type_map),
})
}
TypeKind::Array(elem_ty, len) => {
let elem_ty = elem_ty.clone().get_debug_type_hard(
scope,
debug_info,
debug_types,
type_map,
local_mod,
tokens,
modules,
);
DebugTypeData::Array(DebugArrayType {
size_bits: self.size_of(type_map),
align_bits: self.alignment(),
element_type: elem_ty,
length: *len,
})
}
TypeKind::CustomType(key) => {
let typedef = type_map.get(key).unwrap();
match &typedef.kind {
TypeDefinitionKind::Struct(struct_type) => {
let mut fields = Vec::new();
let mut size_bits = 0;
for field in &struct_type.0 {
let location = if typedef.source_module != local_mod {
None
} else {
field.2.into_debug(&tokens, &scope)
};
fields.push(DebugFieldType {
name: field.0.clone(),
scope: scope.clone(),
pos: location.map(|l| l.pos),
size_bits: field.1.size_of(type_map),
offset: size_bits,
flags: DwarfFlags,
ty: field.1.get_debug_type_hard(
scope,
debug_info,
debug_types,
type_map,
local_mod,
tokens,
modules,
),
});
size_bits += field.1.size_of(type_map);
}
{
let location = if typedef.source_module != local_mod {
None
} else {
typedef.meta.into_debug(&tokens, scope)
};
DebugTypeData::Struct(DebugStructType {
name: key.0.clone(),
scope: scope.clone(),
pos: location.map(|l| l.pos),
size_bits,
flags: DwarfFlags,
fields,
})
}
}
}
}
_ => DebugTypeData::Basic(DebugBasicType {
name,
size_bits: self.size_of(type_map),
encoding: match self {
TypeKind::Bool => DwarfEncoding::Boolean,
TypeKind::I8 => DwarfEncoding::SignedChar,
TypeKind::U8 => DwarfEncoding::UnsignedChar,
TypeKind::I16 | TypeKind::I32 | TypeKind::I64 | TypeKind::I128 => DwarfEncoding::Signed,
TypeKind::U16 | TypeKind::U32 | TypeKind::U64 | TypeKind::U128 => DwarfEncoding::Unsigned,
TypeKind::F16
| TypeKind::F32
| TypeKind::F16B
| TypeKind::F64
| TypeKind::F80
| TypeKind::F128
| TypeKind::F128PPC => DwarfEncoding::Float,
TypeKind::Void => DwarfEncoding::Address,
TypeKind::Char => DwarfEncoding::UnsignedChar,
TypeKind::Array(_, _) => DwarfEncoding::Address,
TypeKind::CustomType(_) => DwarfEncoding::Address,
_ => panic!("tried fetching debug-type for non-supported type!"),
},
flags: DwarfFlags,
}),
};
debug_info.debug_type(data)
}
}
impl Metadata {
pub(super) fn into_debug(&self, tokens: &Vec<FullToken>, scope: &DebugScopeValue) -> Option<DebugLocation> {
if let Some((start, _)) = self.into_positions(tokens) {
Some(start.debug(scope.clone()))
} else {
None
}
}
}
impl Position {
pub(super) fn debug(self, scope: DebugScopeValue) -> DebugLocation {
DebugLocation {
pos: DebugPosition {
line: self.1,
column: self.0,
},
scope,
}
}
}

View File

@ -1,18 +1,14 @@
use std::{
collections::HashMap,
fmt::{Debug, Write},
path::PathBuf,
};
use crate::{
ast::token_stream::{self, TokenRange},
codegen,
lexer::{self, Cursor, FullToken, Position},
mir::{self, macros, pass, typecheck, Metadata, SourceModuleId},
mir::{self, pass, Metadata, SourceModuleId},
token_stream::{self, TokenRange},
};
use crate::mir::typecheck::ErrorKind as TypecheckError;
fn label(text: &str) -> &str {
#[cfg(debug_assertions)]
{
@ -22,58 +18,38 @@ fn label(text: &str) -> &str {
""
}
#[derive(thiserror::Error, Debug, Clone, PartialEq)]
#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
pub enum ErrorKind {
#[error("{}{}", label("(Lexing) "), .0.kind)]
LexerError(#[from] mir::pass::Error<lexer::Error>),
#[error("{}{}", label("(Parsing) "), .0.kind)]
ParserError(#[from] mir::pass::Error<token_stream::Error>),
#[error("{}{}", label("(TypeCheck) "), .0.kind)]
TypeCheckError(#[source] mir::pass::Error<typecheck::ErrorKind>),
TypeCheckError(#[source] mir::pass::Error<mir::typecheck::ErrorKind>),
#[error("{}{}", label("(TypeInference) "), .0.kind)]
TypeInferenceError(#[source] mir::pass::Error<TypecheckError>),
TypeInferenceError(#[source] mir::pass::Error<mir::typecheck::ErrorKind>),
#[error("{}{}", label("(Linker) "), .0.kind)]
LinkerError(#[from] mir::pass::Error<mir::linker::ErrorKind>),
#[error("{}{}", label("(Macro) "), .0)]
MacroError(#[from] mir::pass::Error<macros::ErrorKind>),
#[error("{}{}", label("(Codegen) "), .0)]
CodegenError(#[from] codegen::ErrorKind),
}
impl ErrorKind {
pub fn from_typecheck(err: mir::pass::Error<typecheck::ErrorKind>) -> ErrorKind {
pub fn from_typecheck(err: mir::pass::Error<mir::typecheck::ErrorKind>) -> ErrorKind {
ErrorKind::TypeCheckError(err)
}
pub fn from_typeinference(err: mir::pass::Error<typecheck::ErrorKind>) -> ErrorKind {
pub fn from_typeinference(err: mir::pass::Error<mir::typecheck::ErrorKind>) -> ErrorKind {
ErrorKind::TypeInferenceError(err)
}
}
impl ErrorKind {
pub fn get_meta(&self) -> Metadata {
fn get_meta(&self) -> Metadata {
match &self {
ErrorKind::LexerError(error) => error.metadata,
ErrorKind::ParserError(error) => error.metadata,
ErrorKind::TypeCheckError(error) => error.metadata,
ErrorKind::TypeInferenceError(error) => error.metadata,
ErrorKind::LinkerError(error) => error.metadata,
ErrorKind::CodegenError(error) => match error {
codegen::ErrorKind::Null => Default::default(),
},
ErrorKind::MacroError(error) => error.metadata,
}
}
pub fn get_type_str(&self) -> &str {
match self {
ErrorKind::LexerError(_) => "lexer",
ErrorKind::ParserError(_) => "parser",
ErrorKind::TypeCheckError(_) => "typechecker",
ErrorKind::TypeInferenceError(_) => "type-inferrer",
ErrorKind::LinkerError(_) => "linker",
ErrorKind::MacroError(_) => "macro-pass",
ErrorKind::CodegenError(_) => "codegen",
}
}
}
@ -86,56 +62,37 @@ impl PartialOrd for ErrorKind {
}
}
impl Ord for ErrorKind {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.get_meta().cmp(&other.get_meta())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ErrorModule {
pub struct Module {
pub name: String,
pub tokens: Option<Vec<FullToken>>,
pub source: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ErrorModules {
pub(super) module_map: HashMap<mir::SourceModuleId, ErrorModule>,
pub(super) source_id_map: HashMap<PathBuf, mir::SourceModuleId>,
pub struct ModuleMap {
module_map: HashMap<mir::SourceModuleId, Module>,
module_counter: mir::SourceModuleId,
}
impl ErrorModules {
pub fn add_module<T: Into<String>>(
&mut self,
name: T,
path: Option<PathBuf>,
external_module_id: Option<SourceModuleId>,
) -> Option<mir::SourceModuleId> {
let module_id = path.as_ref().and_then(|p| self.source_id_map.get(p));
if let Some(module_id) = module_id {
Some(*module_id)
} else {
let id = if let Some(module_id) = external_module_id {
self.module_counter = SourceModuleId(module_id.0.max(self.module_counter.0));
if let Some(_) = self.module_map.get(&module_id) {
panic!("Can not use external module id: Module already exists!")
}
module_id
} else {
self.module_counter.increment()
};
if let Some(path) = path {
self.source_id_map.insert(path, id);
}
self.module_map.insert(
id,
ErrorModule {
name: name.into(),
tokens: None,
source: None,
},
);
Some(id)
}
impl ModuleMap {
pub fn add_module<T: Into<String>>(&mut self, name: T) -> Option<mir::SourceModuleId> {
let id = self.module_counter.increment();
self.module_map.insert(
id,
Module {
name: name.into(),
tokens: None,
source: None,
},
);
Some(id)
}
pub fn set_tokens(&mut self, id: mir::SourceModuleId, tokens: Vec<FullToken>) {
@ -150,21 +107,21 @@ impl ErrorModules {
}
}
pub fn module(&self, id: &mir::SourceModuleId) -> Option<&ErrorModule> {
pub fn module(&self, id: &mir::SourceModuleId) -> Option<&Module> {
self.module_map.get(id)
}
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ReidError {
map: ErrorModules,
pub errors: Vec<ErrorKind>,
map: ModuleMap,
errors: Vec<ErrorKind>,
}
impl ReidError {
pub fn from_lexer<U>(
result: Result<U, lexer::Error>,
map: ErrorModules,
map: ModuleMap,
module: SourceModuleId,
) -> Result<U, ReidError> {
result.map_err(|error| {
@ -185,7 +142,7 @@ impl ReidError {
pub fn from_parser<U>(
result: Result<U, token_stream::Error>,
map: ErrorModules,
map: ModuleMap,
module: SourceModuleId,
) -> Result<U, ReidError> {
result.map_err(|error| {
@ -204,13 +161,9 @@ impl ReidError {
})
}
pub fn from_kind(errors: Vec<ErrorKind>, map: ErrorModules) -> ReidError {
pub fn from_kind<U>(errors: Vec<ErrorKind>, map: ModuleMap) -> ReidError {
ReidError { map, errors }
}
pub fn extend(&mut self, other: ReidError) {
self.errors.extend(other.errors);
}
}
impl std::error::Error for ReidError {}
@ -218,49 +171,50 @@ impl std::error::Error for ReidError {}
impl std::fmt::Display for ReidError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut sorted_errors = self.errors.clone();
sorted_errors.sort_by(|a, b| a.partial_cmp(&b).unwrap_or(std::cmp::Ordering::Equal));
sorted_errors.sort_by(|a, b| a.cmp(&b));
sorted_errors.dedup();
let mut curr_module = None;
for error in sorted_errors {
let meta = error.get_meta();
let module = self.map.module(&meta.source_module_id);
let position = if let Some(module) = module {
if let Some(tokens) = &module.tokens {
meta.range.into_position(tokens).or(meta.position.map(|p| (p, p)))
} else if let Some(position) = meta.position {
Some((position, position))
} else {
None
}
let module = self.map.module(&meta.source_module_id).unwrap();
let position = if let Some(tokens) = &module.tokens {
let range_tokens = meta.range.into_tokens(&tokens);
get_position(&range_tokens).or(meta.position.map(|p| (p, p)))
} else if let Some(position) = meta.position {
Some((position, position))
} else {
None
};
let module_name = if curr_module != Some(meta.source_module_id) {
if curr_module != Some(meta.source_module_id) {
curr_module = Some(meta.source_module_id);
if let Some(module) = self.map.module_map.get(&meta.source_module_id) {
module.name.clone()
} else {
"unknown".to_owned()
}
} else {
"unknown".to_owned()
};
writeln!(f, "Errors detected: {}", color_err(format!("{}", module_name))?)?;
writeln!(
f,
"Errors in module {}:",
color_err(format!(
"{}",
self.map
.module_map
.get(&meta.source_module_id)
.unwrap()
.name
))?
)?;
}
writeln!(f)?;
write!(f, " Error: ")?;
writeln!(f, "{}", color_err(format!("{}", error))?)?;
write!(
f,
"{:>20} {}:{}",
"{:>20}{}",
color_warn("At: ")?,
module_name,
position.map(|p| fmt_positions(p)).unwrap_or(String::from("{unknown}")),
position
.map(|p| fmt_positions(p))
.unwrap_or(String::from("{unknown}")),
)?;
if let (Some(position), Some(source)) = (position, &module.and_then(|m| m.source.clone())) {
if let (Some(position), Some(source)) = (position, &module.source) {
writeln!(f, "{}", fmt_lines(source, position, 6)?)?;
}
}
@ -277,11 +231,6 @@ impl TokenRange {
.take(self.end + 1 - self.start)
.collect::<Vec<_>>()
}
pub fn into_position<'v>(&self, tokens: &'v Vec<FullToken>) -> Option<(Position, Position)> {
let tokens = self.into_tokens(tokens);
get_position(&tokens)
}
}
fn get_position(tokens: &Vec<&FullToken>) -> Option<(Position, Position)> {
@ -332,9 +281,9 @@ fn fmt_lines(
fn fmt_positions((start, end): (Position, Position)) -> String {
if start == end {
format!("{}:{}", start.1, start.0)
format!("ln {}, col {}", start.1, start.0)
} else if start.1 == end.1 {
format!("{}:{} - {}", start.1, start.0, end.0)
format!("ln {}, col {}-{}", start.1, start.0, end.0)
} else {
format!("{}:{} - {}:{}", start.1, start.0, end.1, end.0)
}

View File

@ -1,118 +0,0 @@
use std::{path::PathBuf, process::Command, thread, time::Duration};
pub struct LDRunner {
command: String,
dynamic_linker: String,
libraries: Vec<String>,
}
impl LDRunner {
pub fn from_command(command: &str) -> LDRunner {
LDRunner {
command: command.to_owned(),
dynamic_linker: "ld-linux-x86-64.so.2".to_string(),
libraries: Default::default(),
}
}
pub fn with_library(mut self, lib: &str) -> LDRunner {
self.libraries.push(lib.to_owned());
self
}
pub fn invoke(&self, input: &PathBuf, out_path: &PathBuf) {
let input_path = input.canonicalize().unwrap();
let dyn_linker_path = find_objectfile(&self.dynamic_linker);
let crt1_path = find_objectfile("crt1.o");
let crti_path = find_objectfile("crti.o");
let crtn_path = find_objectfile("crtn.o");
log::debug!("LDRunner: Using dynamic linker at: {:?}", dyn_linker_path);
let mut ld = Command::new(&self.command);
ld.arg("-dynamic-linker")
.arg(dyn_linker_path)
.arg(crt1_path)
.arg(crti_path);
for library in &self.libraries {
ld.arg(format!("-l{}", library));
}
ld.arg(crtn_path);
ld.arg(input_path.to_str().unwrap())
.arg("-o")
.arg(out_path.to_str().unwrap());
log::debug!("{:#?}", ld);
log::debug!(
"LDRunner: Executing linker to objfile at {:?} => {:?}",
input_path,
out_path
);
let ld_output = ld.output().expect("Unable to execute ld!");
if !ld_output.status.success() {
let code = ld_output.status.code().unwrap_or(255);
log::error!("LD exited with code {code}");
println!("{}", unsafe { String::from_utf8_unchecked(ld_output.stderr) });
return;
}
thread::sleep(Duration::from_millis(100));
log::debug!("Setting executable bit to {:?}..", out_path);
let chmod_output = Command::new("chmod")
.arg("+x")
.arg(out_path)
.output()
.expect("Unable to execute ld!");
if !chmod_output.status.success() {
let code = chmod_output.status.code().unwrap_or(255);
log::error!("chmod exited with code {code}");
println!("{}", unsafe { String::from_utf8_unchecked(chmod_output.stderr) });
return;
}
thread::sleep(Duration::from_millis(100));
}
}
fn find_objectfile(name: &str) -> String {
let whereis = Command::new("whereis")
.arg(&name)
.output()
.expect("Unable to execute whereis");
if !whereis.status.success() {
let code = whereis.status.code().unwrap_or(255);
log::error!("whereis exited with code {code}");
println!("{}", unsafe { String::from_utf8_unchecked(whereis.stderr) });
panic!();
}
let whereis_output = String::from_utf8(whereis.stdout).unwrap();
whereis_output
.split(" ")
.skip(1)
.next()
.expect(&format!("Unable to find {}: {}", name, whereis_output))
.split("\n")
.next()
.unwrap()
.to_owned()
}
pub fn execute(path: &PathBuf) -> Option<i32> {
let output = Command::new(path.clone()).output().expect("Unable to execute {path}");
if !output.status.success() {
let code = output.status.code().unwrap_or(255);
log::error!("{path:?} exited with code {code}");
println!("{}", unsafe { String::from_utf8_unchecked(output.stderr) });
}
output.status.code()
}

View File

@ -1,27 +1,14 @@
use std::{fmt::Debug, ops::AddAssign, str::Chars};
use std::{fmt::Debug, str::Chars};
static BINARY_NUMERICS: &[char] = &['0', '1'];
static OCTAL_NUMERICS: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7'];
static DECIMAL_NUMERICS: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
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, Hash)]
#[derive(Debug, Eq, PartialEq, Clone, PartialOrd, Ord)]
pub enum Token {
/// Values
Identifier(String),
/// Number in the decimal base
DecimalValue(String),
/// Integer number in the hexadecimal base
HexadecimalValue(String),
/// Integer number in the octal base
OctalValue(String),
/// Integer number in the binary base
BinaryValue(String),
/// Some character literal that was surrounded by 'single-quotes'.
CharLit(String),
/// Some string literal that was surrounded by "double-quotes".
/// Number with at most one decimal point
DecimalValue(u64),
/// Some string literal that was surrounded by "quotes".
StringLit(String),
// Keywords
@ -37,8 +24,6 @@ pub enum Token {
FnKeyword,
/// `pub`
PubKeyword,
/// `as`
AsKeyword,
/// `->`
Arrow,
/// `if`
@ -53,16 +38,6 @@ pub enum Token {
Extern,
/// `struct`
Struct,
/// `while`
While,
/// `for`
For,
/// `in`
In,
/// `impl`
Impl,
/// `binop`
Binop,
// Symbols
/// `;`
@ -77,10 +52,6 @@ pub enum Token {
Star,
/// `-`
Minus,
/// `/`
Slash,
/// `%`
Percent,
/// `>`
GreaterThan,
@ -88,10 +59,6 @@ pub enum Token {
LessThan,
/// `&`
Et,
/// `|`
Pipe,
/// `^`
Hat,
/// `!`
Exclamation,
@ -112,11 +79,6 @@ pub enum Token {
/// `.`
Dot,
Unknown(char),
Whitespace(String),
Comment(String),
Doc(String),
Eof,
}
@ -148,10 +110,6 @@ impl ToString for Token {
match &self {
Token::Identifier(ident) => ident.clone(),
Token::DecimalValue(val) => val.to_string(),
Token::HexadecimalValue(val) => format!("0x{}", val),
Token::OctalValue(val) => format!("0o{}", val),
Token::BinaryValue(val) => format!("0b{}", val),
Token::CharLit(lit) => format!("\'{}\'", lit),
Token::StringLit(lit) => format!("\"{}\"", lit),
Token::LetKeyword => String::from("let"),
Token::MutKeyword => String::from("mut"),
@ -166,12 +124,6 @@ impl ToString for Token {
Token::False => String::from("false"),
Token::Extern => String::from("extern"),
Token::Struct => String::from("struct"),
Token::AsKeyword => String::from("as"),
Token::For => String::from("for"),
Token::In => String::from("in"),
Token::While => String::from("while"),
Token::Impl => String::from("impl"),
Token::Binop => String::from("binop"),
Token::Semi => String::from(';'),
Token::Equals => String::from('='),
Token::Colon => String::from(':'),
@ -181,8 +133,6 @@ 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(')'),
@ -193,27 +143,12 @@ impl ToString for Token {
Token::Comma => String::from(','),
Token::Dot => String::from('.'),
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(),
}
}
}
impl std::fmt::Debug for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Unknown(value) => write!(f, "Unknown(\'{}\')", value.to_string()),
_ => write!(f, "{}", self.to_string()),
}
}
}
/// A token with a position
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct FullToken {
pub token: Token,
pub position: Position,
@ -222,7 +157,7 @@ pub struct FullToken {
impl Debug for FullToken {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"Token({:?}) (Ln {}, Col {})",
"{:?} (Ln {}, Col {})",
self.token, self.position.1, self.position.0
))
}
@ -299,71 +234,38 @@ pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error
let variant = match character {
// Whitespace
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)
}
w if w.is_whitespace() => continue,
// Comments
'/' if cursor.first() == Some('/') => {
cursor.next();
let doc = if cursor.first() == Some('/') {
cursor.next();
true
} else {
false
};
let mut comment = String::new();
while !matches!(cursor.first(), Some('\n') | None) {
if let Some(c) = cursor.next() {
comment.push(c);
}
}
if doc {
Token::Doc(comment)
} else {
Token::Comment(comment)
cursor.next();
}
continue;
}
'\"' | '\'' => {
'\"' => {
let mut value = String::new();
let mut escape_next = false;
while cursor.first().is_some() && (cursor.first() != Some(*character) || escape_next) {
if cursor.first() == Some('\\') && !escape_next {
cursor.next(); // Consume backslash and always add next character
escape_next = true;
let mut ignore_next = false;
while cursor.first().is_some() && (cursor.first() != Some('\"') || ignore_next) {
if cursor.first() == Some('\\') && !ignore_next {
cursor.next(); // Consume backslash anjd always add next character
ignore_next = true;
} else {
let c = &cursor.next().unwrap();
if escape_next {
value += &escape_char(&c).to_string();
} else {
value += &c.to_string();
}
escape_next = false;
ignore_next = false;
value += &cursor.next().unwrap().to_string();
}
}
if cursor.first() == Some(*character) {
if cursor.first() == Some('\"') {
cursor.next();
} else {
return Err(Error::MissingQuotation(position));
}
match character {
'\'' => Token::CharLit(value),
'\"' => Token::StringLit(value),
_ => unreachable!(),
}
Token::StringLit(value)
}
// "words"
c if c.is_alphabetic() => {
let mut value = character.to_string();
while let Some(c) = cursor.first() {
if !(c.is_ascii_alphanumeric() || c == '_') {
if !c.is_ascii_alphanumeric() {
break;
}
value += &c.to_string();
@ -384,54 +286,21 @@ pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error
"extern" => Token::Extern,
"pub" => Token::PubKeyword,
"struct" => Token::Struct,
"as" => Token::AsKeyword,
"for" => Token::For,
"while" => Token::While,
"in" => Token::In,
"impl" => Token::Impl,
"binop" => Token::Binop,
_ => Token::Identifier(value),
};
variant
}
// Decimals
c if DECIMAL_NUMERICS.contains(c) => {
let mut value = NumberType::Decimal(character.to_string());
let mut numerics = DECIMAL_NUMERICS;
if let Some(second) = cursor.second() {
if cursor.first() == Some('x')
&& HEXADECIMAL_NUMERICS.contains(&second.to_lowercase().next().unwrap_or('.'))
{
cursor.next();
value = NumberType::Hexadecimal(String::new());
numerics = HEXADECIMAL_NUMERICS;
} else if cursor.first() == Some('o')
&& OCTAL_NUMERICS.contains(&second.to_lowercase().next().unwrap_or('.'))
{
cursor.next();
value = NumberType::Octal(String::new());
numerics = OCTAL_NUMERICS;
} else if cursor.first() == Some('b')
&& BINARY_NUMERICS.contains(&second.to_lowercase().next().unwrap_or('.'))
{
cursor.next();
value = NumberType::Binary(String::new());
numerics = BINARY_NUMERICS;
}
}
let mut value = character.to_string();
while let Some(c) = cursor.first() {
if !numerics.contains(&c.to_lowercase().next().unwrap_or('.')) {
if !DECIMAL_NUMERICS.contains(&c) {
break;
}
value += c;
value += &c.to_string();
cursor.next();
}
match value {
NumberType::Decimal(dec) => Token::DecimalValue(dec),
NumberType::Hexadecimal(hex) => Token::HexadecimalValue(hex),
NumberType::Octal(oct) => Token::OctalValue(oct),
NumberType::Binary(bin) => Token::BinaryValue(bin),
}
Token::DecimalValue(value.parse().expect("Decimal not parseable to u64"))
}
'-' if cursor.first() == Some('>') => {
cursor.next(); // Eat `>`
@ -447,8 +316,6 @@ 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,
@ -458,10 +325,8 @@ pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error
'}' => Token::BraceClose,
',' => Token::Comma,
'.' => Token::Dot,
'/' => Token::Slash,
'%' => Token::Percent,
// Unknown token
value => Token::Unknown(*value),
// Invalid token
_ => Err(Error::InvalidToken(*character, cursor.position))?,
};
tokens.push(FullToken {
@ -478,34 +343,6 @@ pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error
Ok(tokens)
}
fn escape_char(c: &char) -> char {
match c {
't' => '\t',
'n' => '\n',
'r' => '\r',
'0' => '\0',
_ => *c,
}
}
enum NumberType {
Decimal(String),
Hexadecimal(String),
Octal(String),
Binary(String),
}
impl AddAssign<char> for NumberType {
fn add_assign(&mut self, rhs: char) {
*self = match self {
NumberType::Decimal(val) => NumberType::Decimal(val.to_owned() + &rhs.to_string()),
NumberType::Hexadecimal(val) => NumberType::Hexadecimal(val.to_owned() + &rhs.to_string()),
NumberType::Octal(val) => NumberType::Octal(val.to_owned() + &rhs.to_string()),
NumberType::Binary(val) => NumberType::Binary(val.to_owned() + &rhs.to_string()),
};
}
}
#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Error {
#[error("Invalid token '{}' ", .0)]

View File

@ -8,36 +8,22 @@
//! Much of the syntax in Reid is directly inspired by rust, but mostly it is
//! driven by simplicity.
//!
//! Specifications and a bunch of [documentation for the language can be found
//! here](./documentation/).
//!
//! An example of a real whole program (a CPU pathtracer) can be found [in
//! examples/cpu_raytracer.reid](./examples/cpu_raytracer.reid), go have a look!
//!
//! Reid is currently able to (non-exhaustively):
//! - Do basic algebra binary and unary-operations (e.g. Add, Sub, Div, Mult,
//! And, Not)
//! - Do basic algebra (e.g. Add, Sub, Mult)
//! - Resolve complex one-liners correctly using PEDMAS (e.g. `5 + 2 * 5 - 5 *
//! 5` is calculated correctly)
//! - Handle borrows/derefs, pointers.
//! - Declare and call functions with varying parameters and return types
//! - Perform type-checking and type-inference such that return-types and
//! parameter types must always match.
//! - Do simple logic-operations (e.g. If/And/Or)
//! - Handle, access, define and initialize structs and arrays.
//! - Define and execute For/While loops
//! - Output detailed debug information
//! - Define extern functions that can be linked to outside modules such as
//! `libc`.
//! - Define custom binary operations for any two types that hasn't been defined
//! previously (such as `u16 + u32`).
//!
//!
//! An example program of Reid, that calculates the 5th fibonacci number:
//! An example program of Reid, that calculates the 5th fibonacci number (and
//! uses Rust for highlighting) is:
//! ```reid
//! fn main() -> u16 {
//! return fibonacci(5);
//! }
//!
//! fn fibonacci(n: u16) -> u16 {
//! if n <= 2 {
//! return 1;
@ -46,51 +32,41 @@
//! }
//! ```
//!
//! TODOs still (see README.md for more)
//! - Error handling
//! - Lexing & parsing of whitespace and comments as well
//! - LSP implementation
//! Currently missing relevant features (TODOs) are:
//! - ~~Arrays~~ (DONE)
//! - Structs (and custom types as such)
//! - ~~Extern functions~~ (DONE)
//! - ~~Strings~~ (DONE)
//! - Loops
//! - Debug Symbols
//! ```
use std::{collections::HashMap, path::PathBuf};
use std::path::PathBuf;
use ast::{
lexer::{self, FullToken, Token},
token_stream::TokenStream,
};
use codegen::intrinsics::{form_intrinsic_binops, form_intrinsics};
use error_raporting::{ErrorKind as ErrorRapKind, ErrorModules, ReidError};
use error_raporting::{ErrorKind as ErrorRapKind, ModuleMap, ReidError};
use lexer::FullToken;
use mir::{
linker::LinkerPass,
pass::BinopMap,
typecheck::{typecheck::TypeCheck, typeinference::TypeInference, typerefs::TypeRefs},
linker::LinkerPass, typecheck::TypeCheck, typeinference::TypeInference, typerefs::TypeRefs,
};
use reid_lib::{compile::CompileOutput, Context};
use crate::{
ast::TopLevelStatement,
mir::{
macros::{form_macros, MacroModule, MacroPass},
SourceModuleId,
},
};
use crate::{ast::TopLevelStatement, lexer::Token, token_stream::TokenStream};
pub mod ast;
pub mod codegen;
pub mod error_raporting;
pub mod ld;
mod ast;
mod codegen;
mod error_raporting;
mod lexer;
pub mod mir;
mod pad_adapter;
mod token_stream;
mod util;
pub fn parse_module<'map, T: Into<String>>(
source: &str,
name: T,
path: Option<PathBuf>,
map: &'map mut ErrorModules,
module_id: Option<SourceModuleId>,
map: &'map mut ModuleMap,
) -> Result<(mir::SourceModuleId, Vec<FullToken>), ReidError> {
let id = map.add_module(name.into(), path, module_id).unwrap();
let id = map.add_module(name.into()).unwrap();
map.set_source(id, source.to_owned());
let tokens = ReidError::from_lexer(lexer::tokenize(source), map.clone(), id)?;
@ -98,18 +74,18 @@ pub fn parse_module<'map, T: Into<String>>(
map.set_tokens(id, tokens.clone());
#[cfg(debug_assertions)]
log::trace!("{:#?}", &tokens);
dbg!(&tokens);
Ok((id, tokens))
}
pub fn compile_module<'map>(
module_id: mir::SourceModuleId,
tokens: Vec<FullToken>,
map: &'map mut ErrorModules,
tokens: &Vec<FullToken>,
map: &'map mut ModuleMap,
path: Option<PathBuf>,
is_main: bool,
) -> Result<Result<mir::Module, (ast::Module, ReidError)>, ReidError> {
) -> Result<mir::Module, ReidError> {
let module = map.module(&module_id).cloned().unwrap();
let mut token_stream = TokenStream::from(&tokens);
@ -117,146 +93,61 @@ pub fn compile_module<'map>(
let mut statements = Vec::new();
while !matches!(token_stream.peek().unwrap_or(Token::Eof), Token::Eof) {
let statement = ReidError::from_parser(token_stream.parse::<TopLevelStatement>(), map.clone(), module_id)?;
let statement = ReidError::from_parser(
token_stream.parse::<TopLevelStatement>(),
map.clone(),
module_id,
)?;
statements.push(statement);
}
let errors = token_stream.errors();
drop(token_stream);
let ast_module = ast::Module {
name: module.name,
tokens: tokens,
top_level_statements: statements,
path,
is_main,
};
if errors.len() > 0 {
// dbg!(&ast_module);
return Ok(Err((
ast_module,
ReidError::from_kind(
errors
.into_iter()
.map(|e| {
error_raporting::ErrorKind::from(mir::pass::Error {
metadata: mir::Metadata {
source_module_id: module_id,
range: *e.get_range().unwrap_or(&Default::default()),
position: None,
},
kind: e,
})
})
.collect(),
map.clone(),
),
)));
}
#[cfg(debug_assertions)]
log::trace!("{:#?}", &ast_module);
Ok(Ok(ast_module.process(module_id)))
Ok(ast_module.process(module_id))
}
pub fn perform_all_passes<'map>(
context: &mut mir::Context,
module_map: &'map mut ErrorModules,
module_map: &'map mut ModuleMap,
) -> Result<(), ReidError> {
#[cfg(debug_assertions)]
log::trace!("{:#?}", &context);
dbg!(&context);
#[cfg(debug_assertions)]
log::trace!("{:#}", &context);
println!("{}", &context);
let state = context.pass(&mut LinkerPass {
module_map,
is_lib: true,
})?;
for module in &mut context.modules {
for intrinsic in form_intrinsics() {
module.1.functions.insert(0, intrinsic);
}
}
let state = context.pass(&mut LinkerPass { module_map })?;
#[cfg(debug_assertions)]
log::trace!("{:-^100}", "LINKER OUTPUT");
println!("{}", &context);
#[cfg(debug_assertions)]
log::trace!("{:#}", &context);
#[cfg(debug_assertions)]
log::trace!("{:#?}", &state);
dbg!(&state);
if !state.errors.is_empty() {
return Err(ReidError::from_kind(
return Err(ReidError::from_kind::<()>(
state.errors.iter().map(|e| e.clone().into()).collect(),
module_map.clone(),
));
}
let mut macro_modules: HashMap<_, MacroModule> = HashMap::new();
for (k, v) in &context.modules {
macro_modules.insert(k.clone(), v.into());
}
let refs = TypeRefs::default();
let mut macro_pass = MacroPass {
macros: form_macros(),
module_map: macro_modules,
};
let state = context.pass(&mut macro_pass)?;
let state = context.pass(&mut TypeInference { refs: &refs })?;
#[cfg(debug_assertions)]
log::trace!("{:-^100}", "MACRO OUTPUT");
dbg!(&refs);
#[cfg(debug_assertions)]
log::trace!("{:#}", &context);
println!("{}", &context);
#[cfg(debug_assertions)]
log::trace!("{:#?}", &state);
dbg!(&state);
if !state.errors.is_empty() {
return Err(ReidError::from_kind(
state.errors.iter().map(|e| e.clone().into()).collect(),
module_map.clone(),
));
}
let mut binops = BinopMap::default();
for module in &mut context.modules {
for intrinsic in form_intrinsic_binops() {
binops
.set(
mir::pass::BinopKey {
params: (intrinsic.lhs.ty.clone(), intrinsic.rhs.ty.clone()),
operator: intrinsic.op,
},
mir::pass::ScopeBinopDef {
hands: (intrinsic.lhs.ty.clone(), intrinsic.rhs.ty.clone()),
operator: intrinsic.op,
return_ty: intrinsic.return_type.clone(),
},
)
.ok();
module.1.binop_defs.insert(0, intrinsic);
}
}
let mut refs = TypeRefs::with_binops(binops);
let state = context.pass(&mut TypeInference { refs: &mut refs })?;
#[cfg(debug_assertions)]
log::trace!("{:-^70}", "TYPE INFERRER OUTPUT");
#[cfg(debug_assertions)]
log::trace!("{}", &refs);
#[cfg(debug_assertions)]
log::trace!("{:#}", &context);
#[cfg(debug_assertions)]
log::trace!("{:#?}", &state);
if !state.errors.is_empty() {
return Err(ReidError::from_kind(
return Err(ReidError::from_kind::<()>(
state
.errors
.iter()
@ -269,14 +160,12 @@ pub fn perform_all_passes<'map>(
let state = context.pass(&mut TypeCheck { refs: &refs })?;
#[cfg(debug_assertions)]
log::trace!("{:-^100}", "TYPECHECKER OUTPUT");
println!("{}", &context);
#[cfg(debug_assertions)]
log::trace!("{:#}", &context);
#[cfg(debug_assertions)]
log::trace!("{:#?}", &state);
dbg!(&state);
if !state.errors.is_empty() {
return Err(ReidError::from_kind(
return Err(ReidError::from_kind::<()>(
state
.errors
.iter()
@ -295,55 +184,34 @@ pub fn perform_all_passes<'map>(
pub fn compile_and_pass<'map>(
source: &str,
path: PathBuf,
module_map: &'map mut ErrorModules,
cpu: Option<String>,
features: Vec<String>,
) -> Result<(CompileOutput, CustomIRs), ReidError> {
module_map: &'map mut ModuleMap,
) -> Result<CompileOutput, ReidError> {
let path = path.canonicalize().unwrap();
let name = path.file_name().unwrap().to_str().unwrap().to_owned();
let (id, tokens) = parse_module(source, name, Some(path.clone()), module_map, None)?;
let module = compile_module(id, tokens, module_map, Some(path.clone()), true)?.map_err(|(_, e)| e)?;
let (id, tokens) = parse_module(source, name, module_map)?;
let module = compile_module(id, &tokens, module_map, Some(path.clone()), true)?;
let mut mir_context = mir::Context::from(vec![module], path.parent().unwrap().to_owned());
perform_all_passes(&mut mir_context, module_map)?;
#[cfg(debug_assertions)]
log::trace!("{:-^100}", "FINAL OUTPUT");
dbg!(&mir_context);
#[cfg(debug_assertions)]
log::trace!("{:#}", &mir_context);
println!("{}", &mir_context);
let mut context = Context::new(format!("Reid ({})", env!("CARGO_PKG_VERSION")));
let codegen_modules = match mir_context.codegen(&mut context) {
Ok(modules) => modules,
Err(e) => Err(ReidError::from_kind(vec![e.into()], module_map.clone()))?,
};
let codegen_modules = mir_context.codegen(&mut context, &module_map);
#[cfg(debug_assertions)]
log::trace!("{}", &codegen_modules.context);
dbg!(&codegen_modules);
let compiled = codegen_modules.compile(cpu, features);
Ok((
compiled.output(),
CustomIRs {
llir: format!("{}", codegen_modules.context),
mir: format!("{}", mir_context),
},
))
let compiled = codegen_modules.compile();
Ok(compiled.output())
}
pub struct CustomIRs {
pub llir: String,
pub mir: String,
}
pub fn compile_simple(
source: &str,
path: PathBuf,
cpu: Option<String>,
features: Vec<String>,
) -> Result<(CompileOutput, CustomIRs), ReidError> {
let mut map = ErrorModules::default();
compile_and_pass(source, path, &mut map, cpu, features)
pub fn compile_simple(source: &str, path: PathBuf) -> Result<CompileOutput, ReidError> {
let mut map = ModuleMap::default();
compile_and_pass(source, path, &mut map)
}

View File

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

View File

@ -6,7 +6,7 @@ use super::*;
impl Display for Context {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (_, module) in &self.modules {
for module in &self.modules {
Display::fmt(&module, f)?;
}
Ok(())
@ -15,9 +15,7 @@ impl Display for Context {
impl Display for Module {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let is_alternate = f.alternate();
writeln!(f, "Module({}) ({}) {{", self.name, self.module_id)?;
writeln!(f, "Module({}) {{", self.name)?;
let mut state = Default::default();
let mut inner_f = PadAdapter::wrap(f, &mut state);
@ -25,36 +23,9 @@ impl Display for Module {
for import in &self.imports {
writeln!(inner_f, "{}", import)?;
}
let intrinsic_binops = self
.binop_defs
.iter()
.filter(|b| matches!(b.fn_kind, FunctionDefinitionKind::Intrinsic(_)));
for binop in self
.binop_defs
.iter()
.filter(|b| !matches!(b.fn_kind, FunctionDefinitionKind::Intrinsic(_)))
{
writeln!(inner_f, "{}", binop)?;
}
if is_alternate {
writeln!(inner_f, "... <{}> intrinsic binary operators", intrinsic_binops.count())?;
} else {
for binop in intrinsic_binops {
writeln!(inner_f, "{}", binop)?;
}
}
for typedef in &self.typedefs {
writeln!(inner_f, "{}", typedef)?;
}
for global in &self.globals {
writeln!(inner_f, "global {} = {}", global.name, global.kind)?;
}
for (ty, fun) in &self.associated_functions {
writeln!(inner_f, "(Assoc {}) {}", ty, fun)?;
}
for fun in &self.functions {
writeln!(inner_f, "{}", fun)?;
}
@ -62,66 +33,15 @@ impl Display for Module {
}
}
impl Display for GlobalKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GlobalKind::Literal(literal) => Display::fmt(literal, f),
GlobalKind::Array(global_kinds) => {
f.write_char('[')?;
let mut iter = global_kinds.iter();
if let Some(global) = iter.next() {
Display::fmt(global, f)?;
while let Some(global) = iter.next() {
write!(f, ", ")?;
Display::fmt(global, f)?;
}
}
f.write_char(']')
}
}
}
}
impl Display for Import {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"import {}",
self.0.iter().map(|(s, _)| s.clone()).collect::<Vec<_>>().join("::")
)
}
}
impl Display for BinopDefinition {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}impl binop ({}: {:#}) {} ({}: {:#}) -> {:#} ",
if self.exported { "exported " } else { "" },
self.lhs.name,
self.lhs.ty,
self.op,
self.rhs.name,
self.rhs.ty,
self.return_type
)?;
Display::fmt(&self.fn_kind, f)
write!(f, "import {}", self.0.join("::"))
}
}
impl Display for TypeDefinition {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"type {} (mod {}{}) = ",
self.name,
self.source_module,
if let Some(mod_id) = self.importer {
format!("; imported by {}", mod_id)
} else {
String::new()
}
)?;
write!(f, "type {} = ", self.name)?;
Display::fmt(&self.kind, f)
}
}
@ -152,17 +72,14 @@ impl Display for StructField {
impl Display for FunctionDefinition {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(documentation) = &self.documentation {
writeln!(f, "/// {}", documentation)?;
}
write!(
f,
"{}fn {}({}) -> {:#} ",
"{}fn {}({}) -> {} ",
if self.is_pub { "pub " } else { "" },
self.name,
self.parameters
.iter()
.map(|FunctionParam { name, ty, .. }| format!("{}: {:#}", name, ty))
.map(|(n, t)| format!("{}: {}", n, t))
.collect::<Vec<_>>()
.join(", "),
self.return_type
@ -174,13 +91,12 @@ impl Display for FunctionDefinition {
impl Display for FunctionDefinitionKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FunctionDefinitionKind::Local(block, _) => {
Self::Local(block, _) => {
write!(f, "{}", block)?;
Ok(())
}
FunctionDefinitionKind::Extern(true) => write!(f, "<Imported Extern>"),
FunctionDefinitionKind::Extern(false) => write!(f, "<Linked Extern>"),
FunctionDefinitionKind::Intrinsic(_) => write!(f, "<Intrinsic>"),
Self::Extern(true) => write!(f, "<Imported Extern>"),
Self::Extern(false) => write!(f, "<Linked Extern>"),
}
}
}
@ -194,15 +110,9 @@ impl Display for Block {
write!(inner_f, "{}", statement)?;
}
if let Some(ret) = &self.return_expression {
let ret_fmt = if let Some(ret) = &ret.1 {
format!("{}", ret)
} else {
String::from("void")
};
match ret.0 {
ReturnKind::Hard => writeln!(inner_f, "Return(Hard): {}", ret_fmt),
ReturnKind::Soft => writeln!(inner_f, "Return(Soft): {}", ret_fmt),
ReturnKind::Hard => writeln!(inner_f, "Return(Hard): {}", ret.1),
ReturnKind::Soft => writeln!(inner_f, "Return(Soft): {}", ret.1),
}?;
} else {
writeln!(inner_f, "No Return")?;
@ -220,16 +130,18 @@ impl Display for Statement {
impl Display for StmtKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
StmtKind::Let(var, mutable, block) => {
write!(f, "let{} {} = {}", if *mutable { " mut" } else { "" }, var, block)
}
StmtKind::Set(var, expr) => write!(f, "{} = {}", var, expr),
StmtKind::Import(n) => write!(f, "import {}", n),
StmtKind::Expression(exp) => Display::fmt(exp, f),
StmtKind::While(while_statement) => {
write!(f, "while {} {}", while_statement.condition, while_statement.block,)
Self::Let(var, mutable, block) => {
write!(
f,
"let{} {} = {}",
if *mutable { " mut" } else { "" },
var,
block
)
}
Self::Set(var, expr) => write!(f, "{} = {}", var, expr),
Self::Import(n) => write!(f, "import {}", n),
Self::Expression(exp) => Display::fmt(exp, f),
}
}
}
@ -248,18 +160,13 @@ impl Display for ExprKind {
match self {
ExprKind::Variable(var) => Display::fmt(var, f),
ExprKind::Literal(lit) => Display::fmt(lit, f),
ExprKind::BinOp(op, lhs, rhs, ty) => {
write!(f, "{} {} {} (= ", lhs, op, rhs)?;
Debug::fmt(ty, f)?;
f.write_char(')')?;
Ok(())
}
ExprKind::BinOp(op, lhs, rhs) => write!(f, "{} {} {}", lhs, op, rhs),
ExprKind::FunctionCall(fc) => Display::fmt(fc, f),
ExprKind::If(if_exp) => Display::fmt(&if_exp, f),
ExprKind::Block(block) => Display::fmt(block, f),
ExprKind::Indexed(expression, elem_ty, idx_expr) => {
Display::fmt(&expression, f)?;
write!(f, "<{:#}>", elem_ty)?;
write!(f, "<{}>", elem_ty)?;
write_index(f, idx_expr)
}
ExprKind::Array(expressions) => {
@ -279,16 +186,16 @@ impl Display for ExprKind {
}
f.write_char(']')
}
ExprKind::Struct(key, items) => {
write!(f, "{:?} ", key)?;
ExprKind::Struct(name, items) => {
write!(f, "{} ", name)?;
f.write_char('{')?;
let mut state = Default::default();
let mut inner_f = PadAdapter::wrap(f, &mut state);
let mut iter = items.iter();
if let Some((name, expr, _)) = iter.next() {
if let Some((name, expr)) = iter.next() {
write!(inner_f, "\n{}: {}", name, expr)?;
while let Some((name, expr, _)) = iter.next() {
while let Some((name, expr)) = iter.next() {
writeln!(inner_f, ",")?;
write!(inner_f, "{}: {}", name, expr)?;
}
@ -296,21 +203,13 @@ impl Display for ExprKind {
}
f.write_char('}')
}
ExprKind::Accessed(expression, type_kind, name, _) => {
ExprKind::Accessed(expression, type_kind, name) => {
Display::fmt(&expression, f)?;
write_access(f, name)?;
write!(f, "<{}>", type_kind)
}
ExprKind::Borrow(var_ref, false) => write!(f, "&{}", var_ref),
ExprKind::Borrow(var_ref, true) => write!(f, "&mut {}", var_ref),
ExprKind::Borrow(var_ref) => write!(f, "&{}", var_ref),
ExprKind::Deref(var_ref) => write!(f, "*{}", var_ref),
ExprKind::CastTo(expression, type_kind) => write!(f, "{} as {}", expression, type_kind),
ExprKind::AssociatedFunctionCall(type_kind, function_call) => {
Display::fmt(type_kind, f)?;
write!(f, "::")?;
Display::fmt(function_call, f)
}
ExprKind::GlobalRef(global_value, type_kind) => write!(f, "global<{}>(${})", type_kind, global_value),
}
}
}
@ -319,7 +218,7 @@ impl Display for IfExpression {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "if {} ", self.0)?;
Display::fmt(&self.1, f)?;
if let Some(e) = self.2.as_ref() {
if let Some(e) = &self.2 {
Display::fmt(&e, f)?;
}
Ok(())
@ -328,7 +227,7 @@ impl Display for IfExpression {
impl Display for FunctionCall {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}<{:#}>(", self.name, self.return_type)?;
write!(f, "{}<{}>(", self.name, self.return_type)?;
for (i, param) in self.parameters.iter().enumerate() {
Display::fmt(param, f)?;
if i < (self.parameters.len() - 1) {
@ -341,7 +240,7 @@ impl Display for FunctionCall {
impl Display for NamedVariableRef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "v(\"{}\", {:#})", &self.1, &self.0)
write!(f, "v(\"{}\", {})", &self.1, &self.0)
}
}
@ -361,14 +260,6 @@ impl Display for Literal {
Literal::Bool(val) => write!(f, "{}", val),
Literal::String(val) => std::fmt::Debug::fmt(val, f),
Literal::Vague(val) => val.fmt(f),
Literal::F16(val) => write!(f, "{}f16", val),
Literal::F32B(val) => write!(f, "{}f16b", val),
Literal::F32(val) => write!(f, "{}f32", val),
Literal::F64(val) => write!(f, "{}f64", val),
Literal::F80(val) => write!(f, "{}f80", val),
Literal::F128(val) => write!(f, "{}f128", val),
Literal::F128PPC(val) => write!(f, "{}f128ppc", val),
Literal::Char(c) => std::fmt::Debug::fmt(c, f),
}
}
}
@ -381,14 +272,6 @@ impl Display for BinaryOperator {
BinaryOperator::Mult => write!(f, "*"),
BinaryOperator::And => write!(f, "&&"),
BinaryOperator::Cmp(op) => Display::fmt(op, f),
BinaryOperator::Div => write!(f, "/"),
BinaryOperator::Mod => write!(f, "%"),
BinaryOperator::Or => write!(f, "||"),
BinaryOperator::Xor => write!(f, "^"),
BinaryOperator::BitOr => write!(f, "|"),
BinaryOperator::BitAnd => write!(f, "&"),
BinaryOperator::BitshiftRight => write!(f, ">>"),
BinaryOperator::BitshiftLeft => write!(f, "<<"),
}
}
}
@ -414,7 +297,7 @@ impl Display for Metadata {
impl Display for SourceModuleId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
write!(f, "Mod {}", self.0)
}
}
@ -428,75 +311,3 @@ fn write_access(f: &mut std::fmt::Formatter<'_>, name: &String) -> std::fmt::Res
f.write_char('.')?;
Display::fmt(name, f)
}
impl Display for TypeKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TypeKind::Bool => write!(f, "bool"),
TypeKind::I8 => write!(f, "i8"),
TypeKind::I16 => write!(f, "i16"),
TypeKind::I32 => write!(f, "i32"),
TypeKind::I64 => write!(f, "i64"),
TypeKind::I128 => write!(f, "i128"),
TypeKind::U8 => write!(f, "u8"),
TypeKind::U16 => write!(f, "u16"),
TypeKind::U32 => write!(f, "u32"),
TypeKind::U64 => write!(f, "u64"),
TypeKind::U128 => write!(f, "u128"),
TypeKind::Void => write!(f, "void"),
TypeKind::Char => write!(f, "char"),
TypeKind::Array(type_kind, len) => {
f.write_char('[')?;
Display::fmt(type_kind, f)?;
write!(f, "; ")?;
Display::fmt(len, f)?;
f.write_char(']')
}
TypeKind::CustomType(CustomTypeKey(name, mod_id)) => write!(f, "{}@{}", name, mod_id),
TypeKind::Borrow(type_kind, false) => {
write!(f, "&")?;
Display::fmt(type_kind, f)
}
TypeKind::Borrow(type_kind, true) => {
write!(f, "&mut ")?;
Display::fmt(type_kind, f)
}
TypeKind::UserPtr(type_kind) => {
write!(f, "*")?;
Display::fmt(type_kind, f)
}
TypeKind::CodegenPtr(type_kind) => {
write!(f, "CodegenPtr ")?;
Display::fmt(type_kind, f)
}
TypeKind::Vague(vague_type) => Display::fmt(vague_type, f),
TypeKind::F16 => write!(f, "f16"),
TypeKind::F16B => write!(f, "f16b"),
TypeKind::F32 => write!(f, "f32"),
TypeKind::F64 => write!(f, "f64"),
TypeKind::F128 => write!(f, "f128"),
TypeKind::F80 => write!(f, "f80"),
TypeKind::F128PPC => write!(f, "f128ppc"),
}
}
}
impl Display for VagueType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.alternate() {
match self {
VagueType::Unknown => write!(f, "Unknown"),
VagueType::Integer => write!(f, "Number"),
VagueType::TypeRef(id) => write!(f, "TypeRef({0})", id),
VagueType::Decimal => write!(f, "Decimal"),
}
} else {
match self {
VagueType::Unknown => write!(f, "{{unknown}}"),
VagueType::Integer => write!(f, "Number"),
VagueType::TypeRef(_) => write!(f, "{{unknown}}"),
VagueType::Decimal => write!(f, "Decimal"),
}
}
}
}

382
reid/src/mir/impl.rs Normal file
View File

@ -0,0 +1,382 @@
use super::{typecheck::ErrorKind, typerefs::TypeRefs, VagueType as Vague, *};
#[derive(Debug, Clone)]
pub enum ReturnTypeOther {
Import(Metadata),
Let(Metadata),
Set(Metadata),
EmptyBlock(Metadata),
NoBlockReturn(Metadata),
IndexingNonArray(Metadata),
DerefNonBorrow(Metadata),
}
impl TypeKind {
/// Return the type that is the result of a binary operator between two
/// values of this type
pub fn binop_type(&self, op: &BinaryOperator) -> TypeKind {
// TODO make some type of mechanism that allows to binop two values of
// differing types..
// TODO Return None for arrays later
match op {
BinaryOperator::Add => self.clone(),
BinaryOperator::Minus => self.clone(),
BinaryOperator::Mult => self.clone(),
BinaryOperator::And => TypeKind::Bool,
BinaryOperator::Cmp(_) => TypeKind::Bool,
}
}
pub fn size_of(&self) -> u64 {
match self {
TypeKind::Bool => 1,
TypeKind::I8 => 8,
TypeKind::U8 => 8,
TypeKind::I16 => 16,
TypeKind::U16 => 16,
TypeKind::I32 => 32,
TypeKind::U32 => 32,
TypeKind::I64 => 64,
TypeKind::U64 => 64,
TypeKind::I128 => 128,
TypeKind::U128 => 128,
TypeKind::Void => 0,
TypeKind::StringPtr => 32,
TypeKind::Array(type_kind, len) => type_kind.size_of() * len,
TypeKind::CustomType(_) => 32,
TypeKind::Ptr(_) => 64,
TypeKind::Vague(_) => panic!("Tried to sizeof a vague type!"),
TypeKind::Borrow(_) => 64,
}
}
pub fn alignment(&self) -> u32 {
match self {
TypeKind::Bool => 1,
TypeKind::I8 => 8,
TypeKind::U8 => 8,
TypeKind::I16 => 16,
TypeKind::U16 => 16,
TypeKind::I32 => 32,
TypeKind::U32 => 32,
TypeKind::I64 => 64,
TypeKind::U64 => 64,
TypeKind::I128 => 128,
TypeKind::U128 => 128,
TypeKind::Void => 0,
TypeKind::StringPtr => 32,
TypeKind::Array(type_kind, _) => type_kind.alignment(),
TypeKind::CustomType(_) => 32,
TypeKind::Ptr(_) => 64,
TypeKind::Vague(_) => panic!("Tried to sizeof a vague type!"),
TypeKind::Borrow(_) => 64,
}
}
}
impl StructType {
pub fn get_field_ty(&self, name: &String) -> Option<&TypeKind> {
self.0
.iter()
.find(|StructField(n, _, _)| n == name)
.map(|StructField(_, ty, _)| ty)
}
pub fn get_field_ty_mut(&mut self, name: &String) -> Option<&mut TypeKind> {
self.0
.iter_mut()
.find(|StructField(n, _, _)| n == name)
.map(|StructField(_, ty, _)| ty)
}
}
enum BlockReturn<'b> {
Early(&'b Statement),
Normal(ReturnKind, &'b Expression),
}
impl Block {
fn return_expr(&self) -> Result<BlockReturn, ReturnTypeOther> {
let mut early_return = None;
for statement in &self.statements {
let ret = statement.return_type(&Default::default());
if let Ok((ReturnKind::Hard, _)) = ret {
early_return = Some(statement);
}
}
if let Some(s) = early_return {
return Ok(BlockReturn::Early(s));
}
self.return_expression
.as_ref()
.map(|(r, e)| BlockReturn::Normal(*r, e))
.ok_or(ReturnTypeOther::NoBlockReturn(self.meta))
}
pub fn return_meta(&self) -> Metadata {
self.return_expression
.as_ref()
.map(|e| e.1 .1)
.or(self.statements.last().map(|s| s.1))
.unwrap_or(self.meta)
}
pub fn return_type(&self, refs: &TypeRefs) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
let mut early_return = None;
for statement in &self.statements {
let ret = statement.return_type(refs);
if let Ok((ReturnKind::Hard, _)) = ret {
early_return = early_return.or(ret.ok());
}
}
if let Some((ReturnKind::Hard, ret_ty)) = early_return {
return Ok((ReturnKind::Hard, ret_ty));
}
self.return_expression
.as_ref()
.ok_or(ReturnTypeOther::NoBlockReturn(self.meta))
.and_then(|(kind, stmt)| Ok((*kind, stmt.return_type(refs)?.1)))
}
pub fn backing_var(&self) -> Option<&NamedVariableRef> {
match self.return_expr().ok()? {
BlockReturn::Early(statement) => statement.backing_var(),
BlockReturn::Normal(kind, expr) => {
if kind == ReturnKind::Soft {
expr.backing_var()
} else {
None
}
}
}
}
}
impl Statement {
pub fn return_type(&self, refs: &TypeRefs) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
use StmtKind::*;
match &self.0 {
Let(var, _, expr) => if_hard(
expr.return_type(refs)?,
Err(ReturnTypeOther::Let(var.2 + expr.1)),
),
Set(lhs, rhs) => if_hard(
rhs.return_type(refs)?,
Err(ReturnTypeOther::Set(lhs.1 + rhs.1)),
),
Import(_) => todo!(),
Expression(expression) => expression.return_type(refs),
}
}
pub fn backing_var(&self) -> Option<&NamedVariableRef> {
match &self.0 {
StmtKind::Let(_, _, _) => None,
StmtKind::Set(_, _) => None,
StmtKind::Import(_) => None,
StmtKind::Expression(expr) => expr.backing_var(),
}
}
}
impl Expression {
pub fn return_type(&self, refs: &TypeRefs) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
use ExprKind::*;
match &self.0 {
Literal(lit) => Ok((ReturnKind::Soft, lit.as_type())),
Variable(var) => var.return_type(),
BinOp(_, then_e, else_e) => {
let then_r = then_e.return_type(refs)?;
let else_r = else_e.return_type(refs)?;
Ok(pick_return(then_r, else_r))
}
Block(block) => block.return_type(refs),
FunctionCall(fcall) => fcall.return_type(),
If(expr) => expr.return_type(refs),
Indexed(expression, _, _) => {
let expr_type = expression.return_type(refs)?;
if let TypeKind::Array(elem_ty, _) = expr_type.1.resolve_weak(refs) {
Ok((ReturnKind::Soft, *elem_ty))
} else {
Err(ReturnTypeOther::IndexingNonArray(expression.1))
}
}
Array(expressions) => {
let first = expressions
.iter()
.next()
.map(|e| e.return_type(refs))
.unwrap_or(Ok((ReturnKind::Soft, TypeKind::Void)))?;
Ok((
ReturnKind::Soft,
TypeKind::Array(Box::new(first.1), expressions.len() as u64),
))
}
Accessed(_, type_kind, _) => Ok((ReturnKind::Soft, type_kind.clone())),
Struct(name, _) => Ok((ReturnKind::Soft, TypeKind::CustomType(name.clone()))),
Borrow(var) => {
let ret_type = var.return_type()?;
Ok((ret_type.0, TypeKind::Borrow(Box::new(ret_type.1))))
}
Deref(var) => {
let (kind, ret_type) = var.return_type()?;
match ret_type.resolve_weak(refs) {
TypeKind::Borrow(type_kind) => Ok((kind, *type_kind)),
_ => Err(ReturnTypeOther::DerefNonBorrow(var.2)),
}
}
}
}
pub fn backing_var(&self) -> Option<&NamedVariableRef> {
match &self.0 {
ExprKind::Variable(var_ref) => Some(var_ref),
ExprKind::Indexed(lhs, _, _) => lhs.backing_var(),
ExprKind::Accessed(lhs, _, _) => lhs.backing_var(),
ExprKind::Borrow(var) => Some(var),
ExprKind::Deref(var) => Some(var),
ExprKind::Block(block) => block.backing_var(),
ExprKind::Array(_) => None,
ExprKind::Struct(_, _) => None,
ExprKind::Literal(_) => None,
ExprKind::BinOp(_, _, _) => None,
ExprKind::FunctionCall(_) => None,
ExprKind::If(_) => None,
}
}
}
impl IfExpression {
pub fn return_type(&self, refs: &TypeRefs) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
let then_r = self.1.return_type(refs)?;
if let Some(else_b) = &self.2 {
let else_r = else_b.return_type(refs)?;
let kind = if then_r.0 == ReturnKind::Hard && else_r.0 == ReturnKind::Hard {
ReturnKind::Hard
} else {
ReturnKind::Soft
};
Ok((kind, then_r.1))
} else {
Ok((ReturnKind::Soft, then_r.1))
}
}
}
impl NamedVariableRef {
pub fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
Ok((ReturnKind::Soft, self.0.clone()))
}
}
impl FunctionCall {
pub fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
Ok((ReturnKind::Soft, self.return_type.clone()))
}
}
fn if_hard<TErr>(
return_type: (ReturnKind, TypeKind),
default: Result<(ReturnKind, TypeKind), TErr>,
) -> Result<(ReturnKind, TypeKind), TErr> {
if let (ReturnKind::Hard, _) = return_type {
Ok(return_type)
} else {
default
}
}
pub fn pick_return<T>(lhs: (ReturnKind, T), rhs: (ReturnKind, T)) -> (ReturnKind, T) {
use ReturnKind::*;
match (lhs.0, rhs.0) {
(Hard, Hard) => (Hard, lhs.1),
(Hard, Soft) => (Soft, rhs.1),
(Soft, Hard) => (Soft, lhs.1),
(_, _) => (Soft, lhs.1),
}
}
impl TypeKind {
/// Assert that a type is already known and not vague. Return said type or
/// error.
pub fn assert_known(&self) -> Result<TypeKind, ErrorKind> {
self.known().map_err(ErrorKind::TypeIsVague)
}
/// Try to collapse a type on itself producing a default type if one exists,
/// Error if not.
pub fn or_default(&self) -> Result<TypeKind, ErrorKind> {
match self {
TypeKind::Vague(vague_type) => match &vague_type {
Vague::Unknown => Err(ErrorKind::TypeIsVague(*vague_type)),
Vague::Number => Ok(TypeKind::I32),
Vague::TypeRef(_) => panic!("Hinted default!"),
},
_ => Ok(self.clone()),
}
}
pub fn resolve_weak(&self, refs: &TypeRefs) -> TypeKind {
match self {
TypeKind::Vague(Vague::TypeRef(idx)) => refs.retrieve_type(*idx).unwrap(),
_ => self.clone(),
}
}
pub fn resolve_ref(&self, refs: &TypeRefs) -> TypeKind {
let resolved = self.resolve_weak(refs);
match resolved {
TypeKind::Array(t, len) => TypeKind::Array(Box::new(t.resolve_ref(refs)), len),
TypeKind::Borrow(inner) => TypeKind::Borrow(Box::new(inner.resolve_ref(refs))),
_ => resolved,
}
}
}
#[derive(Debug, Clone, thiserror::Error, PartialEq, Eq, PartialOrd, Ord)]
pub enum EqualsIssue {
#[error("Function is already defined locally at {:?}", (.0).range)]
ExistsLocally(Metadata),
#[error("Equals")]
Equals,
#[error("Function {0} is already declared locally at {:?}", (.1).range)]
AlreadyExtern(String, Metadata),
#[error("Function {0} is already imported from another module")]
ConflictWithImport(String),
}
impl FunctionDefinition {
pub fn equals_as_imported(&self, other: &FunctionDefinition) -> Result<(), EqualsIssue> {
match &self.kind {
FunctionDefinitionKind::Local(_, metadata) => {
Err(EqualsIssue::ExistsLocally(*metadata))
}
FunctionDefinitionKind::Extern(imported) => {
if *imported {
Err(EqualsIssue::ConflictWithImport(self.name.clone()))
} else {
if self.is_pub == other.is_pub
&& self.name == other.name
&& self.parameters == other.parameters
&& self.return_type == other.return_type
{
Ok(())
} else {
Err(EqualsIssue::AlreadyExtern(
self.name.clone(),
self.signature(),
))
}
}
}
}
}
}

View File

@ -1,684 +0,0 @@
use crate::util::maybe;
use super::{typecheck::typerefs::TypeRefs, *};
#[derive(Debug, Clone)]
pub enum ReturnTypeOther {
Import(Metadata),
Let(Metadata),
Set(Metadata),
EmptyBlock(Metadata),
NoBlockReturn(Metadata),
IndexingNonArray(Metadata),
DerefNonBorrow(Metadata),
Loop,
}
#[derive(thiserror::Error, Debug, Clone, PartialEq, PartialOrd)]
pub enum NumValueError {
#[error("Cannot divide by zero")]
DivideZero,
}
enum BlockReturn<'b> {
Early(&'b Statement),
Normal(ReturnKind, &'b Option<Box<Expression>>),
}
impl TypeKind {
pub fn signed(&self) -> bool {
match self {
TypeKind::Bool => false,
TypeKind::I8 => true,
TypeKind::I16 => true,
TypeKind::I32 => true,
TypeKind::I64 => true,
TypeKind::I128 => true,
TypeKind::U8 => false,
TypeKind::U16 => false,
TypeKind::U32 => false,
TypeKind::U64 => false,
TypeKind::U128 => false,
TypeKind::Void => false,
TypeKind::Char => false,
TypeKind::Array(..) => false,
TypeKind::CustomType(..) => false,
TypeKind::CodegenPtr(..) => false,
TypeKind::Vague(..) => false,
TypeKind::Borrow(..) => false,
TypeKind::UserPtr(..) => false,
TypeKind::F16 => true,
TypeKind::F16B => true,
TypeKind::F32 => true,
TypeKind::F64 => true,
TypeKind::F128 => true,
TypeKind::F80 => true,
TypeKind::F128PPC => true,
}
}
pub fn size_of(&self, map: &HashMap<CustomTypeKey, TypeDefinition>) -> u64 {
match self {
TypeKind::Bool => 1,
TypeKind::I8 => 8,
TypeKind::U8 => 8,
TypeKind::I16 => 16,
TypeKind::U16 => 16,
TypeKind::I32 => 32,
TypeKind::U32 => 32,
TypeKind::I64 => 64,
TypeKind::U64 => 64,
TypeKind::I128 => 128,
TypeKind::U128 => 128,
TypeKind::Void => 0,
TypeKind::Char => 8,
TypeKind::Array(type_kind, len) => type_kind.size_of(map) * (*len as u64),
TypeKind::CustomType(key) => match &map.get(key) {
Some(def) => match &def.kind {
TypeDefinitionKind::Struct(struct_type) => {
let mut size = 0;
for field in &struct_type.0 {
size += field.1.size_of(map)
}
size
}
},
// Easy to recognize default number. Used e.g. when sorting
// types by size
None => 404,
},
TypeKind::CodegenPtr(_) => 64,
TypeKind::Vague(_) => panic!("Tried to sizeof a vague type!"),
TypeKind::Borrow(..) => 64,
TypeKind::UserPtr(_) => 64,
TypeKind::F16 => 16,
TypeKind::F16B => 16,
TypeKind::F32 => 32,
TypeKind::F64 => 64,
TypeKind::F128 => 128,
TypeKind::F80 => 80,
TypeKind::F128PPC => 128,
}
}
pub fn alignment(&self) -> u32 {
match self {
TypeKind::Bool => 1,
TypeKind::I8 => 8,
TypeKind::U8 => 8,
TypeKind::I16 => 16,
TypeKind::U16 => 16,
TypeKind::I32 => 32,
TypeKind::U32 => 32,
TypeKind::I64 => 64,
TypeKind::U64 => 64,
TypeKind::I128 => 128,
TypeKind::U128 => 128,
TypeKind::Void => 0,
TypeKind::Char => 8,
TypeKind::Array(type_kind, _) => type_kind.alignment(),
TypeKind::CustomType(..) => 32,
TypeKind::CodegenPtr(_) => 64,
TypeKind::Vague(_) => panic!("Tried to sizeof a vague type!"),
TypeKind::Borrow(_, _) => 64,
TypeKind::UserPtr(_) => 64,
TypeKind::F16 => 16,
TypeKind::F16B => 16,
TypeKind::F32 => 32,
TypeKind::F64 => 64,
TypeKind::F128 => 128,
TypeKind::F80 => 80,
TypeKind::F128PPC => 128,
}
}
pub fn is_mutable(&self) -> bool {
match self {
TypeKind::Borrow(_, false) => false,
_ => true,
}
}
pub fn category(&self) -> TypeCategory {
match self {
TypeKind::I8
| TypeKind::I16
| TypeKind::I32
| TypeKind::I64
| TypeKind::I128
| TypeKind::U8
| TypeKind::U16
| TypeKind::U32
| TypeKind::U64
| TypeKind::U128
| TypeKind::Char => TypeCategory::Integer,
TypeKind::F16
| TypeKind::F16B
| TypeKind::F32
| TypeKind::F64
| TypeKind::F128
| TypeKind::F80
| TypeKind::F128PPC => TypeCategory::Real,
TypeKind::Void => TypeCategory::Other,
TypeKind::Bool => TypeCategory::Bool,
TypeKind::Array(_, _) => TypeCategory::Other,
TypeKind::CustomType(..) => TypeCategory::Other,
TypeKind::Borrow(_, _) => TypeCategory::Other,
TypeKind::UserPtr(_) => TypeCategory::Other,
TypeKind::CodegenPtr(_) => TypeCategory::Other,
TypeKind::Vague(vague_type) => match vague_type {
VagueType::Unknown => TypeCategory::Other,
VagueType::Integer => TypeCategory::Integer,
VagueType::Decimal => TypeCategory::Real,
VagueType::TypeRef(_) => TypeCategory::TypeRef,
},
}
}
pub fn try_collapse_two(
(lhs1, rhs1): (&TypeKind, &TypeKind),
(lhs2, rhs2): (&TypeKind, &TypeKind),
) -> Option<(TypeKind, TypeKind)> {
if let (Ok(lhs), Ok(rhs)) = (lhs1.narrow_into(&lhs2), rhs1.narrow_into(&rhs2)) {
Some((lhs, rhs))
} else if let (Ok(lhs), Ok(rhs)) = (lhs1.narrow_into(&rhs2), rhs1.narrow_into(&lhs2)) {
Some((rhs, lhs))
} else {
None
}
}
pub fn unroll_borrow(&self) -> TypeKind {
match self {
TypeKind::Borrow(type_kind, mut1) => match *type_kind.clone() {
TypeKind::Borrow(type_kind, mut2) => match (mut1, mut2) {
(false, false) => TypeKind::Borrow(Box::new(*type_kind.clone()), false),
_ => TypeKind::Borrow(Box::new(*type_kind.clone()), true),
},
_ => self.clone(),
},
_ => self.clone(),
}
}
}
impl Ord for TypeKind {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
use std::cmp::*;
let category_ord = self.category().partial_cmp(&other.category());
match category_ord {
Some(Ordering::Equal) | None => {
if !self.signed() && other.signed() {
return Ordering::Less;
}
if self.signed() && !other.signed() {
return Ordering::Greater;
}
let self_size = self.size_of(&HashMap::new());
let other_size = other.size_of(&HashMap::new());
if self_size == 32 && other_size != 32 {
return Ordering::Less;
} else if self_size != 32 && other_size == 32 {
return Ordering::Greater;
}
self_size.cmp(&self_size)
}
Some(ord) => ord,
}
}
}
impl BinaryOperator {
pub fn is_commutative(&self) -> bool {
match self {
BinaryOperator::Add => true,
BinaryOperator::Minus => false,
BinaryOperator::Mult => true,
BinaryOperator::Div => false,
BinaryOperator::Mod => false,
BinaryOperator::And => true,
BinaryOperator::Cmp(cmp_operator) => match cmp_operator {
CmpOperator::LT => false,
CmpOperator::LE => false,
CmpOperator::GT => false,
CmpOperator::GE => false,
CmpOperator::EQ => true,
CmpOperator::NE => true,
},
BinaryOperator::Or => true,
BinaryOperator::Xor => true,
BinaryOperator::BitOr => true,
BinaryOperator::BitAnd => true,
BinaryOperator::BitshiftRight => false,
BinaryOperator::BitshiftLeft => false,
}
}
}
const TYPE_CATEGORY_ORDER: [TypeCategory; 5] = [
TypeCategory::Integer,
TypeCategory::Bool,
TypeCategory::Real,
TypeCategory::Other,
TypeCategory::TypeRef,
];
impl PartialOrd for TypeCategory {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
use std::cmp::*;
let self_idx = TYPE_CATEGORY_ORDER.iter().enumerate().find(|s| s.1 == self);
let other_idx = TYPE_CATEGORY_ORDER.iter().enumerate().find(|s| s.1 == other);
if let (Some(self_idx), Some(other_idx)) = (self_idx, other_idx) {
Some(self_idx.cmp(&other_idx))
} else {
None
}
}
}
#[derive(PartialEq, Eq, Ord)]
pub enum TypeCategory {
Integer,
Real,
Bool,
Other,
TypeRef,
}
impl TypeCategory {
pub fn is_simple_maths(&self) -> bool {
match self {
TypeCategory::Integer => true,
TypeCategory::Real => true,
TypeCategory::Other => false,
TypeCategory::TypeRef => false,
TypeCategory::Bool => true,
}
}
}
impl StructType {
pub fn get_field_ty(&self, name: &String) -> Option<&TypeKind> {
self.0
.iter()
.find(|StructField(n, _, _)| n == name)
.map(|StructField(_, ty, _)| ty)
}
pub fn get_field_ty_mut(&mut self, name: &String) -> Option<&mut TypeKind> {
self.0
.iter_mut()
.find(|StructField(n, _, _)| n == name)
.map(|StructField(_, ty, _)| ty)
}
}
impl Block {
fn return_expr(&self) -> Result<BlockReturn, ReturnTypeOther> {
let mut early_return = None;
for statement in &self.statements {
let ret = statement.return_type(&Default::default(), SourceModuleId(0));
if let Ok((ReturnKind::Hard, _)) = ret {
early_return = Some(statement);
}
}
if let Some(s) = early_return {
return Ok(BlockReturn::Early(s));
}
self.return_expression
.as_ref()
.map(|(r, e)| BlockReturn::Normal(*r, e))
.ok_or(ReturnTypeOther::NoBlockReturn(self.meta))
}
pub fn return_meta(&self) -> Metadata {
self.return_expression
.as_ref()
.map(|e| e.1.as_ref().map(|e| e.1).unwrap_or(Metadata::default()))
.or(self.statements.last().map(|s| s.1))
.unwrap_or(self.meta)
}
pub fn return_type(
&self,
refs: &TypeRefs,
mod_id: SourceModuleId,
) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
let mut early_return = None;
for statement in &self.statements {
let ret = statement.return_type(refs, mod_id);
if let Ok((ReturnKind::Hard, _)) = ret {
early_return = early_return.or(ret.ok());
}
}
if let Some((ReturnKind::Hard, ret_ty)) = early_return {
return Ok((ReturnKind::Hard, ret_ty));
}
self.return_expression
.as_ref()
.ok_or(ReturnTypeOther::NoBlockReturn(self.meta))
.and_then(|(kind, stmt)| {
Ok((
*kind,
stmt.as_ref()
.and_then(|s| s.return_type(refs, mod_id).ok())
.map(|s| s.1)
.unwrap_or(TypeKind::Void),
))
})
}
pub fn backing_var(&self) -> Option<&NamedVariableRef> {
match self.return_expr().ok()? {
BlockReturn::Early(statement) => statement.backing_var(),
BlockReturn::Normal(kind, expr) => {
if let Some(expr) = expr {
if kind == ReturnKind::Soft {
expr.backing_var()
} else {
None
}
} else {
None
}
}
}
}
}
impl Statement {
pub fn return_type(
&self,
refs: &TypeRefs,
mod_id: SourceModuleId,
) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
use StmtKind::*;
match &self.0 {
Let(var, _, expr) => if_hard(
expr.return_type(refs, mod_id)?,
Err(ReturnTypeOther::Let(var.2 + expr.1)),
),
Set(lhs, rhs) => if_hard(rhs.return_type(refs, mod_id)?, Err(ReturnTypeOther::Set(lhs.1 + rhs.1))),
Import(_) => todo!(),
Expression(expression) => expression.return_type(refs, mod_id),
While(_) => Err(ReturnTypeOther::Loop),
}
}
pub fn backing_var(&self) -> Option<&NamedVariableRef> {
match &self.0 {
StmtKind::Let(_, _, _) => None,
StmtKind::Set(_, _) => None,
StmtKind::Import(_) => None,
StmtKind::Expression(expr) => expr.backing_var(),
StmtKind::While(_) => None,
}
}
}
impl Expression {
pub fn return_type(
&self,
refs: &TypeRefs,
mod_id: SourceModuleId,
) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
use ExprKind::*;
match &self.0 {
Literal(lit) => Ok((ReturnKind::Soft, lit.as_type())),
Variable(var) => var.return_type(),
BinOp(_, then_e, else_e, return_ty) => {
let then_r = then_e.return_type(refs, mod_id)?;
let else_r = else_e.return_type(refs, mod_id)?;
Ok(match (then_r.0, else_r.0) {
(ReturnKind::Hard, ReturnKind::Hard) => (ReturnKind::Hard, return_ty.clone()),
_ => (ReturnKind::Soft, return_ty.clone()),
})
}
Block(block) => block.return_type(refs, mod_id),
FunctionCall(fcall) => fcall.return_type(),
If(expr) => expr.return_type(refs, mod_id),
Indexed(expression, _, _) => {
let expr_type = expression.return_type(refs, mod_id)?;
if let TypeKind::Array(elem_ty, _) = expr_type.1.resolve_weak(refs) {
Ok((ReturnKind::Soft, *elem_ty))
} else if let TypeKind::UserPtr(_) = expr_type.1.resolve_weak(refs) {
Ok((ReturnKind::Soft, expr_type.1))
} else {
Err(ReturnTypeOther::IndexingNonArray(expression.1))
}
}
Array(expressions) => {
let first = expressions
.iter()
.next()
.map(|e| e.return_type(refs, mod_id))
.unwrap_or(Ok((ReturnKind::Soft, TypeKind::Void)))?;
Ok((
ReturnKind::Soft,
TypeKind::Array(Box::new(first.1), expressions.len() as u64),
))
}
Accessed(_, type_kind, ..) => Ok((ReturnKind::Soft, type_kind.clone())),
Struct(key, _) => Ok((ReturnKind::Soft, TypeKind::CustomType(key.clone()))),
Borrow(expr, mutable) => {
let ret_type = expr.return_type(refs, mod_id)?;
Ok((ret_type.0, TypeKind::Borrow(Box::new(ret_type.1), *mutable)))
}
Deref(expr) => {
let (kind, ret_type) = expr.return_type(refs, mod_id)?;
match ret_type.resolve_weak(refs) {
TypeKind::Borrow(type_kind, _) => Ok((kind, *type_kind)),
_ => Err(ReturnTypeOther::DerefNonBorrow(expr.1)),
}
}
CastTo(expr, type_kind) => match expr.return_type(refs, mod_id) {
Ok(ret_type) => match ret_type {
(ReturnKind::Hard, ty) => Ok((ReturnKind::Hard, ty)),
_ => Ok((ReturnKind::Soft, type_kind.clone())),
},
Err(_) => Ok((ReturnKind::Soft, type_kind.clone())),
},
AssociatedFunctionCall(_, fcall) => fcall.return_type(),
GlobalRef(_, type_kind) => Ok((ReturnKind::Soft, type_kind.clone())),
}
}
pub fn backing_var(&self) -> Option<&NamedVariableRef> {
match &self.0 {
ExprKind::Variable(var_ref) => Some(var_ref),
ExprKind::Indexed(lhs, _, _) => lhs.backing_var(),
ExprKind::Accessed(lhs, ..) => lhs.backing_var(),
ExprKind::Borrow(expr, _) => expr.backing_var(),
ExprKind::Deref(expr) => expr.backing_var(),
ExprKind::Block(block) => block.backing_var(),
ExprKind::Array(_) => None,
ExprKind::Struct(_, _) => None,
ExprKind::Literal(_) => None,
ExprKind::BinOp(_, _, _, _) => None,
ExprKind::FunctionCall(_) => None,
ExprKind::If(_) => None,
ExprKind::CastTo(expression, _) => expression.backing_var(),
ExprKind::AssociatedFunctionCall(..) => None,
ExprKind::GlobalRef(..) => None,
}
}
pub fn num_value(&self) -> Result<Option<i128>, NumValueError> {
Ok(match &self.0 {
ExprKind::Variable(_) => None,
ExprKind::Indexed(..) => None,
ExprKind::Accessed(..) => None,
ExprKind::Array(_) => None,
ExprKind::Struct(..) => None,
ExprKind::Literal(literal) => literal.num_value(),
ExprKind::BinOp(op, lhs, rhs, _) => match op {
BinaryOperator::Add => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a + b),
BinaryOperator::Minus => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a - b),
BinaryOperator::Mult => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a * b),
BinaryOperator::And => None,
BinaryOperator::Cmp(_) => None,
BinaryOperator::Div => {
let rhs_value = rhs.num_value()?;
if rhs_value == Some(0) {
Err(NumValueError::DivideZero)?
}
maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a / b)
}
BinaryOperator::Mod => {
let rhs_value = rhs.num_value()?;
if rhs_value == Some(0) {
Err(NumValueError::DivideZero)?
}
maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a % b)
}
BinaryOperator::Or => None,
BinaryOperator::Xor => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a ^ b),
BinaryOperator::BitOr => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a | b),
BinaryOperator::BitAnd => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a & b),
BinaryOperator::BitshiftRight => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a >> b),
BinaryOperator::BitshiftLeft => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a << b),
},
ExprKind::FunctionCall(..) => None,
ExprKind::If(_) => None,
ExprKind::Block(_) => None,
ExprKind::Borrow(_, _) => None,
ExprKind::Deref(_) => None,
ExprKind::CastTo(expression, _) => expression.num_value()?,
ExprKind::AssociatedFunctionCall(..) => None,
ExprKind::GlobalRef(..) => None,
})
}
}
impl IfExpression {
pub fn return_type(
&self,
refs: &TypeRefs,
mod_id: SourceModuleId,
) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
let then_r = self.1.return_type(refs, mod_id)?;
if let Some(else_e) = self.2.as_ref() {
let else_r = else_e.return_type(refs, mod_id)?;
let kind = if then_r.0 == ReturnKind::Hard && else_r.0 == ReturnKind::Hard {
ReturnKind::Hard
} else {
ReturnKind::Soft
};
Ok((kind, then_r.1))
} else {
Ok((ReturnKind::Soft, then_r.1))
}
}
}
impl NamedVariableRef {
pub fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
Ok((ReturnKind::Soft, self.0.clone()))
}
}
impl FunctionCall {
pub fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
Ok((ReturnKind::Soft, self.return_type.clone()))
}
}
fn if_hard<TErr>(
return_type: (ReturnKind, TypeKind),
default: Result<(ReturnKind, TypeKind), TErr>,
) -> Result<(ReturnKind, TypeKind), TErr> {
if let (ReturnKind::Hard, _) = return_type {
Ok(return_type)
} else {
default
}
}
pub fn pick_return<T>(lhs: (ReturnKind, T), rhs: (ReturnKind, T)) -> (ReturnKind, T) {
use ReturnKind::*;
match (lhs.0, rhs.0) {
(Hard, Hard) => (Hard, lhs.1),
(Hard, Soft) => (Soft, rhs.1),
(Soft, Hard) => (Soft, lhs.1),
(_, _) => (Soft, lhs.1),
}
}
impl Literal {
pub fn num_value(&self) -> Option<i128> {
match self {
Literal::I8(val) => Some(*val as i128),
Literal::I16(val) => Some(*val as i128),
Literal::I32(val) => Some(*val as i128),
Literal::I64(val) => Some(*val as i128),
Literal::I128(val) => Some(*val as i128),
Literal::U8(val) => Some(*val as i128),
Literal::U16(val) => Some(*val as i128),
Literal::U32(val) => Some(*val as i128),
Literal::U64(val) => Some(*val as i128),
Literal::U128(val) => Some(*val as i128),
Literal::Bool(_) => None,
Literal::String(_) => None,
Literal::Vague(VagueLiteral::Number(val)) => Some(*val as i128),
Literal::Vague(VagueLiteral::Decimal(_)) => None,
Literal::F16(_) => None,
Literal::F32B(_) => None,
Literal::F32(_) => None,
Literal::F64(_) => None,
Literal::F80(_) => None,
Literal::F128(_) => None,
Literal::F128PPC(_) => None,
Literal::Char(_) => None,
}
}
}
#[derive(Debug, Clone, thiserror::Error, PartialEq, Eq, PartialOrd, Ord)]
pub enum EqualsIssue {
#[error("Function is already defined locally at {:?}", (.0).range)]
ExistsLocally(Metadata),
#[error("Equals")]
Equals,
#[error("Function {0} is already declared locally at {:?}", (.1).range)]
AlreadyExtern(String, Metadata),
#[error("Function {0} is already imported from another module")]
ConflictWithImport(String),
#[error("Function is defined as an intrinsic")]
ExistsAsIntrinsic,
}
impl FunctionDefinition {
pub fn equals_as_imported(&self, other: &FunctionDefinition) -> Result<(), EqualsIssue> {
match &self.kind {
FunctionDefinitionKind::Local(_, metadata) => Err(EqualsIssue::ExistsLocally(*metadata)),
FunctionDefinitionKind::Extern(imported) => {
if *imported {
Err(EqualsIssue::ConflictWithImport(self.name.clone()))
} else {
if self.is_pub == other.is_pub
&& self.name == other.name
&& self.parameters == other.parameters
&& self.return_type == other.return_type
{
Ok(())
} else {
Err(EqualsIssue::AlreadyExtern(self.name.clone(), self.signature()))
}
}
}
FunctionDefinitionKind::Intrinsic(_) => Err(EqualsIssue::ExistsAsIntrinsic),
}
}
}

Some files were not shown because too many files have changed in this diff Show More