From 8497251dc55626b83f813c647b971ede4a8a6824 Mon Sep 17 00:00:00 2001 From: Nexoniarz Date: Thu, 8 Oct 2026 19:37:34 +0200 Subject: [PATCH] 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 --- README.md | 2 + README.pl.md | 2 + boot/main.fch | 5 -- boot/parser.fch | 2 +- docs/en/for-engineers.md | 3 +- docs/en/for-technicians.md | 29 +++++++- docs/pl/dla-inzynierow.md | 3 +- docs/pl/dla-technikow.md | 29 +++++++- examples/opengl.fch | 105 +++++++++++++++++++++++++++ examples/opengl.h | 6 ++ examples/vulkan.fch | 137 +++++++++++++++++++++++++++++++++++ shell.nix | 4 +- src/cimport.cpp | 10 ++- src/codegen.cpp | 4 +- src/codegen_builtins.cpp | 2 + src/main.cpp | 7 +- src/parser.cpp | 1 + tests/fail/old_extension.fch | 3 - tests/fail/oldmodule.fn | 2 - tests/run.sh | 1 - tests/run/c_pointers.fch | 22 ++++++ tests/run/c_pointers.out | 4 + 22 files changed, 357 insertions(+), 26 deletions(-) create mode 100644 examples/opengl.fch create mode 100644 examples/opengl.h create mode 100644 examples/vulkan.fch delete mode 100644 tests/fail/old_extension.fch delete mode 100644 tests/fail/oldmodule.fn create mode 100644 tests/run/c_pointers.fch create mode 100644 tests/run/c_pointers.out diff --git a/README.md b/README.md index a4f0c34..f713172 100644 --- a/README.md +++ b/README.md @@ -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 | diff --git a/README.pl.md b/README.pl.md index c7ff4ba..45f0212 100644 --- a/README.pl.md +++ b/README.pl.md @@ -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 | diff --git a/boot/main.fch b/boot/main.fch index 0e985e6..23357af 100644 --- a/boot/main.fch +++ b/boot/main.fch @@ -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 + "'") diff --git a/boot/parser.fch b/boot/parser.fch index b4e7dd2..17ec0d5 100644 --- a/boot/parser.fch +++ b/boot/parser.fch @@ -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 diff --git a/docs/en/for-engineers.md b/docs/en/for-engineers.md index 68bedff..03d2f35 100644 --- a/docs/en/for-engineers.md +++ b/docs/en/for-engineers.md @@ -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 diff --git a/docs/en/for-technicians.md b/docs/en/for-technicians.md index 98ce4cc..76b9b26 100644 --- a/docs/en/for-technicians.md +++ b/docs/en/for-technicians.md @@ -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 + #include + #include + ``` + + 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 diff --git a/docs/pl/dla-inzynierow.md b/docs/pl/dla-inzynierow.md index 79d97dc..80a9460 100644 --- a/docs/pl/dla-inzynierow.md +++ b/docs/pl/dla-inzynierow.md @@ -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 diff --git a/docs/pl/dla-technikow.md b/docs/pl/dla-technikow.md index 1ee3e5e..4f4f6d9 100644 --- a/docs/pl/dla-technikow.md +++ b/docs/pl/dla-technikow.md @@ -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 + #include + #include + ``` + + 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 diff --git a/examples/opengl.fch b/examples/opengl.fch new file mode 100644 index 0000000..e30bad6 --- /dev/null +++ b/examples/opengl.fch @@ -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) +} diff --git a/examples/opengl.h b/examples/opengl.h new file mode 100644 index 0000000..235afb7 --- /dev/null +++ b/examples/opengl.h @@ -0,0 +1,6 @@ +// Everything examples/opengl.fch needs from C. GL_GLEXT_PROTOTYPES makes +// declare the modern (OpenGL 3+) functions, which Linux's libGL exports directly. +#define GL_GLEXT_PROTOTYPES +#include +#include +#include diff --git a/examples/vulkan.fch b/examples/vulkan.fch new file mode 100644 index 0000000..32f456f --- /dev/null +++ b/examples/vulkan.fch @@ -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) + } +} diff --git a/shell.nix b/shell.nix index 3f44c9e..41460f3 100644 --- a/shell.nix +++ b/shell.nix @@ -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 ]; } diff --git a/src/cimport.cpp b/src/cimport.cpp index 1123afe..6df3a0f 100644 --- a/src/cimport.cpp +++ b/src/cimport.cpp @@ -341,8 +341,14 @@ CImports importHeaders(const std::vector &imports, const std::vector= 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); diff --git a/src/codegen.cpp b/src/codegen.cpp index 01df567..373cc12 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -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(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}}; } diff --git a/src/codegen_builtins.cpp b/src/codegen_builtins.cpp index 89a7ae5..09b4e09 100644 --- a/src/codegen_builtins.cpp +++ b/src/codegen_builtins.cpp @@ -441,6 +441,8 @@ Value_ Codegen::convert(const FType &to, const std::vector &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); } diff --git a/src/main.cpp b/src/main.cpp index e755330..7dfce5c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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); } diff --git a/src/parser.cpp b/src/parser.cpp index 14889fa..ccd21bc 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -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; diff --git a/tests/fail/old_extension.fch b/tests/fail/old_extension.fch deleted file mode 100644 index 2764d5c..0000000 --- a/tests/fail/old_extension.fch +++ /dev/null @@ -1,3 +0,0 @@ -// expect: but Finch files now end in .fch -import oldmodule -fn main() {} diff --git a/tests/fail/oldmodule.fn b/tests/fail/oldmodule.fn deleted file mode 100644 index 768ca7c..0000000 --- a/tests/fail/oldmodule.fn +++ /dev/null @@ -1,2 +0,0 @@ -fn hi() { -} diff --git a/tests/run.sh b/tests/run.sh index c85f2b8..937e763 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -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=$? diff --git a/tests/run/c_pointers.fch b/tests/run/c_pointers.fch new file mode 100644 index 0000000..9bcb510 --- /dev/null +++ b/tests/run/c_pointers.fch @@ -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) +} diff --git a/tests/run/c_pointers.out b/tests/run/c_pointers.out new file mode 100644 index 0000000..46db063 --- /dev/null +++ b/tests/run/c_pointers.out @@ -0,0 +1,4 @@ +Point(x: 1, y: 2) +i 5 +4096 true true +20