Compare commits

..

No commits in common. "main" and "42e74f49f8aad5e222991cd33df13c905109dc81" have entirely different histories.

117 changed files with 490 additions and 19963 deletions

13
.gitignore vendored
View File

@ -1,14 +1 @@
src/old_llvm
/target
/.vscode
.env
hello.*
main
*.o
*.ll
*.asm
*.out
*.llir
*.mir
foo.reid

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

1476
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,8 @@
[workspace]
members = [
"reid",
"reid-llvm-lib",
"reid-lsp"
]
[package]
name = "reid"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@ -1,17 +0,0 @@
SRC=foo.reid
BIN=$(SRC:.reid=.out)
REID=cargo run --example cli
LD=ld
LDFLAGS=-lSDL3
all: $(BIN)
clean:
rm -f $(BIN) $(SRC:.reid=.o) $(SRC:.reid=.asm) $(SRC:.reid=.ll)
$(BIN): $(SRC:.reid=.o)
$(LD) -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/crt1.o -lc $(LDFLAGS) $< -o$@
.SUFFIXES: .o .reid
.reid.o:
$(REID) $<

175
README.md
View File

@ -1,175 +0,0 @@
# Reid-LLVM
Reid is a toy-language compiler I'm working on to learn LLVM (and compiler
development).
Reid only uses [llvm-sys](https://gitlab.com/taricorp/llvm-sys.rs), which
provide very minimal bindings from the LLVM C-API to Rust. `reid_llvm`-crate
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)
- 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:
```rust
fn main() -> u16 {
return fibonacci(5);
}
fn fibonacci(n: u16) -> u16 {
if n <= 2 {
return 1;
}
return fibonacci(n-1) + fibonacci(n-2);
}
```
Currently missing big 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)
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)
### Why "Reid"
[ᚱ is an Elder Futhark rune](https://en.wikipedia.org/wiki/Raido) which means
"ride" or **"journey"**. As this language is meant for me to primarily learn
language design and compiler development, it is more about the *journey* rather
than the destination. ᚱ is written as "Reið" in Icelandic, which is the
inspiration behind "Reid" here.
### Why "Reid-LLVM"?
Because I have another project also called Reid, which only compiles to a
Virtual Instruction Set Architecture (V-ISA) that is executed via a custom-made
Virtual Machine. It is still hosted
[here](https://git.teascade.net/teascade/reid), but do note that it is very old
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`,
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)
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
finally executed.
This is currently very work-in-progress and many things about this repository
change erratically.
## Various notes in order to get this working properly
This is what worked for me, might not (probably) work for you, depending on
various versions of various libraries.
### Compiling LLVM 20.1.8
#### Context
Context for my computer. I am on ArchLinux, and here are some libraries and
their current versions that I have installed as of compiling, I'm not sure what
of them are relevant, if any, but saving them here still feels like a good idea
for the future:
- `clang 19.1.7-2`
- `cmake 4.0.2-1`
- `extra-cmake-modules 6.14.0-1`
- `gcc 15.1.1+r7+gf36ec88aa85a-1`
- `gcc-libs 15.1.1+r7+gf36ec88aa85a-1`
- `lib32-gcc-libs 15.1.1+r7+gf36ec88aa85a-1`
- `lib32-llvm-libs 1:19.1.7-2`
- `libgccjit 15.1.1+r7+gf36ec88aa85a-1`
- `lld 19.1.7-1`
- `lldb 19.1.7-2`
- `llvm 19.1.7-2`
- `llvm-libs 19.1.7-2`
- `llvm14 14.0.6-5`
- `llvm14-libs 14.0.6-5`
- `llvm15 15.0.7-3`
- `llvm15-libs 15.0.7-3`
- `make 4.4.1-2`
#### Commands
```sh
git clone https://github.com/llvm/llvm-project.git --depth=1 --branch=llvmorg-20.1.8
cd llvm_project
cmake llvm -B build -DCMAKE_BUILD_TYPE=MinSizeRel -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_INCLUDE_TESTS=OFF -DLLVM_BUILD_BENCHMARKS=OFF -G Ninja -DLLVM_USE_LINKER="ld.lld" -DLLVM_PARALLEL_LINK_JOBS=8
ninja -j23
```
### Building this crate itself
Assuming `llvm-project` from the previous step was at
`/path/llvm-project`, building this crate can be done via the following command:
```sh
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

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`.

4
easiest.reid Normal file
View File

@ -0,0 +1,4 @@
// Hello, comment here!
let hello = 32;
let beep =
hello ;

12
easy.reid Normal file
View File

@ -0,0 +1,12 @@
// Arithmetic, function calls and imports!
import std::print;
let test = 5;
let simpleAdd = 2 + 2;
let arithmetic = 3 + 2 * 5 + 1 * 2;
let multiplier = 5 * 2;
let result = arithmetic + multiplier * arithmetic;
print(result);
function(one, two);

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 a = [[ 5, 3, 2 ]];
return a[0][1];
}

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,14 +0,0 @@
// Arithmetic, function calls and imports!
/// Test stuff
fn changer(param: &mut u32) {
*param = 17;
}
fn main() -> u32 {
let mut value = 6;
changer(&mut value);
return value;
}

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,12 +0,0 @@
// Main
fn main() -> bool {
return 144 == fibonacci(0xc);
}
// Fibonacci
fn fibonacci(value: u16) -> u16 {
if value <= 2 {
return 1;
}
fibonacci(value - 1) + fibonacci(value - 2)
}

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,16 +0,0 @@
// Arithmetic, function calls and imports!
fn indirection() -> bool {
return true;
}
fn main() -> u16 {
let mut test = 5;
let addition = 10;
if indirection() {
test = 11;
}
return test + addition;
}

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 @@
fn main() -> u16 {
let hello = "beep boop grw";
return 5;
}

View File

@ -1,18 +0,0 @@
// Arithmetic, function calls and imports!
struct Test {
field: i32,
second: u32
}
fn main() -> u32 {
let mut value = Test {
field: 5,
second: 3,
};
value.second = 17;
return value.second;
}

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 {};
}

8
hard.reid Normal file
View File

@ -0,0 +1,8 @@
// New types, type-casting
import std::print;
let text: string = "hello there!";
let value: i16 = 123;
print(text + (value as string));

View File

@ -1,52 +0,0 @@
#!/bin/sh
# Compiles example libtest, which produces hello.o and hello.asm, which is then
# compiled with main.cpp and executed for final result
#
# Do note this file is extremely simply for my own personal convenience
# export .env
# cargo run --release --example cli $1 && \
# # clang hello.o -o main && \
# ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 \
# -o main /usr/lib/crt1.o hello.o -lc && \
# ./main ; echo "Return value: ""$?"
BINARY="$(echo $1 | cut -d'.' -f1)"".out"
echo $1
cargo run -p reid -- $@ && \
./$BINARY ; echo "Return value: ""$?"
## Command from: clang -v hello.o -o test
## Original command:
# ld --hash-style=gnu \
# --build-id \
# --eh-frame-hdr \
# -m elf_x86_64 \
# -pie \
# -dynamic-linker /lib64/ld-linux-x86-64.so.2 \
# -o test \
# /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/15.1.1/../../../../lib64/Scrt1.o \
# /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/15.1.1/../../../../lib64/crti.o \
# /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/15.1.1/crtbeginS.o \
# -L/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/15.1.1 \
# -L/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/15.1.1/../../../../lib64 \
# -L/lib/../lib64 \
# -L/usr/lib/../lib64 \
# -L/lib \
# -L/usr/lib \
# hello.o \
# -lgcc \
# --as-needed \
# -lgcc_s \
# --no-as-needed \
# -lc \
# -lgcc \
# --as-needed \
# -lgcc_s \
# --no-as-needed \
# /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/15.1.1/crtendS.o \
# /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/15.1.1/../../../../lib64/crtn.o \
# && \

12
medium.reid Normal file
View File

@ -0,0 +1,12 @@
// if-statements, functions
import std::print;
fn fibonacci(value: i32) -> i32 {
if value < 3 {
return 1;
}
return fibonacci(value - 1) + fibonacci(value - 2);
}
print(fibonacci(15));

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

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 +0,0 @@
[package]
name = "reid"
version = "1.0.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = ["color", "cli"]
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" }
argh = { version = "0.1.13", optional = true }
stderrlog = { version = "0.6.0", optional = true }
log = "*"
colored = { version = "3.0.0", optional = true }

View File

@ -1,74 +0,0 @@
use std::{env, fs, path::PathBuf};
use reid::{compile_simple, ld::LDRunner, CustomIRs};
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);
}
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)?;
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!(
"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!")
}
Ok(())
}

View File

@ -1,249 +0,0 @@
extern fn puts(message: *char) -> i32;
extern fn free(ptr: *u8);
extern fn div(numerator: i32, denominator: i32) -> div_t;
/// Editable string value that can be printed and extended
struct String {
inner: *char,
length: u64,
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 {
quotient: i32,
remainder: i32,
}
/// Print given string to stdout
pub fn print(message: String) {
puts(message.inner);
}
/// Divide an integer, returning the quotient and remainder.
pub fn int_div(numerator: i32, denominator: i32) -> div_t {
return div(numerator, denominator);
}
/// (deprecated) creates a new editable string
pub fn new_string() -> String {
String {
inner: char::malloc(0),
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;
}

View File

@ -1,524 +0,0 @@
use std::{fmt::Debug, ops::AddAssign, 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)]
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".
StringLit(String),
// Keywords
/// `let`
LetKeyword,
/// `mut`
MutKeyword,
/// `import`
ImportKeyword,
/// `return`
ReturnKeyword,
/// `fn`
FnKeyword,
/// `pub`
PubKeyword,
/// `as`
AsKeyword,
/// `->`
Arrow,
/// `if`
If,
/// `else`
Else,
/// `true`
True,
/// `false`
False,
/// `extern`
Extern,
/// `struct`
Struct,
/// `while`
While,
/// `for`
For,
/// `in`
In,
/// `impl`
Impl,
/// `binop`
Binop,
// Symbols
/// `;`
Semi,
/// `=`
Equals,
/// `:`
Colon,
/// `+`
Plus,
/// `*`
Star,
/// `-`
Minus,
/// `/`
Slash,
/// `%`
Percent,
/// `>`
GreaterThan,
/// `<`
LessThan,
/// `&`
Et,
/// `|`
Pipe,
/// `^`
Hat,
/// `!`
Exclamation,
/// `(`
ParenOpen,
/// `)`
ParenClose,
/// `{`
BraceOpen,
/// `}`
BraceClose,
/// `[`
BracketOpen,
/// `]`
BracketClose,
/// `,`
Comma,
/// `.`
Dot,
Unknown(char),
Whitespace(String),
Comment(String),
Doc(String),
Eof,
}
impl Token {
pub fn get_token_prec(&self) -> i8 {
match &self {
Token::Plus => 10,
Token::Minus => 10,
Token::Star => 20,
_ => -1,
}
}
}
impl From<Token> for String {
fn from(value: Token) -> Self {
format!("{:?}", value)
}
}
impl Token {
pub fn len(&self) -> usize {
self.to_string().len()
}
}
impl ToString for Token {
fn to_string(&self) -> String {
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"),
Token::ImportKeyword => String::from("import"),
Token::ReturnKeyword => String::from("return"),
Token::FnKeyword => String::from("fn"),
Token::PubKeyword => String::from("pub"),
Token::Arrow => String::from("=>"),
Token::If => String::from("if"),
Token::Else => String::from("else"),
Token::True => String::from("true"),
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(':'),
Token::Plus => String::from('+'),
Token::Star => String::from('*'),
Token::Minus => String::from('-'),
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(')'),
Token::BraceOpen => String::from('{'),
Token::BraceClose => String::from('}'),
Token::BracketOpen => String::from('['),
Token::BracketClose => String::from(']'),
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)]
pub struct FullToken {
pub token: Token,
pub position: Position,
}
impl Debug for FullToken {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"Token({:?}) (Ln {}, Col {})",
self.token, self.position.1, self.position.0
))
}
}
/// (Column, Line)
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Ord)]
pub struct Position(pub u32, pub u32);
impl Position {
pub fn add(&self, num: u32) -> Position {
Position(self.0 + num, self.1)
}
pub fn sub(&self, num: u32) -> Position {
Position(self.0 - num, self.1)
}
}
impl PartialOrd for Position {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
match self.1.partial_cmp(&other.1) {
Some(core::cmp::Ordering::Equal) => {}
ord => return ord,
}
self.0.partial_cmp(&other.0)
}
}
pub struct Cursor<'a> {
pub position: Position,
pub char_stream: Chars<'a>,
}
impl<'a> Cursor<'a> {
pub fn next(&mut self) -> Option<char> {
let next = self.char_stream.next();
if let Some('\n') = next {
self.position.1 += 1;
self.position.0 = 0;
}
self.position.0 += 1;
next
}
fn first(&mut self) -> Option<char> {
// `.next()` optimizes better than `.nth(0)`
self.char_stream.clone().next()
}
#[allow(dead_code)] // Is this actually needed?
fn second(&mut self) -> Option<char> {
// `.next()` optimizes better than `.nth(1)`
let mut stream = self.char_stream.clone();
stream.next();
stream.next()
}
}
/// Take source text and produce a list of [`FullToken`]s from it, ie.
/// tokenizing it.
pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error> {
let to_tokenize = to_tokenize.into();
let mut cursor = Cursor {
char_stream: to_tokenize.chars(),
position: Position(0, 1),
};
let mut tokens = Vec::new();
while let Some(character) = &cursor.next() {
// Save "current" token first character position
let position = cursor.position.sub(1);
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)
}
// 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)
}
}
'\"' | '\'' => {
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;
} else {
let c = &cursor.next().unwrap();
if escape_next {
value += &escape_char(&c).to_string();
} else {
value += &c.to_string();
}
escape_next = false;
}
}
if cursor.first() == Some(*character) {
cursor.next();
} else {
return Err(Error::MissingQuotation(position));
}
match character {
'\'' => Token::CharLit(value),
'\"' => Token::StringLit(value),
_ => unreachable!(),
}
}
// "words"
c if c.is_alphabetic() => {
let mut value = character.to_string();
while let Some(c) = cursor.first() {
if !(c.is_ascii_alphanumeric() || c == '_') {
break;
}
value += &c.to_string();
cursor.next();
}
// Check for keywords
let variant = match value.as_str() {
"let" => Token::LetKeyword,
"mut" => Token::MutKeyword,
"import" => Token::ImportKeyword,
"return" => Token::ReturnKeyword,
"fn" => Token::FnKeyword,
"if" => Token::If,
"else" => Token::Else,
"true" => Token::True,
"false" => Token::False,
"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;
}
}
while let Some(c) = cursor.first() {
if !numerics.contains(&c.to_lowercase().next().unwrap_or('.')) {
break;
}
value += c;
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),
}
}
'-' if cursor.first() == Some('>') => {
cursor.next(); // Eat `>`
Token::Arrow
}
// Single character tokens
'=' => Token::Equals,
';' => Token::Semi,
':' => Token::Colon,
'+' => Token::Plus,
'*' => Token::Star,
'-' => Token::Minus,
'>' => Token::GreaterThan,
'<' => Token::LessThan,
'&' => Token::Et,
'|' => Token::Pipe,
'^' => Token::Hat,
'!' => Token::Exclamation,
'(' => Token::ParenOpen,
')' => Token::ParenClose,
'[' => Token::BracketOpen,
']' => Token::BracketClose,
'{' => Token::BraceOpen,
'}' => Token::BraceClose,
',' => Token::Comma,
'.' => Token::Dot,
'/' => Token::Slash,
'%' => Token::Percent,
// Unknown token
value => Token::Unknown(*value),
};
tokens.push(FullToken {
token: variant,
position,
});
}
tokens.push(FullToken {
token: Token::Eof,
position: cursor.position,
});
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)]
InvalidToken(char, Position),
#[error("String literal is never finished!")]
MissingQuotation(Position),
}
impl Error {
pub fn get_position(&self) -> &Position {
match self {
Error::InvalidToken(_, pos) => pos,
Error::MissingQuotation(pos) => pos,
}
}
}

View File

@ -1,291 +0,0 @@
//! This is the module that contains relevant code to parsing Reid, that is to
//! say transforming a Vec of FullTokens into a loose parsed AST that can be
//! used for unwrapping syntax sugar, and then be transformed into Reid MIR.
use std::path::PathBuf;
use 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);
#[derive(Debug, Clone)]
pub enum TypeKind {
Bool,
I8,
I16,
I32,
I64,
I128,
U8,
U16,
U32,
U64,
U128,
F16,
F32B,
F32,
F64,
F128,
F80,
F128PPC,
Char,
Array(Box<TypeKind>, u64),
Custom(String),
Borrow(Box<TypeKind>, bool),
Ptr(Box<TypeKind>),
Unknown,
}
#[derive(Debug, Clone)]
pub enum Literal {
Integer(u128),
Decimal(f64),
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)]
pub struct Expression(pub ExpressionKind, pub TokenRange);
#[derive(Debug, Clone)]
pub enum ExpressionKind {
VariableName(String),
Borrow(Box<Expression>, bool),
Deref(Box<Expression>),
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>),
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)]
pub enum BinaryOperator {
Add,
Minus,
Mult,
Div,
Mod,
BitshiftRight,
BitshiftLeft,
And,
Or,
Xor,
BitAnd,
BitOr,
LT,
LE,
GT,
GE,
EQ,
NE,
}
impl BinaryOperator {
pub fn get_precedence(&self) -> u8 {
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,
}
}
}
#[derive(Debug, Clone)]
pub struct FunctionCallExpression {
pub name: String,
pub params: Vec<Expression>,
pub range: TokenRange,
pub is_macro: bool,
}
#[derive(Debug, Clone)]
pub struct IfExpression(
pub Expression,
pub Expression,
pub Option<Expression>,
#[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,
}
#[derive(Debug, Clone)]
pub struct ImportStatement(pub Vec<(String, TokenRange)>, pub TokenRange);
#[derive(Debug)]
pub struct FunctionDefinition(pub FunctionSignature, pub bool, pub Block, pub TokenRange);
#[derive(Debug, Clone)]
pub struct FunctionSignature {
pub name: String,
pub documentation: Option<String>,
pub self_kind: SelfKind,
pub params: Vec<(String, Type, TokenRange)>,
pub return_type: Option<Type>,
#[allow(dead_code)]
pub range: TokenRange,
}
#[derive(Debug, Clone)]
pub enum SelfKind {
Owned(Type),
Borrow(Type),
MutBorrow(Type),
None,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ReturnType {
Soft,
Hard,
}
#[derive(Debug, Clone)]
pub struct StructExpression {
name: String,
fields: Vec<(String, Expression, TokenRange)>,
range: TokenRange,
}
#[derive(Debug, Clone)]
pub struct Block(
pub Vec<BlockLevelStatement>,
pub Option<(ReturnType, Option<Expression>)>,
pub TokenRange,
);
#[derive(Debug, Clone)]
pub enum BlockLevelStatement {
Let(LetStatement),
/// Try to set a variable to a specified expression value
Set(Expression, Expression, TokenRange),
Import {
_i: ImportStatement,
},
Expression(Expression),
Return(ReturnType, Option<Expression>),
ForLoop(String, TokenRange, Expression, Expression, Block),
WhileLoop(Expression, Block),
}
#[derive(Debug, Clone)]
pub struct TypeDefinition {
name: String,
kind: TypeDefinitionKind,
range: TokenRange,
}
#[derive(Debug, Clone)]
pub enum TypeDefinitionKind {
Struct(Vec<StructDefinitionField>),
}
#[derive(Debug, Clone)]
pub struct StructDefinitionField {
name: String,
ty: Type,
range: TokenRange,
}
#[derive(Debug)]
pub enum TopLevelStatement {
Import(ImportStatement),
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,596 +0,0 @@
use std::path::PathBuf;
use crate::{
ast::{self, ReturnType},
mir::{
self, CustomTypeKey, FunctionParam, ModuleMap, NamedVariableRef, ReturnKind, SourceModuleId, StmtKind,
StructField, StructType, WhileStatement,
},
};
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 }
}
}
impl ast::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),
));
}
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))
.unwrap_or(mir::TypeKind::Void),
parameters: signature
.params
.iter()
.cloned()
.map(|p| mir::FunctionParam {
name: p.0,
ty: p.1 .0.into_mir(module_id),
meta: p.2.as_meta(module_id),
})
.collect(),
kind: mir::FunctionDefinitionKind::Extern(false),
source: Some(module_id),
signature_meta: signature.range.as_meta(module_id),
};
functions.push(def);
}
TypeDefinition(ast::TypeDefinition { name, kind, range }) => {
let def = mir::TypeDefinition {
name: name.clone(),
kind: match kind {
ast::TypeDefinitionKind::Struct(struct_definition_fields) => {
mir::TypeDefinitionKind::Struct(StructType(
struct_definition_fields
.iter()
.map(|s| {
StructField(
s.name.clone(),
s.ty.clone().0.into_mir(module_id),
s.range.as_meta(module_id),
)
})
.collect(),
))
}
},
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();
for statement in &self.0 {
let (kind, range) = match statement {
ast::BlockLevelStatement::Let(s_let) => (
mir::StmtKind::Let(
mir::NamedVariableRef(
s_let
.ty
.clone()
.map(|t| t.0.into_mir(module_id))
.unwrap_or(mir::TypeKind::Vague(mir::VagueType::Unknown)),
s_let.name.clone(),
s_let.name_range.as_meta(module_id),
),
s_let.mutable,
s_let.value.process(module_id),
),
s_let.name_range + s_let.value.1,
),
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::Return(_, e) => {
if let Some(e) = e {
(StmtKind::Expression(e.process(module_id)), e.1)
} else {
panic!(); // Should never happen?
}
}
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))
}
} else {
None
};
mir::Block {
statements: mir_statements,
return_expression,
meta: self.2.as_meta(module_id),
}
}
}
impl From<ast::ReturnType> for mir::ReturnKind {
fn from(value: ast::ReturnType) -> Self {
match value {
ast::ReturnType::Soft => mir::ReturnKind::Soft,
ast::ReturnType::Hard => mir::ReturnKind::Hard,
}
}
}
impl ast::Expression {
fn process(&self, module_id: SourceModuleId) -> mir::Expression {
let kind = match &self.0 {
ast::ExpressionKind::VariableName(name) => mir::ExprKind::Variable(NamedVariableRef(
mir::TypeKind::Vague(mir::VagueType::Unknown),
name.clone(),
self.1.as_meta(module_id),
)),
ast::ExpressionKind::Literal(literal) => mir::ExprKind::Literal(literal.mir()),
ast::ExpressionKind::Binop(binary_operator, lhs, rhs) => mir::ExprKind::BinOp(
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::IfExpr(if_expression) => {
let cond = if_expression.0.process(module_id);
let then_block = if_expression.1.process(module_id);
let else_block = if let Some(el) = &if_expression.2 {
Some(el.process(module_id))
} else {
None
};
mir::ExprKind::If(mir::IfExpression(
Box::new(cond),
Box::new(then_block),
Box::new(else_block),
))
}
ast::ExpressionKind::Array(expressions) => {
mir::ExprKind::Array(expressions.iter().map(|e| e.process(module_id)).collect())
}
ast::ExpressionKind::Indexed(expression, idx_expr) => mir::ExprKind::Indexed(
Box::new(expression.process(module_id)),
mir::TypeKind::Vague(mir::VagueType::Unknown),
Box::new(idx_expr.process(module_id)),
),
ast::ExpressionKind::StructExpression(struct_init) => mir::ExprKind::Struct(
CustomTypeKey(struct_init.name.clone(), module_id),
struct_init
.fields
.iter()
.map(|(n, e, r)| (n.clone(), e.process(module_id), r.as_meta(module_id)))
.collect(),
),
ast::ExpressionKind::Accessed(expression, name, name_range) => mir::ExprKind::Accessed(
Box::new(expression.process(module_id)),
mir::TypeKind::Vague(mir::VagueType::Unknown),
name.clone(),
name_range.as_meta(module_id),
),
ast::ExpressionKind::Borrow(expr, mutable) => {
mir::ExprKind::Borrow(Box::new(expr.process(module_id)), *mutable)
}
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,
},
)
}
};
mir::Expression(kind, self.1.as_meta(module_id))
}
}
impl ast::BinaryOperator {
fn mir(&self) -> mir::BinaryOperator {
match self {
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,
}
}
}
impl ast::Literal {
fn mir(&self) -> mir::Literal {
match &self {
ast::Literal::Integer(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 {
ast::TypeKind::Bool => mir::TypeKind::Bool,
ast::TypeKind::I8 => mir::TypeKind::I8,
ast::TypeKind::I16 => mir::TypeKind::I16,
ast::TypeKind::I32 => mir::TypeKind::I32,
ast::TypeKind::I64 => mir::TypeKind::I64,
ast::TypeKind::I128 => mir::TypeKind::I128,
ast::TypeKind::U8 => mir::TypeKind::U8,
ast::TypeKind::U16 => mir::TypeKind::U16,
ast::TypeKind::U32 => mir::TypeKind::U32,
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)
}
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),
}
}
}

View File

@ -1,376 +0,0 @@
//! Contains relevant code for parsing tokens received from
//! Lexing/Tokenizing-stage.
use std::{cell::RefCell, rc::Rc};
use crate::{
ast::parse::Parse,
lexer::{FullToken, Token},
};
/// Utility struct that is able to parse [`FullToken`]s while being
/// failure-resistance in that it can backtrack easily, and is able to keep
/// track of parsed Token-ranges easily.
pub struct TokenStream<'a, 'b> {
ref_position: Option<&'b mut usize>,
tokens: &'a [FullToken],
errors: Rc<RefCell<Vec<Error>>>,
pub position: usize,
}
impl<'a, 'b> TokenStream<'a, 'b> {
pub fn from(tokens: &'a [FullToken]) -> Self {
TokenStream {
ref_position: None,
tokens,
errors: Rc::new(RefCell::new(Vec::new())),
position: 0,
}
}
/// Returns expected-error for the next token in-line. Useful in conjunction
/// with [`TokenStream::peek`]
pub fn expected_err<T: Into<String>>(&mut self, expected: T) -> Result<Error, Error> {
let next_token = self.previous().unwrap_or(Token::Eof);
Ok(Error::Expected(
expected.into(),
next_token,
TokenRange {
start: self.position - 1,
end: self.position - 1,
},
))
}
/// Returns expected-error for the next token in-line. Useful in conjunction
/// with [`TokenStream::peek`]
pub fn expected_err_nonfatal<T: Into<String>>(&mut self, expected: T) {
let err = match self.expected_err(expected) {
Ok(e) => e,
Err(e) => e,
};
self.errors.borrow_mut().push(err);
}
/// Returns expected-error for the previous token that was already consumed.
/// Useful in conjunction with [`TokenStream::next`]
pub fn expecting_err<T: Into<String>>(&mut self, expected: T) -> Result<Error, Error> {
let next_token = self.peek().unwrap_or(Token::Eof);
let pos = self.next_token(self.position).0;
Ok(Error::Expected(
expected.into(),
next_token,
TokenRange { start: pos, end: pos },
))
}
/// Returns expected-error for the previous token that was already consumed.
/// Useful in conjunction with [`TokenStream::next`]
pub fn expecting_err_nonfatal<T: Into<String>>(&mut self, expected: T) {
let err = match self.expecting_err(expected) {
Ok(e) => e,
Err(e) => e,
};
self.errors.borrow_mut().push(err);
}
pub fn expect(&mut self, token: Token) -> Result<(), Error> {
if let (pos, Some(peeked)) = self.next_token(self.position) {
if token == peeked.token {
self.position = pos + 1;
Ok(())
} else {
Err(self.expecting_err(token)?)
}
} else {
Err(self.expecting_err(token)?)
}
}
pub fn find_documentation(&mut self) -> Option<String> {
let mut from = self.position;
let mut documentation = None;
while let Some(token) = self.tokens.get(from) {
if matches!(token.token, Token::Whitespace(_) | Token::Comment(_) | Token::Doc(_)) {
from += 1;
if let Token::Doc(doctext) = &token.token {
documentation = Some(
match documentation {
Some(t) => t + " ",
None => String::new(),
} + doctext.trim(),
);
}
} else {
break;
}
}
documentation
}
pub fn expect_nonfatal(&mut self, token: Token) -> Result<(), ()> {
if let (pos, Some(peeked)) = self.next_token(self.position) {
if token == peeked.token {
self.position = pos + 1;
Ok(())
} else {
self.expecting_err_nonfatal(token);
Err(())
}
} else {
self.expecting_err_nonfatal(token);
Err(())
}
}
pub fn next(&mut self) -> Option<Token> {
let (position, token) = self.next_token(self.position);
self.position = position + 1;
token.map(|t| t.token.clone())
}
pub fn previous(&mut self) -> Option<Token> {
let (_, token) = self.previous_token(self.position);
token.map(|t| t.token.clone())
}
pub fn peek(&mut self) -> Option<Token> {
let (_, token) = self.next_token(self.position);
token.map(|t| t.token.clone())
}
pub fn peek2(&mut self) -> Option<Token> {
let (pos2, _) = self.next_token(self.position);
let (_, token) = self.next_token(pos2 + 1);
token.map(|t| t.token.clone())
}
/// Parse the next value of trait Parse. If the parse succeeded, the related
/// tokens are consumed, otherwise token stream does not advance.
///
/// Parsetime-error is returned on failure.
pub fn parse<T: Parse + std::fmt::Debug>(&mut self) -> Result<T, Error> {
let (res, pos) = self.parse_meta()?;
self.position = pos;
Ok(res)
}
/// Parse the next item with Parse-trait (Same as [`TokenStream::parse`])
/// without consuming the related tokens, essentially only peeking.
pub fn parse_peek<T: Parse + std::fmt::Debug>(&mut self) -> Result<T, Error> {
self.parse_meta().map(|(res, _)| res)
}
/// Parse the next item with Parse-trait, also mapping it with the given
/// function. The token-stream is only consumed, if the inner function
/// retuns an Ok.
#[allow(dead_code)]
pub fn parse_map<T: Parse + std::fmt::Debug, F, O>(&mut self, inner: F) -> Result<O, Error>
where
F: Fn(T) -> Result<O, Error>,
{
let (res, pos) = self.parse_meta::<T>()?;
match inner(res) {
Ok(mapped) => {
self.position = pos;
Ok(mapped)
}
Err(e) => Err(e),
}
}
/// Parses the item with Parse if the condition specified by the
/// lambda-function is passed. Errors returned from this should not be
/// passed to the end-user.
pub fn parse_if<T: Parse + std::fmt::Debug, F>(&mut self, inner: F) -> Result<T, Error>
where
F: Fn(&T) -> bool,
{
let (res, pos) = self.parse_meta::<T>()?;
if inner(&res) {
self.position = pos;
Ok(res)
} else {
Err(Error::IfFailed)
}
}
/// Parse the next item with Parse-trait. If successful, returning the
/// parsed item and the new position of the TokenStream. Failing, returning
/// parse-error.
///
/// Used for [`TokenStream::parse`] and [`TokenStream::parse_peek`]
fn parse_meta<T: Parse + std::fmt::Debug>(&mut self) -> Result<(T, usize), Error> {
let mut ref_pos = self.position;
let position = self.position;
let clone = TokenStream {
ref_position: Some(&mut ref_pos),
tokens: self.tokens,
errors: self.errors.clone(),
position,
};
match T::parse(clone) {
Ok(res) => {
let new_pos = ref_pos.max(self.position);
Ok((res, new_pos))
}
Err(e) => Err(e),
}
}
pub fn parse_with<T, U>(&mut self, fun: T) -> Result<U, Error>
where
T: FnOnce(TokenStream) -> Result<U, Error>,
{
let mut ref_pos = self.position;
let position = self.position;
let clone = TokenStream {
ref_position: Some(&mut ref_pos),
tokens: self.tokens,
errors: self.errors.clone(),
position,
};
match fun(clone) {
Ok(res) => {
self.position = ref_pos.max(self.position);
Ok(res)
}
Err(e) => Err(e),
}
}
pub fn get_range(&self) -> Option<TokenRange> {
self.ref_position.as_ref().map(|ref_pos| TokenRange {
start: **ref_pos,
end: self.position,
})
}
/// Gets range from the previous position to the current. Useful when using
/// with [`TokenStream::next`]
pub fn get_range_prev(&self) -> Option<TokenRange> {
self.ref_position.as_ref().map(|ref_pos| TokenRange {
start: **ref_pos,
end: self.previous_token(self.position).0,
})
}
/// Gets range of the previous token only.
pub fn get_range_prev_curr(&self) -> Option<TokenRange> {
Some(TokenRange {
start: self.previous_token(self.position).0,
end: self.previous_token(self.position).0,
})
}
fn previous_token(&self, mut from: usize) -> (usize, Option<&'a FullToken>) {
from -= 1;
while let Some(token) = self.tokens.get(from) {
if matches!(token.token, Token::Whitespace(_) | Token::Comment(_) | Token::Doc(_)) {
from -= 1;
} else {
break;
}
}
(from, self.tokens.get(from))
}
fn next_token(&self, mut from: usize) -> (usize, Option<&'a FullToken>) {
while let Some(token) = self.tokens.get(from) {
if matches!(token.token, Token::Whitespace(_) | Token::Comment(_) | Token::Doc(_)) {
from += 1;
} else {
break;
}
}
(from, self.tokens.get(from))
}
pub fn errors(&self) -> Vec<Error> {
self.errors.borrow().clone().clone()
}
pub fn next_is_whitespace(&self) -> bool {
if let Some(token) = self.tokens.get(self.position) {
if let Token::Whitespace(_) = token.token {
true
} else {
false
}
} else {
true
}
}
}
impl Drop for TokenStream<'_, '_> {
fn drop(&mut self) {
if let Some(ref_pos) = &mut self.ref_position {
**ref_pos = self.position;
}
}
}
/// Index-range that can be used with the original array of [`FullToken`]s to
/// retrieve the precise location of a failure.
#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct TokenRange {
pub start: usize,
pub end: usize,
}
impl std::fmt::Debug for TokenRange {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Tokens[{} - {}]", self.start, self.end)
}
}
impl std::ops::Add for TokenRange {
type Output = TokenRange;
fn add(self, rhs: Self) -> Self::Output {
TokenRange {
start: self.start.min(rhs.start).min(rhs.end),
end: self.end.max(rhs.end).max(rhs.start),
}
}
}
impl std::iter::Sum for TokenRange {
fn sum<I: Iterator<Item = Self>>(mut iter: I) -> Self {
let mut start = iter.next().unwrap_or(Default::default());
for item in iter {
start = start + item;
}
start
}
}
#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Error {
#[error("Expected {} got \"{:?}\"", .0, .1)]
Expected(String, Token, TokenRange),
#[error("Source file contains no tokens")]
FileEmpty,
/// Only use this error in situations where the error never ends up for the end-user!
#[error("Undefined error, should only be used in situations where the error is not emitted!")]
Undefined,
/// Condition failed for the parse-if
#[error("Condition failed for parse-if. Should never be returned to end-user.")]
IfFailed,
}
impl Error {
pub fn get_range(&self) -> Option<&TokenRange> {
match self {
Error::Expected(_, _, pos) => Some(pos),
Error::FileEmpty => None,
Error::Undefined => None,
Error::IfFailed => None,
}
}
}

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,377 +0,0 @@
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},
};
use crate::mir::typecheck::ErrorKind as TypecheckError;
fn label(text: &str) -> &str {
#[cfg(debug_assertions)]
{
return text;
}
#[cfg(not(debug_assertions))]
""
}
#[derive(thiserror::Error, Debug, Clone, PartialEq)]
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>),
#[error("{}{}", label("(TypeInference) "), .0.kind)]
TypeInferenceError(#[source] mir::pass::Error<TypecheckError>),
#[error("{}{}", label("(Linker) "), .0.kind)]
LinkerError(#[from] mir::pass::Error<mir::linker::ErrorKind>),
#[error("{}{}", label("(Macro) "), .0)]
MacroError(#[from] mir::pass::Error<macros::ErrorKind>),
#[error("{}{}", label("(Codegen) "), .0)]
CodegenError(#[from] codegen::ErrorKind),
}
impl ErrorKind {
pub fn from_typecheck(err: mir::pass::Error<typecheck::ErrorKind>) -> ErrorKind {
ErrorKind::TypeCheckError(err)
}
pub fn from_typeinference(err: mir::pass::Error<typecheck::ErrorKind>) -> ErrorKind {
ErrorKind::TypeInferenceError(err)
}
}
impl ErrorKind {
pub 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",
}
}
}
impl PartialOrd for ErrorKind {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.get_meta()
.source_module_id
.partial_cmp(&other.get_meta().source_module_id)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ErrorModule {
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>,
module_counter: mir::SourceModuleId,
}
impl ErrorModules {
pub fn add_module<T: Into<String>>(
&mut self,
name: T,
path: Option<PathBuf>,
external_module_id: Option<SourceModuleId>,
) -> Option<mir::SourceModuleId> {
let module_id = path.as_ref().and_then(|p| self.source_id_map.get(p));
if let Some(module_id) = module_id {
Some(*module_id)
} else {
let id = if let Some(module_id) = external_module_id {
self.module_counter = SourceModuleId(module_id.0.max(self.module_counter.0));
if let Some(_) = self.module_map.get(&module_id) {
panic!("Can not use external module id: Module already exists!")
}
module_id
} else {
self.module_counter.increment()
};
if let Some(path) = path {
self.source_id_map.insert(path, id);
}
self.module_map.insert(
id,
ErrorModule {
name: name.into(),
tokens: None,
source: None,
},
);
Some(id)
}
}
pub fn set_tokens(&mut self, id: mir::SourceModuleId, tokens: Vec<FullToken>) {
if let Some(module) = self.module_map.get_mut(&id) {
module.tokens = Some(tokens);
}
}
pub fn set_source(&mut self, id: mir::SourceModuleId, source: String) {
if let Some(module) = self.module_map.get_mut(&id) {
module.source = Some(source);
}
}
pub fn module(&self, id: &mir::SourceModuleId) -> Option<&ErrorModule> {
self.module_map.get(id)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ReidError {
map: ErrorModules,
pub errors: Vec<ErrorKind>,
}
impl ReidError {
pub fn from_lexer<U>(
result: Result<U, lexer::Error>,
map: ErrorModules,
module: SourceModuleId,
) -> Result<U, ReidError> {
result.map_err(|error| {
let pass_err = pass::Error {
metadata: Metadata {
source_module_id: module,
range: Default::default(),
position: Some(*error.get_position()),
},
kind: error,
};
ReidError {
map,
errors: vec![ErrorKind::LexerError(pass_err)],
}
})
}
pub fn from_parser<U>(
result: Result<U, token_stream::Error>,
map: ErrorModules,
module: SourceModuleId,
) -> Result<U, ReidError> {
result.map_err(|error| {
let pass_err = pass::Error {
metadata: Metadata {
source_module_id: module,
range: *error.get_range().unwrap_or(&Default::default()),
position: None,
},
kind: error,
};
ReidError {
map,
errors: vec![ErrorKind::ParserError(pass_err)],
}
})
}
pub fn from_kind(errors: Vec<ErrorKind>, map: ErrorModules) -> ReidError {
ReidError { map, errors }
}
pub fn extend(&mut self, other: ReidError) {
self.errors.extend(other.errors);
}
}
impl std::error::Error for ReidError {}
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.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
}
} else {
None
};
let module_name = 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)?;
write!(f, " Error: ")?;
writeln!(f, "{}", color_err(format!("{}", error))?)?;
write!(
f,
"{:>20} {}:{}",
color_warn("At: ")?,
module_name,
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())) {
writeln!(f, "{}", fmt_lines(source, position, 6)?)?;
}
}
Ok(())
}
}
impl TokenRange {
pub fn into_tokens<'v>(&self, tokens: &'v Vec<FullToken>) -> Vec<&'v FullToken> {
tokens
.iter()
.skip(self.start)
.by_ref()
.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)> {
if let Some(first) = tokens.first() {
let last = tokens.last().unwrap();
Some((first.position, last.position.add(last.token.len() as u32)))
} else {
None
}
}
fn into_full_lines<'v>((start, end): (Position, Position)) -> (Position, Position) {
(Position(0, start.1), Position(u32::MAX, end.1))
}
fn fmt_lines(
source: &String,
(highlight_start, highlight_end): (Position, Position),
ident: usize,
) -> Result<String, std::fmt::Error> {
let (line_start, line_end) = into_full_lines((highlight_start, highlight_end));
let mut cursor = Cursor {
position: Position(0, 1),
char_stream: source.chars(),
};
let mut text = String::new();
while let Some(c) = cursor.next() {
if cursor.position.1 > line_end.1 {
break;
}
if cursor.position.1 >= line_start.1 {
if c == '\n' {
write!(text, "\n{}", " ".repeat(ident))?;
} else {
if cursor.position > highlight_start && cursor.position <= highlight_end {
write!(text, "{}", color_highlight(c)?)?;
} else {
text.write_char(c)?;
}
}
}
}
Ok(text)
}
fn fmt_positions((start, end): (Position, Position)) -> String {
if start == end {
format!("{}:{}", start.1, start.0)
} else if start.1 == end.1 {
format!("{}:{} - {}", start.1, start.0, end.0)
} else {
format!("{}:{} - {}:{}", start.1, start.0, end.1, end.0)
}
}
fn color_err(elem: impl std::fmt::Display) -> Result<String, std::fmt::Error> {
let mut text = format!("{}", elem);
#[cfg(feature = "color")]
{
use colored::Colorize;
text = format!("{}", text.bright_red())
}
Ok(text)
}
fn color_warn(elem: impl std::fmt::Display) -> Result<String, std::fmt::Error> {
let mut text = format!("{}", elem);
#[cfg(feature = "color")]
{
use colored::Colorize;
text = format!("{}", text.bright_yellow())
}
Ok(text)
}
fn color_highlight(elem: impl std::fmt::Display) -> Result<String, std::fmt::Error> {
let mut text = format!("{}", elem);
#[cfg(feature = "color")]
{
use colored::Colorize;
text = format!("{}", text.bright_yellow().underline())
}
Ok(text)
}

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,349 +0,0 @@
//! Reid is a toy-language compiler I'm working on to learn LLVM (and compiler
//! development).
//!
//! Reid only uses [llvm-sys](https://gitlab.com/taricorp/llvm-sys.rs), which
//! provide very minimal bindings from the LLVM C-API to Rust. `reid_llvm`-crate
//! 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)
//! - 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:
//! ```reid
//! fn main() -> u16 {
//! return fibonacci(5);
//! }
//! fn fibonacci(n: u16) -> u16 {
//! if n <= 2 {
//! return 1;
//! }
//! return fibonacci(n-1) + fibonacci(n-2);
//! }
//! ```
//!
//! TODOs still (see README.md for more)
//! - Error handling
//! - Lexing & parsing of whitespace and comments as well
//! - LSP implementation
//! ```
use std::{collections::HashMap, 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 mir::{
linker::LinkerPass,
pass::BinopMap,
typecheck::{typecheck::TypeCheck, typeinference::TypeInference, typerefs::TypeRefs},
};
use reid_lib::{compile::CompileOutput, Context};
use crate::{
ast::TopLevelStatement,
mir::{
macros::{form_macros, MacroModule, MacroPass},
SourceModuleId,
},
};
pub mod ast;
pub mod codegen;
pub mod error_raporting;
pub mod ld;
pub mod mir;
mod pad_adapter;
mod util;
pub fn parse_module<'map, T: Into<String>>(
source: &str,
name: T,
path: Option<PathBuf>,
map: &'map mut ErrorModules,
module_id: Option<SourceModuleId>,
) -> Result<(mir::SourceModuleId, Vec<FullToken>), ReidError> {
let id = map.add_module(name.into(), path, module_id).unwrap();
map.set_source(id, source.to_owned());
let tokens = ReidError::from_lexer(lexer::tokenize(source), map.clone(), id)?;
map.set_tokens(id, tokens.clone());
#[cfg(debug_assertions)]
log::trace!("{:#?}", &tokens);
Ok((id, tokens))
}
pub fn compile_module<'map>(
module_id: mir::SourceModuleId,
tokens: Vec<FullToken>,
map: &'map mut ErrorModules,
path: Option<PathBuf>,
is_main: bool,
) -> Result<Result<mir::Module, (ast::Module, ReidError)>, ReidError> {
let module = map.module(&module_id).cloned().unwrap();
let mut token_stream = TokenStream::from(&tokens);
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)?;
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)))
}
pub fn perform_all_passes<'map>(
context: &mut mir::Context,
module_map: &'map mut ErrorModules,
) -> Result<(), ReidError> {
#[cfg(debug_assertions)]
log::trace!("{:#?}", &context);
#[cfg(debug_assertions)]
log::trace!("{:#}", &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);
}
}
#[cfg(debug_assertions)]
log::trace!("{:-^100}", "LINKER OUTPUT");
#[cfg(debug_assertions)]
log::trace!("{:#}", &context);
#[cfg(debug_assertions)]
log::trace!("{:#?}", &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 macro_modules: HashMap<_, MacroModule> = HashMap::new();
for (k, v) in &context.modules {
macro_modules.insert(k.clone(), v.into());
}
let mut macro_pass = MacroPass {
macros: form_macros(),
module_map: macro_modules,
};
let state = context.pass(&mut macro_pass)?;
#[cfg(debug_assertions)]
log::trace!("{:-^100}", "MACRO OUTPUT");
#[cfg(debug_assertions)]
log::trace!("{:#}", &context);
#[cfg(debug_assertions)]
log::trace!("{:#?}", &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(
state
.errors
.iter()
.map(|e| ErrorRapKind::TypeInferenceError(e.clone()))
.collect::<Vec<_>>(),
module_map.clone(),
));
}
let state = context.pass(&mut TypeCheck { refs: &refs })?;
#[cfg(debug_assertions)]
log::trace!("{:-^100}", "TYPECHECKER OUTPUT");
#[cfg(debug_assertions)]
log::trace!("{:#}", &context);
#[cfg(debug_assertions)]
log::trace!("{:#?}", &state);
if !state.errors.is_empty() {
return Err(ReidError::from_kind(
state
.errors
.iter()
.map(|e| ErrorRapKind::TypeCheckError(e.clone()))
.collect::<Vec<_>>(),
module_map.clone(),
));
}
Ok(())
}
/// Takes in a bit of source code, parses and compiles it and produces `hello.o`
/// and `hello.asm` from it, which can be linked using `ld` to produce an
/// executable file.
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> {
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 mut mir_context = mir::Context::from(vec![module], path.parent().unwrap().to_owned());
perform_all_passes(&mut mir_context, module_map)?;
#[cfg(debug_assertions)]
log::trace!("{:-^100}", "FINAL OUTPUT");
#[cfg(debug_assertions)]
log::trace!("{:#}", &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()))?,
};
#[cfg(debug_assertions)]
log::trace!("{}", &codegen_modules.context);
let compiled = codegen_modules.compile(cpu, features);
Ok((
compiled.output(),
CustomIRs {
llir: format!("{}", codegen_modules.context),
mir: format!("{}", mir_context),
},
))
}
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)
}

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

