mirror of
https://github.com/Nexoniarz/Finch.git
synced 2026-11-03 00:44:27 +00:00
Rename the Finch file extension from .fn to .fch
.fn is already used by other tools (e.g. Fennel). Modules are now looked up as name.fch; a leftover name.fn gets a clear message telling you to rename it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
01c5badc3e
commit
f897fb3140
92 changed files with 160 additions and 144 deletions
28
README.md
28
README.md
|
|
@ -40,7 +40,7 @@ fn main() {
|
||||||
Operator precedence that does not bite (`x & 1 == 0` means what it says).
|
Operator precedence that does not bite (`x & 1 == 0` means what it says).
|
||||||
- **Errors that talk like a person:**
|
- **Errors that talk like a person:**
|
||||||
```
|
```
|
||||||
game.fn:3:13: error: 'y' must be i32, but this is int (use i32(...) to convert)
|
game.fch:3:13: error: 'y' must be i32, but this is int (use i32(...) to convert)
|
||||||
3 | i32 y = x
|
3 | i32 y = x
|
||||||
| ^
|
| ^
|
||||||
```
|
```
|
||||||
|
|
@ -60,9 +60,9 @@ nix-shell # or install LLVM 21 + libclang + cmake + ni
|
||||||
cmake -S . -B build -G Ninja
|
cmake -S . -B build -G Ninja
|
||||||
ninja -C build
|
ninja -C build
|
||||||
|
|
||||||
./build/finch run examples/hello.fn
|
./build/finch run examples/hello.fch
|
||||||
./build/finch build examples/tour.fn -o tour && ./tour
|
./build/finch build examples/tour.fch -o tour && ./tour
|
||||||
tests/run.sh # 58 passed, 0 failed
|
tests/run.sh # 59 passed, 0 failed
|
||||||
tests/boot.sh # the compiler written in Finch builds itself
|
tests/boot.sh # the compiler written in Finch builds itself
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -97,16 +97,16 @@ Pick the guide that fits you:
|
||||||
|
|
||||||
| File | Shows |
|
| File | Shows |
|
||||||
|---|---|
|
|---|---|
|
||||||
| [`examples/hello.fn`](examples/hello.fn) | the smallest program |
|
| [`examples/hello.fch`](examples/hello.fch) | the smallest program |
|
||||||
| [`examples/tour.fn`](examples/tour.fn) | variables, functions, conditions, loops |
|
| [`examples/tour.fch`](examples/tour.fch) | variables, functions, conditions, loops |
|
||||||
| [`examples/guess.fn`](examples/guess.fn) | a guessing game: input, loops, C's `rand` |
|
| [`examples/guess.fch`](examples/guess.fch) | a guessing game: input, loops, C's `rand` |
|
||||||
| [`examples/todo.fn`](examples/todo.fn) | structs, arrays, strings and files |
|
| [`examples/todo.fch`](examples/todo.fch) | structs, arrays, strings and files |
|
||||||
| [`examples/structs.fn`](examples/structs.fn) | structs, arrays of structs, a linked list with `new`/`free` |
|
| [`examples/structs.fch`](examples/structs.fch) | structs, arrays of structs, a linked list with `new`/`free` |
|
||||||
| [`examples/types.fn`](examples/types.fn) | sized numbers and pointers |
|
| [`examples/types.fch`](examples/types.fch) | sized numbers and pointers |
|
||||||
| [`examples/c_import.fn`](examples/c_import.fn) | `printf`, `math.h`, `malloc`/`free`, `stderr` |
|
| [`examples/c_import.fch`](examples/c_import.fch) | `printf`, `math.h`, `malloc`/`free`, `stderr` |
|
||||||
| [`examples/window.fn`](examples/window.fn) | a GLFW + OpenGL window |
|
| [`examples/window.fch`](examples/window.fch) | a GLFW + OpenGL window |
|
||||||
| [`examples/raylib.fn`](examples/raylib.fn) | raylib with C structs by value |
|
| [`examples/raylib.fch`](examples/raylib.fch) | raylib with C structs by value |
|
||||||
| [`examples/llvm.fn`](examples/llvm.fn) | Finch building LLVM IR through the LLVM-C API |
|
| [`examples/llvm.fch`](examples/llvm.fch) | Finch building LLVM IR through the LLVM-C API |
|
||||||
| [`boot/`](boot/) | the Finch compiler, written in Finch |
|
| [`boot/`](boot/) | the Finch compiler, written in Finch |
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
|
||||||
28
README.pl.md
28
README.pl.md
|
|
@ -40,7 +40,7 @@ fn main() {
|
||||||
powodować niezdefiniowane zachowanie. Priorytety operatorów nie gryzą (`x & 1 == 0` znaczy to, co widać).
|
powodować niezdefiniowane zachowanie. Priorytety operatorów nie gryzą (`x & 1 == 0` znaczy to, co widać).
|
||||||
- **Błędy mówią po ludzku:**
|
- **Błędy mówią po ludzku:**
|
||||||
```
|
```
|
||||||
gra.fn:3:13: error: 'y' must be i32, but this is int (use i32(...) to convert)
|
gra.fch:3:13: error: 'y' must be i32, but this is int (use i32(...) to convert)
|
||||||
3 | i32 y = x
|
3 | i32 y = x
|
||||||
| ^
|
| ^
|
||||||
```
|
```
|
||||||
|
|
@ -60,9 +60,9 @@ nix-shell # albo zainstaluj samodzielnie LLVM 21 + lib
|
||||||
cmake -S . -B build -G Ninja
|
cmake -S . -B build -G Ninja
|
||||||
ninja -C build
|
ninja -C build
|
||||||
|
|
||||||
./build/finch run examples/hello.fn
|
./build/finch run examples/hello.fch
|
||||||
./build/finch build examples/tour.fn -o tour && ./tour
|
./build/finch build examples/tour.fch -o tour && ./tour
|
||||||
tests/run.sh # 58 passed, 0 failed
|
tests/run.sh # 59 passed, 0 failed
|
||||||
tests/boot.sh # kompilator napisany w Finchu buduje sam siebie
|
tests/boot.sh # kompilator napisany w Finchu buduje sam siebie
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -97,16 +97,16 @@ Wybierz przewodnik dla siebie:
|
||||||
|
|
||||||
| Plik | Pokazuje |
|
| Plik | Pokazuje |
|
||||||
|---|---|
|
|---|---|
|
||||||
| [`examples/hello.fn`](examples/hello.fn) | najmniejszy program |
|
| [`examples/hello.fch`](examples/hello.fch) | najmniejszy program |
|
||||||
| [`examples/tour.fn`](examples/tour.fn) | zmienne, funkcje, warunki, pętle |
|
| [`examples/tour.fch`](examples/tour.fch) | zmienne, funkcje, warunki, pętle |
|
||||||
| [`examples/guess.fn`](examples/guess.fn) | gra w zgadywanie: wejście, pętle, `rand` z C |
|
| [`examples/guess.fch`](examples/guess.fch) | gra w zgadywanie: wejście, pętle, `rand` z C |
|
||||||
| [`examples/todo.fn`](examples/todo.fn) | struktury, tablice, teksty i pliki |
|
| [`examples/todo.fch`](examples/todo.fch) | struktury, tablice, teksty i pliki |
|
||||||
| [`examples/structs.fn`](examples/structs.fn) | struktury, tablice struktur, lista wiązana z `new`/`free` |
|
| [`examples/structs.fch`](examples/structs.fch) | struktury, tablice struktur, lista wiązana z `new`/`free` |
|
||||||
| [`examples/types.fn`](examples/types.fn) | liczby z rozmiarem i wskaźniki |
|
| [`examples/types.fch`](examples/types.fch) | liczby z rozmiarem i wskaźniki |
|
||||||
| [`examples/c_import.fn`](examples/c_import.fn) | `printf`, `math.h`, `malloc`/`free`, `stderr` |
|
| [`examples/c_import.fch`](examples/c_import.fch) | `printf`, `math.h`, `malloc`/`free`, `stderr` |
|
||||||
| [`examples/window.fn`](examples/window.fn) | okno GLFW + OpenGL |
|
| [`examples/window.fch`](examples/window.fch) | okno GLFW + OpenGL |
|
||||||
| [`examples/raylib.fn`](examples/raylib.fn) | raylib ze strukturami C przez wartość |
|
| [`examples/raylib.fch`](examples/raylib.fch) | raylib ze strukturami C przez wartość |
|
||||||
| [`examples/llvm.fn`](examples/llvm.fn) | Finch budujący LLVM IR przez API LLVM-C |
|
| [`examples/llvm.fch`](examples/llvm.fch) | Finch budujący LLVM IR przez API LLVM-C |
|
||||||
| [`boot/`](boot/) | kompilator Fincha napisany w Finchu |
|
| [`boot/`](boot/) | kompilator Fincha napisany w Finchu |
|
||||||
|
|
||||||
## Plan rozwoju
|
## Plan rozwoju
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// finch-boot: the Finch compiler written in Finch.
|
// finch-boot: the Finch compiler written in Finch.
|
||||||
//
|
//
|
||||||
// finch-boot build <file.fn> [-o name] compile a program
|
// finch-boot build <file.fch> [-o name] compile a program
|
||||||
// finch-boot ir <file.fn> print the LLVM IR
|
// finch-boot ir <file.fch> print the LLVM IR
|
||||||
//
|
//
|
||||||
// It reads the program and the modules it imports, checks it, writes LLVM IR as text
|
// It reads the program and the modules it imports, checks it, writes LLVM IR as text
|
||||||
// and lets clang turn that into a program, linked with Finch's C runtime.
|
// and lets clang turn that into a program, linked with Finch's C runtime.
|
||||||
|
|
@ -42,6 +42,11 @@ struct Loader {
|
||||||
fn load(ptr[Loader] l, str path, str module) {
|
fn load(ptr[Loader] l, str path, str module) {
|
||||||
if !file_exists(path) {
|
if !file_exists(path) {
|
||||||
if module.len > 0 {
|
if module.len > 0 {
|
||||||
|
old := path.sub(0, path.len - 4) + ".fn"
|
||||||
|
if file_exists(old) {
|
||||||
|
print("error: found " + old + ", but Finch files now end in .fch: rename it to " + path)
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
print("error: can't find the module '" + module + "' (looked for " + path + ")")
|
print("error: can't find the module '" + module + "' (looked for " + path + ")")
|
||||||
} else {
|
} else {
|
||||||
print("error: can't open '" + path + "'")
|
print("error: can't open '" + path + "'")
|
||||||
|
|
@ -59,15 +64,15 @@ fn load(ptr[Loader] l, str path, str module) {
|
||||||
for name in imports {
|
for name in imports {
|
||||||
if !l.loaded.contains(name) {
|
if !l.loaded.contains(name) {
|
||||||
l.loaded.push(name)
|
l.loaded.push(name)
|
||||||
load(l, dirOf(path) + "/" + name + ".fn", name)
|
load(l, dirOf(path) + "/" + name + ".fch", name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage() {
|
fn usage() {
|
||||||
print("finch-boot - the Finch compiler written in Finch")
|
print("finch-boot - the Finch compiler written in Finch")
|
||||||
print("usage: finch-boot build <file.fn> [-o name]")
|
print("usage: finch-boot build <file.fch> [-o name]")
|
||||||
print(" finch-boot ir <file.fn>")
|
print(" finch-boot ir <file.fch>")
|
||||||
exit(1)
|
exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,11 +92,11 @@ fn main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Save it in the `Finch` folder as **`hello.fn`**. The `.fn` at the end tells everyone it is a Finch program.
|
3. Save it in the `Finch` folder as **`hello.fch`**. The `.fch` at the end tells everyone it is a Finch program.
|
||||||
4. In the Terminal, type:
|
4. In the Terminal, type:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./build/finch run hello.fn
|
./build/finch run hello.fch
|
||||||
```
|
```
|
||||||
|
|
||||||
You will see:
|
You will see:
|
||||||
|
|
@ -652,14 +652,14 @@ fn main() {
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
hello.fn:3:11: error: there is no variable named 'agee'
|
hello.fch:3:11: error: there is no variable named 'agee'
|
||||||
3 | print(agee)
|
3 | print(agee)
|
||||||
| ^
|
| ^
|
||||||
```
|
```
|
||||||
|
|
||||||
How to read it:
|
How to read it:
|
||||||
|
|
||||||
- `hello.fn` is the file,
|
- `hello.fch` is the file,
|
||||||
- `3` is the **line** number,
|
- `3` is the **line** number,
|
||||||
- `11` is how far into the line (the **column**),
|
- `11` is how far into the line (the **column**),
|
||||||
- then the explanation, and the line itself with a `^` pointing at the problem.
|
- then the explanation, and the line itself with a `^` pointing at the problem.
|
||||||
|
|
@ -816,7 +816,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`return` inside `main` ends the program. (A version with a random number is in `examples/guess.fn`.)
|
`return` inside `main` ends the program. (A version with a random number is in `examples/guess.fch`.)
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Project 7: Class average
|
### Project 7: Class average
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ Finch is a single-pass front end in front of LLVM: type checking and IR generati
|
||||||
over the AST, with no intermediate representation of its own.
|
over the AST, with no intermediate representation of its own.
|
||||||
|
|
||||||
```
|
```
|
||||||
main.fn ──► Loader::load() ── lex() ── parse() ──► Program (+ each imported module, recursively)
|
main.fch ──► Loader::load() ── lex() ── parse() ──► Program (+ each imported module, recursively)
|
||||||
│
|
│
|
||||||
all `import "x.h"` ──► importHeaders() (libclang) ──► CImports
|
all `import "x.h"` ──► importHeaders() (libclang) ──► CImports
|
||||||
│
|
│
|
||||||
|
|
@ -100,7 +100,7 @@ Design decisions that shape the rest:
|
||||||
| `src/abi.cpp` | 310 | System V x86-64 classification, C calls with structs by value, C-callable thunks |
|
| `src/abi.cpp` | 310 | System V x86-64 classification, C calls with structs by value, C-callable thunks |
|
||||||
| `src/main.cpp` | 390 | Driver: CLI, loader, target machine, O2, object emission, runtime cache, linking, link-error advice, `run` |
|
| `src/main.cpp` | 390 | Driver: CLI, loader, target machine, O2, object emission, runtime cache, linking, link-error advice, `run` |
|
||||||
| `runtime/finch_rt.c` | 440 | The runtime library (§12) |
|
| `runtime/finch_rt.c` | 440 | The runtime library (§12) |
|
||||||
| `boot/*.fn` | 3080 | The self-hosted compiler (§17) |
|
| `boot/*.fch` | 3080 | The self-hosted compiler (§17) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -198,7 +198,7 @@ All nodes are in `src/ast.h`: small class hierarchies with an explicit `kind`, d
|
||||||
| `Member` | `MemberExpr` | `obj`, `field` (`.x`, `.len`, `.ptr`, `.value`) |
|
| `Member` | `MemberExpr` | `obj`, `field` (`.x`, `.len`, `.ptr`, `.value`) |
|
||||||
| `Index` | `IndexExpr` | `obj`, `index` |
|
| `Index` | `IndexExpr` | `obj`, `index` |
|
||||||
| `ArrayLit` | `ArrayLitExpr` | `elems` |
|
| `ArrayLit` | `ArrayLitExpr` | `elems` |
|
||||||
| `Method` | `MethodExpr` | `obj`, `name`, `args`, `argNames`; also `module.fn(...)` |
|
| `Method` | `MethodExpr` | `obj`, `name`, `args`, `argNames`; also `module.fch(...)` |
|
||||||
|
|
||||||
| StmtKind | Node |
|
| StmtKind | Node |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
|
@ -236,7 +236,7 @@ Equality is structural; two `Struct`s are equal iff they share `info`.
|
||||||
|
|
||||||
`Loader::load(path, module, from)` in `main.cpp` reads a file, appends it to `g_files` (positions carry
|
`Loader::load(path, module, from)` in `main.cpp` reads a file, appends it to `g_files` (positions carry
|
||||||
the file index in `Pos::file`), lexes and parses it, then loads each `import name` that wasn't seen yet:
|
the file index in `Pos::file`), lexes and parses it, then loads each `import name` that wasn't seen yet:
|
||||||
`name.fn` next to the importing file, or in a `FINCH_PATH` folder. The result is a `std::vector<Program>`,
|
`name.fch` next to the importing file, or in a `FINCH_PATH` folder. The result is a `std::vector<Program>`,
|
||||||
main file first. Cycles are fine: a module is marked loaded before its own imports are followed.
|
main file first. Cycles are fine: a module is marked loaded before its own imports are followed.
|
||||||
|
|
||||||
All files become **one LLVM module**. Each Finch module has a `ModuleScope` (functions, structs, the set
|
All files become **one LLVM module**. Each Finch module has a `ModuleScope` (functions, structs, the set
|
||||||
|
|
@ -597,7 +597,7 @@ string building (`concat`, `from_int/uint/float/char/bool`, `sub`, `trim`, `uppe
|
||||||
### 13.1 Importing headers
|
### 13.1 Importing headers
|
||||||
|
|
||||||
`importHeaders(imports, dirs)` parses each `import "x.h"` as its own translation unit from an in-memory
|
`importHeaders(imports, dirs)` parses each `import "x.h"` as its own translation unit from an in-memory
|
||||||
`#include "x.h"`, with `-x c -std=gnu11`, `-I` for every `.fn` file's folder, and `-isystem` for each
|
`#include "x.h"`, with `-x c -std=gnu11`, `-I` for every `.fch` file's folder, and `-isystem` for each
|
||||||
directory the system C compiler searches (read from `$CC -E -v -x c /dev/null`, which is what makes it work
|
directory the system C compiler searches (read from `$CC -E -v -x c /dev/null`, which is what makes it work
|
||||||
on NixOS). `gnu11` because glibc hides `M_PI` and friends in strict ISO mode.
|
on NixOS). `gnu11` because glibc hides `M_PI` and friends in strict ISO mode.
|
||||||
|
|
||||||
|
|
@ -647,7 +647,7 @@ type and attributes, and rebuilds struct results the same way.
|
||||||
struct parameters from registers or `byval` memory, calls the Finch function and lowers its result. Only
|
struct parameters from registers or `byval` memory, calls the Finch function and lowers its result. Only
|
||||||
C-compatible signatures are accepted.
|
C-compatible signatures are accepted.
|
||||||
|
|
||||||
`tests/run/c_structs.fn` exercises `{float,float}`, `{float,float,float}`, `{u8×4}`, `{double,int}`,
|
`tests/run/c_structs.fch` exercises `{float,float}`, `{float,float,float}`, `{u8×4}`, `{double,int}`,
|
||||||
`{i64,i64}`, a 24-byte struct, a struct with a `char[8]`, nested structs, register exhaustion with 13
|
`{i64,i64}`, a 24-byte struct, a struct with a `char[8]`, nested structs, register exhaustion with 13
|
||||||
struct arguments, and callbacks taking and returning structs, against a C library compiled with
|
struct arguments, and callbacks taking and returning structs, against a C library compiled with
|
||||||
`link "abi.c"`. The results match the same calls made from C.
|
`link "abi.c"`. The results match the same calls made from C.
|
||||||
|
|
@ -655,7 +655,7 @@ struct arguments, and callbacks taking and returning structs, against a C librar
|
||||||
### 13.4 Linking C code
|
### 13.4 Linking C code
|
||||||
|
|
||||||
`link "name"` tries `pkg-config --libs name`, then `-lname`. `link "file.c"` compiles the file (relative
|
`link "name"` tries `pkg-config --libs name`, then `-lname`. `link "file.c"` compiles the file (relative
|
||||||
to the `.fn` file) with `$CC -O2 -fPIC -c` into a temporary object; `.o` and `.a` files are passed
|
to the `.fch` file) with `$CC -O2 -fPIC -c` into a temporary object; `.o` and `.a` files are passed
|
||||||
through. When linking fails, `linkFailed()` maps undefined symbols back to the header that declared them
|
through. When linking fails, `linkFailed()` maps undefined symbols back to the header that declared them
|
||||||
and prints the `link` line to add.
|
and prints the `link` line to add.
|
||||||
|
|
||||||
|
|
@ -666,7 +666,7 @@ and prints the `link` line to add.
|
||||||
- **Target:** `sys::getDefaultTargetTriple()`, CPU `generic`, `Reloc::PIC_`. The data layout is set on the
|
- **Target:** `sys::getDefaultTargetTriple()`, CPU `generic`, `Reloc::PIC_`. The data layout is set on the
|
||||||
module before any IR is built (alignment of loads/stores depends on it).
|
module before any IR is built (alignment of loads/stores depends on it).
|
||||||
- **Optimization:** `PassBuilder::buildPerModuleDefaultPipeline(O2)`: clang's `-O2` middle end. `-O0`
|
- **Optimization:** `PassBuilder::buildPerModuleDefaultPipeline(O2)`: clang's `-O2` middle end. `-O0`
|
||||||
skips it (`finch ir file.fn -O0` shows `Codegen`'s raw output).
|
skips it (`finch ir file.fch -O0` shows `Codegen`'s raw output).
|
||||||
- **Emission:** legacy `PassManager` + `addPassesToEmitFile(ObjectFile)`.
|
- **Emission:** legacy `PassManager` + `addPassesToEmitFile(ObjectFile)`.
|
||||||
- **Linking:** `$CC prog.o rt.o [user objects] -lm [libs]`. On Nix the cc wrapper turns `-L` paths into
|
- **Linking:** `$CC prog.o rt.o [user objects] -lm [libs]`. On Nix the cc wrapper turns `-L` paths into
|
||||||
`RPATH`, so programs run without `LD_LIBRARY_PATH`.
|
`RPATH`, so programs run without `LD_LIBRARY_PATH`.
|
||||||
|
|
@ -707,11 +707,11 @@ there's an obvious fix, how to fix it. There are no warnings. An LLVM verifier f
|
||||||
|
|
||||||
| File | Contents |
|
| File | Contents |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `boot/lexer.fn` | the lexer (tokens are `{kind, text, line, col, nl}`; kinds are words, keywords and operators are their own kind) |
|
| `boot/lexer.fch` | the lexer (tokens are `{kind, text, line, col, nl}`; kinds are words, keywords and operators are their own kind) |
|
||||||
| `boot/ast.fn` | `Type` (`kind`, `elem` as a 0/1-element array, `name`, `module`), one generic `Node` for everything, `Program` |
|
| `boot/ast.fch` | `Type` (`kind`, `elem` as a 0/1-element array, `name`, `module`), one generic `Node` for everything, `Program` |
|
||||||
| `boot/parser.fn` | the same grammar and newline rules as the C++ parser, precedence climbing for binary operators |
|
| `boot/parser.fch` | the same grammar and newline rules as the C++ parser, precedence climbing for binary operators |
|
||||||
| `boot/gen.fn` | type checker + **textual LLVM IR** generator: scopes, places, coercions, copies, helpers, printing, runtime checks, array/str methods |
|
| `boot/gen.fch` | type checker + **textual LLVM IR** generator: scopes, places, coercions, copies, helpers, printing, runtime checks, array/str methods |
|
||||||
| `boot/main.fn` | loader for modules and the driver: writes `.ll`, runs `clang -O2 file.ll runtime/finch_rt.c` |
|
| `boot/main.fch` | loader for modules and the driver: writes `.ll`, runs `clang -O2 file.ll runtime/finch_rt.c` |
|
||||||
|
|
||||||
It supports the core language: `int float bool char str`, `[]T`, structs (defaults, named/positional
|
It supports the core language: `int float bool char str`, `[]T`, structs (defaults, named/positional
|
||||||
constructors), `ptr[T]`/`addr`/`new`/`free`/`null`, all statements except `defer`, modules, and the
|
constructors), `ptr[T]`/`addr`/`new`/`free`/`null`, all statements except `defer`, modules, and the
|
||||||
|
|
@ -753,9 +753,9 @@ with the expected one. It takes about 2.3 s for the self-hosted compiler to comp
|
||||||
|
|
||||||
## 19. Testing
|
## 19. Testing
|
||||||
|
|
||||||
- `tests/run.sh`: every `tests/run/*.fn` with `fn main` must print exactly its `.out` (stdin from `.in`
|
- `tests/run.sh`: every `tests/run/*.fch` with `fn main` must print exactly its `.out` (stdin from `.in`
|
||||||
if present); files without `main` are modules or helpers. Every `tests/fail/*.fn` must fail with the text
|
if present); files without `main` are modules or helpers. Every `tests/fail/*.fch` must fail with the text
|
||||||
from its `// expect:` line. Currently **58 passed, 0 failed**.
|
from its `// expect:` line. Currently **59 passed, 0 failed**.
|
||||||
- `MEMCHECK=1 tests/run.sh`: the same, plus valgrind on every program (no leaks, no invalid access).
|
- `MEMCHECK=1 tests/run.sh`: the same, plus valgrind on every program (no leaks, no invalid access).
|
||||||
- `tests/boot.sh`: the self-hosting fixpoint and the subset run (§17).
|
- `tests/boot.sh`: the self-hosting fixpoint and the subset run (§17).
|
||||||
|
|
||||||
|
|
@ -787,7 +787,7 @@ with the expected one. It takes about 2.3 s for the self-hosted compiler to comp
|
||||||
|
|
||||||
| Function | Does |
|
| Function | Does |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `Loader::load` / `findModule` | read, lex, parse a file and recursively its modules / locate `name.fn` |
|
| `Loader::load` / `findModule` | read, lex, parse a file and recursively its modules / locate `name.fch` |
|
||||||
| `hostMachine`, `optimize`, `emitObject` | target machine, O2 pipeline, object file |
|
| `hostMachine`, `optimize`, `emitObject` | target machine, O2 pipeline, object file |
|
||||||
| `runtimeObject` | compile and cache the runtime |
|
| `runtimeObject` | compile and cache the runtime |
|
||||||
| `capture`, `libFlags`, `link` | run a command / pkg-config or `-l` / the link step, compiling `link "x.c"` files |
|
| `capture`, `libFlags`, `link` | run a command / pkg-config or `-l` / the link step, compiling `link "x.c"` files |
|
||||||
|
|
@ -858,7 +858,7 @@ with the expected one. It takes about 2.3 s for the self-hosted compiler to comp
|
||||||
|
|
||||||
**A built-in function:** add the name to `isBuiltin()`, handle it in `call()`, implement it (in IR, or as
|
**A built-in function:** add the name to `isBuiltin()`, handle it in `call()`, implement it (in IR, or as
|
||||||
a runtime function declared in `rt()`'s table), add `tests/run/` and `tests/fail/` cases. If it should
|
a runtime function declared in `rt()`'s table), add `tests/run/` and `tests/fail/` cases. If it should
|
||||||
exist in the self-hosted compiler too, add it to `boot/gen.fn`'s `call()` and `runtimeDecls()`.
|
exist in the self-hosted compiler too, add it to `boot/gen.fch`'s `call()` and `runtimeDecls()`.
|
||||||
|
|
||||||
**An array or str method:** add a branch in `arrayMethod()` or the str part of `method()`; if it changes
|
**An array or str method:** add a branch in `arrayMethod()` or the str part of `method()`; if it changes
|
||||||
the array, add its name to the `changing` sets in `method()` and `mutatesExpr()`, so borrowing stays correct.
|
the array, add its name to the `changing` sets in `method()` and `mutatesExpr()`, so borrowing stays correct.
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ compiler works inside. For that, see the [guide for engineers](for-engineers.md)
|
||||||
|
|
||||||
## 1. What Finch is
|
## 1. What Finch is
|
||||||
|
|
||||||
Finch is a **compiled** programming language. The `finch` tool translates your `.fn`
|
Finch is a **compiled** programming language. The `finch` tool translates your `.fch`
|
||||||
file into a real program (a native executable, like the ones made from C), using the
|
file into a real program (a native executable, like the ones made from C), using the
|
||||||
**LLVM** compiler toolkit, the same backend that powers clang, Rust and Swift.
|
**LLVM** compiler toolkit, the same backend that powers clang, Rust and Swift.
|
||||||
|
|
||||||
|
|
@ -106,9 +106,9 @@ Finch's small runtime library, which is kept in `~/.cache/finch`. Set `CC` to us
|
||||||
## 3. The finch command
|
## 3. The finch command
|
||||||
|
|
||||||
```
|
```
|
||||||
finch run <file.fn> [args...] compile and run right away
|
finch run <file.fch> [args...] compile and run right away
|
||||||
finch build <file.fn> [-o name] compile into a program (default name: the file's name)
|
finch build <file.fch> [-o name] compile into a program (default name: the file's name)
|
||||||
finch ir <file.fn> print the LLVM IR (for the curious)
|
finch ir <file.fch> print the LLVM IR (for the curious)
|
||||||
finch version show the version
|
finch version show the version
|
||||||
|
|
||||||
options:
|
options:
|
||||||
|
|
@ -120,10 +120,10 @@ options:
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
finch run hello.fn # prints the output
|
finch run hello.fch # prints the output
|
||||||
finch run tool.fn input.txt -v # arguments after the file go to the program
|
finch run tool.fch input.txt -v # arguments after the file go to the program
|
||||||
finch build game.fn -o mygame # creates ./mygame
|
finch build game.fch -o mygame # creates ./mygame
|
||||||
finch build game.fn -g -O0 # a build for the debugger
|
finch build game.fch -g -O0 # a build for the debugger
|
||||||
```
|
```
|
||||||
|
|
||||||
`finch run` passes on your program's exit code. If the program crashes, it says how
|
`finch run` passes on your program's exit code. If the program crashes, it says how
|
||||||
|
|
@ -139,7 +139,7 @@ finch build game.fn -g -O0 # a build for the debugger
|
||||||
several lines */
|
several lines */
|
||||||
|
|
||||||
import "math.h" // C headers (optional), at the top
|
import "math.h" // C headers (optional), at the top
|
||||||
import shapes // Finch modules (optional): shapes.fn next to this file
|
import shapes // Finch modules (optional): shapes.fch next to this file
|
||||||
link "m" // C libraries or C files to link (optional)
|
link "m" // C libraries or C files to link (optional)
|
||||||
|
|
||||||
struct Point { // your own types
|
struct Point { // your own types
|
||||||
|
|
@ -502,11 +502,11 @@ Using `.value`, `.field` or `[i]` on a `null` pointer **stops the program** with
|
||||||
|
|
||||||
## 16. Modules
|
## 16. Modules
|
||||||
|
|
||||||
Split a program into files. `import shapes` loads `shapes.fn` from the same folder (or from a
|
Split a program into files. `import shapes` loads `shapes.fch` from the same folder (or from a
|
||||||
folder listed in the `FINCH_PATH` environment variable, separated by `:`).
|
folder listed in the `FINCH_PATH` environment variable, separated by `:`).
|
||||||
|
|
||||||
```c
|
```c
|
||||||
// shapes.fn
|
// shapes.fch
|
||||||
struct Box {
|
struct Box {
|
||||||
float w
|
float w
|
||||||
float h
|
float h
|
||||||
|
|
@ -518,7 +518,7 @@ fn area(Box b) -> float {
|
||||||
```
|
```
|
||||||
|
|
||||||
```c
|
```c
|
||||||
// main.fn
|
// main.fch
|
||||||
import shapes
|
import shapes
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -540,7 +540,7 @@ fn main() {
|
||||||
```c
|
```c
|
||||||
import "stdio.h"
|
import "stdio.h"
|
||||||
import "GLFW/glfw3.h" // a path inside the system include folders
|
import "GLFW/glfw3.h" // a path inside the system include folders
|
||||||
import "mylib.h" // a header next to your .fn file
|
import "mylib.h" // a header next to your .fch file
|
||||||
```
|
```
|
||||||
|
|
||||||
Finch reads the header with **libclang** (clang's C parser) and makes these usable:
|
Finch reads the header with **libclang** (clang's C parser) and makes these usable:
|
||||||
|
|
@ -554,7 +554,7 @@ Finch reads the header with **libclang** (clang's C parser) and makes these usab
|
||||||
|
|
||||||
```c
|
```c
|
||||||
link "glfw" // uses libglfw.so (pkg-config is asked first, if installed)
|
link "glfw" // uses libglfw.so (pkg-config is asked first, if installed)
|
||||||
link "helpers.c" // compile and link your own C file (next to the .fn file)
|
link "helpers.c" // compile and link your own C file (next to the .fch file)
|
||||||
link "prebuilt.a" // or an object file / static library
|
link "prebuilt.a" // or an object file / static library
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -617,7 +617,7 @@ Its parameters must be C types (numbers, pointers, C structs).
|
||||||
### Compile errors
|
### Compile errors
|
||||||
|
|
||||||
```
|
```
|
||||||
game.fn:12:9: error: 'score' must be int, but this is str
|
game.fch:12:9: error: 'score' must be int, but this is str
|
||||||
12 | score = "high"
|
12 | score = "high"
|
||||||
| ^
|
| ^
|
||||||
```
|
```
|
||||||
|
|
@ -643,9 +643,9 @@ Instead of undefined behavior (as in C), Finch stops with a message and exit cod
|
||||||
## 19. Debugging
|
## 19. Debugging
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
finch build game.fn -g -O0 -o game
|
finch build game.fch -g -O0 -o game
|
||||||
gdb ./game
|
gdb ./game
|
||||||
(gdb) break game.fn:12
|
(gdb) break game.fch:12
|
||||||
(gdb) run
|
(gdb) run
|
||||||
(gdb) bt # where are we, through which functions
|
(gdb) bt # where are we, through which functions
|
||||||
(gdb) info locals # all variables
|
(gdb) info locals # all variables
|
||||||
|
|
@ -665,7 +665,7 @@ visible; without it the optimizer may remove some.
|
||||||
| `… come from "x.h", but its library isn't linked` | Add `link "name"` at the top of the file. |
|
| `… come from "x.h", but its library isn't linked` | Add `link "name"` at the top of the file. |
|
||||||
| `the library 'x' wasn't found` | Not installed, or a wrong name. On Nix, add it to `shell.nix` and run inside `nix-shell`. |
|
| `the library 'x' wasn't found` | Not installed, or a wrong name. On Nix, add it to `shell.nix` and run inside `nix-shell`. |
|
||||||
| `the C function 'f' can't be used from Finch yet` | It uses a union, bit fields or another unsupported type by value. Look for a variant that takes pointers. |
|
| `the C function 'f' can't be used from Finch yet` | It uses a union, bit fields or another unsupported type by value. Look for a variant that takes pointers. |
|
||||||
| `can't find the module 'x'` | Put `x.fn` next to the importing file, or set `FINCH_PATH`. |
|
| `can't find the module 'x'` | Put `x.fch` next to the importing file, or set `FINCH_PATH`. |
|
||||||
| `couldn't build the Finch runtime` | No working C compiler. Install gcc or clang, or set `CC`. |
|
| `couldn't build the Finch runtime` | No working C compiler. Install gcc or clang, or set `CC`. |
|
||||||
| A library is not found when the program starts | It was linked from a folder the system doesn't search. Run inside the same `nix-shell`, or install it system-wide. |
|
| A library is not found when the program starts | It was linked from a folder the system doesn't search. Run inside the same `nix-shell`, or install it system-wide. |
|
||||||
| `clang` inside `nix-shell` can't find `stdio.h` | You have an old `shell.nix`: `llvmPackages.clang` must be listed before `llvmPackages.libclang`. |
|
| `clang` inside `nix-shell` can't find `stdio.h` | You have an old `shell.nix`: `llvmPackages.clang` must be listed before `llvmPackages.libclang`. |
|
||||||
|
|
@ -692,7 +692,7 @@ Finch/
|
||||||
```
|
```
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
tests/run.sh # → 58 passed, 0 failed
|
tests/run.sh # → 59 passed, 0 failed
|
||||||
MEMCHECK=1 tests/run.sh # the same under valgrind: no leaks, no bad memory access
|
MEMCHECK=1 tests/run.sh # the same under valgrind: no leaks, no bad memory access
|
||||||
tests/boot.sh # the self-hosting check (needs clang)
|
tests/boot.sh # the self-hosting check (needs clang)
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ Finch to jednoprzebiegowy front-end przed LLVM: sprawdzanie typów i generowanie
|
||||||
po AST, bez własnej reprezentacji pośredniej.
|
po AST, bez własnej reprezentacji pośredniej.
|
||||||
|
|
||||||
```
|
```
|
||||||
main.fn ──► Loader::load() ── lex() ── parse() ──► Program (+ każdy importowany moduł, rekurencyjnie)
|
main.fch ──► Loader::load() ── lex() ── parse() ──► Program (+ każdy importowany moduł, rekurencyjnie)
|
||||||
│
|
│
|
||||||
wszystkie `import "x.h"` ──► importHeaders() (libclang) ──► CImports
|
wszystkie `import "x.h"` ──► importHeaders() (libclang) ──► CImports
|
||||||
│
|
│
|
||||||
|
|
@ -100,7 +100,7 @@ Decyzje, które kształtują resztę:
|
||||||
| `src/abi.cpp` | 310 | Klasyfikacja System V x86-64, wywołania C ze strukturami przez wartość, wrappery wywoływalne z C |
|
| `src/abi.cpp` | 310 | Klasyfikacja System V x86-64, wywołania C ze strukturami przez wartość, wrappery wywoływalne z C |
|
||||||
| `src/main.cpp` | 390 | Sterownik: CLI, ładowanie, maszyna docelowa, O2, emisja, cache runtime'u, linkowanie, porady przy błędach linkera, `run` |
|
| `src/main.cpp` | 390 | Sterownik: CLI, ładowanie, maszyna docelowa, O2, emisja, cache runtime'u, linkowanie, porady przy błędach linkera, `run` |
|
||||||
| `runtime/finch_rt.c` | 440 | Biblioteka uruchomieniowa (§12) |
|
| `runtime/finch_rt.c` | 440 | Biblioteka uruchomieniowa (§12) |
|
||||||
| `boot/*.fn` | 3080 | Kompilator samohostujący (§17) |
|
| `boot/*.fch` | 3080 | Kompilator samohostujący (§17) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -199,7 +199,7 @@ Wszystkie węzły są w `src/ast.h`: małe hierarchie klas z jawnym `kind`, rozg
|
||||||
| `Member` | `MemberExpr` | `obj`, `field` (`.x`, `.len`, `.ptr`, `.value`) |
|
| `Member` | `MemberExpr` | `obj`, `field` (`.x`, `.len`, `.ptr`, `.value`) |
|
||||||
| `Index` | `IndexExpr` | `obj`, `index` |
|
| `Index` | `IndexExpr` | `obj`, `index` |
|
||||||
| `ArrayLit` | `ArrayLitExpr` | `elems` |
|
| `ArrayLit` | `ArrayLitExpr` | `elems` |
|
||||||
| `Method` | `MethodExpr` | `obj`, `name`, `args`, `argNames`; także `modul.fn(...)` |
|
| `Method` | `MethodExpr` | `obj`, `name`, `args`, `argNames`; także `modul.fch(...)` |
|
||||||
|
|
||||||
| StmtKind | Węzeł |
|
| StmtKind | Węzeł |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
|
@ -236,7 +236,7 @@ Równość jest strukturalna; dwa `Struct` są równe, gdy mają to samo `info`.
|
||||||
|
|
||||||
`Loader::load(path, module, from)` w `main.cpp` czyta plik, dopisuje go do `g_files` (pozycje niosą
|
`Loader::load(path, module, from)` w `main.cpp` czyta plik, dopisuje go do `g_files` (pozycje niosą
|
||||||
indeks pliku w `Pos::file`), leksuje go i parsuje, a potem wczytuje każdy jeszcze niewidziany `import nazwa`:
|
indeks pliku w `Pos::file`), leksuje go i parsuje, a potem wczytuje każdy jeszcze niewidziany `import nazwa`:
|
||||||
`nazwa.fn` obok importującego pliku albo w folderze z `FINCH_PATH`. Wynik to `std::vector<Program>`,
|
`nazwa.fch` obok importującego pliku albo w folderze z `FINCH_PATH`. Wynik to `std::vector<Program>`,
|
||||||
z głównym plikiem na początku. Cykle nie przeszkadzają: moduł jest oznaczany jako wczytany, zanim
|
z głównym plikiem na początku. Cykle nie przeszkadzają: moduł jest oznaczany jako wczytany, zanim
|
||||||
zaczniemy podążać za jego importami.
|
zaczniemy podążać za jego importami.
|
||||||
|
|
||||||
|
|
@ -605,7 +605,7 @@ własność (`copy`, `own`, `drop`, `from_c`), tablice (`reserve`, `make`, `resi
|
||||||
### 13.1 Import nagłówków
|
### 13.1 Import nagłówków
|
||||||
|
|
||||||
`importHeaders(imports, dirs)` parsuje każdy `import "x.h"` jako osobną jednostkę translacji z pliku
|
`importHeaders(imports, dirs)` parsuje każdy `import "x.h"` jako osobną jednostkę translacji z pliku
|
||||||
w pamięci `#include "x.h"`, z `-x c -std=gnu11`, `-I` dla folderu każdego pliku `.fn` i `-isystem` dla
|
w pamięci `#include "x.h"`, z `-x c -std=gnu11`, `-I` dla folderu każdego pliku `.fch` i `-isystem` dla
|
||||||
każdego katalogu przeszukiwanego przez systemowy kompilator C (odczytanego z `$CC -E -v -x c /dev/null`,
|
każdego katalogu przeszukiwanego przez systemowy kompilator C (odczytanego z `$CC -E -v -x c /dev/null`,
|
||||||
dzięki czemu działa to na NixOS). `gnu11`, bo glibc w trybie ścisłego ISO ukrywa `M_PI` i podobne.
|
dzięki czemu działa to na NixOS). `gnu11`, bo glibc w trybie ścisłego ISO ukrywa `M_PI` i podobne.
|
||||||
|
|
||||||
|
|
@ -657,7 +657,7 @@ i atrybutami, a wyniki-struktury odbudowuje w ten sam sposób.
|
||||||
`cThunk(fn)` to odwrotność dla `addr(fn)`: wewnętrzna funkcja z sygnaturą C, która składa parametry-struktury
|
`cThunk(fn)` to odwrotność dla `addr(fn)`: wewnętrzna funkcja z sygnaturą C, która składa parametry-struktury
|
||||||
z rejestrów albo pamięci `byval`, woła funkcję Fincha i obniża jej wynik. Akceptowane są tylko sygnatury zgodne z C.
|
z rejestrów albo pamięci `byval`, woła funkcję Fincha i obniża jej wynik. Akceptowane są tylko sygnatury zgodne z C.
|
||||||
|
|
||||||
`tests/run/c_structs.fn` sprawdza `{float,float}`, `{float,float,float}`, `{u8×4}`, `{double,int}`,
|
`tests/run/c_structs.fch` sprawdza `{float,float}`, `{float,float,float}`, `{u8×4}`, `{double,int}`,
|
||||||
`{i64,i64}`, strukturę 24-bajtową, strukturę z `char[8]`, zagnieżdżone struktury, wyczerpanie rejestrów
|
`{i64,i64}`, strukturę 24-bajtową, strukturę z `char[8]`, zagnieżdżone struktury, wyczerpanie rejestrów
|
||||||
przy 13 argumentach-strukturach oraz wywołania zwrotne przyjmujące i zwracające struktury, na bibliotece C
|
przy 13 argumentach-strukturach oraz wywołania zwrotne przyjmujące i zwracające struktury, na bibliotece C
|
||||||
kompilowanej przez `link "abi.c"`. Wyniki zgadzają się z tymi samymi wywołaniami zrobionymi z C.
|
kompilowanej przez `link "abi.c"`. Wyniki zgadzają się z tymi samymi wywołaniami zrobionymi z C.
|
||||||
|
|
@ -665,7 +665,7 @@ kompilowanej przez `link "abi.c"`. Wyniki zgadzają się z tymi samymi wywołani
|
||||||
### 13.4 Linkowanie kodu C
|
### 13.4 Linkowanie kodu C
|
||||||
|
|
||||||
`link "nazwa"` próbuje `pkg-config --libs nazwa`, a potem `-lnazwa`. `link "plik.c"` kompiluje plik
|
`link "nazwa"` próbuje `pkg-config --libs nazwa`, a potem `-lnazwa`. `link "plik.c"` kompiluje plik
|
||||||
(względem pliku `.fn`) przez `$CC -O2 -fPIC -c` do tymczasowego obiektu; pliki `.o` i `.a` przechodzą
|
(względem pliku `.fch`) przez `$CC -O2 -fPIC -c` do tymczasowego obiektu; pliki `.o` i `.a` przechodzą
|
||||||
bez zmian. Gdy linkowanie się nie uda, `linkFailed()` mapuje niezdefiniowane symbole na nagłówek, który
|
bez zmian. Gdy linkowanie się nie uda, `linkFailed()` mapuje niezdefiniowane symbole na nagłówek, który
|
||||||
je zadeklarował, i wypisuje linijkę `link` do dopisania.
|
je zadeklarował, i wypisuje linijkę `link` do dopisania.
|
||||||
|
|
||||||
|
|
@ -676,7 +676,7 @@ je zadeklarował, i wypisuje linijkę `link` do dopisania.
|
||||||
- **Cel:** `sys::getDefaultTargetTriple()`, CPU `generic`, `Reloc::PIC_`. Data layout jest ustawiany na
|
- **Cel:** `sys::getDefaultTargetTriple()`, CPU `generic`, `Reloc::PIC_`. Data layout jest ustawiany na
|
||||||
module, zanim powstanie jakikolwiek IR (od niego zależy wyrównanie loadów i store'ów).
|
module, zanim powstanie jakikolwiek IR (od niego zależy wyrównanie loadów i store'ów).
|
||||||
- **Optymalizacja:** `PassBuilder::buildPerModuleDefaultPipeline(O2)`: środkowa część `-O2` z clanga.
|
- **Optymalizacja:** `PassBuilder::buildPerModuleDefaultPipeline(O2)`: środkowa część `-O2` z clanga.
|
||||||
`-O0` ją pomija (`finch ir plik.fn -O0` pokazuje surowe wyjście `Codegen`).
|
`-O0` ją pomija (`finch ir plik.fch -O0` pokazuje surowe wyjście `Codegen`).
|
||||||
- **Emisja:** stary `PassManager` + `addPassesToEmitFile(ObjectFile)`.
|
- **Emisja:** stary `PassManager` + `addPassesToEmitFile(ObjectFile)`.
|
||||||
- **Linkowanie:** `$CC prog.o rt.o [obiekty użytkownika] -lm [biblioteki]`. Na Niksie wrapper cc zamienia
|
- **Linkowanie:** `$CC prog.o rt.o [obiekty użytkownika] -lm [biblioteki]`. Na Niksie wrapper cc zamienia
|
||||||
ścieżki `-L` na `RPATH`, więc programy działają bez `LD_LIBRARY_PATH`.
|
ścieżki `-L` na `RPATH`, więc programy działają bez `LD_LIBRARY_PATH`.
|
||||||
|
|
@ -717,11 +717,11 @@ wypisuje `internal compiler error (please report)` i kończy proces z kodem 2.
|
||||||
|
|
||||||
| Plik | Zawartość |
|
| Plik | Zawartość |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `boot/lexer.fn` | lekser (tokeny to `{kind, text, line, col, nl}`; rodzaje to słowa, a słowa kluczowe i operatory są swoim własnym rodzajem) |
|
| `boot/lexer.fch` | lekser (tokeny to `{kind, text, line, col, nl}`; rodzaje to słowa, a słowa kluczowe i operatory są swoim własnym rodzajem) |
|
||||||
| `boot/ast.fn` | `Type` (`kind`, `elem` jako tablica 0/1 elementów, `name`, `module`), jeden ogólny `Node` na wszystko, `Program` |
|
| `boot/ast.fch` | `Type` (`kind`, `elem` jako tablica 0/1 elementów, `name`, `module`), jeden ogólny `Node` na wszystko, `Program` |
|
||||||
| `boot/parser.fn` | ta sama gramatyka i reguły nowych linii co parser C++, wspinanie się po priorytetach dla operatorów binarnych |
|
| `boot/parser.fch` | ta sama gramatyka i reguły nowych linii co parser C++, wspinanie się po priorytetach dla operatorów binarnych |
|
||||||
| `boot/gen.fn` | sprawdzanie typów + generator **tekstowego LLVM IR**: zasięgi, miejsca, konwersje, kopie, helpery, wypisywanie, kontrole, metody tablic i tekstów |
|
| `boot/gen.fch` | sprawdzanie typów + generator **tekstowego LLVM IR**: zasięgi, miejsca, konwersje, kopie, helpery, wypisywanie, kontrole, metody tablic i tekstów |
|
||||||
| `boot/main.fn` | ładowanie modułów i sterownik: zapisuje `.ll`, uruchamia `clang -O2 plik.ll runtime/finch_rt.c` |
|
| `boot/main.fch` | ładowanie modułów i sterownik: zapisuje `.ll`, uruchamia `clang -O2 plik.ll runtime/finch_rt.c` |
|
||||||
|
|
||||||
Obsługuje rdzeń języka: `int float bool char str`, `[]T`, struktury (wartości domyślne, konstruktory po
|
Obsługuje rdzeń języka: `int float bool char str`, `[]T`, struktury (wartości domyślne, konstruktory po
|
||||||
nazwie i po kolei), `ptr[T]`/`addr`/`new`/`free`/`null`, wszystkie instrukcje poza `defer`, moduły oraz
|
nazwie i po kolei), `ptr[T]`/`addr`/`new`/`free`/`null`, wszystkie instrukcje poza `defer`, moduły oraz
|
||||||
|
|
@ -763,9 +763,9 @@ wynik z oczekiwanym. Kompilacja samego siebie zajmuje kompilatorowi samohostują
|
||||||
|
|
||||||
## 19. Testy
|
## 19. Testy
|
||||||
|
|
||||||
- `tests/run.sh`: każdy `tests/run/*.fn` z `fn main` musi wypisać dokładnie swój `.out` (stdin z `.in`, jeśli
|
- `tests/run.sh`: każdy `tests/run/*.fch` z `fn main` musi wypisać dokładnie swój `.out` (stdin z `.in`, jeśli
|
||||||
jest); pliki bez `main` to moduły albo pliki pomocnicze. Każdy `tests/fail/*.fn` musi się nie udać z tekstem
|
jest); pliki bez `main` to moduły albo pliki pomocnicze. Każdy `tests/fail/*.fch` musi się nie udać z tekstem
|
||||||
z linijki `// expect:`. Obecnie **58 passed, 0 failed**.
|
z linijki `// expect:`. Obecnie **59 passed, 0 failed**.
|
||||||
- `MEMCHECK=1 tests/run.sh`: to samo plus valgrind na każdym programie (bez wycieków i złych dostępów).
|
- `MEMCHECK=1 tests/run.sh`: to samo plus valgrind na każdym programie (bez wycieków i złych dostępów).
|
||||||
- `tests/boot.sh`: punkt stały samohostowania i przebieg na podzbiorze (§17).
|
- `tests/boot.sh`: punkt stały samohostowania i przebieg na podzbiorze (§17).
|
||||||
|
|
||||||
|
|
@ -797,7 +797,7 @@ wynik z oczekiwanym. Kompilacja samego siebie zajmuje kompilatorowi samohostują
|
||||||
|
|
||||||
| Funkcja | Robi |
|
| Funkcja | Robi |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `Loader::load` / `findModule` | czyta, leksuje i parsuje plik oraz rekurencyjnie jego moduły / szuka `nazwa.fn` |
|
| `Loader::load` / `findModule` | czyta, leksuje i parsuje plik oraz rekurencyjnie jego moduły / szuka `nazwa.fch` |
|
||||||
| `hostMachine`, `optimize`, `emitObject` | maszyna docelowa, potok O2, plik obiektowy |
|
| `hostMachine`, `optimize`, `emitObject` | maszyna docelowa, potok O2, plik obiektowy |
|
||||||
| `runtimeObject` | kompiluje runtime i trzyma go w cache |
|
| `runtimeObject` | kompiluje runtime i trzyma go w cache |
|
||||||
| `capture`, `libFlags`, `link` | uruchamia polecenie / pkg-config albo `-l` / linkowanie, z kompilacją plików z `link "x.c"` |
|
| `capture`, `libFlags`, `link` | uruchamia polecenie / pkg-config albo `-l` / linkowanie, z kompilacją plików z `link "x.c"` |
|
||||||
|
|
@ -868,7 +868,7 @@ wynik z oczekiwanym. Kompilacja samego siebie zajmuje kompilatorowi samohostują
|
||||||
|
|
||||||
**Funkcja wbudowana:** dodaj nazwę do `isBuiltin()`, obsłuż ją w `call()`, zaimplementuj (w IR albo jako
|
**Funkcja wbudowana:** dodaj nazwę do `isBuiltin()`, obsłuż ją w `call()`, zaimplementuj (w IR albo jako
|
||||||
funkcję runtime'u zadeklarowaną w tabeli `rt()`), dodaj przypadki w `tests/run/` i `tests/fail/`. Jeśli ma
|
funkcję runtime'u zadeklarowaną w tabeli `rt()`), dodaj przypadki w `tests/run/` i `tests/fail/`. Jeśli ma
|
||||||
istnieć też w kompilatorze samohostującym, dodaj ją do `call()` i `runtimeDecls()` w `boot/gen.fn`.
|
istnieć też w kompilatorze samohostującym, dodaj ją do `call()` i `runtimeDecls()` w `boot/gen.fch`.
|
||||||
|
|
||||||
**Metoda tablicy albo tekstu:** dodaj gałąź w `arrayMethod()` albo w części `str` w `method()`; jeśli
|
**Metoda tablicy albo tekstu:** dodaj gałąź w `arrayMethod()` albo w części `str` w `method()`; jeśli
|
||||||
zmienia tablicę, dopisz jej nazwę do zbiorów `changing` w `method()` i `mutatesExpr()`, żeby pożyczanie
|
zmienia tablicę, dopisz jej nazwę do zbiorów `changing` w `method()` i `mutatesExpr()`, żeby pożyczanie
|
||||||
|
|
|
||||||
|
|
@ -92,11 +92,11 @@ fn main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Zapisz plik w folderze `Finch` pod nazwą **`czesc.fn`**. Końcówka `.fn` mówi, że to program w Finchu.
|
3. Zapisz plik w folderze `Finch` pod nazwą **`czesc.fch`**. Końcówka `.fch` mówi, że to program w Finchu.
|
||||||
4. W Terminalu wpisz:
|
4. W Terminalu wpisz:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./build/finch run czesc.fn
|
./build/finch run czesc.fch
|
||||||
```
|
```
|
||||||
|
|
||||||
Zobaczysz:
|
Zobaczysz:
|
||||||
|
|
@ -659,14 +659,14 @@ fn main() {
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
czesc.fn:3:11: error: there is no variable named 'wiekk'
|
czesc.fch:3:11: error: there is no variable named 'wiekk'
|
||||||
3 | print(wiekk)
|
3 | print(wiekk)
|
||||||
| ^
|
| ^
|
||||||
```
|
```
|
||||||
|
|
||||||
Jak to czytać:
|
Jak to czytać:
|
||||||
|
|
||||||
- `czesc.fn` to plik,
|
- `czesc.fch` to plik,
|
||||||
- `3` to numer **linijki**,
|
- `3` to numer **linijki**,
|
||||||
- `11` to który znak w linijce (**kolumna**),
|
- `11` to który znak w linijce (**kolumna**),
|
||||||
- potem wyjaśnienie, a pod nim ta linijka ze strzałką `^` wskazującą problem.
|
- potem wyjaśnienie, a pod nim ta linijka ze strzałką `^` wskazującą problem.
|
||||||
|
|
@ -823,7 +823,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`return` w `main` kończy program. (Wersja z losową liczbą jest w `examples/guess.fn`.)
|
`return` w `main` kończy program. (Wersja z losową liczbą jest w `examples/guess.fch`.)
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Projekt 7: Średnia ocen
|
### Projekt 7: Średnia ocen
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ kompilator działa w środku. Do tego służy [przewodnik dla inżynierów](dla-
|
||||||
|
|
||||||
## 1. Czym jest Finch
|
## 1. Czym jest Finch
|
||||||
|
|
||||||
Finch to język **kompilowany**. Narzędzie `finch` tłumaczy plik `.fn` na prawdziwy program
|
Finch to język **kompilowany**. Narzędzie `finch` tłumaczy plik `.fch` na prawdziwy program
|
||||||
(natywny plik wykonywalny, taki jak te z C). Korzysta przy tym z **LLVM**, tego samego zaplecza
|
(natywny plik wykonywalny, taki jak te z C). Korzysta przy tym z **LLVM**, tego samego zaplecza
|
||||||
kompilatorów, na którym działają clang, Rust i Swift.
|
kompilatorów, na którym działają clang, Rust i Swift.
|
||||||
|
|
||||||
|
|
@ -107,9 +107,9 @@ Inny kompilator wskażesz zmienną `CC`.
|
||||||
## 3. Polecenie finch
|
## 3. Polecenie finch
|
||||||
|
|
||||||
```
|
```
|
||||||
finch run <plik.fn> [argumenty...] skompiluj i od razu uruchom
|
finch run <plik.fch> [argumenty...] skompiluj i od razu uruchom
|
||||||
finch build <plik.fn> [-o nazwa] skompiluj do programu (domyślna nazwa: nazwa pliku)
|
finch build <plik.fch> [-o nazwa] skompiluj do programu (domyślna nazwa: nazwa pliku)
|
||||||
finch ir <plik.fn> pokaż LLVM IR (dla ciekawskich)
|
finch ir <plik.fch> pokaż LLVM IR (dla ciekawskich)
|
||||||
finch version pokaż wersję
|
finch version pokaż wersję
|
||||||
|
|
||||||
opcje:
|
opcje:
|
||||||
|
|
@ -121,10 +121,10 @@ opcje:
|
||||||
Przykłady:
|
Przykłady:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
finch run hello.fn # wypisuje wynik
|
finch run hello.fch # wypisuje wynik
|
||||||
finch run narzedzie.fn dane.txt -v # argumenty po nazwie pliku trafiają do programu
|
finch run narzedzie.fch dane.txt -v # argumenty po nazwie pliku trafiają do programu
|
||||||
finch build gra.fn -o mojagra # tworzy ./mojagra
|
finch build gra.fch -o mojagra # tworzy ./mojagra
|
||||||
finch build gra.fn -g -O0 # wersja dla debuggera
|
finch build gra.fch -g -O0 # wersja dla debuggera
|
||||||
```
|
```
|
||||||
|
|
||||||
`finch run` przekazuje dalej kod wyjścia programu. Jeśli program się wysypie, powie jak
|
`finch run` przekazuje dalej kod wyjścia programu. Jeśli program się wysypie, powie jak
|
||||||
|
|
@ -140,7 +140,7 @@ finch build gra.fn -g -O0 # wersja dla debuggera
|
||||||
kilka linijek */
|
kilka linijek */
|
||||||
|
|
||||||
import "math.h" // nagłówki C (opcjonalne), na górze
|
import "math.h" // nagłówki C (opcjonalne), na górze
|
||||||
import ksztalty // moduły Fincha (opcjonalne): ksztalty.fn obok tego pliku
|
import ksztalty // moduły Fincha (opcjonalne): ksztalty.fch obok tego pliku
|
||||||
link "m" // biblioteki albo pliki C do dolinkowania (opcjonalne)
|
link "m" // biblioteki albo pliki C do dolinkowania (opcjonalne)
|
||||||
|
|
||||||
struct Punkt { // własne typy
|
struct Punkt { // własne typy
|
||||||
|
|
@ -503,11 +503,11 @@ Użycie `.value`, `.pole` albo `[i]` na wskaźniku `null` **zatrzymuje program**
|
||||||
|
|
||||||
## 16. Moduły
|
## 16. Moduły
|
||||||
|
|
||||||
Program możesz podzielić na pliki. `import ksztalty` wczytuje `ksztalty.fn` z tego samego folderu
|
Program możesz podzielić na pliki. `import ksztalty` wczytuje `ksztalty.fch` z tego samego folderu
|
||||||
(albo z folderu wymienionego w zmiennej środowiskowej `FINCH_PATH`, rozdzielonych `:`).
|
(albo z folderu wymienionego w zmiennej środowiskowej `FINCH_PATH`, rozdzielonych `:`).
|
||||||
|
|
||||||
```c
|
```c
|
||||||
// ksztalty.fn
|
// ksztalty.fch
|
||||||
struct Pudlo {
|
struct Pudlo {
|
||||||
float w
|
float w
|
||||||
float h
|
float h
|
||||||
|
|
@ -519,7 +519,7 @@ fn pole(Pudlo p) -> float {
|
||||||
```
|
```
|
||||||
|
|
||||||
```c
|
```c
|
||||||
// main.fn
|
// main.fch
|
||||||
import ksztalty
|
import ksztalty
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -541,7 +541,7 @@ fn main() {
|
||||||
```c
|
```c
|
||||||
import "stdio.h"
|
import "stdio.h"
|
||||||
import "GLFW/glfw3.h" // ścieżka w systemowych katalogach nagłówków
|
import "GLFW/glfw3.h" // ścieżka w systemowych katalogach nagłówków
|
||||||
import "mojalib.h" // nagłówek obok twojego pliku .fn
|
import "mojalib.h" // nagłówek obok twojego pliku .fch
|
||||||
```
|
```
|
||||||
|
|
||||||
Finch czyta nagłówek przez **libclang** (parser C z clanga) i udostępnia:
|
Finch czyta nagłówek przez **libclang** (parser C z clanga) i udostępnia:
|
||||||
|
|
@ -555,7 +555,7 @@ Finch czyta nagłówek przez **libclang** (parser C z clanga) i udostępnia:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
link "glfw" // używa libglfw.so (najpierw pyta pkg-config, jeśli jest)
|
link "glfw" // używa libglfw.so (najpierw pyta pkg-config, jeśli jest)
|
||||||
link "pomocnicze.c" // skompiluj i dołącz własny plik C (obok pliku .fn)
|
link "pomocnicze.c" // skompiluj i dołącz własny plik C (obok pliku .fch)
|
||||||
link "gotowe.a" // albo plik obiektowy / bibliotekę statyczną
|
link "gotowe.a" // albo plik obiektowy / bibliotekę statyczną
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -618,7 +618,7 @@ Jej parametry muszą być typami C (liczby, wskaźniki, struktury C).
|
||||||
### Błędy kompilacji
|
### Błędy kompilacji
|
||||||
|
|
||||||
```
|
```
|
||||||
gra.fn:12:9: error: 'wynik' must be int, but this is str
|
gra.fch:12:9: error: 'wynik' must be int, but this is str
|
||||||
12 | wynik = "wysoki"
|
12 | wynik = "wysoki"
|
||||||
| ^
|
| ^
|
||||||
```
|
```
|
||||||
|
|
@ -645,9 +645,9 @@ Zamiast niezdefiniowanego zachowania (jak w C) Finch zatrzymuje się z komunikat
|
||||||
## 19. Debugowanie
|
## 19. Debugowanie
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
finch build gra.fn -g -O0 -o gra
|
finch build gra.fch -g -O0 -o gra
|
||||||
gdb ./gra
|
gdb ./gra
|
||||||
(gdb) break gra.fn:12
|
(gdb) break gra.fch:12
|
||||||
(gdb) run
|
(gdb) run
|
||||||
(gdb) bt # gdzie jesteśmy, przez które funkcje
|
(gdb) bt # gdzie jesteśmy, przez które funkcje
|
||||||
(gdb) info locals # wszystkie zmienne
|
(gdb) info locals # wszystkie zmienne
|
||||||
|
|
@ -667,7 +667,7 @@ zmienne widoczne; bez niego optymalizator może część z nich usunąć.
|
||||||
| `… come from "x.h", but its library isn't linked` | Dopisz `link "nazwa"` na górze pliku. |
|
| `… come from "x.h", but its library isn't linked` | Dopisz `link "nazwa"` na górze pliku. |
|
||||||
| `the library 'x' wasn't found` | Nie jest zainstalowana albo nazwa jest zła. Na Niksie dopisz ją do `shell.nix` i pracuj w `nix-shell`. |
|
| `the library 'x' wasn't found` | Nie jest zainstalowana albo nazwa jest zła. Na Niksie dopisz ją do `shell.nix` i pracuj w `nix-shell`. |
|
||||||
| `the C function 'f' can't be used from Finch yet` | Używa unii, pól bitowych albo innego nieobsługiwanego typu przez wartość. Poszukaj wariantu ze wskaźnikami. |
|
| `the C function 'f' can't be used from Finch yet` | Używa unii, pól bitowych albo innego nieobsługiwanego typu przez wartość. Poszukaj wariantu ze wskaźnikami. |
|
||||||
| `can't find the module 'x'` | Połóż `x.fn` obok importującego pliku albo ustaw `FINCH_PATH`. |
|
| `can't find the module 'x'` | Połóż `x.fch` obok importującego pliku albo ustaw `FINCH_PATH`. |
|
||||||
| `couldn't build the Finch runtime` | Brak działającego kompilatora C. Zainstaluj gcc lub clang albo ustaw `CC`. |
|
| `couldn't build the Finch runtime` | Brak działającego kompilatora C. Zainstaluj gcc lub clang albo ustaw `CC`. |
|
||||||
| Biblioteka nie znajduje się przy starcie programu | Zlinkowano ją z folderu, którego system nie przeszukuje. Uruchamiaj w tym samym `nix-shell` albo zainstaluj ją systemowo. |
|
| Biblioteka nie znajduje się przy starcie programu | Zlinkowano ją z folderu, którego system nie przeszukuje. Uruchamiaj w tym samym `nix-shell` albo zainstaluj ją systemowo. |
|
||||||
| `clang` w `nix-shell` nie widzi `stdio.h` | Masz stary `shell.nix`: `llvmPackages.clang` musi być przed `llvmPackages.libclang`. |
|
| `clang` w `nix-shell` nie widzi `stdio.h` | Masz stary `shell.nix`: `llvmPackages.clang` musi być przed `llvmPackages.libclang`. |
|
||||||
|
|
@ -694,7 +694,7 @@ Finch/
|
||||||
```
|
```
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
tests/run.sh # → 58 passed, 0 failed
|
tests/run.sh # → 59 passed, 0 failed
|
||||||
MEMCHECK=1 tests/run.sh # to samo pod valgrindem: bez wycieków i złych dostępów do pamięci
|
MEMCHECK=1 tests/run.sh # to samo pod valgrindem: bez wycieków i złych dostępów do pamięci
|
||||||
tests/boot.sh # sprawdzenie samohostowania (wymaga clanga)
|
tests/boot.sh # sprawdzenie samohostowania (wymaga clanga)
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ pkgs.mkShell {
|
||||||
ninja
|
ninja
|
||||||
pkg-config # finds the right flags for `link "..."`
|
pkg-config # finds the right flags for `link "..."`
|
||||||
|
|
||||||
# for examples/window.fn and examples/raylib.fn
|
# for examples/window.fch and examples/raylib.fch
|
||||||
glfw
|
glfw
|
||||||
libGL
|
libGL
|
||||||
raylib
|
raylib
|
||||||
|
|
|
||||||
|
|
@ -298,10 +298,10 @@ struct Import {
|
||||||
|
|
||||||
struct Link {
|
struct Link {
|
||||||
Pos pos;
|
Pos pos;
|
||||||
std::string lib; // "glfw" -> libglfw.so; "helpers.c" / ".o" / ".a" -> a file next to the .fn file
|
std::string lib; // "glfw" -> libglfw.so; "helpers.c" / ".o" / ".a" -> a file next to the .fch file
|
||||||
};
|
};
|
||||||
|
|
||||||
// One .fn file.
|
// One .fch file.
|
||||||
struct Program {
|
struct Program {
|
||||||
std::string module; // "" for the main file, else the module's name
|
std::string module; // "" for the main file, else the module's name
|
||||||
std::string path;
|
std::string path;
|
||||||
|
|
|
||||||
|
|
@ -52,5 +52,5 @@ struct CImports {
|
||||||
std::map<std::string, CStruct> structs;
|
std::map<std::string, CStruct> structs;
|
||||||
};
|
};
|
||||||
|
|
||||||
// `dirs`: folders searched first for "local.h" headers (the .fn files' folders).
|
// `dirs`: folders searched first for "local.h" headers (the .fch files' folders).
|
||||||
CImports importHeaders(const std::vector<Import> &imports, const std::vector<std::string> &dirs);
|
CImports importHeaders(const std::vector<Import> &imports, const std::vector<std::string> &dirs);
|
||||||
|
|
|
||||||
17
src/main.cpp
17
src/main.cpp
|
|
@ -34,9 +34,9 @@ static void usage() {
|
||||||
std::fprintf(stderr,
|
std::fprintf(stderr,
|
||||||
"Finch " FINCH_VERSION " - small, quick, sharp.\n\n"
|
"Finch " FINCH_VERSION " - small, quick, sharp.\n\n"
|
||||||
"usage:\n"
|
"usage:\n"
|
||||||
" finch run <file.fn> [args...] compile and run\n"
|
" finch run <file.fch> [args...] compile and run\n"
|
||||||
" finch build <file.fn> [-o name] compile to a program\n"
|
" finch build <file.fch> [-o name] compile to a program\n"
|
||||||
" finch ir <file.fn> show the generated LLVM IR\n"
|
" finch ir <file.fch> show the generated LLVM IR\n"
|
||||||
" finch version show the version\n"
|
" finch version show the version\n"
|
||||||
"\n"
|
"\n"
|
||||||
" -l <lib> link a C library, same as link \"lib\" in the file\n"
|
" -l <lib> link a C library, same as link \"lib\" in the file\n"
|
||||||
|
|
@ -68,7 +68,12 @@ struct Loader {
|
||||||
void load(const std::string &path, const std::string &module, Pos from) {
|
void load(const std::string &path, const std::string &module, Pos from) {
|
||||||
std::string text;
|
std::string text;
|
||||||
if (!readFile(path, text)) {
|
if (!readFile(path, text)) {
|
||||||
if (from.line) failAt(from.file, from.line, from.col, "can't find the module '" + module + "' (looked for " + path + ")");
|
if (from.line) {
|
||||||
|
std::string old = path.substr(0, path.size() - 4) + ".fn";
|
||||||
|
if (sys::fs::exists(old))
|
||||||
|
failAt(from.file, from.line, from.col, "found " + old + ", but Finch files now end in .fch: rename it to " + path);
|
||||||
|
failAt(from.file, from.line, from.col, "can't find the module '" + module + "' (looked for " + path + ")");
|
||||||
|
}
|
||||||
std::fprintf(stderr, "error: can't open '%s'\n", path.c_str());
|
std::fprintf(stderr, "error: can't open '%s'\n", path.c_str());
|
||||||
std::exit(1);
|
std::exit(1);
|
||||||
}
|
}
|
||||||
|
|
@ -91,12 +96,12 @@ struct Loader {
|
||||||
|
|
||||||
// next to the importing file, then in $FINCH_PATH folders
|
// next to the importing file, then in $FINCH_PATH folders
|
||||||
static std::string findModule(const std::string &dir, const std::string &name) {
|
static std::string findModule(const std::string &dir, const std::string &name) {
|
||||||
std::string here = dir + "/" + name + ".fn";
|
std::string here = dir + "/" + name + ".fch";
|
||||||
if (sys::fs::exists(here)) return here;
|
if (sys::fs::exists(here)) return here;
|
||||||
if (const char *fp = std::getenv("FINCH_PATH")) {
|
if (const char *fp = std::getenv("FINCH_PATH")) {
|
||||||
std::stringstream ss(fp);
|
std::stringstream ss(fp);
|
||||||
for (std::string d; std::getline(ss, d, ':');)
|
for (std::string d; std::getline(ss, d, ':');)
|
||||||
if (!d.empty() && sys::fs::exists(d + "/" + name + ".fn")) return d + "/" + name + ".fn";
|
if (!d.empty() && sys::fs::exists(d + "/" + name + ".fch")) return d + "/" + name + ".fch";
|
||||||
}
|
}
|
||||||
return here;
|
return here;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ private:
|
||||||
expect(Tok::Link);
|
expect(Tok::Link);
|
||||||
l.lib = expect(Tok::String, "a library name in quotes, like link \"glfw\"").text;
|
l.lib = expect(Tok::String, "a library name in quotes, like link \"glfw\"").text;
|
||||||
auto ends = [&](const char *x) { size_t n = std::strlen(x); return l.lib.size() > n && l.lib.compare(l.lib.size() - n, n, x) == 0; };
|
auto ends = [&](const char *x) { size_t n = std::strlen(x); return l.lib.size() > n && l.lib.compare(l.lib.size() - n, n, x) == 0; };
|
||||||
if (ends(".c") || ends(".o") || ends(".a")) return l; // your own C code, next to the .fn file
|
if (ends(".c") || ends(".o") || ends(".a")) return l; // your own C code, next to the .fch file
|
||||||
if (l.lib.rfind("lib", 0) == 0 || l.lib.find(".so") != std::string::npos || l.lib.find('/') != std::string::npos)
|
if (l.lib.rfind("lib", 0) == 0 || l.lib.find(".so") != std::string::npos || l.lib.find('/') != std::string::npos)
|
||||||
fail(l.pos.line, l.pos.col, "write just the library's name: for libglfw.so that's link \"glfw\"");
|
fail(l.pos.line, l.pos.col, "write just the library's name: for libglfw.so that's link \"glfw\"");
|
||||||
return l;
|
return l;
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ cd "$(dirname "$0")/.."
|
||||||
tmp=$(mktemp -d)
|
tmp=$(mktemp -d)
|
||||||
trap 'rm -rf "$tmp"' EXIT
|
trap 'rm -rf "$tmp"' EXIT
|
||||||
|
|
||||||
build/finch build boot/main.fn -o "$tmp/boot1" || exit 1
|
build/finch build boot/main.fch -o "$tmp/boot1" || exit 1
|
||||||
"$tmp/boot1" build boot/main.fn -o "$tmp/boot2" || exit 1
|
"$tmp/boot1" build boot/main.fch -o "$tmp/boot2" || exit 1
|
||||||
"$tmp/boot1" ir boot/main.fn > "$tmp/stage2.ll"
|
"$tmp/boot1" ir boot/main.fch > "$tmp/stage2.ll"
|
||||||
"$tmp/boot2" ir boot/main.fn > "$tmp/stage3.ll"
|
"$tmp/boot2" ir boot/main.fch > "$tmp/stage3.ll"
|
||||||
if cmp -s "$tmp/stage2.ll" "$tmp/stage3.ll"; then
|
if cmp -s "$tmp/stage2.ll" "$tmp/stage3.ll"; then
|
||||||
echo "bootstrap: stage 2 and stage 3 are identical ($(wc -l < "$tmp/stage3.ll") lines of IR)"
|
echo "bootstrap: stage 2 and stage 3 are identical ($(wc -l < "$tmp/stage3.ll") lines of IR)"
|
||||||
else
|
else
|
||||||
|
|
@ -19,17 +19,17 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
pass=0; failed=0; skipped=0
|
pass=0; failed=0; skipped=0
|
||||||
for f in tests/run/*.fn; do
|
for f in tests/run/*.fch; do
|
||||||
grep -q "^fn main" "$f" || continue
|
grep -q "^fn main" "$f" || continue
|
||||||
# the bootstrap compiler doesn't do C imports, sized numbers or defer
|
# the bootstrap compiler doesn't do C imports, sized numbers or defer
|
||||||
# (checked in the test and in the modules it imports)
|
# (checked in the test and in the modules it imports)
|
||||||
files="$f"
|
files="$f"
|
||||||
for m in $(sed -n 's/^import \([a-z_]*\)$/\1/p' "$f"); do files="$files tests/run/$m.fn"; done
|
for m in $(sed -n 's/^import \([a-z_]*\)$/\1/p' "$f"); do files="$files tests/run/$m.fch"; done
|
||||||
if grep -qE '^import "|^link |\b(u8|u16|u32|u64|i8|i16|i32|f32|f64)\b|defer |0x' $files; then
|
if grep -qE '^import "|^link |\b(u8|u16|u32|u64|i8|i16|i32|f32|f64)\b|defer |0x' $files; then
|
||||||
skipped=$((skipped + 1))
|
skipped=$((skipped + 1))
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
input="${f%.fn}.in"
|
input="${f%.fch}.in"
|
||||||
[ -f "$input" ] || input=/dev/null
|
[ -f "$input" ] || input=/dev/null
|
||||||
if ! "$tmp/boot2" build "$f" -o "$tmp/prog" > "$tmp/err" 2>&1; then
|
if ! "$tmp/boot2" build "$f" -o "$tmp/prog" > "$tmp/err" 2>&1; then
|
||||||
failed=$((failed + 1))
|
failed=$((failed + 1))
|
||||||
|
|
@ -38,12 +38,12 @@ for f in tests/run/*.fn; do
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
got=$("$tmp/prog" < "$input" 2>&1)
|
got=$("$tmp/prog" < "$input" 2>&1)
|
||||||
if [ "$got" == "$(cat "${f%.fn}.out")" ]; then
|
if [ "$got" == "$(cat "${f%.fch}.out")" ]; then
|
||||||
pass=$((pass + 1))
|
pass=$((pass + 1))
|
||||||
else
|
else
|
||||||
failed=$((failed + 1))
|
failed=$((failed + 1))
|
||||||
echo "FAIL (output) $f"
|
echo "FAIL (output) $f"
|
||||||
diff <(echo "$got") "${f%.fn}.out" | head -10
|
diff <(echo "$got") "${f%.fch}.out" | head -10
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
echo "self-hosted compiler: $pass passed, $failed failed, $skipped skipped (features it doesn't have)"
|
echo "self-hosted compiler: $pass passed, $failed failed, $skipped skipped (features it doesn't have)"
|
||||||
|
|
|
||||||
3
tests/fail/old_extension.fch
Normal file
3
tests/fail/old_extension.fch
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
// expect: but Finch files now end in .fch
|
||||||
|
import oldmodule
|
||||||
|
fn main() {}
|
||||||
2
tests/fail/oldmodule.fn
Normal file
2
tests/fail/oldmodule.fn
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
fn hi() {
|
||||||
|
}
|
||||||
15
tests/run.sh
15
tests/run.sh
|
|
@ -1,9 +1,9 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Finch test suite.
|
# Finch test suite.
|
||||||
# tests/run/*.fn must compile and print exactly what is in the matching .out file
|
# tests/run/*.fch must compile and print exactly what is in the matching .out file
|
||||||
# (files without `fn main` are modules or helpers and are skipped;
|
# (files without `fn main` are modules or helpers and are skipped;
|
||||||
# a matching .in file is fed to the program as its input)
|
# a matching .in file is fed to the program as its input)
|
||||||
# tests/fail/*.fn must fail; the first line is "// expect: <text that must appear in the error>"
|
# tests/fail/*.fch must fail; the first line is "// expect: <text that must appear in the error>"
|
||||||
# MEMCHECK=1 tests/run.sh also runs every program under valgrind: no leaks, no bad memory access
|
# MEMCHECK=1 tests/run.sh also runs every program under valgrind: no leaks, no bad memory access
|
||||||
set -u
|
set -u
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
@ -12,10 +12,10 @@ pass=0; failed=0
|
||||||
tmp=$(mktemp -d)
|
tmp=$(mktemp -d)
|
||||||
trap 'rm -rf "$tmp"' EXIT
|
trap 'rm -rf "$tmp"' EXIT
|
||||||
|
|
||||||
for f in run/*.fn; do
|
for f in run/*.fch; do
|
||||||
grep -q "^fn main" "$f" || continue
|
grep -q "^fn main" "$f" || continue
|
||||||
want="${f%.fn}.out"
|
want="${f%.fch}.out"
|
||||||
input="${f%.fn}.in"
|
input="${f%.fch}.in"
|
||||||
[ -f "$input" ] || input=/dev/null
|
[ -f "$input" ] || input=/dev/null
|
||||||
got=$("$FINCH" run "$f" < "$input" 2>&1)
|
got=$("$FINCH" run "$f" < "$input" 2>&1)
|
||||||
if [ "$got" != "$(cat "$want")" ]; then
|
if [ "$got" != "$(cat "$want")" ]; then
|
||||||
|
|
@ -25,7 +25,7 @@ for f in run/*.fn; do
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
if [ "${MEMCHECK:-0}" = 1 ]; then
|
if [ "${MEMCHECK:-0}" = 1 ]; then
|
||||||
exe="$tmp/$(basename "${f%.fn}")"
|
exe="$tmp/$(basename "${f%.fch}")"
|
||||||
"$FINCH" build "$f" -o "$exe" >/dev/null 2>&1
|
"$FINCH" build "$f" -o "$exe" >/dev/null 2>&1
|
||||||
if ! valgrind -q --leak-check=full --errors-for-leak-kinds=all --error-exitcode=99 "$exe" < "$input" >/dev/null 2>"$tmp/vg"; then
|
if ! valgrind -q --leak-check=full --errors-for-leak-kinds=all --error-exitcode=99 "$exe" < "$input" >/dev/null 2>"$tmp/vg"; then
|
||||||
failed=$((failed + 1))
|
failed=$((failed + 1))
|
||||||
|
|
@ -37,7 +37,8 @@ for f in run/*.fn; do
|
||||||
pass=$((pass + 1))
|
pass=$((pass + 1))
|
||||||
done
|
done
|
||||||
|
|
||||||
for f in fail/*.fn; do
|
for f in fail/*.fch; do
|
||||||
|
grep -q "^// expect:" "$f" || continue
|
||||||
expect=$(head -1 "$f" | sed -n 's|^// expect: ||p')
|
expect=$(head -1 "$f" | sed -n 's|^// expect: ||p')
|
||||||
got=$("$FINCH" run "$f" < /dev/null 2>&1)
|
got=$("$FINCH" run "$f" < /dev/null 2>&1)
|
||||||
status=$?
|
status=$?
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue