mirror of
https://github.com/Nexoniarz/Finch.git
synced 2026-11-03 00:44:27 +00:00
OpenGL and Vulkan examples; ptr(int)/int(ptr); trailing commas in calls; drop .fn hint
- examples/opengl.fch: OpenGL 3.3 core, shaders, VAO/VBO, a spinning triangle (checked offscreen) - examples/vulkan.fch: instance, GPUs with properties/memory/queues, logical device and queue - ptr(n) and int(p) conversions for C APIs that pass offsets as pointers - .ptr works on string literals; a comma after the last call argument is allowed - a missing header included by an imported header is now named in the error - the old .fn extension hint is gone (Finch is new, there are no old files) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
f897fb3140
commit
8497251dc5
22 changed files with 357 additions and 26 deletions
|
|
@ -105,6 +105,8 @@ Pick the guide that fits you:
|
|||
| [`examples/types.fch`](examples/types.fch) | sized numbers and pointers |
|
||||
| [`examples/c_import.fch`](examples/c_import.fch) | `printf`, `math.h`, `malloc`/`free`, `stderr` |
|
||||
| [`examples/window.fch`](examples/window.fch) | a GLFW + OpenGL window |
|
||||
| [`examples/opengl.fch`](examples/opengl.fch) | modern OpenGL 3.3: shaders, buffers, a spinning triangle |
|
||||
| [`examples/vulkan.fch`](examples/vulkan.fch) | Vulkan: list the GPUs, their memory and queues, create a device |
|
||||
| [`examples/raylib.fch`](examples/raylib.fch) | raylib with C structs by value |
|
||||
| [`examples/llvm.fch`](examples/llvm.fch) | Finch building LLVM IR through the LLVM-C API |
|
||||
| [`boot/`](boot/) | the Finch compiler, written in Finch |
|
||||
|
|
|
|||
|
|
@ -105,6 +105,8 @@ Wybierz przewodnik dla siebie:
|
|||
| [`examples/types.fch`](examples/types.fch) | liczby z rozmiarem i wskaźniki |
|
||||
| [`examples/c_import.fch`](examples/c_import.fch) | `printf`, `math.h`, `malloc`/`free`, `stderr` |
|
||||
| [`examples/window.fch`](examples/window.fch) | okno GLFW + OpenGL |
|
||||
| [`examples/opengl.fch`](examples/opengl.fch) | nowoczesny OpenGL 3.3: shadery, bufory, obracający się trójkąt |
|
||||
| [`examples/vulkan.fch`](examples/vulkan.fch) | Vulkan: lista kart graficznych, ich pamięć i kolejki, tworzenie urządzenia |
|
||||
| [`examples/raylib.fch`](examples/raylib.fch) | raylib ze strukturami C przez wartość |
|
||||
| [`examples/llvm.fch`](examples/llvm.fch) | Finch budujący LLVM IR przez API LLVM-C |
|
||||
| [`boot/`](boot/) | kompilator Fincha napisany w Finchu |
|
||||
|
|
|
|||
|
|
@ -42,11 +42,6 @@ struct Loader {
|
|||
fn load(ptr[Loader] l, str path, str module) {
|
||||
if !file_exists(path) {
|
||||
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 + ")")
|
||||
} else {
|
||||
print("error: can't open '" + path + "'")
|
||||
|
|
|
|||
|
|
@ -385,7 +385,7 @@ fn parseArgs(ptr[Parser] p, ptr[ast.Node] call) {
|
|||
expect(p, "(", "'('")
|
||||
p.depth += 1
|
||||
if !at(p, ")") {
|
||||
while true {
|
||||
while !at(p, ")") {
|
||||
name := ""
|
||||
if at(p, "ident") && peekKind(p, 1) == ":" {
|
||||
name = next(p).text
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ unary = ( "-" | "!" | "~" ) unary | postfix ;
|
|||
postfix = primary { "." IDENT [ args ] | "[" expr "]" } ;
|
||||
primary = INT | FLOAT | STRING | CHAR | "true" | "false" | "null"
|
||||
| IDENT [ args ] | "(" expr ")" | "[" [ expr { "," expr } [","] ] "]" ;
|
||||
args = "(" [ arg { "," arg } ] ")" ; arg = [ IDENT ":" ] expr ;
|
||||
args = "(" [ arg { "," arg } [","] ] ")" ; arg = [ IDENT ":" ] expr ;
|
||||
```
|
||||
|
||||
**Statement termination.** After a statement, `endOfStatement()` requires `}`, end of file, or a token
|
||||
|
|
@ -343,6 +343,7 @@ int + float → float; f32 + f64 → f64. There is no C integer promotion: `u8 +
|
|||
| `[]u8`/`[]char` → str | `finch_str_from_bytes` |
|
||||
| ptr → str | `finch_str_from_c` (borrowed, `cap = -1`) |
|
||||
| ptr ↔ ptr | nothing |
|
||||
| int → ptr / ptr → int | `inttoptr` / `ptrtoint` |
|
||||
|
||||
### Comparisons
|
||||
|
||||
|
|
|
|||
|
|
@ -244,6 +244,8 @@ str(42) // "42" (also floats, bools, chars; str([]u8) turns bytes into
|
|||
int("42") // 42 (stops the program if the text isn't a number)
|
||||
float("2.5") // 2.5
|
||||
ptr(p) // a typed pointer as an untyped one
|
||||
ptr(16) // a number as a pointer (C APIs that pass offsets as pointers, like OpenGL)
|
||||
int(p) // a pointer as a number (its address)
|
||||
str(p) // a C char pointer as text
|
||||
```
|
||||
|
||||
|
|
@ -345,6 +347,7 @@ fn no_result(str message) { // no "->": returns nothing
|
|||
To change the caller's variable, pass a pointer: see [Pointers](#14-pointers).
|
||||
- A function with `-> type` must return a value on every path. Finch checks this.
|
||||
- A function cannot be defined inside another function.
|
||||
- Long calls can be split over several lines inside the brackets; a comma after the last argument is fine.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -604,6 +607,30 @@ fn main() {
|
|||
To give C a function to call back (like `qsort`'s comparator), use `addr(myFunction)`.
|
||||
Its parameters must be C types (numbers, pointers, C structs).
|
||||
|
||||
### Graphics: OpenGL and Vulkan
|
||||
|
||||
Both work directly; `examples/opengl.fch` and `examples/vulkan.fch` are complete programs.
|
||||
|
||||
- **OpenGL 3+** on Linux: the modern functions (`glCreateShader`, `glGenVertexArrays`, …) are only
|
||||
declared when `GL_GLEXT_PROTOTYPES` is defined. Finch can't define C macros, so put the includes in a
|
||||
tiny header next to your program and import that:
|
||||
|
||||
```c
|
||||
// opengl.h
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
```
|
||||
|
||||
Then `import "opengl.h"`, `link "glfw"`, `link "GL"`. Out-parameters use `addr(...)`
|
||||
(`glGenBuffers(1, addr(vbo))`), buffer data uses `.ptr` (`vertices.ptr`), and byte offsets use
|
||||
`ptr(...)` (`glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, stride, ptr(8))`).
|
||||
- **Vulkan**: `import "vulkan/vulkan.h"` and `link "vulkan"`. Vulkan's create-info structs are built with
|
||||
named fields (missing ones are zero), handles are `ptr`, and lists come from the usual "call twice"
|
||||
pattern: once for the count, once with `list.ptr` after `list.resize(count)`. Function-like macros such as
|
||||
`VK_MAKE_API_VERSION` aren't available; compute the number instead (`1 << 22` is Vulkan 1.0).
|
||||
|
||||
### What does not work yet
|
||||
|
||||
- C **unions** by value, and structs with **bit fields** by value (pointers to them work).
|
||||
|
|
@ -680,7 +707,7 @@ Finch/
|
|||
├── src/ the compiler (C++)
|
||||
├── runtime/ finch_rt.c: the small runtime (text, arrays, input, files)
|
||||
├── boot/ the Finch compiler written in Finch (see the engineers' guide)
|
||||
├── examples/ hello, tour, types, structs, todo, guess, c_import, window, raylib, llvm
|
||||
├── examples/ hello, tour, types, structs, todo, guess, c_import, window, opengl, vulkan, raylib, llvm
|
||||
├── tests/
|
||||
│ ├── run/ programs + the exact output they must print (.out), input (.in)
|
||||
│ ├── fail/ programs that must fail, with the expected error in line 1
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ unary = ( "-" | "!" | "~" ) unary | postfix ;
|
|||
postfix = primary { "." IDENT [ args ] | "[" expr "]" } ;
|
||||
primary = INT | FLOAT | STRING | CHAR | "true" | "false" | "null"
|
||||
| IDENT [ args ] | "(" expr ")" | "[" [ expr { "," expr } [","] ] "]" ;
|
||||
args = "(" [ arg { "," arg } ] ")" ; arg = [ IDENT ":" ] expr ;
|
||||
args = "(" [ arg { "," arg } [","] ] ")" ; arg = [ IDENT ":" ] expr ;
|
||||
```
|
||||
|
||||
**Koniec instrukcji.** Po instrukcji `endOfStatement()` wymaga `}`, końca pliku albo tokenu
|
||||
|
|
@ -347,6 +347,7 @@ Nie ma promocji całkowitych z C: `u8 + u8` zostaje `u8`.
|
|||
| `[]u8`/`[]char` → str | `finch_str_from_bytes` |
|
||||
| ptr → str | `finch_str_from_c` (pożyczony, `cap = -1`) |
|
||||
| ptr ↔ ptr | nic |
|
||||
| int → ptr / ptr → int | `inttoptr` / `ptrtoint` |
|
||||
|
||||
### Porównania
|
||||
|
||||
|
|
|
|||
|
|
@ -244,6 +244,8 @@ str(42) // "42" (też float, bool, char; str([]u8) zamienia bajty na t
|
|||
int("42") // 42 (zatrzymuje program, jeśli tekst nie jest liczbą)
|
||||
float("2.5") // 2.5
|
||||
ptr(p) // wskaźnik z typem jako wskaźnik bez typu
|
||||
ptr(16) // liczba jako wskaźnik (API C, które przekazują przesunięcia jako wskaźniki, np. OpenGL)
|
||||
int(p) // wskaźnik jako liczba (jego adres)
|
||||
str(p) // wskaźnik na znaki z C jako tekst
|
||||
```
|
||||
|
||||
|
|
@ -346,6 +348,7 @@ fn bez_wyniku(str wiadomosc) { // bez "->": nic nie zwraca
|
|||
Żeby zmienić zmienną wywołującego, przekaż wskaźnik, zobacz [Wskaźniki](#14-wskaźniki).
|
||||
- Funkcja z `-> typ` musi zwrócić wartość na każdej ścieżce. Finch to sprawdza.
|
||||
- Funkcji nie można definiować wewnątrz innej funkcji.
|
||||
- Długie wywołania możesz rozbić na kilka linijek wewnątrz nawiasów; przecinek po ostatnim argumencie jest dozwolony.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -605,6 +608,30 @@ fn main() {
|
|||
Żeby dać C funkcję do wywołania zwrotnego (np. porównywarkę dla `qsort`), użyj `addr(mojaFunkcja)`.
|
||||
Jej parametry muszą być typami C (liczby, wskaźniki, struktury C).
|
||||
|
||||
### Grafika: OpenGL i Vulkan
|
||||
|
||||
Oba działają bezpośrednio; `examples/opengl.fch` i `examples/vulkan.fch` to kompletne programy.
|
||||
|
||||
- **OpenGL 3+** na Linuksie: nowoczesne funkcje (`glCreateShader`, `glGenVertexArrays`, …) są deklarowane
|
||||
dopiero po zdefiniowaniu `GL_GLEXT_PROTOTYPES`. Finch nie definiuje makr C, więc dołączenia umieść w małym
|
||||
nagłówku obok programu i zaimportuj go:
|
||||
|
||||
```c
|
||||
// opengl.h
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
```
|
||||
|
||||
Potem `import "opengl.h"`, `link "glfw"`, `link "GL"`. Parametry wyjściowe przekazujesz przez `addr(...)`
|
||||
(`glGenBuffers(1, addr(vbo))`), dane bufora przez `.ptr` (`vertices.ptr`), a przesunięcia w bajtach przez
|
||||
`ptr(...)` (`glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, stride, ptr(8))`).
|
||||
- **Vulkan**: `import "vulkan/vulkan.h"` i `link "vulkan"`. Struktury „create info” budujesz z nazwanymi polami
|
||||
(brakujące są zerami), uchwyty to `ptr`, a listy pobierasz typowym wzorcem „dwóch wywołań”: raz po liczbę,
|
||||
raz z `lista.ptr` po `lista.resize(liczba)`. Makra-funkcje, jak `VK_MAKE_API_VERSION`, nie są dostępne;
|
||||
policz liczbę sam (`1 << 22` to Vulkan 1.0).
|
||||
|
||||
### Co jeszcze nie działa
|
||||
|
||||
- **Unie** C przez wartość i struktury z **polami bitowymi** przez wartość (wskaźniki do nich działają).
|
||||
|
|
@ -682,7 +709,7 @@ Finch/
|
|||
├── src/ kompilator (C++)
|
||||
├── runtime/ finch_rt.c: mała biblioteka uruchomieniowa (teksty, tablice, wejście, pliki)
|
||||
├── boot/ kompilator Fincha napisany w Finchu (zobacz przewodnik dla inżynierów)
|
||||
├── examples/ hello, tour, types, structs, todo, guess, c_import, window, raylib, llvm
|
||||
├── examples/ hello, tour, types, structs, todo, guess, c_import, window, opengl, vulkan, raylib, llvm
|
||||
├── tests/
|
||||
│ ├── run/ programy + dokładny wynik (.out) i wejście (.in)
|
||||
│ ├── fail/ programy, które muszą się nie udać, z oczekiwanym błędem w 1. linijce
|
||||
|
|
|
|||
105
examples/opengl.fch
Normal file
105
examples/opengl.fch
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
// Modern OpenGL (3.3 core): a spinning, colorful triangle drawn with shaders.
|
||||
// Shows structs of numbers going to the GPU, C out-parameters with addr(), and
|
||||
// pointers made from numbers with ptr(...). Needs GLFW and OpenGL (in shell.nix).
|
||||
import "opengl.h"
|
||||
link "glfw"
|
||||
link "GL"
|
||||
|
||||
fn compileShader(u32 kind, str source) -> u32 {
|
||||
shader := glCreateShader(kind)
|
||||
src := source.ptr
|
||||
glShaderSource(shader, 1, addr(src), null)
|
||||
glCompileShader(shader)
|
||||
i32 ok
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, addr(ok))
|
||||
if ok == 0 {
|
||||
log := " ".repeat(1024)
|
||||
glGetShaderInfoLog(shader, 1024, null, log)
|
||||
print("shader error:", str(log.ptr))
|
||||
exit(1)
|
||||
}
|
||||
return shader
|
||||
}
|
||||
|
||||
fn makeProgram() -> u32 {
|
||||
vertex := "#version 330 core\n" +
|
||||
"layout(location = 0) in vec2 pos;\n" +
|
||||
"layout(location = 1) in vec3 color;\n" +
|
||||
"uniform float time;\n" +
|
||||
"out vec3 vColor;\n" +
|
||||
"void main() {\n" +
|
||||
" float c = cos(time), s = sin(time);\n" +
|
||||
" gl_Position = vec4(c * pos.x - s * pos.y, s * pos.x + c * pos.y, 0.0, 1.0);\n" +
|
||||
" vColor = color;\n" +
|
||||
"}\n"
|
||||
fragment := "#version 330 core\n" +
|
||||
"in vec3 vColor;\n" +
|
||||
"out vec4 outColor;\n" +
|
||||
"void main() { outColor = vec4(vColor, 1.0); }\n"
|
||||
|
||||
vs := compileShader(GL_VERTEX_SHADER, vertex)
|
||||
fs := compileShader(GL_FRAGMENT_SHADER, fragment)
|
||||
program := glCreateProgram()
|
||||
glAttachShader(program, vs)
|
||||
glAttachShader(program, fs)
|
||||
glLinkProgram(program)
|
||||
glDeleteShader(vs)
|
||||
glDeleteShader(fs)
|
||||
return program
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if glfwInit() == 0 {
|
||||
print("could not start GLFW")
|
||||
return
|
||||
}
|
||||
defer glfwTerminate()
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3)
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3)
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE)
|
||||
|
||||
window := glfwCreateWindow(800, 600, "Finch + OpenGL", null, null)
|
||||
if window == null {
|
||||
print("could not open a window (does your GPU support OpenGL 3.3?)")
|
||||
return
|
||||
}
|
||||
glfwMakeContextCurrent(window)
|
||||
glfwSwapInterval(1)
|
||||
print("OpenGL", str(glGetString(GL_VERSION)), "on", str(glGetString(GL_RENDERER)))
|
||||
|
||||
// x, y, r, g, b for each corner
|
||||
[]f32 vertices = [
|
||||
0.0, 0.6, 1.0, 0.48, 0.1,
|
||||
-0.6, -0.4, 0.2, 0.6, 1.0,
|
||||
0.6, -0.4, 0.6, 1.0, 0.4,
|
||||
]
|
||||
u32 vao
|
||||
u32 vbo
|
||||
glGenVertexArrays(1, addr(vao))
|
||||
glGenBuffers(1, addr(vbo))
|
||||
glBindVertexArray(vao)
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo)
|
||||
glBufferData(GL_ARRAY_BUFFER, vertices.len * 4, vertices.ptr, GL_STATIC_DRAW)
|
||||
i32 stride = 5 * 4
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, stride, ptr(0))
|
||||
glEnableVertexAttribArray(0)
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, stride, ptr(2 * 4))
|
||||
glEnableVertexAttribArray(1)
|
||||
|
||||
program := makeProgram()
|
||||
timeLoc := glGetUniformLocation(program, "time")
|
||||
|
||||
while glfwWindowShouldClose(window) == 0 {
|
||||
glClearColor(0.106, 0.122, 0.165, 1.0)
|
||||
glClear(GL_COLOR_BUFFER_BIT)
|
||||
glUseProgram(program)
|
||||
glUniform1f(timeLoc, f32(glfwGetTime()))
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3)
|
||||
glfwSwapBuffers(window)
|
||||
glfwPollEvents()
|
||||
}
|
||||
|
||||
glDeleteBuffers(1, addr(vbo))
|
||||
glDeleteVertexArrays(1, addr(vao))
|
||||
glDeleteProgram(program)
|
||||
}
|
||||
6
examples/opengl.h
Normal file
6
examples/opengl.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// Everything examples/opengl.fch needs from C. GL_GLEXT_PROTOTYPES makes <GL/glext.h>
|
||||
// declare the modern (OpenGL 3+) functions, which Linux's libGL exports directly.
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
137
examples/vulkan.fch
Normal file
137
examples/vulkan.fch
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
// Vulkan from Finch: create an instance, list every GPU with its properties, memory and
|
||||
// queues, then create a logical device and get a graphics queue. No window needed.
|
||||
// Shows big C structs (with arrays of structs inside), out-parameters and Vulkan handles.
|
||||
// Needs the Vulkan loader and headers (in shell.nix) and a Vulkan driver.
|
||||
import "vulkan/vulkan.h"
|
||||
link "vulkan"
|
||||
|
||||
fn check(i32 result, str what) {
|
||||
if result != VK_SUCCESS {
|
||||
print(what, "failed with VkResult", result)
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
fn version(u32 v) -> str {
|
||||
return str(v >> 22) + "." + str((v >> 12) & 0x3FF) + "." + str(v & 0xFFF)
|
||||
}
|
||||
|
||||
fn deviceType(i32 t) -> str {
|
||||
if t == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU {
|
||||
return "discrete GPU"
|
||||
}
|
||||
if t == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU {
|
||||
return "integrated GPU"
|
||||
}
|
||||
if t == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU {
|
||||
return "virtual GPU"
|
||||
}
|
||||
if t == VK_PHYSICAL_DEVICE_TYPE_CPU {
|
||||
return "CPU (software)"
|
||||
}
|
||||
return "other"
|
||||
}
|
||||
|
||||
fn queueFlags(u32 f) -> str {
|
||||
[]str parts
|
||||
if f & VK_QUEUE_GRAPHICS_BIT != 0 {
|
||||
parts.push("graphics")
|
||||
}
|
||||
if f & VK_QUEUE_COMPUTE_BIT != 0 {
|
||||
parts.push("compute")
|
||||
}
|
||||
if f & VK_QUEUE_TRANSFER_BIT != 0 {
|
||||
parts.push("transfer")
|
||||
}
|
||||
if f & VK_QUEUE_SPARSE_BINDING_BIT != 0 {
|
||||
parts.push("sparse")
|
||||
}
|
||||
if f & VK_QUEUE_VIDEO_DECODE_BIT_KHR != 0 {
|
||||
parts.push("video decode")
|
||||
}
|
||||
if f & VK_QUEUE_VIDEO_ENCODE_BIT_KHR != 0 {
|
||||
parts.push("video encode")
|
||||
}
|
||||
if parts.len == 0 {
|
||||
return "other"
|
||||
}
|
||||
return parts.join(", ")
|
||||
}
|
||||
|
||||
fn main() {
|
||||
app := VkApplicationInfo(
|
||||
sType: VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||
pApplicationName: "Finch".ptr,
|
||||
applicationVersion: 1,
|
||||
pEngineName: "none".ptr,
|
||||
apiVersion: 1 << 22, // Vulkan 1.0
|
||||
)
|
||||
info := VkInstanceCreateInfo(sType: VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, pApplicationInfo: addr(app))
|
||||
ptr instance
|
||||
check(vkCreateInstance(addr(info), null, addr(instance)), "vkCreateInstance")
|
||||
defer vkDestroyInstance(instance, null)
|
||||
|
||||
u32 count
|
||||
check(vkEnumeratePhysicalDevices(instance, addr(count), null), "counting GPUs")
|
||||
[]ptr gpus
|
||||
gpus.resize(count)
|
||||
check(vkEnumeratePhysicalDevices(instance, addr(count), gpus.ptr), "listing GPUs")
|
||||
print("Vulkan found", count, "device(s)")
|
||||
|
||||
for gpu in gpus {
|
||||
VkPhysicalDeviceProperties props
|
||||
vkGetPhysicalDeviceProperties(gpu, addr(props))
|
||||
print()
|
||||
print(str(props.deviceName.ptr))
|
||||
print(" type:", deviceType(props.deviceType))
|
||||
print(" Vulkan " + version(props.apiVersion) + ", driver " + str(props.driverVersion))
|
||||
print(" biggest 2D image:", props.limits.maxImageDimension2D, "px")
|
||||
|
||||
VkPhysicalDeviceMemoryProperties mem
|
||||
vkGetPhysicalDeviceMemoryProperties(gpu, addr(mem))
|
||||
for h in 0..mem.memoryHeapCount {
|
||||
heap := mem.memoryHeaps[h]
|
||||
local := ""
|
||||
if heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT != 0 {
|
||||
local = " (on the GPU)"
|
||||
}
|
||||
print(" memory heap " + str(h) + ": " + str(float(heap.size) / 1073741824.0) + " GiB" + local)
|
||||
}
|
||||
|
||||
u32 families
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(gpu, addr(families), null)
|
||||
[]VkQueueFamilyProperties queues
|
||||
queues.resize(families)
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(gpu, addr(families), queues.ptr)
|
||||
graphics := -1
|
||||
for q in 0..queues.len {
|
||||
print(" queue family " + str(q) + ": " + str(queues[q].queueCount) + " x " + queueFlags(queues[q].queueFlags))
|
||||
if graphics < 0 && queues[q].queueFlags & VK_QUEUE_GRAPHICS_BIT != 0 {
|
||||
graphics = q
|
||||
}
|
||||
}
|
||||
if graphics < 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// a logical device with one graphics queue: what a renderer would start from
|
||||
f32 priority = 1.0
|
||||
queueInfo := VkDeviceQueueCreateInfo(
|
||||
sType: VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||||
queueFamilyIndex: u32(graphics),
|
||||
queueCount: 1,
|
||||
pQueuePriorities: addr(priority),
|
||||
)
|
||||
deviceInfo := VkDeviceCreateInfo(
|
||||
sType: VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||
queueCreateInfoCount: 1,
|
||||
pQueueCreateInfos: addr(queueInfo),
|
||||
)
|
||||
ptr device
|
||||
check(vkCreateDevice(gpu, addr(deviceInfo), null, addr(device)), "vkCreateDevice")
|
||||
ptr queue
|
||||
vkGetDeviceQueue(device, u32(graphics), 0, addr(queue))
|
||||
print(" created a device and got graphics queue", queue != null)
|
||||
vkDestroyDevice(device, null)
|
||||
}
|
||||
}
|
||||
|
|
@ -10,9 +10,11 @@ pkgs.mkShell {
|
|||
ninja
|
||||
pkg-config # finds the right flags for `link "..."`
|
||||
|
||||
# for examples/window.fch and examples/raylib.fch
|
||||
# for the graphics examples (window, opengl, raylib, vulkan)
|
||||
glfw
|
||||
libGL
|
||||
raylib
|
||||
vulkan-headers
|
||||
vulkan-loader
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -341,8 +341,14 @@ CImports importHeaders(const std::vector<Import> &imports, const std::vector<std
|
|||
if (clang_getDiagnosticSeverity(d) >= CXDiagnostic_Error) {
|
||||
std::string msg = str(clang_getDiagnosticSpelling(d));
|
||||
clang_disposeDiagnostic(d);
|
||||
if (msg.find("file not found") != std::string::npos)
|
||||
failAt(im.pos.file, im.pos.line, im.pos.col, "can't find the C header '" + im.path + "'");
|
||||
if (msg.find("file not found") != std::string::npos) {
|
||||
// "'GL/gl.h' file not found": it may be a header that this one includes
|
||||
std::string missing = msg.substr(1, msg.find('\'', 1) - 1);
|
||||
if (missing == im.path)
|
||||
failAt(im.pos.file, im.pos.line, im.pos.col, "can't find the C header '" + im.path + "'");
|
||||
failAt(im.pos.file, im.pos.line, im.pos.col, "can't find the C header '" + missing + "', which '" + im.path +
|
||||
"' includes (is its library's development package installed?)");
|
||||
}
|
||||
failAt(im.pos.file, im.pos.line, im.pos.col, "C header '" + im.path + "' has an error: " + msg);
|
||||
}
|
||||
clang_disposeDiagnostic(d);
|
||||
|
|
|
|||
|
|
@ -758,7 +758,9 @@ LRef Codegen::member(const MemberExpr &m, bool forWrite) {
|
|||
if (!obj.isPlace) release(obj.val);
|
||||
return {false, {}, {len, FType::I64}};
|
||||
}
|
||||
if (!obj.isPlace && obj.val.fresh) failAt(p.file, p.line, p.col, ".ptr of a temporary value would point to freed memory");
|
||||
// a literal lives as long as the program; other temporaries are freed right after this expression
|
||||
if (!obj.isPlace && obj.val.fresh && !isa<Constant>(obj.val.v))
|
||||
failAt(p.file, p.line, p.col, ".ptr of a temporary value would point to freed memory");
|
||||
FType pt = t.kind == FType::Str ? FType::ptrTo(FType::Char) : FType::ptrTo(*t.elem);
|
||||
return {false, {}, {t.kind == FType::Str ? cstr(v) : b.CreateExtractValue(v, 0), pt}};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -441,6 +441,8 @@ Value_ Codegen::convert(const FType &to, const std::vector<ExprPtr> &args, Pos,
|
|||
}
|
||||
if (to.kind == FType::Bool && from.isInt()) return {b.CreateICmpNE(v.v, ConstantInt::get(ty(from), 0)), to};
|
||||
if (to.isPtr() && (from.isPtr() || from.kind == FType::Null)) return {v.v, to};
|
||||
if (to.isPtr() && from.isInt()) return {b.CreateIntToPtr(intCast(v.v, from, FType::U64), b.getPtrTy()), to}; // an address or offset for C
|
||||
if (to.isInt() && from.isPtr()) return {b.CreatePtrToInt(v.v, ty(to)), to};
|
||||
if (to.isPtr() && from.kind == FType::Str) failAt(p.file, p.line, p.col, "use .ptr to get a str's address (it is only valid while the str lives)");
|
||||
failAt(p.file, p.line, p.col, "can't turn " + from.show() + " into " + name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,12 +68,7 @@ struct Loader {
|
|||
void load(const std::string &path, const std::string &module, Pos from) {
|
||||
std::string text;
|
||||
if (!readFile(path, text)) {
|
||||
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 + ")");
|
||||
}
|
||||
if (from.line) 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::exit(1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -456,6 +456,7 @@ private:
|
|||
parenDepth++;
|
||||
if (!at(Tok::RParen)) {
|
||||
do {
|
||||
if (at(Tok::RParen)) break; // a trailing comma is fine
|
||||
std::string name;
|
||||
if (at(Tok::Ident) && peekTok().kind == Tok::Colon) {
|
||||
name = next().text;
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
// expect: but Finch files now end in .fch
|
||||
import oldmodule
|
||||
fn main() {}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
fn hi() {
|
||||
}
|
||||
|
|
@ -38,7 +38,6 @@ for f in run/*.fch; do
|
|||
done
|
||||
|
||||
for f in fail/*.fch; do
|
||||
grep -q "^// expect:" "$f" || continue
|
||||
expect=$(head -1 "$f" | sed -n 's|^// expect: ||p')
|
||||
got=$("$FINCH" run "$f" < /dev/null 2>&1)
|
||||
status=$?
|
||||
|
|
|
|||
22
tests/run/c_pointers.fch
Normal file
22
tests/run/c_pointers.fch
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import "string.h"
|
||||
|
||||
struct Point {
|
||||
int x
|
||||
int y
|
||||
}
|
||||
|
||||
fn main() {
|
||||
p := Point(
|
||||
x: 1,
|
||||
y: 2,
|
||||
)
|
||||
print(p)
|
||||
c := "finch".ptr
|
||||
print(c[1], strlen("finch"))
|
||||
q := ptr(4096)
|
||||
print(int(q), int(ptr(0)) == 0, q != null)
|
||||
a := [10, 20, 30]
|
||||
address := int(a.ptr)
|
||||
ptr[int] second = ptr(address + 8)
|
||||
print(second.value)
|
||||
}
|
||||
4
tests/run/c_pointers.out
Normal file
4
tests/run/c_pointers.out
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
Point(x: 1, y: 2)
|
||||
i 5
|
||||
4096 true true
|
||||
20
|
||||
Loading…
Reference in a new issue