@ -1,502 +0,0 @@
use std::fmt::{Debug, Display, Write};
use crate::pad_adapter::PadAdapter;
use super::*;
impl Display for Context {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (_, module) in &self.modules {
Display::fmt(&module, f)?;
}
Ok(())
}
}
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)?;
let mut state = Default::default();
let mut inner_f = PadAdapter::wrap(f, &mut state);
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)?;
}
writeln!(f, "}}")
}
}
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)
}
}
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()
}
)?;
Display::fmt(&self.kind, f)
}
}
impl Display for TypeDefinitionKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TypeDefinitionKind::Struct(items) => {
write!(f, "struct ")?;
f.write_char('{')?;
writeln!(f)?;
let mut state = Default::default();
let mut inner_f = PadAdapter::wrap(f, &mut state);
for field in &items.0 {
writeln!(inner_f, "{},", field)?;
}
f.write_char('}')
}
}
}
}
impl Display for StructField {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}: {:?}", self.0, self.1)
}
}
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 {}({}) -> {:#} ",
if self.is_pub { "pub " } else { "" },
self.name,
self.parameters
.iter()
.map(|FunctionParam { name, ty, .. }| format!("{}: {:#}", name, ty))
.collect::<Vec<_>>()
.join(", "),
self.return_type
)?;
Display::fmt(&self.kind, f)
}
}
impl Display for FunctionDefinitionKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FunctionDefinitionKind::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>"),
}
}
}
impl Display for Block {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{{")?;
let mut state = Default::default();
let mut inner_f = PadAdapter::wrap(f, &mut state);
for statement in &self.statements {
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),
}?;
} else {
writeln!(inner_f, "No Return")?;
}
writeln!(f, "}}")
}
}
impl Display for Statement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{}", self.0)
}
}
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,)
}
}
}
}
impl Display for Expression {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_char('(')?;
Display::fmt(&self.0, f)?;
f.write_char(')')?;
Ok(())
}
}
impl Display for ExprKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
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::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_index(f, idx_expr)
}
ExprKind::Array(expressions) => {
f.write_char('[')?;
let mut state = Default::default();
let mut inner_f = PadAdapter::wrap(f, &mut state);
let mut iter = expressions.iter();
if let Some(item) = iter.next() {
write!(inner_f, "\n{}", item)?;
while let Some(item) = iter.next() {
writeln!(inner_f, ",")?;
write!(inner_f, "{}", item)?;
}
writeln!(inner_f, "")?;
}
f.write_char(']')
}
ExprKind::Struct(key, items) => {
write!(f, "{:?} ", key)?;
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() {
write!(inner_f, "\n{}: {}", name, expr)?;
while let Some((name, expr, _)) = iter.next() {
writeln!(inner_f, ",")?;
write!(inner_f, "{}: {}", name, expr)?;
}
writeln!(inner_f, "")?;
}
f.write_char('}')
}
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::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),
}
}
}
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() {
Display::fmt(&e, f)?;
}
Ok(())
}
}
impl Display for FunctionCall {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
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) {
write!(f, ", ")?;
}
}
write!(f, ")")
}
}
impl Display for NamedVariableRef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "v(\"{}\", {:#})", &self.1, &self.0)
}
}
impl Display for Literal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Literal::I8(val) => write!(f, "{}i8", val),
Literal::I16(val) => write!(f, "{}i16", val),
Literal::I32(val) => write!(f, "{}i32", val),
Literal::I64(val) => write!(f, "{}i64", val),
Literal::I128(val) => write!(f, "{}i128", val),
Literal::U8(val) => write!(f, "{}u8", val),
Literal::U16(val) => write!(f, "{}u16", val),
Literal::U32(val) => write!(f, "{}u32", val),
Literal::U64(val) => write!(f, "{}u64", val),
Literal::U128(val) => write!(f, "{}u128", val),
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),
}
}
}
impl Display for BinaryOperator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BinaryOperator::Add => write!(f, "+"),
BinaryOperator::Minus => write!(f, "-"),
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, "<<"),
}
}
}
impl Display for CmpOperator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CmpOperator::LT => write!(f, "<"),
CmpOperator::LE => write!(f, "<="),
CmpOperator::GT => write!(f, ">"),
CmpOperator::GE => write!(f, ">="),
CmpOperator::EQ => write!(f, "=="),
CmpOperator::NE => write!(f, "!="),
}
}
}
impl Display for Metadata {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?} ({})", self.range, self.source_module_id)
}
}
impl Display for SourceModuleId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
fn write_index(f: &mut std::fmt::Formatter<'_>, idx: impl std::fmt::Display) -> std::fmt::Result {
f.write_char('[')?;
Display::fmt(&idx, f)?;
f.write_char(']')
}
fn write_access(f: &mut std::fmt::Formatter<'_>, name: &String) -> std::fmt::Result {
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"),
}
}
}
}

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