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
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
||||
[submodule "reid-llvm-lib"]
|
||||
path = reid-llvm-lib
|
||||
url = gitea@git.teascade.net:teascade/reid-llvm-lib.git
|
@ -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 +0,0 @@
|
||||
Subproject commit 100cd96a6dc3bb882ce60e78c4eab47e77fdd8c4
|
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-language-server" extension will be documented in this file.
|
||||
|
||||
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- Initial release
|
@ -1,13 +0,0 @@
|
||||
[package]
|
||||
name = "reid-language-server"
|
||||
version = "1.0.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
socket = "0.0.7"
|
||||
tokio = { version = "1.47.0", features = ["full"] }
|
||||
tower-lsp = "0.20.0"
|
||||
reid = { path = "../reid", version = "1.0.0", registry="gitea-teascade", features=[] }
|
||||
dashmap = "6.1.0"
|
||||
serde = "*"
|
||||
serde_json = "*"
|
@ -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,94 +0,0 @@
|
||||
{
|
||||
"name": "reid-language-server",
|
||||
"displayName": "Reid Language Server",
|
||||
"description": "Language Server Extension for Reid",
|
||||
"version": "1.0.0",
|
||||
"repository": {
|
||||
"url": "https://git.teascade.net/teascade"
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "^1.102.0"
|
||||
},
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
"main": "./dist/extension.js",
|
||||
"icon": "./reid.png",
|
||||
"publisher": "Teascade",
|
||||
"author": {
|
||||
"email": "teascade@teascade.net",
|
||||
"name": "Teascade",
|
||||
"url": "https://teascade.net"
|
||||
},
|
||||
"contributes": {
|
||||
"languages": [
|
||||
{
|
||||
"id": "reid",
|
||||
"extensions": [
|
||||
".reid"
|
||||
],
|
||||
"aliases": [
|
||||
"Reid"
|
||||
],
|
||||
"configuration": "./language-configuration.json",
|
||||
"icon": {
|
||||
"dark": "./reid.png",
|
||||
"light": "./reid.png"
|
||||
}
|
||||
}
|
||||
],
|
||||
"breakpoints": [
|
||||
{
|
||||
"language": "reid"
|
||||
}
|
||||
],
|
||||
"configuration": {
|
||||
"type": "object",
|
||||
"title": "reid-language-server",
|
||||
"properties": {
|
||||
"reid-language-server.language-server-path": {
|
||||
"type": "string",
|
||||
"scope": "window",
|
||||
"default": "$HOME/.cargo/bin/reid-language-server",
|
||||
"description": "Path to the Reid Language Server executable"
|
||||
}
|
||||
}
|
||||
},
|
||||
"grammars": [
|
||||
{
|
||||
"language": "reid",
|
||||
"scopeName": "source.reid",
|
||||
"path": "./syntaxes/grammar.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "pnpm run package",
|
||||
"compile": "npx js-yaml syntaxes/grammar.yaml > syntaxes/grammar.json && webpack",
|
||||
"watch": "webpack --watch",
|
||||
"package": "webpack --mode production --devtool hidden-source-map",
|
||||
"compile-tests": "tsc -p . --outDir out",
|
||||
"watch-tests": "tsc -p . -w --outDir out",
|
||||
"pretest": "pnpm run compile-tests && pnpm run compile && pnpm run lint",
|
||||
"lint": "eslint src",
|
||||
"test": "vscode-test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^10.0.10",
|
||||
"@types/node": "20.x",
|
||||
"@types/vscode": "^1.102.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.31.1",
|
||||
"@typescript-eslint/parser": "^8.31.1",
|
||||
"@vscode/test-cli": "^0.0.11",
|
||||
"@vscode/test-electron": "^2.5.2",
|
||||
"eslint": "^9.25.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"ts-loader": "^9.5.2",
|
||||
"typescript": "^5.8.3",
|
||||
"webpack": "^5.99.7",
|
||||
"webpack-cli": "^6.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-languageclient": "^9.0.1"
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 1.8 KiB |
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
[features]
|
||||
default = ["color", "cli"]
|
||||
|
||||
color = ["colored"]
|
||||
cli = ["argh", "stderrlog"]
|
||||
|
||||
[dependencies]
|
||||
## Make it easier to generate errors
|
||||
thiserror = "1.0.44"
|
||||
reid-lib = { path = "../reid-llvm-lib", version = "1.0.0", registry="gitea-teascade" }
|
||||
|
||||
argh = { version = "0.1.13", optional = true }
|
||||
stderrlog = { version = "0.6.0", optional = true }
|
||||
log = "*"
|
||||
colored = { version = "3.0.0", optional = true }
|
@ -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
@ -1,246 +0,0 @@
|
||||
use std::{cell::RefCell, collections::HashMap, mem, rc::Rc};
|
||||
|
||||
use reid_lib::{
|
||||
builder::{FunctionValue, GlobalValue, InstructionValue, TypeValue},
|
||||
debug_information::{DebugInformation, DebugLocation, DebugScopeValue, DebugTypeValue},
|
||||
intrinsics::LLVMIntrinsic,
|
||||
Block, Context, Function, Instr, Module,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
codegen::intrinsics::LLVMIntrinsicKind,
|
||||
lexer::FullToken,
|
||||
mir::{
|
||||
self,
|
||||
pass::{AssociatedFunctionKey, BinopKey},
|
||||
CustomTypeKey, FunctionParam, Metadata, SourceModuleId, TypeDefinition, TypeKind,
|
||||
},
|
||||
};
|
||||
|
||||
use super::{allocator::Allocator, ErrorKind, IntrinsicFunction};
|
||||
|
||||
pub struct Scope<'ctx, 'scope> {
|
||||
pub(super) context: &'ctx Context,
|
||||
pub(super) modules: &'scope HashMap<SourceModuleId, &'ctx mir::Module>,
|
||||
pub(super) tokens: &'ctx Vec<FullToken>,
|
||||
pub(super) module: &'ctx Module<'ctx>,
|
||||
pub(super) module_id: SourceModuleId,
|
||||
pub(super) function: &'ctx Function<'ctx>,
|
||||
pub(super) block: Block<'ctx>,
|
||||
pub(super) types: &'scope HashMap<TypeValue, TypeDefinition>,
|
||||
pub(super) type_values: &'scope HashMap<CustomTypeKey, TypeValue>,
|
||||
pub(super) type_map: &'scope HashMap<CustomTypeKey, TypeDefinition>,
|
||||
pub(super) assoc_functions: &'scope HashMap<AssociatedFunctionKey, ScopeFunctionKind<'ctx>>,
|
||||
pub(super) functions: &'scope HashMap<String, ScopeFunctionKind<'ctx>>,
|
||||
pub(super) binops: &'scope HashMap<BinopKey, StackBinopDefinition<'ctx>>,
|
||||
pub(super) stack_values: HashMap<String, StackValue>,
|
||||
pub(super) globals: &'scope HashMap<String, GlobalValue>,
|
||||
pub(super) debug: Option<Debug<'ctx>>,
|
||||
pub(super) allocator: Rc<RefCell<Allocator>>,
|
||||
pub(super) llvm_intrinsics: Rc<RefCell<HashMap<LLVMIntrinsicKind, FunctionValue>>>,
|
||||
}
|
||||
|
||||
impl<'ctx, 'a> Scope<'ctx, 'a> {
|
||||
pub fn with_block(&self, block: Block<'ctx>) -> Scope<'ctx, 'a> {
|
||||
Scope {
|
||||
block,
|
||||
modules: self.modules,
|
||||
tokens: self.tokens,
|
||||
function: self.function,
|
||||
context: self.context,
|
||||
module: self.module,
|
||||
module_id: self.module_id,
|
||||
assoc_functions: self.assoc_functions,
|
||||
functions: self.functions,
|
||||
types: self.types,
|
||||
type_values: self.type_values,
|
||||
type_map: self.type_map,
|
||||
stack_values: self.stack_values.clone(),
|
||||
debug: self.debug.clone(),
|
||||
allocator: self.allocator.clone(),
|
||||
globals: self.globals,
|
||||
binops: self.binops,
|
||||
llvm_intrinsics: self.llvm_intrinsics.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes the block out from this scope, swaps the given block in it's place
|
||||
/// and returns the old block.
|
||||
pub fn swap_block(&mut self, block: Block<'ctx>) -> Block<'ctx> {
|
||||
let mut old_block = block;
|
||||
mem::swap(&mut self.block, &mut old_block);
|
||||
old_block
|
||||
}
|
||||
|
||||
pub fn get_typedef(&self, key: &CustomTypeKey) -> Option<&TypeDefinition> {
|
||||
self.type_values.get(key).and_then(|v| self.types.get(v))
|
||||
}
|
||||
|
||||
pub fn allocate(&self, meta: &Metadata, ty: &TypeKind) -> Option<InstructionValue> {
|
||||
self.allocator.borrow_mut().allocate(meta, ty)
|
||||
}
|
||||
|
||||
pub fn get_intrinsic(&self, kind: LLVMIntrinsicKind) -> FunctionValue {
|
||||
let mut intrinsics = self.llvm_intrinsics.borrow_mut();
|
||||
if let Some(fun) = intrinsics.get(&kind) {
|
||||
*fun
|
||||
} else {
|
||||
let intrinsic = self
|
||||
.module
|
||||
.intrinsic(match &kind {
|
||||
LLVMIntrinsicKind::Max(ty) => LLVMIntrinsic::Max(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::Min(ty) => LLVMIntrinsic::Min(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::Abs(ty) => LLVMIntrinsic::Abs(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::Memcpy(ty) => LLVMIntrinsic::Memcpy(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::Sqrt(ty) => LLVMIntrinsic::Sqrt(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::PowI(lhs, rhs) => {
|
||||
LLVMIntrinsic::PowI(lhs.get_type(self.type_values), rhs.get_type(self.type_values))
|
||||
}
|
||||
LLVMIntrinsicKind::Pow(ty) => LLVMIntrinsic::Pow(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::Sin(ty) => LLVMIntrinsic::Sin(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::Cos(ty) => LLVMIntrinsic::Cos(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::Tan(ty) => LLVMIntrinsic::Tan(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::ASin(ty) => LLVMIntrinsic::ASin(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::ACos(ty) => LLVMIntrinsic::ACos(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::ATan(ty) => LLVMIntrinsic::ATan(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::ATan2(ty) => LLVMIntrinsic::ATan2(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::SinH(ty) => LLVMIntrinsic::SinH(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::CosH(ty) => LLVMIntrinsic::CosH(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::TanH(ty) => LLVMIntrinsic::TanH(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::Log(ty) => LLVMIntrinsic::Log(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::Log2(ty) => LLVMIntrinsic::Log2(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::Log10(ty) => LLVMIntrinsic::Log10(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::Copysign(ty) => LLVMIntrinsic::Copysign(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::Floor(ty) => LLVMIntrinsic::Floor(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::Ceil(ty) => LLVMIntrinsic::Ceil(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::Trunc(ty) => LLVMIntrinsic::Trunc(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::RoundEven(ty) => LLVMIntrinsic::RoundEven(ty.get_type(self.type_values)),
|
||||
LLVMIntrinsicKind::Round(ty) => LLVMIntrinsic::Round(ty.get_type(self.type_values)),
|
||||
})
|
||||
.unwrap();
|
||||
intrinsics.insert(kind, intrinsic.clone());
|
||||
intrinsic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Debug<'ctx> {
|
||||
pub(super) info: &'ctx DebugInformation,
|
||||
pub(super) scope: DebugScopeValue,
|
||||
pub(super) types: &'ctx HashMap<TypeKind, DebugTypeValue>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct StackValue(pub(super) StackValueKind, pub(super) TypeKind);
|
||||
|
||||
impl StackValue {
|
||||
pub fn instr(&self) -> InstructionValue {
|
||||
self.0.instr()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum StackValueKind {
|
||||
Immutable(InstructionValue),
|
||||
Mutable(InstructionValue),
|
||||
Literal(InstructionValue),
|
||||
}
|
||||
|
||||
impl StackValueKind {
|
||||
pub fn mutable(mutable: bool, instr: InstructionValue) -> StackValueKind {
|
||||
match mutable {
|
||||
true => StackValueKind::Mutable(instr),
|
||||
false => StackValueKind::Immutable(instr),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn instr(&self) -> InstructionValue {
|
||||
match &self {
|
||||
StackValueKind::Immutable(val) => *val,
|
||||
StackValueKind::Mutable(val) => *val,
|
||||
StackValueKind::Literal(val) => *val,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn derive(&self, instr: InstructionValue) -> StackValueKind {
|
||||
match &self {
|
||||
StackValueKind::Immutable(_) => StackValueKind::Immutable(instr),
|
||||
StackValueKind::Mutable(_) => StackValueKind::Mutable(instr),
|
||||
StackValueKind::Literal(_) => StackValueKind::Literal(instr),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn map<F>(&self, lambda: F) -> StackValueKind
|
||||
where
|
||||
F: FnOnce(InstructionValue) -> InstructionValue,
|
||||
{
|
||||
self.derive(lambda(self.instr()))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StackBinopDefinition<'ctx> {
|
||||
pub(super) parameters: (FunctionParam, FunctionParam),
|
||||
pub(super) return_ty: TypeKind,
|
||||
pub(super) kind: ScopeFunctionKind<'ctx>,
|
||||
}
|
||||
|
||||
pub enum ScopeFunctionKind<'ctx> {
|
||||
UserGenerated(Function<'ctx>),
|
||||
Intrinsic(&'ctx Box<dyn IntrinsicFunction>),
|
||||
IntrinsicOwned(Box<dyn IntrinsicFunction>),
|
||||
}
|
||||
|
||||
impl<'ctx> StackBinopDefinition<'ctx> {
|
||||
pub fn codegen<'a>(
|
||||
&self,
|
||||
lhs: StackValue,
|
||||
rhs: StackValue,
|
||||
scope: &mut Scope<'ctx, 'a>,
|
||||
) -> Result<StackValue, ErrorKind> {
|
||||
let (lhs, rhs) = if lhs.1 == self.parameters.0.ty && rhs.1 == self.parameters.1.ty {
|
||||
(lhs, rhs)
|
||||
} else {
|
||||
(rhs, lhs)
|
||||
};
|
||||
let name = format!(
|
||||
"binop.{}.{}.{}.call",
|
||||
self.parameters.0.ty, self.parameters.1.ty, self.return_ty
|
||||
);
|
||||
self.kind.codegen(&name, &[lhs, rhs], &self.return_ty, None, scope)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> ScopeFunctionKind<'ctx> {
|
||||
pub fn codegen<'a>(
|
||||
&self,
|
||||
name: &str,
|
||||
params: &[StackValue],
|
||||
return_ty: &TypeKind,
|
||||
location: Option<DebugLocation>,
|
||||
scope: &mut Scope<'ctx, 'a>,
|
||||
) -> Result<StackValue, ErrorKind> {
|
||||
match self {
|
||||
ScopeFunctionKind::UserGenerated(function) => {
|
||||
let val = scope
|
||||
.block
|
||||
.build_named(
|
||||
name,
|
||||
Instr::FunctionCall(function.value(), params.iter().map(|p| p.instr()).collect()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if let Some(debug) = &scope.debug {
|
||||
if let Some(location) = location {
|
||||
let location_val = debug.info.location(&debug.scope, location);
|
||||
val.with_location(&mut scope.block, location_val);
|
||||
}
|
||||
}
|
||||
Ok(StackValue(StackValueKind::Immutable(val), return_ty.clone()))
|
||||
}
|
||||
ScopeFunctionKind::Intrinsic(fun) => fun.codegen(scope, params),
|
||||
ScopeFunctionKind::IntrinsicOwned(fun) => fun.codegen(scope, params),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,264 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use reid_lib::{
|
||||
builder::{InstructionValue, TypeValue},
|
||||
debug_information::{
|
||||
DebugArrayType, DebugBasicType, DebugFieldType, DebugInformation, DebugLocation, DebugPointerType,
|
||||
DebugPosition, DebugScopeValue, DebugStructType, DebugTypeData, DebugTypeValue, DwarfEncoding, DwarfFlags,
|
||||
},
|
||||
Block, CmpPredicate, ConstValueKind, Instr, Type,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
lexer::{FullToken, Position},
|
||||
mir::{self, CustomTypeKey, Metadata, SourceModuleId, TypeDefinition, TypeDefinitionKind, TypeKind, VagueLiteral},
|
||||
};
|
||||
|
||||
use super::scope::{Debug, Scope};
|
||||
|
||||
impl mir::CmpOperator {
|
||||
pub(super) fn predicate(&self) -> CmpPredicate {
|
||||
match self {
|
||||
mir::CmpOperator::LT => CmpPredicate::LT,
|
||||
mir::CmpOperator::GT => CmpPredicate::GT,
|
||||
mir::CmpOperator::LE => CmpPredicate::LE,
|
||||
mir::CmpOperator::GE => CmpPredicate::GE,
|
||||
mir::CmpOperator::EQ => CmpPredicate::EQ,
|
||||
mir::CmpOperator::NE => CmpPredicate::NE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl mir::Literal {
|
||||
pub(super) fn as_const(&self, block: &mut Block) -> InstructionValue {
|
||||
block
|
||||
.build_named(format!("{}", self), Instr::Constant(self.as_const_kind()))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub(super) fn as_const_kind(&self) -> ConstValueKind {
|
||||
match self.clone() {
|
||||
mir::Literal::I8(val) => ConstValueKind::I8(val),
|
||||
mir::Literal::I16(val) => ConstValueKind::I16(val),
|
||||
mir::Literal::I32(val) => ConstValueKind::I32(val),
|
||||
mir::Literal::I64(val) => ConstValueKind::I64(val),
|
||||
mir::Literal::I128(val) => ConstValueKind::I128(val),
|
||||
mir::Literal::U8(val) => ConstValueKind::U8(val),
|
||||
mir::Literal::U16(val) => ConstValueKind::U16(val),
|
||||
mir::Literal::U32(val) => ConstValueKind::U32(val),
|
||||
mir::Literal::U64(val) => ConstValueKind::U64(val),
|
||||
mir::Literal::U128(val) => ConstValueKind::U128(val),
|
||||
mir::Literal::Bool(val) => ConstValueKind::Bool(val),
|
||||
mir::Literal::String(val) => ConstValueKind::Str(val.clone()),
|
||||
mir::Literal::Vague(VagueLiteral::Number(val)) => ConstValueKind::I32(val as i32),
|
||||
mir::Literal::Vague(VagueLiteral::Decimal(val)) => ConstValueKind::F32(val as f32),
|
||||
mir::Literal::F16(val) => ConstValueKind::F16(val),
|
||||
mir::Literal::F32B(val) => ConstValueKind::F32B(val),
|
||||
mir::Literal::F32(val) => ConstValueKind::F32(val),
|
||||
mir::Literal::F64(val) => ConstValueKind::F64(val),
|
||||
mir::Literal::F80(val) => ConstValueKind::F80(val),
|
||||
mir::Literal::F128(val) => ConstValueKind::F128(val),
|
||||
mir::Literal::F128PPC(val) => ConstValueKind::F128PPC(val),
|
||||
mir::Literal::Char(c) => ConstValueKind::U8(c as u8),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeKind {
|
||||
pub(super) fn get_type(&self, type_vals: &HashMap<CustomTypeKey, TypeValue>) -> Type {
|
||||
match &self {
|
||||
TypeKind::I8 => Type::I8,
|
||||
TypeKind::I16 => Type::I16,
|
||||
TypeKind::I32 => Type::I32,
|
||||
TypeKind::I64 => Type::I64,
|
||||
TypeKind::I128 => Type::I128,
|
||||
TypeKind::U8 => Type::U8,
|
||||
TypeKind::U16 => Type::U16,
|
||||
TypeKind::U32 => Type::U32,
|
||||
TypeKind::U64 => Type::U64,
|
||||
TypeKind::U128 => Type::U128,
|
||||
TypeKind::Bool => Type::Bool,
|
||||
TypeKind::F16 => Type::F16,
|
||||
TypeKind::F16B => Type::F32B,
|
||||
TypeKind::F32 => Type::F32,
|
||||
TypeKind::F64 => Type::F64,
|
||||
TypeKind::F128 => Type::F128,
|
||||
TypeKind::F80 => Type::F80,
|
||||
TypeKind::F128PPC => Type::F128PPC,
|
||||
TypeKind::Char => Type::U8,
|
||||
TypeKind::Array(elem_t, len) => Type::Array(Box::new(elem_t.get_type(type_vals)), *len),
|
||||
TypeKind::Void => Type::Void,
|
||||
TypeKind::Vague(_) => panic!("Tried to compile a vague type!"),
|
||||
TypeKind::CustomType(n) => {
|
||||
let type_val = type_vals.get(n).unwrap().clone();
|
||||
Type::CustomType(type_val)
|
||||
}
|
||||
TypeKind::UserPtr(type_kind) => Type::Ptr(Box::new(type_kind.get_type(type_vals))),
|
||||
TypeKind::CodegenPtr(type_kind) => Type::Ptr(Box::new(type_kind.get_type(type_vals))),
|
||||
TypeKind::Borrow(type_kind, _) => Type::Ptr(Box::new(type_kind.get_type(type_vals))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeKind {
|
||||
pub(super) fn get_debug_type(&self, debug: &Debug, scope: &Scope) -> DebugTypeValue {
|
||||
self.get_debug_type_hard(
|
||||
&debug.scope,
|
||||
debug.info,
|
||||
debug.types,
|
||||
scope.type_map,
|
||||
scope.module_id,
|
||||
scope.tokens,
|
||||
scope.modules,
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn get_debug_type_hard(
|
||||
&self,
|
||||
scope: &DebugScopeValue,
|
||||
debug_info: &DebugInformation,
|
||||
debug_types: &HashMap<TypeKind, DebugTypeValue>,
|
||||
type_map: &HashMap<CustomTypeKey, TypeDefinition>,
|
||||
local_mod: SourceModuleId,
|
||||
tokens: &Vec<FullToken>,
|
||||
modules: &HashMap<SourceModuleId, &mir::Module>,
|
||||
) -> DebugTypeValue {
|
||||
if let Some(ty) = debug_types.get(self) {
|
||||
return *ty;
|
||||
}
|
||||
|
||||
let name = format!("{}", self);
|
||||
|
||||
let data = match self {
|
||||
TypeKind::CodegenPtr(inner) | TypeKind::UserPtr(inner) | TypeKind::Borrow(inner, _) => {
|
||||
DebugTypeData::Pointer(DebugPointerType {
|
||||
name,
|
||||
pointee: inner.get_debug_type_hard(
|
||||
scope,
|
||||
debug_info,
|
||||
debug_types,
|
||||
type_map,
|
||||
local_mod,
|
||||
tokens,
|
||||
modules,
|
||||
),
|
||||
size_bits: self.size_of(type_map),
|
||||
})
|
||||
}
|
||||
TypeKind::Array(elem_ty, len) => {
|
||||
let elem_ty = elem_ty.clone().get_debug_type_hard(
|
||||
scope,
|
||||
debug_info,
|
||||
debug_types,
|
||||
type_map,
|
||||
local_mod,
|
||||
tokens,
|
||||
modules,
|
||||
);
|
||||
DebugTypeData::Array(DebugArrayType {
|
||||
size_bits: self.size_of(type_map),
|
||||
align_bits: self.alignment(),
|
||||
element_type: elem_ty,
|
||||
length: *len,
|
||||
})
|
||||
}
|
||||
TypeKind::CustomType(key) => {
|
||||
let typedef = type_map.get(key).unwrap();
|
||||
match &typedef.kind {
|
||||
TypeDefinitionKind::Struct(struct_type) => {
|
||||
let mut fields = Vec::new();
|
||||
let mut size_bits = 0;
|
||||
|
||||
for field in &struct_type.0 {
|
||||
let location = if typedef.source_module != local_mod {
|
||||
None
|
||||
} else {
|
||||
field.2.into_debug(&tokens, &scope)
|
||||
};
|
||||
fields.push(DebugFieldType {
|
||||
name: field.0.clone(),
|
||||
scope: scope.clone(),
|
||||
pos: location.map(|l| l.pos),
|
||||
size_bits: field.1.size_of(type_map),
|
||||
offset: size_bits,
|
||||
flags: DwarfFlags,
|
||||
ty: field.1.get_debug_type_hard(
|
||||
scope,
|
||||
debug_info,
|
||||
debug_types,
|
||||
type_map,
|
||||
local_mod,
|
||||
tokens,
|
||||
modules,
|
||||
),
|
||||
});
|
||||
size_bits += field.1.size_of(type_map);
|
||||
}
|
||||
{
|
||||
let location = if typedef.source_module != local_mod {
|
||||
None
|
||||
} else {
|
||||
typedef.meta.into_debug(&tokens, scope)
|
||||
};
|
||||
DebugTypeData::Struct(DebugStructType {
|
||||
name: key.0.clone(),
|
||||
scope: scope.clone(),
|
||||
pos: location.map(|l| l.pos),
|
||||
size_bits,
|
||||
flags: DwarfFlags,
|
||||
fields,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => DebugTypeData::Basic(DebugBasicType {
|
||||
name,
|
||||
size_bits: self.size_of(type_map),
|
||||
encoding: match self {
|
||||
TypeKind::Bool => DwarfEncoding::Boolean,
|
||||
TypeKind::I8 => DwarfEncoding::SignedChar,
|
||||
TypeKind::U8 => DwarfEncoding::UnsignedChar,
|
||||
TypeKind::I16 | TypeKind::I32 | TypeKind::I64 | TypeKind::I128 => DwarfEncoding::Signed,
|
||||
TypeKind::U16 | TypeKind::U32 | TypeKind::U64 | TypeKind::U128 => DwarfEncoding::Unsigned,
|
||||
TypeKind::F16
|
||||
| TypeKind::F32
|
||||
| TypeKind::F16B
|
||||
| TypeKind::F64
|
||||
| TypeKind::F80
|
||||
| TypeKind::F128
|
||||
| TypeKind::F128PPC => DwarfEncoding::Float,
|
||||
TypeKind::Void => DwarfEncoding::Address,
|
||||
TypeKind::Char => DwarfEncoding::UnsignedChar,
|
||||
TypeKind::Array(_, _) => DwarfEncoding::Address,
|
||||
TypeKind::CustomType(_) => DwarfEncoding::Address,
|
||||
_ => panic!("tried fetching debug-type for non-supported type!"),
|
||||
},
|
||||
flags: DwarfFlags,
|
||||
}),
|
||||
};
|
||||
debug_info.debug_type(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl Metadata {
|
||||
pub(super) fn into_debug(&self, tokens: &Vec<FullToken>, scope: &DebugScopeValue) -> Option<DebugLocation> {
|
||||
if let Some((start, _)) = self.into_positions(tokens) {
|
||||
Some(start.debug(scope.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Position {
|
||||
pub(super) fn debug(self, scope: DebugScopeValue) -> DebugLocation {
|
||||
DebugLocation {
|
||||
pos: DebugPosition {
|
||||
line: self.1,
|
||||
column: self.0,
|
||||
},
|
||||
scope,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,377 +0,0 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt::{Debug, Write},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
ast::token_stream::{self, TokenRange},
|
||||
codegen,
|
||||
lexer::{self, Cursor, FullToken, Position},
|
||||
mir::{self, macros, pass, typecheck, Metadata, SourceModuleId},
|
||||
};
|
||||
|
||||
use crate::mir::typecheck::ErrorKind as TypecheckError;
|
||||
|
||||
fn label(text: &str) -> &str {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
return text;
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
""
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, PartialEq)]
|
||||
pub enum ErrorKind {
|
||||
#[error("{}{}", label("(Lexing) "), .0.kind)]
|
||||
LexerError(#[from] mir::pass::Error<lexer::Error>),
|
||||
#[error("{}{}", label("(Parsing) "), .0.kind)]
|
||||
ParserError(#[from] mir::pass::Error<token_stream::Error>),
|
||||
#[error("{}{}", label("(TypeCheck) "), .0.kind)]
|
||||
TypeCheckError(#[source] mir::pass::Error<typecheck::ErrorKind>),
|
||||
#[error("{}{}", label("(TypeInference) "), .0.kind)]
|
||||
TypeInferenceError(#[source] mir::pass::Error<TypecheckError>),
|
||||
#[error("{}{}", label("(Linker) "), .0.kind)]
|
||||
LinkerError(#[from] mir::pass::Error<mir::linker::ErrorKind>),
|
||||
#[error("{}{}", label("(Macro) "), .0)]
|
||||
MacroError(#[from] mir::pass::Error<macros::ErrorKind>),
|
||||
#[error("{}{}", label("(Codegen) "), .0)]
|
||||
CodegenError(#[from] codegen::ErrorKind),
|
||||
}
|
||||
|
||||
impl ErrorKind {
|
||||
pub fn from_typecheck(err: mir::pass::Error<typecheck::ErrorKind>) -> ErrorKind {
|
||||
ErrorKind::TypeCheckError(err)
|
||||
}
|
||||
|
||||
pub fn from_typeinference(err: mir::pass::Error<typecheck::ErrorKind>) -> ErrorKind {
|
||||
ErrorKind::TypeInferenceError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl ErrorKind {
|
||||
pub fn get_meta(&self) -> Metadata {
|
||||
match &self {
|
||||
ErrorKind::LexerError(error) => error.metadata,
|
||||
ErrorKind::ParserError(error) => error.metadata,
|
||||
ErrorKind::TypeCheckError(error) => error.metadata,
|
||||
ErrorKind::TypeInferenceError(error) => error.metadata,
|
||||
ErrorKind::LinkerError(error) => error.metadata,
|
||||
ErrorKind::CodegenError(error) => match error {
|
||||
codegen::ErrorKind::Null => Default::default(),
|
||||
},
|
||||
ErrorKind::MacroError(error) => error.metadata,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_type_str(&self) -> &str {
|
||||
match self {
|
||||
ErrorKind::LexerError(_) => "lexer",
|
||||
ErrorKind::ParserError(_) => "parser",
|
||||
ErrorKind::TypeCheckError(_) => "typechecker",
|
||||
ErrorKind::TypeInferenceError(_) => "type-inferrer",
|
||||
ErrorKind::LinkerError(_) => "linker",
|
||||
ErrorKind::MacroError(_) => "macro-pass",
|
||||
ErrorKind::CodegenError(_) => "codegen",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for ErrorKind {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
self.get_meta()
|
||||
.source_module_id
|
||||
.partial_cmp(&other.get_meta().source_module_id)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ErrorModule {
|
||||
pub name: String,
|
||||
pub tokens: Option<Vec<FullToken>>,
|
||||
pub source: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub struct ErrorModules {
|
||||
pub(super) module_map: HashMap<mir::SourceModuleId, ErrorModule>,
|
||||
pub(super) source_id_map: HashMap<PathBuf, mir::SourceModuleId>,
|
||||
module_counter: mir::SourceModuleId,
|
||||
}
|
||||
|
||||
impl ErrorModules {
|
||||
pub fn add_module<T: Into<String>>(
|
||||
&mut self,
|
||||
name: T,
|
||||
path: Option<PathBuf>,
|
||||
external_module_id: Option<SourceModuleId>,
|
||||
) -> Option<mir::SourceModuleId> {
|
||||
let module_id = path.as_ref().and_then(|p| self.source_id_map.get(p));
|
||||
|
||||
if let Some(module_id) = module_id {
|
||||
Some(*module_id)
|
||||
} else {
|
||||
let id = if let Some(module_id) = external_module_id {
|
||||
self.module_counter = SourceModuleId(module_id.0.max(self.module_counter.0));
|
||||
if let Some(_) = self.module_map.get(&module_id) {
|
||||
panic!("Can not use external module id: Module already exists!")
|
||||
}
|
||||
module_id
|
||||
} else {
|
||||
self.module_counter.increment()
|
||||
};
|
||||
|
||||
if let Some(path) = path {
|
||||
self.source_id_map.insert(path, id);
|
||||
}
|
||||
|
||||
self.module_map.insert(
|
||||
id,
|
||||
ErrorModule {
|
||||
name: name.into(),
|
||||
tokens: None,
|
||||
source: None,
|
||||
},
|
||||
);
|
||||
Some(id)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_tokens(&mut self, id: mir::SourceModuleId, tokens: Vec<FullToken>) {
|
||||
if let Some(module) = self.module_map.get_mut(&id) {
|
||||
module.tokens = Some(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_source(&mut self, id: mir::SourceModuleId, source: String) {
|
||||
if let Some(module) = self.module_map.get_mut(&id) {
|
||||
module.source = Some(source);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn module(&self, id: &mir::SourceModuleId) -> Option<&ErrorModule> {
|
||||
self.module_map.get(id)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ReidError {
|
||||
map: ErrorModules,
|
||||
pub errors: Vec<ErrorKind>,
|
||||
}
|
||||
|
||||
impl ReidError {
|
||||
pub fn from_lexer<U>(
|
||||
result: Result<U, lexer::Error>,
|
||||
map: ErrorModules,
|
||||
module: SourceModuleId,
|
||||
) -> Result<U, ReidError> {
|
||||
result.map_err(|error| {
|
||||
let pass_err = pass::Error {
|
||||
metadata: Metadata {
|
||||
source_module_id: module,
|
||||
range: Default::default(),
|
||||
position: Some(*error.get_position()),
|
||||
},
|
||||
kind: error,
|
||||
};
|
||||
ReidError {
|
||||
map,
|
||||
errors: vec![ErrorKind::LexerError(pass_err)],
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_parser<U>(
|
||||
result: Result<U, token_stream::Error>,
|
||||
map: ErrorModules,
|
||||
module: SourceModuleId,
|
||||
) -> Result<U, ReidError> {
|
||||
result.map_err(|error| {
|
||||
let pass_err = pass::Error {
|
||||
metadata: Metadata {
|
||||
source_module_id: module,
|
||||
range: *error.get_range().unwrap_or(&Default::default()),
|
||||
position: None,
|
||||
},
|
||||
kind: error,
|
||||
};
|
||||
ReidError {
|
||||
map,
|
||||
errors: vec![ErrorKind::ParserError(pass_err)],
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_kind(errors: Vec<ErrorKind>, map: ErrorModules) -> ReidError {
|
||||
ReidError { map, errors }
|
||||
}
|
||||
|
||||
pub fn extend(&mut self, other: ReidError) {
|
||||
self.errors.extend(other.errors);
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ReidError {}
|
||||
|
||||
impl std::fmt::Display for ReidError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut sorted_errors = self.errors.clone();
|
||||
sorted_errors.sort_by(|a, b| a.partial_cmp(&b).unwrap_or(std::cmp::Ordering::Equal));
|
||||
sorted_errors.dedup();
|
||||
|
||||
let mut curr_module = None;
|
||||
for error in sorted_errors {
|
||||
let meta = error.get_meta();
|
||||
let module = self.map.module(&meta.source_module_id);
|
||||
let position = if let Some(module) = module {
|
||||
if let Some(tokens) = &module.tokens {
|
||||
meta.range.into_position(tokens).or(meta.position.map(|p| (p, p)))
|
||||
} else if let Some(position) = meta.position {
|
||||
Some((position, position))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let module_name = if curr_module != Some(meta.source_module_id) {
|
||||
curr_module = Some(meta.source_module_id);
|
||||
if let Some(module) = self.map.module_map.get(&meta.source_module_id) {
|
||||
module.name.clone()
|
||||
} else {
|
||||
"unknown".to_owned()
|
||||
}
|
||||
} else {
|
||||
"unknown".to_owned()
|
||||
};
|
||||
|
||||
writeln!(f, "Errors detected: {}", color_err(format!("{}", module_name))?)?;
|
||||
|
||||
writeln!(f)?;
|
||||
write!(f, " Error: ")?;
|
||||
writeln!(f, "{}", color_err(format!("{}", error))?)?;
|
||||
write!(
|
||||
f,
|
||||
"{:>20} {}:{}",
|
||||
color_warn("At: ")?,
|
||||
module_name,
|
||||
position.map(|p| fmt_positions(p)).unwrap_or(String::from("{unknown}")),
|
||||
)?;
|
||||
if let (Some(position), Some(source)) = (position, &module.and_then(|m| m.source.clone())) {
|
||||
writeln!(f, "{}", fmt_lines(source, position, 6)?)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenRange {
|
||||
pub fn into_tokens<'v>(&self, tokens: &'v Vec<FullToken>) -> Vec<&'v FullToken> {
|
||||
tokens
|
||||
.iter()
|
||||
.skip(self.start)
|
||||
.by_ref()
|
||||
.take(self.end + 1 - self.start)
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
pub fn into_position<'v>(&self, tokens: &'v Vec<FullToken>) -> Option<(Position, Position)> {
|
||||
let tokens = self.into_tokens(tokens);
|
||||
get_position(&tokens)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_position(tokens: &Vec<&FullToken>) -> Option<(Position, Position)> {
|
||||
if let Some(first) = tokens.first() {
|
||||
let last = tokens.last().unwrap();
|
||||
Some((first.position, last.position.add(last.token.len() as u32)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn into_full_lines<'v>((start, end): (Position, Position)) -> (Position, Position) {
|
||||
(Position(0, start.1), Position(u32::MAX, end.1))
|
||||
}
|
||||
|
||||
fn fmt_lines(
|
||||
source: &String,
|
||||
(highlight_start, highlight_end): (Position, Position),
|
||||
ident: usize,
|
||||
) -> Result<String, std::fmt::Error> {
|
||||
let (line_start, line_end) = into_full_lines((highlight_start, highlight_end));
|
||||
let mut cursor = Cursor {
|
||||
position: Position(0, 1),
|
||||
char_stream: source.chars(),
|
||||
};
|
||||
|
||||
let mut text = String::new();
|
||||
|
||||
while let Some(c) = cursor.next() {
|
||||
if cursor.position.1 > line_end.1 {
|
||||
break;
|
||||
}
|
||||
if cursor.position.1 >= line_start.1 {
|
||||
if c == '\n' {
|
||||
write!(text, "\n{}", " ".repeat(ident))?;
|
||||
} else {
|
||||
if cursor.position > highlight_start && cursor.position <= highlight_end {
|
||||
write!(text, "{}", color_highlight(c)?)?;
|
||||
} else {
|
||||
text.write_char(c)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(text)
|
||||
}
|
||||
|
||||
fn fmt_positions((start, end): (Position, Position)) -> String {
|
||||
if start == end {
|
||||
format!("{}:{}", start.1, start.0)
|
||||
} else if start.1 == end.1 {
|
||||
format!("{}:{} - {}", start.1, start.0, end.0)
|
||||
} else {
|
||||
format!("{}:{} - {}:{}", start.1, start.0, end.1, end.0)
|
||||
}
|
||||
}
|
||||
|
||||
fn color_err(elem: impl std::fmt::Display) -> Result<String, std::fmt::Error> {
|
||||
let mut text = format!("{}", elem);
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
{
|
||||
use colored::Colorize;
|
||||
text = format!("{}", text.bright_red())
|
||||
}
|
||||
|
||||
Ok(text)
|
||||
}
|
||||
|
||||
fn color_warn(elem: impl std::fmt::Display) -> Result<String, std::fmt::Error> {
|
||||
let mut text = format!("{}", elem);
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
{
|
||||
use colored::Colorize;
|
||||
text = format!("{}", text.bright_yellow())
|
||||
}
|
||||
|
||||
Ok(text)
|
||||
}
|
||||
|
||||
fn color_highlight(elem: impl std::fmt::Display) -> Result<String, std::fmt::Error> {
|
||||
let mut text = format!("{}", elem);
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
{
|
||||
use colored::Colorize;
|
||||
text = format!("{}", text.bright_yellow().underline())
|
||||
}
|
||||
|
||||
Ok(text)
|
||||
}
|
118
reid/src/ld.rs
118
reid/src/ld.rs
@ -1,118 +0,0 @@
|
||||
use std::{path::PathBuf, process::Command, thread, time::Duration};
|
||||
|
||||
pub struct LDRunner {
|
||||
command: String,
|
||||
dynamic_linker: String,
|
||||
libraries: Vec<String>,
|
||||
}
|
||||
|
||||
impl LDRunner {
|
||||
pub fn from_command(command: &str) -> LDRunner {
|
||||
LDRunner {
|
||||
command: command.to_owned(),
|
||||
dynamic_linker: "ld-linux-x86-64.so.2".to_string(),
|
||||
libraries: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_library(mut self, lib: &str) -> LDRunner {
|
||||
self.libraries.push(lib.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn invoke(&self, input: &PathBuf, out_path: &PathBuf) {
|
||||
let input_path = input.canonicalize().unwrap();
|
||||
|
||||
let dyn_linker_path = find_objectfile(&self.dynamic_linker);
|
||||
let crt1_path = find_objectfile("crt1.o");
|
||||
let crti_path = find_objectfile("crti.o");
|
||||
let crtn_path = find_objectfile("crtn.o");
|
||||
|
||||
log::debug!("LDRunner: Using dynamic linker at: {:?}", dyn_linker_path);
|
||||
|
||||
let mut ld = Command::new(&self.command);
|
||||
ld.arg("-dynamic-linker")
|
||||
.arg(dyn_linker_path)
|
||||
.arg(crt1_path)
|
||||
.arg(crti_path);
|
||||
|
||||
for library in &self.libraries {
|
||||
ld.arg(format!("-l{}", library));
|
||||
}
|
||||
ld.arg(crtn_path);
|
||||
|
||||
ld.arg(input_path.to_str().unwrap())
|
||||
.arg("-o")
|
||||
.arg(out_path.to_str().unwrap());
|
||||
|
||||
log::debug!("{:#?}", ld);
|
||||
|
||||
log::debug!(
|
||||
"LDRunner: Executing linker to objfile at {:?} => {:?}",
|
||||
input_path,
|
||||
out_path
|
||||
);
|
||||
|
||||
let ld_output = ld.output().expect("Unable to execute ld!");
|
||||
if !ld_output.status.success() {
|
||||
let code = ld_output.status.code().unwrap_or(255);
|
||||
log::error!("LD exited with code {code}");
|
||||
println!("{}", unsafe { String::from_utf8_unchecked(ld_output.stderr) });
|
||||
return;
|
||||
}
|
||||
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
|
||||
log::debug!("Setting executable bit to {:?}..", out_path);
|
||||
|
||||
let chmod_output = Command::new("chmod")
|
||||
.arg("+x")
|
||||
.arg(out_path)
|
||||
.output()
|
||||
.expect("Unable to execute ld!");
|
||||
|
||||
if !chmod_output.status.success() {
|
||||
let code = chmod_output.status.code().unwrap_or(255);
|
||||
log::error!("chmod exited with code {code}");
|
||||
println!("{}", unsafe { String::from_utf8_unchecked(chmod_output.stderr) });
|
||||
return;
|
||||
}
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
|
||||
fn find_objectfile(name: &str) -> String {
|
||||
let whereis = Command::new("whereis")
|
||||
.arg(&name)
|
||||
.output()
|
||||
.expect("Unable to execute whereis");
|
||||
if !whereis.status.success() {
|
||||
let code = whereis.status.code().unwrap_or(255);
|
||||
log::error!("whereis exited with code {code}");
|
||||
println!("{}", unsafe { String::from_utf8_unchecked(whereis.stderr) });
|
||||
panic!();
|
||||
}
|
||||
let whereis_output = String::from_utf8(whereis.stdout).unwrap();
|
||||
|
||||
whereis_output
|
||||
.split(" ")
|
||||
.skip(1)
|
||||
.next()
|
||||
.expect(&format!("Unable to find {}: {}", name, whereis_output))
|
||||
.split("\n")
|
||||
.next()
|
||||
.unwrap()
|
||||
.to_owned()
|
||||
}
|
||||
|
||||
pub fn execute(path: &PathBuf) -> Option<i32> {
|
||||
let output = Command::new(path.clone()).output().expect("Unable to execute {path}");
|
||||
|
||||
if !output.status.success() {
|
||||
let code = output.status.code().unwrap_or(255);
|
||||
log::error!("{path:?} exited with code {code}");
|
||||
println!("{}", unsafe { String::from_utf8_unchecked(output.stderr) });
|
||||
}
|
||||
|
||||
output.status.code()
|
||||
}
|
349
reid/src/lib.rs
349
reid/src/lib.rs
@ -1,349 +0,0 @@
|
||||
//! Reid is a toy-language compiler I'm working on to learn LLVM (and compiler
|
||||
//! development).
|
||||
//!
|
||||
//! Reid only uses [llvm-sys](https://gitlab.com/taricorp/llvm-sys.rs), which
|
||||
//! provide very minimal bindings from the LLVM C-API to Rust. `reid_llvm`-crate
|
||||
//! contains the relevant abstraction to produce a more Rust'y API from that.
|
||||
//!
|
||||
//! Much of the syntax in Reid is directly inspired by rust, but mostly it is
|
||||
//! driven by simplicity.
|
||||
//!
|
||||
//! Specifications and a bunch of [documentation for the language can be found
|
||||
//! here](./documentation/).
|
||||
//!
|
||||
//! An example of a real whole program (a CPU pathtracer) can be found [in
|
||||
//! examples/cpu_raytracer.reid](./examples/cpu_raytracer.reid), go have a look!
|
||||
//!
|
||||
//! Reid is currently able to (non-exhaustively):
|
||||
//! - Do basic algebra binary and unary-operations (e.g. Add, Sub, Div, Mult,
|
||||
//! And, Not)
|
||||
//! - Resolve complex one-liners correctly using PEDMAS (e.g. `5 + 2 * 5 - 5 *
|
||||
//! 5` is calculated correctly)
|
||||
//! - Handle borrows/derefs, pointers.
|
||||
//! - Declare and call functions with varying parameters and return types
|
||||
//! - Perform type-checking and type-inference such that return-types and
|
||||
//! parameter types must always match.
|
||||
//! - Do simple logic-operations (e.g. If/And/Or)
|
||||
//! - Handle, access, define and initialize structs and arrays.
|
||||
//! - Define and execute For/While loops
|
||||
//! - Output detailed debug information
|
||||
//! - Define extern functions that can be linked to outside modules such as
|
||||
//! `libc`.
|
||||
//! - Define custom binary operations for any two types that hasn't been defined
|
||||
//! previously (such as `u16 + u32`).
|
||||
//!
|
||||
//!
|
||||
//! An example program of Reid, that calculates the 5th fibonacci number:
|
||||
//! ```reid
|
||||
//! fn main() -> u16 {
|
||||
//! return fibonacci(5);
|
||||
//! }
|
||||
//! fn fibonacci(n: u16) -> u16 {
|
||||
//! if n <= 2 {
|
||||
//! return 1;
|
||||
//! }
|
||||
//! return fibonacci(n-1) + fibonacci(n-2);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! TODOs still (see README.md for more)
|
||||
//! - Error handling
|
||||
//! - Lexing & parsing of whitespace and comments as well
|
||||
//! - LSP implementation
|
||||
//! ```
|
||||
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
||||
use ast::{
|
||||
lexer::{self, FullToken, Token},
|
||||
token_stream::TokenStream,
|
||||
};
|
||||
use codegen::intrinsics::{form_intrinsic_binops, form_intrinsics};
|
||||
use error_raporting::{ErrorKind as ErrorRapKind, ErrorModules, ReidError};
|
||||
use mir::{
|
||||
linker::LinkerPass,
|
||||
pass::BinopMap,
|
||||
typecheck::{typecheck::TypeCheck, typeinference::TypeInference, typerefs::TypeRefs},
|
||||
};
|
||||
use reid_lib::{compile::CompileOutput, Context};
|
||||
|
||||
use crate::{
|
||||
ast::TopLevelStatement,
|
||||
mir::{
|
||||
macros::{form_macros, MacroModule, MacroPass},
|
||||
SourceModuleId,
|
||||
},
|
||||
};
|
||||
|
||||
pub mod ast;
|
||||
pub mod codegen;
|
||||
pub mod error_raporting;
|
||||
pub mod ld;
|
||||
pub mod mir;
|
||||
mod pad_adapter;
|
||||
mod util;
|
||||
|
||||
pub fn parse_module<'map, T: Into<String>>(
|
||||
source: &str,
|
||||
name: T,
|
||||
path: Option<PathBuf>,
|
||||
map: &'map mut ErrorModules,
|
||||
module_id: Option<SourceModuleId>,
|
||||
) -> Result<(mir::SourceModuleId, Vec<FullToken>), ReidError> {
|
||||
let id = map.add_module(name.into(), path, module_id).unwrap();
|
||||
map.set_source(id, source.to_owned());
|
||||
|
||||
let tokens = ReidError::from_lexer(lexer::tokenize(source), map.clone(), id)?;
|
||||
|
||||
map.set_tokens(id, tokens.clone());
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("{:#?}", &tokens);
|
||||
|
||||
Ok((id, tokens))
|
||||
}
|
||||
|
||||
pub fn compile_module<'map>(
|
||||
module_id: mir::SourceModuleId,
|
||||
tokens: Vec<FullToken>,
|
||||
map: &'map mut ErrorModules,
|
||||
path: Option<PathBuf>,
|
||||
is_main: bool,
|
||||
) -> Result<Result<mir::Module, (ast::Module, ReidError)>, ReidError> {
|
||||
let module = map.module(&module_id).cloned().unwrap();
|
||||
|
||||
let mut token_stream = TokenStream::from(&tokens);
|
||||
|
||||
let mut statements = Vec::new();
|
||||
|
||||
while !matches!(token_stream.peek().unwrap_or(Token::Eof), Token::Eof) {
|
||||
let statement = ReidError::from_parser(token_stream.parse::<TopLevelStatement>(), map.clone(), module_id)?;
|
||||
statements.push(statement);
|
||||
}
|
||||
|
||||
let errors = token_stream.errors();
|
||||
|
||||
drop(token_stream);
|
||||
|
||||
let ast_module = ast::Module {
|
||||
name: module.name,
|
||||
tokens: tokens,
|
||||
top_level_statements: statements,
|
||||
path,
|
||||
is_main,
|
||||
};
|
||||
|
||||
if errors.len() > 0 {
|
||||
// dbg!(&ast_module);
|
||||
return Ok(Err((
|
||||
ast_module,
|
||||
ReidError::from_kind(
|
||||
errors
|
||||
.into_iter()
|
||||
.map(|e| {
|
||||
error_raporting::ErrorKind::from(mir::pass::Error {
|
||||
metadata: mir::Metadata {
|
||||
source_module_id: module_id,
|
||||
range: *e.get_range().unwrap_or(&Default::default()),
|
||||
position: None,
|
||||
},
|
||||
kind: e,
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
map.clone(),
|
||||
),
|
||||
)));
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("{:#?}", &ast_module);
|
||||
|
||||
Ok(Ok(ast_module.process(module_id)))
|
||||
}
|
||||
|
||||
pub fn perform_all_passes<'map>(
|
||||
context: &mut mir::Context,
|
||||
module_map: &'map mut ErrorModules,
|
||||
) -> Result<(), ReidError> {
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("{:#?}", &context);
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("{:#}", &context);
|
||||
|
||||
let state = context.pass(&mut LinkerPass {
|
||||
module_map,
|
||||
is_lib: true,
|
||||
})?;
|
||||
|
||||
for module in &mut context.modules {
|
||||
for intrinsic in form_intrinsics() {
|
||||
module.1.functions.insert(0, intrinsic);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("{:-^100}", "LINKER OUTPUT");
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("{:#}", &context);
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("{:#?}", &state);
|
||||
|
||||
if !state.errors.is_empty() {
|
||||
return Err(ReidError::from_kind(
|
||||
state.errors.iter().map(|e| e.clone().into()).collect(),
|
||||
module_map.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut macro_modules: HashMap<_, MacroModule> = HashMap::new();
|
||||
for (k, v) in &context.modules {
|
||||
macro_modules.insert(k.clone(), v.into());
|
||||
}
|
||||
|
||||
let mut macro_pass = MacroPass {
|
||||
macros: form_macros(),
|
||||
module_map: macro_modules,
|
||||
};
|
||||
let state = context.pass(&mut macro_pass)?;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("{:-^100}", "MACRO OUTPUT");
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("{:#}", &context);
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("{:#?}", &state);
|
||||
|
||||
if !state.errors.is_empty() {
|
||||
return Err(ReidError::from_kind(
|
||||
state.errors.iter().map(|e| e.clone().into()).collect(),
|
||||
module_map.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut binops = BinopMap::default();
|
||||
for module in &mut context.modules {
|
||||
for intrinsic in form_intrinsic_binops() {
|
||||
binops
|
||||
.set(
|
||||
mir::pass::BinopKey {
|
||||
params: (intrinsic.lhs.ty.clone(), intrinsic.rhs.ty.clone()),
|
||||
operator: intrinsic.op,
|
||||
},
|
||||
mir::pass::ScopeBinopDef {
|
||||
hands: (intrinsic.lhs.ty.clone(), intrinsic.rhs.ty.clone()),
|
||||
operator: intrinsic.op,
|
||||
return_ty: intrinsic.return_type.clone(),
|
||||
},
|
||||
)
|
||||
.ok();
|
||||
module.1.binop_defs.insert(0, intrinsic);
|
||||
}
|
||||
}
|
||||
|
||||
let mut refs = TypeRefs::with_binops(binops);
|
||||
|
||||
let state = context.pass(&mut TypeInference { refs: &mut refs })?;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("{:-^70}", "TYPE INFERRER OUTPUT");
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("{}", &refs);
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("{:#}", &context);
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("{:#?}", &state);
|
||||
|
||||
if !state.errors.is_empty() {
|
||||
return Err(ReidError::from_kind(
|
||||
state
|
||||
.errors
|
||||
.iter()
|
||||
.map(|e| ErrorRapKind::TypeInferenceError(e.clone()))
|
||||
.collect::<Vec<_>>(),
|
||||
module_map.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
let state = context.pass(&mut TypeCheck { refs: &refs })?;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("{:-^100}", "TYPECHECKER OUTPUT");
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("{:#}", &context);
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("{:#?}", &state);
|
||||
|
||||
if !state.errors.is_empty() {
|
||||
return Err(ReidError::from_kind(
|
||||
state
|
||||
.errors
|
||||
.iter()
|
||||
.map(|e| ErrorRapKind::TypeCheckError(e.clone()))
|
||||
.collect::<Vec<_>>(),
|
||||
module_map.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Takes in a bit of source code, parses and compiles it and produces `hello.o`
|
||||
/// and `hello.asm` from it, which can be linked using `ld` to produce an
|
||||
/// executable file.
|
||||
pub fn compile_and_pass<'map>(
|
||||
source: &str,
|
||||
path: PathBuf,
|
||||
module_map: &'map mut ErrorModules,
|
||||
cpu: Option<String>,
|
||||
features: Vec<String>,
|
||||
) -> Result<(CompileOutput, CustomIRs), ReidError> {
|
||||
let path = path.canonicalize().unwrap();
|
||||
let name = path.file_name().unwrap().to_str().unwrap().to_owned();
|
||||
|
||||
let (id, tokens) = parse_module(source, name, Some(path.clone()), module_map, None)?;
|
||||
let module = compile_module(id, tokens, module_map, Some(path.clone()), true)?.map_err(|(_, e)| e)?;
|
||||
|
||||
let mut mir_context = mir::Context::from(vec![module], path.parent().unwrap().to_owned());
|
||||
|
||||
perform_all_passes(&mut mir_context, module_map)?;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("{:-^100}", "FINAL OUTPUT");
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("{:#}", &mir_context);
|
||||
|
||||
let mut context = Context::new(format!("Reid ({})", env!("CARGO_PKG_VERSION")));
|
||||
let codegen_modules = match mir_context.codegen(&mut context) {
|
||||
Ok(modules) => modules,
|
||||
Err(e) => Err(ReidError::from_kind(vec![e.into()], module_map.clone()))?,
|
||||
};
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("{}", &codegen_modules.context);
|
||||
|
||||
let compiled = codegen_modules.compile(cpu, features);
|
||||
Ok((
|
||||
compiled.output(),
|
||||
CustomIRs {
|
||||
llir: format!("{}", codegen_modules.context),
|
||||
mir: format!("{}", mir_context),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
pub struct CustomIRs {
|
||||
pub llir: String,
|
||||
pub mir: String,
|
||||
}
|
||||
|
||||
pub fn compile_simple(
|
||||
source: &str,
|
||||
path: PathBuf,
|
||||
cpu: Option<String>,
|
||||
features: Vec<String>,
|
||||
) -> Result<(CompileOutput, CustomIRs), ReidError> {
|
||||
let mut map = ErrorModules::default();
|
||||
compile_and_pass(source, path, &mut map, cpu, features)
|
||||
}
|
149
reid/src/main.rs
149
reid/src/main.rs
@ -1,149 +0,0 @@
|
||||
use std::{fs, path::PathBuf, process};
|
||||
|
||||
use argh::FromArgs;
|
||||
use log::*;
|
||||
use reid::{
|
||||
compile_simple,
|
||||
ld::{execute, LDRunner},
|
||||
CustomIRs,
|
||||
};
|
||||
use reid_lib::compile::CompileOutput;
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
/// Compile or run a Reid (.reid) file
|
||||
#[argh(help_triggers("-h", "--help", "help"))]
|
||||
struct Options {
|
||||
#[argh(option, short = 'l', default = "log::Level::Info")]
|
||||
/// log level
|
||||
log_level: log::Level,
|
||||
#[argh(switch, short = 't')]
|
||||
/// should logs be timestamped
|
||||
timestamps: bool,
|
||||
#[argh(subcommand)]
|
||||
command: Command,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand)]
|
||||
enum Command {
|
||||
Build(BuildOpts),
|
||||
Run(RunOpts),
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
/// Build an executable file without running it
|
||||
#[argh(subcommand, name = "build")]
|
||||
struct BuildOpts {
|
||||
#[argh(option, long = "lib", short = 'l')]
|
||||
/// additional libraries to link against (with ld)
|
||||
libraries: Vec<String>,
|
||||
#[argh(positional)]
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
/// Build and then execute the executable
|
||||
#[argh(subcommand, name = "run")]
|
||||
struct RunOpts {
|
||||
#[argh(option, long = "lib", short = 'l')]
|
||||
/// additional libraries to link against (with ld)
|
||||
libraries: Vec<String>,
|
||||
#[argh(positional)]
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let options: Options = argh::from_env();
|
||||
let mut errlog = stderrlog::new();
|
||||
errlog.module(module_path!()).verbosity(options.log_level);
|
||||
if options.timestamps {
|
||||
errlog.timestamp(stderrlog::Timestamp::Second);
|
||||
}
|
||||
errlog.init().unwrap();
|
||||
|
||||
match &options.command {
|
||||
Command::Build(BuildOpts { libraries, path }) | Command::Run(RunOpts { libraries, path }) => {
|
||||
let cpu = std::env::var("CPU").unwrap_or("generic".to_owned());
|
||||
let features = std::env::var("REIDFLAGS").unwrap_or("".to_owned());
|
||||
|
||||
let path = match path.canonicalize() {
|
||||
Ok(path) => path,
|
||||
Err(e) => {
|
||||
error!("{e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let parent = path.with_extension("");
|
||||
let llvm_ir_path = parent.with_extension("ll");
|
||||
let object_path = parent.with_extension("o");
|
||||
let llir_path = parent.with_extension("llir");
|
||||
let mir_path = parent.with_extension("mir");
|
||||
let asm_path = parent.with_extension("asm");
|
||||
let out_path = object_path.with_extension("out");
|
||||
|
||||
let before = std::time::SystemTime::now();
|
||||
|
||||
let text = match fs::read_to_string(&path) {
|
||||
Ok(text) => text,
|
||||
Err(e) => {
|
||||
error!("Could not read file: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match compile_simple(&text, PathBuf::from(&path), Some(cpu), vec![features]) {
|
||||
Ok((
|
||||
CompileOutput {
|
||||
triple: _triple,
|
||||
assembly,
|
||||
obj_buffer,
|
||||
llvm_ir: _llvm_ir,
|
||||
},
|
||||
CustomIRs { llir, mir },
|
||||
)) => {
|
||||
log::trace!("{}", _llvm_ir);
|
||||
log::debug!("Compiled with triple: {}\n", &_triple);
|
||||
log::debug!("Output LLVM IR to {:?}", llvm_ir_path);
|
||||
log::debug!("Output Assembly to {:?}", asm_path);
|
||||
log::debug!("Output Object-file to {:?}\n", object_path);
|
||||
log::debug!("Output LLIR-file to {:?}\n", llir_path);
|
||||
log::debug!("Output MIR-file to {:?}\n", mir_path);
|
||||
|
||||
fs::write(&llvm_ir_path, &_llvm_ir).expect("Could not write LLVM IR -file!");
|
||||
fs::write(&asm_path, &assembly).expect("Could not write Assembly-file!");
|
||||
fs::write(&object_path, &obj_buffer).expect("Could not write Object-file!");
|
||||
fs::write(&llir_path, &llir).expect("Could not write LLIR-file!");
|
||||
fs::write(&mir_path, &mir).expect("Could not write MIR-file!");
|
||||
let after = std::time::SystemTime::now();
|
||||
log::info!(
|
||||
"Compilation took: {:.2}ms\n",
|
||||
(after.duration_since(before).unwrap().as_micros() as f32) / 1000.
|
||||
);
|
||||
|
||||
log::info!("Linking {:?}", &object_path);
|
||||
|
||||
let linker = std::env::var("LD").unwrap_or("ld".to_owned());
|
||||
let mut linker = LDRunner::from_command(&linker).with_library("c").with_library("m");
|
||||
for library in libraries {
|
||||
linker = linker.with_library(&library);
|
||||
}
|
||||
linker.invoke(&object_path, &out_path);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match &options.command {
|
||||
Command::Build(_) => {}
|
||||
Command::Run(_) => {
|
||||
if let Some(code) = execute(&out_path) {
|
||||
process::exit(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,502 +0,0 @@
|
||||
use std::fmt::{Debug, Display, Write};
|
||||
|
||||
use crate::pad_adapter::PadAdapter;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl Display for Context {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for (_, module) in &self.modules {
|
||||
Display::fmt(&module, f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Module {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let is_alternate = f.alternate();
|
||||
|
||||
writeln!(f, "Module({}) ({}) {{", self.name, self.module_id)?;
|
||||
|
||||
let mut state = Default::default();
|
||||
let mut inner_f = PadAdapter::wrap(f, &mut state);
|
||||
|
||||
for import in &self.imports {
|
||||
writeln!(inner_f, "{}", import)?;
|
||||
}
|
||||
|
||||
let intrinsic_binops = self
|
||||
.binop_defs
|
||||
.iter()
|
||||
.filter(|b| matches!(b.fn_kind, FunctionDefinitionKind::Intrinsic(_)));
|
||||
|
||||
for binop in self
|
||||
.binop_defs
|
||||
.iter()
|
||||
.filter(|b| !matches!(b.fn_kind, FunctionDefinitionKind::Intrinsic(_)))
|
||||
{
|
||||
writeln!(inner_f, "{}", binop)?;
|
||||
}
|
||||
|
||||
if is_alternate {
|
||||
writeln!(inner_f, "... <{}> intrinsic binary operators", intrinsic_binops.count())?;
|
||||
} else {
|
||||
for binop in intrinsic_binops {
|
||||
writeln!(inner_f, "{}", binop)?;
|
||||
}
|
||||
}
|
||||
for typedef in &self.typedefs {
|
||||
writeln!(inner_f, "{}", typedef)?;
|
||||
}
|
||||
for global in &self.globals {
|
||||
writeln!(inner_f, "global {} = {}", global.name, global.kind)?;
|
||||
}
|
||||
for (ty, fun) in &self.associated_functions {
|
||||
writeln!(inner_f, "(Assoc {}) {}", ty, fun)?;
|
||||
}
|
||||
for fun in &self.functions {
|
||||
writeln!(inner_f, "{}", fun)?;
|
||||
}
|
||||
writeln!(f, "}}")
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for GlobalKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
GlobalKind::Literal(literal) => Display::fmt(literal, f),
|
||||
GlobalKind::Array(global_kinds) => {
|
||||
f.write_char('[')?;
|
||||
let mut iter = global_kinds.iter();
|
||||
if let Some(global) = iter.next() {
|
||||
Display::fmt(global, f)?;
|
||||
while let Some(global) = iter.next() {
|
||||
write!(f, ", ")?;
|
||||
Display::fmt(global, f)?;
|
||||
}
|
||||
}
|
||||
f.write_char(']')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Import {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"import {}",
|
||||
self.0.iter().map(|(s, _)| s.clone()).collect::<Vec<_>>().join("::")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for BinopDefinition {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}impl binop ({}: {:#}) {} ({}: {:#}) -> {:#} ",
|
||||
if self.exported { "exported " } else { "" },
|
||||
self.lhs.name,
|
||||
self.lhs.ty,
|
||||
self.op,
|
||||
self.rhs.name,
|
||||
self.rhs.ty,
|
||||
self.return_type
|
||||
)?;
|
||||
Display::fmt(&self.fn_kind, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for TypeDefinition {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"type {} (mod {}{}) = ",
|
||||
self.name,
|
||||
self.source_module,
|
||||
if let Some(mod_id) = self.importer {
|
||||
format!("; imported by {}", mod_id)
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
)?;
|
||||
Display::fmt(&self.kind, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for TypeDefinitionKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
TypeDefinitionKind::Struct(items) => {
|
||||
write!(f, "struct ")?;
|
||||
f.write_char('{')?;
|
||||
writeln!(f)?;
|
||||
let mut state = Default::default();
|
||||
let mut inner_f = PadAdapter::wrap(f, &mut state);
|
||||
for field in &items.0 {
|
||||
writeln!(inner_f, "{},", field)?;
|
||||
}
|
||||
f.write_char('}')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for StructField {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}: {:?}", self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for FunctionDefinition {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if let Some(documentation) = &self.documentation {
|
||||
writeln!(f, "/// {}", documentation)?;
|
||||
}
|
||||
write!(
|
||||
f,
|
||||
"{}fn {}({}) -> {:#} ",
|
||||
if self.is_pub { "pub " } else { "" },
|
||||
self.name,
|
||||
self.parameters
|
||||
.iter()
|
||||
.map(|FunctionParam { name, ty, .. }| format!("{}: {:#}", name, ty))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
self.return_type
|
||||
)?;
|
||||
Display::fmt(&self.kind, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for FunctionDefinitionKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
FunctionDefinitionKind::Local(block, _) => {
|
||||
write!(f, "{}", block)?;
|
||||
Ok(())
|
||||
}
|
||||
FunctionDefinitionKind::Extern(true) => write!(f, "<Imported Extern>"),
|
||||
FunctionDefinitionKind::Extern(false) => write!(f, "<Linked Extern>"),
|
||||
FunctionDefinitionKind::Intrinsic(_) => write!(f, "<Intrinsic>"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Block {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "{{")?;
|
||||
let mut state = Default::default();
|
||||
let mut inner_f = PadAdapter::wrap(f, &mut state);
|
||||
for statement in &self.statements {
|
||||
write!(inner_f, "{}", statement)?;
|
||||
}
|
||||
if let Some(ret) = &self.return_expression {
|
||||
let ret_fmt = if let Some(ret) = &ret.1 {
|
||||
format!("{}", ret)
|
||||
} else {
|
||||
String::from("void")
|
||||
};
|
||||
|
||||
match ret.0 {
|
||||
ReturnKind::Hard => writeln!(inner_f, "Return(Hard): {}", ret_fmt),
|
||||
ReturnKind::Soft => writeln!(inner_f, "Return(Soft): {}", ret_fmt),
|
||||
}?;
|
||||
} else {
|
||||
writeln!(inner_f, "No Return")?;
|
||||
}
|
||||
writeln!(f, "}}")
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Statement {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for StmtKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
StmtKind::Let(var, mutable, block) => {
|
||||
write!(f, "let{} {} = {}", if *mutable { " mut" } else { "" }, var, block)
|
||||
}
|
||||
StmtKind::Set(var, expr) => write!(f, "{} = {}", var, expr),
|
||||
StmtKind::Import(n) => write!(f, "import {}", n),
|
||||
StmtKind::Expression(exp) => Display::fmt(exp, f),
|
||||
|
||||
StmtKind::While(while_statement) => {
|
||||
write!(f, "while {} {}", while_statement.condition, while_statement.block,)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Expression {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_char('(')?;
|
||||
Display::fmt(&self.0, f)?;
|
||||
f.write_char(')')?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ExprKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ExprKind::Variable(var) => Display::fmt(var, f),
|
||||
ExprKind::Literal(lit) => Display::fmt(lit, f),
|
||||
ExprKind::BinOp(op, lhs, rhs, ty) => {
|
||||
write!(f, "{} {} {} (= ", lhs, op, rhs)?;
|
||||
Debug::fmt(ty, f)?;
|
||||
f.write_char(')')?;
|
||||
Ok(())
|
||||
}
|
||||
ExprKind::FunctionCall(fc) => Display::fmt(fc, f),
|
||||
ExprKind::If(if_exp) => Display::fmt(&if_exp, f),
|
||||
ExprKind::Block(block) => Display::fmt(block, f),
|
||||
ExprKind::Indexed(expression, elem_ty, idx_expr) => {
|
||||
Display::fmt(&expression, f)?;
|
||||
write!(f, "<{:#}>", elem_ty)?;
|
||||
write_index(f, idx_expr)
|
||||
}
|
||||
ExprKind::Array(expressions) => {
|
||||
f.write_char('[')?;
|
||||
|
||||
let mut state = Default::default();
|
||||
let mut inner_f = PadAdapter::wrap(f, &mut state);
|
||||
|
||||
let mut iter = expressions.iter();
|
||||
if let Some(item) = iter.next() {
|
||||
write!(inner_f, "\n{}", item)?;
|
||||
while let Some(item) = iter.next() {
|
||||
writeln!(inner_f, ",")?;
|
||||
write!(inner_f, "{}", item)?;
|
||||
}
|
||||
writeln!(inner_f, "")?;
|
||||
}
|
||||
f.write_char(']')
|
||||
}
|
||||
ExprKind::Struct(key, items) => {
|
||||
write!(f, "{:?} ", key)?;
|
||||
|
||||
f.write_char('{')?;
|
||||
let mut state = Default::default();
|
||||
let mut inner_f = PadAdapter::wrap(f, &mut state);
|
||||
let mut iter = items.iter();
|
||||
if let Some((name, expr, _)) = iter.next() {
|
||||
write!(inner_f, "\n{}: {}", name, expr)?;
|
||||
while let Some((name, expr, _)) = iter.next() {
|
||||
writeln!(inner_f, ",")?;
|
||||
write!(inner_f, "{}: {}", name, expr)?;
|
||||
}
|
||||
writeln!(inner_f, "")?;
|
||||
}
|
||||
f.write_char('}')
|
||||
}
|
||||
ExprKind::Accessed(expression, type_kind, name, _) => {
|
||||
Display::fmt(&expression, f)?;
|
||||
write_access(f, name)?;
|
||||
write!(f, "<{}>", type_kind)
|
||||
}
|
||||
ExprKind::Borrow(var_ref, false) => write!(f, "&{}", var_ref),
|
||||
ExprKind::Borrow(var_ref, true) => write!(f, "&mut {}", var_ref),
|
||||
ExprKind::Deref(var_ref) => write!(f, "*{}", var_ref),
|
||||
ExprKind::CastTo(expression, type_kind) => write!(f, "{} as {}", expression, type_kind),
|
||||
ExprKind::AssociatedFunctionCall(type_kind, function_call) => {
|
||||
Display::fmt(type_kind, f)?;
|
||||
write!(f, "::")?;
|
||||
Display::fmt(function_call, f)
|
||||
}
|
||||
ExprKind::GlobalRef(global_value, type_kind) => write!(f, "global<{}>(${})", type_kind, global_value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for IfExpression {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "if {} ", self.0)?;
|
||||
Display::fmt(&self.1, f)?;
|
||||
if let Some(e) = self.2.as_ref() {
|
||||
Display::fmt(&e, f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for FunctionCall {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}<{:#}>(", self.name, self.return_type)?;
|
||||
for (i, param) in self.parameters.iter().enumerate() {
|
||||
Display::fmt(param, f)?;
|
||||
if i < (self.parameters.len() - 1) {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for NamedVariableRef {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "v(\"{}\", {:#})", &self.1, &self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Literal {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Literal::I8(val) => write!(f, "{}i8", val),
|
||||
Literal::I16(val) => write!(f, "{}i16", val),
|
||||
Literal::I32(val) => write!(f, "{}i32", val),
|
||||
Literal::I64(val) => write!(f, "{}i64", val),
|
||||
Literal::I128(val) => write!(f, "{}i128", val),
|
||||
Literal::U8(val) => write!(f, "{}u8", val),
|
||||
Literal::U16(val) => write!(f, "{}u16", val),
|
||||
Literal::U32(val) => write!(f, "{}u32", val),
|
||||
Literal::U64(val) => write!(f, "{}u64", val),
|
||||
Literal::U128(val) => write!(f, "{}u128", val),
|
||||
Literal::Bool(val) => write!(f, "{}", val),
|
||||
Literal::String(val) => std::fmt::Debug::fmt(val, f),
|
||||
Literal::Vague(val) => val.fmt(f),
|
||||
Literal::F16(val) => write!(f, "{}f16", val),
|
||||
Literal::F32B(val) => write!(f, "{}f16b", val),
|
||||
Literal::F32(val) => write!(f, "{}f32", val),
|
||||
Literal::F64(val) => write!(f, "{}f64", val),
|
||||
Literal::F80(val) => write!(f, "{}f80", val),
|
||||
Literal::F128(val) => write!(f, "{}f128", val),
|
||||
Literal::F128PPC(val) => write!(f, "{}f128ppc", val),
|
||||
Literal::Char(c) => std::fmt::Debug::fmt(c, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for BinaryOperator {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
BinaryOperator::Add => write!(f, "+"),
|
||||
BinaryOperator::Minus => write!(f, "-"),
|
||||
BinaryOperator::Mult => write!(f, "*"),
|
||||
BinaryOperator::And => write!(f, "&&"),
|
||||
BinaryOperator::Cmp(op) => Display::fmt(op, f),
|
||||
BinaryOperator::Div => write!(f, "/"),
|
||||
BinaryOperator::Mod => write!(f, "%"),
|
||||
BinaryOperator::Or => write!(f, "||"),
|
||||
BinaryOperator::Xor => write!(f, "^"),
|
||||
BinaryOperator::BitOr => write!(f, "|"),
|
||||
BinaryOperator::BitAnd => write!(f, "&"),
|
||||
BinaryOperator::BitshiftRight => write!(f, ">>"),
|
||||
BinaryOperator::BitshiftLeft => write!(f, "<<"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for CmpOperator {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
CmpOperator::LT => write!(f, "<"),
|
||||
CmpOperator::LE => write!(f, "<="),
|
||||
CmpOperator::GT => write!(f, ">"),
|
||||
CmpOperator::GE => write!(f, ">="),
|
||||
CmpOperator::EQ => write!(f, "=="),
|
||||
CmpOperator::NE => write!(f, "!="),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Metadata {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?} ({})", self.range, self.source_module_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SourceModuleId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
fn write_index(f: &mut std::fmt::Formatter<'_>, idx: impl std::fmt::Display) -> std::fmt::Result {
|
||||
f.write_char('[')?;
|
||||
Display::fmt(&idx, f)?;
|
||||
f.write_char(']')
|
||||
}
|
||||
|
||||
fn write_access(f: &mut std::fmt::Formatter<'_>, name: &String) -> std::fmt::Result {
|
||||
f.write_char('.')?;
|
||||
Display::fmt(name, f)
|
||||
}
|
||||
|
||||
impl Display for TypeKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
TypeKind::Bool => write!(f, "bool"),
|
||||
TypeKind::I8 => write!(f, "i8"),
|
||||
TypeKind::I16 => write!(f, "i16"),
|
||||
TypeKind::I32 => write!(f, "i32"),
|
||||
TypeKind::I64 => write!(f, "i64"),
|
||||
TypeKind::I128 => write!(f, "i128"),
|
||||
TypeKind::U8 => write!(f, "u8"),
|
||||
TypeKind::U16 => write!(f, "u16"),
|
||||
TypeKind::U32 => write!(f, "u32"),
|
||||
TypeKind::U64 => write!(f, "u64"),
|
||||
TypeKind::U128 => write!(f, "u128"),
|
||||
TypeKind::Void => write!(f, "void"),
|
||||
TypeKind::Char => write!(f, "char"),
|
||||
TypeKind::Array(type_kind, len) => {
|
||||
f.write_char('[')?;
|
||||
Display::fmt(type_kind, f)?;
|
||||
write!(f, "; ")?;
|
||||
Display::fmt(len, f)?;
|
||||
f.write_char(']')
|
||||
}
|
||||
TypeKind::CustomType(CustomTypeKey(name, mod_id)) => write!(f, "{}@{}", name, mod_id),
|
||||
TypeKind::Borrow(type_kind, false) => {
|
||||
write!(f, "&")?;
|
||||
Display::fmt(type_kind, f)
|
||||
}
|
||||
TypeKind::Borrow(type_kind, true) => {
|
||||
write!(f, "&mut ")?;
|
||||
Display::fmt(type_kind, f)
|
||||
}
|
||||
TypeKind::UserPtr(type_kind) => {
|
||||
write!(f, "*")?;
|
||||
Display::fmt(type_kind, f)
|
||||
}
|
||||
TypeKind::CodegenPtr(type_kind) => {
|
||||
write!(f, "CodegenPtr ")?;
|
||||
Display::fmt(type_kind, f)
|
||||
}
|
||||
TypeKind::Vague(vague_type) => Display::fmt(vague_type, f),
|
||||
TypeKind::F16 => write!(f, "f16"),
|
||||
TypeKind::F16B => write!(f, "f16b"),
|
||||
TypeKind::F32 => write!(f, "f32"),
|
||||
TypeKind::F64 => write!(f, "f64"),
|
||||
TypeKind::F128 => write!(f, "f128"),
|
||||
TypeKind::F80 => write!(f, "f80"),
|
||||
TypeKind::F128PPC => write!(f, "f128ppc"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for VagueType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if f.alternate() {
|
||||
match self {
|
||||
VagueType::Unknown => write!(f, "Unknown"),
|
||||
VagueType::Integer => write!(f, "Number"),
|
||||
VagueType::TypeRef(id) => write!(f, "TypeRef({0})", id),
|
||||
VagueType::Decimal => write!(f, "Decimal"),
|
||||
}
|
||||
} else {
|
||||
match self {
|
||||
VagueType::Unknown => write!(f, "{{unknown}}"),
|
||||
VagueType::Integer => write!(f, "Number"),
|
||||
VagueType::TypeRef(_) => write!(f, "{{unknown}}"),
|
||||
VagueType::Decimal => write!(f, "Decimal"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,684 +0,0 @@
|
||||
use crate::util::maybe;
|
||||
|
||||
use super::{typecheck::typerefs::TypeRefs, *};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ReturnTypeOther {
|
||||
Import(Metadata),
|
||||
Let(Metadata),
|
||||
Set(Metadata),
|
||||
EmptyBlock(Metadata),
|
||||
NoBlockReturn(Metadata),
|
||||
IndexingNonArray(Metadata),
|
||||
DerefNonBorrow(Metadata),
|
||||
Loop,
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, PartialEq, PartialOrd)]
|
||||
pub enum NumValueError {
|
||||
#[error("Cannot divide by zero")]
|
||||
DivideZero,
|
||||
}
|
||||
|
||||
enum BlockReturn<'b> {
|
||||
Early(&'b Statement),
|
||||
Normal(ReturnKind, &'b Option<Box<Expression>>),
|
||||
}
|
||||
|
||||
impl TypeKind {
|
||||
pub fn signed(&self) -> bool {
|
||||
match self {
|
||||
TypeKind::Bool => false,
|
||||
TypeKind::I8 => true,
|
||||
TypeKind::I16 => true,
|
||||
TypeKind::I32 => true,
|
||||
TypeKind::I64 => true,
|
||||
TypeKind::I128 => true,
|
||||
TypeKind::U8 => false,
|
||||
TypeKind::U16 => false,
|
||||
TypeKind::U32 => false,
|
||||
TypeKind::U64 => false,
|
||||
TypeKind::U128 => false,
|
||||
TypeKind::Void => false,
|
||||
TypeKind::Char => false,
|
||||
TypeKind::Array(..) => false,
|
||||
TypeKind::CustomType(..) => false,
|
||||
TypeKind::CodegenPtr(..) => false,
|
||||
TypeKind::Vague(..) => false,
|
||||
TypeKind::Borrow(..) => false,
|
||||
TypeKind::UserPtr(..) => false,
|
||||
TypeKind::F16 => true,
|
||||
TypeKind::F16B => true,
|
||||
TypeKind::F32 => true,
|
||||
TypeKind::F64 => true,
|
||||
TypeKind::F128 => true,
|
||||
TypeKind::F80 => true,
|
||||
TypeKind::F128PPC => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size_of(&self, map: &HashMap<CustomTypeKey, TypeDefinition>) -> u64 {
|
||||
match self {
|
||||
TypeKind::Bool => 1,
|
||||
TypeKind::I8 => 8,
|
||||
TypeKind::U8 => 8,
|
||||
TypeKind::I16 => 16,
|
||||
TypeKind::U16 => 16,
|
||||
TypeKind::I32 => 32,
|
||||
TypeKind::U32 => 32,
|
||||
TypeKind::I64 => 64,
|
||||
TypeKind::U64 => 64,
|
||||
TypeKind::I128 => 128,
|
||||
TypeKind::U128 => 128,
|
||||
TypeKind::Void => 0,
|
||||
TypeKind::Char => 8,
|
||||
TypeKind::Array(type_kind, len) => type_kind.size_of(map) * (*len as u64),
|
||||
TypeKind::CustomType(key) => match &map.get(key) {
|
||||
Some(def) => match &def.kind {
|
||||
TypeDefinitionKind::Struct(struct_type) => {
|
||||
let mut size = 0;
|
||||
for field in &struct_type.0 {
|
||||
size += field.1.size_of(map)
|
||||
}
|
||||
size
|
||||
}
|
||||
},
|
||||
// Easy to recognize default number. Used e.g. when sorting
|
||||
// types by size
|
||||
None => 404,
|
||||
},
|
||||
TypeKind::CodegenPtr(_) => 64,
|
||||
TypeKind::Vague(_) => panic!("Tried to sizeof a vague type!"),
|
||||
TypeKind::Borrow(..) => 64,
|
||||
TypeKind::UserPtr(_) => 64,
|
||||
TypeKind::F16 => 16,
|
||||
TypeKind::F16B => 16,
|
||||
TypeKind::F32 => 32,
|
||||
TypeKind::F64 => 64,
|
||||
TypeKind::F128 => 128,
|
||||
TypeKind::F80 => 80,
|
||||
TypeKind::F128PPC => 128,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn alignment(&self) -> u32 {
|
||||
match self {
|
||||
TypeKind::Bool => 1,
|
||||
TypeKind::I8 => 8,
|
||||
TypeKind::U8 => 8,
|
||||
TypeKind::I16 => 16,
|
||||
TypeKind::U16 => 16,
|
||||
TypeKind::I32 => 32,
|
||||
TypeKind::U32 => 32,
|
||||
TypeKind::I64 => 64,
|
||||
TypeKind::U64 => 64,
|
||||
TypeKind::I128 => 128,
|
||||
TypeKind::U128 => 128,
|
||||
TypeKind::Void => 0,
|
||||
TypeKind::Char => 8,
|
||||
TypeKind::Array(type_kind, _) => type_kind.alignment(),
|
||||
TypeKind::CustomType(..) => 32,
|
||||
TypeKind::CodegenPtr(_) => 64,
|
||||
TypeKind::Vague(_) => panic!("Tried to sizeof a vague type!"),
|
||||
TypeKind::Borrow(_, _) => 64,
|
||||
TypeKind::UserPtr(_) => 64,
|
||||
TypeKind::F16 => 16,
|
||||
TypeKind::F16B => 16,
|
||||
TypeKind::F32 => 32,
|
||||
TypeKind::F64 => 64,
|
||||
TypeKind::F128 => 128,
|
||||
TypeKind::F80 => 80,
|
||||
TypeKind::F128PPC => 128,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_mutable(&self) -> bool {
|
||||
match self {
|
||||
TypeKind::Borrow(_, false) => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn category(&self) -> TypeCategory {
|
||||
match self {
|
||||
TypeKind::I8
|
||||
| TypeKind::I16
|
||||
| TypeKind::I32
|
||||
| TypeKind::I64
|
||||
| TypeKind::I128
|
||||
| TypeKind::U8
|
||||
| TypeKind::U16
|
||||
| TypeKind::U32
|
||||
| TypeKind::U64
|
||||
| TypeKind::U128
|
||||
| TypeKind::Char => TypeCategory::Integer,
|
||||
TypeKind::F16
|
||||
| TypeKind::F16B
|
||||
| TypeKind::F32
|
||||
| TypeKind::F64
|
||||
| TypeKind::F128
|
||||
| TypeKind::F80
|
||||
| TypeKind::F128PPC => TypeCategory::Real,
|
||||
TypeKind::Void => TypeCategory::Other,
|
||||
TypeKind::Bool => TypeCategory::Bool,
|
||||
TypeKind::Array(_, _) => TypeCategory::Other,
|
||||
TypeKind::CustomType(..) => TypeCategory::Other,
|
||||
TypeKind::Borrow(_, _) => TypeCategory::Other,
|
||||
TypeKind::UserPtr(_) => TypeCategory::Other,
|
||||
TypeKind::CodegenPtr(_) => TypeCategory::Other,
|
||||
TypeKind::Vague(vague_type) => match vague_type {
|
||||
VagueType::Unknown => TypeCategory::Other,
|
||||
VagueType::Integer => TypeCategory::Integer,
|
||||
VagueType::Decimal => TypeCategory::Real,
|
||||
VagueType::TypeRef(_) => TypeCategory::TypeRef,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_collapse_two(
|
||||
(lhs1, rhs1): (&TypeKind, &TypeKind),
|
||||
(lhs2, rhs2): (&TypeKind, &TypeKind),
|
||||
) -> Option<(TypeKind, TypeKind)> {
|
||||
if let (Ok(lhs), Ok(rhs)) = (lhs1.narrow_into(&lhs2), rhs1.narrow_into(&rhs2)) {
|
||||
Some((lhs, rhs))
|
||||
} else if let (Ok(lhs), Ok(rhs)) = (lhs1.narrow_into(&rhs2), rhs1.narrow_into(&lhs2)) {
|
||||
Some((rhs, lhs))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unroll_borrow(&self) -> TypeKind {
|
||||
match self {
|
||||
TypeKind::Borrow(type_kind, mut1) => match *type_kind.clone() {
|
||||
TypeKind::Borrow(type_kind, mut2) => match (mut1, mut2) {
|
||||
(false, false) => TypeKind::Borrow(Box::new(*type_kind.clone()), false),
|
||||
_ => TypeKind::Borrow(Box::new(*type_kind.clone()), true),
|
||||
},
|
||||
_ => self.clone(),
|
||||
},
|
||||
_ => self.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for TypeKind {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
use std::cmp::*;
|
||||
|
||||
let category_ord = self.category().partial_cmp(&other.category());
|
||||
|
||||
match category_ord {
|
||||
Some(Ordering::Equal) | None => {
|
||||
if !self.signed() && other.signed() {
|
||||
return Ordering::Less;
|
||||
}
|
||||
if self.signed() && !other.signed() {
|
||||
return Ordering::Greater;
|
||||
}
|
||||
|
||||
let self_size = self.size_of(&HashMap::new());
|
||||
let other_size = other.size_of(&HashMap::new());
|
||||
if self_size == 32 && other_size != 32 {
|
||||
return Ordering::Less;
|
||||
} else if self_size != 32 && other_size == 32 {
|
||||
return Ordering::Greater;
|
||||
}
|
||||
|
||||
self_size.cmp(&self_size)
|
||||
}
|
||||
Some(ord) => ord,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BinaryOperator {
|
||||
pub fn is_commutative(&self) -> bool {
|
||||
match self {
|
||||
BinaryOperator::Add => true,
|
||||
BinaryOperator::Minus => false,
|
||||
BinaryOperator::Mult => true,
|
||||
BinaryOperator::Div => false,
|
||||
BinaryOperator::Mod => false,
|
||||
BinaryOperator::And => true,
|
||||
BinaryOperator::Cmp(cmp_operator) => match cmp_operator {
|
||||
CmpOperator::LT => false,
|
||||
CmpOperator::LE => false,
|
||||
CmpOperator::GT => false,
|
||||
CmpOperator::GE => false,
|
||||
CmpOperator::EQ => true,
|
||||
CmpOperator::NE => true,
|
||||
},
|
||||
BinaryOperator::Or => true,
|
||||
BinaryOperator::Xor => true,
|
||||
BinaryOperator::BitOr => true,
|
||||
BinaryOperator::BitAnd => true,
|
||||
BinaryOperator::BitshiftRight => false,
|
||||
BinaryOperator::BitshiftLeft => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const TYPE_CATEGORY_ORDER: [TypeCategory; 5] = [
|
||||
TypeCategory::Integer,
|
||||
TypeCategory::Bool,
|
||||
TypeCategory::Real,
|
||||
TypeCategory::Other,
|
||||
TypeCategory::TypeRef,
|
||||
];
|
||||
|
||||
impl PartialOrd for TypeCategory {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
use std::cmp::*;
|
||||
let self_idx = TYPE_CATEGORY_ORDER.iter().enumerate().find(|s| s.1 == self);
|
||||
let other_idx = TYPE_CATEGORY_ORDER.iter().enumerate().find(|s| s.1 == other);
|
||||
if let (Some(self_idx), Some(other_idx)) = (self_idx, other_idx) {
|
||||
Some(self_idx.cmp(&other_idx))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Ord)]
|
||||
pub enum TypeCategory {
|
||||
Integer,
|
||||
Real,
|
||||
Bool,
|
||||
Other,
|
||||
TypeRef,
|
||||
}
|
||||
|
||||
impl TypeCategory {
|
||||
pub fn is_simple_maths(&self) -> bool {
|
||||
match self {
|
||||
TypeCategory::Integer => true,
|
||||
TypeCategory::Real => true,
|
||||
TypeCategory::Other => false,
|
||||
TypeCategory::TypeRef => false,
|
||||
TypeCategory::Bool => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StructType {
|
||||
pub fn get_field_ty(&self, name: &String) -> Option<&TypeKind> {
|
||||
self.0
|
||||
.iter()
|
||||
.find(|StructField(n, _, _)| n == name)
|
||||
.map(|StructField(_, ty, _)| ty)
|
||||
}
|
||||
|
||||
pub fn get_field_ty_mut(&mut self, name: &String) -> Option<&mut TypeKind> {
|
||||
self.0
|
||||
.iter_mut()
|
||||
.find(|StructField(n, _, _)| n == name)
|
||||
.map(|StructField(_, ty, _)| ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Block {
|
||||
fn return_expr(&self) -> Result<BlockReturn, ReturnTypeOther> {
|
||||
let mut early_return = None;
|
||||
|
||||
for statement in &self.statements {
|
||||
let ret = statement.return_type(&Default::default(), SourceModuleId(0));
|
||||
if let Ok((ReturnKind::Hard, _)) = ret {
|
||||
early_return = Some(statement);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(s) = early_return {
|
||||
return Ok(BlockReturn::Early(s));
|
||||
}
|
||||
|
||||
self.return_expression
|
||||
.as_ref()
|
||||
.map(|(r, e)| BlockReturn::Normal(*r, e))
|
||||
.ok_or(ReturnTypeOther::NoBlockReturn(self.meta))
|
||||
}
|
||||
|
||||
pub fn return_meta(&self) -> Metadata {
|
||||
self.return_expression
|
||||
.as_ref()
|
||||
.map(|e| e.1.as_ref().map(|e| e.1).unwrap_or(Metadata::default()))
|
||||
.or(self.statements.last().map(|s| s.1))
|
||||
.unwrap_or(self.meta)
|
||||
}
|
||||
|
||||
pub fn return_type(
|
||||
&self,
|
||||
refs: &TypeRefs,
|
||||
mod_id: SourceModuleId,
|
||||
) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
|
||||
let mut early_return = None;
|
||||
|
||||
for statement in &self.statements {
|
||||
let ret = statement.return_type(refs, mod_id);
|
||||
if let Ok((ReturnKind::Hard, _)) = ret {
|
||||
early_return = early_return.or(ret.ok());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((ReturnKind::Hard, ret_ty)) = early_return {
|
||||
return Ok((ReturnKind::Hard, ret_ty));
|
||||
}
|
||||
|
||||
self.return_expression
|
||||
.as_ref()
|
||||
.ok_or(ReturnTypeOther::NoBlockReturn(self.meta))
|
||||
.and_then(|(kind, stmt)| {
|
||||
Ok((
|
||||
*kind,
|
||||
stmt.as_ref()
|
||||
.and_then(|s| s.return_type(refs, mod_id).ok())
|
||||
.map(|s| s.1)
|
||||
.unwrap_or(TypeKind::Void),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn backing_var(&self) -> Option<&NamedVariableRef> {
|
||||
match self.return_expr().ok()? {
|
||||
BlockReturn::Early(statement) => statement.backing_var(),
|
||||
BlockReturn::Normal(kind, expr) => {
|
||||
if let Some(expr) = expr {
|
||||
if kind == ReturnKind::Soft {
|
||||
expr.backing_var()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Statement {
|
||||
pub fn return_type(
|
||||
&self,
|
||||
refs: &TypeRefs,
|
||||
mod_id: SourceModuleId,
|
||||
) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
|
||||
use StmtKind::*;
|
||||
match &self.0 {
|
||||
Let(var, _, expr) => if_hard(
|
||||
expr.return_type(refs, mod_id)?,
|
||||
Err(ReturnTypeOther::Let(var.2 + expr.1)),
|
||||
),
|
||||
Set(lhs, rhs) => if_hard(rhs.return_type(refs, mod_id)?, Err(ReturnTypeOther::Set(lhs.1 + rhs.1))),
|
||||
Import(_) => todo!(),
|
||||
Expression(expression) => expression.return_type(refs, mod_id),
|
||||
While(_) => Err(ReturnTypeOther::Loop),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn backing_var(&self) -> Option<&NamedVariableRef> {
|
||||
match &self.0 {
|
||||
StmtKind::Let(_, _, _) => None,
|
||||
StmtKind::Set(_, _) => None,
|
||||
StmtKind::Import(_) => None,
|
||||
StmtKind::Expression(expr) => expr.backing_var(),
|
||||
StmtKind::While(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
pub fn return_type(
|
||||
&self,
|
||||
refs: &TypeRefs,
|
||||
mod_id: SourceModuleId,
|
||||
) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
|
||||
use ExprKind::*;
|
||||
match &self.0 {
|
||||
Literal(lit) => Ok((ReturnKind::Soft, lit.as_type())),
|
||||
Variable(var) => var.return_type(),
|
||||
BinOp(_, then_e, else_e, return_ty) => {
|
||||
let then_r = then_e.return_type(refs, mod_id)?;
|
||||
let else_r = else_e.return_type(refs, mod_id)?;
|
||||
|
||||
Ok(match (then_r.0, else_r.0) {
|
||||
(ReturnKind::Hard, ReturnKind::Hard) => (ReturnKind::Hard, return_ty.clone()),
|
||||
_ => (ReturnKind::Soft, return_ty.clone()),
|
||||
})
|
||||
}
|
||||
Block(block) => block.return_type(refs, mod_id),
|
||||
FunctionCall(fcall) => fcall.return_type(),
|
||||
If(expr) => expr.return_type(refs, mod_id),
|
||||
Indexed(expression, _, _) => {
|
||||
let expr_type = expression.return_type(refs, mod_id)?;
|
||||
if let TypeKind::Array(elem_ty, _) = expr_type.1.resolve_weak(refs) {
|
||||
Ok((ReturnKind::Soft, *elem_ty))
|
||||
} else if let TypeKind::UserPtr(_) = expr_type.1.resolve_weak(refs) {
|
||||
Ok((ReturnKind::Soft, expr_type.1))
|
||||
} else {
|
||||
Err(ReturnTypeOther::IndexingNonArray(expression.1))
|
||||
}
|
||||
}
|
||||
Array(expressions) => {
|
||||
let first = expressions
|
||||
.iter()
|
||||
.next()
|
||||
.map(|e| e.return_type(refs, mod_id))
|
||||
.unwrap_or(Ok((ReturnKind::Soft, TypeKind::Void)))?;
|
||||
Ok((
|
||||
ReturnKind::Soft,
|
||||
TypeKind::Array(Box::new(first.1), expressions.len() as u64),
|
||||
))
|
||||
}
|
||||
Accessed(_, type_kind, ..) => Ok((ReturnKind::Soft, type_kind.clone())),
|
||||
Struct(key, _) => Ok((ReturnKind::Soft, TypeKind::CustomType(key.clone()))),
|
||||
Borrow(expr, mutable) => {
|
||||
let ret_type = expr.return_type(refs, mod_id)?;
|
||||
Ok((ret_type.0, TypeKind::Borrow(Box::new(ret_type.1), *mutable)))
|
||||
}
|
||||
Deref(expr) => {
|
||||
let (kind, ret_type) = expr.return_type(refs, mod_id)?;
|
||||
match ret_type.resolve_weak(refs) {
|
||||
TypeKind::Borrow(type_kind, _) => Ok((kind, *type_kind)),
|
||||
_ => Err(ReturnTypeOther::DerefNonBorrow(expr.1)),
|
||||
}
|
||||
}
|
||||
CastTo(expr, type_kind) => match expr.return_type(refs, mod_id) {
|
||||
Ok(ret_type) => match ret_type {
|
||||
(ReturnKind::Hard, ty) => Ok((ReturnKind::Hard, ty)),
|
||||
_ => Ok((ReturnKind::Soft, type_kind.clone())),
|
||||
},
|
||||
Err(_) => Ok((ReturnKind::Soft, type_kind.clone())),
|
||||
},
|
||||
AssociatedFunctionCall(_, fcall) => fcall.return_type(),
|
||||
GlobalRef(_, type_kind) => Ok((ReturnKind::Soft, type_kind.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn backing_var(&self) -> Option<&NamedVariableRef> {
|
||||
match &self.0 {
|
||||
ExprKind::Variable(var_ref) => Some(var_ref),
|
||||
ExprKind::Indexed(lhs, _, _) => lhs.backing_var(),
|
||||
ExprKind::Accessed(lhs, ..) => lhs.backing_var(),
|
||||
ExprKind::Borrow(expr, _) => expr.backing_var(),
|
||||
ExprKind::Deref(expr) => expr.backing_var(),
|
||||
ExprKind::Block(block) => block.backing_var(),
|
||||
ExprKind::Array(_) => None,
|
||||
ExprKind::Struct(_, _) => None,
|
||||
ExprKind::Literal(_) => None,
|
||||
ExprKind::BinOp(_, _, _, _) => None,
|
||||
ExprKind::FunctionCall(_) => None,
|
||||
ExprKind::If(_) => None,
|
||||
ExprKind::CastTo(expression, _) => expression.backing_var(),
|
||||
ExprKind::AssociatedFunctionCall(..) => None,
|
||||
ExprKind::GlobalRef(..) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn num_value(&self) -> Result<Option<i128>, NumValueError> {
|
||||
Ok(match &self.0 {
|
||||
ExprKind::Variable(_) => None,
|
||||
ExprKind::Indexed(..) => None,
|
||||
ExprKind::Accessed(..) => None,
|
||||
ExprKind::Array(_) => None,
|
||||
ExprKind::Struct(..) => None,
|
||||
ExprKind::Literal(literal) => literal.num_value(),
|
||||
ExprKind::BinOp(op, lhs, rhs, _) => match op {
|
||||
BinaryOperator::Add => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a + b),
|
||||
BinaryOperator::Minus => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a - b),
|
||||
BinaryOperator::Mult => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a * b),
|
||||
BinaryOperator::And => None,
|
||||
BinaryOperator::Cmp(_) => None,
|
||||
BinaryOperator::Div => {
|
||||
let rhs_value = rhs.num_value()?;
|
||||
if rhs_value == Some(0) {
|
||||
Err(NumValueError::DivideZero)?
|
||||
}
|
||||
maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a / b)
|
||||
}
|
||||
BinaryOperator::Mod => {
|
||||
let rhs_value = rhs.num_value()?;
|
||||
if rhs_value == Some(0) {
|
||||
Err(NumValueError::DivideZero)?
|
||||
}
|
||||
maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a % b)
|
||||
}
|
||||
BinaryOperator::Or => None,
|
||||
BinaryOperator::Xor => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a ^ b),
|
||||
BinaryOperator::BitOr => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a | b),
|
||||
BinaryOperator::BitAnd => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a & b),
|
||||
BinaryOperator::BitshiftRight => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a >> b),
|
||||
BinaryOperator::BitshiftLeft => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a << b),
|
||||
},
|
||||
ExprKind::FunctionCall(..) => None,
|
||||
ExprKind::If(_) => None,
|
||||
ExprKind::Block(_) => None,
|
||||
ExprKind::Borrow(_, _) => None,
|
||||
ExprKind::Deref(_) => None,
|
||||
ExprKind::CastTo(expression, _) => expression.num_value()?,
|
||||
ExprKind::AssociatedFunctionCall(..) => None,
|
||||
ExprKind::GlobalRef(..) => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl IfExpression {
|
||||
pub fn return_type(
|
||||
&self,
|
||||
refs: &TypeRefs,
|
||||
mod_id: SourceModuleId,
|
||||
) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
|
||||
let then_r = self.1.return_type(refs, mod_id)?;
|
||||
if let Some(else_e) = self.2.as_ref() {
|
||||
let else_r = else_e.return_type(refs, mod_id)?;
|
||||
|
||||
let kind = if then_r.0 == ReturnKind::Hard && else_r.0 == ReturnKind::Hard {
|
||||
ReturnKind::Hard
|
||||
} else {
|
||||
ReturnKind::Soft
|
||||
};
|
||||
Ok((kind, then_r.1))
|
||||
} else {
|
||||
Ok((ReturnKind::Soft, then_r.1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NamedVariableRef {
|
||||
pub fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
|
||||
Ok((ReturnKind::Soft, self.0.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl FunctionCall {
|
||||
pub fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
|
||||
Ok((ReturnKind::Soft, self.return_type.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
fn if_hard<TErr>(
|
||||
return_type: (ReturnKind, TypeKind),
|
||||
default: Result<(ReturnKind, TypeKind), TErr>,
|
||||
) -> Result<(ReturnKind, TypeKind), TErr> {
|
||||
if let (ReturnKind::Hard, _) = return_type {
|
||||
Ok(return_type)
|
||||
} else {
|
||||
default
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pick_return<T>(lhs: (ReturnKind, T), rhs: (ReturnKind, T)) -> (ReturnKind, T) {
|
||||
use ReturnKind::*;
|
||||
match (lhs.0, rhs.0) {
|
||||
(Hard, Hard) => (Hard, lhs.1),
|
||||
(Hard, Soft) => (Soft, rhs.1),
|
||||
(Soft, Hard) => (Soft, lhs.1),
|
||||
(_, _) => (Soft, lhs.1),
|
||||
}
|
||||
}
|
||||
|
||||
impl Literal {
|
||||
pub fn num_value(&self) -> Option<i128> {
|
||||
match self {
|
||||
Literal::I8(val) => Some(*val as i128),
|
||||
Literal::I16(val) => Some(*val as i128),
|
||||
Literal::I32(val) => Some(*val as i128),
|
||||
Literal::I64(val) => Some(*val as i128),
|
||||
Literal::I128(val) => Some(*val as i128),
|
||||
Literal::U8(val) => Some(*val as i128),
|
||||
Literal::U16(val) => Some(*val as i128),
|
||||
Literal::U32(val) => Some(*val as i128),
|
||||
Literal::U64(val) => Some(*val as i128),
|
||||
Literal::U128(val) => Some(*val as i128),
|
||||
Literal::Bool(_) => None,
|
||||
Literal::String(_) => None,
|
||||
Literal::Vague(VagueLiteral::Number(val)) => Some(*val as i128),
|
||||
Literal::Vague(VagueLiteral::Decimal(_)) => None,
|
||||
Literal::F16(_) => None,
|
||||
Literal::F32B(_) => None,
|
||||
Literal::F32(_) => None,
|
||||
Literal::F64(_) => None,
|
||||
Literal::F80(_) => None,
|
||||
Literal::F128(_) => None,
|
||||
Literal::F128PPC(_) => None,
|
||||
Literal::Char(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, thiserror::Error, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum EqualsIssue {
|
||||
#[error("Function is already defined locally at {:?}", (.0).range)]
|
||||
ExistsLocally(Metadata),
|
||||
#[error("Equals")]
|
||||
Equals,
|
||||
#[error("Function {0} is already declared locally at {:?}", (.1).range)]
|
||||
AlreadyExtern(String, Metadata),
|
||||
#[error("Function {0} is already imported from another module")]
|
||||
ConflictWithImport(String),
|
||||
#[error("Function is defined as an intrinsic")]
|
||||
ExistsAsIntrinsic,
|
||||
}
|
||||
|
||||
impl FunctionDefinition {
|
||||
pub fn equals_as_imported(&self, other: &FunctionDefinition) -> Result<(), EqualsIssue> {
|
||||
match &self.kind {
|
||||
FunctionDefinitionKind::Local(_, metadata) => Err(EqualsIssue::ExistsLocally(*metadata)),
|
||||
FunctionDefinitionKind::Extern(imported) => {
|
||||
if *imported {
|
||||
Err(EqualsIssue::ConflictWithImport(self.name.clone()))
|
||||
} else {
|
||||
if self.is_pub == other.is_pub
|
||||
&& self.name == other.name
|
||||
&& self.parameters == other.parameters
|
||||
&& self.return_type == other.return_type
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(EqualsIssue::AlreadyExtern(self.name.clone(), self.signature()))
|
||||
}
|
||||
}
|
||||
}
|
||||
FunctionDefinitionKind::Intrinsic(_) => Err(EqualsIssue::ExistsAsIntrinsic),
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user