Compare commits
No commits in common. "main" and "42e74f49f8aad5e222991cd33df13c905109dc81" have entirely different histories.
main
...
42e74f49f8
13
.gitignore
vendored
13
.gitignore
vendored
@ -1,14 +1 @@
|
||||
|
||||
src/old_llvm
|
||||
/target
|
||||
/.vscode
|
||||
.env
|
||||
hello.*
|
||||
main
|
||||
*.o
|
||||
*.ll
|
||||
*.asm
|
||||
*.out
|
||||
*.llir
|
||||
*.mir
|
||||
foo.reid
|
@ -1 +0,0 @@
|
||||
max_width = 120
|
1476
Cargo.lock
generated
1476
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
14
Cargo.toml
14
Cargo.toml
@ -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]
|
||||
|
17
Makefile
17
Makefile
@ -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
175
README.md
@ -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
|
@ -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
|
||||
```
|
@ -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!
|
@ -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.
|
@ -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
4
easiest.reid
Normal file
@ -0,0 +1,4 @@
|
||||
// Hello, comment here!
|
||||
let hello = 32;
|
||||
let beep =
|
||||
hello ;
|
12
easy.reid
Normal file
12
easy.reid
Normal 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);
|
@ -1,9 +0,0 @@
|
||||
// Arithmetic, function calls and imports!
|
||||
|
||||
|
||||
fn main() -> u32 {
|
||||
let value = 0b110;
|
||||
let other = 0o17;
|
||||
|
||||
return value * other + 7 * -value;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
// Arithmetic, function calls and imports!
|
||||
|
||||
fn main() -> u16 {
|
||||
let a = [[ 5, 3, 2 ]];
|
||||
|
||||
return a[0][1];
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
// Arithmetic, function calls and imports!
|
||||
|
||||
fn main() -> u16 {
|
||||
let mut a = [5; 20];
|
||||
|
||||
return a[15];
|
||||
}
|
@ -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];
|
||||
}
|
@ -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;
|
||||
}
|
@ -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();
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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];
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
// Arithmetic, function calls and imports!
|
||||
|
||||
pub fn main() -> char {
|
||||
return 'b';
|
||||
}
|
@ -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;
|
||||
}
|
@ -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) {}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
// Arithmetic, function calls and imports!
|
||||
|
||||
fn main() -> u16 {
|
||||
|
||||
return (50 / 5 + 52 % 5);
|
||||
}
|
@ -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)
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
// Arithmetic, function calls and imports!
|
||||
|
||||
|
||||
pub fn main() -> bool {
|
||||
return 7.5 > 5.001;
|
||||
}
|
@ -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!"));
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
import foreign_struct::Vec2;
|
||||
|
||||
fn main() -> u32 {
|
||||
let a = Vec2 {x: 16, y: 32};
|
||||
return a.x;
|
||||
}
|
@ -1 +0,0 @@
|
||||
struct Vec2 { x: u32, y: u32 }
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
fn main() -> i32 {
|
||||
for i in 0..1 {
|
||||
let j = i;
|
||||
if i != j {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -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;
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
// Arithmetic, function calls and imports!
|
||||
|
||||
fn main() -> u32 {
|
||||
|
||||
let mut num = 0;
|
||||
while num < 10 {
|
||||
num = num + 1;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
@ -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];
|
||||
}
|
@ -1 +0,0 @@
|
||||
hello
|
@ -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];
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
|
||||
struct Otus {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
pub fn test() -> Otus {
|
||||
Otus {field: 4}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
fn main() -> bool {
|
||||
let ptr = i32::null();
|
||||
return i32::is_null(ptr);
|
||||
}
|
@ -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);
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
// Arithmetic, function calls and imports!
|
||||
|
||||
fn main() -> u8 {
|
||||
let mut ptr = u8::malloc(4);
|
||||
|
||||
ptr[0] = 5;
|
||||
|
||||
return ptr[0];
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
fn main() -> u8 {
|
||||
let mut a = 5;
|
||||
inner(&mut a);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn inner(a: &mut i32) {
|
||||
*a = *a + 1;
|
||||
}
|
@ -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;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
|
||||
|
||||
fn main() -> u16 {
|
||||
let hello = "beep boop grw";
|
||||
|
||||
return 5;
|
||||
}
|
@ -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;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
// Arithmetic, function calls and imports!
|
||||
|
||||
|
||||
fn main() -> bool {
|
||||
|
||||
return 5.0 > -1;
|
||||
}
|
@ -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
8
hard.reid
Normal 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));
|
52
libtest.sh
52
libtest.sh
@ -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
12
medium.reid
Normal 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,12 +0,0 @@
|
||||
[package]
|
||||
name = "reid-lib"
|
||||
version = "1.0.0-beta.4"
|
||||
edition = "2024"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
## LLVM Bindings
|
||||
llvm-sys = {version ="201.0.1" }
|
||||
## Make it easier to generate errors
|
||||
thiserror = "1.0.44"
|
@ -1,56 +0,0 @@
|
||||
use reid_lib::{CmpPredicate, ConstValueKind, Context, FunctionFlags, Instr, TerminatorKind, Type};
|
||||
|
||||
fn main() {
|
||||
use ConstValueKind::*;
|
||||
use Instr::*;
|
||||
|
||||
let context = Context::new("libtest");
|
||||
|
||||
let module = context.module("test", true);
|
||||
|
||||
let main = module.function("main", None, Type::I32, Vec::new(), FunctionFlags::default());
|
||||
let mut m_entry = main.block("entry");
|
||||
|
||||
let fibonacci = module.function("fibonacci", None, Type::I32, vec![Type::I32], FunctionFlags::default());
|
||||
|
||||
let arg = m_entry.build_named("const", Constant(I32(5))).unwrap();
|
||||
let fibonacci_call = m_entry
|
||||
.build_named("const", FunctionCall(fibonacci.value(), vec![arg]))
|
||||
.unwrap();
|
||||
m_entry.terminate(TerminatorKind::Ret(fibonacci_call)).unwrap();
|
||||
|
||||
let mut f_entry = fibonacci.block("entry");
|
||||
|
||||
let num_3 = f_entry.build_named("const", Constant(I32(3))).unwrap();
|
||||
let param_n = f_entry.build_named("param", Param(0)).unwrap();
|
||||
let cond = f_entry
|
||||
.build_named("cmp", ICmp(CmpPredicate::LT, param_n, num_3))
|
||||
.unwrap();
|
||||
|
||||
let mut then_b = fibonacci.block("then");
|
||||
let mut else_b = fibonacci.block("else");
|
||||
|
||||
f_entry
|
||||
.terminate(TerminatorKind::CondBr(cond, then_b.value(), else_b.value()))
|
||||
.unwrap();
|
||||
|
||||
let ret_const = then_b.build_named("const", Constant(I32(1))).unwrap();
|
||||
then_b.terminate(TerminatorKind::Ret(ret_const)).unwrap();
|
||||
|
||||
let const_1 = else_b.build_named("const", Constant(I32(1))).unwrap();
|
||||
let const_2 = else_b.build_named("const", Constant(I32(2))).unwrap();
|
||||
let param_1 = else_b.build_named("sub", Sub(param_n, const_1)).unwrap();
|
||||
let param_2 = else_b.build_named("sub", Sub(param_n, const_2)).unwrap();
|
||||
let call_1 = else_b
|
||||
.build_named("fibonacci", FunctionCall(fibonacci.value(), vec![param_1]))
|
||||
.unwrap();
|
||||
let call_2 = else_b
|
||||
.build_named("fibonacci", FunctionCall(fibonacci.value(), vec![param_2]))
|
||||
.unwrap();
|
||||
|
||||
let add = else_b.build_named("add", Add(call_1, call_2)).unwrap();
|
||||
|
||||
else_b.terminate(TerminatorKind::Ret(add)).unwrap();
|
||||
|
||||
context.compile(None, Vec::new());
|
||||
}
|
@ -1,835 +0,0 @@
|
||||
//! This module contains simply [`Builder`] and it's related utility Values.
|
||||
//! Builder is the actual struct being modified when building the LLIR.
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
Block, BlockData, CompileResult, ConstValueKind, CustomTypeKind, ErrorKind, FunctionData, Instr, InstructionData,
|
||||
ModuleData, NamedStruct, TerminatorKind, Type, TypeCategory, TypeData,
|
||||
debug_information::{
|
||||
DebugInformation, DebugLocationValue, DebugMetadataValue, DebugScopeValue, InstructionDebugRecordData,
|
||||
},
|
||||
util::match_types,
|
||||
};
|
||||
|
||||
#[derive(Clone, Hash, Copy, PartialEq, Eq, PartialOrd)]
|
||||
pub struct ModuleValue(pub(crate) usize);
|
||||
|
||||
#[derive(Clone, Hash, Copy, PartialEq, Eq, PartialOrd)]
|
||||
pub struct TypeValue(pub(crate) ModuleValue, pub(crate) usize);
|
||||
|
||||
#[derive(Clone, Hash, Copy, PartialEq, Eq, PartialOrd)]
|
||||
pub struct FunctionValue(pub(crate) ModuleValue, pub(crate) usize);
|
||||
|
||||
#[derive(Clone, Hash, Copy, PartialEq, Eq, PartialOrd)]
|
||||
pub struct BlockValue(pub(crate) FunctionValue, pub(crate) usize);
|
||||
|
||||
#[derive(Clone, Hash, Copy, PartialEq, Eq, PartialOrd)]
|
||||
pub struct InstructionValue(pub(crate) BlockValue, pub(crate) usize);
|
||||
|
||||
#[derive(Debug, Clone, Hash, Copy, PartialEq, Eq)]
|
||||
pub struct ConstantValue(pub(crate) ModuleValue, pub(crate) usize);
|
||||
|
||||
#[derive(Debug, Clone, Hash, Copy, PartialEq, Eq)]
|
||||
pub struct GlobalValue(pub(crate) ModuleValue, pub(crate) usize);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ModuleHolder {
|
||||
pub(crate) value: ModuleValue,
|
||||
pub(crate) data: ModuleData,
|
||||
pub(crate) functions: Vec<FunctionHolder>,
|
||||
pub(crate) types: Vec<TypeHolder>,
|
||||
pub(crate) debug_information: Option<DebugInformation>,
|
||||
pub(crate) constants: Vec<ConstantValueHolder>,
|
||||
pub(crate) globals: Vec<GlobalValueHolder>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConstantValueHolder {
|
||||
pub(crate) value: ConstantValue,
|
||||
pub(crate) kind: ConstValueKind,
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct GlobalValueHolder {
|
||||
pub(crate) value: GlobalValue,
|
||||
pub(crate) name: String,
|
||||
pub(crate) initializer: ConstantValue,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TypeHolder {
|
||||
pub(crate) value: TypeValue,
|
||||
pub(crate) data: TypeData,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FunctionHolder {
|
||||
pub(crate) value: FunctionValue,
|
||||
pub(crate) data: FunctionData,
|
||||
pub(crate) blocks: Vec<BlockHolder>,
|
||||
/// Debug scope value of this current function
|
||||
pub(crate) debug_info: Option<DebugScopeValue>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BlockHolder {
|
||||
pub(crate) value: BlockValue,
|
||||
pub(crate) data: BlockData,
|
||||
pub(crate) instructions: Vec<InstructionHolder>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InstructionHolder {
|
||||
pub(crate) value: InstructionValue,
|
||||
pub(crate) data: InstructionData,
|
||||
pub(crate) name: String,
|
||||
pub(crate) record: Option<InstructionDebugRecordData>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct Builder {
|
||||
pub(crate) modules: Rc<RefCell<Vec<ModuleHolder>>>,
|
||||
pub(crate) producer: String,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
pub fn new(producer: String) -> Builder {
|
||||
Builder {
|
||||
modules: Rc::new(RefCell::new(Vec::new())),
|
||||
producer,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_module(&self, data: ModuleData) -> ModuleValue {
|
||||
let value = ModuleValue(self.modules.borrow().len());
|
||||
self.modules.borrow_mut().push(ModuleHolder {
|
||||
value,
|
||||
data,
|
||||
functions: Vec::new(),
|
||||
types: Vec::new(),
|
||||
debug_information: None,
|
||||
constants: Vec::new(),
|
||||
globals: Vec::new(),
|
||||
});
|
||||
value
|
||||
}
|
||||
|
||||
pub(crate) fn set_debug_information(&self, mod_val: &ModuleValue, debug_info: DebugInformation) {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(mod_val.0);
|
||||
module.debug_information = Some(debug_info);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn add_type(&self, mod_val: &ModuleValue, data: TypeData) -> TypeValue {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(mod_val.0);
|
||||
let value = TypeValue(module.value, module.types.len());
|
||||
module.types.push(TypeHolder { value, data });
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn add_function(&self, mod_val: &ModuleValue, data: FunctionData) -> FunctionValue {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(mod_val.0);
|
||||
let value = FunctionValue(module.value, module.functions.len());
|
||||
module.functions.push(FunctionHolder {
|
||||
value,
|
||||
data,
|
||||
blocks: Vec::new(),
|
||||
debug_info: None,
|
||||
});
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn add_block(&self, fun_val: &FunctionValue, data: BlockData) -> BlockValue {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(fun_val.0.0);
|
||||
let function = module.functions.get_unchecked_mut(fun_val.1);
|
||||
let value = BlockValue(function.value, function.blocks.len());
|
||||
function.blocks.push(BlockHolder {
|
||||
value,
|
||||
data,
|
||||
instructions: Vec::new(),
|
||||
});
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn add_instruction(
|
||||
&self,
|
||||
block_val: &BlockValue,
|
||||
data: InstructionData,
|
||||
name: String,
|
||||
) -> CompileResult<InstructionValue> {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(block_val.0.0.0);
|
||||
let function = module.functions.get_unchecked_mut(block_val.0.1);
|
||||
let block = function.blocks.get_unchecked_mut(block_val.1);
|
||||
let value = InstructionValue(block.value, block.instructions.len());
|
||||
block.instructions.push(InstructionHolder {
|
||||
value,
|
||||
data,
|
||||
name,
|
||||
record: None,
|
||||
});
|
||||
|
||||
// Drop modules so that it is no longer mutable borrowed
|
||||
// (check_instruction requires an immutable borrow).
|
||||
drop(modules);
|
||||
|
||||
self.check_instruction(&value)?;
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn build_constant(&self, module: ModuleValue, kind: ConstValueKind) -> ConstantValue {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(module.0);
|
||||
let value = ConstantValue(module.value, module.constants.len());
|
||||
module.constants.push(ConstantValueHolder { value, kind });
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn add_global(
|
||||
&self,
|
||||
module: ModuleValue,
|
||||
name: String,
|
||||
initializer: ConstantValue,
|
||||
) -> GlobalValue {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(module.0);
|
||||
let value = GlobalValue(module.value, module.globals.len());
|
||||
module.globals.push(GlobalValueHolder {
|
||||
value,
|
||||
name,
|
||||
initializer,
|
||||
});
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn find_function(&self, module: ModuleValue, name: &String) -> Option<FunctionValue> {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(module.0);
|
||||
module.functions.iter().find(|f| f.data.name == *name).map(|f| f.value)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn add_instruction_location(&self, value: &InstructionValue, location: DebugLocationValue) {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(value.0.0.0.0);
|
||||
let function = module.functions.get_unchecked_mut(value.0.0.1);
|
||||
let block = function.blocks.get_unchecked_mut(value.0.1);
|
||||
let instr = block.instructions.get_unchecked_mut(value.1);
|
||||
instr.data.location = Some(location)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn add_instruction_metadata(&self, value: &InstructionValue, metadata: DebugMetadataValue) {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(value.0.0.0.0);
|
||||
let function = module.functions.get_unchecked_mut(value.0.0.1);
|
||||
let block = function.blocks.get_unchecked_mut(value.0.1);
|
||||
let instr = block.instructions.get_unchecked_mut(value.1);
|
||||
instr.data.meta = Some(metadata)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn add_instruction_record(&self, value: &InstructionValue, record: InstructionDebugRecordData) {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(value.0.0.0.0);
|
||||
let function = module.functions.get_unchecked_mut(value.0.0.1);
|
||||
let block = function.blocks.get_unchecked_mut(value.0.1);
|
||||
let instr = block.instructions.get_unchecked_mut(value.1);
|
||||
instr.record = Some(record)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn set_debug_subprogram(&self, value: &FunctionValue, subprogram: DebugScopeValue) {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(value.0.0);
|
||||
let function = module.functions.get_unchecked_mut(value.1);
|
||||
function.debug_info = Some(subprogram)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn terminate(&self, block: &BlockValue, value: TerminatorKind) -> CompileResult<()> {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(block.0.0.0);
|
||||
let function = module.functions.get_unchecked_mut(block.0.1);
|
||||
let block = function.blocks.get_unchecked_mut(block.1);
|
||||
if let Some(_) = &block.data.terminator {
|
||||
Err(ErrorKind::BlockAlreadyTerminated)
|
||||
} else {
|
||||
block.data.terminator = Some(value);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn set_terminator_location(
|
||||
&self,
|
||||
block: &BlockValue,
|
||||
location: DebugLocationValue,
|
||||
) -> CompileResult<()> {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(block.0.0.0);
|
||||
let function = module.functions.get_unchecked_mut(block.0.1);
|
||||
let block = function.blocks.get_unchecked_mut(block.1);
|
||||
if let Some(_) = &block.data.terminator_location {
|
||||
Err(ErrorKind::BlockTerminatorLocated)
|
||||
} else {
|
||||
block.data.terminator_location = Some(location);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn delete_block(&self, block: &BlockValue) -> CompileResult<()> {
|
||||
unsafe {
|
||||
let mut modules = self.modules.borrow_mut();
|
||||
let module = modules.get_unchecked_mut(block.0.0.0);
|
||||
let function = module.functions.get_unchecked_mut(block.0.1);
|
||||
let block = function.blocks.get_unchecked_mut(block.1);
|
||||
block.data.deleted = true;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) unsafe fn module_data(&self, value: &ModuleValue) -> ModuleData {
|
||||
unsafe { self.modules.borrow().get_unchecked(value.0).data.clone() }
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn function_data(&self, value: &FunctionValue) -> FunctionData {
|
||||
unsafe {
|
||||
self.modules
|
||||
.borrow()
|
||||
.get_unchecked(value.0.0)
|
||||
.functions
|
||||
.get_unchecked(value.1)
|
||||
.data
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) unsafe fn block_data(&self, value: &BlockValue) -> BlockData {
|
||||
unsafe {
|
||||
self.modules
|
||||
.borrow()
|
||||
.get_unchecked(value.0.0.0)
|
||||
.functions
|
||||
.get_unchecked(value.0.1)
|
||||
.blocks
|
||||
.get_unchecked(value.1)
|
||||
.data
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn instr_data(&self, value: &InstructionValue) -> InstructionData {
|
||||
unsafe {
|
||||
self.modules
|
||||
.borrow()
|
||||
.get_unchecked(value.0.0.0.0)
|
||||
.functions
|
||||
.get_unchecked(value.0.0.1)
|
||||
.blocks
|
||||
.get_unchecked(value.0.1)
|
||||
.instructions
|
||||
.get_unchecked(value.1)
|
||||
.data
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn type_data(&self, value: &TypeValue) -> TypeData {
|
||||
unsafe {
|
||||
self.modules
|
||||
.borrow()
|
||||
.get_unchecked(value.0.0)
|
||||
.types
|
||||
.get_unchecked(value.1)
|
||||
.data
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn find_module<'ctx>(&'ctx self, value: ModuleValue) -> ModuleHolder {
|
||||
unsafe { self.modules.borrow().get_unchecked(value.0).clone() }
|
||||
}
|
||||
|
||||
pub(crate) fn get_modules(&self) -> Rc<RefCell<Vec<ModuleHolder>>> {
|
||||
self.modules.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn get_global_initializer(&self, value: GlobalValue) -> ConstantValue {
|
||||
unsafe {
|
||||
let modules = self.modules.borrow();
|
||||
let module = modules.get_unchecked(value.0.0);
|
||||
let global = module.globals.get_unchecked(value.1);
|
||||
global.initializer
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_const_kind(&self, value: ConstantValue) -> ConstValueKind {
|
||||
unsafe {
|
||||
let modules = self.modules.borrow();
|
||||
let module = modules.get_unchecked(value.0.0);
|
||||
let constant = module.constants.get_unchecked(value.1);
|
||||
constant.kind.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_instruction(&self, instruction: &InstructionValue) -> CompileResult<()> {
|
||||
unsafe {
|
||||
match self.instr_data(&instruction).kind {
|
||||
Instr::Param(_) => Ok(()),
|
||||
Instr::Constant(_) => Ok(()),
|
||||
Instr::Add(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category().integer() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::TypeNotInteger(lhs, lhs.get_type(&self)?))
|
||||
}
|
||||
}
|
||||
Instr::FAdd(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::TypeWrongCategory(
|
||||
lhs,
|
||||
lhs.get_type(&self)?,
|
||||
TypeCategory::Real,
|
||||
))
|
||||
}
|
||||
}
|
||||
Instr::Sub(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category().integer() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::TypeNotInteger(lhs, lhs.get_type(&self)?))
|
||||
}
|
||||
}
|
||||
Instr::FSub(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::TypeWrongCategory(
|
||||
lhs,
|
||||
lhs.get_type(&self)?,
|
||||
TypeCategory::Real,
|
||||
))
|
||||
}
|
||||
}
|
||||
Instr::Mul(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category().integer() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::TypeNotInteger(lhs, lhs.get_type(&self)?))
|
||||
}
|
||||
}
|
||||
Instr::FMul(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::TypeWrongCategory(
|
||||
lhs,
|
||||
lhs.get_type(&self)?,
|
||||
TypeCategory::Real,
|
||||
))
|
||||
}
|
||||
}
|
||||
Instr::UDiv(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::UnsignedInteger {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::TypeWrongCategory(
|
||||
lhs,
|
||||
lhs.get_type(&self)?,
|
||||
TypeCategory::UnsignedInteger,
|
||||
))
|
||||
}
|
||||
}
|
||||
Instr::SDiv(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::SignedInteger {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::TypeWrongCategory(
|
||||
lhs,
|
||||
lhs.get_type(&self)?,
|
||||
TypeCategory::SignedInteger,
|
||||
))
|
||||
}
|
||||
}
|
||||
Instr::FDiv(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::TypeWrongCategory(
|
||||
lhs,
|
||||
lhs.get_type(&self)?,
|
||||
TypeCategory::Real,
|
||||
))
|
||||
}
|
||||
}
|
||||
Instr::URem(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::UnsignedInteger {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::TypeWrongCategory(
|
||||
lhs,
|
||||
lhs.get_type(&self)?,
|
||||
TypeCategory::UnsignedInteger,
|
||||
))
|
||||
}
|
||||
}
|
||||
Instr::SRem(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::SignedInteger {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::TypeWrongCategory(
|
||||
lhs,
|
||||
lhs.get_type(&self)?,
|
||||
TypeCategory::SignedInteger,
|
||||
))
|
||||
}
|
||||
}
|
||||
Instr::FRem(lhs, rhs) => {
|
||||
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::TypeWrongCategory(
|
||||
lhs,
|
||||
lhs.get_type(&self)?,
|
||||
TypeCategory::Real,
|
||||
))
|
||||
}
|
||||
}
|
||||
Instr::And(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
|
||||
Instr::Or(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
|
||||
Instr::XOr(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
|
||||
Instr::ICmp(_, lhs, rhs) => {
|
||||
let t = match_types(&lhs, &rhs, self)?;
|
||||
if t.category().comparable() || !t.category().integer() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::TypeNotComparable(lhs, t))
|
||||
}
|
||||
}
|
||||
Instr::FCmp(_, lhs, rhs) => {
|
||||
let t = match_types(&lhs, &rhs, self)?;
|
||||
if t.category().comparable() || t.category() != TypeCategory::Real {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::TypeNotComparable(lhs, t))
|
||||
}
|
||||
}
|
||||
Instr::FunctionCall(fun, params) => {
|
||||
let param_types = self.function_data(&fun).params;
|
||||
if param_types.len() != params.len() {
|
||||
return Err(ErrorKind::InvalidLenParams(params.len(), param_types.len()));
|
||||
}
|
||||
for (a, b) in param_types.iter().zip(params) {
|
||||
if *a != b.get_type(&self)? {
|
||||
return Err(ErrorKind::TypesIncompatible(a.clone(), b.get_type(&self)?));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Instr::Phi(vals) => {
|
||||
let mut iter = vals.iter();
|
||||
// TODO error: Phi must contain at least one item
|
||||
|
||||
// TODO error: compile can actually crash here if any of the
|
||||
// incoming values come from blocks that are added later
|
||||
// than the one where this one exists.
|
||||
|
||||
let first = iter.next().ok_or(ErrorKind::EmptyPhiList)?;
|
||||
for item in iter {
|
||||
match_types(first, item, &self)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Instr::Alloca(_) => Ok(()),
|
||||
Instr::Load(ptr, load_ty) => {
|
||||
let ptr_ty = ptr.get_type(&self)?;
|
||||
if let Type::Ptr(ptr_ty_inner) = ptr_ty {
|
||||
if *ptr_ty_inner == load_ty {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::TypesIncompatible(*ptr_ty_inner, load_ty))
|
||||
}
|
||||
} else {
|
||||
Err(ErrorKind::NotPointer(ptr, ptr_ty))
|
||||
}
|
||||
}
|
||||
Instr::Store(ptr, _) => {
|
||||
let ty = ptr.get_type(&self)?;
|
||||
if let Type::Ptr(_) = ty {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::NotPointer(ptr, ty))
|
||||
}
|
||||
}
|
||||
Instr::ArrayAlloca(_, val) => {
|
||||
if val.get_type(self)?.category() == TypeCategory::UnsignedInteger {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::TypeWrongCategory(
|
||||
val,
|
||||
val.get_type(self)?,
|
||||
TypeCategory::UnsignedInteger,
|
||||
))
|
||||
}
|
||||
}
|
||||
Instr::GetElemPtr(ptr_val, _) => {
|
||||
let ptr_ty = ptr_val.get_type(&self)?;
|
||||
match ptr_ty {
|
||||
Type::Ptr(_) => Ok(()),
|
||||
_ => Err(ErrorKind::NotPointer(ptr_val, ptr_ty)),
|
||||
}
|
||||
}
|
||||
Instr::GetStructElemPtr(ptr_val, idx) => {
|
||||
let ptr_ty = ptr_val.get_type(&self)?;
|
||||
if let Type::Ptr(ty) = ptr_ty {
|
||||
if let Type::CustomType(val) = *ty {
|
||||
match self.type_data(&val).kind {
|
||||
CustomTypeKind::NamedStruct(NamedStruct(_, fields)) => {
|
||||
if fields.len() <= idx as usize {
|
||||
return Err(ErrorKind::NoSuchField(*ty, idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::NotStruct(ptr_val, *ty))
|
||||
}
|
||||
} else {
|
||||
Err(ErrorKind::NotPointer(ptr_val, ptr_ty))
|
||||
}
|
||||
}
|
||||
Instr::ExtractValue(val, _) => {
|
||||
let val_ty = val.get_type(&self)?;
|
||||
match val_ty {
|
||||
Type::CustomType(custom) => match self.type_data(&custom).kind {
|
||||
CustomTypeKind::NamedStruct(_) => Ok(()),
|
||||
},
|
||||
Type::Array(_, _) => Ok(()),
|
||||
_ => Err(ErrorKind::NotExtractable(val, val_ty)),
|
||||
}
|
||||
}
|
||||
Instr::Trunc(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::ZExt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::SExt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::FPTrunc(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::FPExt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::FPToUI(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::FPToSI(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::UIToFP(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::SIToFP(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::PtrToInt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::IntToPtr(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||
Instr::BitCast(..) => Ok(()),
|
||||
Instr::ShiftRightLogical(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
|
||||
Instr::ShiftRightArithmetic(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
|
||||
Instr::ShiftLeft(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
|
||||
Instr::GetGlobal(_) => Ok(()),
|
||||
Instr::IsNull(val) => {
|
||||
let val_ty = val.get_type(&self)?;
|
||||
if let Type::Ptr(_) = val_ty {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::NotPointer(val, val_ty))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_block_used(&self, block_v: BlockValue) -> bool {
|
||||
unsafe {
|
||||
let modules = self.modules.borrow();
|
||||
let module = modules.get_unchecked(block_v.0.0.0);
|
||||
let function = module.functions.get_unchecked(block_v.0.1);
|
||||
let block = function.blocks.get_unchecked(block_v.1);
|
||||
|
||||
if block.instructions.len() > 0 || block.data.terminator.is_some() {
|
||||
return true;
|
||||
}
|
||||
|
||||
for other in &function.blocks {
|
||||
if let Some(term) = &other.data.terminator {
|
||||
match term {
|
||||
TerminatorKind::Ret(_) => {}
|
||||
TerminatorKind::RetVoid => {}
|
||||
TerminatorKind::Br(other_val) => {
|
||||
if other_val == &block_v {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
TerminatorKind::CondBr(_, then_other_v, else_other_v) => {
|
||||
if then_other_v == &block_v || else_other_v == &block_v {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionValue {
|
||||
pub fn with_location(self, block: &Block, location: DebugLocationValue) -> InstructionValue {
|
||||
unsafe {
|
||||
block.builder.add_instruction_location(&self, location);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn maybe_location(self, block: &mut Block, location: Option<DebugLocationValue>) -> InstructionValue {
|
||||
unsafe {
|
||||
if let Some(location) = location {
|
||||
block.builder.add_instruction_location(&self, location);
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_record(&self, block: &mut Block, record: InstructionDebugRecordData) {
|
||||
unsafe {
|
||||
block.builder.add_instruction_record(self, record);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_type(&self, builder: &Builder) -> CompileResult<Type> {
|
||||
use Instr::*;
|
||||
unsafe {
|
||||
match &builder.instr_data(self).kind {
|
||||
Param(nth) => builder
|
||||
.function_data(&self.0.0)
|
||||
.params
|
||||
.get(*nth)
|
||||
.cloned()
|
||||
.ok_or(ErrorKind::NoSuchParam(self.0.0, *nth)),
|
||||
Constant(c) => Ok(c.get_type()),
|
||||
Add(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
FAdd(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
Sub(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
FSub(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
Mul(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
FMul(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
UDiv(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
SDiv(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
FDiv(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
URem(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
SRem(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
FRem(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
And(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
Or(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
XOr(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||
ICmp(_, _, _) => Ok(Type::Bool),
|
||||
FCmp(_, _, _) => Ok(Type::Bool),
|
||||
FunctionCall(function_value, _) => Ok(builder.function_data(function_value).ret),
|
||||
Phi(values) => values
|
||||
.first()
|
||||
.ok_or(ErrorKind::EmptyPhiList)
|
||||
.and_then(|v| v.get_type(&builder)),
|
||||
Alloca(ty) => Ok(Type::Ptr(Box::new(ty.clone()))),
|
||||
Load(_, ty) => Ok(ty.clone()),
|
||||
Store(_, value) => value.get_type(builder),
|
||||
ArrayAlloca(ty, _) => Ok(Type::Ptr(Box::new(ty.clone()))),
|
||||
GetElemPtr(instr, _) => {
|
||||
let instr_ty = instr.get_type(builder)?;
|
||||
let Type::Ptr(inner_ty) = &instr_ty else {
|
||||
panic!("GetStructElemPtr on non-pointer! ({:?})", &instr_ty)
|
||||
};
|
||||
match *inner_ty.clone() {
|
||||
Type::Array(elem_ty, _) => Ok(Type::Ptr(Box::new(*elem_ty.clone()))),
|
||||
_ => Ok(instr_ty),
|
||||
}
|
||||
}
|
||||
GetStructElemPtr(instr, idx) => {
|
||||
let instr_ty = instr.get_type(builder)?;
|
||||
let Type::Ptr(inner_ty) = instr_ty else {
|
||||
panic!("GetStructElemPtr on non-pointer! ({:?})", &instr_ty)
|
||||
};
|
||||
let Type::CustomType(ty_value) = *inner_ty else {
|
||||
panic!("GetStructElemPtr on non-struct! ({:?})", &inner_ty)
|
||||
};
|
||||
let field_ty = match builder.type_data(&ty_value).kind {
|
||||
CustomTypeKind::NamedStruct(NamedStruct(_, fields)) => {
|
||||
fields.get_unchecked(*idx as usize).clone()
|
||||
}
|
||||
};
|
||||
Ok(Type::Ptr(Box::new(field_ty)))
|
||||
}
|
||||
ExtractValue(instr, idx) => {
|
||||
let instr_ty = instr.get_type(builder)?;
|
||||
Ok(match instr_ty {
|
||||
Type::CustomType(struct_ty) => {
|
||||
let data = builder.type_data(&struct_ty);
|
||||
match data.kind {
|
||||
CustomTypeKind::NamedStruct(named_struct) => {
|
||||
named_struct.1.get(*idx as usize).unwrap().clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
Type::Array(elem_ty, _) => *elem_ty.clone(),
|
||||
_ => return Err(ErrorKind::NotExtractable(*instr, instr_ty)),
|
||||
})
|
||||
}
|
||||
Trunc(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
ZExt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
SExt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
FPTrunc(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
FPExt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
FPToUI(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
FPToSI(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
UIToFP(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
SIToFP(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
PtrToInt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
IntToPtr(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||
BitCast(_, ty) => Ok(ty.clone()),
|
||||
ShiftRightLogical(lhs, _) => lhs.get_type(builder),
|
||||
ShiftRightArithmetic(lhs, _) => lhs.get_type(builder),
|
||||
ShiftLeft(lhs, _) => lhs.get_type(builder),
|
||||
GetGlobal(global_value) => {
|
||||
let constant = builder.get_global_initializer(*global_value);
|
||||
let kind = builder.get_const_kind(constant);
|
||||
Ok(kind.get_type())
|
||||
}
|
||||
IsNull(_) => Ok(Type::Bool),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cast_to(&self, builder: &Builder, ty: &Type) -> CompileResult<Instr> {
|
||||
let own_type = self.get_type(builder)?;
|
||||
own_type
|
||||
.cast_instruction(*self, &ty)
|
||||
.ok_or(ErrorKind::ImpossibleCast(own_type, ty.clone()))
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,399 +0,0 @@
|
||||
use std::{
|
||||
cell::{Ref, RefCell, RefMut},
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use crate::builder::InstructionValue;
|
||||
|
||||
/// Represents 1. the compilation context, 2. subprogram or 3. a lexical scope
|
||||
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||
pub struct DebugScopeValue(pub Vec<usize>);
|
||||
|
||||
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||
pub struct DebugLocationValue(pub DebugScopeValue, pub usize);
|
||||
|
||||
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
||||
pub struct DebugTypeValue(pub usize);
|
||||
|
||||
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
||||
pub struct DebugMetadataValue(pub usize);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DebugFileData {
|
||||
pub name: String,
|
||||
pub directory: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct DebugScopeHolder {
|
||||
pub(crate) value: DebugScopeValue,
|
||||
pub(crate) data: DebugScopeData,
|
||||
pub(crate) inner_scopes: Vec<DebugScopeHolder>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DebugMetadataHolder {
|
||||
pub(crate) location: DebugLocation,
|
||||
pub(crate) value: DebugMetadataValue,
|
||||
pub(crate) data: DebugMetadata,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DebugTypeHolder {
|
||||
pub(crate) value: DebugTypeValue,
|
||||
pub(crate) data: DebugTypeData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct DebugLocationHolder {
|
||||
pub(crate) scope: DebugScopeValue,
|
||||
pub(crate) value: DebugLocationValue,
|
||||
pub(crate) location: DebugLocation,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DebugInformation {
|
||||
pub file: DebugFileData,
|
||||
scope: Rc<RefCell<DebugScopeHolder>>,
|
||||
locations: Rc<RefCell<Vec<DebugLocationHolder>>>,
|
||||
metadata: Rc<RefCell<Vec<DebugMetadataHolder>>>,
|
||||
types: Rc<RefCell<Vec<DebugTypeHolder>>>,
|
||||
}
|
||||
|
||||
impl DebugInformation {
|
||||
pub fn from_file(file: DebugFileData) -> (DebugInformation, DebugScopeValue) {
|
||||
let scope_value = DebugScopeValue(Vec::new());
|
||||
(
|
||||
DebugInformation {
|
||||
file,
|
||||
scope: Rc::new(RefCell::new(DebugScopeHolder {
|
||||
value: scope_value.clone(),
|
||||
inner_scopes: Vec::new(),
|
||||
data: DebugScopeData {
|
||||
parent: None,
|
||||
kind: DebugScopeKind::CodegenContext,
|
||||
},
|
||||
})),
|
||||
locations: Rc::new(RefCell::new(Vec::new())),
|
||||
metadata: Rc::new(RefCell::new(Vec::new())),
|
||||
types: Rc::new(RefCell::new(Vec::new())),
|
||||
},
|
||||
scope_value,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn location(&self, scope_value: &DebugScopeValue, location: DebugLocation) -> DebugLocationValue {
|
||||
let value = DebugLocationValue(scope_value.clone(), self.locations.borrow().len());
|
||||
let location = DebugLocationHolder {
|
||||
scope: scope_value.clone(),
|
||||
value: value.clone(),
|
||||
location,
|
||||
};
|
||||
self.locations.borrow_mut().push(location);
|
||||
value
|
||||
}
|
||||
|
||||
pub fn debug_type(&self, kind: DebugTypeData) -> DebugTypeValue {
|
||||
let mut types = self.types.borrow_mut();
|
||||
let value = DebugTypeValue(types.len());
|
||||
types.push(DebugTypeHolder {
|
||||
value: value.clone(),
|
||||
data: kind,
|
||||
});
|
||||
value
|
||||
}
|
||||
|
||||
pub fn metadata(&self, location: &DebugLocation, kind: DebugMetadata) -> DebugMetadataValue {
|
||||
let mut metadata = self.metadata.borrow_mut();
|
||||
let value = DebugMetadataValue(metadata.len());
|
||||
metadata.push(DebugMetadataHolder {
|
||||
location: location.clone(),
|
||||
value: value.clone(),
|
||||
data: kind,
|
||||
});
|
||||
value
|
||||
}
|
||||
|
||||
pub fn subprogram(&self, parent: DebugScopeValue, kind: DebugSubprogramData) -> DebugScopeValue {
|
||||
unsafe {
|
||||
let mut outer_scope = RefMut::map(self.scope.borrow_mut(), |mut v| {
|
||||
for i in &parent.0 {
|
||||
v = v.inner_scopes.get_unchecked_mut(*i);
|
||||
}
|
||||
v
|
||||
});
|
||||
|
||||
let mut arr = parent.0.clone();
|
||||
arr.push(outer_scope.inner_scopes.len());
|
||||
let value = DebugScopeValue(arr);
|
||||
|
||||
outer_scope.inner_scopes.push(DebugScopeHolder {
|
||||
value: value.clone(),
|
||||
inner_scopes: Vec::new(),
|
||||
data: DebugScopeData {
|
||||
parent: Some(parent.clone()),
|
||||
kind: DebugScopeKind::Subprogram(kind),
|
||||
},
|
||||
});
|
||||
value
|
||||
}
|
||||
}
|
||||
pub fn lexical_scope(&self, parent: &DebugScopeValue, data: DebugLexicalScope) -> DebugScopeValue {
|
||||
unsafe {
|
||||
let mut outer_scope = RefMut::map(self.scope.borrow_mut(), |mut v| {
|
||||
for i in &parent.0 {
|
||||
v = v.inner_scopes.get_unchecked_mut(*i);
|
||||
}
|
||||
v
|
||||
});
|
||||
|
||||
let mut arr = parent.0.clone();
|
||||
arr.push(outer_scope.inner_scopes.len());
|
||||
let value = DebugScopeValue(arr);
|
||||
|
||||
outer_scope.inner_scopes.push(DebugScopeHolder {
|
||||
value: value.clone(),
|
||||
inner_scopes: Vec::new(),
|
||||
data: DebugScopeData {
|
||||
parent: Some(parent.clone()),
|
||||
kind: DebugScopeKind::LexicalScope(data),
|
||||
},
|
||||
});
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_metadata(&self, value: DebugMetadataValue) -> DebugMetadata {
|
||||
unsafe { self.metadata.borrow().get_unchecked(value.0).data.clone() }
|
||||
}
|
||||
|
||||
pub fn get_metadata_location(&self, value: DebugMetadataValue) -> DebugLocation {
|
||||
unsafe { self.metadata.borrow().get_unchecked(value.0).location.clone() }
|
||||
}
|
||||
|
||||
pub fn get_scope_data(&self, value: &DebugScopeValue) -> Option<DebugScopeData> {
|
||||
let scope = Ref::filter_map(self.scope.borrow(), |v: &DebugScopeHolder| {
|
||||
let mut opt = Some(v);
|
||||
for i in &value.0 {
|
||||
if let Some(inner) = opt {
|
||||
opt = inner.inner_scopes.get(*i);
|
||||
}
|
||||
}
|
||||
opt
|
||||
});
|
||||
|
||||
if let Ok(scope) = scope {
|
||||
Some(scope.data.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_type_data(&self, value: DebugTypeValue) -> DebugTypeData {
|
||||
unsafe { self.types.borrow().get_unchecked(value.0).data.clone() }
|
||||
}
|
||||
|
||||
pub fn get_location(&self, value: &DebugLocationValue) -> DebugLocation {
|
||||
unsafe { self.locations.borrow().get_unchecked(value.1).location.clone() }
|
||||
}
|
||||
|
||||
pub fn get_metadatas(&self) -> Rc<RefCell<Vec<DebugMetadataHolder>>> {
|
||||
self.metadata.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn get_scope(&self) -> Rc<RefCell<DebugScopeHolder>> {
|
||||
self.scope.clone()
|
||||
}
|
||||
|
||||
pub fn get_types(&self) -> Rc<RefCell<Vec<DebugTypeHolder>>> {
|
||||
self.types.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn get_locations(&self) -> Rc<RefCell<Vec<DebugLocationHolder>>> {
|
||||
self.locations.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DebugLocation {
|
||||
pub scope: DebugScopeValue,
|
||||
pub pos: DebugPosition,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct DebugPosition {
|
||||
pub line: u32,
|
||||
pub column: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum DebugMetadata {
|
||||
ParamVar(DebugParamVariable),
|
||||
LocalVar(DebugLocalVariable),
|
||||
VarAssignment,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DebugParamVariable {
|
||||
pub name: String,
|
||||
/// the index (starting from 1) of this variable in the subprogram
|
||||
/// parameters. arg_idx should not conflict with other parameters of the
|
||||
/// same subprogram.
|
||||
pub arg_idx: u32,
|
||||
pub ty: DebugTypeValue,
|
||||
/// If this variable will be referenced from its containing subprogram, and
|
||||
/// will survive some optimizations.
|
||||
pub always_preserve: bool,
|
||||
pub flags: DwarfFlags,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DebugLocalVariable {
|
||||
pub name: String,
|
||||
pub ty: DebugTypeValue,
|
||||
pub always_preserve: bool,
|
||||
pub flags: DwarfFlags,
|
||||
}
|
||||
|
||||
impl Default for DebugSubprogramOptionals {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
scope_line: 0,
|
||||
is_local: false,
|
||||
is_definition: true,
|
||||
is_optimized: false,
|
||||
flags: DwarfFlags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DwarfFlags;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum DebugTypeData {
|
||||
Basic(DebugBasicType),
|
||||
Subprogram(DebugSubprogramType),
|
||||
Pointer(DebugPointerType),
|
||||
Array(DebugArrayType),
|
||||
Struct(DebugStructType),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DebugBasicType {
|
||||
pub name: String,
|
||||
/// Size of the type.
|
||||
pub size_bits: u64,
|
||||
/// DWARF encoding code, e.g., dwarf::DW_ATE_float.
|
||||
pub encoding: DwarfEncoding,
|
||||
/// Optional DWARF attributes, e.g., DW_AT_endianity.
|
||||
pub flags: DwarfFlags,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DebugArrayType {
|
||||
pub size_bits: u64,
|
||||
pub align_bits: u32,
|
||||
pub element_type: DebugTypeValue,
|
||||
pub length: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DebugPointerType {
|
||||
pub name: String,
|
||||
pub pointee: DebugTypeValue,
|
||||
pub size_bits: u64,
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct DebugStructType {
|
||||
pub name: String,
|
||||
pub scope: DebugScopeValue,
|
||||
pub pos: Option<DebugPosition>,
|
||||
pub size_bits: u64,
|
||||
pub flags: DwarfFlags,
|
||||
pub fields: Vec<DebugFieldType>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DebugFieldType {
|
||||
pub name: String,
|
||||
pub scope: DebugScopeValue,
|
||||
pub pos: Option<DebugPosition>,
|
||||
pub size_bits: u64,
|
||||
pub offset: u64,
|
||||
pub flags: DwarfFlags,
|
||||
pub ty: DebugTypeValue,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DebugSubprogramType {
|
||||
pub parameters: Vec<DebugTypeValue>,
|
||||
pub flags: DwarfFlags,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum DwarfEncoding {
|
||||
Address = 1,
|
||||
Boolean = 2,
|
||||
Float = 4,
|
||||
Signed = 5,
|
||||
SignedChar = 6,
|
||||
Unsigned = 7,
|
||||
UnsignedChar = 8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DebugScopeData {
|
||||
pub parent: Option<DebugScopeValue>,
|
||||
pub kind: DebugScopeKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum DebugScopeKind {
|
||||
CodegenContext,
|
||||
LexicalScope(DebugLexicalScope),
|
||||
Subprogram(DebugSubprogramData),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DebugLexicalScope {
|
||||
pub location: DebugLocation,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DebugSubprogramData {
|
||||
/// Function name.
|
||||
pub name: String,
|
||||
pub outer_scope: DebugScopeValue,
|
||||
/// Used for line number.
|
||||
pub location: DebugLocation,
|
||||
/// Function type.
|
||||
pub ty: DebugTypeValue,
|
||||
pub opts: DebugSubprogramOptionals,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DebugSubprogramOptionals {
|
||||
/// Set to the beginning of the scope this starts
|
||||
pub scope_line: u32,
|
||||
pub is_local: bool,
|
||||
pub is_definition: bool,
|
||||
pub is_optimized: bool,
|
||||
/// These flags are used to emit dwarf attributes. e.g. is this function
|
||||
/// prototyped or not.
|
||||
pub flags: DwarfFlags,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InstructionDebugRecordData {
|
||||
pub scope: DebugScopeValue,
|
||||
pub variable: DebugMetadataValue,
|
||||
pub location: DebugLocation,
|
||||
pub kind: DebugRecordKind,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum DebugRecordKind {
|
||||
Declare(InstructionValue),
|
||||
Value(InstructionValue),
|
||||
}
|
@ -1,595 +0,0 @@
|
||||
//! Debug implementations for relevant types
|
||||
|
||||
use std::{
|
||||
fmt::{Debug, Display, Write},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
CmpPredicate, Context, Instr, InstructionData, TerminatorKind,
|
||||
builder::*,
|
||||
debug_information::{
|
||||
DebugArrayType, DebugBasicType, DebugFieldType, DebugInformation, DebugLocalVariable, DebugLocation,
|
||||
DebugLocationValue, DebugMetadata, DebugMetadataValue, DebugParamVariable, DebugPointerType, DebugPosition,
|
||||
DebugRecordKind, DebugScopeValue, DebugStructType, DebugSubprogramType, DebugTypeData, DebugTypeHolder,
|
||||
DebugTypeValue,
|
||||
},
|
||||
pad_adapter::PadAdapter,
|
||||
};
|
||||
|
||||
impl Display for Context {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Display::fmt(&self.builder, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Builder {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "Producer: {}", self.producer)?;
|
||||
for module in self.modules.borrow().iter() {
|
||||
if module.data.is_main {
|
||||
write!(f, "main ")?;
|
||||
}
|
||||
writeln!(f, "{} ({:?}) {{", module.data.name, module.value)?;
|
||||
for function in &module.functions {
|
||||
let mut state = Default::default();
|
||||
let mut inner = PadAdapter::wrap(f, &mut state);
|
||||
function.builder_fmt(&mut inner, self, &module.debug_information)?;
|
||||
}
|
||||
writeln!(f, "}}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FunctionHolder {
|
||||
fn builder_fmt(
|
||||
&self,
|
||||
f: &mut impl std::fmt::Write,
|
||||
builder: &Builder,
|
||||
debug: &Option<DebugInformation>,
|
||||
) -> std::fmt::Result {
|
||||
if self.data.flags.is_imported {
|
||||
write!(f, "imported ")?;
|
||||
}
|
||||
if self.data.flags.is_extern {
|
||||
write!(f, "extern ")?;
|
||||
}
|
||||
if self.data.flags.is_pub {
|
||||
write!(f, "pub ")?;
|
||||
}
|
||||
if self.data.flags.is_main {
|
||||
write!(f, "main ")?;
|
||||
}
|
||||
let params = self
|
||||
.data
|
||||
.params
|
||||
.iter()
|
||||
.map(|p| format!("{:?}", p))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
write!(f, "fn {}({}) -> {:?} ", self.data.name, params, self.data.ret)?;
|
||||
|
||||
writeln!(f, "{{")?;
|
||||
let mut state = Default::default();
|
||||
let mut inner = PadAdapter::wrap(f, &mut state);
|
||||
writeln!(inner, "(Value = {:?}) ", self.value)?;
|
||||
if let Some(debug) = &self.debug_info {
|
||||
writeln!(inner, "(Debug = {:?})", debug)?;
|
||||
}
|
||||
|
||||
for block in &self.blocks {
|
||||
let mut state = Default::default();
|
||||
let mut inner = PadAdapter::wrap(&mut inner, &mut state);
|
||||
block.builder_fmt(&mut inner, builder, debug)?;
|
||||
}
|
||||
|
||||
writeln!(f, "}}")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockHolder {
|
||||
fn builder_fmt(
|
||||
&self,
|
||||
f: &mut impl std::fmt::Write,
|
||||
builder: &Builder,
|
||||
debug: &Option<DebugInformation>,
|
||||
) -> std::fmt::Result {
|
||||
if self.data.deleted {
|
||||
write!(f, "deleted ")?;
|
||||
}
|
||||
writeln!(f, "{} ({:?}):", self.data.name, self.value)?;
|
||||
|
||||
let mut state = Default::default();
|
||||
let mut inner = PadAdapter::wrap(f, &mut state);
|
||||
|
||||
for instr in &self.instructions {
|
||||
instr.builder_fmt(&mut inner, builder, debug)?;
|
||||
}
|
||||
|
||||
if let Some(terminator) = &self.data.terminator {
|
||||
terminator.builder_fmt(&mut inner, builder, debug)?;
|
||||
}
|
||||
if let Some(location) = &self.data.terminator_location {
|
||||
writeln!(inner, " ^ (At {}) ", debug.as_ref().unwrap().get_location(location))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionHolder {
|
||||
fn builder_fmt(
|
||||
&self,
|
||||
f: &mut impl std::fmt::Write,
|
||||
_builder: &Builder,
|
||||
debug: &Option<DebugInformation>,
|
||||
) -> std::fmt::Result {
|
||||
if let Some(record) = &self.record {
|
||||
let kind = match record.kind {
|
||||
DebugRecordKind::Declare(instruction_value) => {
|
||||
format!("= {:?} (Assign)", instruction_value)
|
||||
}
|
||||
DebugRecordKind::Value(instruction_value) => {
|
||||
format!("= {:?} (Value)", instruction_value)
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(debug) = debug {
|
||||
writeln!(f, " (Debug {} {})", record.variable.hr(debug), kind)?;
|
||||
}
|
||||
}
|
||||
writeln!(f, "{:?} ({}) = {:?} ", self.value, self.name, self.data.kind)?;
|
||||
if let Some(debug) = debug {
|
||||
if let Some(location) = &self.data.location {
|
||||
writeln!(f, " ^ (At {}) ", debug.get_location(location))?;
|
||||
}
|
||||
if let Some(meta) = self.data.meta {
|
||||
writeln!(f, " ^ (Meta {}) ", meta.hr(debug))?;
|
||||
}
|
||||
}
|
||||
writeln!(f)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl TerminatorKind {
|
||||
fn builder_fmt(
|
||||
&self,
|
||||
f: &mut impl std::fmt::Write,
|
||||
_builder: &Builder,
|
||||
_debug: &Option<DebugInformation>,
|
||||
) -> std::fmt::Result {
|
||||
match self {
|
||||
TerminatorKind::Ret(instr) => writeln!(f, "ret {:?}", instr),
|
||||
TerminatorKind::RetVoid => writeln!(f, "ret void"),
|
||||
TerminatorKind::Br(block) => writeln!(f, "br {:?}", block),
|
||||
TerminatorKind::CondBr(instr, lhs, rhs) => {
|
||||
writeln!(f, "condbr {:?}, {:?} or {:?}", instr, lhs, rhs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DebugMetadataValue {
|
||||
fn hr(&self, debug: &DebugInformation) -> String {
|
||||
let kind = match debug.get_metadata(*self) {
|
||||
DebugMetadata::ParamVar(DebugParamVariable { name, arg_idx, ty, .. }) => {
|
||||
format!("param {} (idx {}) (type {:?}) ", name, arg_idx, ty)
|
||||
}
|
||||
DebugMetadata::LocalVar(DebugLocalVariable { name, ty, .. }) => {
|
||||
format!("var {} (type {:?}) ", name, ty)
|
||||
}
|
||||
DebugMetadata::VarAssignment => todo!(),
|
||||
};
|
||||
format!("{} at {}", kind, debug.get_metadata_location(*self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DebugLocation {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?} on scope {:?}", self.pos, self.scope)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DebugPosition {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "line {}, col {}", self.line, self.column)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Builder {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_list().entries(self.get_modules().borrow().iter());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PrintableModule<'ctx> {
|
||||
pub phantom: PhantomData<&'ctx ()>,
|
||||
pub module: ModuleHolder,
|
||||
}
|
||||
|
||||
impl<'ctx> Debug for PrintableModule<'ctx> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Debug::fmt(&self.module, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ModuleHolder {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple(&format!("{}({:#?}) ", self.data.name, self.value))
|
||||
.field(&self.functions)
|
||||
// .field(&self.debug_information)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for FunctionHolder {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple(&format!(
|
||||
"{}({:?}) -> {:?} ",
|
||||
self.data.name, self.data.params, self.data.ret
|
||||
))
|
||||
.field(&self.blocks)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for BlockHolder {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let deleted = if self.data.deleted { " (deleted)" } else { "" };
|
||||
f.debug_tuple(&format!("{}[{:?}]{} ", &self.data.name, &self.value, deleted))
|
||||
.field(&self.instructions)
|
||||
.field(&self.data.terminator)
|
||||
.field(&self.data.terminator_location)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for InstructionHolder {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.value.fmt(f)?;
|
||||
write!(f, " ({})", self.name)?;
|
||||
f.write_str(" = ")?;
|
||||
self.data.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for InstructionData {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.kind.fmt(f)?;
|
||||
if let Some(location) = &self.location {
|
||||
write!(f, " ({:?})", location)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ModuleValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "M[{:0>2}]", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for FunctionValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "F[{:0>2}-{:0>2}]", &self.0.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for BlockValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "B[{:0>2}-{:0>2}-{:0>2}]", &self.0.0.0, &self.0.1, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for InstructionValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "%{}.{}.{}.{}", self.0.0.0.0, self.0.0.1, self.0.1, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
// impl Debug for InstructionValue {
|
||||
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// write!(
|
||||
// f,
|
||||
// "I[{:0>2}-{:0>2}-{:0>2}-{:0>2}]",
|
||||
// &self.0.0.0.0, &self.0.0.1, &self.0.1, self.1
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Debug for TypeValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Ty[{:0>2}-{:0>2}]", &self.0.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Instr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Instr::Param(nth) => fmt_call(f, &"Param", &nth),
|
||||
Instr::Constant(c) => c.fmt(f),
|
||||
Instr::Add(lhs, rhs) => fmt_binop(f, lhs, &"+", rhs),
|
||||
Instr::FAdd(lhs, rhs) => fmt_binop(f, lhs, &"+", rhs),
|
||||
Instr::Sub(lhs, rhs) => fmt_binop(f, lhs, &"-", rhs),
|
||||
Instr::FSub(lhs, rhs) => fmt_binop(f, lhs, &"-", rhs),
|
||||
Instr::Mul(lhs, rhs) => fmt_binop(f, lhs, &"*", rhs),
|
||||
Instr::FMul(lhs, rhs) => fmt_binop(f, lhs, &"*", rhs),
|
||||
Instr::UDiv(lhs, rhs) => fmt_binop(f, lhs, &"/", rhs),
|
||||
Instr::SDiv(lhs, rhs) => fmt_binop(f, lhs, &"/", rhs),
|
||||
Instr::FDiv(lhs, rhs) => fmt_binop(f, lhs, &"/", rhs),
|
||||
Instr::URem(lhs, rhs) => fmt_binop(f, lhs, &"%", rhs),
|
||||
Instr::SRem(lhs, rhs) => fmt_binop(f, lhs, &"%", rhs),
|
||||
Instr::FRem(lhs, rhs) => fmt_binop(f, lhs, &"%", rhs),
|
||||
Instr::And(lhs, rhs) => fmt_binop(f, lhs, &"&&", rhs),
|
||||
Instr::Phi(val) => fmt_call(f, &"Phi", &val),
|
||||
Instr::ICmp(cmp, lhs, rhs) => fmt_binop(f, lhs, cmp, rhs),
|
||||
Instr::FCmp(cmp, lhs, rhs) => fmt_binop(f, lhs, cmp, rhs),
|
||||
Instr::FunctionCall(fun, params) => fmt_call(f, fun, params),
|
||||
Instr::Alloca(ty) => write!(f, "alloca<{:?}>", ty),
|
||||
Instr::Load(val, ty) => write!(f, "load<{:?}>({:?})", ty, val),
|
||||
Instr::Store(ptr, val) => write!(f, "store({:?} = {:?})", ptr, val),
|
||||
Instr::ArrayAlloca(ty, instruction_value) => {
|
||||
write!(f, "array_alloca<{:?}>({:?})", ty, instruction_value)
|
||||
}
|
||||
Instr::GetElemPtr(instruction_value, items) => fmt_index(
|
||||
f,
|
||||
instruction_value,
|
||||
&items
|
||||
.iter()
|
||||
.map(|expr| format!("{:?}", expr))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
),
|
||||
Instr::GetStructElemPtr(instruction_value, index) => {
|
||||
write!(f, "GEP(")?;
|
||||
fmt_index(f, instruction_value, &index.to_string())?;
|
||||
write!(f, ")")
|
||||
}
|
||||
Instr::ExtractValue(instruction_value, index) => fmt_index(f, instruction_value, &index.to_string()),
|
||||
Instr::Trunc(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::ZExt(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::SExt(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::FPTrunc(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::FPExt(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::FPToUI(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::FPToSI(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::UIToFP(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::SIToFP(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::PtrToInt(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::IntToPtr(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::BitCast(instr_val, ty) => {
|
||||
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
|
||||
}
|
||||
Instr::Or(lhs, rhs) => fmt_binop(f, lhs, &"||", rhs),
|
||||
Instr::XOr(lhs, rhs) => fmt_binop(f, lhs, &"^", rhs),
|
||||
Instr::ShiftRightLogical(lhs, rhs) => fmt_binop(f, lhs, &">>l", rhs),
|
||||
Instr::ShiftRightArithmetic(lhs, rhs) => fmt_binop(f, lhs, &">>a", rhs),
|
||||
Instr::ShiftLeft(lhs, rhs) => fmt_binop(f, lhs, &"<<", rhs),
|
||||
Instr::GetGlobal(global_value) => write!(f, "global {:?}", global_value),
|
||||
Instr::IsNull(_) => write!(f, "is_null"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_binop(
|
||||
f: &mut std::fmt::Formatter<'_>,
|
||||
lhs: &impl std::fmt::Debug,
|
||||
op: &impl std::fmt::Debug,
|
||||
rhs: &impl std::fmt::Debug,
|
||||
) -> std::fmt::Result {
|
||||
lhs.fmt(f)?;
|
||||
f.write_char(' ')?;
|
||||
op.fmt(f)?;
|
||||
f.write_char(' ')?;
|
||||
rhs.fmt(f)
|
||||
}
|
||||
|
||||
fn fmt_call(
|
||||
f: &mut std::fmt::Formatter<'_>,
|
||||
fun: &impl std::fmt::Debug,
|
||||
params: &impl std::fmt::Debug,
|
||||
) -> std::fmt::Result {
|
||||
fun.fmt(f)?;
|
||||
f.write_char('(')?;
|
||||
params.fmt(f)?;
|
||||
f.write_char(')')
|
||||
}
|
||||
|
||||
fn fmt_index(
|
||||
f: &mut std::fmt::Formatter<'_>,
|
||||
fun: &impl std::fmt::Debug,
|
||||
params: &impl std::fmt::Debug,
|
||||
) -> std::fmt::Result {
|
||||
fun.fmt(f)?;
|
||||
f.write_char('[')?;
|
||||
params.fmt(f)?;
|
||||
f.write_char(']')
|
||||
}
|
||||
|
||||
impl Debug for CmpPredicate {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::LT => write!(f, "<"),
|
||||
Self::GT => write!(f, ">"),
|
||||
Self::LE => write!(f, "<="),
|
||||
Self::GE => write!(f, ">="),
|
||||
Self::EQ => write!(f, "=="),
|
||||
Self::NE => write!(f, "!="),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for TerminatorKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Ret(val) => {
|
||||
write!(f, "Ret ")?;
|
||||
val.fmt(f)
|
||||
}
|
||||
Self::RetVoid => write!(f, "Void Ret"),
|
||||
Self::Br(val) => {
|
||||
write!(f, "Br ")?;
|
||||
val.fmt(f)
|
||||
}
|
||||
Self::CondBr(cond, b1, b2) => {
|
||||
write!(f, "CondBr ")?;
|
||||
cond.fmt(f)?;
|
||||
write!(f, " ? ")?;
|
||||
b1.fmt(f)?;
|
||||
write!(f, " : ")?;
|
||||
b2.fmt(f)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugTypeHolder {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple(&format!("DebugTypeHolder {:?}", self.value))
|
||||
.field(&self.data)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugTypeData {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
DebugTypeData::Basic(ty) => Debug::fmt(ty, f),
|
||||
DebugTypeData::Subprogram(ty) => Debug::fmt(ty, f),
|
||||
DebugTypeData::Pointer(ty) => Debug::fmt(ty, f),
|
||||
DebugTypeData::Array(ty) => Debug::fmt(ty, f),
|
||||
DebugTypeData::Struct(ty) => Debug::fmt(ty, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugBasicType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("BasicType")
|
||||
.field(&self.name)
|
||||
.field(&self.size_bits)
|
||||
.field(&self.encoding)
|
||||
.field(&self.flags)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugStructType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Struct")
|
||||
.field("name", &self.name)
|
||||
.field("scope", &self.scope)
|
||||
.field("pos", &self.pos)
|
||||
.field("size_bit", &self.size_bits)
|
||||
.field("flags", &self.flags)
|
||||
.field("elements", &self.fields)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugFieldType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct(&format!("Field({})", self.name))
|
||||
.field("scope", &self.scope)
|
||||
.field("pos", &self.pos)
|
||||
.field("size_bits", &self.size_bits)
|
||||
.field("offset", &self.offset)
|
||||
.field("flags", &self.flags)
|
||||
.field("ty", &self.ty)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugSubprogramType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("Subprogram")
|
||||
.field(&self.parameters)
|
||||
.field(&self.flags)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugPointerType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple(&format!("Pointer<{:?}>({})", self.pointee, self.name))
|
||||
.field(&self.size_bits)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugArrayType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct(&format!("Array<{:?}>[{}]", self.element_type, self.length))
|
||||
.field("size_bits", &self.size_bits)
|
||||
.field("align_bits", &self.align_bits)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugMetadataValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Meta[{}]", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugScopeValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Scope[{}]",
|
||||
self.0.iter().map(|v| v.to_string()).collect::<Vec<_>>().join(", ")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugTypeValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Type[{}]", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugLocationValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Value[{:?}][{}]", self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugLocation {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?} on scope {:?}", self.pos, self.scope)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebugPosition {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "ln {}, col {}", self.line, self.column)
|
||||
}
|
||||
}
|
@ -1,263 +0,0 @@
|
||||
use crate::{CompileResult, Type, TypeCategory, builder::Builder};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum LLVMIntrinsic {
|
||||
Abs(Type),
|
||||
Max(Type),
|
||||
Min(Type),
|
||||
Memcpy(Type),
|
||||
Sqrt(Type),
|
||||
PowI(Type, Type),
|
||||
Pow(Type),
|
||||
Sin(Type),
|
||||
Cos(Type),
|
||||
Tan(Type),
|
||||
ASin(Type),
|
||||
ACos(Type),
|
||||
ATan(Type),
|
||||
ATan2(Type),
|
||||
SinH(Type),
|
||||
CosH(Type),
|
||||
TanH(Type),
|
||||
Log(Type),
|
||||
Log2(Type),
|
||||
Log10(Type),
|
||||
Copysign(Type),
|
||||
Floor(Type),
|
||||
Ceil(Type),
|
||||
Trunc(Type),
|
||||
RoundEven(Type),
|
||||
Round(Type),
|
||||
}
|
||||
|
||||
impl LLVMIntrinsic {
|
||||
pub(crate) fn signature(&self, builder: &Builder) -> CompileResult<(String, Vec<Type>, Type)> {
|
||||
match self {
|
||||
LLVMIntrinsic::Max(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::SignedInteger => format!("llvm.smax.{}", ty.llvm_ty_str(builder)),
|
||||
TypeCategory::UnsignedInteger => format!("llvm.umax.{}", ty.llvm_ty_str(builder)),
|
||||
TypeCategory::Real => format!("llvm.maximum.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone(), ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::Min(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::SignedInteger => format!("llvm.smin.{}", ty.llvm_ty_str(builder)),
|
||||
TypeCategory::UnsignedInteger => format!("llvm.umin.{}", ty.llvm_ty_str(builder)),
|
||||
TypeCategory::Real => format!("llvm.minimum.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone(), ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::Abs(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::SignedInteger => format!("llvm.abs.{}", ty.llvm_ty_str(builder)),
|
||||
TypeCategory::UnsignedInteger => format!("llvm.abs.{}", ty.llvm_ty_str(builder)),
|
||||
TypeCategory::Real => format!("llvm.fabs.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone(), Type::Bool], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::Memcpy(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Ptr => String::from("llvm.memcpy"),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone(), ty.clone(), Type::U64, Type::Bool], Type::Void))
|
||||
}
|
||||
LLVMIntrinsic::Sqrt(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Real => format!("llvm.sqrt.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::PowI(ty1, ty2) => {
|
||||
let name = match (ty1.category(), ty2.category()) {
|
||||
(TypeCategory::Real, TypeCategory::SignedInteger) => {
|
||||
format!("llvm.powi.{}.{}", ty1.llvm_ty_str(builder), ty2.llvm_ty_str(builder))
|
||||
}
|
||||
(TypeCategory::Real, TypeCategory::UnsignedInteger) => {
|
||||
format!("llvm.powi.{}.{}", ty1.llvm_ty_str(builder), ty2.llvm_ty_str(builder))
|
||||
}
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty1.clone(), ty2.clone()], ty1.clone()))
|
||||
}
|
||||
LLVMIntrinsic::Pow(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Real => format!("llvm.pow.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone(), ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::Sin(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Real => format!("llvm.sin.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::Cos(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Real => format!("llvm.cos.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::Tan(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Real => format!("llvm.tan.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::ASin(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Real => format!("llvm.asin.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::ACos(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Real => format!("llvm.acos.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::ATan(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Real => format!("llvm.atan.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::ATan2(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Real => format!("llvm.atan2.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone(), ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::SinH(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Real => format!("llvm.sinh.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::CosH(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Real => format!("llvm.cosh.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::TanH(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Real => format!("llvm.tanh.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::Log(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Real => format!("llvm.log.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::Log2(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Real => format!("llvm.log2.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::Log10(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Real => format!("llvm.log10.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::Copysign(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Real => format!("llvm.copysign.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::Floor(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Real => format!("llvm.floor.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::Ceil(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Real => format!("llvm.ceil.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::Trunc(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Real => format!("llvm.trunc.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::RoundEven(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Real => format!("llvm.roundeven.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone()], ty.clone()))
|
||||
}
|
||||
LLVMIntrinsic::Round(ty) => {
|
||||
let name = match ty.category() {
|
||||
TypeCategory::Real => format!("llvm.rint.{}", ty.llvm_ty_str(builder)),
|
||||
_ => return Err(crate::ErrorKind::Null),
|
||||
};
|
||||
Ok((name, vec![ty.clone()], ty.clone()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Type {
|
||||
fn llvm_ty_str(&self, builder: &Builder) -> String {
|
||||
match self {
|
||||
Type::I8 => String::from("i8"),
|
||||
Type::I16 => String::from("u16"),
|
||||
Type::I32 => String::from("i32"),
|
||||
Type::I64 => String::from("i64"),
|
||||
Type::I128 => String::from("i128"),
|
||||
Type::U8 => String::from("i8"),
|
||||
Type::U16 => String::from("i16"),
|
||||
Type::U32 => String::from("i32"),
|
||||
Type::U64 => String::from("i64"),
|
||||
Type::U128 => String::from("i128"),
|
||||
Type::F16 => String::from("f16"),
|
||||
Type::F32B => String::from("f32b"),
|
||||
Type::F32 => String::from("f32"),
|
||||
Type::F64 => String::from("f64"),
|
||||
Type::F80 => String::from("x86_fp80"),
|
||||
Type::F128 => String::from("fp128"),
|
||||
Type::F128PPC => String::from("ppc_fp128"),
|
||||
Type::Bool => String::from("i1"),
|
||||
Type::Void => String::from("void"),
|
||||
Type::CustomType(type_value) => {
|
||||
let ty = unsafe { builder.type_data(type_value) };
|
||||
ty.name.clone()
|
||||
}
|
||||
Type::Array(ty, len) => format!("[{} x {}]", len, ty.llvm_ty_str(builder)),
|
||||
Type::Ptr(_) => String::from("ptr"),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,741 +0,0 @@
|
||||
//! Reid LLVM Lib is an ergonomic Rust'y API which is used to produce a
|
||||
//! Low-Level IR (LLIR) using [`Context`] and [`Builder`]. This Builder can then
|
||||
//! be used at the end to compile said LLIR into LLVM IR.
|
||||
|
||||
use std::{fmt::Debug, marker::PhantomData};
|
||||
|
||||
use builder::{BlockValue, Builder, FunctionValue, InstructionValue, ModuleValue, TypeValue};
|
||||
use debug_information::{DebugFileData, DebugInformation, DebugLocationValue, DebugMetadataValue};
|
||||
use fmt::PrintableModule;
|
||||
|
||||
use crate::{
|
||||
builder::{ConstantValue, GlobalValue},
|
||||
debug_information::DebugScopeValue,
|
||||
intrinsics::LLVMIntrinsic,
|
||||
};
|
||||
|
||||
pub mod builder;
|
||||
pub mod compile;
|
||||
pub mod debug_information;
|
||||
mod fmt;
|
||||
pub mod intrinsics;
|
||||
mod pad_adapter;
|
||||
mod util;
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, PartialEq, PartialOrd)]
|
||||
pub enum ErrorKind {
|
||||
#[error("NULL error, should never occur!")]
|
||||
Null,
|
||||
#[error("Types {0:?} and {1:?} incompatible")]
|
||||
TypesIncompatible(Type, Type),
|
||||
#[error("Phi list of values is empty")]
|
||||
EmptyPhiList,
|
||||
#[error("Type {1:?} of value {0:?} is not extractable")]
|
||||
NotExtractable(InstructionValue, Type),
|
||||
#[error("Type {0:?} is not castable to {1:?}")]
|
||||
ImpossibleCast(Type, Type),
|
||||
#[error("Block is already terminated")]
|
||||
BlockAlreadyTerminated,
|
||||
#[error("Block terminator already has a location")]
|
||||
BlockTerminatorLocated,
|
||||
#[error("Value {0:?} must be an integer type. Is {1:?}")]
|
||||
TypeNotInteger(InstructionValue, Type),
|
||||
#[error("Value {0:?} must be a {2:?} type. Is {1:?}")]
|
||||
TypeWrongCategory(InstructionValue, Type, TypeCategory),
|
||||
#[error("Value {0:?} must be comparable, was {1:?}")]
|
||||
TypeNotComparable(InstructionValue, Type),
|
||||
#[error("Got {0:?} parameters, expected {1:?}")]
|
||||
InvalidLenParams(usize, usize),
|
||||
#[error("Value {0:?} is not a pointer, is {1:?}")]
|
||||
NotPointer(InstructionValue, Type),
|
||||
#[error("Value {0:?} is not a struct, is {1:?}")]
|
||||
NotStruct(InstructionValue, Type),
|
||||
#[error("Struct {0:?} has no such field as {1:?}")]
|
||||
NoSuchField(Type, u32),
|
||||
#[error("Function {0:?} has no such parameter as {1:?}")]
|
||||
NoSuchParam(FunctionValue, usize),
|
||||
}
|
||||
|
||||
pub type CompileResult<T> = Result<T, ErrorKind>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Context {
|
||||
builder: Builder,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new<T: Into<String>>(producer: T) -> Context {
|
||||
Context {
|
||||
builder: Builder::new(producer.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn module<'ctx>(&'ctx self, name: &str, main: bool) -> Module<'ctx> {
|
||||
let value = self.builder.add_module(ModuleData {
|
||||
name: name.to_owned(),
|
||||
is_main: main,
|
||||
});
|
||||
Module {
|
||||
phantom: PhantomData,
|
||||
builder: self.builder.clone(),
|
||||
value,
|
||||
debug_info: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub struct ModuleData {
|
||||
name: String,
|
||||
is_main: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Module<'ctx> {
|
||||
phantom: PhantomData<&'ctx ()>,
|
||||
builder: Builder,
|
||||
value: ModuleValue,
|
||||
debug_info: Option<DebugInformation>,
|
||||
}
|
||||
|
||||
impl<'ctx> Module<'ctx> {
|
||||
pub fn function(
|
||||
&self,
|
||||
name: &str,
|
||||
linkage: Option<String>,
|
||||
ret: Type,
|
||||
params: Vec<Type>,
|
||||
flags: FunctionFlags,
|
||||
) -> Function<'ctx> {
|
||||
unsafe {
|
||||
Function {
|
||||
phantom: PhantomData,
|
||||
builder: self.builder.clone(),
|
||||
value: self.builder.add_function(
|
||||
&self.value,
|
||||
FunctionData {
|
||||
name: name.to_owned(),
|
||||
linkage_name: linkage,
|
||||
ret,
|
||||
params,
|
||||
flags,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intrinsic(&self, intrinsic: LLVMIntrinsic) -> CompileResult<FunctionValue> {
|
||||
unsafe {
|
||||
let (name, params, ret) = intrinsic.signature(&self.builder)?;
|
||||
Ok(self.builder.add_function(
|
||||
&self.value,
|
||||
FunctionData {
|
||||
name: name.to_owned(),
|
||||
linkage_name: Some(name.to_owned()),
|
||||
ret,
|
||||
params,
|
||||
flags: FunctionFlags {
|
||||
is_extern: true,
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn custom_type(&self, ty: CustomTypeKind) -> TypeValue {
|
||||
unsafe {
|
||||
let (name, kind) = match &ty {
|
||||
CustomTypeKind::NamedStruct(NamedStruct(name, _)) => (name.clone(), ty),
|
||||
};
|
||||
self.builder.add_type(&self.value, TypeData { name, kind })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(&self) -> ModuleValue {
|
||||
self.value
|
||||
}
|
||||
|
||||
pub fn as_printable(&self) -> PrintableModule<'ctx> {
|
||||
PrintableModule {
|
||||
phantom: PhantomData,
|
||||
module: self.builder.find_module(self.value),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_debug_info(&mut self, file: DebugFileData) -> (DebugInformation, DebugScopeValue) {
|
||||
let (debug_info, scope_value) = DebugInformation::from_file(file);
|
||||
self.debug_info = Some(debug_info.clone());
|
||||
(debug_info, scope_value)
|
||||
}
|
||||
|
||||
pub fn get_debug_info(&self) -> &Option<DebugInformation> {
|
||||
&self.debug_info
|
||||
}
|
||||
|
||||
pub fn add_constant(&self, constant: ConstValueKind) -> ConstantValue {
|
||||
unsafe { self.builder.build_constant(self.value, constant) }
|
||||
}
|
||||
|
||||
pub fn add_global<T: Into<String>>(&self, name: T, constant: ConstantValue) -> GlobalValue {
|
||||
unsafe { self.builder.add_global(self.value, name.into(), constant) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> Drop for Module<'ctx> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(debug_info) = self.debug_info.take() {
|
||||
self.builder.set_debug_information(&self.value, debug_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub struct FunctionData {
|
||||
name: String,
|
||||
linkage_name: Option<String>,
|
||||
ret: Type,
|
||||
params: Vec<Type>,
|
||||
flags: FunctionFlags,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash)]
|
||||
pub struct FunctionFlags {
|
||||
/// True in the destination module of the import, false in the source module.
|
||||
pub is_extern: bool,
|
||||
/// Whether this function is the main function of the module, that should be
|
||||
/// executed (and linked externally also).
|
||||
pub is_main: bool,
|
||||
/// Whether this function should be available externally always.
|
||||
pub is_pub: bool,
|
||||
/// If this function is an imported function (either in the source or
|
||||
/// destination module)
|
||||
pub is_imported: bool,
|
||||
/// Whether this function should add "alwaysinline"-attribute.
|
||||
pub inline: bool,
|
||||
}
|
||||
|
||||
impl Default for FunctionFlags {
|
||||
fn default() -> FunctionFlags {
|
||||
FunctionFlags {
|
||||
is_extern: false,
|
||||
is_main: false,
|
||||
is_pub: false,
|
||||
is_imported: false,
|
||||
inline: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Function<'ctx> {
|
||||
phantom: PhantomData<&'ctx ()>,
|
||||
builder: Builder,
|
||||
value: FunctionValue,
|
||||
}
|
||||
|
||||
impl<'ctx> Function<'ctx> {
|
||||
pub fn block(&self, name: &str) -> Block<'ctx> {
|
||||
unsafe {
|
||||
Block {
|
||||
phantom: PhantomData,
|
||||
builder: self.builder.clone(),
|
||||
value: self.builder.add_block(
|
||||
&self.value,
|
||||
BlockData {
|
||||
name: name.to_owned(),
|
||||
terminator: None,
|
||||
terminator_location: None,
|
||||
deleted: false,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_debug(&self, subprogram: DebugScopeValue) {
|
||||
unsafe {
|
||||
self.builder.set_debug_subprogram(&self.value, subprogram);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(&self) -> FunctionValue {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub struct BlockData {
|
||||
name: String,
|
||||
terminator: Option<TerminatorKind>,
|
||||
terminator_location: Option<DebugLocationValue>,
|
||||
deleted: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Block<'builder> {
|
||||
phantom: PhantomData<&'builder ()>,
|
||||
builder: Builder,
|
||||
value: BlockValue,
|
||||
}
|
||||
|
||||
impl Instr {
|
||||
pub fn default_name(&self) -> &str {
|
||||
match self {
|
||||
Instr::Param(_) => "param",
|
||||
Instr::Constant(_) => "const1",
|
||||
Instr::Add(..) => "add",
|
||||
Instr::FAdd(..) => "fadd",
|
||||
Instr::Sub(..) => "sub",
|
||||
Instr::FSub(..) => "fsub",
|
||||
Instr::Mul(..) => "mul",
|
||||
Instr::FMul(..) => "fmul",
|
||||
Instr::UDiv(..) => "udiv",
|
||||
Instr::SDiv(..) => "sdiv",
|
||||
Instr::FDiv(..) => "fdiv",
|
||||
Instr::URem(..) => "urem",
|
||||
Instr::SRem(..) => "srem",
|
||||
Instr::FRem(..) => "frem",
|
||||
Instr::And(..) => "and",
|
||||
Instr::Phi(_) => "phi",
|
||||
Instr::Alloca(_) => "alloca",
|
||||
Instr::Load(_, _) => "load",
|
||||
Instr::Store(..) => "store",
|
||||
Instr::ArrayAlloca(_, _) => "arrayalloca",
|
||||
Instr::GetElemPtr(..) => "getelemptr",
|
||||
Instr::GetStructElemPtr(..) => "getstructelemptr",
|
||||
Instr::ExtractValue(..) => "extractvalue",
|
||||
Instr::ICmp(..) => "icmp",
|
||||
Instr::FunctionCall(..) => "call",
|
||||
Instr::FCmp(_, _, _) => "fcmp",
|
||||
Instr::Trunc(_, _) => "trunc",
|
||||
Instr::ZExt(_, _) => "zext",
|
||||
Instr::SExt(_, _) => "sext",
|
||||
Instr::FPTrunc(_, _) => "fptrunc",
|
||||
Instr::FPExt(_, _) => "pfext",
|
||||
Instr::FPToUI(_, _) => "fptoui",
|
||||
Instr::FPToSI(_, _) => "fptosi",
|
||||
Instr::UIToFP(_, _) => "uitofp",
|
||||
Instr::SIToFP(_, _) => "sitofp",
|
||||
Instr::PtrToInt(_, _) => "ptrtoint",
|
||||
Instr::IntToPtr(_, _) => "inttoptr",
|
||||
Instr::BitCast(_, _) => "bitcast",
|
||||
Instr::Or(..) => "or",
|
||||
Instr::XOr(..) => "xor",
|
||||
Instr::ShiftRightLogical(..) => "lshr",
|
||||
Instr::ShiftRightArithmetic(..) => "ashr",
|
||||
Instr::ShiftLeft(..) => "shl",
|
||||
Instr::GetGlobal(..) => "global",
|
||||
Instr::IsNull(..) => "is_null",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'builder> Block<'builder> {
|
||||
pub fn build_named<T: Into<String>>(&mut self, name: T, instruction: Instr) -> CompileResult<InstructionValue> {
|
||||
unsafe {
|
||||
self.builder.add_instruction(
|
||||
&self.value,
|
||||
InstructionData {
|
||||
kind: instruction,
|
||||
location: None,
|
||||
meta: None,
|
||||
},
|
||||
name.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(&mut self, instruction: Instr) -> CompileResult<InstructionValue> {
|
||||
unsafe {
|
||||
let name = instruction.default_name().to_owned();
|
||||
self.builder.add_instruction(
|
||||
&self.value,
|
||||
InstructionData {
|
||||
kind: instruction,
|
||||
location: None,
|
||||
meta: None,
|
||||
},
|
||||
name,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_function(&mut self, name: &String) -> Option<FunctionValue> {
|
||||
unsafe { self.builder.find_function(self.value.0.0, name) }
|
||||
}
|
||||
|
||||
pub fn set_instr_location(&self, instruction: InstructionValue, location: DebugLocationValue) {
|
||||
unsafe {
|
||||
self.builder.add_instruction_location(&instruction, location);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_instr_metadata(&self, instruction: InstructionValue, location: DebugMetadataValue) {
|
||||
unsafe {
|
||||
self.builder.add_instruction_metadata(&instruction, location);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn terminate(&mut self, instruction: TerminatorKind) -> CompileResult<()> {
|
||||
unsafe { self.builder.terminate(&self.value, instruction) }
|
||||
}
|
||||
|
||||
pub fn set_terminator_location(&mut self, location: DebugLocationValue) -> CompileResult<()> {
|
||||
unsafe { self.builder.set_terminator_location(&self.value, location) }
|
||||
}
|
||||
|
||||
/// Delete block if it is unused. Return true if deleted, false if not.
|
||||
pub fn delete_if_unused(&mut self) -> CompileResult<bool> {
|
||||
unsafe {
|
||||
if !self.builder.is_block_used(self.value()) {
|
||||
self.builder.delete_block(&self.value)?;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(&self) -> BlockValue {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InstructionData {
|
||||
kind: Instr,
|
||||
location: Option<DebugLocationValue>,
|
||||
meta: Option<DebugMetadataValue>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Hash)]
|
||||
pub enum CmpPredicate {
|
||||
LT,
|
||||
LE,
|
||||
GT,
|
||||
GE,
|
||||
EQ,
|
||||
NE,
|
||||
}
|
||||
|
||||
/// https://llvm.org/docs/LangRef.html#instruction-reference
|
||||
#[derive(Clone)]
|
||||
pub enum Instr {
|
||||
Param(usize),
|
||||
Constant(ConstValueKind),
|
||||
GetGlobal(GlobalValue),
|
||||
|
||||
/// Add two integers
|
||||
Add(InstructionValue, InstructionValue),
|
||||
/// Add two floats
|
||||
FAdd(InstructionValue, InstructionValue),
|
||||
/// Subtract two integers
|
||||
Sub(InstructionValue, InstructionValue),
|
||||
/// Subtract two floats
|
||||
FSub(InstructionValue, InstructionValue),
|
||||
/// Multiply two integers
|
||||
Mul(InstructionValue, InstructionValue),
|
||||
/// Multiply two floats
|
||||
FMul(InstructionValue, InstructionValue),
|
||||
/// Divide two unsigned integers
|
||||
UDiv(InstructionValue, InstructionValue),
|
||||
/// Divide two signed integers
|
||||
SDiv(InstructionValue, InstructionValue),
|
||||
/// Divide two floats
|
||||
FDiv(InstructionValue, InstructionValue),
|
||||
/// Get the remainder from two unsigned integers
|
||||
URem(InstructionValue, InstructionValue),
|
||||
/// Get the remainder from two signed integers
|
||||
SRem(InstructionValue, InstructionValue),
|
||||
/// Get the remainder from two floats
|
||||
FRem(InstructionValue, InstructionValue),
|
||||
|
||||
And(InstructionValue, InstructionValue),
|
||||
Or(InstructionValue, InstructionValue),
|
||||
XOr(InstructionValue, InstructionValue),
|
||||
ShiftRightLogical(InstructionValue, InstructionValue),
|
||||
ShiftRightArithmetic(InstructionValue, InstructionValue),
|
||||
ShiftLeft(InstructionValue, InstructionValue),
|
||||
|
||||
Phi(Vec<InstructionValue>),
|
||||
|
||||
Alloca(Type),
|
||||
Load(InstructionValue, Type),
|
||||
Store(InstructionValue, InstructionValue),
|
||||
ArrayAlloca(Type, InstructionValue),
|
||||
GetElemPtr(InstructionValue, Vec<InstructionValue>),
|
||||
GetStructElemPtr(InstructionValue, u32),
|
||||
ExtractValue(InstructionValue, u32),
|
||||
|
||||
/// Integer Comparison
|
||||
ICmp(CmpPredicate, InstructionValue, InstructionValue),
|
||||
/// FLoat Comparison
|
||||
FCmp(CmpPredicate, InstructionValue, InstructionValue),
|
||||
|
||||
/// The `trunc` instruction truncates the high order bits in value and
|
||||
/// converts the remaining bits to ty2. Since the source size must be larger
|
||||
/// than the destination size, `trunc` cannot be a no-op cast. It will
|
||||
/// always truncate bits.
|
||||
Trunc(InstructionValue, Type),
|
||||
/// The `zext` fills the high order bits of the value with zero bits until
|
||||
/// it reaches the size of the destination type, ty2.
|
||||
ZExt(InstructionValue, Type),
|
||||
/// The `sext` instruction performs a sign extension by copying the sign bit
|
||||
/// (highest order bit) of the value until it reaches the bit size of the
|
||||
/// type ty2.
|
||||
SExt(InstructionValue, Type),
|
||||
/// The `fptrunc` instruction casts a value from a larger floating-point
|
||||
/// type to a smaller floating-point type.
|
||||
FPTrunc(InstructionValue, Type),
|
||||
/// The `fpext` instruction extends the value from a smaller floating-point
|
||||
/// type to a larger floating-point type.
|
||||
FPExt(InstructionValue, Type),
|
||||
/// The `fptoui` instruction takes a value to cast, which must be a scalar
|
||||
/// or vector floating-point value, and a type to cast it to ty2, which must
|
||||
/// be an integer type.
|
||||
FPToUI(InstructionValue, Type),
|
||||
/// The `fptosi` instruction takes a value to cast, which must be a scalar
|
||||
/// or vector floating-point value, and a type to cast it to ty2, which must
|
||||
/// be an integer type.
|
||||
FPToSI(InstructionValue, Type),
|
||||
/// The `uitofp` instruction takes a value to cast, which must be a scalar
|
||||
/// or vector integer value, and a type to cast it to ty2, which must be an
|
||||
/// floating-point type.
|
||||
UIToFP(InstructionValue, Type),
|
||||
/// The `sitofp` instruction takes a value to cast, which must be a scalar
|
||||
/// or vector integer value, and a type to cast it to ty2, which must be an
|
||||
/// floating-point type
|
||||
SIToFP(InstructionValue, Type),
|
||||
/// The `ptrtoint` instruction converts value to integer type ty2 by
|
||||
/// interpreting the all pointer representation bits as an integer
|
||||
/// (equivalent to a bitcast) and either truncating or zero extending that
|
||||
/// value to the size of the integer type.
|
||||
PtrToInt(InstructionValue, Type),
|
||||
/// The `inttoptr` instruction converts value to type ty2 by applying either
|
||||
/// a zero extension or a truncation depending on the size of the integer
|
||||
/// value.
|
||||
IntToPtr(InstructionValue, Type),
|
||||
/// The `bitcast` instruction converts value to type ty2. It is always a
|
||||
/// no-op cast because no bits change with this conversion.
|
||||
BitCast(InstructionValue, Type),
|
||||
|
||||
/// Check if the given instruction value is a null pointer
|
||||
IsNull(InstructionValue),
|
||||
|
||||
FunctionCall(FunctionValue, Vec<InstructionValue>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd)]
|
||||
pub enum Type {
|
||||
I8,
|
||||
I16,
|
||||
I32,
|
||||
I64,
|
||||
I128,
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
U64,
|
||||
U128,
|
||||
F16,
|
||||
F32B,
|
||||
F32,
|
||||
F64,
|
||||
F80,
|
||||
F128,
|
||||
F128PPC,
|
||||
Bool,
|
||||
Void,
|
||||
CustomType(TypeValue),
|
||||
Array(Box<Type>, u64),
|
||||
Ptr(Box<Type>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ConstValueKind {
|
||||
I8(i8),
|
||||
I16(i16),
|
||||
I32(i32),
|
||||
I64(i64),
|
||||
I128(i128),
|
||||
U8(u8),
|
||||
U16(u16),
|
||||
U32(u32),
|
||||
U64(u64),
|
||||
U128(u128),
|
||||
Bool(bool),
|
||||
Str(String),
|
||||
F16(f32),
|
||||
F32B(f32),
|
||||
F32(f32),
|
||||
F64(f64),
|
||||
F80(f64),
|
||||
F128(f64),
|
||||
F128PPC(f64),
|
||||
Array(Vec<ConstantValue>, Type),
|
||||
}
|
||||
|
||||
#[derive(Clone, Hash)]
|
||||
pub enum TerminatorKind {
|
||||
Ret(InstructionValue),
|
||||
RetVoid,
|
||||
Br(BlockValue),
|
||||
CondBr(InstructionValue, BlockValue, BlockValue),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||
pub struct TypeData {
|
||||
name: String,
|
||||
kind: CustomTypeKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||
pub enum CustomTypeKind {
|
||||
NamedStruct(NamedStruct),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||
pub struct NamedStruct(pub String, pub Vec<Type>);
|
||||
|
||||
impl ConstValueKind {
|
||||
pub fn get_type(&self) -> Type {
|
||||
use Type::*;
|
||||
match self {
|
||||
ConstValueKind::I8(_) => I8,
|
||||
ConstValueKind::I16(_) => I16,
|
||||
ConstValueKind::I32(_) => I32,
|
||||
ConstValueKind::I64(_) => I64,
|
||||
ConstValueKind::I128(_) => I128,
|
||||
ConstValueKind::U8(_) => U8,
|
||||
ConstValueKind::U16(_) => U16,
|
||||
ConstValueKind::U32(_) => U32,
|
||||
ConstValueKind::U64(_) => U64,
|
||||
ConstValueKind::U128(_) => U128,
|
||||
ConstValueKind::Str(_) => Type::Ptr(Box::new(U8)),
|
||||
ConstValueKind::Bool(_) => Bool,
|
||||
ConstValueKind::F16(_) => F16,
|
||||
ConstValueKind::F32B(_) => F32B,
|
||||
ConstValueKind::F32(_) => F32,
|
||||
ConstValueKind::F64(_) => F64,
|
||||
ConstValueKind::F80(_) => F80,
|
||||
ConstValueKind::F128(_) => F128,
|
||||
ConstValueKind::F128PPC(_) => F128PPC,
|
||||
ConstValueKind::Array(vals, ty) => Type::Array(Box::new(ty.clone()), vals.len() as u64),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone)]
|
||||
pub enum TypeCategory {
|
||||
SignedInteger,
|
||||
UnsignedInteger,
|
||||
Void,
|
||||
Real,
|
||||
Ptr,
|
||||
CustomType,
|
||||
Array,
|
||||
}
|
||||
|
||||
impl TypeCategory {
|
||||
pub fn comparable(&self) -> bool {
|
||||
match self {
|
||||
TypeCategory::SignedInteger => true,
|
||||
TypeCategory::UnsignedInteger => true,
|
||||
TypeCategory::Real => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn signed(&self) -> bool {
|
||||
match self {
|
||||
TypeCategory::SignedInteger => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn integer(&self) -> bool {
|
||||
match self {
|
||||
TypeCategory::SignedInteger => true,
|
||||
TypeCategory::UnsignedInteger => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn category(&self) -> TypeCategory {
|
||||
match self {
|
||||
Type::I8 | Type::I16 | Type::I32 | Type::I64 | Type::I128 => TypeCategory::SignedInteger,
|
||||
Type::U8 | Type::U16 | Type::U32 | Type::U64 | Type::U128 => TypeCategory::UnsignedInteger,
|
||||
Type::F16 | Type::F32B | Type::F32 | Type::F64 | Type::F80 | Type::F128 | Type::F128PPC => {
|
||||
TypeCategory::Real
|
||||
}
|
||||
Type::Bool => TypeCategory::UnsignedInteger,
|
||||
Type::Void => TypeCategory::Void,
|
||||
Type::CustomType(_) => TypeCategory::CustomType,
|
||||
Type::Array(_, _) => TypeCategory::Array,
|
||||
Type::Ptr(_) => TypeCategory::Ptr,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cast_instruction(&self, value: InstructionValue, other: &Type) -> Option<Instr> {
|
||||
use Type::*;
|
||||
match (self, other) {
|
||||
(I8, I16 | I32 | I64 | I128) => Some(Instr::SExt(value, other.clone())),
|
||||
(I16, I32 | I64 | I128) => Some(Instr::SExt(value, other.clone())),
|
||||
(I32, I64 | I128) => Some(Instr::SExt(value, other.clone())),
|
||||
(I64, I128) => Some(Instr::SExt(value, other.clone())),
|
||||
(I128 | U128, I64 | U64 | I32 | U32 | I16 | U16 | I8 | U8) => Some(Instr::Trunc(value, other.clone())),
|
||||
(I64 | U64, I32 | U32 | I16 | U16 | I8 | U8) => Some(Instr::Trunc(value, other.clone())),
|
||||
(I32 | U32, I16 | U16 | I8 | U8) => Some(Instr::Trunc(value, other.clone())),
|
||||
(I16 | U16, I8 | U8) => Some(Instr::Trunc(value, other.clone())),
|
||||
(U8 | I8, U8 | I8 | U16 | I16 | U32 | I32 | U64 | I64 | U128 | I128) => {
|
||||
Some(Instr::ZExt(value, other.clone()))
|
||||
}
|
||||
(U16 | I16, U16 | I16 | U32 | I32 | U64 | I64 | U128 | I128) => Some(Instr::ZExt(value, other.clone())),
|
||||
(U32 | I32, U32 | I32 | U64 | I64 | U128 | I128) => Some(Instr::ZExt(value, other.clone())),
|
||||
(U64 | I64, U64 | I64 | U128 | I128) => Some(Instr::ZExt(value, other.clone())),
|
||||
(U128 | I128, U128 | I128) => Some(Instr::ZExt(value, other.clone())),
|
||||
(U8 | U16 | U32 | U64 | U128, F16 | F32 | F32B | F64 | F80 | F128 | F128PPC) => {
|
||||
Some(Instr::UIToFP(value, other.clone()))
|
||||
}
|
||||
(I8 | I16 | I32 | I64 | I128, F16 | F32 | F32B | F64 | F80 | F128 | F128PPC) => {
|
||||
Some(Instr::SIToFP(value, other.clone()))
|
||||
}
|
||||
(F16 | F32 | F32B | F64 | F80 | F128 | F128PPC, U8 | U16 | U32 | U64 | U128) => {
|
||||
Some(Instr::FPToUI(value, other.clone()))
|
||||
}
|
||||
(F16 | F32 | F32B | F64 | F80 | F128 | F128PPC, I8 | I16 | I32 | I64 | I128) => {
|
||||
Some(Instr::FPToSI(value, other.clone()))
|
||||
}
|
||||
(I128 | U128 | I64 | U64 | I32 | U32 | I16 | U16 | I8 | U8, Ptr(_)) => {
|
||||
Some(Instr::IntToPtr(value, other.clone()))
|
||||
}
|
||||
(Ptr(_), I128 | U128 | I64 | U64 | I32 | U32 | I16 | U16 | I8 | U8) => {
|
||||
Some(Instr::PtrToInt(value, other.clone()))
|
||||
}
|
||||
(F16, F32 | F32B | F64 | F80 | F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())),
|
||||
(F32 | F32B, F64 | F80 | F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())),
|
||||
(F64, F80 | F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())),
|
||||
(F80, F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())),
|
||||
(F128PPC | F128, F80 | F64 | F32B | F32 | F16) => Some(Instr::FPTrunc(value, other.clone())),
|
||||
(F80, F64 | F32B | F32 | F16) => Some(Instr::FPTrunc(value, other.clone())),
|
||||
(F64, F32B | F32 | F16) => Some(Instr::FPTrunc(value, other.clone())),
|
||||
(F32B | F32, F16) => Some(Instr::FPTrunc(value, other.clone())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TerminatorKind {
|
||||
pub(crate) fn get_type(&self, builder: &Builder) -> CompileResult<Type> {
|
||||
use TerminatorKind::*;
|
||||
match self {
|
||||
Ret(instr_val) => instr_val.get_type(builder),
|
||||
RetVoid => Ok(Type::Void),
|
||||
Br(_) => Ok(Type::Void),
|
||||
CondBr(_, _, _) => Ok(Type::Void),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
//! Copied from
|
||||
//! https://github.com/rust-lang/rust/blob/6b3ae3f6e45a33c2d95fa0362c9b2593e567fd34/library/core/src/fmt/builders.rs#L102
|
||||
|
||||
// Copyright (c) The Rust Project Contributors
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
pub struct PadAdapter<'buf, 'state> {
|
||||
buf: &'buf mut (dyn fmt::Write + 'buf),
|
||||
state: &'state mut PadAdapterState,
|
||||
}
|
||||
|
||||
pub struct PadAdapterState {
|
||||
on_newline: bool,
|
||||
}
|
||||
|
||||
impl Default for PadAdapterState {
|
||||
fn default() -> Self {
|
||||
PadAdapterState { on_newline: true }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'buf, 'state> PadAdapter<'buf, 'state> {
|
||||
pub fn wrap<'slot, 'fmt: 'buf + 'slot>(
|
||||
fmt: &'buf mut (dyn fmt::Write + 'buf),
|
||||
state: &'state mut PadAdapterState,
|
||||
) -> Self {
|
||||
PadAdapter { buf: fmt, state }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for PadAdapter<'_, '_> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for s in s.split_inclusive('\n') {
|
||||
if self.state.on_newline {
|
||||
self.buf.write_str(" ")?;
|
||||
}
|
||||
self.state.on_newline = s.ends_with('\n');
|
||||
self.buf.write_str(s)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_char(&mut self, c: char) -> fmt::Result {
|
||||
if self.state.on_newline {
|
||||
self.buf.write_str(" ")?;
|
||||
}
|
||||
self.state.on_newline = c == '\n';
|
||||
self.buf.write_char(c)
|
||||
}
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
use std::{
|
||||
ffi::{CStr, CString, c_char},
|
||||
ptr::null_mut,
|
||||
string::FromUtf8Error,
|
||||
};
|
||||
|
||||
use llvm_sys::{
|
||||
core::{LLVMCreateMemoryBufferWithMemoryRange, LLVMDisposeMemoryBuffer, LLVMGetBufferSize, LLVMGetBufferStart},
|
||||
error::LLVMDisposeErrorMessage,
|
||||
prelude::LLVMMemoryBufferRef,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
CompileResult, ErrorKind, Type,
|
||||
builder::{Builder, InstructionValue},
|
||||
};
|
||||
|
||||
pub fn into_cstring<T: Into<String>>(value: T) -> CString {
|
||||
let string = value.into();
|
||||
unsafe { CString::from_vec_with_nul_unchecked((string + "\0").into_bytes()) }
|
||||
}
|
||||
|
||||
pub fn from_cstring(pointer: *mut c_char) -> Option<String> {
|
||||
if pointer.is_null() {
|
||||
None
|
||||
} else {
|
||||
unsafe { CStr::from_ptr(pointer).to_str().ok().map(|s| s.to_owned()) }
|
||||
}
|
||||
}
|
||||
|
||||
fn cstring_to_err(value: *mut c_char) -> Result<(), String> {
|
||||
from_cstring(value).filter(|s| !s.is_empty()).map_or(Ok(()), |s| Err(s))
|
||||
}
|
||||
|
||||
/// Utility struct for LLVM's Error Messages, which need to be disposed
|
||||
/// manually.
|
||||
pub struct ErrorMessageHolder(*mut c_char);
|
||||
|
||||
impl ErrorMessageHolder {
|
||||
pub fn null() -> Self {
|
||||
ErrorMessageHolder(null_mut())
|
||||
}
|
||||
|
||||
pub fn borrow_mut(&mut self) -> *mut *mut c_char {
|
||||
&mut self.0
|
||||
}
|
||||
|
||||
pub fn into_result(&self) -> Result<(), String> {
|
||||
cstring_to_err(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ErrorMessageHolder {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if !self.0.is_null() {
|
||||
LLVMDisposeErrorMessage(self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Utility for creating and handling LLVM MemoryBuffers, needed for printing
|
||||
/// out ASM and .o -files without relying on LLVM's own API.
|
||||
pub struct MemoryBufferHolder {
|
||||
pub buffer: LLVMMemoryBufferRef,
|
||||
}
|
||||
|
||||
impl MemoryBufferHolder {
|
||||
pub fn empty(name: &str) -> MemoryBufferHolder {
|
||||
let array = [0i8; 0];
|
||||
unsafe {
|
||||
let buffer =
|
||||
LLVMCreateMemoryBufferWithMemoryRange(array.as_ptr(), array.len(), into_cstring(name).as_ptr(), 0);
|
||||
MemoryBufferHolder { buffer }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_buffer(&self) -> Vec<u8> {
|
||||
unsafe {
|
||||
let start = LLVMGetBufferStart(self.buffer);
|
||||
let size = LLVMGetBufferSize(self.buffer);
|
||||
|
||||
let mut buff = Vec::with_capacity(size);
|
||||
for i in 0..size {
|
||||
buff.push(*start.add(i) as u8);
|
||||
}
|
||||
buff
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_string(&self) -> Result<String, FromUtf8Error> {
|
||||
String::from_utf8(self.as_buffer())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MemoryBufferHolder {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
LLVMDisposeMemoryBuffer(self.buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Make sure types for given instructions match. Return Ok(type) if they do,
|
||||
/// and error otherwise.
|
||||
pub fn match_types(lhs: &InstructionValue, rhs: &InstructionValue, builder: &Builder) -> CompileResult<Type> {
|
||||
let lhs_t = lhs.get_type(&builder)?;
|
||||
let rhs_t = rhs.get_type(&builder)?;
|
||||
if lhs_t == rhs_t {
|
||||
Ok(lhs_t)
|
||||
} else {
|
||||
Err(ErrorKind::TypesIncompatible(lhs_t, rhs_t))
|
||||
}
|
||||
}
|
7
reid-lsp/.gitignore
vendored
7
reid-lsp/.gitignore
vendored
@ -1,7 +0,0 @@
|
||||
.vscode
|
||||
node_modules
|
||||
dist
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
tsconfig.tsbuildinfo
|
||||
*.vsix
|
@ -1 +0,0 @@
|
||||
enable-pre-post-scripts = true
|
@ -1,5 +0,0 @@
|
||||
import { defineConfig } from '@vscode/test-cli';
|
||||
|
||||
export default defineConfig({
|
||||
files: 'out/test/**/*.test.js',
|
||||
});
|
@ -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.*
|
@ -1,9 +0,0 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to the "reid-lsp" extension will be documented in this file.
|
||||
|
||||
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- Initial release
|
@ -1,13 +0,0 @@
|
||||
[package]
|
||||
name = "reid-language-server"
|
||||
version = "1.0.0-beta.1"
|
||||
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-beta.4", registry="gitea-teascade", features=[] }
|
||||
dashmap = "6.1.0"
|
||||
serde = "*"
|
||||
serde_json = "*"
|
@ -1 +0,0 @@
|
||||
# Reid Language Server
|
@ -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"
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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));
|
||||
});
|
||||
});
|
@ -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"
|
||||
]
|
||||
}
|
@ -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",
|
||||
},
|
||||
}];
|
@ -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": [
|
||||
[
|
||||
"(",
|
||||
")"
|
||||
],
|
||||
[
|
||||
"[",
|
||||
"]"
|
||||
],
|
||||
[
|
||||
"{",
|
||||
"}"
|
||||
]
|
||||
]
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
{
|
||||
"name": "reid-language-server",
|
||||
"displayName": "Reid Language Server",
|
||||
"description": "Language Server Extension for Reid",
|
||||
"version": "1.0.0-beta.1",
|
||||
"repository": {
|
||||
"url": "https://git.teascade.net/teascade"
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "^1.102.0"
|
||||
},
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
"main": "./dist/extension.js",
|
||||
"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-lsp",
|
||||
"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
@ -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(¶ms.text, file_path.clone(), &mut map, module_id);
|
||||
let (tokens, result) = match parse_res {
|
||||
Ok((module_id, tokens)) => (
|
||||
tokens.clone(),
|
||||
analyze(module_id, tokens, file_path.clone(), &mut map, &self.state_map()),
|
||||
),
|
||||
Err(e) => (Vec::new(), Err(e)),
|
||||
};
|
||||
|
||||
let mut diagnostics = Vec::new();
|
||||
match result {
|
||||
Ok(Some(mut analysis)) => {
|
||||
if let Some(reid_error) = &mut analysis.error {
|
||||
self.client
|
||||
.log_message(
|
||||
MessageType::INFO,
|
||||
format!("Successfully compiled despite parsing errors!"),
|
||||
)
|
||||
.await;
|
||||
reid_error.errors.dedup();
|
||||
for error in &reid_error.errors {
|
||||
diagnostics.push(reid_error_into_diagnostic(error, &tokens));
|
||||
self.client.log_message(MessageType::INFO, format!("{}", error)).await;
|
||||
}
|
||||
}
|
||||
self.analysis.insert(file_path, analysis);
|
||||
}
|
||||
Ok(_) => {}
|
||||
Err(mut reid_error) => {
|
||||
reid_error.errors.dedup();
|
||||
for error in &reid_error.errors {
|
||||
diagnostics.push(reid_error_into_diagnostic(error, &tokens));
|
||||
self.client.log_message(MessageType::INFO, format!("{}", error)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.client
|
||||
.publish_diagnostics(params.uri.clone(), diagnostics, Some(params.version))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
fn reid_error_into_diagnostic(error: &error_raporting::ErrorKind, tokens: &Vec<FullToken>) -> Diagnostic {
|
||||
let meta = error.get_meta();
|
||||
let positions = meta
|
||||
.range
|
||||
.into_position(&tokens)
|
||||
.unwrap_or((Position(0, 0), Position(0, 0)));
|
||||
|
||||
Diagnostic {
|
||||
range: Range {
|
||||
start: lsp_types::Position {
|
||||
line: ((positions.0.1 as i32) - 1).max(0) as u32,
|
||||
character: ((positions.0.0 as i32) - 1).max(0) as u32,
|
||||
},
|
||||
end: lsp_types::Position {
|
||||
line: ((positions.1.1 as i32) - 1).max(0) as u32,
|
||||
character: ((positions.1.0 as i32) - 1).max(0) as u32,
|
||||
},
|
||||
},
|
||||
severity: Some(DiagnosticSeverity::ERROR),
|
||||
code: None,
|
||||
code_description: None,
|
||||
source: Some(error.get_type_str().to_owned()),
|
||||
message: format!("{}", error),
|
||||
related_information: None,
|
||||
tags: None,
|
||||
data: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse(
|
||||
source: &str,
|
||||
path: PathBuf,
|
||||
map: &mut ErrorModules,
|
||||
module_id: SourceModuleId,
|
||||
) -> Result<(SourceModuleId, Vec<FullToken>), ReidError> {
|
||||
let file_name = path.file_name().unwrap().to_str().unwrap().to_owned();
|
||||
|
||||
Ok(parse_module(
|
||||
source,
|
||||
file_name.clone(),
|
||||
Some(path),
|
||||
map,
|
||||
Some(module_id),
|
||||
)?)
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let stdin = tokio::io::stdin();
|
||||
let stdout = tokio::io::stdout();
|
||||
|
||||
let (service, socket) = LspService::new(|client| Backend {
|
||||
client,
|
||||
analysis: DashMap::new(),
|
||||
module_to_path: DashMap::new(),
|
||||
path_to_module: DashMap::new(),
|
||||
module_id_counter: Mutex::new(SourceModuleId(0)),
|
||||
});
|
||||
Server::new(stdin, stdout, socket).serve(service).await;
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
|
||||
|
@ -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/"
|
||||
},
|
||||
]
|
||||
}
|
@ -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 doesn’t yet need to load the plugin.
|
||||
* `src/extension.ts` - this is the main file where you will provide the implementation of your command.
|
||||
* The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
|
||||
* We pass the function containing the implementation of the command as the second parameter to `registerCommand`.
|
||||
|
||||
## Setup
|
||||
|
||||
* install the recommended extensions (amodio.tsl-problem-matcher, ms-vscode.extension-test-runner, and dbaeumer.vscode-eslint)
|
||||
|
||||
|
||||
## Get up and running straight away
|
||||
|
||||
* Press `F5` to open a new window with your extension loaded.
|
||||
* Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`.
|
||||
* Set breakpoints in your code inside `src/extension.ts` to debug your extension.
|
||||
* Find output from your extension in the debug console.
|
||||
|
||||
## Make changes
|
||||
|
||||
* You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`.
|
||||
* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
|
||||
|
||||
|
||||
## Explore the API
|
||||
|
||||
* You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`.
|
||||
|
||||
## Run tests
|
||||
|
||||
* Install the [Extension Test Runner](https://marketplace.visualstudio.com/items?itemName=ms-vscode.extension-test-runner)
|
||||
* Run the "watch" task via the **Tasks: Run Task** command. Make sure this is running, or tests might not be discovered.
|
||||
* Open the Testing view from the activity bar and click the Run Test" button, or use the hotkey `Ctrl/Cmd + ; A`
|
||||
* See the output of the test result in the Test Results view.
|
||||
* Make changes to `src/test/extension.test.ts` or create new test files inside the `test` folder.
|
||||
* The provided test runner will only consider files matching the name pattern `**.test.ts`.
|
||||
* You can create folders inside the `test` folder to structure your tests any way you want.
|
||||
|
||||
## Go further
|
||||
|
||||
* Reduce the extension size and improve the startup time by [bundling your extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension).
|
||||
* [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VS Code extension marketplace.
|
||||
* Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration).
|
@ -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];
|
@ -1,23 +0,0 @@
|
||||
[package]
|
||||
name = "reid"
|
||||
version = "1.0.0-beta.4"
|
||||
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-beta.4", 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 }
|
@ -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(())
|
||||
}
|
@ -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;
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
@ -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
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user