Init new repository
This commit is contained in:
25
.github/workflows/run_tests.yaml
vendored
Normal file
25
.github/workflows/run_tests.yaml
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
on: [push]
|
||||||
|
jobs:
|
||||||
|
run-and-compile-ubuntu:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: sudo apt install g++
|
||||||
|
- run: sudo apt install clang
|
||||||
|
- run: g++ -o bld src/build_tool/main.cpp -g -lm && ./bld
|
||||||
|
run-and-compile-mac:
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: brew install llvm
|
||||||
|
- run: clang++ src/build_tool/main.cpp -std=c++11 -o bld && ./bld
|
||||||
|
run-and-compile-windows:
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- name: Setup MSVC Developer Command Prompt
|
||||||
|
uses: TheMrMilchmann/setup-msvc-dev@v3.0.0
|
||||||
|
with:
|
||||||
|
arch: x64
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: cl.exe src/build_tool/main.cpp /Fe:bld.exe && bld.exe
|
||||||
|
shell: cmd
|
||||||
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
build/
|
||||||
|
private.bat
|
||||||
|
todo.txt
|
||||||
|
*.sublime-project
|
||||||
|
*.sublime-workspace
|
||||||
|
*.rdbg
|
||||||
|
*.wasm
|
||||||
20
LICENSE
Normal file
20
LICENSE
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
Copyright (c) 2024 Krzosa Karol
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
674
README.md
Normal file
674
README.md
Normal file
@@ -0,0 +1,674 @@
|
|||||||
|
# Compiler front-end in a single-header-file C library
|
||||||
|
|
||||||
|
I have no illusions here, this is not the next big language. What I propose is very simple, "a return to C", a language that is smaller but with modern features, distributed as a dependency free, easy to use single-header-file library. Add compiler checks, generate code, do whatever floats your boat. Enjoy.
|
||||||
|
|
||||||
|
- **User has full control over compilation!**
|
||||||
|
- **No dependencies, permissive license, single file that compile both in C and C++!**
|
||||||
|
- **Simpler then C:** core of the language is 4000 loc and entire package 11k loc.
|
||||||
|
- **Statically typed, procedural and MODERN:** it's a mix of Go/Odin/Jai with "return to C" as ideal.
|
||||||
|
- **Complete:** it supports conditional compilation, modularity via packages etc.
|
||||||
|
- **State of art error handling techniques** like AST poisoning, proper parsing recovery, catches tons of errors without misreporting!
|
||||||
|
- **Great C integration:** using C libraries feels native, the language compiles easily to C with great debug info.
|
||||||
|
|
||||||
|
**Library is in beta so I reserve the right to change things!**
|
||||||
|
|
||||||
|
## Example or [you can try the language online](https://krzosa.xyz/playground.html)
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
import "raylib";
|
||||||
|
|
||||||
|
main :: proc(): int {
|
||||||
|
InitWindow(800, 600, "Thing");
|
||||||
|
SetTargetFPS(60);
|
||||||
|
|
||||||
|
for !WindowShouldClose() {
|
||||||
|
BeginDrawing();
|
||||||
|
ClearBackground(RAYWHITE);
|
||||||
|
DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY);
|
||||||
|
EndDrawing();
|
||||||
|
}
|
||||||
|
CloseWindow();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Program examples:**
|
||||||
|
|
||||||
|
- [Hello world](examples/hello_world)
|
||||||
|
- [Text editor](examples/text_editor)
|
||||||
|
- [Path-finding visualizer](examples/pathfinding_visualizer)
|
||||||
|
|
||||||
|
**Examples of hooking into the compilation:**
|
||||||
|
|
||||||
|
- [Generating type information](examples/generate_type_info)
|
||||||
|
- [Adding printf checks](examples/add_printf_format_check)
|
||||||
|
- [Adding instrumentation to every procedure](examples/add_instrumentation)
|
||||||
|
- [Adding a generic dynamic array using AST modification](examples/add_dynamic_array_macro)
|
||||||
|
|
||||||
|
**Other:**
|
||||||
|
|
||||||
|
- [Using the language as a data format (with type safety)](examples/use_as_data_format_with_typechecking)
|
||||||
|
|
||||||
|
## How to integrate
|
||||||
|
|
||||||
|
To create the implementation do this in **one** of your C or C++ files:
|
||||||
|
|
||||||
|
``` c
|
||||||
|
#define LIB_COMPILER_IMPLEMENTATION
|
||||||
|
#include "lib_compiler.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
## How to build the compiler executable
|
||||||
|
|
||||||
|
Simply compile one file with whatever compiler you want:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
clang tools/lcompile.c -o lc.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
Once you have the executable you can compile examples and stuff:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
lc.exe examples/create_raylib_window
|
||||||
|
```
|
||||||
|
|
||||||
|
When in doubt: `./lc.exe --help`.
|
||||||
|
|
||||||
|
## How to run the test suite
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
clang++ src/build_tool/main.cpp -o bld.exe
|
||||||
|
./bld.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
You only need to compile the build tool once. Afterwards just call `./bld.exe`. There are multiple testing options so checkout: `./bld.exe --help`.
|
||||||
|
|
||||||
|
## Further plans
|
||||||
|
|
||||||
|
My priority is to improve the C user API, harden the compiler, accommodate things that I didn't foresee and stuff like that.
|
||||||
|
|
||||||
|
Sometime in the future I want to implement a bytecode backend so that the language can be used like Lua as a nice scripting language.
|
||||||
|
|
||||||
|
I'm also considering the addition of overloaded procedures because it would greatly aid in writing macros.
|
||||||
|
|
||||||
|
# Language overview
|
||||||
|
|
||||||
|
The language is strictly procedural, I have taken most of the inspiration from C, Golang, Ion, Jai and Odin. There are no classes, methods etc. only procedures and data types.
|
||||||
|
|
||||||
|
## Packages
|
||||||
|
|
||||||
|
Package is a real directory on your disk that contains source files. It's also an aggregation of declarations - functions, constants, variables, types which you can `import` into your program.
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
import "libc";
|
||||||
|
import RL "raylib";
|
||||||
|
|
||||||
|
main :: proc(): int {
|
||||||
|
RL.SetClipboardText("**magic**");
|
||||||
|
printf("**magic**");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- Packages need to follow a "directed acyclic graph" kind of modularity (you can google the image of how the graph looks). Essentially it means that you cannot perform a 'cyclic' import, 2 packages cannot import each other creating a loop. Such a situation breaks modularity, when this happens you stop having a layer, a library. It becomes a spaghetti ball. -->
|
||||||
|
|
||||||
|
<!-- **Important** thing to note here is that packages break the goal of seamless C integration! This outlines exactly how important I think this system is. Due to introduction of packages - all declaration names in the output code need to be mangled. To make it simple for the programmer I introduced a simple naming scheme, all symbols are named like: `lc_{package}_{symbol}`. -->
|
||||||
|
|
||||||
|
## Constants
|
||||||
|
|
||||||
|
By which I mean the literal values but also the named declarations. There are 3 constant types in the language: untyped ints, untyped floats and untyped strings. The "untyped" here means that the type is inferred from context. There are no suffixes like 'ull', no random overflows and the values are also bounds checked for safety. If you have ever used Go, I have a good news for you, you are already familiar with this concept.
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
binary :: 0b10000110;
|
||||||
|
hex1 :: 0xabcd3245;
|
||||||
|
hex2 :: 0xABCD3245;
|
||||||
|
decimal :: 1235204950;
|
||||||
|
floating_point :: 2342.44230;
|
||||||
|
|
||||||
|
boolean :: false;
|
||||||
|
nil_val :: nil;
|
||||||
|
|
||||||
|
string_val :: "Something";
|
||||||
|
raw_string :: `
|
||||||
|
{
|
||||||
|
"cool_json": "yes",
|
||||||
|
"cool_array": ["a", "b", 32],
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Types
|
||||||
|
|
||||||
|
The type system is strict, most conversions require explicit casts.
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
a: int = 10;
|
||||||
|
b: long = :long(a); // all of these assignments require a cast
|
||||||
|
c: float = :float(a);
|
||||||
|
d: short = :short(a);
|
||||||
|
e: llong = 1234; // untyped constants automatically fit into the context
|
||||||
|
// no matter the type
|
||||||
|
```
|
||||||
|
|
||||||
|
There is one exception which is the `*void` type. It has very permissive semantics.
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
i1: int;
|
||||||
|
v : *void = &i1;
|
||||||
|
i2: *int = v;
|
||||||
|
```
|
||||||
|
|
||||||
|
When using regular pointers, again, casts are necessary.
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
i1: *int = nil;
|
||||||
|
i2: *float = :*float(i1);
|
||||||
|
```
|
||||||
|
|
||||||
|
Compound types are read from left to right. This creates a nice mirroring between spoken and the symbolic language:
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
a: *int; // a: pointer '*' to an int
|
||||||
|
b: [32]*int; // an: array of 32 pointers to int
|
||||||
|
c: *proc(): int; // a: pointer to a proc type that returns int
|
||||||
|
```
|
||||||
|
|
||||||
|
## Structs and unions
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
Node :: struct {
|
||||||
|
v1: Value;
|
||||||
|
v2: Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value :: union {
|
||||||
|
i64: llong;
|
||||||
|
u64: ullong;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Basic types
|
||||||
|
|
||||||
|
The language uses exactly the same types as C for greater compatibility. I don't want to go against the current. I want the language to be as simple as possible.
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
c: char; uc: uchar; s: short; us: ushort;
|
||||||
|
i: int; ui: uint; l: long; ul: ulong;
|
||||||
|
ll: llong; ull: ullong; f: float; d: double;
|
||||||
|
b: bool;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Typedefs
|
||||||
|
|
||||||
|
Typedef allows you to create an alias to an existsing type but with additional layer of type safety. `my_int` is not an `int`, it's a different type and so it requires a cast.
|
||||||
|
|
||||||
|
```
|
||||||
|
my_int :: typedef int;
|
||||||
|
|
||||||
|
i: int = 10;
|
||||||
|
mi: my_int = :my_int(i); // requires a cast
|
||||||
|
```
|
||||||
|
|
||||||
|
A simple alias can be defined by adding a '@weak' note:
|
||||||
|
|
||||||
|
```
|
||||||
|
my_int: typedef int; @weak
|
||||||
|
|
||||||
|
i: int = 10;
|
||||||
|
mi: my_int = i;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Compound expressions
|
||||||
|
|
||||||
|
These are expressions meant for initializing structs and unions.
|
||||||
|
|
||||||
|
- Particular members can be targeted by name.
|
||||||
|
- Members that were not mentioned are initialized to zero.
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
Vector2 :: struct { x: float; y: float; }
|
||||||
|
Camera2D :: struct {
|
||||||
|
offset: Vector2;
|
||||||
|
target: Vector2;
|
||||||
|
rotation: float;
|
||||||
|
zoom: float;
|
||||||
|
}
|
||||||
|
|
||||||
|
Camera: Camera2D = {
|
||||||
|
offset = {100, 100},
|
||||||
|
zoom = 1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
SecondCamera := :Camera2D{{1, 1}, zoom = 1.0};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Arrays and compound expressions
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
array: [100]int; // array of 100 integers
|
||||||
|
|
||||||
|
inited_array: [100]int = {
|
||||||
|
0,
|
||||||
|
[4] = 4,
|
||||||
|
[50] = 50,
|
||||||
|
[99] = 99,
|
||||||
|
// remaining values are '0'ed
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Named and default procedure arguments
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
RED :: 0xff0000ff;
|
||||||
|
DrawRectangle :: proc(x: int, y: int, w: int, h: int, color: uint = RED);
|
||||||
|
|
||||||
|
main :: proc(): int {
|
||||||
|
DrawRectangle(x = 100, y = 100, w = 50, h = 50);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Simulating enums
|
||||||
|
|
||||||
|
The language doesn't have enums but similar feature can be easily simulated. Operator '^' allows you to generate a list of enumerated constants: 1, 2, 3 ... using typedef you can create a simple integer type with additional type safety.
|
||||||
|
|
||||||
|
```
|
||||||
|
TextKind :: typedef int;
|
||||||
|
TEXT_INVALID :: 0;
|
||||||
|
TEXT_BOLD :: ^; // 1
|
||||||
|
TEXT_CURSIVE :: ^; // 2
|
||||||
|
TEXT_NORMAL :: ^; // 3
|
||||||
|
```
|
||||||
|
|
||||||
|
## Enumerating bit flags
|
||||||
|
|
||||||
|
Operator '<<' allows you to generate a list of enumerated flags: 0b1, 0b10, 0b100 ...
|
||||||
|
|
||||||
|
```
|
||||||
|
EntityProps :: typedef uint;
|
||||||
|
ENTITY_CAN_BURN :: 0b1;
|
||||||
|
ENTITY_CAN_MOVE :: <<; // 0b10
|
||||||
|
ENTITY_CAN_BE_SHOCKED :: <<; // 0b100
|
||||||
|
ENTITY_WALKING :: <<; // 0b1000
|
||||||
|
```
|
||||||
|
|
||||||
|
## `*char` and String types
|
||||||
|
|
||||||
|
The language by default is very `*char` friendly. Strings default to the cstring type which functions in exactly the same way as in C. It functions as an array of bytes which is null terminated by a null `0`.
|
||||||
|
|
||||||
|
But the language also has it's own String type:
|
||||||
|
|
||||||
|
```
|
||||||
|
String :: struct {
|
||||||
|
str: *char;
|
||||||
|
len: int;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Untyped strings work well with both:
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
a: String = "sized string";
|
||||||
|
b: *char = "c string";
|
||||||
|
c: = "this defaults to *char";
|
||||||
|
```
|
||||||
|
|
||||||
|
`String` is a structure so you need to index it using `.str` or `.len`.
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
test :: proc() {
|
||||||
|
a: *char = "C string";
|
||||||
|
c := a[1];
|
||||||
|
|
||||||
|
b: String = "Len string";
|
||||||
|
c = b.str[1];
|
||||||
|
len := b.len; @unused
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
A `lengthof` operator can be used to learn how big a constant string is, this also works for arrays:
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
str :: "String";
|
||||||
|
len :: lengthof(str);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pointer arithmetic
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
import "libc";
|
||||||
|
|
||||||
|
test :: proc() {
|
||||||
|
a: *int = malloc(sizeof(:int) * 10);
|
||||||
|
c: *int = addptr(a, 4);
|
||||||
|
d: *int = &a[4];
|
||||||
|
// there is no: (a + 4)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Casting between types
|
||||||
|
|
||||||
|
```
|
||||||
|
a: float = :float(10);
|
||||||
|
|
||||||
|
b := &a;
|
||||||
|
c := :*int(b);
|
||||||
|
```
|
||||||
|
|
||||||
|
## If statement
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
if_statement_showcase :: proc(cond: int) {
|
||||||
|
if cond == 1 {
|
||||||
|
//
|
||||||
|
} else if cond == 2 {
|
||||||
|
//
|
||||||
|
} else {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Switch statement
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
import "libc";
|
||||||
|
switch_statement_showcase :: proc(value: int)
|
||||||
|
{
|
||||||
|
switch value
|
||||||
|
{
|
||||||
|
case 0,4,8:
|
||||||
|
{
|
||||||
|
printf("0, 4, 8\n");
|
||||||
|
}
|
||||||
|
case 1, 2, 3:
|
||||||
|
{
|
||||||
|
printf("1, 2, 3\n");
|
||||||
|
} @fallthrough
|
||||||
|
case 5:
|
||||||
|
{
|
||||||
|
printf("this activates when 1, 2, 3 and 5\n");
|
||||||
|
}
|
||||||
|
default: printf("default case");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## For loop
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
for_loop_showcase :: proc() {
|
||||||
|
|
||||||
|
// infinite loop
|
||||||
|
for {
|
||||||
|
/* .. */
|
||||||
|
}
|
||||||
|
|
||||||
|
cond := true;
|
||||||
|
for cond {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 4; i += 1 {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
i := 0;
|
||||||
|
for i < 4 {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
for i < 4; i += 1 {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Labeled loops, breaks and continues
|
||||||
|
|
||||||
|
You can label a loop and use that label to control the outer loop.
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
test :: proc() {
|
||||||
|
cond := true;
|
||||||
|
outer_loop: for cond {
|
||||||
|
for cond {
|
||||||
|
break outer_loop; // continue works too
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Defer statement
|
||||||
|
|
||||||
|
A defer statement allows to postpone a certain action to the end of scope. For example - you might allocate memory and use defer just bellow your allocation to free the memory. This free will be deferred, it will happen a bit later.
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
import "libc";
|
||||||
|
test :: proc() {
|
||||||
|
// it's useful to keep certain pairs of actions together:
|
||||||
|
file := fopen("data.txt", "rb");
|
||||||
|
if (file) {
|
||||||
|
defer fclose(file);
|
||||||
|
// .. do things
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
You can annotate declarations, statements etc. This puts metadata on the AST which you can access from a metaprogram.
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
// a note that starts with '#', doesn't bind to anything, it basically acts as a directive.
|
||||||
|
#do_something(a, b, c);
|
||||||
|
|
||||||
|
|
||||||
|
Data :: struct {
|
||||||
|
a: int;
|
||||||
|
b: *int;
|
||||||
|
} @serialize // binds to AST of Data
|
||||||
|
```
|
||||||
|
|
||||||
|
The language uses notes to implement a bunch of features, for example:
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
#static_assert(sizeof(:int) == 4);
|
||||||
|
|
||||||
|
|
||||||
|
test :: proc() {
|
||||||
|
// ...
|
||||||
|
} @build_if(LC_OS == OS_WINDOWS)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conditional compilation
|
||||||
|
|
||||||
|
It's important for a low level language to have some form of platform awareness. We don't have includes and preprocessor here so I decided to implement a construct that works during parsing stage. It discards declarations and doesn't let them into typechecking based on the evaluation of the inner expression.
|
||||||
|
|
||||||
|
'#build_if' can appear at the top of the file, it decides whether to compile that particular file or not:
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
#build_if(LC_OS == OS_WINDOWS);
|
||||||
|
```
|
||||||
|
|
||||||
|
'@build_if' can be bound to any top level declaration.
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
LONG_MAX :: 0xFFFFFFFF; @build_if(LC_OS == OS_WINDOWS)
|
||||||
|
LONG_MAX :: 0xFFFFFFFFFFFFFFFF; @build_if(LC_OS == OS_MAC || LC_OS == OS_LINUX)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Builtins (like sizeof)
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
a := sizeof(:int);
|
||||||
|
b := alignof(:int);
|
||||||
|
|
||||||
|
A :: struct { a: int; b: int; }
|
||||||
|
c := offsetof(:A, b);
|
||||||
|
|
||||||
|
d := lengthof("asd");
|
||||||
|
e := typeof(d);
|
||||||
|
|
||||||
|
#static_assert(offsetof(:A, b) == 4);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Raw C code when targeting the C generator
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
#`#include <stdlib.h>`;
|
||||||
|
|
||||||
|
global_variable := 0;
|
||||||
|
|
||||||
|
main :: proc(): int {
|
||||||
|
a: *int = #`malloc(32)`;
|
||||||
|
#`a[0] = lc_package_global_variable`;
|
||||||
|
return a[0];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Any and typeof
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
Any :: struct {
|
||||||
|
type: int;
|
||||||
|
data: *void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
test :: proc() {
|
||||||
|
a: Any = 32;
|
||||||
|
b: Any = "thing";
|
||||||
|
c: Any = b;
|
||||||
|
|
||||||
|
i: int = 10;
|
||||||
|
d: Any = &i;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
print :: proc(v: Any) {
|
||||||
|
switch(v.type) {
|
||||||
|
case typeof(:int): {
|
||||||
|
val := :*int(v.data);
|
||||||
|
// ..
|
||||||
|
}
|
||||||
|
case typeof(:float): {
|
||||||
|
val := :*float(v.data);
|
||||||
|
// ..
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
print(32);
|
||||||
|
print("asd");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Variadic arguments with Any promotion
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
import "libc";
|
||||||
|
|
||||||
|
any_vargs :: proc(fmt: *char, ...Any) {
|
||||||
|
va: va_list;
|
||||||
|
va_start(va, fmt);
|
||||||
|
|
||||||
|
for i := 0; fmt[i]; i += 1 {
|
||||||
|
if fmt[i] == '%' {
|
||||||
|
arg := va_arg_any(va);
|
||||||
|
|
||||||
|
if arg.type == typeof(:int) {
|
||||||
|
val := :*int(arg.data);
|
||||||
|
// ..
|
||||||
|
} else {
|
||||||
|
// ..
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
va_end(va);
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
any_vargs("testing % %", 32, "thing");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Comments
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
// I'm a line comment
|
||||||
|
|
||||||
|
/* I'm a block comment
|
||||||
|
/*
|
||||||
|
I can even nest!
|
||||||
|
*/
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Doc comments
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
/** package
|
||||||
|
|
||||||
|
This is a package doc comment,
|
||||||
|
there can only be 1 per package,
|
||||||
|
it appears at top of the file.
|
||||||
|
It binds to the package AST.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** file
|
||||||
|
|
||||||
|
This is a file doc comment,
|
||||||
|
there can only be 1 per file,
|
||||||
|
it appears at top of the file under package doc comment.
|
||||||
|
It binds to the file AST.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
|
||||||
|
This is a top level declaration doc comment,
|
||||||
|
1 per declaration,
|
||||||
|
it appears above the declaration.
|
||||||
|
It binds to the declaration AST.
|
||||||
|
|
||||||
|
*/
|
||||||
|
A :: proc(): int {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Packed structs
|
||||||
|
|
||||||
|
``` odin
|
||||||
|
A :: struct {
|
||||||
|
a: short;
|
||||||
|
b: char;
|
||||||
|
c: int;
|
||||||
|
d: char;
|
||||||
|
e: llong;
|
||||||
|
f: uchar;
|
||||||
|
} @packed
|
||||||
|
```
|
||||||
|
|
||||||
|
## Useful resources for compiler development
|
||||||
|
|
||||||
|
* https://c3.handmade.network/blog/p/8632-handling_parsing_and_semantic_errors_in_a_compiler - great article by Christoffer Lerno (C3 author) about handling errors in compilers.
|
||||||
|
* https://www.youtube.com/watch?v=bNJhtKPugSQ - Walter Bright (D author) talks about AST poisoning here.
|
||||||
|
* https://bitwise.handmade.network/ - series by Per Vognsen where he actually creates a C like language, very helpful, very hands on!
|
||||||
|
* https://hero.handmade.network/episode/code/day206/ - this episode of handmade hero started me on the entire compiler journey, a long, long time ago.
|
||||||
|
* https://www.youtube.com/watch?v=TH9VCN6UkyQ&list=PLmV5I2fxaiCKfxMBrNsU1kgKJXD3PkyxO - I have re-watched this playlist many this, searching for keywords and ideas. Jonathan Blow's compiler was a big inspiration of mine when learning programming languages.
|
||||||
|
* A Retargetable C Compiler: Design and Implementation by Christopher W. Fraser and David R. Hanson - sometimes looked at this as a reference to figure stuff out. Very helpful resource on compiler construction, the way it's written reads more like documentation but you use what you have.
|
||||||
|
* Compiler Construction by Niklaus Wirth - https://people.inf.ethz.ch/wirth/CompilerConstruction/index.html - have to learn to read Pascal / Oberon but he goes over implementing all the stages of the compiler. Wirth's project of implementing an entire hardware / software stack is really inspiring.
|
||||||
|
* https://github.com/rui314/chibicc - great resource for learning how to write a very dumb x64 backend. I like the format, you go over git commits, doesn't work well on github though, with Sublime Merge it's a pleasure to follow.
|
||||||
|
* https://go.dev/blog/constants - article on golang type system, untyped types, constants that kind of stuff.
|
||||||
|
* https://github.com/JoshuaManton/sif - looked at this as a reference from time to time, author seems like a Jonathan Blow fan so it was a good resource informed by similar resources as I used.
|
||||||
|
* https://github.com/gingerbill/odin - I sometimes peeked at the compiler to figure stuff out when I was confused.
|
||||||
|
https://c3.handmade.network/blog - Christoffer Lerno (C3 author) blog.
|
||||||
|
* https://github.com/c3lang/c3c - I sometimes looked at C3 compiler as a reference, the author also let me use his bigint library, thanks a lot! :)
|
||||||
11
build.bat
Normal file
11
build.bat
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
if not exist build\bld.exe (
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cl -Fe:bld.exe ../src/build_tool/main.cpp -FC -WX -W3 -wd4200 -wd4244 -diagnostics:column -nologo -Zi -D_CRT_SECURE_NO_WARNINGS
|
||||||
|
cd ..
|
||||||
|
)
|
||||||
|
|
||||||
|
rem ubuntu run ./build.sh
|
||||||
|
build\bld.exe
|
||||||
311
build_file.cpp
Normal file
311
build_file.cpp
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
/* This build file runs all the tests, compiles them using all
|
||||||
|
available compilers, cleans the house and walks the dog. Wowie.
|
||||||
|
*/
|
||||||
|
#include "src/build_tool/library.cpp"
|
||||||
|
|
||||||
|
#define LC_USE_CUSTOM_ARENA
|
||||||
|
#define LC_Arena MA_Arena
|
||||||
|
#define LC__PushSizeNonZeroed MA__PushSizeNonZeroed
|
||||||
|
#define LC__PushSize MA__PushSize
|
||||||
|
#define LC_InitArena MA_Init
|
||||||
|
#define LC_DeallocateArena MA_DeallocateArena
|
||||||
|
#define LC_BootstrapArena MA_Bootstrap
|
||||||
|
#define LC_TempArena MA_Temp
|
||||||
|
#define LC_BeginTemp MA_BeginTemp
|
||||||
|
#define LC_EndTemp MA_EndTemp
|
||||||
|
|
||||||
|
#define LC_String S8_String
|
||||||
|
#include "src/compiler/lib_compiler.c"
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
bool UseClang;
|
||||||
|
bool UseCL;
|
||||||
|
bool UseTCC;
|
||||||
|
bool UseGCC;
|
||||||
|
bool QuickRun;
|
||||||
|
bool BuildX64Sandbox;
|
||||||
|
bool BreakpointOnError;
|
||||||
|
S8_List TestsToRun;
|
||||||
|
bool UseColoredIO;
|
||||||
|
int ThreadCount = 24;
|
||||||
|
bool PrintAllFunctions;
|
||||||
|
|
||||||
|
S8_String RaylibLIB;
|
||||||
|
S8_String RaylibDLL;
|
||||||
|
|
||||||
|
#include "src/build_file/ast_verify.cpp"
|
||||||
|
#include "src/build_file/test_readme.cpp"
|
||||||
|
#include "src/build_file/testsuite.cpp"
|
||||||
|
#include "src/build_file/package_compiler.cpp"
|
||||||
|
|
||||||
|
#include "examples/add_printf_format_check/build.cpp"
|
||||||
|
#include "examples/pathfinding_visualizer/build.cpp"
|
||||||
|
#include "examples/generate_type_info/build.cpp"
|
||||||
|
#include "examples/add_dynamic_array_macro/build.cpp"
|
||||||
|
#include "examples/text_editor/build.cpp"
|
||||||
|
#include "examples/hello_world/build.cpp"
|
||||||
|
#include "examples/sandbox/build.cpp"
|
||||||
|
#include "examples/create_raylib_window/build.cpp"
|
||||||
|
#include "examples/add_instrumentation/build.cpp"
|
||||||
|
#include "examples/use_as_data_format_with_typechecking/build.cpp"
|
||||||
|
#include "examples/add_source_location_macro/build.cpp"
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
MA_InitScratch();
|
||||||
|
|
||||||
|
MA_Scratch scratch;
|
||||||
|
UseColoredIO = OS_EnableTerminalColors();
|
||||||
|
|
||||||
|
{
|
||||||
|
CmdParser p = MakeCmdParser(scratch, argc, argv, "I'm a build tool for this codebase, by default I build the entire test suite");
|
||||||
|
AddBool(&p, &BuildX64Sandbox, "build-x64-sandbox", "build the x64 sandbox program using msvc");
|
||||||
|
AddBool(&p, &QuickRun, "quick", "build tests using tcc compiler only");
|
||||||
|
AddBool(&p, &BreakpointOnError, "breakpoint", "breakpoint if a compiler error is thrown");
|
||||||
|
AddBool(&p, &PrintAllFunctions, "print-functions", "prints all functions marked with LC_FUNCTION");
|
||||||
|
AddInt(&p, &ThreadCount, "threads", "number of threads to use when running the test suite");
|
||||||
|
AddList(&p, &TestsToRun, "tests", "run only these particular tests");
|
||||||
|
if (!ParseCmd(Perm, &p)) return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Find compilers in the path
|
||||||
|
//
|
||||||
|
{
|
||||||
|
char *path_string = getenv("PATH");
|
||||||
|
Array<S8_String> path = Split(path_string, IF_WINDOWS_ELSE(";", ":"));
|
||||||
|
|
||||||
|
char *end = IF_WINDOWS_ELSE(".exe", "");
|
||||||
|
For(path) {
|
||||||
|
S8_String clpath = Fmt("%.*s/cl%s", S8_Expand(it), end);
|
||||||
|
S8_String clangpath = Fmt("%.*s/clang%s", S8_Expand(it), end);
|
||||||
|
S8_String tccpath = Fmt("%.*s/tcc%s", S8_Expand(it), end);
|
||||||
|
S8_String gccpath = Fmt("%.*s/gcc%s", S8_Expand(it), end);
|
||||||
|
|
||||||
|
if (OS_FileExists(clpath)) UseCL = true;
|
||||||
|
if (OS_FileExists(clangpath)) UseClang = true;
|
||||||
|
if (OS_FileExists(tccpath)) UseTCC = true;
|
||||||
|
#if OS_WINDOWS == 0
|
||||||
|
if (OS_FileExists(gccpath)) UseGCC = true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UseClang == false && UseCL == false && UseTCC == false && UseGCC == false) {
|
||||||
|
IO_FatalErrorf("Found no supported compiler on the PATH! Make sure to have GCC, Clang, CL/MSVC or TCC on the PATH");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (QuickRun) {
|
||||||
|
if (!UseTCC) IO_FatalErrorf("TCC is not on the path");
|
||||||
|
UseClang = false;
|
||||||
|
UseCL = false;
|
||||||
|
UseGCC = false;
|
||||||
|
UseTCC = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RaylibLIB = OS_GetAbsolutePath(Perm, "../pkgs/raylib/raylib-5.0_win64_msvc16/lib/raylibdll.lib");
|
||||||
|
RaylibDLL = OS_GetAbsolutePath(Perm, "../pkgs/raylib/raylib-5.0_win64_msvc16/lib/raylib.dll");
|
||||||
|
|
||||||
|
if (BuildX64Sandbox) {
|
||||||
|
PushDir("x64_sandbox");
|
||||||
|
S8_String cc = "cl";
|
||||||
|
|
||||||
|
Array<S8_String> flags = {MA_GetAllocator(scratch)};
|
||||||
|
flags += "/MP /Zi -D_CRT_SECURE_NO_WARNINGS";
|
||||||
|
flags += "/FC /WX /W3 /wd4200 /diagnostics:column /nologo";
|
||||||
|
flags += "/GF /Gm- /Oi";
|
||||||
|
flags += "/GR- /EHa-";
|
||||||
|
flags += "/D_DEBUG -RTC1 -Od";
|
||||||
|
flags += Fmt("/Fe:x64main.exe");
|
||||||
|
OS_DeleteFile("x64main.pdb");
|
||||||
|
Run(cc + "../../src/x64/x64main.cpp" + flags);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PackageCompiler();
|
||||||
|
IO_Printf("Compiler packed successfully: lib_compiler.h\n");
|
||||||
|
|
||||||
|
if (ShouldRun("test_readme")) {
|
||||||
|
TestReadme();
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_MakeDir("examples");
|
||||||
|
if (ShouldRun("add_printf_format_check")) {
|
||||||
|
bool result = add_printf_format_check();
|
||||||
|
if (result) IO_Printf("%-50s - OK\n", "add_printf_format_check");
|
||||||
|
else IO_Printf("%-50s - ERROR\n", "add_printf_format_check");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ShouldRun("generate_type_info")) {
|
||||||
|
bool result = generate_type_info();
|
||||||
|
if (result) IO_Printf("%-50s - OK\n", "generate_type_info");
|
||||||
|
else IO_Printf("%-50s - ERROR\n", "generate_type_info");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ShouldRun("add_dynamic_array_macro")) {
|
||||||
|
bool result = add_dynamic_array_macro();
|
||||||
|
if (result) IO_Printf("%-50s - OK\n", "add_dynamic_array_macro");
|
||||||
|
else IO_Printf("%-50s - ERROR\n", "add_dynamic_array_macro");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ShouldRun("text_editor")) {
|
||||||
|
bool result = text_editor();
|
||||||
|
if (result) IO_Printf("%-50s - OK\n", "text_editor");
|
||||||
|
else IO_Printf("%-50s - ERROR\n", "text_editor");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ShouldRun("pathfinding_visualizer")) {
|
||||||
|
bool result = pathfinding_visualizer();
|
||||||
|
if (result) IO_Printf("%-50s - OK\n", "pathfinding_visualizer");
|
||||||
|
else IO_Printf("%-50s - ERROR\n", "pathfinding_visualizer");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ShouldRun("hello_world")) {
|
||||||
|
bool result = hello_world();
|
||||||
|
if (result) IO_Printf("%-50s - OK\n", "hello_world");
|
||||||
|
else IO_Printf("%-50s - ERROR\n", "hello_world");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ShouldRun("sandbox")) {
|
||||||
|
bool result = sandbox();
|
||||||
|
if (result) IO_Printf("%-50s - OK\n", "sandbox");
|
||||||
|
else IO_Printf("%-50s - ERROR\n", "sandbox");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ShouldRun("create_raylib_window")) {
|
||||||
|
bool result = create_raylib_window();
|
||||||
|
if (result) IO_Printf("%-50s - OK\n", "create_raylib_window");
|
||||||
|
else IO_Printf("%-50s - ERROR\n", "create_raylib_window");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ShouldRun("add_instrumentation")) {
|
||||||
|
bool result = add_instrumentation();
|
||||||
|
if (result) IO_Printf("%-50s - OK\n", "add_instrumentation");
|
||||||
|
else IO_Printf("%-50s - ERROR\n", "add_instrumentation");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ShouldRun("wasm_playground") && UseClang) {
|
||||||
|
OS_MakeDir("wasm_playground");
|
||||||
|
int result = Run("clang --target=wasm32 -mbulk-memory -Oz -Wno-writable-strings --no-standard-libraries -Wl,--strip-all -Wl,--import-memory -Wl,--no-entry -o wasm_playground/playground.wasm ../src/wasm_playground/wasm_main.c -DOS_WASM=1");
|
||||||
|
|
||||||
|
S8_String index = OS_ReadFile(scratch, "../src/wasm_playground/index.html");
|
||||||
|
S8_List programs = S8_MakeEmptyList();
|
||||||
|
|
||||||
|
OS_SetWorkingDir("wasm_playground"); // so that RegisterDir("../../pkgs") works
|
||||||
|
for (OS_FileIter it = OS_IterateFiles(scratch, "../../src/wasm_playground/"); OS_IsValid(it); OS_Advance(&it)) {
|
||||||
|
if (S8_EndsWith(it.filename, ".lc", false)) {
|
||||||
|
RunTestFile({TestKind_File, it.absolute_path, it.filename, "not_needed", true});
|
||||||
|
|
||||||
|
S8_String file = OS_ReadFile(scratch, it.absolute_path);
|
||||||
|
file = S8_ReplaceAll(scratch, file, S8_Lit("\\"), S8_Lit("\\\\"), true);
|
||||||
|
S8_AddF(scratch, &programs, "`%.*s`,\n", S8_Expand(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OS_SetWorkingDir("..");
|
||||||
|
|
||||||
|
S8_String programs_string = S8_Merge(scratch, programs);
|
||||||
|
S8_String new_index = S8_ReplaceAll(scratch, index, "<InsertPrograms>", programs_string, false);
|
||||||
|
|
||||||
|
OS_WriteFile("wasm_playground/playground.html", new_index);
|
||||||
|
OS_CopyFile("../src/wasm_playground/run_server.bat", "wasm_playground/run_server.bat", true);
|
||||||
|
|
||||||
|
if (result == 0) IO_Printf("%-50s - OK\n", "wasm_playground");
|
||||||
|
else IO_Printf("%-50s - ERROR\n", "wasm_playground");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ShouldRun("use_as_data_format_with_typechecking")) {
|
||||||
|
bool result = use_as_data_format_with_typechecking();
|
||||||
|
if (result) IO_Printf("%-50s - OK\n", "use_as_data_format_with_typechecking");
|
||||||
|
else IO_Printf("%-50s - ERROR\n", "use_as_data_format_with_typechecking");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ShouldRun("add_source_location_macro")) {
|
||||||
|
bool result = add_source_location_macro();
|
||||||
|
if (result) IO_Printf("%-50s - OK\n", "add_source_location_macro");
|
||||||
|
else IO_Printf("%-50s - ERROR\n", "add_source_location_macro");
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<Process> processes = {MA_GetAllocator(scratch)};
|
||||||
|
if (ShouldRun("compilation")) {
|
||||||
|
//
|
||||||
|
// Test if things compile in C and C++ mode on all available compilers
|
||||||
|
//
|
||||||
|
S8_String working_dir = PushDir("targets");
|
||||||
|
Array<S8_String> files = {MA_GetAllocator(scratch)};
|
||||||
|
|
||||||
|
files.add("../../../tests/compilation/test_compile_packed.c");
|
||||||
|
files.add("../../../tests/compilation/test_compile_packed_cpp.c");
|
||||||
|
files.add("../../../tests/compilation/test_compile_with_core.c");
|
||||||
|
files.add("../../../tests/compilation/test_compile_with_core_cpp.cpp");
|
||||||
|
files.add("../../../tests/compilation/test_compile_header.cpp");
|
||||||
|
files.add("../../../tools/lcompile.c");
|
||||||
|
|
||||||
|
For(files) {
|
||||||
|
S8_String name_no_ext = S8_GetNameNoExt(it);
|
||||||
|
S8_String exe = Fmt("%.*s.exe", S8_Expand(name_no_ext));
|
||||||
|
S8_String file_to_compile = it;
|
||||||
|
bool is_cpp = S8_EndsWith(it, ".cpp");
|
||||||
|
|
||||||
|
if (UseCL) {
|
||||||
|
S8_String cc = "cl";
|
||||||
|
|
||||||
|
Array<S8_String> flags = {MA_GetAllocator(scratch)};
|
||||||
|
flags += "/Zi -D_CRT_SECURE_NO_WARNINGS";
|
||||||
|
flags += "/FC /WX /W3 /wd4200 /diagnostics:column /nologo";
|
||||||
|
flags += Fmt("/Fe:%.*s /Fd:%.*s.pdb", S8_Expand(exe), S8_Expand(name_no_ext));
|
||||||
|
|
||||||
|
Array<S8_String> link = {MA_GetAllocator(scratch)};
|
||||||
|
link += "/link /incremental:no";
|
||||||
|
|
||||||
|
S8_String dir = Fmt("%.*s_cl_debug_" OS_NAME, S8_Expand(name_no_ext));
|
||||||
|
Process p = RunEx(cc + file_to_compile + flags, dir);
|
||||||
|
processes.add(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UseClang) {
|
||||||
|
S8_String cc = "clang";
|
||||||
|
|
||||||
|
Array<S8_String> flags = {MA_GetAllocator(scratch)};
|
||||||
|
flags += "-g -Wno-write-strings";
|
||||||
|
flags += "-fdiagnostics-absolute-paths";
|
||||||
|
flags += "-fsanitize=address";
|
||||||
|
if (is_cpp) flags += "-std=c++11";
|
||||||
|
IF_LINUX(flags += "-lm";)
|
||||||
|
flags += Fmt("-o %.*s", S8_Expand(exe));
|
||||||
|
|
||||||
|
S8_String dir = Fmt("%.*s_clang_debug_" OS_NAME, S8_Expand(name_no_ext));
|
||||||
|
Process p = RunEx(cc + file_to_compile + flags, dir);
|
||||||
|
processes.add(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UseGCC) {
|
||||||
|
S8_String cc = "gcc";
|
||||||
|
|
||||||
|
Array<S8_String> flags = {MA_GetAllocator(scratch)};
|
||||||
|
flags += "-g -Wno-write-strings";
|
||||||
|
flags += "-fsanitize=address";
|
||||||
|
if (is_cpp) flags += "-std=c++11";
|
||||||
|
IF_LINUX(flags += "-lm";)
|
||||||
|
flags += Fmt("-o %.*s", S8_Expand(exe));
|
||||||
|
|
||||||
|
S8_String dir = Fmt("%.*s_gcc_debug_" OS_NAME, S8_Expand(name_no_ext));
|
||||||
|
Process p = RunEx(cc + file_to_compile + flags, dir);
|
||||||
|
processes.add(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OS_SetWorkingDir(working_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
RunTests();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Wait for compilation from before tests to finish
|
||||||
|
//
|
||||||
|
int result = 0;
|
||||||
|
For(processes) {
|
||||||
|
int exit_code = Wait(&it);
|
||||||
|
if (exit_code != 0) result = exit_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
173
examples/add_dynamic_array_macro/build.cpp
Normal file
173
examples/add_dynamic_array_macro/build.cpp
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
bool add_dynamic_array_macro() {
|
||||||
|
LC_Lang *lang = LC_LangAlloc();
|
||||||
|
lang->use_colored_terminal_output = UseColoredIO;
|
||||||
|
lang->breakpoint_on_error = BreakpointOnError;
|
||||||
|
LC_LangBegin(lang);
|
||||||
|
|
||||||
|
LC_RegisterPackageDir("../pkgs");
|
||||||
|
LC_RegisterPackageDir("../examples");
|
||||||
|
|
||||||
|
LC_Intern name = LC_ILit("add_dynamic_array_macro");
|
||||||
|
LC_ParsePackagesUsingRegistry(name);
|
||||||
|
if (L->errors) {
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<LC_Intern> array_of_to_gen = {MA_GetAllocator(L->arena)};
|
||||||
|
|
||||||
|
LC_Intern init_array = LC_ILit("init_array");
|
||||||
|
LC_AST *init_array_ast = LC_ParseStmtf("{ init_array_base(REF, SIZE, sizeof(REF.data[0])); }");
|
||||||
|
LC_AST *init_array_add_ast = LC_ParseStmtf("{ REF.data[REF.len] = ITEM; REF.len += 1; }");
|
||||||
|
|
||||||
|
LC_Intern add = LC_ILit("add");
|
||||||
|
LC_AST *add_ast = LC_ParseStmtf(
|
||||||
|
"{"
|
||||||
|
" try_growing_array(REF, sizeof(REF.data[0]));"
|
||||||
|
" REF.data[REF.len] = ITEM;"
|
||||||
|
" REF.len += 1;"
|
||||||
|
"}");
|
||||||
|
LC_Intern ITEM = LC_ILit("ITEM");
|
||||||
|
LC_Intern REF = LC_ILit("REF");
|
||||||
|
|
||||||
|
LC_AST *package = LC_GetPackageByName(name);
|
||||||
|
LC_AST *first_ast = (LC_AST *)L->ast_arena->memory.data;
|
||||||
|
for (int i = 0; i < L->ast_count; i += 1) {
|
||||||
|
LC_AST *it = first_ast + i;
|
||||||
|
|
||||||
|
// Detect and save to array every unique use of 'ArrayOfT'
|
||||||
|
if (it->kind == LC_ASTKind_TypespecIdent) {
|
||||||
|
S8_String name = S8_MakeFromChar((char *)it->eident.name);
|
||||||
|
if (S8_StartsWith(name, "ArrayOf")) {
|
||||||
|
S8_String s8 = S8_Skip(name, S8_Lit("ArrayOf").len);
|
||||||
|
LC_Intern type = LC_InternStrLen(s8.str, (int)s8.len);
|
||||||
|
|
||||||
|
bool is_unique = true;
|
||||||
|
For2(in_array, array_of_to_gen) {
|
||||||
|
if (in_array == type) {
|
||||||
|
is_unique = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_unique) array_of_to_gen.add(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it->kind == LC_ASTKind_StmtExpr && it->sexpr.expr->kind == LC_ASTKind_ExprCall) {
|
||||||
|
LC_AST *name = it->sexpr.expr->ecompo.name;
|
||||||
|
LC_AST *call = it->sexpr.expr;
|
||||||
|
|
||||||
|
if (name->kind != LC_ASTKind_ExprIdent) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name->eident.name == add) {
|
||||||
|
if (call->ecompo.size != 2) {
|
||||||
|
LC_ReportASTError(call, "wrong argument count in macro call");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
LC_AST *first_item = call->ecompo.first;
|
||||||
|
LC_AST *arr_ref = first_item->ecompo_item.expr;
|
||||||
|
LC_AST *item = first_item->next->ecompo_item.expr;
|
||||||
|
|
||||||
|
LC_AST *macro = LC_CopyAST(L->arena, add_ast);
|
||||||
|
LC_ASTArray array = LC_FlattenAST(L->arena, macro);
|
||||||
|
|
||||||
|
for (int m_i = 0; m_i < array.len; m_i += 1) {
|
||||||
|
LC_AST *m_it = array.data[m_i];
|
||||||
|
if (m_it->kind == LC_ASTKind_ExprIdent) {
|
||||||
|
if (m_it->eident.name == ITEM) {
|
||||||
|
*m_it = *LC_CopyAST(L->arena, item);
|
||||||
|
} else if (m_it->eident.name == REF) {
|
||||||
|
*m_it = *LC_CopyAST(L->arena, arr_ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_it->pos = it->pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro->next = it->next;
|
||||||
|
macro->prev = it->prev;
|
||||||
|
*it = *macro;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name->eident.name == init_array) {
|
||||||
|
if (call->ecompo.size != 2) {
|
||||||
|
LC_ReportASTError(call, "wrong argument count in macro call");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_AST *first_item = call->ecompo.first;
|
||||||
|
LC_AST *arr_ref = first_item->ecompo_item.expr;
|
||||||
|
LC_AST *item = first_item->next->ecompo_item.expr;
|
||||||
|
|
||||||
|
if (item->kind != LC_ASTKind_ExprCompound) {
|
||||||
|
LC_ReportASTError(item, "expected compound");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_AST *macro = LC_CopyAST(L->arena, init_array_ast);
|
||||||
|
LC_ASTArray array = LC_FlattenAST(L->arena, macro);
|
||||||
|
for (int m_i = 0; m_i < array.len; m_i += 1) {
|
||||||
|
LC_AST *m_it = array.data[m_i];
|
||||||
|
if (m_it->kind != LC_ASTKind_ExprIdent) continue;
|
||||||
|
|
||||||
|
if (m_it->eident.name == LC_ILit("SIZE")) {
|
||||||
|
// This is a bit dangerous here because through
|
||||||
|
// this call we are bumping L->ast_count += 1
|
||||||
|
*m_it = *LC_CreateAST(item->pos, LC_ASTKind_ExprInt);
|
||||||
|
m_it->eatom.i = LC_Bigint_u64(item->ecompo.size);
|
||||||
|
} else if (m_it->eident.name == REF) {
|
||||||
|
*m_it = *LC_CopyAST(L->arena, arr_ref);
|
||||||
|
}
|
||||||
|
m_it->pos = it->pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_ASTFor(item_it, item->ecompo.first) {
|
||||||
|
LC_AST *init_item = LC_CopyAST(L->arena, init_array_add_ast);
|
||||||
|
LC_ASTArray array = LC_FlattenAST(L->arena, init_item);
|
||||||
|
for (int m_i = 0; m_i < array.len; m_i += 1) {
|
||||||
|
LC_AST *m_it = array.data[m_i];
|
||||||
|
|
||||||
|
if (m_it->kind == LC_ASTKind_ExprIdent) {
|
||||||
|
if (m_it->eident.name == ITEM) {
|
||||||
|
*m_it = *LC_CopyAST(L->arena, item_it->ecompo_item.expr);
|
||||||
|
} else if (m_it->eident.name == REF) {
|
||||||
|
*m_it = *LC_CopyAST(L->arena, arr_ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_it->pos = name->pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_DLLAdd(macro->sblock.first, macro->sblock.last, init_item);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro->next = it->next;
|
||||||
|
macro->prev = it->prev;
|
||||||
|
*it = *macro;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo: take package into consideration, my current idea is to have a shared package
|
||||||
|
// that is going to be imported into every other package, for now we use only the current package
|
||||||
|
For(array_of_to_gen) {
|
||||||
|
LC_AST *ast = LC_ParseDeclf("ArrayOf%s :: struct { data: *%s; len: int; cap: int; }", (char *)it, (char *)it);
|
||||||
|
LC_AST *file = package->apackage.ffile;
|
||||||
|
|
||||||
|
LC_DLLAdd(file->afile.fdecl, file->afile.ldecl, ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_OrderAndResolveTopLevelDecls(name);
|
||||||
|
LC_ResolveAllProcBodies();
|
||||||
|
if (L->errors) {
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_String code = LC_GenerateUnityBuild(L->ordered_packages);
|
||||||
|
OS_MakeDir("examples/add_dynamic_array_macro");
|
||||||
|
OS_WriteFile("examples/add_dynamic_array_macro/add_dynamic_array_macro.c", code);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
64
examples/add_dynamic_array_macro/main.lc
Normal file
64
examples/add_dynamic_array_macro/main.lc
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import "libc";
|
||||||
|
|
||||||
|
try_growing_array :: proc(arrp: *void, element_size: size_t) {
|
||||||
|
arr: *ArrayOfvoid = arrp;
|
||||||
|
if (arr.len + 1 > arr.cap) {
|
||||||
|
cap := arr.cap * 2;
|
||||||
|
if (cap == 0) cap = 16;
|
||||||
|
|
||||||
|
arr.data = realloc(arr.data, element_size * :size_t(cap));
|
||||||
|
arr.cap = cap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init_array_base :: proc(arrp: *void, size: int, element_size: size_t) {
|
||||||
|
arr: *ArrayOfvoid = arrp;
|
||||||
|
arr.data = malloc(element_size * :size_t(size * 2));
|
||||||
|
arr.len = size * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc(): int {
|
||||||
|
{
|
||||||
|
arr: ArrayOfint;
|
||||||
|
init_array(&arr, {0, 1, 2, 3, 4, 6, 7, 9, 10});
|
||||||
|
for i := 0; i < 10; i += 1 {
|
||||||
|
assert(arr.data[i] == i);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for it := &arr.data[0]; it < &arr.data[arr.len]; it = &it[1] {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
arr: ArrayOfint;
|
||||||
|
|
||||||
|
for i := 0; i < 128; i += 1 {
|
||||||
|
add(&arr, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 128; i += 1 {
|
||||||
|
assert(arr.data[i] == i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
arr: ArrayOffloat;
|
||||||
|
|
||||||
|
for i: float = 0; i < 128; i += 1 {
|
||||||
|
add(&arr, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 128; i += 1 {
|
||||||
|
i0 := :int(arr.data[i]);
|
||||||
|
assert(i0 == i);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
62
examples/add_instrumentation/build.cpp
Normal file
62
examples/add_instrumentation/build.cpp
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
bool OnDeclParsed_AddInstrumentation(bool discard, LC_AST *n) {
|
||||||
|
if (discard) return discard;
|
||||||
|
|
||||||
|
if (n->kind == LC_ASTKind_DeclProc && n->dproc.body) {
|
||||||
|
if (n->dbase.name == LC_ILit("BeginProc")) return false;
|
||||||
|
if (n->dbase.name == LC_ILit("EndProc")) return false;
|
||||||
|
|
||||||
|
LC_AST *body = n->dproc.body;
|
||||||
|
|
||||||
|
LC_AST *begin = LC_ParseStmtf("BeginProc(\"%s\", \"%s\", %d)", n->dbase.name, n->pos->lex->file, n->pos->line);
|
||||||
|
LC_AST *end = LC_ParseStmtf("defer EndProc();");
|
||||||
|
|
||||||
|
LC_DLLAddFront(body->sblock.first, body->sblock.last, end);
|
||||||
|
LC_DLLAddFront(body->sblock.first, body->sblock.last, begin);
|
||||||
|
}
|
||||||
|
return discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool add_instrumentation() {
|
||||||
|
LC_Lang *lang = LC_LangAlloc();
|
||||||
|
lang->use_colored_terminal_output = UseColoredIO;
|
||||||
|
lang->breakpoint_on_error = BreakpointOnError;
|
||||||
|
lang->on_decl_parsed = OnDeclParsed_AddInstrumentation;
|
||||||
|
LC_LangBegin(lang);
|
||||||
|
|
||||||
|
LC_RegisterPackageDir("../pkgs");
|
||||||
|
LC_RegisterPackageDir("../examples");
|
||||||
|
|
||||||
|
LC_Intern name = LC_ILit("add_instrumentation");
|
||||||
|
LC_ASTRefList packages = LC_ResolvePackageByName(name);
|
||||||
|
if (L->errors) {
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugVerifyAST(packages);
|
||||||
|
if (L->errors) {
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_String code = LC_GenerateUnityBuild(packages);
|
||||||
|
S8_String path = "examples/add_instrumentation/add_instrumentation.c";
|
||||||
|
OS_MakeDir("examples");
|
||||||
|
OS_MakeDir("examples/add_instrumentation");
|
||||||
|
OS_WriteFile(path, code);
|
||||||
|
if (!UseCL) {
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_String cmd = Fmt("cl %.*s -Zi -std:c11 -nologo -FC -Fd:examples/add_instrumentation/a.pdb -Fe:examples/add_instrumentation/add_instrumentation.exe %.*s", S8_Expand(path), S8_Expand(RaylibLIB));
|
||||||
|
int errcode = Run(cmd);
|
||||||
|
if (errcode != 0) {
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Run("examples/add_instrumentation/add_instrumentation.exe");
|
||||||
|
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
13
examples/add_instrumentation/main.lc
Normal file
13
examples/add_instrumentation/main.lc
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import c "libc";
|
||||||
|
|
||||||
|
BeginProc :: proc(func: *char, file: *char, line: int) {
|
||||||
|
c.printf("begin timming proc: %s at %s:%d\n", func, file, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
EndProc :: proc() {
|
||||||
|
c.printf("end timming proc\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc(): int {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
56
examples/add_printf_format_check/build.cpp
Normal file
56
examples/add_printf_format_check/build.cpp
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
void OnExprResolved_CheckPrintf(LC_AST *n, LC_Operand *op);
|
||||||
|
|
||||||
|
bool add_printf_format_check() {
|
||||||
|
bool result = false;
|
||||||
|
LC_Lang *lang = LC_LangAlloc();
|
||||||
|
lang->use_colored_terminal_output = UseColoredIO;
|
||||||
|
lang->breakpoint_on_error = BreakpointOnError;
|
||||||
|
lang->user_data = (void *)&result;
|
||||||
|
lang->on_expr_resolved = OnExprResolved_CheckPrintf;
|
||||||
|
lang->on_message = LC_IgnoreMessage;
|
||||||
|
LC_LangBegin(lang);
|
||||||
|
LC_RegisterPackageDir("../pkgs");
|
||||||
|
LC_RegisterPackageDir("../examples");
|
||||||
|
|
||||||
|
LC_Intern name = LC_ILit("add_printf_format_check");
|
||||||
|
LC_ResolvePackageByName(name);
|
||||||
|
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnExprResolved_CheckPrintf(LC_AST *n, LC_Operand *op) {
|
||||||
|
if (n->kind == LC_ASTKind_ExprCall) {
|
||||||
|
LC_AST *name = n->ecompo.name;
|
||||||
|
if (name->kind == LC_ASTKind_ExprIdent) {
|
||||||
|
LC_Decl *decl = name->eident.resolved_decl;
|
||||||
|
if (decl->name == LC_ILit("printf")) {
|
||||||
|
LC_ResolvedCompo *items = n->ecompo.resolved_items;
|
||||||
|
LC_AST *string = items->first->expr;
|
||||||
|
IO_Assert(string->kind == LC_ASTKind_ExprString);
|
||||||
|
|
||||||
|
char *c = (char *)string->eatom.name;
|
||||||
|
LC_ResolvedCompoItem *item = items->first->next;
|
||||||
|
for (int i = 0; c[i]; i += 1) {
|
||||||
|
if (c[i] == '%') {
|
||||||
|
if (item == NULL) {
|
||||||
|
LC_ReportASTError(n, "too few arguments passed");
|
||||||
|
|
||||||
|
// Test reports success if error thrown correctly
|
||||||
|
*(bool *)L->user_data = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (c[i + 1] == 'd') {
|
||||||
|
if (item->expr->type != L->tint) LC_ReportASTError(item->expr, "expected int type");
|
||||||
|
item = item->next;
|
||||||
|
} else if (c[i + 1] == 's') {
|
||||||
|
if (item->expr->type != L->tpchar) LC_ReportASTError(item->expr, "expected *char type");
|
||||||
|
item = item->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (item != NULL) LC_ReportASTError(item->expr, "too many arguments passed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6
examples/add_printf_format_check/main.lc
Normal file
6
examples/add_printf_format_check/main.lc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import "libc";
|
||||||
|
|
||||||
|
main :: proc(): int {
|
||||||
|
printf("testing %d %s %d\n", 32, "str");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
48
examples/add_source_location_macro/build.cpp
Normal file
48
examples/add_source_location_macro/build.cpp
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/* This function is called after compiler determined procedure type but
|
||||||
|
** before the arguments got verified. This opens up a window for call manipulation.
|
||||||
|
**
|
||||||
|
** I basically swoop in and add the {file, line} information to the call LC_AST before
|
||||||
|
** the arguments get resolved.
|
||||||
|
*/
|
||||||
|
void ModifyCallsDuringResolution(LC_AST *n, LC_Type *type) {
|
||||||
|
LC_TypeMember *tm = type->tproc.args.last;
|
||||||
|
if (tm && tm->type->kind == LC_TypeKind_Struct && tm->type->decl->name == LC_ILit("SourceLoc")) {
|
||||||
|
LC_AST *call_item = LC_CreateAST(n->pos, LC_ASTKind_ExprCallItem);
|
||||||
|
call_item->ecompo_item.name = LC_ILit("source_loc");
|
||||||
|
call_item->ecompo_item.expr = LC_ParseExprf("{\"%s\", %d}", n->pos->lex->file, n->pos->line);
|
||||||
|
LC_SetASTPosOnAll(call_item->ecompo_item.expr, n->pos);
|
||||||
|
|
||||||
|
LC_DLLAdd(n->ecompo.first, n->ecompo.last, call_item);
|
||||||
|
n->ecompo.size += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool add_source_location_macro() {
|
||||||
|
LC_Lang *lang = LC_LangAlloc();
|
||||||
|
lang->use_colored_terminal_output = UseColoredIO;
|
||||||
|
lang->breakpoint_on_error = BreakpointOnError;
|
||||||
|
lang->before_call_args_resolved = ModifyCallsDuringResolution;
|
||||||
|
LC_LangBegin(lang);
|
||||||
|
LC_RegisterPackageDir("../examples/");
|
||||||
|
LC_RegisterPackageDir("../pkgs");
|
||||||
|
|
||||||
|
LC_Intern name = LC_ILit("add_source_location_macro");
|
||||||
|
LC_ASTRefList packages = LC_ResolvePackageByName(name);
|
||||||
|
if (L->errors) {
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_String dir = "examples/add_source_location_macro";
|
||||||
|
OS_MakeDir(dir);
|
||||||
|
S8_String cfile = "examples/add_source_location_macro/add_source_location_macro.c";
|
||||||
|
S8_String code = LC_GenerateUnityBuild(packages);
|
||||||
|
OS_WriteFile(cfile, code);
|
||||||
|
if (L->errors) {
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
16
examples/add_source_location_macro/main.lc
Normal file
16
examples/add_source_location_macro/main.lc
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import "libc";
|
||||||
|
|
||||||
|
SourceLoc :: struct {
|
||||||
|
file: String;
|
||||||
|
line: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
Allocate :: proc(size: int, source_loc: SourceLoc = {}): *void {
|
||||||
|
printf("%.*s:%d\n", source_loc.file.len, source_loc.file.str, source_loc.line);
|
||||||
|
return malloc(:size_t(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc(): int {
|
||||||
|
value: *int = Allocate(32);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
30
examples/create_raylib_window/build.cpp
Normal file
30
examples/create_raylib_window/build.cpp
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
bool create_raylib_window() {
|
||||||
|
LC_Lang *lang = LC_LangAlloc();
|
||||||
|
lang->use_colored_terminal_output = UseColoredIO;
|
||||||
|
lang->breakpoint_on_error = BreakpointOnError;
|
||||||
|
LC_LangBegin(lang);
|
||||||
|
LC_RegisterPackageDir("../examples/");
|
||||||
|
LC_RegisterPackageDir("../pkgs");
|
||||||
|
|
||||||
|
LC_Intern name = LC_ILit("create_raylib_window");
|
||||||
|
LC_ASTRefList packages = LC_ResolvePackageByName(name);
|
||||||
|
if (L->errors) {
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_MakeDir("examples/create_raylib_window");
|
||||||
|
S8_String path = "examples/create_raylib_window/create_raylib_window.c";
|
||||||
|
S8_String code = LC_GenerateUnityBuild(packages);
|
||||||
|
OS_WriteFile(path, code);
|
||||||
|
|
||||||
|
bool success = true;
|
||||||
|
if (UseCL) {
|
||||||
|
OS_CopyFile(RaylibDLL, "examples/create_raylib_window/raylib.dll", true);
|
||||||
|
S8_String cmd = Fmt("cl %.*s -Zi -std:c11 -nologo -FC -Fd:examples/create_raylib_window/a.pdb -Fe:examples/create_raylib_window/create_raylib_window.exe %.*s", S8_Expand(path), S8_Expand(RaylibLIB));
|
||||||
|
int errcode = Run(cmd);
|
||||||
|
if (errcode != 0) success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
16
examples/create_raylib_window/main.lc
Normal file
16
examples/create_raylib_window/main.lc
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import RL "raylib";
|
||||||
|
|
||||||
|
main :: proc(): int {
|
||||||
|
RL.InitWindow(800, 600, "Thing");
|
||||||
|
RL.SetTargetFPS(60);
|
||||||
|
|
||||||
|
for !RL.WindowShouldClose() {
|
||||||
|
RL.BeginDrawing();
|
||||||
|
RL.ClearBackground(RL.RAYWHITE);
|
||||||
|
RL.DrawText("Congrats! You created your first window!", 190, 200, 20, RL.LIGHTGRAY);
|
||||||
|
RL.EndDrawing();
|
||||||
|
}
|
||||||
|
|
||||||
|
RL.CloseWindow();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
64
examples/generate_type_info/build.cpp
Normal file
64
examples/generate_type_info/build.cpp
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
bool generate_type_info() {
|
||||||
|
OS_DeleteFile("../examples/generate_type_info/generated.lc");
|
||||||
|
|
||||||
|
LC_Lang *lang = LC_LangAlloc();
|
||||||
|
lang->use_colored_terminal_output = UseColoredIO;
|
||||||
|
lang->breakpoint_on_error = BreakpointOnError;
|
||||||
|
LC_LangBegin(lang);
|
||||||
|
|
||||||
|
LC_RegisterPackageDir("../pkgs");
|
||||||
|
LC_RegisterPackageDir("../examples");
|
||||||
|
|
||||||
|
LC_Intern name = LC_ILit("generate_type_info");
|
||||||
|
|
||||||
|
LC_ParsePackagesUsingRegistry(name);
|
||||||
|
if (L->errors) {
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_OrderAndResolveTopLevelDecls(name);
|
||||||
|
if (L->errors) {
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_BeginStringGen(L->arena);
|
||||||
|
LC_AST *package = LC_GetPackageByName(name);
|
||||||
|
|
||||||
|
// Iterate through all the types and generate struct member information
|
||||||
|
LC_Type *first = (LC_Type *)L->type_arena->memory.data;
|
||||||
|
for (int i = 0; i < L->type_count; i += 1) {
|
||||||
|
LC_Type *type = first + i;
|
||||||
|
if (type->kind != LC_TypeKind_Struct) continue;
|
||||||
|
|
||||||
|
LC_GenLinef("%s_Members := :[]StructMem{", (char *)type->decl->name);
|
||||||
|
L->printer.indent += 1;
|
||||||
|
|
||||||
|
LC_TypeFor(it, type->tagg.mems.first) {
|
||||||
|
LC_GenLinef("{name = \"%s\", offset = %d},", (char *)it->name, it->offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
L->printer.indent -= 1;
|
||||||
|
LC_GenLinef("};");
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_String code = LC_EndStringGen(L->arena);
|
||||||
|
S8_String path = OS_GetAbsolutePath(L->arena, "../examples/generate_type_info/generated.lc");
|
||||||
|
OS_WriteFile(path, code);
|
||||||
|
|
||||||
|
LC_ParseFile(package, path.str, code.str, 0);
|
||||||
|
if (L->errors) {
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve decls again with new content added in
|
||||||
|
LC_OrderAndResolveTopLevelDecls(name);
|
||||||
|
LC_ResolveAllProcBodies();
|
||||||
|
LC_FindUnusedLocalsAndRemoveUnusedGlobalDecls();
|
||||||
|
|
||||||
|
bool result = L->errors ? false : true;
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
13
examples/generate_type_info/generated.lc
Normal file
13
examples/generate_type_info/generated.lc
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
StructMem_Members := :[]StructMem{
|
||||||
|
{name = "name", offset = 0},
|
||||||
|
{name = "offset", offset = 8},
|
||||||
|
};
|
||||||
|
Typeinfo_Members := :[]StructMem{
|
||||||
|
{name = "kind", offset = 0},
|
||||||
|
{name = "name", offset = 8},
|
||||||
|
{name = "size", offset = 16},
|
||||||
|
{name = "align", offset = 20},
|
||||||
|
{name = "child_count", offset = 24},
|
||||||
|
{name = "struct_mems", offset = 32},
|
||||||
|
};
|
||||||
19
examples/generate_type_info/main.lc
Normal file
19
examples/generate_type_info/main.lc
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
TypeKind :: typedef int;
|
||||||
|
TYPE_KIND_STRUCT :: 0;
|
||||||
|
|
||||||
|
StructMem :: struct {
|
||||||
|
name: *char;
|
||||||
|
offset: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
Typeinfo :: struct {
|
||||||
|
kind: TypeKind;
|
||||||
|
name: *char;
|
||||||
|
|
||||||
|
size: int;
|
||||||
|
align: int;
|
||||||
|
|
||||||
|
child_count: int;
|
||||||
|
struct_mems: *StructMem;
|
||||||
|
}
|
||||||
|
|
||||||
29
examples/hello_world/build.cpp
Normal file
29
examples/hello_world/build.cpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
bool hello_world() {
|
||||||
|
LC_Lang *lang = LC_LangAlloc();
|
||||||
|
lang->use_colored_terminal_output = UseColoredIO;
|
||||||
|
lang->breakpoint_on_error = BreakpointOnError;
|
||||||
|
LC_LangBegin(lang);
|
||||||
|
LC_RegisterPackageDir("../examples/");
|
||||||
|
LC_RegisterPackageDir("../pkgs");
|
||||||
|
|
||||||
|
LC_Intern name = LC_ILit("hello_world");
|
||||||
|
LC_ASTRefList packages = LC_ResolvePackageByName(name);
|
||||||
|
if (L->errors) {
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_MakeDir("examples/hello_world");
|
||||||
|
S8_String code = LC_GenerateUnityBuild(packages);
|
||||||
|
S8_String path = "examples/hello_world/hello_world.c";
|
||||||
|
OS_WriteFile(path, code);
|
||||||
|
|
||||||
|
bool success = true;
|
||||||
|
if (UseCL) {
|
||||||
|
S8_String cmd = Fmt("cl %.*s -Zi -std:c11 -nologo -FC -Fd:examples/hello_world/hello_world.pdb -Fe:examples/hello_world/hello_world.exe %.*s", S8_Expand(path), S8_Expand(RaylibLIB));
|
||||||
|
if (Run(cmd) != 0) success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
6
examples/hello_world/main.lc
Normal file
6
examples/hello_world/main.lc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import "libc";
|
||||||
|
|
||||||
|
main :: proc(): int {
|
||||||
|
printf("hello world!\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
147
examples/pathfinding_visualizer/array.lc
Normal file
147
examples/pathfinding_visualizer/array.lc
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
AddActorToArray :: proc(arr: *ArrayOfActors, actor: Actor) {
|
||||||
|
if arr.len + 1 > arr.cap {
|
||||||
|
new_cap := arr.cap * 2;
|
||||||
|
if (new_cap == 0) new_cap = 16;
|
||||||
|
|
||||||
|
arr.data = libc.realloc(arr.data, sizeof(:Actor) * :libc.size_t(new_cap));
|
||||||
|
arr.cap = new_cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
arr.data[arr.len] = actor;
|
||||||
|
arr.len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
TryGrowingActorArray :: proc(a: *ArrayOfActors) {
|
||||||
|
if a.len + 1 > a.cap {
|
||||||
|
new_cap := a.cap * 2;
|
||||||
|
if (new_cap == 0) new_cap = 16;
|
||||||
|
a.data = libc.realloc(a.data, sizeof(:Actor) * :libc.size_t(new_cap));
|
||||||
|
a.cap = new_cap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertActor :: proc(a: *ArrayOfActors, item: Actor, index: int) {
|
||||||
|
if index == a.len {
|
||||||
|
AddActorToArray(a, item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert(index < a.len);
|
||||||
|
Assert(index >= 0);
|
||||||
|
|
||||||
|
TryGrowingActorArray(a);
|
||||||
|
right_len := :libc.size_t(a.len - index);
|
||||||
|
libc.memmove(&a.data[index + 1], &a.data[index], sizeof(:Actor) * right_len);
|
||||||
|
a.data[index] = item;
|
||||||
|
a.len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetLastActor :: proc(a: ArrayOfActors): *Actor {
|
||||||
|
Assert(a.len > 0);
|
||||||
|
result := &a.data[a.len - 1];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
AddPath :: proc(arr: *ArrayOfPaths, actor: Path) {
|
||||||
|
if arr.len + 1 > arr.cap {
|
||||||
|
new_cap := arr.cap * 2;
|
||||||
|
if (new_cap == 0) new_cap = 16;
|
||||||
|
|
||||||
|
arr.data = libc.realloc(arr.data, sizeof(:Path) * :libc.size_t(new_cap));
|
||||||
|
arr.cap = new_cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
arr.data[arr.len] = actor;
|
||||||
|
arr.len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
TryGrowingPathArray :: proc(a: *ArrayOfPaths) {
|
||||||
|
if a.len + 1 > a.cap {
|
||||||
|
new_cap := a.cap * 2;
|
||||||
|
if (new_cap == 0) new_cap = 16;
|
||||||
|
a.data = libc.realloc(a.data, sizeof(:Path) * :libc.size_t(new_cap));
|
||||||
|
a.cap = new_cap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertPath :: proc(a: *ArrayOfPaths, item: Path, index: int) {
|
||||||
|
if index == a.len {
|
||||||
|
AddPath(a, item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert(index < a.len);
|
||||||
|
Assert(index >= 0);
|
||||||
|
|
||||||
|
TryGrowingPathArray(a);
|
||||||
|
right_len := :libc.size_t(a.len - index);
|
||||||
|
libc.memmove(&a.data[index + 1], &a.data[index], sizeof(:Path) * right_len);
|
||||||
|
a.data[index] = item;
|
||||||
|
a.len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertSortedPath :: proc(a: *ArrayOfPaths, item: Path) {
|
||||||
|
insert_index := -1;
|
||||||
|
for i := 0; i < a.len; i += 1 {
|
||||||
|
it := &a.data[i];
|
||||||
|
if it.value_to_sort_by <= item.value_to_sort_by {
|
||||||
|
insert_index = i;
|
||||||
|
InsertPath(a, item, i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if insert_index == -1 {
|
||||||
|
AddPath(a, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GetLastPath :: proc(a: ArrayOfPaths): *Path {
|
||||||
|
Assert(a.len > 0);
|
||||||
|
result := &a.data[a.len - 1];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
PopPath :: proc(a: *ArrayOfPaths): Path {
|
||||||
|
a.len -= 1;
|
||||||
|
result := a.data[a.len];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
AddV2I :: proc(arr: *ArrayOfV2Is, item: V2I) {
|
||||||
|
if arr.len + 1 > arr.cap {
|
||||||
|
new_cap := arr.cap * 2;
|
||||||
|
if (new_cap == 0) new_cap = 16;
|
||||||
|
|
||||||
|
arr.data = libc.realloc(arr.data, sizeof(:V2I) * :libc.size_t(new_cap));
|
||||||
|
arr.cap = new_cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
arr.data[arr.len] = item;
|
||||||
|
arr.len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
AddAnimationSetTile :: proc(arr: *ArrayOfAnimationSetTiles, item: AnimationSetTile) {
|
||||||
|
if arr.len + 1 > arr.cap {
|
||||||
|
new_cap := arr.cap * 2;
|
||||||
|
if (new_cap == 0) new_cap = 16;
|
||||||
|
|
||||||
|
arr.data = libc.realloc(arr.data, sizeof(:AnimationSetTile) * :libc.size_t(new_cap));
|
||||||
|
arr.cap = new_cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
arr.data[arr.len] = item;
|
||||||
|
arr.len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnorderedRemoveAnimationSetTile :: proc(a: *ArrayOfAnimationSetTiles, item: *AnimationSetTile) {
|
||||||
|
Assert(a.len > 0);
|
||||||
|
Assert(item >= a.data && item < &a.data[a.len]);
|
||||||
|
a.len -= 1;
|
||||||
|
*item = a.data[a.len];
|
||||||
|
}
|
||||||
42
examples/pathfinding_visualizer/build.cpp
Normal file
42
examples/pathfinding_visualizer/build.cpp
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
bool pathfinding_visualizer() {
|
||||||
|
LC_Lang *lang = LC_LangAlloc();
|
||||||
|
lang->use_colored_terminal_output = UseColoredIO;
|
||||||
|
lang->breakpoint_on_error = BreakpointOnError;
|
||||||
|
LC_LangBegin(lang);
|
||||||
|
|
||||||
|
LC_RegisterPackageDir("../examples/");
|
||||||
|
LC_RegisterPackageDir("../pkgs");
|
||||||
|
|
||||||
|
LC_Intern name = LC_ILit("pathfinding_visualizer");
|
||||||
|
LC_ASTRefList packages = LC_ResolvePackageByName(name);
|
||||||
|
LC_FindUnusedLocalsAndRemoveUnusedGlobalDecls();
|
||||||
|
if (L->errors) {
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugVerifyAST(packages);
|
||||||
|
if (L->errors) {
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_MakeDir("examples");
|
||||||
|
OS_MakeDir("examples/pathfinding_visualizer");
|
||||||
|
S8_String code = LC_GenerateUnityBuild(packages);
|
||||||
|
S8_String path = "examples/pathfinding_visualizer/pathfinding_visualizer.c";
|
||||||
|
OS_WriteFile(path, code);
|
||||||
|
|
||||||
|
if (!UseCL) {
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_String cmd = Fmt("cl %.*s -Zi -std:c11 -nologo -FC -Fd:examples/pathfinding_visualizer/a.pdb -Fe:examples/pathfinding_visualizer/pathfinding_visualizer.exe %.*s", S8_Expand(path), S8_Expand(RaylibLIB));
|
||||||
|
int errcode = Run(cmd);
|
||||||
|
OS_CopyFile(RaylibDLL, "examples/pathfinding_visualizer/raylib.dll", true);
|
||||||
|
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
bool result = errcode == 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
266
examples/pathfinding_visualizer/main.lc
Normal file
266
examples/pathfinding_visualizer/main.lc
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
import "raylib";
|
||||||
|
import libc "libc";
|
||||||
|
import "std_types";
|
||||||
|
|
||||||
|
WinX := 1280;
|
||||||
|
WinY := 720;
|
||||||
|
Mode := 0;
|
||||||
|
RectX :: 16;
|
||||||
|
RectY :: 16;
|
||||||
|
Dt: float;
|
||||||
|
|
||||||
|
MouseX:= 0;
|
||||||
|
MouseY:= 0;
|
||||||
|
MouseP: Vector2 = {0, 0};
|
||||||
|
|
||||||
|
MouseSelecting := false;
|
||||||
|
MouseSelectionPivot: Vector2;
|
||||||
|
MouseSelectionBox: Rectangle;
|
||||||
|
MouseSelectedActors: ArrayOfActors;
|
||||||
|
|
||||||
|
AnimationSetTiles: ArrayOfAnimationSetTiles;
|
||||||
|
|
||||||
|
AnimationSetTile :: struct {
|
||||||
|
set: bool;
|
||||||
|
p: V2I;
|
||||||
|
t: float;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayOfAnimationSetTiles :: struct {
|
||||||
|
data: *AnimationSetTile;
|
||||||
|
len: int;
|
||||||
|
cap: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc(): int {
|
||||||
|
InitMap();
|
||||||
|
InitWindow(WinX, WinY, "pathfinding visualizer");
|
||||||
|
SetTargetFPS(60);
|
||||||
|
|
||||||
|
orange := ORANGE;
|
||||||
|
orange.a = 255/2;
|
||||||
|
brown := BROWN;
|
||||||
|
brown.a = 255/2;
|
||||||
|
dark_green := DARKGREEN;
|
||||||
|
dark_green.a = 255/2;
|
||||||
|
blue := BLUE;
|
||||||
|
blue.a = 255/2;
|
||||||
|
green := GREEN;
|
||||||
|
green.a = 255/2;
|
||||||
|
red := RED;
|
||||||
|
red.a = 255/2;
|
||||||
|
|
||||||
|
actor_color := dark_green;
|
||||||
|
past_actor_color := blue;
|
||||||
|
target_color := red;
|
||||||
|
selection_box_color := green;
|
||||||
|
selected_color := selection_box_color;
|
||||||
|
|
||||||
|
for !WindowShouldClose() {
|
||||||
|
WinX = GetScreenHeight();
|
||||||
|
WinY = GetScreenWidth();
|
||||||
|
MouseX = GetMouseX();
|
||||||
|
MouseY = GetMouseY();
|
||||||
|
MouseP = GetMousePosition();
|
||||||
|
Dt = GetFrameTime();
|
||||||
|
@unused map := &CurrentMap;
|
||||||
|
|
||||||
|
MouseSelecting = false;
|
||||||
|
if IsMouseButtonDown(MOUSE_BUTTON_LEFT) {
|
||||||
|
MouseSelecting = true;
|
||||||
|
if IsMouseButtonPressed(MOUSE_BUTTON_LEFT) {
|
||||||
|
MouseSelectionPivot = MouseP;
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseSelectionBox = {
|
||||||
|
MouseSelectionPivot.x,
|
||||||
|
MouseSelectionPivot.y,
|
||||||
|
MouseP.x - MouseSelectionPivot.x,
|
||||||
|
MouseP.y - MouseSelectionPivot.y,
|
||||||
|
};
|
||||||
|
|
||||||
|
if MouseSelectionBox.width < 0 {
|
||||||
|
MouseSelectionBox.x += MouseSelectionBox.width;
|
||||||
|
MouseSelectionBox.width = -MouseSelectionBox.width;
|
||||||
|
}
|
||||||
|
if MouseSelectionBox.height < 0 {
|
||||||
|
MouseSelectionBox.y += MouseSelectionBox.height;
|
||||||
|
MouseSelectionBox.height = -MouseSelectionBox.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if IsKeyPressed(KEY_F1) {
|
||||||
|
Mode = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if IsKeyPressed(KEY_F2) {
|
||||||
|
Mode = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if IsKeyPressed(KEY_F3) {
|
||||||
|
for i := 0; i < map.actors.len; i += 1 {
|
||||||
|
it := &map.actors.data[i];
|
||||||
|
MoveTowardsTarget(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PathFindUpdate(map);
|
||||||
|
|
||||||
|
if IsKeyPressed(KEY_F4) {
|
||||||
|
RandomizeActors();
|
||||||
|
}
|
||||||
|
|
||||||
|
BeginDrawing();
|
||||||
|
ClearBackground(RAYWHITE);
|
||||||
|
|
||||||
|
map_rectangle: Rectangle = {0, 0, :float(map.x) * RectX, :float(map.y) * RectY};
|
||||||
|
DrawRectangleRec(map_rectangle, LIGHTGRAY);
|
||||||
|
|
||||||
|
for x := 0; x < map.x; x += 1 {
|
||||||
|
for y := 0; y < map.y; y += 1 {
|
||||||
|
it := map.data[x + y*map.x];
|
||||||
|
r : Rectangle = {:float(x) * RectX, :float(y) * RectY, RectX, RectY};
|
||||||
|
r2: Rectangle = {r.x + 1, r.y + 1, r.width - 2, r.height - 2};
|
||||||
|
|
||||||
|
colliding := CheckCollisionPointRec(MouseP, r);
|
||||||
|
color := RAYWHITE;
|
||||||
|
if it == 1 { color = GRAY; }
|
||||||
|
|
||||||
|
if Mode == 0 {
|
||||||
|
if colliding && IsMouseButtonDown(MOUSE_BUTTON_LEFT) {
|
||||||
|
AddAnimationSetTile(&AnimationSetTiles, {true, {x,y}});
|
||||||
|
}
|
||||||
|
if colliding && IsMouseButtonDown(MOUSE_BUTTON_RIGHT) {
|
||||||
|
AddAnimationSetTile(&AnimationSetTiles, {false, {x,y}});
|
||||||
|
}
|
||||||
|
if colliding {
|
||||||
|
color.a = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawRectangleRec(r2, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for tile_i := 0; tile_i < AnimationSetTiles.len; tile_i += 1 {
|
||||||
|
tile_it := &AnimationSetTiles.data[tile_i];
|
||||||
|
remove := false;
|
||||||
|
|
||||||
|
t := tile_it.t;
|
||||||
|
if tile_it.set == false {
|
||||||
|
t = 1 - t;
|
||||||
|
}
|
||||||
|
|
||||||
|
x := :float(tile_it.p.x) * RectX + 1;
|
||||||
|
y := :float(tile_it.p.y) * RectY + 1;
|
||||||
|
|
||||||
|
w: float = RectX - 2;
|
||||||
|
h: float = RectY - 2;
|
||||||
|
wt := w * t;
|
||||||
|
ht := h * t;
|
||||||
|
wd := w - wt;
|
||||||
|
hd := h - ht;
|
||||||
|
|
||||||
|
r: Rectangle = {x + wd/2, y + hd/2, wt, ht};
|
||||||
|
DrawRectangleRec(r, GRAY);
|
||||||
|
|
||||||
|
if tile_it.t > 1 {
|
||||||
|
map_tile := &map.data[tile_it.p.x + tile_it.p.y*map.x];
|
||||||
|
if tile_it.set {*map_tile |= TILE_BLOCKER;}
|
||||||
|
else {*map_tile &= ~TILE_BLOCKER;}
|
||||||
|
|
||||||
|
remove = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
tile_it.t += Dt*8;
|
||||||
|
if remove {
|
||||||
|
UnorderedRemoveAnimationSetTile(&AnimationSetTiles, tile_it);
|
||||||
|
tile_i -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for actor_i := 0; actor_i < map.actors.len; actor_i += 1 {
|
||||||
|
actor_it := &map.actors.data[actor_i];
|
||||||
|
target_r := Rect(actor_it.target_p);
|
||||||
|
|
||||||
|
main_p := Circle(actor_it.p);
|
||||||
|
DrawCircleV(main_p, RectX/2, actor_color);
|
||||||
|
DrawRectangleRec(target_r, target_color);
|
||||||
|
|
||||||
|
smaller_the_further: float;
|
||||||
|
for tile_i := actor_it.tiles_visited.len - 1; tile_i >= 0; tile_i -= 1 {
|
||||||
|
tile_it := &actor_it.tiles_visited.data[tile_i];
|
||||||
|
p := Circle({tile_it.x, tile_it.y});
|
||||||
|
DrawCircleV(p, RectX/2 - smaller_the_further, past_actor_color);
|
||||||
|
smaller_the_further += 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
for path_i := 0; path_i < actor_it.open_paths.len; path_i += 1 {
|
||||||
|
path_it := &actor_it.open_paths.data[path_i];
|
||||||
|
path_r := Rect(path_it.p);
|
||||||
|
DrawRectangleRec(path_r, orange);
|
||||||
|
s := TextFormat("%d", :int(libc.sqrtf(:float(path_it.value_to_sort_by))));
|
||||||
|
DrawText(s, :int(path_r.x), :int(path_r.y), 1, RAYWHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
for path_i := 0; path_i < actor_it.close_paths.len; path_i += 1 {
|
||||||
|
path_it := &actor_it.close_paths.data[path_i];
|
||||||
|
path_r := Rect(path_it.p);
|
||||||
|
DrawRectangleRec(path_r, brown);
|
||||||
|
}
|
||||||
|
|
||||||
|
for path_i := 0; path_i < actor_it.history.len; path_i += 1 {
|
||||||
|
path_it := &actor_it.history.data[path_i];
|
||||||
|
|
||||||
|
p0 := Circle(path_it.came_from);
|
||||||
|
p1 := Circle(path_it.p);
|
||||||
|
|
||||||
|
DrawLineEx(p0, p1, 5, LIGHTGRAY);
|
||||||
|
DrawCircleV(p0, 4, LIGHTGRAY);
|
||||||
|
DrawCircleV(p1, 4, LIGHTGRAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if Mode == 1 {
|
||||||
|
for actor_i := 0; actor_i < MouseSelectedActors.len; actor_i += 1 {
|
||||||
|
actor_it := &MouseSelectedActors.data[actor_i];
|
||||||
|
actor_box := Rect(actor_it.p);
|
||||||
|
DrawRectangleRec(actor_box, selected_color);
|
||||||
|
|
||||||
|
if IsMouseButtonPressed(MOUSE_BUTTON_RIGHT) {
|
||||||
|
p := ScreenToMap(MouseP);
|
||||||
|
SetTargetP(actor_it, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if MouseSelecting {
|
||||||
|
MouseSelectedActors.len = 0;
|
||||||
|
for actor_i := 0; actor_i < MouseSelectedActors.len; actor_i += 1 {
|
||||||
|
actor_it := &MouseSelectedActors.data[actor_i];
|
||||||
|
actor_box := Rect(actor_it.p);
|
||||||
|
|
||||||
|
if CheckCollisionRecs(actor_box, MouseSelectionBox) {
|
||||||
|
AddActorToArray(&MouseSelectedActors, *actor_it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DrawRectangleRec(MouseSelectionBox, selection_box_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text_size := 24;
|
||||||
|
text_p := 4;
|
||||||
|
text_y := text_size * 2;
|
||||||
|
|
||||||
|
DrawText("F4 :: Randomize actors", text_p, text_y, text_size, GRAY);
|
||||||
|
text_y -= text_size;
|
||||||
|
DrawText("F3 :: Simulate actors", text_p, text_y, text_size, GRAY);
|
||||||
|
text_y -= text_size;
|
||||||
|
text: *char = "Mode(F1) :: Block placing";
|
||||||
|
if Mode == 1 { text = "Mode(F2) :: Actor placing"; }
|
||||||
|
DrawText(text, text_p, text_y, text_size, GRAY);
|
||||||
|
text_y -= text_size;
|
||||||
|
|
||||||
|
EndDrawing();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
286
examples/pathfinding_visualizer/map.lc
Normal file
286
examples/pathfinding_visualizer/map.lc
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
CurrentMap: Map;
|
||||||
|
|
||||||
|
Tile :: typedef int;
|
||||||
|
TILE_BLOCKER :: 1;
|
||||||
|
TILE_ACTOR_IS_STANDING :: <<;
|
||||||
|
|
||||||
|
Map :: struct {
|
||||||
|
data: *Tile;
|
||||||
|
x: int;
|
||||||
|
y: int;
|
||||||
|
actors: ArrayOfActors;
|
||||||
|
}
|
||||||
|
|
||||||
|
V2I :: struct {
|
||||||
|
x: int;
|
||||||
|
y: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
Actor :: struct {
|
||||||
|
p: V2I;
|
||||||
|
target_p: V2I;
|
||||||
|
map: *Map;
|
||||||
|
|
||||||
|
open_paths: ArrayOfPaths;
|
||||||
|
close_paths: ArrayOfPaths;
|
||||||
|
tiles_visited: ArrayOfV2Is;
|
||||||
|
history: ArrayOfPaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
Path :: struct {
|
||||||
|
value_to_sort_by: int;
|
||||||
|
p: V2I;
|
||||||
|
came_from: V2I;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayOfActors :: struct {
|
||||||
|
data: *Actor;
|
||||||
|
len: int;
|
||||||
|
cap: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayOfPaths :: struct {
|
||||||
|
data: *Path;
|
||||||
|
len: int;
|
||||||
|
cap: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayOfV2Is :: struct {
|
||||||
|
data: *V2I;
|
||||||
|
len: int;
|
||||||
|
cap: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect :: proc(p: V2I): Rectangle {
|
||||||
|
result: Rectangle = {:float(p.x) * RectX, :float(p.y) * RectY, RectX, RectY};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Circle :: proc(p: V2I): Vector2 {
|
||||||
|
result: Vector2 = {:float(p.x) * RectX + RectX/2, :float(p.y) * RectY + RectY/2};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScreenToMap :: proc(p: Vector2): V2I {
|
||||||
|
x := p.x / RectX;
|
||||||
|
y := p.y / RectY;
|
||||||
|
|
||||||
|
result: V2I = {:int(x), :int(y)};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaxInt :: proc(x: int, y: int): int {
|
||||||
|
if x > y return x;
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert :: proc(x: bool) {
|
||||||
|
if (!x) {
|
||||||
|
libc.printf("assertion failed\n");
|
||||||
|
libc.fflush(libc.stdout);
|
||||||
|
libc.debug_break();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InvalidCodepath :: proc() {
|
||||||
|
libc.printf("invalid codepath\n");
|
||||||
|
libc.fflush(libc.stdout);
|
||||||
|
libc.debug_break();
|
||||||
|
}
|
||||||
|
|
||||||
|
AddActor :: proc(map: *Map, p: V2I): *Actor {
|
||||||
|
AddActorToArray(&map.actors, {p, p, map});
|
||||||
|
map.data[p.x + p.y * map.x] |= TILE_ACTOR_IS_STANDING;
|
||||||
|
result := GetLastActor(map.actors);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetActorP :: proc(actor: *Actor, p: V2I) {
|
||||||
|
map := actor.map;
|
||||||
|
new_tile := &map.data[p.x + p.y * map.x];
|
||||||
|
if *new_tile != 0 return;
|
||||||
|
|
||||||
|
tile := &map.data[actor.p.x + actor.p.y * map.x];
|
||||||
|
Assert((*tile & TILE_ACTOR_IS_STANDING) != 0);
|
||||||
|
*tile &= ~TILE_ACTOR_IS_STANDING;
|
||||||
|
|
||||||
|
*new_tile |= TILE_ACTOR_IS_STANDING;
|
||||||
|
actor.p = p;
|
||||||
|
|
||||||
|
actor.tiles_visited.len = 0;
|
||||||
|
actor.history.len = 0;
|
||||||
|
actor.open_paths.len = 0;
|
||||||
|
actor.close_paths.len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetTargetP :: proc(actor: *Actor, p: V2I) {
|
||||||
|
actor.target_p = p;
|
||||||
|
|
||||||
|
actor.tiles_visited.len = 0;
|
||||||
|
actor.history.len = 0;
|
||||||
|
actor.open_paths.len = 0;
|
||||||
|
actor.close_paths.len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetRandomP :: proc(m: *Map): V2I {
|
||||||
|
result: V2I = {GetRandomInt(0, m.x - 1), GetRandomInt(0, m.y - 1)};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetRandomUnblockedP :: proc(m: *Map): V2I {
|
||||||
|
for i := 0; i < 128; i += 1 {
|
||||||
|
p := GetRandomP(m);
|
||||||
|
if m.data[p.x + p.y * m.x] == 0 {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assert(!:ullong(:*char("invalid codepath")));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
InitMap :: proc() {
|
||||||
|
CurrentMap.x = WinX / RectX;
|
||||||
|
CurrentMap.y = WinY / RectY;
|
||||||
|
|
||||||
|
bytes := sizeof(:Tile) * CurrentMap.x * CurrentMap.y;
|
||||||
|
CurrentMap.data = libc.malloc(:libc.size_t(bytes));
|
||||||
|
libc.memset(CurrentMap.data, 0, :libc.size_t(bytes));
|
||||||
|
|
||||||
|
actor := AddActor(&CurrentMap, GetRandomUnblockedP(&CurrentMap));
|
||||||
|
actor.target_p = GetRandomUnblockedP(&CurrentMap);
|
||||||
|
|
||||||
|
actor2 := AddActor(&CurrentMap, GetRandomUnblockedP(&CurrentMap));
|
||||||
|
actor2.target_p = GetRandomUnblockedP(&CurrentMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
RandomizeActors :: proc() {
|
||||||
|
map := &CurrentMap;
|
||||||
|
for i := 0; i < map.actors.len; i += 1 {
|
||||||
|
it := &map.actors.data[i];
|
||||||
|
p := GetRandomUnblockedP(&CurrentMap);
|
||||||
|
SetActorP(it, p);
|
||||||
|
it.target_p = GetRandomUnblockedP(&CurrentMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertOpenPath :: proc(actor: *Actor, p: V2I, came_from: V2I, ignore_blocks: bool = false) {
|
||||||
|
if p.x < 0 || p.x >= actor.map.x return;
|
||||||
|
if p.y < 0 || p.y >= actor.map.y return;
|
||||||
|
if ignore_blocks == false && actor.map.data[p.x + p.y * actor.map.x] != 0 return;
|
||||||
|
|
||||||
|
for i := 0; i < actor.close_paths.len; i += 1 {
|
||||||
|
it := &actor.close_paths.data[i];
|
||||||
|
if it.p.x == p.x && it.p.y == p.y return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < actor.open_paths.len; i += 1 {
|
||||||
|
it := &actor.open_paths.data[i];
|
||||||
|
if it.p.x == p.x && it.p.y == p.y return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dx := actor.target_p.x - p.x;
|
||||||
|
dy := actor.target_p.y - p.y;
|
||||||
|
d := dx*dx + dy*dy;
|
||||||
|
InsertSortedPath(&actor.open_paths, {d, p, came_from});
|
||||||
|
}
|
||||||
|
|
||||||
|
GetCloseP :: proc(actor: *Actor, p: V2I): *Path {
|
||||||
|
for i := 0; i < actor.close_paths.len; i += 1 {
|
||||||
|
it := &actor.close_paths.data[i];
|
||||||
|
if it.p.x == p.x && it.p.y == p.y return it;
|
||||||
|
}
|
||||||
|
InvalidCodepath();
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecomputeHistory :: proc(actor: *Actor) {
|
||||||
|
if actor.close_paths.len > 1 {
|
||||||
|
actor.history.len = 0;
|
||||||
|
it := GetLastPath(actor.close_paths);
|
||||||
|
AddPath(&actor.history, *it);
|
||||||
|
for i := 0;;i += 1 {
|
||||||
|
if it.p.x == actor.p.x && it.p.y == actor.p.y { break; }
|
||||||
|
if i > 512 { actor.history.len = 0; break; } // @todo: Pop after this and error?
|
||||||
|
it = GetCloseP(actor, it.came_from);
|
||||||
|
AddPath(&actor.history, *it);
|
||||||
|
}
|
||||||
|
PopPath(&actor.history);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MoveTowardsTarget :: proc(actor: *Actor) {
|
||||||
|
tile := &actor.map.data[actor.p.x + actor.p.y * actor.map.x];
|
||||||
|
if actor.history.len > 0 {
|
||||||
|
step := PopPath(&actor.history);
|
||||||
|
new_tile := &actor.map.data[step.p.x + step.p.y * actor.map.x];
|
||||||
|
if *new_tile == 0 {
|
||||||
|
AddV2I(&actor.tiles_visited, actor.p);
|
||||||
|
actor.p = step.p;
|
||||||
|
*tile &= ~TILE_ACTOR_IS_STANDING;
|
||||||
|
*new_tile |= TILE_ACTOR_IS_STANDING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PathFindUpdate :: proc(map: *Map) {
|
||||||
|
for actor_i := 0; actor_i < map.actors.len; actor_i += 1 {
|
||||||
|
actor_it := &map.actors.data[actor_i];
|
||||||
|
for i := 0; i < actor_it.history.len; i += 1 {
|
||||||
|
history_it := &actor_it.history.data[i];
|
||||||
|
|
||||||
|
tile := actor_it.map.data[history_it.p.x + history_it.p.y * actor_it.map.x];
|
||||||
|
if tile != 0 {
|
||||||
|
actor_it.close_paths.len = 0;
|
||||||
|
actor_it.open_paths.len = 0;
|
||||||
|
actor_it.history.len = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PathFind(actor_it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PathFindStep :: proc(s: *Actor, compute_history: bool = true): bool {
|
||||||
|
if s.open_paths.len == 0 {
|
||||||
|
// Reset if we didn't find solution
|
||||||
|
if s.close_paths.len != 0 {
|
||||||
|
last := GetLastPath(s.close_paths);
|
||||||
|
reached_target := last.p.x == s.target_p.x && last.p.y == s.target_p.y;
|
||||||
|
if reached_target == false {
|
||||||
|
s.close_paths.len = 0;
|
||||||
|
s.open_paths.len = 0;
|
||||||
|
s.history.len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertOpenPath(s, s.p, s.p, ignore_blocks = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.close_paths.len != 0 {
|
||||||
|
last := GetLastPath(s.close_paths);
|
||||||
|
reached_target := last.p.x == s.target_p.x && last.p.y == s.target_p.y;
|
||||||
|
if reached_target return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
it := PopPath(&s.open_paths);
|
||||||
|
AddPath(&s.close_paths, it);
|
||||||
|
|
||||||
|
for y := -1; y <= 1; y += 1 {
|
||||||
|
for x := -1; x <= 1; x += 1 {
|
||||||
|
if x == 0 && y == 0 continue;
|
||||||
|
p: V2I = {it.p.x + x, it.p.y + y};
|
||||||
|
InsertOpenPath(s, p, it.p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if compute_history RecomputeHistory(s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PathFind :: proc(actor: *Actor) {
|
||||||
|
for i := 0; i < 32; i += 1 {
|
||||||
|
done := PathFindStep(actor, false);
|
||||||
|
if done break;
|
||||||
|
}
|
||||||
|
RecomputeHistory(actor);
|
||||||
|
}
|
||||||
22
examples/pathfinding_visualizer/random.lc
Normal file
22
examples/pathfinding_visualizer/random.lc
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
RandomSeedValue: RandomSeed = {121521923492};
|
||||||
|
RandomSeed :: struct {
|
||||||
|
a: u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetRandomU64 :: proc(s: *RandomSeed): u64 {
|
||||||
|
x := s.a;
|
||||||
|
x ^= x << 13;
|
||||||
|
x ^= x >> 7;
|
||||||
|
x ^= x << 17;
|
||||||
|
s.a = x;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetRandomInt :: proc(min: int, max: int): int {
|
||||||
|
random := GetRandomU64(&RandomSeedValue);
|
||||||
|
range_size: u64 = :u64(max - min) + 1;
|
||||||
|
|
||||||
|
result := :int(random % range_size);
|
||||||
|
result = result + min;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
68
examples/sandbox/arena.lc
Normal file
68
examples/sandbox/arena.lc
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import "std_types";
|
||||||
|
import libc "libc";
|
||||||
|
|
||||||
|
PAGE_SIZE :: 4096;
|
||||||
|
|
||||||
|
Arena :: struct {
|
||||||
|
data: *u8;
|
||||||
|
len: usize;
|
||||||
|
commit: usize;
|
||||||
|
reserve: usize;
|
||||||
|
alignment: usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetAlignOffset :: proc(size: usize, align: usize): usize {
|
||||||
|
mask: usize = align - 1;
|
||||||
|
val: usize = size & mask;
|
||||||
|
if val {
|
||||||
|
val = align - val;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
AlignUp :: proc(size: usize, align: usize): usize {
|
||||||
|
result := size + GetAlignOffset(size, align);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
InitArena :: proc(arena: *Arena, reserve: usize) {
|
||||||
|
aligned_reserve := AlignUp(reserve, PAGE_SIZE);
|
||||||
|
data := VReserve(aligned_reserve);
|
||||||
|
libc.assert(data != nil);
|
||||||
|
|
||||||
|
if data {
|
||||||
|
arena.data = data;
|
||||||
|
arena.reserve = aligned_reserve;
|
||||||
|
arena.alignment = 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PushSize :: proc(arena: *Arena, size: usize): *void {
|
||||||
|
libc.assert(arena.data != nil);
|
||||||
|
|
||||||
|
pointer := :usize(arena.data) + arena.len + size;
|
||||||
|
align := GetAlignOffset(pointer, arena.alignment);
|
||||||
|
aligned_size := size + align;
|
||||||
|
|
||||||
|
a := arena.len + aligned_size;
|
||||||
|
if a > arena.commit {
|
||||||
|
diff := a - arena.commit;
|
||||||
|
commit_size := AlignUp(diff, PAGE_SIZE*4);
|
||||||
|
libc.assert(commit_size + arena.commit <= arena.reserve);
|
||||||
|
|
||||||
|
if VCommit(&arena.data[arena.commit], commit_size) {
|
||||||
|
arena.commit += commit_size;
|
||||||
|
} else {
|
||||||
|
libc.assert(false && "commit failed"[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
arena.len += align;
|
||||||
|
result: *void = &arena.data[arena.len];
|
||||||
|
arena.len += size;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestArena :: proc() {
|
||||||
|
arena: Arena;
|
||||||
|
}
|
||||||
28
examples/sandbox/arena_win32.lc
Normal file
28
examples/sandbox/arena_win32.lc
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#build_if(LC_OS == OS_WINDOWS);
|
||||||
|
|
||||||
|
DWORD :: typedef u32;
|
||||||
|
SIZE_T :: typedef uintptr;
|
||||||
|
BOOL :: typedef int;
|
||||||
|
|
||||||
|
MEM_RESERVE :: 0x00002000;
|
||||||
|
MEM_COMMIT :: 0x00001000;
|
||||||
|
PAGE_READWRITE :: 0x04;
|
||||||
|
VirtualAlloc :: proc(lpAddress: *void, dwSize: SIZE_T, flAllocationType: DWORD, flProtect: DWORD): *void; @api
|
||||||
|
|
||||||
|
MEM_RELEASE :: 0x00008000;
|
||||||
|
MEM_DECOMMIT :: 0x00004000;
|
||||||
|
VirtualFree :: proc(lpAddress: *void, dwSize: SIZE_T, dwFreeType: DWORD): BOOL; @api
|
||||||
|
|
||||||
|
VReserve :: proc(size: usize): *void {
|
||||||
|
result := VirtualAlloc(nil, :SIZE_T(size), MEM_RESERVE, PAGE_READWRITE);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
VCommit :: proc(p: *void, size: usize): bool {
|
||||||
|
result := VirtualAlloc(p, :SIZE_T(size), MEM_COMMIT, PAGE_READWRITE) != 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
VRelease :: proc(p: *void) {
|
||||||
|
VirtualFree(p, 0, MEM_RELEASE);
|
||||||
|
}
|
||||||
121
examples/sandbox/build.cpp
Normal file
121
examples/sandbox/build.cpp
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
namespace Sandbox {
|
||||||
|
Array<LC_Intern> TypesToGen;
|
||||||
|
|
||||||
|
void RegisterMarkedTypes(LC_AST *n) {
|
||||||
|
if (n->kind == LC_ASTKind_TypespecIdent) {
|
||||||
|
S8_String name = S8_MakeFromChar((char *)n->eident.name);
|
||||||
|
S8_String array_of = "ArrayOf";
|
||||||
|
if (S8_StartsWith(name, array_of)) {
|
||||||
|
if (name == "ArrayOfName") return;
|
||||||
|
|
||||||
|
name = S8_Skip(name, array_of.len);
|
||||||
|
LC_Intern intern = LC_InternStrLen(name.str, (int)name.len);
|
||||||
|
|
||||||
|
bool exists = false;
|
||||||
|
For(TypesToGen) {
|
||||||
|
if (intern == it) {
|
||||||
|
exists = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!exists) {
|
||||||
|
TypesToGen.add(intern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WalkAndRename(LC_ASTWalker *ctx, LC_AST *n) {
|
||||||
|
LC_Intern *p = NULL;
|
||||||
|
if (n->kind == LC_ASTKind_TypespecIdent || n->kind == LC_ASTKind_ExprIdent) {
|
||||||
|
p = &n->eident.name;
|
||||||
|
}
|
||||||
|
if (LC_IsDecl(n)) {
|
||||||
|
p = &n->dbase.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p) {
|
||||||
|
LC_Intern user = *(LC_Intern *)ctx->user_data;
|
||||||
|
S8_String p8 = S8_MakeFromChar((char *)*p);
|
||||||
|
|
||||||
|
if (S8_Seek(p8, "Name")) {
|
||||||
|
S8_String new_name = S8_ReplaceAll(L->arena, p8, "Name", S8_MakeFromChar((char *)user));
|
||||||
|
*p = LC_InternStrLen(new_name.str, (int)new_name.len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeforeCallArgsResolved(LC_AST *n, LC_Type *type) {
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Sandbox
|
||||||
|
|
||||||
|
bool sandbox() {
|
||||||
|
using namespace Sandbox;
|
||||||
|
|
||||||
|
LC_Lang *lang = LC_LangAlloc();
|
||||||
|
lang->use_colored_terminal_output = UseColoredIO;
|
||||||
|
lang->breakpoint_on_error = BreakpointOnError;
|
||||||
|
lang->on_typespec_parsed = RegisterMarkedTypes;
|
||||||
|
lang->before_call_args_resolved = BeforeCallArgsResolved;
|
||||||
|
LC_LangBegin(lang);
|
||||||
|
|
||||||
|
LC_RegisterPackageDir("../pkgs");
|
||||||
|
LC_RegisterPackageDir("../examples");
|
||||||
|
|
||||||
|
LC_Intern name = LC_ILit("sandbox");
|
||||||
|
LC_ParsePackagesUsingRegistry(name);
|
||||||
|
if (L->errors) {
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypesToGen.allocator = MA_GetAllocator(lang->arena);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Remove dynamic array file
|
||||||
|
//
|
||||||
|
LC_AST *package = LC_GetPackageByName(name);
|
||||||
|
LC_AST *dynamic_array_file = NULL;
|
||||||
|
LC_ASTFor(it, package->apackage.ffile) {
|
||||||
|
S8_String path = S8_MakeFromChar((char *)it->afile.x->file);
|
||||||
|
if (S8_EndsWith(path, "dynamic_array.lc")) {
|
||||||
|
dynamic_array_file = it;
|
||||||
|
DLL_QUEUE_REMOVE(package->apackage.ffile, package->apackage.lfile, it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate copies
|
||||||
|
LC_ASTWalker walker = LC_GetDefaultWalker(L->arena, WalkAndRename);
|
||||||
|
For(TypesToGen) {
|
||||||
|
LC_AST *new_array_file = LC_CopyAST(L->arena, dynamic_array_file);
|
||||||
|
|
||||||
|
walker.user_data = (void *)⁢
|
||||||
|
LC_WalkAST(&walker, new_array_file);
|
||||||
|
LC_DLLAdd(package->apackage.ffile, package->apackage.lfile, new_array_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_OrderAndResolveTopLevelDecls(name);
|
||||||
|
LC_ResolveAllProcBodies();
|
||||||
|
if (L->errors) {
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_String code = LC_GenerateUnityBuild(L->ordered_packages);
|
||||||
|
S8_String path = "examples/sandbox/sandbox.c";
|
||||||
|
|
||||||
|
OS_MakeDir("examples/sandbox");
|
||||||
|
OS_WriteFile(path, code);
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
|
||||||
|
if (UseCL) {
|
||||||
|
OS_CopyFile(RaylibDLL, "examples/sandbox/raylib.dll", true);
|
||||||
|
S8_String cmd = Fmt("cl %.*s -Zi -std:c11 -nologo -FC -Fd:examples/sandbox/a.pdb -Fe:examples/sandbox/sandbox.exe %.*s", S8_Expand(path), S8_Expand(RaylibLIB));
|
||||||
|
int errcode = Run(cmd);
|
||||||
|
if (errcode != 0) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
96
examples/sandbox/common.lc
Normal file
96
examples/sandbox/common.lc
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
FloatMin :: proc(a: float, b: float): float {
|
||||||
|
if (a > b) return b;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatMax :: proc(a: float, b: float): float {
|
||||||
|
if (a > b) return a;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatClamp :: proc(val: float, min: float, max: float): float {
|
||||||
|
if (val > max) return max;
|
||||||
|
if (val < min) return min;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntMin :: proc(a: int, b: int): int {
|
||||||
|
if (a > b) return b;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntMax :: proc(a: int, b: int): int {
|
||||||
|
if (a > b) return a;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntClamp :: proc(val: int, min: int, max: int): int {
|
||||||
|
if (val > max) return max;
|
||||||
|
if (val < min) return min;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect2 :: struct {
|
||||||
|
min: Vector2;
|
||||||
|
max: Vector2;
|
||||||
|
}
|
||||||
|
|
||||||
|
CutLeft :: proc(r: *Rect2, value: float): Rect2 {
|
||||||
|
minx := r.min.x;
|
||||||
|
r.min.x = FloatMin(r.max.x, r.min.x + value);
|
||||||
|
return :Rect2{
|
||||||
|
{ minx, r.min.y},
|
||||||
|
{r.min.x, r.max.y},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
CutRight :: proc(r: *Rect2, value: float): Rect2 {
|
||||||
|
maxx := r.max.x;
|
||||||
|
r.max.x = FloatMax(r.max.x - value, r.min.x);
|
||||||
|
return :Rect2{
|
||||||
|
{r.max.x, r.min.y},
|
||||||
|
{ maxx, r.max.y},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
CutBottom :: proc(r: *Rect2, value: float): Rect2 { // Y is down
|
||||||
|
maxy := r.max.y;
|
||||||
|
r.max.y = FloatMax(r.min.y, r.max.y - value);
|
||||||
|
return :Rect2{
|
||||||
|
{r.min.x, r.max.y},
|
||||||
|
{r.max.x, maxy},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
CutTop :: proc(r: *Rect2, value: float): Rect2 { // Y is down
|
||||||
|
miny := r.min.y;
|
||||||
|
r.min.y = FloatMin(r.min.y + value, r.max.y);
|
||||||
|
return :Rect2{
|
||||||
|
{r.min.x, miny},
|
||||||
|
{r.max.x, r.min.y},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Shrink :: proc(r: Rect2, value: float): Rect2 {
|
||||||
|
r.min.x += value;
|
||||||
|
r.max.x -= value;
|
||||||
|
r.min.y += value;
|
||||||
|
r.max.y -= value;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
RectFromSize :: proc(pos: Vector2, size: Vector2): Rect2 {
|
||||||
|
result: Rect2 = {pos, Vector2Add(pos, size)};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetRectSize :: proc(rect: Rect2): Vector2 {
|
||||||
|
result: Vector2 = {rect.max.x - rect.min.x, rect.max.y - rect.min.y};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
RectToRectangle :: proc(rect: Rect2): Rectangle {
|
||||||
|
size := GetRectSize(rect);
|
||||||
|
result: Rectangle = {rect.min.x, rect.min.y, size.x, size.y};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
49
examples/sandbox/dynamic_array.lc
Normal file
49
examples/sandbox/dynamic_array.lc
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
ArrayOfName :: struct {
|
||||||
|
data: *Name;
|
||||||
|
len: int;
|
||||||
|
cap: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
TryGrowingNameArray :: proc(arr: *ArrayOfName) {
|
||||||
|
if (arr.len + 1 > arr.cap) {
|
||||||
|
cap := arr.cap * 2;
|
||||||
|
if (cap == 0) cap = 16;
|
||||||
|
|
||||||
|
arr.data = libc.realloc(arr.data, sizeof(arr.data[0]) * :libc.size_t(cap));
|
||||||
|
arr.cap = cap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddName :: proc(arr: *ArrayOfName, item: Name) {
|
||||||
|
TryGrowingNameArray(arr);
|
||||||
|
arr.data[arr.len] = item;
|
||||||
|
arr.len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertName :: proc(a: *ArrayOfName, item: Name, index: int) {
|
||||||
|
if index == a.len {
|
||||||
|
AddName(a, item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
libc.assert(index < a.len);
|
||||||
|
libc.assert(index >= 0);
|
||||||
|
|
||||||
|
TryGrowingNameArray(a);
|
||||||
|
right_len := :libc.size_t(a.len - index);
|
||||||
|
libc.memmove(&a.data[index + 1], &a.data[index], sizeof(a.data[0]) * right_len);
|
||||||
|
a.data[index] = item;
|
||||||
|
a.len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetLastName :: proc(a: ArrayOfName): *Name {
|
||||||
|
libc.assert(a.len > 0);
|
||||||
|
result := &a.data[a.len - 1];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
PopName :: proc(a: *ArrayOfName): Name {
|
||||||
|
a.len -= 1;
|
||||||
|
result := a.data[a.len];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
93
examples/sandbox/main.lc
Normal file
93
examples/sandbox/main.lc
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import "raylib";
|
||||||
|
|
||||||
|
Tile :: struct {
|
||||||
|
value: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map :: struct {
|
||||||
|
tiles: *Tile;
|
||||||
|
x: int;
|
||||||
|
y: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateMap :: proc(x: int, y: int, map: *int): Map {
|
||||||
|
result: Map = {x = x, y = y};
|
||||||
|
result.tiles = MemAlloc(:uint(x*y) * sizeof(:Tile));
|
||||||
|
|
||||||
|
for i := 0; i < x*y; i += 1 {
|
||||||
|
result.tiles[i].value = map[i];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc(): int {
|
||||||
|
TestArena();
|
||||||
|
return 0;
|
||||||
|
InitWindow(800, 600, "Thing");
|
||||||
|
SetTargetFPS(60);
|
||||||
|
|
||||||
|
XPIX :: 32;
|
||||||
|
YPIX :: 32;
|
||||||
|
|
||||||
|
camera: Camera2D = {
|
||||||
|
zoom = 1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
_map_x :: 8;
|
||||||
|
_map_y :: 8;
|
||||||
|
map := CreateMap(_map_x, _map_y, &:[_map_x * _map_y]int{
|
||||||
|
1,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
1,0,0,0,0,0,0,1,
|
||||||
|
0,0,0,0,1,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,
|
||||||
|
}[0]);
|
||||||
|
|
||||||
|
for !WindowShouldClose() {
|
||||||
|
if IsMouseButtonDown(MOUSE_BUTTON_LEFT) {
|
||||||
|
delta := GetMouseDelta();
|
||||||
|
camera.offset = Vector2Add(camera.offset, delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
BeginDrawing();
|
||||||
|
ClearBackground(RAYWHITE);
|
||||||
|
|
||||||
|
BeginMode2D(camera);
|
||||||
|
for y_it := 0; y_it < map.y; y_it += 1 {
|
||||||
|
for x_it := 0; x_it < map.x; x_it += 1 {
|
||||||
|
tile := &map.tiles[x_it + y_it * map.x];
|
||||||
|
original_rect := RectFromSize({XPIX * :float(x_it), YPIX * :float(y_it)}, {XPIX, YPIX});
|
||||||
|
rect := Shrink(original_rect, 2);
|
||||||
|
|
||||||
|
r := RectToRectangle(rect);
|
||||||
|
|
||||||
|
min_screen := GetWorldToScreen2D(original_rect.min, camera);
|
||||||
|
size := GetRectSize(original_rect);
|
||||||
|
|
||||||
|
screen_rect: Rectangle = {min_screen.x, min_screen.y, size.x, size.y};
|
||||||
|
mouse_p := GetMousePosition();
|
||||||
|
|
||||||
|
if tile.value == 1 {
|
||||||
|
DrawRectangleRec(r, RED);
|
||||||
|
} else {
|
||||||
|
DrawRectangleRec(r, GREEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
if CheckCollisionPointRec(mouse_p, screen_rect) {
|
||||||
|
DrawRectangleRec(r, BLUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EndMode2D();
|
||||||
|
|
||||||
|
// DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY);
|
||||||
|
EndDrawing();
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseWindow();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
249
examples/text_editor/buffer.lc
Normal file
249
examples/text_editor/buffer.lc
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
Buffer :: struct {
|
||||||
|
data: *u8;
|
||||||
|
len: int;
|
||||||
|
cap: int;
|
||||||
|
|
||||||
|
lines: Lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
Lines :: struct {
|
||||||
|
data: *Range;
|
||||||
|
len: int;
|
||||||
|
cap: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
Range :: struct {
|
||||||
|
min: int;
|
||||||
|
max: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
LineInfo :: struct {
|
||||||
|
number: int;
|
||||||
|
range: Range;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetRangeSize :: proc(range: Range): int {
|
||||||
|
result := range.max - range.min;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClampInt :: proc(val: int, min: int, max: int): int {
|
||||||
|
result := val;
|
||||||
|
if (val < min) result = min;
|
||||||
|
if (val > max) result = max;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MinInt :: proc(a: int, b: int): int {
|
||||||
|
result := a;
|
||||||
|
if (a > b) result = b;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaxInt :: proc(a: int, b: int): int {
|
||||||
|
result := b;
|
||||||
|
if (a > b) result = a;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
AdjustUTF8PosUnsafe :: proc(buffer: *Buffer, pos: int, direction: int = 1): int {
|
||||||
|
for pos >= 0 && pos < buffer.len && IsUTF8ContinuationByte(buffer.data[pos]) {
|
||||||
|
pos += direction;
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
AdjustUTF8Pos :: proc(buffer: *Buffer, pos: int, direction: int = 1): int {
|
||||||
|
assert(direction == 1 || direction == -1);
|
||||||
|
pos = AdjustUTF8PosUnsafe(buffer, pos, direction);
|
||||||
|
pos = ClampInt(pos, 0, buffer.len - 1);
|
||||||
|
if (buffer.data) assert(!IsUTF8ContinuationByte(buffer.data[pos]));
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
AdjustRange :: proc(buffer: *Buffer, range: *Range) {
|
||||||
|
range.min = AdjustUTF8Pos(buffer, range.min, direction = -1);
|
||||||
|
range.max = AdjustUTF8Pos(buffer, range.max, direction = +1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReplaceText :: proc(buffer: *Buffer, replace: Range, with: String) {
|
||||||
|
AdjustRange(buffer, &replace);
|
||||||
|
|
||||||
|
new_buffer_len := buffer.len + :int(with.len) - GetRangeSize(replace);
|
||||||
|
new_buffer_size := new_buffer_len + 1; // possible addition of a null terminator
|
||||||
|
if new_buffer_size > buffer.cap {
|
||||||
|
new_buffer_cap := MaxInt(4096, new_buffer_size * 2);
|
||||||
|
new_buffer := malloc(:usize(new_buffer_cap));
|
||||||
|
if (buffer.data) {
|
||||||
|
memcpy(new_buffer, buffer.data, :usize(buffer.len));
|
||||||
|
free(buffer.data);
|
||||||
|
}
|
||||||
|
buffer.data = new_buffer;
|
||||||
|
buffer.cap = new_buffer_cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
old_text_range: Range = replace;
|
||||||
|
new_text_range: Range = {old_text_range.min, old_text_range.min + :int(with.len)};
|
||||||
|
right_range: Range = {old_text_range.max, buffer.len};
|
||||||
|
|
||||||
|
memmove(&buffer.data[new_text_range.max], &buffer.data[right_range.min], :usize(GetRangeSize(right_range)));
|
||||||
|
memmove(&buffer.data[new_text_range.min], with.str, :usize(GetRangeSize(new_text_range)));
|
||||||
|
|
||||||
|
buffer.len = new_buffer_len;
|
||||||
|
if buffer.len && buffer.data[buffer.len - 1] != 0 {
|
||||||
|
buffer.data[buffer.len] = 0;
|
||||||
|
buffer.len += 1;
|
||||||
|
assert(buffer.len <= buffer.cap);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateBufferLines(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
AddLine :: proc(lines: *Lines, line: Range) {
|
||||||
|
if lines.len + 1 > lines.cap {
|
||||||
|
new_cap := MaxInt(16, lines.cap * 2);
|
||||||
|
lines.data = realloc(lines.data, :usize(new_cap) * sizeof(:Range));
|
||||||
|
lines.cap = new_cap;
|
||||||
|
}
|
||||||
|
lines.data[lines.len] = line;
|
||||||
|
lines.len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateBufferLines :: proc(buffer: *Buffer) {
|
||||||
|
buffer.lines.len = 0;
|
||||||
|
|
||||||
|
line: Range = {0, 0};
|
||||||
|
for i := 0; i < buffer.len; i += 1 {
|
||||||
|
if buffer.data[i] == '\n' {
|
||||||
|
AddLine(&buffer.lines, line);
|
||||||
|
line.min = i + 1;
|
||||||
|
line.max = i + 1;
|
||||||
|
} else {
|
||||||
|
line.max += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
line.min = AdjustUTF8Pos(buffer, line.min);
|
||||||
|
line.max = AdjustUTF8Pos(buffer, line.max);
|
||||||
|
AddLine(&buffer.lines, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
AllocStringFromBuffer :: proc(buffer: *Buffer, range: Range, null_terminate: bool = false): String {
|
||||||
|
AdjustRange(buffer, &range);
|
||||||
|
size := GetRangeSize(range);
|
||||||
|
|
||||||
|
alloc_size := :usize(size);
|
||||||
|
if (null_terminate) alloc_size += 1;
|
||||||
|
data := malloc(alloc_size);
|
||||||
|
memcpy(data, &buffer.data[range.min], :usize(size));
|
||||||
|
|
||||||
|
result: String = {data, size};
|
||||||
|
if (null_terminate) result.str[result.len] = 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetStringFromBuffer :: proc(buffer: *Buffer, range: Range): String {
|
||||||
|
AdjustRange(buffer, &range);
|
||||||
|
size := GetRangeSize(range);
|
||||||
|
result: String = {:*char(&buffer.data[range.min]), size};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddText :: proc(buffer: *Buffer, text: String) {
|
||||||
|
end_of_buffer: Range = {buffer.len - 1, buffer.len - 1};
|
||||||
|
ReplaceText(buffer, end_of_buffer, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
FindLineOfPos :: proc(buffer: *Buffer, pos: int): LineInfo {
|
||||||
|
for i := 0; i < buffer.lines.len; i += 1 {
|
||||||
|
line := buffer.lines.data[i];
|
||||||
|
if pos >= line.min && pos <= line.max {
|
||||||
|
return {i, line};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
IsLineValid :: proc(buffer: *Buffer, line: int): bool {
|
||||||
|
result := line >= 0 && line < buffer.lines.len;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetLine :: proc(buffer: *Buffer, line: int): LineInfo {
|
||||||
|
line = ClampInt(line, 0, buffer.lines.len - 1);
|
||||||
|
range := buffer.lines.data[line];
|
||||||
|
return {line, range};
|
||||||
|
}
|
||||||
|
|
||||||
|
IsPosInBounds :: proc(buffer: *Buffer, pos: int): bool {
|
||||||
|
result := buffer.len != 0 && pos >= 0 && pos < buffer.len;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetChar :: proc(buffer: *Buffer, pos: int): u8 {
|
||||||
|
result: u8;
|
||||||
|
if IsPosInBounds(buffer, pos) {
|
||||||
|
result = buffer.data[pos];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetUTF32 :: proc(buffer: *Buffer, pos: int, codepoint_size: *int = nil): u32 {
|
||||||
|
if !IsPosInBounds(buffer, pos) return 0;
|
||||||
|
p := &buffer.data[pos];
|
||||||
|
max := buffer.len - pos;
|
||||||
|
utf32 := UTF8ToUTF32(:*uchar(p), max);
|
||||||
|
assert(utf32.error == 0);
|
||||||
|
if (utf32.error != 0) return 0;
|
||||||
|
if (codepoint_size) codepoint_size[0] = utf32.advance;
|
||||||
|
return utf32.out_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
IsWhitespace :: proc(w: u8): bool {
|
||||||
|
result := w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r';
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
SeekOnWordBoundary :: proc(buffer: *Buffer, pos: int, direction: int = GO_FORWARD): int {
|
||||||
|
assert(direction == GO_FORWARD || direction == GO_BACKWARD);
|
||||||
|
check_pos: int;
|
||||||
|
if direction == GO_FORWARD {
|
||||||
|
check_pos = AdjustUTF8Pos(buffer, pos + 1, direction);
|
||||||
|
pos = check_pos;
|
||||||
|
// this difference here because the Backward for loop is not inclusive.
|
||||||
|
// It doesn't move an inch forward after "pos"
|
||||||
|
// - - - - pos - -
|
||||||
|
// It goes backward on first Advance
|
||||||
|
} else {
|
||||||
|
check_pos = AdjustUTF8Pos(buffer, pos - 1, direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor_char := GetChar(buffer, check_pos);
|
||||||
|
standing_on_whitespace := IsWhitespace(cursor_char);
|
||||||
|
seek_whitespace := standing_on_whitespace == false;
|
||||||
|
seek_word := standing_on_whitespace;
|
||||||
|
|
||||||
|
end := buffer.len - 1;
|
||||||
|
if (direction == GO_BACKWARD) end = 0;
|
||||||
|
|
||||||
|
result := end;
|
||||||
|
iter := Iterate(buffer, pos, end, direction);
|
||||||
|
prev_pos := iter.pos;
|
||||||
|
for IsValid(iter); Advance(&iter) {
|
||||||
|
if seek_word && !IsWhitespace(:u8(iter.item)) {
|
||||||
|
result = prev_pos;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if seek_whitespace && IsWhitespace(:u8(iter.item)) {
|
||||||
|
if direction == GO_FORWARD {
|
||||||
|
result = iter.pos;
|
||||||
|
} else {
|
||||||
|
result = prev_pos;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev_pos = iter.pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
54
examples/text_editor/buffer_iter.lc
Normal file
54
examples/text_editor/buffer_iter.lc
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
GO_FORWARD :: 1;
|
||||||
|
GO_BACKWARD :: -1;
|
||||||
|
|
||||||
|
BufferIter :: struct {
|
||||||
|
buffer: *Buffer;
|
||||||
|
pos: int;
|
||||||
|
end: int;
|
||||||
|
item: u32;
|
||||||
|
|
||||||
|
utf8_codepoint_size: int;
|
||||||
|
direction: int;
|
||||||
|
codepoint_index: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
IsValid :: proc(iter: BufferIter): bool {
|
||||||
|
assert(iter.direction == GO_FORWARD || iter.direction == GO_BACKWARD);
|
||||||
|
|
||||||
|
result := false;
|
||||||
|
if iter.direction == GO_BACKWARD {
|
||||||
|
result = iter.pos >= iter.end;
|
||||||
|
} else {
|
||||||
|
result = iter.pos < iter.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if result {
|
||||||
|
assert(!IsUTF8ContinuationByte(GetChar(iter.buffer, iter.pos)));
|
||||||
|
assert(IsPosInBounds(iter.buffer, iter.pos));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Advance :: proc(iter: *BufferIter) {
|
||||||
|
assert(iter.direction == GO_FORWARD || iter.direction == GO_BACKWARD);
|
||||||
|
|
||||||
|
iter.codepoint_index += 1;
|
||||||
|
if iter.direction == GO_FORWARD {
|
||||||
|
iter.pos += iter.utf8_codepoint_size;
|
||||||
|
} else {
|
||||||
|
iter.pos = AdjustUTF8PosUnsafe(iter.buffer, iter.pos - 1, GO_BACKWARD);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !IsValid(*iter) return;
|
||||||
|
iter.item = GetUTF32(iter.buffer, iter.pos, &iter.utf8_codepoint_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterate :: proc(buffer: *Buffer, pos: int, end: int, direction: int = GO_FORWARD): BufferIter {
|
||||||
|
assert(!IsUTF8ContinuationByte(GetChar(buffer, pos)));
|
||||||
|
assert(!IsUTF8ContinuationByte(GetChar(buffer, end)));
|
||||||
|
assert(direction == GO_FORWARD || direction == GO_BACKWARD);
|
||||||
|
|
||||||
|
result: BufferIter = {buffer = buffer, pos = pos, end = end, direction = direction, codepoint_index = -1};
|
||||||
|
Advance(&result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
34
examples/text_editor/build.cpp
Normal file
34
examples/text_editor/build.cpp
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
bool text_editor() {
|
||||||
|
LC_Lang *lang = LC_LangAlloc();
|
||||||
|
lang->use_colored_terminal_output = UseColoredIO;
|
||||||
|
lang->breakpoint_on_error = BreakpointOnError;
|
||||||
|
LC_LangBegin(lang);
|
||||||
|
defer { LC_LangEnd(lang); };
|
||||||
|
|
||||||
|
LC_RegisterPackageDir("../pkgs");
|
||||||
|
LC_RegisterPackageDir("../examples");
|
||||||
|
|
||||||
|
LC_Intern name = LC_ILit("text_editor");
|
||||||
|
LC_ASTRefList packages = LC_ResolvePackageByName(name);
|
||||||
|
if (L->errors) return false;
|
||||||
|
|
||||||
|
DebugVerifyAST(packages);
|
||||||
|
if (L->errors) return false;
|
||||||
|
|
||||||
|
LC_FindUnusedLocalsAndRemoveUnusedGlobalDecls();
|
||||||
|
|
||||||
|
OS_MakeDir("examples");
|
||||||
|
OS_MakeDir("examples/text_editor");
|
||||||
|
OS_CopyFile(RaylibDLL, "examples/text_editor/raylib.dll", true);
|
||||||
|
|
||||||
|
S8_String code = LC_GenerateUnityBuild(packages);
|
||||||
|
S8_String path = "examples/text_editor/text_editor.c";
|
||||||
|
OS_WriteFile(path, code);
|
||||||
|
|
||||||
|
if (!UseCL) return true;
|
||||||
|
S8_String cmd = Fmt("cl %.*s -Zi -std:c11 -nologo -FC -Fd:examples/text_editor/a.pdb -Fe:examples/text_editor/text_editor.exe %.*s", S8_Expand(path), S8_Expand(RaylibLIB));
|
||||||
|
int errcode = Run(cmd);
|
||||||
|
if (errcode != 0) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
362
examples/text_editor/main.lc
Normal file
362
examples/text_editor/main.lc
Normal file
@@ -0,0 +1,362 @@
|
|||||||
|
import "raylib";
|
||||||
|
import "std_types";
|
||||||
|
import "libc";
|
||||||
|
|
||||||
|
MainCursor: Selection;
|
||||||
|
|
||||||
|
Selection :: struct {
|
||||||
|
a: int;
|
||||||
|
b: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetRange :: proc(s: Selection): Range {
|
||||||
|
result: Range = {MinInt(s.a, s.b), MaxInt(s.a, s.b)};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc(): int {
|
||||||
|
InitWindow(800, 600, "TextEditor");
|
||||||
|
SetTargetFPS(60);
|
||||||
|
|
||||||
|
font_size: float = 35;
|
||||||
|
font_spacing: float = 1;
|
||||||
|
font: Font = LoadFontEx("C:/Windows/Fonts/consola.ttf", :int(font_size), nil, 0);
|
||||||
|
|
||||||
|
glyph_info: GlyphInfo = GetGlyphInfo(font, 'A');
|
||||||
|
size := MeasureTextEx(font, "A", font_size, font_spacing);
|
||||||
|
Monosize = {:float(glyph_info.image.width), size.y};
|
||||||
|
|
||||||
|
buffer: Buffer;
|
||||||
|
AddText(&buffer, "1Testing and stuff 1Testing and stuff 1Testing and stuff 1Testing and stuff\n");
|
||||||
|
AddText(&buffer, "2Testing and stuff\n");
|
||||||
|
AddText(&buffer, "3Testing and stuff\n");
|
||||||
|
AddText(&buffer, "4Testing and stuff\n");
|
||||||
|
AddText(&buffer, "5Testing and stuff\n");
|
||||||
|
AddText(&buffer, "6Testing and stuff\n");
|
||||||
|
AddText(&buffer, "7Testing and stuff\n");
|
||||||
|
AddText(&buffer, "8Testing and stuff\n");
|
||||||
|
AddText(&buffer, "9Testing and stuff\n");
|
||||||
|
AddText(&buffer, "1Testing and stuff\n");
|
||||||
|
AddText(&buffer, "2Testing and stuff\n");
|
||||||
|
AddText(&buffer, "3Testing and stuff\n");
|
||||||
|
AddText(&buffer, "4Testing and stuff\n");
|
||||||
|
AddText(&buffer, "5Testing and stuff\n");
|
||||||
|
AddText(&buffer, "6Testing and stuff\n");
|
||||||
|
AddText(&buffer, "7Testing and stuff\n");
|
||||||
|
AddText(&buffer, "8Testing and stuff\n");
|
||||||
|
AddText(&buffer, "9Testing and stuff\n");
|
||||||
|
AddText(&buffer, "1Testing and stuff\n");
|
||||||
|
AddText(&buffer, "22esting and stuff\n");
|
||||||
|
AddText(&buffer, "3Testing and stuff\n");
|
||||||
|
AddText(&buffer, "4Testing and stuff\n");
|
||||||
|
AddText(&buffer, "5Testing and stuff\n");
|
||||||
|
AddText(&buffer, "6Testing and stuff\n");
|
||||||
|
AddText(&buffer, "7Testing and stuff\n");
|
||||||
|
AddText(&buffer, "8Testing and stuff\n");
|
||||||
|
AddText(&buffer, "9Testing and stuff\n");
|
||||||
|
AddText(&buffer, "1Testing and stuff\n");
|
||||||
|
AddText(&buffer, "2Testing and stuff\n");
|
||||||
|
AddText(&buffer, "3Testing and stuff\n");
|
||||||
|
AddText(&buffer, "4Testing and stuff\n");
|
||||||
|
|
||||||
|
for !WindowShouldClose() {
|
||||||
|
ScreenSize = {:f32(GetScreenWidth()), :f32(GetScreenHeight())};
|
||||||
|
initial_cursor: Selection = MainCursor;
|
||||||
|
|
||||||
|
if IsKeyPressed(KEY_LEFT) || IsKeyPressedRepeat(KEY_LEFT) {
|
||||||
|
if IsKeyDown(KEY_LEFT_SHIFT) {
|
||||||
|
if IsKeyDown(KEY_LEFT_CONTROL) {
|
||||||
|
MainCursor.b = SeekOnWordBoundary(&buffer, MainCursor.b, GO_BACKWARD);
|
||||||
|
} else {
|
||||||
|
MainCursor.b = MoveLeft(&buffer, MainCursor.b);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if IsKeyDown(KEY_LEFT_CONTROL) {
|
||||||
|
MainCursor.a = SeekOnWordBoundary(&buffer, MainCursor.a, GO_BACKWARD);
|
||||||
|
MainCursor.b = MainCursor.a;
|
||||||
|
} else {
|
||||||
|
MainCursor.a = MoveLeft(&buffer, MainCursor.a);
|
||||||
|
MainCursor.b = MainCursor.a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if IsKeyPressed(KEY_RIGHT) || IsKeyPressedRepeat(KEY_RIGHT) {
|
||||||
|
if IsKeyDown(KEY_LEFT_SHIFT) {
|
||||||
|
if IsKeyDown(KEY_LEFT_CONTROL) {
|
||||||
|
MainCursor.b = SeekOnWordBoundary(&buffer, MainCursor.b, GO_FORWARD);
|
||||||
|
} else {
|
||||||
|
MainCursor.b = MoveRight(&buffer, MainCursor.b);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if IsKeyDown(KEY_LEFT_CONTROL) {
|
||||||
|
MainCursor.a = SeekOnWordBoundary(&buffer, MainCursor.a, GO_FORWARD);
|
||||||
|
MainCursor.b = MainCursor.a;
|
||||||
|
} else {
|
||||||
|
MainCursor.a = MoveRight(&buffer, MainCursor.a);
|
||||||
|
MainCursor.b = MainCursor.a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsKeyPressed(KEY_DOWN) || IsKeyPressedRepeat(KEY_DOWN)) {
|
||||||
|
if IsKeyDown(KEY_LEFT_SHIFT) {
|
||||||
|
MainCursor.b = MoveDown(&buffer, MainCursor.b);
|
||||||
|
} else {
|
||||||
|
range := GetRange(MainCursor);
|
||||||
|
if GetRangeSize(range) > 0 {
|
||||||
|
MainCursor.b = range.max;
|
||||||
|
MainCursor.a = range.max;
|
||||||
|
}
|
||||||
|
MainCursor.a = MoveDown(&buffer, MainCursor.a);
|
||||||
|
MainCursor.b = MainCursor.a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsKeyPressed(KEY_UP) || IsKeyPressedRepeat(KEY_UP)) {
|
||||||
|
if IsKeyDown(KEY_LEFT_SHIFT) {
|
||||||
|
MainCursor.b = MoveUp(&buffer, MainCursor.b);
|
||||||
|
} else {
|
||||||
|
range := GetRange(MainCursor);
|
||||||
|
if GetRangeSize(range) > 0 {
|
||||||
|
MainCursor.b = range.min;
|
||||||
|
MainCursor.a = range.min;
|
||||||
|
}
|
||||||
|
MainCursor.a = MoveUp(&buffer, MainCursor.a);
|
||||||
|
MainCursor.b = MainCursor.a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if IsKeyPressed(KEY_BACKSPACE) || IsKeyPressedRepeat(KEY_BACKSPACE) {
|
||||||
|
range := GetRange(MainCursor);
|
||||||
|
range_size := GetRangeSize(range);
|
||||||
|
|
||||||
|
if range_size == 0 {
|
||||||
|
if IsKeyDown(KEY_LEFT_CONTROL) {
|
||||||
|
left := SeekOnWordBoundary(&buffer, MainCursor.a, GO_BACKWARD);
|
||||||
|
ReplaceText(&buffer, {left, MainCursor.a}, "");
|
||||||
|
MainCursor.a = left;
|
||||||
|
MainCursor.b = MainCursor.a;
|
||||||
|
} else {
|
||||||
|
left := MoveLeft(&buffer, MainCursor.a);
|
||||||
|
ReplaceText(&buffer, {left, MainCursor.a}, "");
|
||||||
|
MainCursor.a = left;
|
||||||
|
MainCursor.b = MainCursor.a;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ReplaceText(&buffer, range, "");
|
||||||
|
MainCursor.b = range.min;
|
||||||
|
MainCursor.a = MainCursor.b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if IsKeyPressed(KEY_DELETE) || IsKeyPressedRepeat(KEY_DELETE) {
|
||||||
|
range := GetRange(MainCursor);
|
||||||
|
range_size := GetRangeSize(range);
|
||||||
|
|
||||||
|
if range_size == 0 {
|
||||||
|
if IsKeyDown(KEY_LEFT_CONTROL) {
|
||||||
|
right := SeekOnWordBoundary(&buffer, MainCursor.a);
|
||||||
|
ReplaceText(&buffer, {MainCursor.a, right}, "");
|
||||||
|
} else {
|
||||||
|
right := MoveRight(&buffer, MainCursor.a);
|
||||||
|
ReplaceText(&buffer, {MainCursor.a, right}, "");
|
||||||
|
}
|
||||||
|
MainCursor.b = MainCursor.a;
|
||||||
|
} else {
|
||||||
|
ReplaceText(&buffer, range, "");
|
||||||
|
MainCursor.b = range.min;
|
||||||
|
MainCursor.a = MainCursor.b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if IsKeyPressed(KEY_ENTER) || IsKeyPressedRepeat(KEY_ENTER) {
|
||||||
|
ReplaceText(&buffer, GetRange(MainCursor), "\n");
|
||||||
|
MainCursor.a = MoveRight(&buffer, MainCursor.a);
|
||||||
|
MainCursor.b = MainCursor.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
if IsKeyPressed(KEY_HOME) {
|
||||||
|
line := FindLineOfPos(&buffer, MainCursor.b);
|
||||||
|
if IsKeyDown(KEY_LEFT_SHIFT) {
|
||||||
|
MainCursor.b = line.range.min;
|
||||||
|
} else {
|
||||||
|
MainCursor.a = line.range.min;
|
||||||
|
MainCursor.b = line.range.min;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if IsKeyPressed(KEY_END) {
|
||||||
|
line := FindLineOfPos(&buffer, MainCursor.b);
|
||||||
|
if IsKeyDown(KEY_LEFT_SHIFT) {
|
||||||
|
MainCursor.b = line.range.max;
|
||||||
|
} else {
|
||||||
|
MainCursor.a = line.range.max;
|
||||||
|
MainCursor.b = line.range.max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if IsKeyPressed(KEY_PAGE_DOWN) || IsKeyPressedRepeat(KEY_PAGE_DOWN) {
|
||||||
|
vpos := CalculateVisualPos(&buffer, MainCursor.b);
|
||||||
|
move_by := :int(roundf(ScreenSize.y / Monosize.y));
|
||||||
|
vpos.y += move_by;
|
||||||
|
MainCursor.b = CalculatePosFromVisualPos(&buffer, vpos);
|
||||||
|
if !IsKeyDown(KEY_LEFT_SHIFT) {
|
||||||
|
MainCursor.a = MainCursor.b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if IsKeyPressed(KEY_PAGE_UP) || IsKeyPressedRepeat(KEY_PAGE_UP) {
|
||||||
|
vpos := CalculateVisualPos(&buffer, MainCursor.b);
|
||||||
|
move_by := :int(roundf(ScreenSize.y / Monosize.y));
|
||||||
|
vpos.y -= move_by;
|
||||||
|
MainCursor.b = CalculatePosFromVisualPos(&buffer, vpos);
|
||||||
|
if !IsKeyDown(KEY_LEFT_SHIFT) {
|
||||||
|
MainCursor.a = MainCursor.b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mouse_p := GetMousePosition();
|
||||||
|
if IsMouseButtonDown(MOUSE_BUTTON_LEFT) {
|
||||||
|
p := Vector2Add(mouse_p, Scroll);
|
||||||
|
p = Vector2Divide(p, Monosize);
|
||||||
|
x := :int(floorf(p.x));
|
||||||
|
y := :int(floorf(p.y));
|
||||||
|
MainCursor.a = CalculatePosFromVisualPos(&buffer, {x, y});
|
||||||
|
MainCursor.b = MainCursor.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
if CheckCollisionPointRec(mouse_p, {0, 0, ScreenSize.x, ScreenSize.y}) {
|
||||||
|
SetMouseCursor(MOUSE_CURSOR_IBEAM);
|
||||||
|
} else {
|
||||||
|
SetMouseCursor(MOUSE_CURSOR_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
for key := GetCharPressed(); key; key = GetCharPressed() {
|
||||||
|
selection_range := GetRange(MainCursor);
|
||||||
|
|
||||||
|
result: UTF8_Result = UTF32ToUTF8(:u32(key));
|
||||||
|
if result.error == 0 {
|
||||||
|
ReplaceText(&buffer, selection_range, {:*char(&result.out_str[0]), result.len});
|
||||||
|
} else {
|
||||||
|
ReplaceText(&buffer, selection_range, "?");
|
||||||
|
}
|
||||||
|
|
||||||
|
range_size := GetRangeSize(selection_range);
|
||||||
|
if range_size != 0 {
|
||||||
|
MainCursor.a = selection_range.min;
|
||||||
|
MainCursor.b = selection_range.min;
|
||||||
|
}
|
||||||
|
MainCursor.a = MoveRight(&buffer, MainCursor.a);
|
||||||
|
MainCursor.b = MainCursor.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Scrolling
|
||||||
|
//
|
||||||
|
mouse_wheel := GetMouseWheelMove() * 16;
|
||||||
|
Scroll.y -= mouse_wheel;
|
||||||
|
|
||||||
|
if initial_cursor.b != MainCursor.b {
|
||||||
|
cursor_vpos := CalculateVisualPos(&buffer, MainCursor.b);
|
||||||
|
|
||||||
|
world_pos := CalculateWorldPosUnscrolled(cursor_vpos);
|
||||||
|
world_pos_cursor_end := Vector2Add(world_pos, Monosize);
|
||||||
|
|
||||||
|
scrolled_begin := Scroll;
|
||||||
|
scrolled_end := Vector2Add(Scroll, ScreenSize);
|
||||||
|
|
||||||
|
if world_pos_cursor_end.x > scrolled_end.x {
|
||||||
|
Scroll.x += world_pos_cursor_end.x - scrolled_end.x;
|
||||||
|
}
|
||||||
|
if world_pos.x < scrolled_begin.x {
|
||||||
|
Scroll.x -= scrolled_begin.x - world_pos.x;
|
||||||
|
}
|
||||||
|
if world_pos_cursor_end.y > scrolled_end.y {
|
||||||
|
Scroll.y += world_pos_cursor_end.y - scrolled_end.y;
|
||||||
|
}
|
||||||
|
if world_pos.y < scrolled_begin.y {
|
||||||
|
Scroll.y -= scrolled_begin.y - world_pos.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Scroll.x < 0) Scroll.x = 0;
|
||||||
|
if (Scroll.y < 0) Scroll.y = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
BeginDrawing();
|
||||||
|
ClearBackground(RAYWHITE);
|
||||||
|
|
||||||
|
_miny := Scroll.y / Monosize.y;
|
||||||
|
_maxy := (Scroll.y + ScreenSize.y) / Monosize.y;
|
||||||
|
|
||||||
|
_minx := Scroll.x / Monosize.x;
|
||||||
|
_maxx := (Scroll.x + ScreenSize.x) / Monosize.x;
|
||||||
|
|
||||||
|
miny := :int(floorf(_miny));
|
||||||
|
minx := :int(floorf(_minx));
|
||||||
|
|
||||||
|
maxy := :int(ceilf(_maxy));
|
||||||
|
maxx := :int(ceilf(_maxx));
|
||||||
|
|
||||||
|
// Draw grid
|
||||||
|
{
|
||||||
|
for y := miny; y < maxy; y += 1 {
|
||||||
|
for x := minx; x < maxx; x += 1 {
|
||||||
|
p := CalculateWorldPos({x, y});
|
||||||
|
rect := Rect2PSize(p.x, p.y, Monosize.x, Monosize.y);
|
||||||
|
rect = Shrink(rect, 1);
|
||||||
|
DrawRect(rect, {255, 0, 0, 40});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw text
|
||||||
|
{
|
||||||
|
miny = ClampInt(miny, 0, buffer.lines.len - 1);
|
||||||
|
maxy = ClampInt(maxy, 0, buffer.lines.len - 1);
|
||||||
|
for y := miny; y <= maxy; y += 1 {
|
||||||
|
line := buffer.lines.data[y];
|
||||||
|
|
||||||
|
string := AllocStringFromBuffer(&buffer, line, null_terminate = true);
|
||||||
|
defer free(string.str);
|
||||||
|
|
||||||
|
pos := CalculateWorldPos({0, y});
|
||||||
|
DrawTextEx(font, string.str, pos, font_size, font_spacing, BLACK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw selection
|
||||||
|
{
|
||||||
|
range := GetRange(MainCursor);
|
||||||
|
start_vpos := CalculateVisualPos(&buffer, range.min);
|
||||||
|
pos := start_vpos;
|
||||||
|
for iter := Iterate(&buffer, range.min, range.max); IsValid(iter); Advance(&iter) {
|
||||||
|
world_pos := CalculateWorldPos(pos);
|
||||||
|
rect := Rect2PSize(world_pos.x, world_pos.y, Monosize.x, Monosize.y);
|
||||||
|
DrawRect(rect, {0, 255, 0, 40});
|
||||||
|
|
||||||
|
pos.x += 1;
|
||||||
|
if iter.item == '\n' {
|
||||||
|
pos.x = 0;
|
||||||
|
pos.y += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw cursor
|
||||||
|
{
|
||||||
|
c := CalculateVisualPos(&buffer, MainCursor.b);
|
||||||
|
p := CalculateWorldPos(c);
|
||||||
|
|
||||||
|
cursor_size: f32 = Monosize.x * 0.2;
|
||||||
|
rect := Rect2PSize(p.x, p.y, Monosize.x, Monosize.y);
|
||||||
|
rect = CutLeft(&rect, cursor_size);
|
||||||
|
|
||||||
|
DrawRect(rect, RED);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
EndDrawing();
|
||||||
|
}
|
||||||
|
CloseWindow();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
79
examples/text_editor/rect2p.lc
Normal file
79
examples/text_editor/rect2p.lc
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
Rect2P :: struct {
|
||||||
|
min: Vector2;
|
||||||
|
max: Vector2;
|
||||||
|
}
|
||||||
|
|
||||||
|
F32_Max :: proc(a: f32, b: f32): f32 {
|
||||||
|
if a > b return a;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
F32_Min :: proc(a: f32, b: f32): f32 {
|
||||||
|
if a > b return b;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
F32_Clamp :: proc(val: f32, min: f32, max: f32): f32 {
|
||||||
|
if (val > max) return max;
|
||||||
|
if (val < min) return min;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetRectSize :: proc(rect: Rect2P): Vector2 {
|
||||||
|
result: Vector2 = {rect.max.x - rect.min.x, rect.max.y - rect.min.y};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
CutLeft :: proc(r: *Rect2P, value: f32): Rect2P {
|
||||||
|
minx := r.min.x;
|
||||||
|
r.min.x = F32_Min(r.max.x, r.min.x + value);
|
||||||
|
return :Rect2P{
|
||||||
|
{ minx, r.min.y},
|
||||||
|
{r.min.x, r.max.y},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
CutRight :: proc(r: *Rect2P, value: f32): Rect2P {
|
||||||
|
maxx := r.max.x;
|
||||||
|
r.max.x = F32_Max(r.max.x - value, r.min.x);
|
||||||
|
return :Rect2P{
|
||||||
|
{r.max.x, r.min.y},
|
||||||
|
{ maxx, r.max.y},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
CutBottom :: proc(r: *Rect2P, value: f32): Rect2P { // Y is down
|
||||||
|
maxy := r.max.y;
|
||||||
|
r.max.y = F32_Max(r.min.y, r.max.y - value);
|
||||||
|
return :Rect2P{
|
||||||
|
{r.min.x, r.max.y},
|
||||||
|
{r.max.x, maxy},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
CutTop :: proc(r: *Rect2P, value: f32): Rect2P { // Y is down
|
||||||
|
miny := r.min.y;
|
||||||
|
r.min.y = F32_Min(r.min.y + value, r.max.y);
|
||||||
|
return :Rect2P{
|
||||||
|
{r.min.x, miny},
|
||||||
|
{r.max.x, r.min.y},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect2PToRectangle :: proc(r: Rect2P): Rectangle {
|
||||||
|
result: Rectangle = {r.min.x, r.min.y, r.max.x - r.min.x, r.max.y - r.min.y};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect2PSize :: proc(x: f32, y: f32, w: f32, h: f32): Rect2P {
|
||||||
|
result: Rect2P = {{x, y}, {x+w, y+h}};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Shrink :: proc(r: Rect2P, v: f32): Rect2P {
|
||||||
|
r.min.x += v;
|
||||||
|
r.min.y += v;
|
||||||
|
r.max.x -= v;
|
||||||
|
r.max.y -= v;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
94
examples/text_editor/unicode.lc
Normal file
94
examples/text_editor/unicode.lc
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
UTF32_Result :: struct {
|
||||||
|
out_str: u32;
|
||||||
|
advance: int;
|
||||||
|
error: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
UTF8_Result :: struct {
|
||||||
|
out_str: [4]u8;
|
||||||
|
len: int;
|
||||||
|
error: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
IsUTF8ContinuationByte :: proc(c: u8): bool {
|
||||||
|
result := (c & 0b11000000) == 0b10000000;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
UTF8ToUTF32 :: proc(c: *uchar, max_advance: int): UTF32_Result {
|
||||||
|
result: UTF32_Result;
|
||||||
|
|
||||||
|
if (c[0] & 0x80) == 0 { // Check if leftmost zero of first byte is unset
|
||||||
|
if max_advance >= 1 {
|
||||||
|
result.out_str = :u32(c[0]);
|
||||||
|
result.advance = 1;
|
||||||
|
}
|
||||||
|
else result.error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (c[0] & 0xe0) == 0xc0 {
|
||||||
|
if (c[1] & 0xc0) == 0x80 { // Continuation byte required
|
||||||
|
if max_advance >= 2 {
|
||||||
|
result.out_str = (:u32(c[0] & 0x1f) << 6) | :u32(c[1] & 0x3f);
|
||||||
|
result.advance = 2;
|
||||||
|
}
|
||||||
|
else result.error = 2;
|
||||||
|
}
|
||||||
|
else result.error = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (c[0] & 0xf0) == 0xe0 {
|
||||||
|
if ((c[1] & 0xc0) == 0x80) && ((c[2] & 0xc0) == 0x80) { // Two continuation bytes required
|
||||||
|
if max_advance >= 3 {
|
||||||
|
result.out_str = (:u32(c[0] & 0xf) << 12) | (:u32(c[1] & 0x3f) << 6) | :u32(c[2] & 0x3f);
|
||||||
|
result.advance = 3;
|
||||||
|
}
|
||||||
|
else result.error = 3;
|
||||||
|
}
|
||||||
|
else result.error = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (c[0] & 0xf8) == 0xf0 {
|
||||||
|
if (c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80 && (c[3] & 0xc0) == 0x80 { // Three continuation bytes required
|
||||||
|
if max_advance >= 4 {
|
||||||
|
result.out_str = :u32(c[0] & 0xf) << 18 | :u32(c[1] & 0x3f) << 12 | :u32(c[2] & 0x3f) << 6 | :u32(c[3] & 0x3f);
|
||||||
|
result.advance = 4;
|
||||||
|
}
|
||||||
|
else result.error = 4;
|
||||||
|
}
|
||||||
|
else result.error = 4;
|
||||||
|
}
|
||||||
|
else result.error = 4;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
UTF32ToUTF8 :: proc(codepoint: u32): UTF8_Result {
|
||||||
|
result: UTF8_Result;
|
||||||
|
|
||||||
|
if codepoint <= 0x7F {
|
||||||
|
result.len = 1;
|
||||||
|
result.out_str[0] = :u8(codepoint);
|
||||||
|
}
|
||||||
|
else if codepoint <= 0x7FF {
|
||||||
|
result.len = 2;
|
||||||
|
result.out_str[0] = :u8 (0xc0 | 0x1f & (codepoint >> 6));
|
||||||
|
result.out_str[1] = :u8 (0x80 | 0x3f & codepoint);
|
||||||
|
}
|
||||||
|
else if codepoint <= 0xFFFF { // 16 bit word
|
||||||
|
result.len = 3;
|
||||||
|
result.out_str[0] = :u8 (0xe0 | 0xf & (codepoint >> 12)); // 4 bits
|
||||||
|
result.out_str[1] = :u8 (0x80 | 0x3f & (codepoint >> 6)); // 6 bits
|
||||||
|
result.out_str[2] = :u8 (0x80 | 0x3f & codepoint); // 6 bits
|
||||||
|
}
|
||||||
|
else if codepoint <= 0x10FFFF { // 21 bit word
|
||||||
|
result.len = 4;
|
||||||
|
result.out_str[0] = :u8 (0xf0 | 0x7 & (codepoint >> 18)); // 3 bits
|
||||||
|
result.out_str[1] = :u8 (0x80 | 0x3f & (codepoint >> 12)); // 6 bits
|
||||||
|
result.out_str[2] = :u8 (0x80 | 0x3f & (codepoint >> 6)); // 6 bits
|
||||||
|
result.out_str[3] = :u8 (0x80 | 0x3f & codepoint); // 6 bits
|
||||||
|
}
|
||||||
|
else result.error = 1;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
76
examples/text_editor/visual_pos.lc
Normal file
76
examples/text_editor/visual_pos.lc
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
Monosize: Vector2;
|
||||||
|
ScreenSize: Vector2;
|
||||||
|
Scroll: Vector2;
|
||||||
|
|
||||||
|
Vec2I :: struct {
|
||||||
|
x: int;
|
||||||
|
y: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
MoveLeft :: proc(buffer: *Buffer, pos: int): int {
|
||||||
|
pos -= 1;
|
||||||
|
pos = AdjustUTF8Pos(buffer, pos, direction = -1);
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
MoveRight :: proc(buffer: *Buffer, pos: int): int {
|
||||||
|
pos += 1;
|
||||||
|
pos = AdjustUTF8Pos(buffer, pos, direction = +1);
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
CalculateVisualPos :: proc(buffer: *Buffer, pos: int): Vec2I {
|
||||||
|
line: LineInfo = FindLineOfPos(buffer, pos);
|
||||||
|
|
||||||
|
iter := Iterate(buffer, line.range.min, line.range.max);
|
||||||
|
for IsValid(iter); Advance(&iter) {
|
||||||
|
if iter.pos == pos {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result: Vec2I = {iter.codepoint_index, line.number};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
CalculatePosFromVisualPos :: proc(buffer: *Buffer, vpos: Vec2I): int {
|
||||||
|
line := GetLine(buffer, vpos.y);
|
||||||
|
iter := Iterate(buffer, line.range.min, line.range.max);
|
||||||
|
for IsValid(iter); Advance(&iter) {
|
||||||
|
if iter.codepoint_index == vpos.x {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iter.pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
CalculateWorldPosUnscrolled :: proc(vpos: Vec2I): Vector2 {
|
||||||
|
result: Vector2 = {Monosize.x * :f32(vpos.x), Monosize.y * :f32(vpos.y)};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
CalculateWorldPos :: proc(vpos: Vec2I): Vector2 {
|
||||||
|
result: Vector2 = {Monosize.x * :f32(vpos.x) - Scroll.x, Monosize.y * :f32(vpos.y) - Scroll.y};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MoveDown :: proc(buffer: *Buffer, pos: int): int {
|
||||||
|
vpos := CalculateVisualPos(buffer, pos);
|
||||||
|
if !IsLineValid(buffer, vpos.y + 1) return pos;
|
||||||
|
|
||||||
|
result := CalculatePosFromVisualPos(buffer, {vpos.x, vpos.y + 1});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MoveUp :: proc(buffer: *Buffer, pos: int): int {
|
||||||
|
vpos := CalculateVisualPos(buffer, pos);
|
||||||
|
if !IsLineValid(buffer, vpos.y - 1) return pos;
|
||||||
|
|
||||||
|
result := CalculatePosFromVisualPos(buffer, {vpos.x, vpos.y - 1});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawRect :: proc(rec: Rect2P, color: Color) {
|
||||||
|
rectangle := Rect2PToRectangle(rec);
|
||||||
|
DrawRectangleRec(rectangle, color);
|
||||||
|
}
|
||||||
105
examples/use_as_data_format_with_typechecking/build.cpp
Normal file
105
examples/use_as_data_format_with_typechecking/build.cpp
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
void OnExprResolved_MakeSureConstantsWork(LC_AST *n, LC_Operand *op);
|
||||||
|
bool use_as_data_format_with_typechecking() {
|
||||||
|
LC_Lang *lang = LC_LangAlloc();
|
||||||
|
lang->use_colored_terminal_output = UseColoredIO;
|
||||||
|
lang->breakpoint_on_error = BreakpointOnError;
|
||||||
|
lang->on_expr_resolved = OnExprResolved_MakeSureConstantsWork;
|
||||||
|
LC_LangBegin(lang);
|
||||||
|
LC_RegisterPackageDir("../examples/");
|
||||||
|
LC_RegisterPackageDir("../pkgs");
|
||||||
|
|
||||||
|
LC_Intern name = LC_ILit("use_as_data_format_with_typechecking");
|
||||||
|
LC_ASTRefList packages = LC_ResolvePackageByName(name);
|
||||||
|
if (L->errors) {
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_AST *package = LC_GetPackageByName(name);
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
{
|
||||||
|
LC_Decl *int_value = LC_FindDeclInScope(package->apackage.scope, LC_ILit("IntValue"));
|
||||||
|
int64_t IntValue = LC_Bigint_as_signed(&int_value->val.i);
|
||||||
|
IO_Assert(IntValue == 232);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
LC_Decl *decl = LC_FindDeclInScope(package->apackage.scope, LC_ILit("FloatValue"));
|
||||||
|
IO_Assert(decl->val.d == 13.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
LC_Decl *decl = LC_FindDeclInScope(package->apackage.scope, LC_ILit("SomeString"));
|
||||||
|
IO_Assert(decl->val.name == LC_ILit("Thing"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Player
|
||||||
|
|
||||||
|
int64_t LEVEL_BEGIN = -1;
|
||||||
|
{
|
||||||
|
LC_Decl *int_value = LC_FindDeclInScope(package->apackage.scope, LC_ILit("LEVEL_BEGIN"));
|
||||||
|
LEVEL_BEGIN = LC_Bigint_as_signed(&int_value->val.i);
|
||||||
|
IO_Assert(LEVEL_BEGIN == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
/* @todo:
|
||||||
|
double DashSpeed = GetFloatValue(p, "Player.DashSpeed");
|
||||||
|
double Hurtbox = GetFloatValue(p, "Player[0].Something.min.x");
|
||||||
|
int64_t DashVFloorSnapDist = GetIntValue(p, "Player.DashVFloorSnapDist");
|
||||||
|
|
||||||
|
double Variable = GetFloatValue(p, "Variable");
|
||||||
|
*/
|
||||||
|
|
||||||
|
LC_Decl *decl = LC_FindDeclInScope(package->apackage.scope, LC_ILit("Player"));
|
||||||
|
LC_ResolvedCompo *items = decl->ast->dvar.expr->ecompo.resolved_items;
|
||||||
|
for (LC_ResolvedCompoItem *it = items->first; it; it = it->next) {
|
||||||
|
LC_Intern name = it->t->name;
|
||||||
|
|
||||||
|
if (name == LC_ILit("DashSpeed")) {
|
||||||
|
double DashSpeed = it->expr->const_val.d;
|
||||||
|
IO_Assert(DashSpeed == 240.0);
|
||||||
|
} else if (name == LC_ILit("DashCooldown")) {
|
||||||
|
double DashCooldown = it->expr->const_val.d;
|
||||||
|
IO_Assert(DashCooldown > 0.19 && DashCooldown < 0.21);
|
||||||
|
} else if (name == LC_ILit("Level")) {
|
||||||
|
int64_t Level = LC_Bigint_as_signed(&it->expr->const_val.i);
|
||||||
|
IO_Assert(Level == LEVEL_BEGIN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
LC_Decl *decl = LC_FindDeclInScope(package->apackage.scope, LC_ILit("Variable"));
|
||||||
|
IO_Assert(decl->val.d == 32.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
LC_Decl *decl = LC_FindDeclInScope(package->apackage.scope, LC_ILit("Reference"));
|
||||||
|
IO_Assert(decl->val.d == 64.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnExprResolved_MakeSureConstantsWork(LC_AST *n, LC_Operand *op) {
|
||||||
|
switch (n->kind) {
|
||||||
|
case LC_ASTKind_ExprCast:
|
||||||
|
case LC_ASTKind_ExprAddPtr:
|
||||||
|
case LC_ASTKind_ExprIndex:
|
||||||
|
case LC_ASTKind_ExprGetPointerOfValue:
|
||||||
|
case LC_ASTKind_ExprGetValueOfPointer:
|
||||||
|
case LC_ASTKind_ExprCall:
|
||||||
|
LC_ReportASTError(n, "illegal expression kind");
|
||||||
|
return;
|
||||||
|
default: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LC_IsFloat(op->type)) op->type = L->tuntypedfloat;
|
||||||
|
if (LC_IsInt(op->type)) op->type = L->tuntypedint;
|
||||||
|
if (LC_IsStr(op->type)) op->type = L->tuntypedstring;
|
||||||
|
op->flags |= LC_OPF_UTConst;
|
||||||
|
}
|
||||||
57
examples/use_as_data_format_with_typechecking/data.lc
Normal file
57
examples/use_as_data_format_with_typechecking/data.lc
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
Vector2 :: struct {
|
||||||
|
x: float;
|
||||||
|
y: float;
|
||||||
|
}
|
||||||
|
|
||||||
|
Hitbox :: struct {
|
||||||
|
min: Vector2;
|
||||||
|
max: Vector2;
|
||||||
|
}
|
||||||
|
|
||||||
|
TPlayer :: struct {
|
||||||
|
DashSpeed: float;
|
||||||
|
EndDashSpeed: float;
|
||||||
|
EndDashUpMult: float;
|
||||||
|
DashTime: float;
|
||||||
|
DashCooldown: float;
|
||||||
|
DashRefillCooldown: float;
|
||||||
|
DashHJumpThruNudge: int;
|
||||||
|
DashCornerCorrection: int;
|
||||||
|
DashVFloorSnapDist: int;
|
||||||
|
DashAttackTime: float;
|
||||||
|
|
||||||
|
Level: int;
|
||||||
|
NormalHitbox: Hitbox;
|
||||||
|
DuckHitbox: Hitbox;
|
||||||
|
|
||||||
|
NormalHurtbox: Hitbox;
|
||||||
|
DuckHurtbox: Hitbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player: TPlayer = {
|
||||||
|
DashSpeed = 240,
|
||||||
|
EndDashSpeed = 160,
|
||||||
|
EndDashUpMult = 0.75,
|
||||||
|
DashTime = 0.15,
|
||||||
|
DashCooldown = 0.2,
|
||||||
|
DashRefillCooldown = 0.1,
|
||||||
|
DashHJumpThruNudge = 6,
|
||||||
|
DashCornerCorrection = 4,
|
||||||
|
DashVFloorSnapDist = 3,
|
||||||
|
DashAttackTime = 0.3,
|
||||||
|
|
||||||
|
Level = LEVEL_BEGIN,
|
||||||
|
NormalHitbox = { { 1, 1 }, { 2, 2 } },
|
||||||
|
NormalHurtbox = { min = { x = 1, y = 1 }, max = { 2 } },
|
||||||
|
};
|
||||||
|
|
||||||
|
LEVEL_TUTORIAL :: 1;
|
||||||
|
LEVEL_BEGIN :: ^;
|
||||||
|
|
||||||
|
IntValue :: 232;
|
||||||
|
FloatValue :: 13.0;
|
||||||
|
SomeString :: "Thing";
|
||||||
|
|
||||||
|
Variable: float = 32.0;
|
||||||
|
Reference := Variable + Variable;
|
||||||
|
|
||||||
11089
lib_compiler.h
Normal file
11089
lib_compiler.h
Normal file
File diff suppressed because it is too large
Load Diff
13
pkgs/libc/assert.lc
Normal file
13
pkgs/libc/assert.lc
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#`#include <assert.h>`;
|
||||||
|
@foreign assert :: proc(b: bool); @foreign
|
||||||
|
|
||||||
|
#`
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#define lc_assertion_debug_break() (__debugbreak(), 0)
|
||||||
|
#else
|
||||||
|
#define lc_assertion_debug_break() (__builtin_trap(), 0)
|
||||||
|
#endif
|
||||||
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
debug_break :: proc(): int; @foreign(lc_assertion_debug_break)
|
||||||
17
pkgs/libc/ctype.lc
Normal file
17
pkgs/libc/ctype.lc
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#`#include<ctype.h>`;
|
||||||
|
|
||||||
|
isalnum :: proc(c: int): int; @foreign
|
||||||
|
isalpha :: proc(c: int): int; @foreign
|
||||||
|
isblank :: proc(c: int): int; @foreign
|
||||||
|
iscntrl :: proc(c: int): int; @foreign
|
||||||
|
isdigit :: proc(c: int): int; @foreign
|
||||||
|
isgraph :: proc(c: int): int; @foreign
|
||||||
|
islower :: proc(c: int): int; @foreign
|
||||||
|
isprint :: proc(c: int): int; @foreign
|
||||||
|
ispunct :: proc(c: int): int; @foreign
|
||||||
|
isspace :: proc(c: int): int; @foreign
|
||||||
|
isupper :: proc(c: int): int; @foreign
|
||||||
|
isxdigit :: proc(c: int): int; @foreign
|
||||||
|
|
||||||
|
tolower :: proc(c: int): int; @foreign
|
||||||
|
toupper :: proc(c: int): int; @foreign
|
||||||
2
pkgs/libc/errno.lc
Normal file
2
pkgs/libc/errno.lc
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#`#include<errno.h>`;
|
||||||
|
errno: int; @foreign
|
||||||
122
pkgs/libc/math.lc
Normal file
122
pkgs/libc/math.lc
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
#`#include <math.h>`;
|
||||||
|
|
||||||
|
acos :: proc(x: double): double; @foreign
|
||||||
|
acosf :: proc(x: float): float; @foreign
|
||||||
|
asin :: proc(x: double): double; @foreign
|
||||||
|
asinf :: proc(x: float): float; @foreign
|
||||||
|
atan :: proc(x: double): double; @foreign
|
||||||
|
atanf :: proc(x: float): float; @foreign
|
||||||
|
atan2 :: proc(y: double, x: double): double; @foreign
|
||||||
|
atan2f :: proc(y: float, x: float): float; @foreign
|
||||||
|
cos :: proc(x: double): double; @foreign
|
||||||
|
cosf :: proc(x: float): float; @foreign
|
||||||
|
sin :: proc(x: double): double; @foreign
|
||||||
|
sinf :: proc(x: float): float; @foreign
|
||||||
|
tan :: proc(x: double): double; @foreign
|
||||||
|
tanf :: proc(x: float): float; @foreign
|
||||||
|
|
||||||
|
acosh :: proc(x: double): double; @foreign
|
||||||
|
acoshf :: proc(x: float): float; @foreign
|
||||||
|
asinh :: proc(x: double): double; @foreign
|
||||||
|
asinhf :: proc(x: float): float; @foreign
|
||||||
|
atanh :: proc(x: double): double; @foreign
|
||||||
|
atanhf :: proc(x: float): float; @foreign
|
||||||
|
cosh :: proc(x: double): double; @foreign
|
||||||
|
coshf :: proc(x: float): float; @foreign
|
||||||
|
sinh :: proc(x: double): double; @foreign
|
||||||
|
sinhf :: proc(x: float): float; @foreign
|
||||||
|
tanh :: proc(x: double): double; @foreign
|
||||||
|
tanhf :: proc(x: float): float; @foreign
|
||||||
|
|
||||||
|
exp :: proc(x: double): double; @foreign
|
||||||
|
expf :: proc(x: float): float; @foreign
|
||||||
|
exp2 :: proc(x: double): double; @foreign
|
||||||
|
exp2f :: proc(x: float): float; @foreign
|
||||||
|
expm1 :: proc(x: double): double; @foreign
|
||||||
|
expm1f :: proc(x: float): float; @foreign
|
||||||
|
frexp :: proc(value: double, exp: *int): double; @foreign
|
||||||
|
frexpf :: proc(value: float, exp: *int): float; @foreign
|
||||||
|
ilogb :: proc(x: double): int; @foreign
|
||||||
|
ilogbf :: proc(x: float): int; @foreign
|
||||||
|
ldexp :: proc(x: double, exp: int): double; @foreign
|
||||||
|
ldexpf :: proc(x: float, exp: int): float; @foreign
|
||||||
|
log :: proc(x: double): double; @foreign
|
||||||
|
logf :: proc(x: float): float; @foreign
|
||||||
|
log10 :: proc(x: double): double; @foreign
|
||||||
|
log10f :: proc(x: float): float; @foreign
|
||||||
|
log1p :: proc(x: double): double; @foreign
|
||||||
|
log1pf :: proc(x: float): float; @foreign
|
||||||
|
log2 :: proc(x: double): double; @foreign
|
||||||
|
log2f :: proc(x: float): float; @foreign
|
||||||
|
logb :: proc(x: double): double; @foreign
|
||||||
|
logbf :: proc(x: float): float; @foreign
|
||||||
|
modf :: proc(value: double, iptr: *double): double; @foreign
|
||||||
|
modff :: proc(value: float, iptr: *float): float; @foreign
|
||||||
|
scalbn :: proc(x: double, n: int): double; @foreign
|
||||||
|
scalbnf :: proc(x: float, n: int): float; @foreign
|
||||||
|
scalbln :: proc(x: double, n: long): double; @foreign
|
||||||
|
scalblnf :: proc(x: float, n: long): float; @foreign
|
||||||
|
|
||||||
|
cbrt :: proc(x: double): double; @foreign
|
||||||
|
cbrtf :: proc(x: float): float; @foreign
|
||||||
|
fabs :: proc(x: double): double; @foreign
|
||||||
|
fabsf :: proc(x: float): float; @foreign
|
||||||
|
hypot :: proc(x: double, y: double): double; @foreign
|
||||||
|
hypotf :: proc(x: float, y: float): float; @foreign
|
||||||
|
pow :: proc(x: double, y: double): double; @foreign
|
||||||
|
powf :: proc(x: float, y: float): float; @foreign
|
||||||
|
sqrt :: proc(x: double): double; @foreign
|
||||||
|
sqrtf :: proc(x: float): float; @foreign
|
||||||
|
|
||||||
|
erf :: proc(x: double): double; @foreign
|
||||||
|
erff :: proc(x: float): float; @foreign
|
||||||
|
erfc :: proc(x: double): double; @foreign
|
||||||
|
erfcf :: proc(x: float): float; @foreign
|
||||||
|
lgamma :: proc(x: double): double; @foreign
|
||||||
|
lgammaf :: proc(x: float): float; @foreign
|
||||||
|
tgamma :: proc(x: double): double; @foreign
|
||||||
|
tgammaf :: proc(x: float): float; @foreign
|
||||||
|
|
||||||
|
ceil :: proc(x: double): double; @foreign
|
||||||
|
ceilf :: proc(x: float): float; @foreign
|
||||||
|
floor :: proc(x: double): double; @foreign
|
||||||
|
floorf :: proc(x: float): float; @foreign
|
||||||
|
nearbyint :: proc(x: double): double; @foreign
|
||||||
|
nearbyintf :: proc(x: float): float; @foreign
|
||||||
|
rint :: proc(x: double): double; @foreign
|
||||||
|
rintf :: proc(x: float): float; @foreign
|
||||||
|
lrint :: proc(x: double): long; @foreign
|
||||||
|
lrintf :: proc(x: float): long; @foreign
|
||||||
|
llrint :: proc(x: double): llong; @foreign
|
||||||
|
llrintf :: proc(x: float): llong; @foreign
|
||||||
|
round :: proc(x: double): double; @foreign
|
||||||
|
roundf :: proc(x: float): float; @foreign
|
||||||
|
lround :: proc(x: double): long; @foreign
|
||||||
|
lroundf :: proc(x: float): long; @foreign
|
||||||
|
llround :: proc(x: double): llong; @foreign
|
||||||
|
llroundf :: proc(x: float): llong; @foreign
|
||||||
|
trunc :: proc(x: double): double; @foreign
|
||||||
|
truncf :: proc(x: float): float; @foreign
|
||||||
|
|
||||||
|
fmod :: proc(x: double, y: double): double; @foreign
|
||||||
|
fmodf :: proc(x: float, y: float): float; @foreign
|
||||||
|
remainder :: proc(x: double, y: double): double; @foreign
|
||||||
|
remainderf :: proc(x: float, y: float): float; @foreign
|
||||||
|
remquo :: proc(x: double, y: double, quo: *int): double; @foreign
|
||||||
|
remquof :: proc(x: float, y: float, quo: *int): float; @foreign
|
||||||
|
|
||||||
|
copysign :: proc(x: double, y: double): double; @foreign
|
||||||
|
copysignf :: proc(x: float, y: float): float; @foreign
|
||||||
|
nan :: proc(tagp: *char): double; @foreign
|
||||||
|
nanf :: proc(tagp: *char): float; @foreign
|
||||||
|
nextafter :: proc(x: double, y: double): double; @foreign
|
||||||
|
nextafterf :: proc(x: float, y: float): float; @foreign
|
||||||
|
|
||||||
|
fdim :: proc(x: double, y: double): double; @foreign
|
||||||
|
fdimf :: proc(x: float, y: float): float; @foreign
|
||||||
|
fmax :: proc(x: double, y: double): double; @foreign
|
||||||
|
fmaxf :: proc(x: float, y: float): float; @foreign
|
||||||
|
fmin :: proc(x: double, y: double): double; @foreign
|
||||||
|
fminf :: proc(x: float, y: float): float; @foreign
|
||||||
|
fma :: proc(x: double, y: double, z: double): double; @foreign
|
||||||
|
fmaf :: proc(x: float, y: float, z: float): float; @foreign
|
||||||
3
pkgs/libc/size_t.lc
Normal file
3
pkgs/libc/size_t.lc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import std_types "std_types";
|
||||||
|
|
||||||
|
size_t :: typedef std_types.usize; @foreign @weak
|
||||||
12
pkgs/libc/stdarg.lc
Normal file
12
pkgs/libc/stdarg.lc
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#`
|
||||||
|
#include <stdarg.h>
|
||||||
|
#define lc_va_arg_any(va) va_arg(va, LC_Any)
|
||||||
|
`;
|
||||||
|
|
||||||
|
va_list :: struct { a: ullong; } @foreign
|
||||||
|
va_start :: proc(args: va_list, arg: *void); @foreign
|
||||||
|
va_end :: proc(args: va_list); @foreign
|
||||||
|
va_copy :: proc(args: va_list, src: va_list); @foreign
|
||||||
|
// va_arg :: proc(args: va_list, arg: *void); @foreign
|
||||||
|
|
||||||
|
va_arg_any :: proc(args: va_list): Any; @foreign(lc_va_arg_any)
|
||||||
61
pkgs/libc/stdio.lc
Normal file
61
pkgs/libc/stdio.lc
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#`#include<stdio.h>`;
|
||||||
|
|
||||||
|
SEEK_SET :: 0;
|
||||||
|
SEEK_CUR :: 1;
|
||||||
|
SEEK_END :: 2;
|
||||||
|
|
||||||
|
FILE :: struct {a: int;} @foreign
|
||||||
|
fpos_t :: typedef long; @foreign
|
||||||
|
|
||||||
|
stderr: *FILE; @foreign
|
||||||
|
stdin: *FILE; @foreign
|
||||||
|
stdout: *FILE; @foreign
|
||||||
|
|
||||||
|
remove :: proc(filename: *char): int; @foreign
|
||||||
|
rename :: proc(old: *char, new: *char): int; @foreign
|
||||||
|
tmpfile :: proc(): *FILE; @foreign
|
||||||
|
tmpnam :: proc(s: *char): *char; @foreign
|
||||||
|
|
||||||
|
fclose :: proc(stream: *FILE): int; @foreign
|
||||||
|
fflush :: proc(stream: *FILE): int; @foreign
|
||||||
|
fopen :: proc(filename: *char, mode: *char): *FILE; @foreign
|
||||||
|
freopen :: proc(filename: *char, mode: *char, stream: *FILE): *FILE; @foreign
|
||||||
|
setbuf :: proc(stream: *FILE, buf: *char); @foreign
|
||||||
|
setvbuf :: proc(stream: *FILE, buf: *char, mode: int, size: size_t): int; @foreign
|
||||||
|
|
||||||
|
fprintf :: proc(stream: *FILE, format: *char, ...): int; @foreign
|
||||||
|
fscanf :: proc(stream: *FILE, format: *char, ...): int; @foreign
|
||||||
|
printf :: proc(format: *char, ...): int; @foreign
|
||||||
|
scanf :: proc(format: *char, ...): int; @foreign
|
||||||
|
snprintf :: proc(s: *char, n: size_t, format: *char, ...): int; @foreign
|
||||||
|
sscanf :: proc(s: *char, format: *char, ...): int; @foreign
|
||||||
|
vfprintf :: proc(stream: *FILE, format: *char, arg: *va_list): int; @foreign
|
||||||
|
vfscanf :: proc(stream: *FILE, format: *char, arg: *va_list): int; @foreign
|
||||||
|
vprintf :: proc(format: *char, arg: *va_list): int; @foreign
|
||||||
|
vscanf :: proc(format: *char, arg: *va_list): int; @foreign
|
||||||
|
vsnprintf :: proc(s: *char, n: size_t, format: *char, arg: *va_list): int; @foreign
|
||||||
|
vsprintf :: proc(s: *char, format: *char, arg: *va_list): int; @foreign
|
||||||
|
vsscanf :: proc(s: *char, format: *char, arg: *va_list): int; @foreign
|
||||||
|
|
||||||
|
fgetc :: proc(stream: *FILE): int; @foreign
|
||||||
|
fgets :: proc(s: *char, n: int, stream: *FILE): *char; @foreign
|
||||||
|
fputc :: proc(s: *char, stream: *FILE): int; @foreign
|
||||||
|
getc :: proc(stream: *FILE): int; @foreign
|
||||||
|
getchar :: proc(): int; @foreign
|
||||||
|
putc :: proc(c: int, stream: *FILE): int; @foreign
|
||||||
|
putchar :: proc(c: int): int; @foreign
|
||||||
|
puts :: proc(s: *char): int; @foreign
|
||||||
|
ungetc :: proc(c: int, stream: *FILE): int; @foreign
|
||||||
|
fread :: proc(ptr: *void, size: size_t, nmemb: size_t, stream: *FILE): size_t; @foreign
|
||||||
|
fwrite :: proc(ptr: *void, size: size_t, nmemb: size_t, stream: *FILE): size_t; @foreign
|
||||||
|
|
||||||
|
fgetpos :: proc(stream: *FILE, pos: *fpos_t): int; @foreign
|
||||||
|
fseek :: proc(stream: *FILE, offset: long, whence: int): int; @foreign
|
||||||
|
fsetpos :: proc(stream: *FILE, pos: *fpos_t): int; @foreign
|
||||||
|
ftell :: proc(stream: *FILE): long; @foreign
|
||||||
|
rewind :: proc(stream: *FILE); @foreign
|
||||||
|
|
||||||
|
clearerr :: proc(stream: *FILE); @foreign
|
||||||
|
feof :: proc(stream: *FILE): int; @foreign
|
||||||
|
ferror :: proc(stream: *FILE): int; @foreign
|
||||||
|
perror :: proc(s: *char); @foreign
|
||||||
48
pkgs/libc/stdlib.lc
Normal file
48
pkgs/libc/stdlib.lc
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#`#include<stdlib.h>`;
|
||||||
|
|
||||||
|
wchar_t :: typedef std_types.u16; @foreign
|
||||||
|
div_t :: struct { quot: int; rem: int; } @foreign
|
||||||
|
ldiv_t :: struct { quot: long; rem: long; } @foreign
|
||||||
|
lldiv_t :: struct { quot: llong; rem: llong; } @foreign
|
||||||
|
|
||||||
|
atof :: proc(nptr: *char): double; @foreign
|
||||||
|
atoi :: proc(nptr: *char): int; @foreign
|
||||||
|
atol :: proc(nptr: *char): long; @foreign
|
||||||
|
atoll :: proc(nptr: *char): llong; @foreign
|
||||||
|
strtod :: proc(nptr: *char, endptr: **char): double; @foreign
|
||||||
|
strtof :: proc(nptr: *char, endptr: **char): float; @foreign
|
||||||
|
strtol :: proc(nptr: *char, endptr: **char, base: int): long; @foreign
|
||||||
|
strtoll :: proc(nptr: *char, endptr: **char, base: int): llong; @foreign
|
||||||
|
strtoul :: proc(nptr: *char, endptr: **char, base: int): ulong; @foreign
|
||||||
|
strtoull :: proc(nptr: *char, endptr: **char, base: int): ullong; @foreign
|
||||||
|
|
||||||
|
rand :: proc(): int; @foreign
|
||||||
|
srand :: proc(seed: uint); @foreign
|
||||||
|
|
||||||
|
calloc :: proc(nmemb: size_t, size: size_t): *void; @foreign
|
||||||
|
free :: proc(ptr: *void); @foreign
|
||||||
|
malloc :: proc(size: size_t): *void; @foreign
|
||||||
|
realloc :: proc(ptr: *void, size: size_t): *void; @foreign
|
||||||
|
|
||||||
|
abort :: proc(); @foreign
|
||||||
|
atexit :: proc(func: proc()): int; @foreign
|
||||||
|
at_quick_exit :: proc(func: proc()): int; @foreign
|
||||||
|
exit :: proc(status: int); @foreign
|
||||||
|
_Exit :: proc(status: int); @foreign
|
||||||
|
getenv :: proc(name: *char): *char; @foreign
|
||||||
|
quick_exit :: proc(status: int); @foreign
|
||||||
|
system :: proc(cmd: *char): int; @foreign
|
||||||
|
|
||||||
|
abs :: proc(j: int): int; @foreign
|
||||||
|
labs :: proc(j: long): long; @foreign
|
||||||
|
llabs :: proc(j: llong): llong; @foreign
|
||||||
|
div :: proc(numer: int, denom: int): div_t; @foreign
|
||||||
|
ldiv :: proc(numer: long, denom: long): ldiv_t; @foreign
|
||||||
|
lldiv :: proc(numer: llong, denom: llong): lldiv_t; @foreign
|
||||||
|
|
||||||
|
mblen :: proc(s: *char, n: size_t): int; @foreign
|
||||||
|
mbtowc :: proc(pwc: *wchar_t, s: *char, n: size_t): int; @foreign
|
||||||
|
wctomb :: proc(s: *char, wc: wchar_t): int; @foreign
|
||||||
|
|
||||||
|
mbstowcs :: proc(pwcs: *wchar_t, s: *char, n: size_t): size_t; @foreign
|
||||||
|
wcstombs :: proc(s: *char, pwcs: *wchar_t, n: size_t): size_t; @foreign
|
||||||
28
pkgs/libc/string.lc
Normal file
28
pkgs/libc/string.lc
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#`#include<string.h>`;
|
||||||
|
|
||||||
|
|
||||||
|
memset :: proc(s: *void, value: int, n: size_t): *void; @foreign
|
||||||
|
memcpy :: proc(s1: *void, s2: *void, n: size_t): *void; @foreign
|
||||||
|
memmove :: proc(s1: *void, s2: *void, n: size_t): *void; @foreign
|
||||||
|
strcpy :: proc(s1: *char, s2: *char): *char; @foreign
|
||||||
|
strncpy :: proc(s1: *char, s2: *char, n: size_t): *char; @foreign
|
||||||
|
|
||||||
|
strcat :: proc(s1: *char, s2: *char): *char; @foreign
|
||||||
|
strncat :: proc(s1: *char, s2: *char, n: size_t): *char; @foreign
|
||||||
|
|
||||||
|
memcmp :: proc(s1: *void, s2: *void, n: size_t): int; @foreign
|
||||||
|
strcmp :: proc(s1: *char, s2: *char): int; @foreign
|
||||||
|
strcoll :: proc(s1: *char, s2: *char): int; @foreign
|
||||||
|
strncmp :: proc(s1: *char, s2: *char, n: size_t): int; @foreign
|
||||||
|
strxfrm :: proc(s1: *char, s2: *char, n: size_t): size_t; @foreign
|
||||||
|
|
||||||
|
memchr :: proc(s : *void, c: int, n: size_t): *void; @foreign
|
||||||
|
strchr :: proc(s : *char, c: int): *char; @foreign
|
||||||
|
strcspn :: proc(s1: *char, s2: *char): size_t; @foreign
|
||||||
|
strpbrk :: proc(s1: *char, s2: *char): *char; @foreign
|
||||||
|
strrchr :: proc(s : *char, c: int): *char; @foreign
|
||||||
|
strcpn :: proc(s1: *char, s2: *char): *char; @foreign
|
||||||
|
strtok :: proc(s1: *char, s2: *char): *char; @foreign
|
||||||
|
|
||||||
|
strerror :: proc(errnum: int): *char; @foreign
|
||||||
|
strlen :: proc(s: *char): size_t; @foreign
|
||||||
16
pkgs/raylib/raylib-5.0_win64_msvc16/LICENSE
Normal file
16
pkgs/raylib/raylib-5.0_win64_msvc16/LICENSE
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
Copyright (c) 2013-2023 Ramon Santamaria (@raysan5)
|
||||||
|
|
||||||
|
This software is provided "as-is", without any express or implied warranty. In no event
|
||||||
|
will the authors be held liable for any damages arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose, including commercial
|
||||||
|
applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not claim that you
|
||||||
|
wrote the original software. If you use this software in a product, an acknowledgment
|
||||||
|
in the product documentation would be appreciated but is not required.
|
||||||
|
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be misrepresented
|
||||||
|
as being the original software.
|
||||||
|
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
1662
pkgs/raylib/raylib-5.0_win64_msvc16/include/raylib.h
Normal file
1662
pkgs/raylib/raylib-5.0_win64_msvc16/include/raylib.h
Normal file
File diff suppressed because it is too large
Load Diff
2190
pkgs/raylib/raylib-5.0_win64_msvc16/include/raymath.h
Normal file
2190
pkgs/raylib/raylib-5.0_win64_msvc16/include/raymath.h
Normal file
File diff suppressed because it is too large
Load Diff
4859
pkgs/raylib/raylib-5.0_win64_msvc16/include/rlgl.h
Normal file
4859
pkgs/raylib/raylib-5.0_win64_msvc16/include/rlgl.h
Normal file
File diff suppressed because it is too large
Load Diff
BIN
pkgs/raylib/raylib-5.0_win64_msvc16/lib/raylib.dll
Normal file
BIN
pkgs/raylib/raylib-5.0_win64_msvc16/lib/raylib.dll
Normal file
Binary file not shown.
BIN
pkgs/raylib/raylib-5.0_win64_msvc16/lib/raylib.lib
Normal file
BIN
pkgs/raylib/raylib-5.0_win64_msvc16/lib/raylib.lib
Normal file
Binary file not shown.
BIN
pkgs/raylib/raylib-5.0_win64_msvc16/lib/raylibdll.lib
Normal file
BIN
pkgs/raylib/raylib-5.0_win64_msvc16/lib/raylibdll.lib
Normal file
Binary file not shown.
1012
pkgs/raylib/raylib.lc
Normal file
1012
pkgs/raylib/raylib.lc
Normal file
File diff suppressed because it is too large
Load Diff
122
pkgs/raylib/raymath.lc
Normal file
122
pkgs/raylib/raymath.lc
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
float3 :: struct {
|
||||||
|
v: [3]float;
|
||||||
|
}
|
||||||
|
|
||||||
|
float16 :: struct {
|
||||||
|
v: [16]float;
|
||||||
|
}
|
||||||
|
|
||||||
|
Clamp :: proc(value: float, min: float, max: float): float; @api
|
||||||
|
Lerp :: proc(start: float, end: float, amount: float): float; @api
|
||||||
|
Normalize :: proc(value: float, start: float, end: float): float; @api
|
||||||
|
Remap :: proc(value: float, inputStart: float, inputEnd: float, outputStart: float, outputEnd: float): float; @api
|
||||||
|
Wrap :: proc(value: float, min: float, max: float): float; @api
|
||||||
|
FloatEquals :: proc(x: float, y: float): int; @api
|
||||||
|
Vector2Zero :: proc(): Vector2; @api
|
||||||
|
Vector2One :: proc(): Vector2; @api
|
||||||
|
Vector2Add :: proc(v1: Vector2, v2: Vector2): Vector2; @api
|
||||||
|
Vector2AddValue :: proc(v: Vector2, add: float): Vector2; @api
|
||||||
|
Vector2Subtract :: proc(v1: Vector2, v2: Vector2): Vector2; @api
|
||||||
|
Vector2SubtractValue :: proc(v: Vector2, sub: float): Vector2; @api
|
||||||
|
Vector2Length :: proc(v: Vector2): float; @api
|
||||||
|
Vector2LengthSqr :: proc(v: Vector2): float; @api
|
||||||
|
Vector2DotProduct :: proc(v1: Vector2, v2: Vector2): float; @api
|
||||||
|
Vector2Distance :: proc(v1: Vector2, v2: Vector2): float; @api
|
||||||
|
Vector2DistanceSqr :: proc(v1: Vector2, v2: Vector2): float; @api
|
||||||
|
Vector2Angle :: proc(v1: Vector2, v2: Vector2): float; @api
|
||||||
|
Vector2LineAngle :: proc(start: Vector2, end: Vector2): float; @api
|
||||||
|
Vector2Scale :: proc(v: Vector2, scale: float): Vector2; @api
|
||||||
|
Vector2Multiply :: proc(v1: Vector2, v2: Vector2): Vector2; @api
|
||||||
|
Vector2Negate :: proc(v: Vector2): Vector2; @api
|
||||||
|
Vector2Divide :: proc(v1: Vector2, v2: Vector2): Vector2; @api
|
||||||
|
Vector2Normalize :: proc(v: Vector2): Vector2; @api
|
||||||
|
Vector2Transform :: proc(v: Vector2, mat: Matrix): Vector2; @api
|
||||||
|
Vector2Lerp :: proc(v1: Vector2, v2: Vector2, amount: float): Vector2; @api
|
||||||
|
Vector2Reflect :: proc(v: Vector2, normal: Vector2): Vector2; @api
|
||||||
|
Vector2Rotate :: proc(v: Vector2, angle: float): Vector2; @api
|
||||||
|
Vector2MoveTowards :: proc(v: Vector2, target: Vector2, maxDistance: float): Vector2; @api
|
||||||
|
Vector2Invert :: proc(v: Vector2): Vector2; @api
|
||||||
|
Vector2Clamp :: proc(v: Vector2, min: Vector2, max: Vector2): Vector2; @api
|
||||||
|
Vector2ClampValue :: proc(v: Vector2, min: float, max: float): Vector2; @api
|
||||||
|
Vector2Equals :: proc(p: Vector2, q: Vector2): int; @api
|
||||||
|
Vector3Zero :: proc(): Vector3; @api
|
||||||
|
Vector3One :: proc(): Vector3; @api
|
||||||
|
Vector3Add :: proc(v1: Vector3, v2: Vector3): Vector3; @api
|
||||||
|
Vector3AddValue :: proc(v: Vector3, add: float): Vector3; @api
|
||||||
|
Vector3Subtract :: proc(v1: Vector3, v2: Vector3): Vector3; @api
|
||||||
|
Vector3SubtractValue :: proc(v: Vector3, sub: float): Vector3; @api
|
||||||
|
Vector3Scale :: proc(v: Vector3, scalar: float): Vector3; @api
|
||||||
|
Vector3Multiply :: proc(v1: Vector3, v2: Vector3): Vector3; @api
|
||||||
|
Vector3CrossProduct :: proc(v1: Vector3, v2: Vector3): Vector3; @api
|
||||||
|
Vector3Perpendicular :: proc(v: Vector3): Vector3; @api
|
||||||
|
Vector3Length :: proc(v: Vector3): float; @api
|
||||||
|
Vector3LengthSqr :: proc(v: Vector3): float; @api
|
||||||
|
Vector3DotProduct :: proc(v1: Vector3, v2: Vector3): float; @api
|
||||||
|
Vector3Distance :: proc(v1: Vector3, v2: Vector3): float; @api
|
||||||
|
Vector3DistanceSqr :: proc(v1: Vector3, v2: Vector3): float; @api
|
||||||
|
Vector3Angle :: proc(v1: Vector3, v2: Vector3): float; @api
|
||||||
|
Vector3Negate :: proc(v: Vector3): Vector3; @api
|
||||||
|
Vector3Divide :: proc(v1: Vector3, v2: Vector3): Vector3; @api
|
||||||
|
Vector3Normalize :: proc(v: Vector3): Vector3; @api
|
||||||
|
Vector3Project :: proc(v1: Vector3, v2: Vector3): Vector3; @api
|
||||||
|
Vector3Reject :: proc(v1: Vector3, v2: Vector3): Vector3; @api
|
||||||
|
Vector3OrthoNormalize :: proc(v1: *Vector3, v2: *Vector3); @api
|
||||||
|
Vector3Transform :: proc(v: Vector3, mat: Matrix): Vector3; @api
|
||||||
|
Vector3RotateByQuaternion :: proc(v: Vector3, q: Quaternion): Vector3; @api
|
||||||
|
Vector3RotateByAxisAngle :: proc(v: Vector3, axis: Vector3, angle: float): Vector3; @api
|
||||||
|
Vector3Lerp :: proc(v1: Vector3, v2: Vector3, amount: float): Vector3; @api
|
||||||
|
Vector3Reflect :: proc(v: Vector3, normal: Vector3): Vector3; @api
|
||||||
|
Vector3Min :: proc(v1: Vector3, v2: Vector3): Vector3; @api
|
||||||
|
Vector3Max :: proc(v1: Vector3, v2: Vector3): Vector3; @api
|
||||||
|
Vector3Barycenter :: proc(p: Vector3, a: Vector3, b: Vector3, c: Vector3): Vector3; @api
|
||||||
|
Vector3Unproject :: proc(source: Vector3, projection: Matrix, view: Matrix): Vector3; @api
|
||||||
|
Vector3ToFloatV :: proc(v: Vector3): float3; @api
|
||||||
|
Vector3Invert :: proc(v: Vector3): Vector3; @api
|
||||||
|
Vector3Clamp :: proc(v: Vector3, min: Vector3, max: Vector3): Vector3; @api
|
||||||
|
Vector3ClampValue :: proc(v: Vector3, min: float, max: float): Vector3; @api
|
||||||
|
Vector3Equals :: proc(p: Vector3, q: Vector3): int; @api
|
||||||
|
Vector3Refract :: proc(v: Vector3, n: Vector3, r: float): Vector3; @api
|
||||||
|
MatrixDeterminant :: proc(mat: Matrix): float; @api
|
||||||
|
MatrixTrace :: proc(mat: Matrix): float; @api
|
||||||
|
MatrixTranspose :: proc(mat: Matrix): Matrix; @api
|
||||||
|
MatrixInvert :: proc(mat: Matrix): Matrix; @api
|
||||||
|
MatrixIdentity :: proc(): Matrix; @api
|
||||||
|
MatrixAdd :: proc(left: Matrix, right: Matrix): Matrix; @api
|
||||||
|
MatrixSubtract :: proc(left: Matrix, right: Matrix): Matrix; @api
|
||||||
|
MatrixMultiply :: proc(left: Matrix, right: Matrix): Matrix; @api
|
||||||
|
MatrixTranslate :: proc(x: float, y: float, z: float): Matrix; @api
|
||||||
|
MatrixRotate :: proc(axis: Vector3, angle: float): Matrix; @api
|
||||||
|
MatrixRotateX :: proc(angle: float): Matrix; @api
|
||||||
|
MatrixRotateY :: proc(angle: float): Matrix; @api
|
||||||
|
MatrixRotateZ :: proc(angle: float): Matrix; @api
|
||||||
|
MatrixRotateXYZ :: proc(angle: Vector3): Matrix; @api
|
||||||
|
MatrixRotateZYX :: proc(angle: Vector3): Matrix; @api
|
||||||
|
MatrixScale :: proc(x: float, y: float, z: float): Matrix; @api
|
||||||
|
MatrixFrustum :: proc(left: double, right: double, bottom: double, top: double, near: double, far: double): Matrix; @api
|
||||||
|
MatrixPerspective :: proc(fovY: double, aspect: double, nearPlane: double, farPlane: double): Matrix; @api
|
||||||
|
MatrixOrtho :: proc(left: double, right: double, bottom: double, top: double, nearPlane: double, farPlane: double): Matrix; @api
|
||||||
|
MatrixLookAt :: proc(eye: Vector3, target: Vector3, up: Vector3): Matrix; @api
|
||||||
|
MatrixToFloatV :: proc(mat: Matrix): float16; @api
|
||||||
|
QuaternionAdd :: proc(q1: Quaternion, q2: Quaternion): Quaternion; @api
|
||||||
|
QuaternionAddValue :: proc(q: Quaternion, add: float): Quaternion; @api
|
||||||
|
QuaternionSubtract :: proc(q1: Quaternion, q2: Quaternion): Quaternion; @api
|
||||||
|
QuaternionSubtractValue :: proc(q: Quaternion, sub: float): Quaternion; @api
|
||||||
|
QuaternionIdentity :: proc(): Quaternion; @api
|
||||||
|
QuaternionLength :: proc(q: Quaternion): float; @api
|
||||||
|
QuaternionNormalize :: proc(q: Quaternion): Quaternion; @api
|
||||||
|
QuaternionInvert :: proc(q: Quaternion): Quaternion; @api
|
||||||
|
QuaternionMultiply :: proc(q1: Quaternion, q2: Quaternion): Quaternion; @api
|
||||||
|
QuaternionScale :: proc(q: Quaternion, mul: float): Quaternion; @api
|
||||||
|
QuaternionDivide :: proc(q1: Quaternion, q2: Quaternion): Quaternion; @api
|
||||||
|
QuaternionLerp :: proc(q1: Quaternion, q2: Quaternion, amount: float): Quaternion; @api
|
||||||
|
QuaternionNlerp :: proc(q1: Quaternion, q2: Quaternion, amount: float): Quaternion; @api
|
||||||
|
QuaternionSlerp :: proc(q1: Quaternion, q2: Quaternion, amount: float): Quaternion; @api
|
||||||
|
QuaternionFromVector3ToVector3 :: proc(from: Vector3, to: Vector3): Quaternion; @api
|
||||||
|
QuaternionFromMatrix :: proc(mat: Matrix): Quaternion; @api
|
||||||
|
QuaternionToMatrix :: proc(q: Quaternion): Matrix; @api
|
||||||
|
QuaternionFromAxisAngle :: proc(axis: Vector3, angle: float): Quaternion; @api
|
||||||
|
QuaternionToAxisAngle :: proc(q: Quaternion, outAxis: *Vector3, outAngle: *float); @api
|
||||||
|
QuaternionFromEuler :: proc(pitch: float, yaw: float, roll: float): Quaternion; @api
|
||||||
|
QuaternionToEuler :: proc(q: Quaternion): Vector3; @api
|
||||||
|
QuaternionTransform :: proc(q: Quaternion, mat: Matrix): Quaternion; @api
|
||||||
|
QuaternionEquals :: proc(p: Quaternion, q: Quaternion): int; @api
|
||||||
173
pkgs/raylib/rlgl.lc
Normal file
173
pkgs/raylib/rlgl.lc
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
rlVertexBuffer :: struct {
|
||||||
|
elementCount: int;
|
||||||
|
vertices: *float;
|
||||||
|
texcoords: *float;
|
||||||
|
colors: *uchar;
|
||||||
|
indices: *uint;
|
||||||
|
vaoId: uint;
|
||||||
|
vboId: [4]uint;
|
||||||
|
}
|
||||||
|
|
||||||
|
rlDrawCall :: struct {
|
||||||
|
mode: int;
|
||||||
|
vertexCount: int;
|
||||||
|
vertexAlignment: int;
|
||||||
|
textureId: uint;
|
||||||
|
}
|
||||||
|
|
||||||
|
rlRenderBatch :: struct {
|
||||||
|
bufferCount: int;
|
||||||
|
currentBuffer: int;
|
||||||
|
vertexBuffer: *rlVertexBuffer;
|
||||||
|
draws: *rlDrawCall;
|
||||||
|
drawCounter: int;
|
||||||
|
currentDepth: float;
|
||||||
|
}
|
||||||
|
|
||||||
|
rlMatrixMode :: proc(mode: int); @dont_mangle @api
|
||||||
|
rlPushMatrix :: proc(); @dont_mangle @api
|
||||||
|
rlPopMatrix :: proc(); @dont_mangle @api
|
||||||
|
rlLoadIdentity :: proc(); @dont_mangle @api
|
||||||
|
rlTranslatef :: proc(x: float, y: float, z: float); @dont_mangle @api
|
||||||
|
rlRotatef :: proc(angle: float, x: float, y: float, z: float); @dont_mangle @api
|
||||||
|
rlScalef :: proc(x: float, y: float, z: float); @dont_mangle @api
|
||||||
|
rlMultMatrixf :: proc(matf: *float); @dont_mangle @api
|
||||||
|
rlFrustum :: proc(left: double, right: double, bottom: double, top: double, znear: double, zfar: double); @dont_mangle @api
|
||||||
|
rlOrtho :: proc(left: double, right: double, bottom: double, top: double, znear: double, zfar: double); @dont_mangle @api
|
||||||
|
rlViewport :: proc(x: int, y: int, width: int, height: int); @dont_mangle @api
|
||||||
|
rlBegin :: proc(mode: int); @dont_mangle @api
|
||||||
|
rlEnd :: proc(); @dont_mangle @api
|
||||||
|
rlVertex2i :: proc(x: int, y: int); @dont_mangle @api
|
||||||
|
rlVertex2f :: proc(x: float, y: float); @dont_mangle @api
|
||||||
|
rlVertex3f :: proc(x: float, y: float, z: float); @dont_mangle @api
|
||||||
|
rlTexCoord2f :: proc(x: float, y: float); @dont_mangle @api
|
||||||
|
rlNormal3f :: proc(x: float, y: float, z: float); @dont_mangle @api
|
||||||
|
rlColor4ub :: proc(r: uchar, g: uchar, b: uchar, a: uchar); @dont_mangle @api
|
||||||
|
rlColor3f :: proc(x: float, y: float, z: float); @dont_mangle @api
|
||||||
|
rlColor4f :: proc(x: float, y: float, z: float, w: float); @dont_mangle @api
|
||||||
|
rlEnableVertexArray :: proc(vaoId: uint): bool; @dont_mangle @api
|
||||||
|
rlDisableVertexArray :: proc(); @dont_mangle @api
|
||||||
|
rlEnableVertexBuffer :: proc(id: uint); @dont_mangle @api
|
||||||
|
rlDisableVertexBuffer :: proc(); @dont_mangle @api
|
||||||
|
rlEnableVertexBufferElement :: proc(id: uint); @dont_mangle @api
|
||||||
|
rlDisableVertexBufferElement :: proc(); @dont_mangle @api
|
||||||
|
rlEnableVertexAttribute :: proc(index: uint); @dont_mangle @api
|
||||||
|
rlDisableVertexAttribute :: proc(index: uint); @dont_mangle @api
|
||||||
|
rlActiveTextureSlot :: proc(slot: int); @dont_mangle @api
|
||||||
|
rlEnableTexture :: proc(id: uint); @dont_mangle @api
|
||||||
|
rlDisableTexture :: proc(); @dont_mangle @api
|
||||||
|
rlEnableTextureCubemap :: proc(id: uint); @dont_mangle @api
|
||||||
|
rlDisableTextureCubemap :: proc(); @dont_mangle @api
|
||||||
|
rlTextureParameters :: proc(id: uint, param: int, value: int); @dont_mangle @api
|
||||||
|
rlCubemapParameters :: proc(id: uint, param: int, value: int); @dont_mangle @api
|
||||||
|
rlEnableShader :: proc(id: uint); @dont_mangle @api
|
||||||
|
rlDisableShader :: proc(); @dont_mangle @api
|
||||||
|
rlEnableFramebuffer :: proc(id: uint); @dont_mangle @api
|
||||||
|
rlDisableFramebuffer :: proc(); @dont_mangle @api
|
||||||
|
rlActiveDrawBuffers :: proc(count: int); @dont_mangle @api
|
||||||
|
rlBlitFramebuffer :: proc(srcX: int, srcY: int, srcWidth: int, srcHeight: int, dstX: int, dstY: int, dstWidth: int, dstHeight: int, bufferMask: int); @dont_mangle @api
|
||||||
|
rlEnableColorBlend :: proc(); @dont_mangle @api
|
||||||
|
rlDisableColorBlend :: proc(); @dont_mangle @api
|
||||||
|
rlEnableDepthTest :: proc(); @dont_mangle @api
|
||||||
|
rlDisableDepthTest :: proc(); @dont_mangle @api
|
||||||
|
rlEnableDepthMask :: proc(); @dont_mangle @api
|
||||||
|
rlDisableDepthMask :: proc(); @dont_mangle @api
|
||||||
|
rlEnableBackfaceCulling :: proc(); @dont_mangle @api
|
||||||
|
rlDisableBackfaceCulling :: proc(); @dont_mangle @api
|
||||||
|
rlSetCullFace :: proc(mode: int); @dont_mangle @api
|
||||||
|
rlEnableScissorTest :: proc(); @dont_mangle @api
|
||||||
|
rlDisableScissorTest :: proc(); @dont_mangle @api
|
||||||
|
rlScissor :: proc(x: int, y: int, width: int, height: int); @dont_mangle @api
|
||||||
|
rlEnableWireMode :: proc(); @dont_mangle @api
|
||||||
|
rlEnablePointMode :: proc(); @dont_mangle @api
|
||||||
|
rlDisableWireMode :: proc(); @dont_mangle @api
|
||||||
|
rlSetLineWidth :: proc(width: float); @dont_mangle @api
|
||||||
|
rlGetLineWidth :: proc(): float; @dont_mangle @api
|
||||||
|
rlEnableSmoothLines :: proc(); @dont_mangle @api
|
||||||
|
rlDisableSmoothLines :: proc(); @dont_mangle @api
|
||||||
|
rlEnableStereoRender :: proc(); @dont_mangle @api
|
||||||
|
rlDisableStereoRender :: proc(); @dont_mangle @api
|
||||||
|
rlIsStereoRenderEnabled :: proc(): bool; @dont_mangle @api
|
||||||
|
rlClearColor :: proc(r: uchar, g: uchar, b: uchar, a: uchar); @dont_mangle @api
|
||||||
|
rlClearScreenBuffers :: proc(); @dont_mangle @api
|
||||||
|
rlCheckErrors :: proc(); @dont_mangle @api
|
||||||
|
rlSetBlendMode :: proc(mode: int); @dont_mangle @api
|
||||||
|
rlSetBlendFactors :: proc(glSrcFactor: int, glDstFactor: int, glEquation: int); @dont_mangle @api
|
||||||
|
rlSetBlendFactorsSeparate :: proc(glSrcRGB: int, glDstRGB: int, glSrcAlpha: int, glDstAlpha: int, glEqRGB: int, glEqAlpha: int); @dont_mangle @api
|
||||||
|
rlglInit :: proc(width: int, height: int); @dont_mangle @api
|
||||||
|
rlglClose :: proc(); @dont_mangle @api
|
||||||
|
rlLoadExtensions :: proc(loader: *void); @dont_mangle @api
|
||||||
|
rlGetVersion :: proc(): int; @dont_mangle @api
|
||||||
|
rlSetFramebufferWidth :: proc(width: int); @dont_mangle @api
|
||||||
|
rlGetFramebufferWidth :: proc(): int; @dont_mangle @api
|
||||||
|
rlSetFramebufferHeight :: proc(height: int); @dont_mangle @api
|
||||||
|
rlGetFramebufferHeight :: proc(): int; @dont_mangle @api
|
||||||
|
rlGetTextureIdDefault :: proc(): uint; @dont_mangle @api
|
||||||
|
rlGetShaderIdDefault :: proc(): uint; @dont_mangle @api
|
||||||
|
rlGetShaderLocsDefault :: proc(): *int; @dont_mangle @api
|
||||||
|
rlLoadRenderBatch :: proc(numBuffers: int, bufferElements: int): rlRenderBatch; @dont_mangle @api
|
||||||
|
rlUnloadRenderBatch :: proc(batch: rlRenderBatch); @dont_mangle @api
|
||||||
|
rlDrawRenderBatch :: proc(batch: *rlRenderBatch); @dont_mangle @api
|
||||||
|
rlSetRenderBatchActive :: proc(batch: *rlRenderBatch); @dont_mangle @api
|
||||||
|
rlDrawRenderBatchActive :: proc(); @dont_mangle @api
|
||||||
|
rlCheckRenderBatchLimit :: proc(vCount: int): bool; @dont_mangle @api
|
||||||
|
rlSetTexture :: proc(id: uint); @dont_mangle @api
|
||||||
|
rlLoadVertexArray :: proc(): uint; @dont_mangle @api
|
||||||
|
rlLoadVertexBuffer :: proc(buffer: *void, size: int, dynamic: bool): uint; @dont_mangle @api
|
||||||
|
rlLoadVertexBufferElement :: proc(buffer: *void, size: int, dynamic: bool): uint; @dont_mangle @api
|
||||||
|
rlUpdateVertexBuffer :: proc(bufferId: uint, data: *void, dataSize: int, offset: int); @dont_mangle @api
|
||||||
|
rlUpdateVertexBufferElements :: proc(id: uint, data: *void, dataSize: int, offset: int); @dont_mangle @api
|
||||||
|
rlUnloadVertexArray :: proc(vaoId: uint); @dont_mangle @api
|
||||||
|
rlUnloadVertexBuffer :: proc(vboId: uint); @dont_mangle @api
|
||||||
|
rlSetVertexAttribute :: proc(index: uint, compSize: int, type: int, normalized: bool, stride: int, pointer: *void); @dont_mangle @api
|
||||||
|
rlSetVertexAttributeDivisor :: proc(index: uint, divisor: int); @dont_mangle @api
|
||||||
|
rlSetVertexAttributeDefault :: proc(locIndex: int, value: *void, attribType: int, count: int); @dont_mangle @api
|
||||||
|
rlDrawVertexArray :: proc(offset: int, count: int); @dont_mangle @api
|
||||||
|
rlDrawVertexArrayElements :: proc(offset: int, count: int, buffer: *void); @dont_mangle @api
|
||||||
|
rlDrawVertexArrayInstanced :: proc(offset: int, count: int, instances: int); @dont_mangle @api
|
||||||
|
rlDrawVertexArrayElementsInstanced :: proc(offset: int, count: int, buffer: *void, instances: int); @dont_mangle @api
|
||||||
|
rlLoadTexture :: proc(data: *void, width: int, height: int, format: int, mipmapCount: int): uint; @dont_mangle @api
|
||||||
|
rlLoadTextureDepth :: proc(width: int, height: int, useRenderBuffer: bool): uint; @dont_mangle @api
|
||||||
|
rlLoadTextureCubemap :: proc(data: *void, size: int, format: int): uint; @dont_mangle @api
|
||||||
|
rlUpdateTexture :: proc(id: uint, offsetX: int, offsetY: int, width: int, height: int, format: int, data: *void); @dont_mangle @api
|
||||||
|
rlGetGlTextureFormats :: proc(format: int, glInternalFormat: *uint, glFormat: *uint, glType: *uint); @dont_mangle @api
|
||||||
|
rlGetPixelFormatName :: proc(format: uint): *char; @dont_mangle @api
|
||||||
|
rlUnloadTexture :: proc(id: uint); @dont_mangle @api
|
||||||
|
rlGenTextureMipmaps :: proc(id: uint, width: int, height: int, format: int, mipmaps: *int); @dont_mangle @api
|
||||||
|
rlReadTexturePixels :: proc(id: uint, width: int, height: int, format: int): *void; @dont_mangle @api
|
||||||
|
rlReadScreenPixels :: proc(width: int, height: int): *uchar; @dont_mangle @api
|
||||||
|
rlLoadFramebuffer :: proc(width: int, height: int): uint; @dont_mangle @api
|
||||||
|
rlFramebufferAttach :: proc(fboId: uint, texId: uint, attachType: int, texType: int, mipLevel: int); @dont_mangle @api
|
||||||
|
rlFramebufferComplete :: proc(id: uint): bool; @dont_mangle @api
|
||||||
|
rlUnloadFramebuffer :: proc(id: uint); @dont_mangle @api
|
||||||
|
rlLoadShaderCode :: proc(vsCode: *char, fsCode: *char): uint; @dont_mangle @api
|
||||||
|
rlCompileShader :: proc(shaderCode: *char, type: int): uint; @dont_mangle @api
|
||||||
|
rlLoadShaderProgram :: proc(vShaderId: uint, fShaderId: uint): uint; @dont_mangle @api
|
||||||
|
rlUnloadShaderProgram :: proc(id: uint); @dont_mangle @api
|
||||||
|
rlGetLocationUniform :: proc(shaderId: uint, uniformName: *char): int; @dont_mangle @api
|
||||||
|
rlGetLocationAttrib :: proc(shaderId: uint, attribName: *char): int; @dont_mangle @api
|
||||||
|
rlSetUniform :: proc(locIndex: int, value: *void, uniformType: int, count: int); @dont_mangle @api
|
||||||
|
rlSetUniformMatrix :: proc(locIndex: int, mat: Matrix); @dont_mangle @api
|
||||||
|
rlSetUniformSampler :: proc(locIndex: int, textureId: uint); @dont_mangle @api
|
||||||
|
rlSetShader :: proc(id: uint, locs: *int); @dont_mangle @api
|
||||||
|
rlLoadComputeShaderProgram :: proc(shaderId: uint): uint; @dont_mangle @api
|
||||||
|
rlComputeShaderDispatch :: proc(groupX: uint, groupY: uint, groupZ: uint); @dont_mangle @api
|
||||||
|
rlLoadShaderBuffer :: proc(size: uint, data: *void, usageHint: int): uint; @dont_mangle @api
|
||||||
|
rlUnloadShaderBuffer :: proc(ssboId: uint); @dont_mangle @api
|
||||||
|
rlUpdateShaderBuffer :: proc(id: uint, data: *void, dataSize: uint, offset: uint); @dont_mangle @api
|
||||||
|
rlBindShaderBuffer :: proc(id: uint, index: uint); @dont_mangle @api
|
||||||
|
rlReadShaderBuffer :: proc(id: uint, dest: *void, count: uint, offset: uint); @dont_mangle @api
|
||||||
|
rlCopyShaderBuffer :: proc(destId: uint, srcId: uint, destOffset: uint, srcOffset: uint, count: uint); @dont_mangle @api
|
||||||
|
rlGetShaderBufferSize :: proc(id: uint): uint; @dont_mangle @api
|
||||||
|
rlBindImageTexture :: proc(id: uint, index: uint, format: int, readonly: bool); @dont_mangle @api
|
||||||
|
rlGetMatrixModelview :: proc(): Matrix; @dont_mangle @api
|
||||||
|
rlGetMatrixProjection :: proc(): Matrix; @dont_mangle @api
|
||||||
|
rlGetMatrixTransform :: proc(): Matrix; @dont_mangle @api
|
||||||
|
rlGetMatrixProjectionStereo :: proc(eye: int): Matrix; @dont_mangle @api
|
||||||
|
rlGetMatrixViewOffsetStereo :: proc(eye: int): Matrix; @dont_mangle @api
|
||||||
|
rlSetMatrixProjection :: proc(proj: Matrix); @dont_mangle @api
|
||||||
|
rlSetMatrixModelview :: proc(view: Matrix); @dont_mangle @api
|
||||||
|
rlSetMatrixProjectionStereo :: proc(right: Matrix, left: Matrix); @dont_mangle @api
|
||||||
|
rlSetMatrixViewOffsetStereo :: proc(right: Matrix, left: Matrix); @dont_mangle @api
|
||||||
|
rlLoadDrawCube :: proc(); @dont_mangle @api
|
||||||
|
rlLoadDrawQuad :: proc(); @dont_mangle @api
|
||||||
12
pkgs/std_types/c_types.lc
Normal file
12
pkgs/std_types/c_types.lc
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#build_if(LC_GEN == GEN_C);
|
||||||
|
#`#include <stdint.h>`;
|
||||||
|
|
||||||
|
i8 :: typedef char; @foreign(int8_t)
|
||||||
|
i16 :: typedef short; @foreign(int16_t)
|
||||||
|
i32 :: typedef int; @foreign(int32_t)
|
||||||
|
i64 :: typedef llong; @foreign(int64_t)
|
||||||
|
|
||||||
|
u8 :: typedef uchar; @foreign(uint8_t)
|
||||||
|
u16 :: typedef ushort; @foreign(uint16_t)
|
||||||
|
u32 :: typedef uint; @foreign(uint32_t)
|
||||||
|
u64 :: typedef ullong; @foreign(uint64_t)
|
||||||
11
pkgs/std_types/non_c_types.lc
Normal file
11
pkgs/std_types/non_c_types.lc
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#build_if(LC_GEN != GEN_C);
|
||||||
|
|
||||||
|
i8 :: typedef char;
|
||||||
|
i16 :: typedef short;
|
||||||
|
i32 :: typedef int;
|
||||||
|
i64 :: typedef llong;
|
||||||
|
|
||||||
|
u8 :: typedef uchar;
|
||||||
|
u16 :: typedef ushort;
|
||||||
|
u32 :: typedef uint;
|
||||||
|
u64 :: typedef ullong;
|
||||||
44
pkgs/std_types/types.lc
Normal file
44
pkgs/std_types/types.lc
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#static_assert(sizeof(:i8) == 1);
|
||||||
|
#static_assert(sizeof(:i16) == 2);
|
||||||
|
#static_assert(sizeof(:i32) == 4);
|
||||||
|
#static_assert(sizeof(:i64) == 8);
|
||||||
|
#static_assert(sizeof(:u8) == 1);
|
||||||
|
#static_assert(sizeof(:u16) == 2);
|
||||||
|
#static_assert(sizeof(:u32) == 4);
|
||||||
|
#static_assert(sizeof(:u64) == 8);
|
||||||
|
|
||||||
|
f32 :: typedef float; @weak @foreign
|
||||||
|
f64 :: typedef double; @weak @foreign
|
||||||
|
|
||||||
|
UINT64_MAX :: 0xffffffffffffffff;
|
||||||
|
UINT64_MIN :: 0;
|
||||||
|
UINT32_MAX :: 0xffffffff;
|
||||||
|
UINT32_MIN :: 0;
|
||||||
|
UINT16_MAX :: 0xffff;
|
||||||
|
UINT16_MIN :: 0;
|
||||||
|
UINT8_MAX :: 0xff;
|
||||||
|
UINT8_MIN :: 0;
|
||||||
|
INT64_MAX :: 9223372036854775807;
|
||||||
|
INT64_MIN :: -9223372036854775808;
|
||||||
|
INT32_MAX :: 2147483647;
|
||||||
|
INT32_MIN :: -2147483648;
|
||||||
|
INT16_MAX :: 32767;
|
||||||
|
INT16_MIN :: -32768;
|
||||||
|
INT8_MAX :: 127;
|
||||||
|
INT8_MIN :: -128;
|
||||||
|
CHAR_MAX :: INT8_MAX;
|
||||||
|
CHAR_MIN :: INT8_MIN;
|
||||||
|
UCHAR_MAX :: UINT8_MAX;
|
||||||
|
UCHAR_MIN :: UINT8_MIN;
|
||||||
|
SHORT_MAX :: INT16_MAX;
|
||||||
|
SHORT_MIN :: INT16_MIN;
|
||||||
|
USHORT_MAX :: UINT16_MAX;
|
||||||
|
USHORT_MIN :: UINT16_MIN;
|
||||||
|
INT_MAX :: INT32_MAX;
|
||||||
|
INT_MIN :: INT32_MIN;
|
||||||
|
UINT_MAX :: UINT32_MAX;
|
||||||
|
UINT_MIN :: UINT32_MIN;
|
||||||
|
LLONG_MAX :: INT64_MAX;
|
||||||
|
LLONG_MIN :: INT64_MIN;
|
||||||
|
ULLONG_MAX :: UINT64_MAX;
|
||||||
|
ULLONG_MIN :: UINT64_MIN;
|
||||||
9
pkgs/std_types/windows_types.lc
Normal file
9
pkgs/std_types/windows_types.lc
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#build_if(LC_OS == OS_WINDOWS);
|
||||||
|
|
||||||
|
#static_assert(sizeof(:long) == 4);
|
||||||
|
|
||||||
|
LONG_MAX :: INT32_MAX;
|
||||||
|
LONG_MIN :: INT32_MIN;
|
||||||
|
ULONG_MAX :: UINT32_MAX;
|
||||||
|
ULONG_MIN :: UINT32_MIN;
|
||||||
|
|
||||||
8
pkgs/std_types/x64_linux_types.lc
Normal file
8
pkgs/std_types/x64_linux_types.lc
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#build_if((LC_OS == OS_MAC || LC_OS == OS_LINUX) && LC_ARCH == ARCH_X64);
|
||||||
|
|
||||||
|
#static_assert(sizeof(:long) == 8);
|
||||||
|
|
||||||
|
LONG_MAX :: INT64_MAX;
|
||||||
|
LONG_MIN :: INT64_MIN;
|
||||||
|
ULONG_MAX :: UINT64_MAX;
|
||||||
|
ULONG_MIN :: UINT64_MIN;
|
||||||
15
pkgs/std_types/x64_types.lc
Normal file
15
pkgs/std_types/x64_types.lc
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#build_if(LC_ARCH == ARCH_X64);
|
||||||
|
|
||||||
|
usize :: typedef u64; @foreign(size_t)
|
||||||
|
isize :: typedef i64; @foreign(intptr_t)
|
||||||
|
intptr :: typedef i64; @foreign(intptr_t)
|
||||||
|
uintptr :: typedef u64; @foreign(uintptr_t)
|
||||||
|
|
||||||
|
INTPTR_MAX :: INT64_MAX;
|
||||||
|
INTPTR_MIN :: INT64_MIN;
|
||||||
|
UINTPTR_MAX :: UINT64_MAX;
|
||||||
|
UINTPTR_MIN :: UINT64_MIN;
|
||||||
|
ISIZE_MAX :: INT64_MAX;
|
||||||
|
ISIZE_MIN :: INT64_MIN;
|
||||||
|
USIZE_MAX :: UINT64_MAX;
|
||||||
|
USIZE_MIN :: UINT64_MIN;
|
||||||
22
pkgs/std_types/x86_types.lc
Normal file
22
pkgs/std_types/x86_types.lc
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#build_if(LC_ARCH == ARCH_X86);
|
||||||
|
|
||||||
|
#static_assert(sizeof(:long) == 4);
|
||||||
|
|
||||||
|
LONG_MAX :: INT32_MAX;
|
||||||
|
LONG_MIN :: INT32_MIN;
|
||||||
|
ULONG_MAX :: UINT32_MAX;
|
||||||
|
ULONG_MIN :: UINT32_MIN;
|
||||||
|
|
||||||
|
usize :: typedef u32; @foreign(size_t)
|
||||||
|
isize :: typedef i32; @foreign(intptr_t)
|
||||||
|
intptr :: typedef i32; @foreign(intptr_t)
|
||||||
|
uintptr :: typedef u32; @foreign(uintptr_t)
|
||||||
|
|
||||||
|
INTPTR_MAX :: INT32_MAX;
|
||||||
|
INTPTR_MIN :: INT32_MIN;
|
||||||
|
UINTPTR_MAX :: UINT32_MAX;
|
||||||
|
UINTPTR_MIN :: UINT32_MIN;
|
||||||
|
ISIZE_MAX :: INT32_MAX;
|
||||||
|
ISIZE_MIN :: INT32_MIN;
|
||||||
|
USIZE_MAX :: UINT32_MAX;
|
||||||
|
USIZE_MIN :: UINT32_MIN;
|
||||||
125
src/build_file/ast_verify.cpp
Normal file
125
src/build_file/ast_verify.cpp
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
thread_local LC_Map DebugASTMap;
|
||||||
|
|
||||||
|
void DebugVerify_WalkAST(LC_ASTWalker *ctx, LC_AST *n) {
|
||||||
|
bool created = LC_InsertWithoutReplace(&DebugASTMap, n, (void *)(intptr_t)100);
|
||||||
|
if (!created) LC_ReportASTError(n, "we got to the same ast node twice using ast walker");
|
||||||
|
// AST_Decl doesn't need to hold types, it's just a standin for LC_Decl
|
||||||
|
if (LC_IsDecl(n)) {
|
||||||
|
if (n->type) {
|
||||||
|
LC_ReportASTError(n, "decl holds type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LC_IsStmt(n)) {
|
||||||
|
// These might pop up in a for loop as expressions
|
||||||
|
if (n->kind == LC_ASTKind_StmtVar || n->kind == LC_ASTKind_StmtConst || n->kind == LC_ASTKind_StmtAssign || n->kind == LC_ASTKind_StmtExpr) {
|
||||||
|
if (!n->type) {
|
||||||
|
LC_ReportASTError(n, "typed stmt doesn't hold type");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (n->type) {
|
||||||
|
LC_ReportASTError(n, "stmt holds type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LC_IsType(n) && ctx->inside_discarded == 0) {
|
||||||
|
if (!n->type) {
|
||||||
|
LC_ReportASTError(n, "typespec doesn't hold type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LC_IsExpr(n)) {
|
||||||
|
bool parent_is_const = false;
|
||||||
|
bool parent_is_builtin = false;
|
||||||
|
for (int i = ctx->stack.len - 1; i >= 0; i -= 1) {
|
||||||
|
LC_AST *parent = ctx->stack.data[i];
|
||||||
|
if (parent->kind == LC_ASTKind_DeclConst || parent->kind == LC_ASTKind_StmtConst || parent->const_val.type) {
|
||||||
|
parent_is_const = true;
|
||||||
|
}
|
||||||
|
if (parent->kind == LC_ASTKind_ExprBuiltin) {
|
||||||
|
parent_is_builtin = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool should_have_type = parent_is_builtin == false && ctx->inside_note == 0 && ctx->inside_discarded == 0;
|
||||||
|
if (should_have_type && !n->type) {
|
||||||
|
LC_ReportASTError(n, "expression doesn't have type");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_have_type && !parent_is_const) {
|
||||||
|
bool type_is_ok = !LC_IsUntyped(n->type) || n->type == L->tuntypedbool;
|
||||||
|
if (!type_is_ok) {
|
||||||
|
LC_ReportASTError(n, "expression is still untyped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_have_type && n->kind == LC_ASTKind_ExprIdent) {
|
||||||
|
if (n->eident.resolved_decl == NULL) {
|
||||||
|
LC_ReportASTError(n, "identifier doesn't hold resolved declaration");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifyCopy_Walk(LC_ASTWalker *ctx, LC_AST *n) {
|
||||||
|
int *counter = (int *)ctx->user_data;
|
||||||
|
counter[0] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifyASTCopy(LC_ASTRefList packages) {
|
||||||
|
for (LC_ASTRef *it = packages.first; it; it = it->next) {
|
||||||
|
LC_ASTFor(file, it->ast->apackage.ffile) {
|
||||||
|
LC_TempArena c = LC_BeginTemp(L->arena);
|
||||||
|
LC_AST *copy = LC_CopyAST(L->arena, file);
|
||||||
|
|
||||||
|
int copy_counter = 0;
|
||||||
|
int original_counter = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
LC_ASTWalker walker = LC_GetDefaultWalker(L->arena, VerifyCopy_Walk);
|
||||||
|
walker.visit_notes = walker.visit_discarded = true;
|
||||||
|
walker.user_data = (void *)©_counter;
|
||||||
|
LC_WalkAST(&walker, copy);
|
||||||
|
walker.user_data = (void *)&original_counter;
|
||||||
|
LC_WalkAST(&walker, copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
IO_Assert(copy_counter == original_counter);
|
||||||
|
IO_Assert(copy_counter > 1);
|
||||||
|
IO_Assert(original_counter > 1);
|
||||||
|
|
||||||
|
LC_EndTemp(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugVerifyAST(LC_ASTRefList packages) {
|
||||||
|
LC_TempArena checkpoint = LC_BeginTemp(L->arena);
|
||||||
|
|
||||||
|
DebugASTMap = {L->arena};
|
||||||
|
LC_MapReserve(&DebugASTMap, 4096);
|
||||||
|
|
||||||
|
LC_ASTWalker walker = LC_GetDefaultWalker(L->arena, DebugVerify_WalkAST);
|
||||||
|
walker.visit_notes = walker.visit_discarded = true;
|
||||||
|
for (LC_ASTRef *it = packages.first; it; it = it->next) {
|
||||||
|
LC_WalkAST(&walker, it->ast);
|
||||||
|
}
|
||||||
|
LC_WalkAST(&walker, L->tstring->decl->ast);
|
||||||
|
LC_WalkAST(&walker, L->tany->decl->ast);
|
||||||
|
|
||||||
|
for (int i = 0; i < L->ast_count; i += 1) {
|
||||||
|
LC_AST *it = (LC_AST *)L->ast_arena->memory.data + i;
|
||||||
|
if (it == L->builtin_package) continue;
|
||||||
|
if (it->kind == LC_ASTKind_Ignore) continue;
|
||||||
|
intptr_t value = (intptr_t)LC_MapGetP(&DebugASTMap, it);
|
||||||
|
|
||||||
|
if (value != 100) {
|
||||||
|
LC_ReportASTError(it, "marking verification failed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LC_MapClear(&DebugASTMap);
|
||||||
|
|
||||||
|
VerifyASTCopy(packages);
|
||||||
|
LC_EndTemp(checkpoint);
|
||||||
|
}
|
||||||
166
src/build_file/package_compiler.cpp
Normal file
166
src/build_file/package_compiler.cpp
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
bool Expand(MA_Arena *arena, S8_List *out, S8_String filepath) {
|
||||||
|
S8_String c = OS_ReadFile(arena, filepath);
|
||||||
|
if (c.str == 0) return false;
|
||||||
|
// S8_String name = S8_SkipToLastSlash(filepath);
|
||||||
|
// S8_AddF(arena, out, "// %.*s\n", S8_Expand(name));
|
||||||
|
|
||||||
|
S8_String path = S8_ChopLastSlash(filepath);
|
||||||
|
S8_String include = S8_Lit("#include \"");
|
||||||
|
for (;;) {
|
||||||
|
int64_t idx = -1;
|
||||||
|
if (S8_Seek(c, include, 0, &idx)) {
|
||||||
|
S8_String str_to_add = S8_GetPrefix(c, idx);
|
||||||
|
S8_AddNode(arena, out, str_to_add);
|
||||||
|
S8_String save = c;
|
||||||
|
c = S8_Skip(c, idx + include.len);
|
||||||
|
|
||||||
|
S8_String filename = c;
|
||||||
|
filename.len = 0;
|
||||||
|
while (filename.str[filename.len] != '"' && filename.len < c.len) {
|
||||||
|
filename.len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = S8_Skip(c, filename.len + 1);
|
||||||
|
S8_String inc_path = S8_Format(arena, "%.*s/%.*s", S8_Expand(path), S8_Expand(filename));
|
||||||
|
|
||||||
|
bool dont_expand_and_remove_include = false;
|
||||||
|
if (S8_EndsWith(inc_path, "lib_compiler.h")) dont_expand_and_remove_include = true;
|
||||||
|
|
||||||
|
if (!dont_expand_and_remove_include) {
|
||||||
|
if (!Expand(arena, out, inc_path)) {
|
||||||
|
S8_String s = S8_GetPrefix(save, save.len - c.len);
|
||||||
|
S8_AddNode(arena, out, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
S8_AddNode(arena, out, c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_String ExpandIncludes(MA_Arena *arena, S8_String filepath) {
|
||||||
|
S8_List out = S8_MakeEmptyList();
|
||||||
|
S8_String result = S8_MakeEmpty();
|
||||||
|
Expand(arena, &out, filepath);
|
||||||
|
result = S8_Merge(arena, out);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PackageCompiler() {
|
||||||
|
MA_Scratch scratch;
|
||||||
|
S8_String header = ExpandIncludes(scratch, S8_Lit("../src/compiler/lib_compiler.h"));
|
||||||
|
S8_String impl = ExpandIncludes(scratch, S8_Lit("../src/compiler/lib_compiler.c"));
|
||||||
|
|
||||||
|
S8_List list = {0};
|
||||||
|
S8_AddF(scratch, &list, R"FOO(/*
|
||||||
|
This is a compiler frontend in a single-header-file library form.
|
||||||
|
This is a **beta** so things may change between versions!
|
||||||
|
|
||||||
|
# How to use
|
||||||
|
|
||||||
|
In *one* of your C or C++ files to create the implementation:
|
||||||
|
|
||||||
|
```
|
||||||
|
#define LIB_COMPILER_IMPLEMENTATION
|
||||||
|
#include "lib_compiler.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
In the rest of your files you can just include it like a regular
|
||||||
|
header.
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
See online repository for code examples
|
||||||
|
|
||||||
|
# Overrides
|
||||||
|
|
||||||
|
You can override libc calls, the arena implementation using
|
||||||
|
preprocessor at compile time, here is an example of how you
|
||||||
|
would go about it:
|
||||||
|
|
||||||
|
```
|
||||||
|
#define LC_vsnprintf stbsp_vsnprintf
|
||||||
|
#define LC_MemoryZero(p, size) __builtin_memset(p, 0, size);
|
||||||
|
#define LC_MemoryCopy(dst, src, size) __builtin_memcpy(dst, src, size)
|
||||||
|
|
||||||
|
#define LIB_COMPILER_IMPLEMENTATION
|
||||||
|
#include "lib_compiler.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
Look for '@override' to find things that can be overridden using macro preprocessor
|
||||||
|
Look for '@api' to find the main functions that you are supposed to use
|
||||||
|
Look for '@configurable' to find runtime callbacks you can register and other settings
|
||||||
|
|
||||||
|
# License (MIT)
|
||||||
|
|
||||||
|
See end of file
|
||||||
|
|
||||||
|
*/
|
||||||
|
)FOO");
|
||||||
|
S8_AddNode(scratch, &list, header);
|
||||||
|
S8_AddF(scratch, &list, "\n#ifdef LIB_COMPILER_IMPLEMENTATION\n");
|
||||||
|
S8_AddNode(scratch, &list, impl);
|
||||||
|
S8_AddF(scratch, &list, "\n#endif // LIB_COMPILER_IMPLEMENTATION\n");
|
||||||
|
S8_AddF(scratch, &list, R"FOO(
|
||||||
|
/*
|
||||||
|
Copyright (c) 2024 Krzosa Karol
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
)FOO");
|
||||||
|
S8_String result = S8_Merge(scratch, list);
|
||||||
|
|
||||||
|
if (PrintAllFunctions) {
|
||||||
|
S8_List lines = S8_Split(Perm, result, S8_Lit("\n"), 0);
|
||||||
|
S8_For(_line, lines) {
|
||||||
|
S8_String line = _line->string;
|
||||||
|
|
||||||
|
if (!S8_Seek(line, "{", S8_FindFlag_MatchFindLast)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t idx = 0;
|
||||||
|
if (S8_Seek(line, ")", 0, &idx)) {
|
||||||
|
line.len = idx + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_String proc_name = line;
|
||||||
|
if (S8_Seek(line, "(", 0, &idx)) {
|
||||||
|
proc_name.len = idx;
|
||||||
|
for (int64_t i = proc_name.len - 1; i > 0; i -= 1) {
|
||||||
|
bool is_ident = CHAR_IsIdent(proc_name.str[i]) || CHAR_IsDigit(proc_name.str[i]);
|
||||||
|
if (!is_ident) {
|
||||||
|
proc_name = S8_Skip(proc_name, i + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S8_StartsWith(line, "LC_FUNCTION", false)) {
|
||||||
|
if (PrintAllFunctions) IO_Printf("%.*s;\n", S8_Expand(line));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_WriteFile(S8_Lit("../lib_compiler.h"), result);
|
||||||
|
}
|
||||||
37
src/build_file/test_readme.cpp
Normal file
37
src/build_file/test_readme.cpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
void ReadmeReadFile(LC_AST *package, LoadedFile *file) {
|
||||||
|
S8_String result = {};
|
||||||
|
if (package->apackage.name == LC_ILit("readme_test")) {
|
||||||
|
S8_String code = *(S8_String *)L->user_data;
|
||||||
|
file->content = S8_Copy(L->arena, code);
|
||||||
|
} else {
|
||||||
|
file->content = OS_ReadFile(L->arena, file->path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestReadme() {
|
||||||
|
MA_Scratch scratch;
|
||||||
|
|
||||||
|
S8_String readme_path = OS_GetAbsolutePath(scratch, "../README.md");
|
||||||
|
S8_String readme = OS_ReadFile(scratch, readme_path);
|
||||||
|
S8_List split = S8_Split(scratch, readme, "```");
|
||||||
|
S8_For(it, split) {
|
||||||
|
if (S8_StartsWith(it->string, " odin", S8_IgnoreCase)) {
|
||||||
|
S8_String code = S8_Skip(it->string, 5);
|
||||||
|
|
||||||
|
LC_Lang *lang = LC_LangAlloc();
|
||||||
|
lang->use_colored_terminal_output = UseColoredIO;
|
||||||
|
lang->user_data = (void *)&code;
|
||||||
|
lang->on_file_load = ReadmeReadFile;
|
||||||
|
LC_LangBegin(lang);
|
||||||
|
LC_DeclareNote(LC_ILit("do_something"));
|
||||||
|
LC_DeclareNote(LC_ILit("serialize"));
|
||||||
|
defer { LC_LangEnd(lang); };
|
||||||
|
|
||||||
|
LC_RegisterPackageDir("../pkgs");
|
||||||
|
LC_Intern name = LC_ILit("readme_test");
|
||||||
|
LC_AddSingleFilePackage(name, readme_path);
|
||||||
|
LC_ResolvePackageByName(name);
|
||||||
|
// LC_FindUnusedLocalsAndRemoveUnusedGlobalDecls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
463
src/build_file/testsuite.cpp
Normal file
463
src/build_file/testsuite.cpp
Normal file
@@ -0,0 +1,463 @@
|
|||||||
|
|
||||||
|
enum TestResult {
|
||||||
|
OK,
|
||||||
|
Failed_Lex,
|
||||||
|
Failed_Parse,
|
||||||
|
Failed_Resolve,
|
||||||
|
Failed_CCompile,
|
||||||
|
Failed_Run,
|
||||||
|
Failed_Condition,
|
||||||
|
Failed_Package,
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *TestResultString[] = {
|
||||||
|
"OK",
|
||||||
|
"Failed_Lex",
|
||||||
|
"Failed_Parse",
|
||||||
|
"Failed_Resolve",
|
||||||
|
"Failed_CCompile",
|
||||||
|
"Failed_Run",
|
||||||
|
"Failed_Condition",
|
||||||
|
"Failed_Package",
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Test {
|
||||||
|
char *path;
|
||||||
|
bool msg_found[256];
|
||||||
|
char *msg_expected[256];
|
||||||
|
int msg_count;
|
||||||
|
TestResult expected_result;
|
||||||
|
bool dont_run;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Task {
|
||||||
|
bool do_run;
|
||||||
|
S8_String exe;
|
||||||
|
S8_String cmd;
|
||||||
|
Process process;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum TestKind {
|
||||||
|
TestKind_File,
|
||||||
|
TestKind_Dir,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TestDesc {
|
||||||
|
TestKind kind;
|
||||||
|
S8_String absolute_path;
|
||||||
|
S8_String filename;
|
||||||
|
S8_String package_name;
|
||||||
|
bool dont_run;
|
||||||
|
};
|
||||||
|
|
||||||
|
TestResult Compile(S8_String name, S8_String file_to_compile) {
|
||||||
|
TestResult result = OK;
|
||||||
|
int errcode = 0;
|
||||||
|
Array<Task> tasks = {MA_GetAllocator(Perm)};
|
||||||
|
|
||||||
|
{
|
||||||
|
Task tcc = {};
|
||||||
|
tcc.do_run = UseTCC;
|
||||||
|
tcc.exe = Fmt("%.*s/tcc_%.*s.exe", S8_Expand(name), S8_Expand(name));
|
||||||
|
tcc.cmd = Fmt("tcc -std=c99 %.*s " IF_MAC("-lm") IF_LINUX("-lm") " -o %.*s", S8_Expand(file_to_compile), S8_Expand(tcc.exe));
|
||||||
|
tasks.add(tcc);
|
||||||
|
|
||||||
|
Task clang = {};
|
||||||
|
clang.do_run = UseClang;
|
||||||
|
clang.exe = Fmt("%.*s/clang_%.*s.exe", S8_Expand(name), S8_Expand(name));
|
||||||
|
clang.cmd = Fmt("clang -Wno-string-plus-int -g -std=c99 " IF_MAC("-lm") IF_LINUX("-lm") " %.*s -o %.*s", S8_Expand(file_to_compile), S8_Expand(clang.exe));
|
||||||
|
tasks.add(clang);
|
||||||
|
|
||||||
|
Task cl = {};
|
||||||
|
cl.do_run = UseCL;
|
||||||
|
cl.exe = Fmt("%.*s/cl_%.*s.exe", S8_Expand(name), S8_Expand(name));
|
||||||
|
cl.cmd = Fmt("cl %.*s -Zi -std:c11 -nologo /Fd:%.*s.pdb -FC -Fe:%.*s", S8_Expand(file_to_compile), S8_Expand(file_to_compile), S8_Expand(cl.exe));
|
||||||
|
tasks.add(cl);
|
||||||
|
|
||||||
|
Task gcc = {};
|
||||||
|
gcc.do_run = UseGCC;
|
||||||
|
gcc.exe = Fmt("%.*s/gcc_%.*s.exe", S8_Expand(name), S8_Expand(name));
|
||||||
|
gcc.cmd = Fmt("gcc -Wno-string-plus-int -g -std=c99 " IF_MAC("-lm") IF_LINUX("-lm") " %.*s -o %.*s", S8_Expand(file_to_compile), S8_Expand(gcc.exe));
|
||||||
|
tasks.add(gcc);
|
||||||
|
}
|
||||||
|
|
||||||
|
For(tasks) {
|
||||||
|
if (!it.do_run) continue;
|
||||||
|
errcode = Run(it.cmd);
|
||||||
|
if (errcode != 0) {
|
||||||
|
result = Failed_CCompile;
|
||||||
|
goto end_of_test;
|
||||||
|
}
|
||||||
|
it.exe = OS_GetAbsolutePath(Perm, it.exe);
|
||||||
|
}
|
||||||
|
|
||||||
|
For(tasks) {
|
||||||
|
if (!it.do_run) continue;
|
||||||
|
it.process = RunEx(it.exe);
|
||||||
|
}
|
||||||
|
|
||||||
|
For(tasks) {
|
||||||
|
if (!it.do_run) continue;
|
||||||
|
errcode += Wait(&it.process);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errcode != 0) {
|
||||||
|
result = Failed_Run;
|
||||||
|
goto end_of_test;
|
||||||
|
}
|
||||||
|
end_of_test:
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local Test *T;
|
||||||
|
thread_local Array<S8_String> Messages;
|
||||||
|
|
||||||
|
void HandleOutputMessageGlobalTest(LC_Token *pos, char *str, int len) {
|
||||||
|
S8_String s = S8_Make(str, len);
|
||||||
|
s = S8_Trim(s);
|
||||||
|
|
||||||
|
if (S8_StartsWith(s, S8_Lit("Executing: "), 0)) return;
|
||||||
|
if (S8_StartsWith(s, S8_Lit("> "), 0)) return;
|
||||||
|
if (T && T->msg_expected[0]) {
|
||||||
|
for (int i = 0; i < 256; i += 1) {
|
||||||
|
if (T->msg_expected[i] == 0) break;
|
||||||
|
|
||||||
|
char *str = T->msg_expected[i];
|
||||||
|
bool *found = T->msg_found + i;
|
||||||
|
if (!found[0]) {
|
||||||
|
S8_String expected = S8_MakeFromChar(str);
|
||||||
|
if (S8_Seek(s, expected, S8_FindFlag_IgnoreCase, NULL)) {
|
||||||
|
found[0] = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos) s = S8_Format(Perm, "%s(%d,%d): error: %.*s\n", (char *)pos->lex->file, pos->line, pos->column, len, str);
|
||||||
|
Messages.add(S8_Copy(Perm, s));
|
||||||
|
}
|
||||||
|
|
||||||
|
TestResult PrintGlobalTestResult(char *path, TestResult result) {
|
||||||
|
const char *red = "\033[31m";
|
||||||
|
const char *reset = "\033[0m";
|
||||||
|
const char *green = "\033[32m";
|
||||||
|
if (!UseColoredIO) {
|
||||||
|
red = "";
|
||||||
|
reset = "";
|
||||||
|
green = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
TestResult actual_result = result;
|
||||||
|
if (result == T->expected_result) {
|
||||||
|
result = OK;
|
||||||
|
} else {
|
||||||
|
result = Failed_Condition;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (T->msg_expected[0]) {
|
||||||
|
bool all_found = true;
|
||||||
|
for (int i = 0; i < T->msg_count; i += 1) {
|
||||||
|
if (T->msg_expected[i] == 0) {
|
||||||
|
IO_Printf("%sCount of expected messages doesn't match expected messages%s\n", red, reset);
|
||||||
|
all_found = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!T->msg_found[i]) {
|
||||||
|
all_found = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!all_found) {
|
||||||
|
result = Failed_Condition;
|
||||||
|
|
||||||
|
for (int i = 0; i < 256; i += 1) {
|
||||||
|
char *str = T->msg_expected[i];
|
||||||
|
if (str == 0) break;
|
||||||
|
|
||||||
|
const char *color = green;
|
||||||
|
if (!T->msg_found[i]) color = red;
|
||||||
|
IO_Printf("%sMessage not found - %s%s\n", color, T->msg_expected[i], reset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (T->msg_count != Messages.len) {
|
||||||
|
IO_Printf("%sCount of expected messages: %d doesn't match actual message count: %d%s\n", red, T->msg_count, Messages.len, reset);
|
||||||
|
result = Failed_Condition;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *color = green;
|
||||||
|
if (result != OK) {
|
||||||
|
color = red;
|
||||||
|
IO_Printf("%-50s - %s Expected result: %s, got instead: %s%s\n", path, color, TestResultString[T->expected_result], TestResultString[actual_result], reset);
|
||||||
|
} else {
|
||||||
|
IO_Printf("%-50s - %s%s%s\n", path, color, TestResultString[result], reset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != OK) {
|
||||||
|
For(Messages) IO_Printf("%.*s", S8_Expand(it));
|
||||||
|
}
|
||||||
|
Messages.reset();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParseErrorNotationAndFillGlobalTest(S8_String s) {
|
||||||
|
S8_String count_lit = S8_Lit("// #expected_error_count: ");
|
||||||
|
S8_String errlit = S8_Lit("// #error: ");
|
||||||
|
S8_String resultlit = S8_Lit("// #failed: ");
|
||||||
|
S8_String dont_run_lit = S8_Lit("// #dont_run");
|
||||||
|
S8_List errors = S8_Split(Perm, s, S8_Lit("\n"), 0);
|
||||||
|
S8_For(it, errors) {
|
||||||
|
if (S8_StartsWith(it->string, errlit, 0)) {
|
||||||
|
S8_String error = S8_Skip(it->string, errlit.len);
|
||||||
|
error = S8_Trim(error);
|
||||||
|
error = S8_Copy(Perm, error);
|
||||||
|
T->msg_expected[T->msg_count++] = error.str;
|
||||||
|
}
|
||||||
|
if (S8_StartsWith(it->string, resultlit, 0)) {
|
||||||
|
if (S8_Seek(it->string, S8_Lit("parse"), 0, 0)) {
|
||||||
|
T->expected_result = Failed_Parse;
|
||||||
|
}
|
||||||
|
if (S8_Seek(it->string, S8_Lit("resolve"), 0, 0)) {
|
||||||
|
T->expected_result = Failed_Resolve;
|
||||||
|
}
|
||||||
|
if (S8_Seek(it->string, S8_Lit("lex"), 0, 0)) {
|
||||||
|
T->expected_result = Failed_Lex;
|
||||||
|
}
|
||||||
|
if (S8_Seek(it->string, S8_Lit("package"), 0, 0)) {
|
||||||
|
T->expected_result = Failed_Package;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (S8_StartsWith(it->string, count_lit, 0)) {
|
||||||
|
S8_String count = S8_Skip(it->string, count_lit.len);
|
||||||
|
count = S8_Copy(Perm, count);
|
||||||
|
T->msg_count = atoi(count.str);
|
||||||
|
}
|
||||||
|
if (S8_StartsWith(it->string, dont_run_lit, 0)) {
|
||||||
|
T->dont_run = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_String PushDir(S8_String dir_name) {
|
||||||
|
S8_String prev = OS_GetWorkingDir(Perm);
|
||||||
|
OS_MakeDir(dir_name);
|
||||||
|
OS_SetWorkingDir(dir_name);
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunTestFile(TestDesc it) {
|
||||||
|
LC_Lang *lang = LC_LangAlloc();
|
||||||
|
lang->on_message = HandleOutputMessageGlobalTest;
|
||||||
|
lang->use_colored_terminal_output = UseColoredIO;
|
||||||
|
lang->breakpoint_on_error = BreakpointOnError;
|
||||||
|
LC_LangBegin(lang);
|
||||||
|
|
||||||
|
Test test = {};
|
||||||
|
test.dont_run = it.dont_run;
|
||||||
|
T = &test;
|
||||||
|
TestResult result = OK;
|
||||||
|
|
||||||
|
S8_String s = OS_ReadFile(L->arena, it.absolute_path);
|
||||||
|
if (s.len == 0) {
|
||||||
|
IO_FatalErrorf("Failed to open file %.*s", S8_Expand(it.absolute_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseErrorNotationAndFillGlobalTest(s);
|
||||||
|
|
||||||
|
S8_String code = {};
|
||||||
|
S8_String out_path = {};
|
||||||
|
|
||||||
|
LC_RegisterPackageDir("../../pkgs");
|
||||||
|
|
||||||
|
LC_Intern name = LC_ILit("testing");
|
||||||
|
LC_AddSingleFilePackage(name, it.absolute_path);
|
||||||
|
|
||||||
|
L->first_package = name;
|
||||||
|
LC_ParsePackagesUsingRegistry(name);
|
||||||
|
if (L->errors) {
|
||||||
|
result = Failed_Parse;
|
||||||
|
goto end_of_test;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_OrderAndResolveTopLevelDecls(name);
|
||||||
|
LC_ResolveAllProcBodies();
|
||||||
|
LC_FindUnusedLocalsAndRemoveUnusedGlobalDecls();
|
||||||
|
if (L->errors) {
|
||||||
|
result = Failed_Resolve;
|
||||||
|
goto end_of_test;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugVerifyAST(L->ordered_packages);
|
||||||
|
|
||||||
|
OS_MakeDir(it.filename);
|
||||||
|
code = LC_GenerateUnityBuild(L->ordered_packages);
|
||||||
|
out_path = S8_Format(L->arena, "%.*s/%.*s.c", S8_Expand(it.filename), S8_Expand(it.filename));
|
||||||
|
OS_WriteFile(out_path, code);
|
||||||
|
|
||||||
|
{
|
||||||
|
LC_BeginStringGen(L->arena);
|
||||||
|
LC_GenLCNode(L->ordered_packages.first->ast);
|
||||||
|
S8_String c = LC_EndStringGen(L->arena);
|
||||||
|
S8_String p = S8_Format(L->arena, "%.*s/%.*s.generated.lc", S8_Expand(it.filename), S8_Expand(it.filename));
|
||||||
|
p = OS_GetAbsolutePath(L->arena, p);
|
||||||
|
OS_WriteFile(p, c);
|
||||||
|
LC_ParseFile(NULL, p.str, c.str, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!T->dont_run) result = Compile(it.filename, out_path);
|
||||||
|
|
||||||
|
end_of_test:
|
||||||
|
result = PrintGlobalTestResult(it.filename.str, result);
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunTestDir(TestDesc it, S8_String package_name) {
|
||||||
|
LC_Lang *lang = LC_LangAlloc();
|
||||||
|
lang->on_message = HandleOutputMessageGlobalTest;
|
||||||
|
lang->use_colored_terminal_output = UseColoredIO;
|
||||||
|
lang->breakpoint_on_error = BreakpointOnError;
|
||||||
|
LC_LangBegin(lang);
|
||||||
|
|
||||||
|
Test test = {};
|
||||||
|
T = &test;
|
||||||
|
TestResult result = OK;
|
||||||
|
|
||||||
|
S8_String path_to_test_file = S8_Format(L->arena, "%.*s/test.txt", S8_Expand(it.absolute_path));
|
||||||
|
S8_String error_notation = OS_ReadFile(L->arena, path_to_test_file);
|
||||||
|
if (error_notation.str) ParseErrorNotationAndFillGlobalTest(error_notation);
|
||||||
|
|
||||||
|
LC_Intern first_package = LC_ILit(package_name.str);
|
||||||
|
LC_RegisterPackageDir(it.absolute_path.str);
|
||||||
|
LC_RegisterPackageDir("../../pkgs");
|
||||||
|
LC_ASTRefList packages = LC_ResolvePackageByName(first_package);
|
||||||
|
LC_FindUnusedLocalsAndRemoveUnusedGlobalDecls();
|
||||||
|
if (L->errors != 0) result = Failed_Package;
|
||||||
|
|
||||||
|
if (result == OK && T->expected_result == OK) {
|
||||||
|
DebugVerifyAST(packages);
|
||||||
|
|
||||||
|
OS_MakeDir(it.filename);
|
||||||
|
S8_String code = LC_GenerateUnityBuild(packages);
|
||||||
|
S8_String out_path = S8_Format(L->arena, "%.*s/%.*s.c", S8_Expand(it.filename), S8_Expand(it.filename));
|
||||||
|
OS_WriteFile(out_path, code);
|
||||||
|
if (!T->dont_run) result = Compile(it.filename, out_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = PrintGlobalTestResult(it.filename.str, result);
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestThread(Array<TestDesc> tests) {
|
||||||
|
MA_InitScratch();
|
||||||
|
For(tests) {
|
||||||
|
if (it.kind == TestKind_Dir) {
|
||||||
|
RunTestDir(it, it.package_name);
|
||||||
|
} else if (it.kind == TestKind_File) {
|
||||||
|
RunTestFile(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShouldSkip(S8_String filename, bool is_directory = false) {
|
||||||
|
if (TestsToRun.first) {
|
||||||
|
bool found = false;
|
||||||
|
S8_For(match, TestsToRun) {
|
||||||
|
if (match->string == filename) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_directory) {
|
||||||
|
if (S8_StartsWith(filename, "example_", true) || S8_StartsWith(filename, "compilation", true))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShouldRun(S8_String filename) {
|
||||||
|
bool result = !ShouldSkip(filename, false);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunTests() {
|
||||||
|
MA_Scratch scratch;
|
||||||
|
int test_count = 0;
|
||||||
|
S8_String working_dir = PushDir("test_result");
|
||||||
|
|
||||||
|
Array<TestDesc> tests = {MA_GetAllocator(scratch)};
|
||||||
|
|
||||||
|
for (OS_FileIter it = OS_IterateFiles(scratch, S8_Lit("../../tests")); OS_IsValid(it); OS_Advance(&it)) {
|
||||||
|
if (ShouldSkip(it.filename, it.is_directory)) continue;
|
||||||
|
TestDesc desc = {it.is_directory ? TestKind_Dir : TestKind_File, it.absolute_path, it.filename, "main"};
|
||||||
|
tests.add(desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
test_count = tests.len;
|
||||||
|
const int thread_count = ThreadCount;
|
||||||
|
Array<Array<TestDesc>> work = {MA_GetAllocator(scratch)};
|
||||||
|
Array<std::thread *> threads = {MA_GetAllocator(scratch)};
|
||||||
|
|
||||||
|
for (int i = 0; i < thread_count; i += 1) {
|
||||||
|
work.add({MA_GetAllocator(scratch)});
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
For(tests) {
|
||||||
|
work[i].add(it);
|
||||||
|
i = (i + 1) % thread_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
For(work) threads.add(new std::thread(TestThread, it));
|
||||||
|
For(threads) it->join();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Non automated tests
|
||||||
|
//
|
||||||
|
if (ShouldRun("example_ui_and_hot_reloading")) {
|
||||||
|
test_count += 1;
|
||||||
|
TestResult result = OK;
|
||||||
|
Test t = {};
|
||||||
|
t.path = OS_GetAbsolutePath(scratch, "../../tests/example_ui_and_hot_reloading/").str;
|
||||||
|
t.expected_result = OK;
|
||||||
|
T = &t;
|
||||||
|
|
||||||
|
LC_Lang *lang = LC_LangAlloc();
|
||||||
|
lang->on_message = HandleOutputMessageGlobalTest;
|
||||||
|
lang->use_colored_terminal_output = UseColoredIO;
|
||||||
|
lang->breakpoint_on_error = BreakpointOnError;
|
||||||
|
LC_LangBegin(lang);
|
||||||
|
LC_RegisterPackageDir(T->path);
|
||||||
|
LC_ASTRefList dll_packages = LC_ResolvePackageByName(LC_ILit("dll"));
|
||||||
|
LC_ASTRefList exe_packages = LC_ResolvePackageByName(LC_ILit("exe"));
|
||||||
|
result = L->errors >= 1 ? Failed_Package : OK;
|
||||||
|
|
||||||
|
if (result == OK) {
|
||||||
|
DebugVerifyAST(dll_packages);
|
||||||
|
// DebugVerifyAST(exe_packages);
|
||||||
|
|
||||||
|
S8_String prev = PushDir("example_ui_and_hot_reloading");
|
||||||
|
S8_String code = LC_GenerateUnityBuild(exe_packages);
|
||||||
|
OS_WriteFile("example_ui_and_hot_reloading", code);
|
||||||
|
OS_SetWorkingDir(prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_LangEnd(lang);
|
||||||
|
PrintGlobalTestResult("example_ui_and_hot_reloading", result);
|
||||||
|
#if 0
|
||||||
|
OS_SystemF("clang unity_exe.c -o platform.exe -g -O0 -I\"../..\"");
|
||||||
|
OS_SystemF("clang unity_dll.c -o game.dll -O0 -shared -g -I\"../..\" -Wl,-export:APP_Update");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_SetWorkingDir(working_dir);
|
||||||
|
IO_Printf("Total test count = %d\n", test_count);
|
||||||
|
}
|
||||||
127
src/build_tool/cache.cpp
Normal file
127
src/build_tool/cache.cpp
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
#define SRC_CACHE_ENTRY_COUNT 1024
|
||||||
|
|
||||||
|
struct SRC_CacheEntry {
|
||||||
|
uint64_t filepath_hash;
|
||||||
|
uint64_t file_hash;
|
||||||
|
uint64_t includes_hash;
|
||||||
|
uint64_t total_hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SRC_Cache {
|
||||||
|
int entry_cap;
|
||||||
|
int entry_len;
|
||||||
|
SRC_CacheEntry entries[SRC_CACHE_ENTRY_COUNT];
|
||||||
|
};
|
||||||
|
|
||||||
|
double SRC_Time;
|
||||||
|
SRC_Cache *SRC_InMemoryCache;
|
||||||
|
SRC_Cache *SRC_FromFileCache;
|
||||||
|
S8_String SRC_CacheFilename;
|
||||||
|
CL_SearchPaths SRC_SearchPaths = {}; // @todo;
|
||||||
|
|
||||||
|
void SRC_InitCache(MA_Arena *arena, S8_String cachefilename) {
|
||||||
|
SRC_CacheFilename = cachefilename;
|
||||||
|
|
||||||
|
SRC_InMemoryCache = MA_PushStruct(arena, SRC_Cache);
|
||||||
|
SRC_InMemoryCache->entry_cap = SRC_CACHE_ENTRY_COUNT;
|
||||||
|
|
||||||
|
SRC_FromFileCache = MA_PushStruct(arena, SRC_Cache);
|
||||||
|
SRC_FromFileCache->entry_cap = SRC_CACHE_ENTRY_COUNT;
|
||||||
|
|
||||||
|
S8_String cache = OS_ReadFile(arena, SRC_CacheFilename);
|
||||||
|
if (cache.len) MA_MemoryCopy(SRC_FromFileCache, cache.str, cache.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SRC_SaveCache() {
|
||||||
|
S8_String dump = S8_Make((char *)SRC_InMemoryCache, sizeof(SRC_Cache));
|
||||||
|
OS_WriteFile(SRC_CacheFilename, dump);
|
||||||
|
}
|
||||||
|
|
||||||
|
SRC_CacheEntry *SRC_AddHash(uint64_t filepath, uint64_t file, uint64_t includes) {
|
||||||
|
IO_Assert(SRC_InMemoryCache->entry_len + 1 < SRC_InMemoryCache->entry_cap);
|
||||||
|
SRC_CacheEntry *result = SRC_InMemoryCache->entries + SRC_InMemoryCache->entry_len++;
|
||||||
|
result->filepath_hash = filepath;
|
||||||
|
result->file_hash = file;
|
||||||
|
result->includes_hash = includes;
|
||||||
|
result->total_hash = HashBytes(result, sizeof(uint64_t) * 3);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
SRC_CacheEntry *SRC_FindCache(SRC_Cache *cache, uint64_t filepath_hash) {
|
||||||
|
for (int cache_i = 0; cache_i < cache->entry_len; cache_i += 1) {
|
||||||
|
SRC_CacheEntry *it = cache->entries + cache_i;
|
||||||
|
if (it->filepath_hash == filepath_hash) {
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SRC_CacheEntry *SRC_HashFile(S8_String file, char *parent_file) {
|
||||||
|
char *resolved_file = CL_ResolveFilepath(Perm, &SRC_SearchPaths, file.str, parent_file, false);
|
||||||
|
if (!resolved_file) {
|
||||||
|
IO_Printf("Failed to resolve file: %.*s\n", S8_Expand(file));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t filepath_hash = HashBytes(resolved_file, S8_Length(resolved_file));
|
||||||
|
SRC_CacheEntry *entry = SRC_FindCache(SRC_InMemoryCache, filepath_hash);
|
||||||
|
if (entry) return entry;
|
||||||
|
|
||||||
|
S8_String filecontent = OS_ReadFile(Perm, S8_MakeFromChar(resolved_file));
|
||||||
|
IO_Assert(filecontent.str);
|
||||||
|
|
||||||
|
uint64_t file_hash = HashBytes(filecontent.str, filecontent.len);
|
||||||
|
uint64_t includes_hash = 13;
|
||||||
|
|
||||||
|
CL_Lexer lexer = CL_Begin(Perm, filecontent.str, resolved_file);
|
||||||
|
lexer.select_includes = true;
|
||||||
|
|
||||||
|
for (CL_Token token = CL_Next(&lexer); token.kind != CL_EOF; token = CL_Next(&lexer)) {
|
||||||
|
if (token.is_system_include) continue;
|
||||||
|
|
||||||
|
S8_String file_it = S8_MakeFromChar(token.string_literal);
|
||||||
|
SRC_CacheEntry *cache = SRC_HashFile(file_it, resolved_file);
|
||||||
|
if (!cache) {
|
||||||
|
// error was reported already IO_Printf("Missing cache for: %.*s\n", S8_Expand(file_it));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
includes_hash = HashMix(includes_hash, cache->total_hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
SRC_CacheEntry *result = SRC_AddHash(filepath_hash, file_hash, includes_hash);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SRC_WasModified(S8_String file, S8_String artifact_path) {
|
||||||
|
double time_start = OS_GetTime();
|
||||||
|
|
||||||
|
if (OS_FileExists(file) == false) {
|
||||||
|
IO_Printf("FAILED File doesnt exist: %.*s\n", S8_Expand(file));
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
if (OS_IsAbsolute(file) == false) {
|
||||||
|
file = OS_GetAbsolutePath(Perm, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_String without_ext = S8_ChopLastPeriod(file);
|
||||||
|
S8_String name_only = S8_SkipToLastSlash(without_ext);
|
||||||
|
|
||||||
|
if (artifact_path.len == 0) artifact_path = S8_Format(Perm, "%.*s.%s", S8_Expand(name_only), IF_WINDOWS_ELSE("obj", "o"));
|
||||||
|
bool modified = OS_FileExists(artifact_path) == false;
|
||||||
|
|
||||||
|
SRC_CacheEntry *in_memory = SRC_HashFile(file, 0);
|
||||||
|
IO_Assert(in_memory);
|
||||||
|
|
||||||
|
if (modified == false) {
|
||||||
|
SRC_CacheEntry *from_file = SRC_FindCache(SRC_FromFileCache, in_memory->filepath_hash);
|
||||||
|
if (from_file == 0 || (in_memory->total_hash != from_file->total_hash)) {
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SRC_Time = SRC_Time + (OS_GetTime() - time_start);
|
||||||
|
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
116
src/build_tool/easy_strings.cpp
Normal file
116
src/build_tool/easy_strings.cpp
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
S8_String Fmt(const char *str, ...) S8__PrintfFormat(1, 2);
|
||||||
|
|
||||||
|
Array<S8_String> operator+(Array<S8_String> a, Array<S8_String> b) {
|
||||||
|
Array<S8_String> c = a.copy(MA_GetAllocator(Perm));
|
||||||
|
c.add_array(b);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<S8_String> operator+(Array<S8_String> a, S8_String b) {
|
||||||
|
Array<S8_String> c = a.copy(MA_GetAllocator(Perm));
|
||||||
|
c.add(b);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<S8_String> operator+(S8_String a, Array<S8_String> b) {
|
||||||
|
Array<S8_String> c = b.copy(MA_GetAllocator(Perm));
|
||||||
|
c.insert(a, 0);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<S8_String> operator+(S8_String a, S8_String b) {
|
||||||
|
Array<S8_String> c = {MA_GetAllocator(Perm)};
|
||||||
|
c.add(a);
|
||||||
|
c.add(b);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<S8_String> &operator+=(Array<S8_String> &a, Array<S8_String> b) {
|
||||||
|
a.add_array(b);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<S8_String> &operator+=(Array<S8_String> &a, S8_String s) {
|
||||||
|
a.add(s);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
//@todo: split on any whitespace instead!
|
||||||
|
Array<S8_String> Split(S8_String s, S8_String sep = " ") {
|
||||||
|
S8_List list = S8_Split(Perm, s, sep, 0);
|
||||||
|
Array<S8_String> result = {MA_GetAllocator(Perm)};
|
||||||
|
S8_For(it, list) result.add(it->string);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_String Merge(MA_Arena *arena, Array<S8_String> list, S8_String separator = " "_s) {
|
||||||
|
int64_t char_count = 0;
|
||||||
|
For(list) char_count += it.len;
|
||||||
|
if (char_count == 0) return {};
|
||||||
|
int64_t node_count = list.len;
|
||||||
|
|
||||||
|
int64_t base_size = (char_count + 1);
|
||||||
|
int64_t sep_size = (node_count - 1) * separator.len;
|
||||||
|
int64_t size = base_size + sep_size;
|
||||||
|
char *buff = (char *)MA_PushSize(arena, sizeof(char) * (size + 1));
|
||||||
|
S8_String string = S8_Make(buff, 0);
|
||||||
|
For(list) {
|
||||||
|
IO_Assert(string.len + it.len <= size);
|
||||||
|
MA_MemoryCopy(string.str + string.len, it.str, it.len);
|
||||||
|
string.len += it.len;
|
||||||
|
if (!list.is_last(it)) {
|
||||||
|
MA_MemoryCopy(string.str + string.len, separator.str, separator.len);
|
||||||
|
string.len += separator.len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IO_Assert(string.len == size - 1);
|
||||||
|
string.str[size] = 0;
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_String Merge(Array<S8_String> list, S8_String separator = " ") {
|
||||||
|
return Merge(Perm, list, separator);
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_String Fmt(const char *str, ...) {
|
||||||
|
S8_FORMAT(Perm, str, str_fmt);
|
||||||
|
return str_fmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<S8_String> ListDir(char *dir) {
|
||||||
|
Array<S8_String> result = {MA_GetAllocator(Perm)};
|
||||||
|
for (OS_FileIter it = OS_IterateFiles(Perm, S8_MakeFromChar(dir)); OS_IsValid(it); OS_Advance(&it)) {
|
||||||
|
result.add(S8_Copy(Perm, it.absolute_path));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<S8_String> CMD_Make(char **argv, int argc) {
|
||||||
|
Array<S8_String> result = {MA_GetAllocator(Perm)};
|
||||||
|
for (int i = 1; i < argc; i += 1) {
|
||||||
|
S8_String it = S8_MakeFromChar(argv[i]);
|
||||||
|
result.add(it);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_String CMD_Get(Array<S8_String> &cmd, S8_String name, S8_String default_value = "") {
|
||||||
|
For(cmd) {
|
||||||
|
int64_t idx = 0;
|
||||||
|
if (S8_Seek(it, "="_s, 0, &idx)) {
|
||||||
|
S8_String key = S8_GetPrefix(it, idx);
|
||||||
|
S8_String value = S8_Skip(it, idx + 1);
|
||||||
|
if (key == name) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return default_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CMD_Match(Array<S8_String> &cmd, S8_String name) {
|
||||||
|
For(cmd) {
|
||||||
|
if (it == name) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
55
src/build_tool/library.cpp
Normal file
55
src/build_tool/library.cpp
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "../core/core.c"
|
||||||
|
|
||||||
|
#define CL_Allocator MA_Arena *
|
||||||
|
#define CL_Allocate(a, s) MA_PushSizeNonZeroed(a, s)
|
||||||
|
#define CL_ASSERT IO_Assert
|
||||||
|
#define CL_VSNPRINTF stbsp_vsnprintf
|
||||||
|
#define CL_SNPRINTF stbsp_snprintf
|
||||||
|
#define AND_CL_STRING_TERMINATE_ON_NEW_LINE
|
||||||
|
#include "../standalone_libraries/clexer.c"
|
||||||
|
|
||||||
|
thread_local MA_Arena PernamentArena;
|
||||||
|
thread_local MA_Arena *Perm = &PernamentArena;
|
||||||
|
|
||||||
|
#include "cache.cpp"
|
||||||
|
#include "easy_strings.cpp"
|
||||||
|
#include "process.cpp"
|
||||||
|
|
||||||
|
S8_String CL_Flags = "/MP /Zi /FC /WX /W3 /wd4200 /diagnostics:column /nologo -D_CRT_SECURE_NO_WARNINGS /GF /Gm- /Oi";
|
||||||
|
S8_String CL_Link = "/link /incremental:no";
|
||||||
|
S8_String CL_StdOff = "/GR- /EHa-";
|
||||||
|
S8_String CL_StdOn = "/EHsc";
|
||||||
|
S8_String CL_Debug = "-Od -D_DEBUG -fsanitize=address -RTC1";
|
||||||
|
S8_String CL_Release = "-O2 -MT -DNDEBUG -GL";
|
||||||
|
S8_String CL_ReleaseLink = "-opt:ref -opt:icf";
|
||||||
|
/*
|
||||||
|
/FC = Print full paths in diagnostics
|
||||||
|
/Gm- = Old feature, 'minimal compilation', in case it's not off by default
|
||||||
|
/GF = Pools strings as read-only. If you try to modify strings under /GF, an application error occurs.
|
||||||
|
/Oi = Replaces some function calls with intrinsic
|
||||||
|
/MP = Multithreaded compilation
|
||||||
|
/GR- = Disable runtime type information
|
||||||
|
/EHa- = Disable exceptions
|
||||||
|
/EHsc = Enable exceptions
|
||||||
|
/MT = Link static libc. The 'd' means debug version
|
||||||
|
/MD = Link dynamic libc. The 'd' means debug version
|
||||||
|
/GL = Whole program optimization
|
||||||
|
/RTC1 = runtime error checks
|
||||||
|
/opt:ref = eliminates functions and data that are never referenced
|
||||||
|
/opt:icf = eliminates redundant 'COMDAT's
|
||||||
|
*/
|
||||||
|
|
||||||
|
S8_String Clang_Flags = "-fdiagnostics-absolute-paths -Wno-writable-strings";
|
||||||
|
S8_String Clang_NoStd = "-fno-exceptions";
|
||||||
|
S8_String Clang_Debug = "-fsanitize=address -g";
|
||||||
|
/*
|
||||||
|
-std=c++11
|
||||||
|
*/
|
||||||
|
|
||||||
|
S8_String GCC_Flags = "-Wno-write-strings";
|
||||||
|
S8_String GCC_NoStd = "-fno-exceptions";
|
||||||
|
S8_String GCC_Debug = "-fsanitize=address -g";
|
||||||
90
src/build_tool/main.cpp
Normal file
90
src/build_tool/main.cpp
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
#include "library.cpp"
|
||||||
|
|
||||||
|
int main(int argument_count, char **arguments) {
|
||||||
|
MA_InitScratch();
|
||||||
|
OS_MakeDir(S8_Lit("build"));
|
||||||
|
OS_SetWorkingDir(S8_Lit("build"));
|
||||||
|
S8_String working_dir = OS_GetWorkingDir(Perm);
|
||||||
|
IO_Printf("WORKING DIR: %.*s\n", S8_Expand(working_dir));
|
||||||
|
|
||||||
|
Array<S8_String> cmd = CMD_Make(arguments, argument_count);
|
||||||
|
S8_String cc = CMD_Get(cmd, "cc", IF_WINDOWS("cl") IF_MAC("clang") IF_LINUX("gcc"));
|
||||||
|
|
||||||
|
// Search for build file in the project directory
|
||||||
|
S8_String build_file = {};
|
||||||
|
{
|
||||||
|
for (OS_FileIter it = OS_IterateFiles(Perm, S8_Lit("..")); OS_IsValid(it); OS_Advance(&it)) {
|
||||||
|
if (S8_Seek(it.filename, S8_Lit("build_file.c"), S8_IgnoreCase)) {
|
||||||
|
build_file = it.absolute_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (build_file.str == 0) {
|
||||||
|
IO_Printf("Couldnt find build file in current dir: %.*s, exiting ... \n", S8_Expand(working_dir));
|
||||||
|
IO_Printf("- Proper build file contains 'build_file.c' in it's name\n");
|
||||||
|
IO_Printf("- Alternative compiler can be chosen like this: bld cc=clang\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SRC_InitCache(Perm, "build_tool.cache");
|
||||||
|
S8_String name_no_ext = S8_GetNameNoExt(build_file);
|
||||||
|
S8_String exe_name = S8_Format(Perm, "%.*s.exe", S8_Expand(name_no_ext));
|
||||||
|
|
||||||
|
// Compile the build file only if code changed
|
||||||
|
if (SRC_WasModified(build_file, exe_name)) {
|
||||||
|
double time = OS_GetTime();
|
||||||
|
int result = 0;
|
||||||
|
if (cc == "cl") {
|
||||||
|
Array<S8_String> flags = {MA_GetAllocator(Perm)};
|
||||||
|
flags += "-nologo -Zi";
|
||||||
|
flags += "-WX -W3 -wd4200 -diagnostics:column";
|
||||||
|
flags += Fmt("/Fe:%.*s /Fd:%.*s.pdb", S8_Expand(exe_name), S8_Expand(exe_name));
|
||||||
|
result = Run(cc + build_file + flags);
|
||||||
|
} else if (cc == "clang") {
|
||||||
|
Array<S8_String> flags = {MA_GetAllocator(Perm)};
|
||||||
|
cc = "clang++";
|
||||||
|
|
||||||
|
flags += "-std=c++11 -g";
|
||||||
|
flags += "-fdiagnostics-absolute-paths";
|
||||||
|
flags += "-Wno-writable-strings";
|
||||||
|
flags += "-lm";
|
||||||
|
flags += Fmt("-o %.*s", S8_Expand(exe_name));
|
||||||
|
result = Run(cc + build_file + flags);
|
||||||
|
} else {
|
||||||
|
IO_Assert(cc == "gcc");
|
||||||
|
cc = "g++";
|
||||||
|
|
||||||
|
Array<S8_String> flags = {MA_GetAllocator(Perm)};
|
||||||
|
flags += "-std=c++11 -g";
|
||||||
|
flags += "-Wno-write-strings";
|
||||||
|
flags += "-lm";
|
||||||
|
flags += Fmt("-o %.*s", S8_Expand(exe_name));
|
||||||
|
result = Run(cc + build_file + flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != 0) {
|
||||||
|
IO_Printf("FAILED compilation of the build file!\n");
|
||||||
|
OS_DeleteFile("build_tool.cache");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
time = OS_GetTime() - time;
|
||||||
|
IO_Printf("TIME Build file compilation: %f\n", time);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the build file
|
||||||
|
double time = OS_GetTime();
|
||||||
|
if (build_file.str) {
|
||||||
|
exe_name = OS_GetAbsolutePath(Perm, exe_name);
|
||||||
|
int result = Run(exe_name + cmd);
|
||||||
|
if (result != 0) {
|
||||||
|
IO_Printf("FAILED execution of the build file!\n");
|
||||||
|
OS_DeleteFile("build_tool.cache");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SRC_SaveCache();
|
||||||
|
time = OS_GetTime() - time;
|
||||||
|
IO_Printf("TIME total build file execution: %f\n", time);
|
||||||
|
}
|
||||||
153
src/build_tool/process.cpp
Normal file
153
src/build_tool/process.cpp
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
struct Process {
|
||||||
|
bool is_valid;
|
||||||
|
char platform[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
#if OS_WINDOWS
|
||||||
|
Process RunEx(S8_String in_cmd) {
|
||||||
|
MA_Scratch scratch;
|
||||||
|
wchar_t *application_name = NULL;
|
||||||
|
wchar_t *cmd = S8_ToWidechar(scratch, in_cmd);
|
||||||
|
BOOL inherit_handles = FALSE;
|
||||||
|
DWORD creation_flags = 0;
|
||||||
|
void *enviroment = NULL;
|
||||||
|
wchar_t *working_dir = NULL;
|
||||||
|
STARTUPINFOW startup_info = {};
|
||||||
|
startup_info.cb = sizeof(STARTUPINFOW);
|
||||||
|
Process result = {};
|
||||||
|
IO_Assert(sizeof(result.platform) >= sizeof(PROCESS_INFORMATION));
|
||||||
|
PROCESS_INFORMATION *process_info = (PROCESS_INFORMATION *)result.platform;
|
||||||
|
BOOL success = CreateProcessW(application_name, cmd, NULL, NULL, inherit_handles, creation_flags, enviroment, working_dir, &startup_info, process_info);
|
||||||
|
result.is_valid = true;
|
||||||
|
if (!success) {
|
||||||
|
result.is_valid = false;
|
||||||
|
|
||||||
|
LPVOID lpMsgBuf;
|
||||||
|
DWORD dw = GetLastError();
|
||||||
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);
|
||||||
|
LocalFree(lpMsgBuf);
|
||||||
|
|
||||||
|
IO_FatalErrorf("Failed to create process \ncmd: %.*s\nwindows_message: %s", S8_Expand(in_cmd), lpMsgBuf);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Wait(Process *process) {
|
||||||
|
IO_Assert(process->is_valid);
|
||||||
|
PROCESS_INFORMATION *pi = (PROCESS_INFORMATION *)process->platform;
|
||||||
|
WaitForSingleObject(pi->hProcess, INFINITE);
|
||||||
|
|
||||||
|
DWORD exit_code;
|
||||||
|
BOOL err = GetExitCodeProcess(pi->hProcess, &exit_code);
|
||||||
|
IO_Assert(err != 0);
|
||||||
|
|
||||||
|
CloseHandle(pi->hProcess);
|
||||||
|
CloseHandle(pi->hThread);
|
||||||
|
process[0] = {};
|
||||||
|
return (int)exit_code;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#include <spawn.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
struct TH_UnixProcess {
|
||||||
|
pid_t pid;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern char **environ;
|
||||||
|
|
||||||
|
Process RunEx(S8_String cmd) {
|
||||||
|
MA_Scratch scratch;
|
||||||
|
Process result = {};
|
||||||
|
IO_Assert(sizeof(result.platform) >= sizeof(TH_UnixProcess));
|
||||||
|
TH_UnixProcess *u = (TH_UnixProcess *)result.platform;
|
||||||
|
|
||||||
|
S8_String exec_file = cmd;
|
||||||
|
S8_String argv = "";
|
||||||
|
int64_t pos;
|
||||||
|
if (S8_Seek(cmd, S8_Lit(" "), 0, &pos)) {
|
||||||
|
exec_file = S8_GetPrefix(cmd, pos);
|
||||||
|
argv = S8_Skip(cmd, pos + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
exec_file = S8_Copy(scratch, exec_file);
|
||||||
|
|
||||||
|
// Split string on whitespace and conform with argv format
|
||||||
|
Array<char *> args = {MA_GetAllocator(scratch)};
|
||||||
|
{
|
||||||
|
args.add(exec_file.str);
|
||||||
|
for (int64_t i = 0; i < argv.len;) {
|
||||||
|
while (i < argv.len && CHAR_IsWhitespace(argv.str[i])) {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
S8_String word = {argv.str + i, 0};
|
||||||
|
while (i < argv.len && !CHAR_IsWhitespace(argv.str[i])) {
|
||||||
|
word.len += 1;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
word = S8_Copy(scratch, word);
|
||||||
|
args.add(word.str);
|
||||||
|
}
|
||||||
|
args.add(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = posix_spawnp(&u->pid, exec_file.str, NULL, NULL, args.data, environ);
|
||||||
|
if (err == 0) {
|
||||||
|
result.is_valid = true;
|
||||||
|
} else {
|
||||||
|
perror("posix_spawnp error");
|
||||||
|
IO_FatalErrorf("Failed to create process, cmd: %.*s", S8_Expand(cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Wait(Process *process) {
|
||||||
|
if (!process->is_valid) return 1;
|
||||||
|
TH_UnixProcess *u = (TH_UnixProcess *)process->platform;
|
||||||
|
|
||||||
|
int status = 0;
|
||||||
|
int pid = waitpid(u->pid, &status, 0);
|
||||||
|
IO_Assert(pid != -1);
|
||||||
|
|
||||||
|
int result = 0;
|
||||||
|
if (WIFEXITED(status)) {
|
||||||
|
result = WEXITSTATUS(status);
|
||||||
|
} else {
|
||||||
|
result = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
process[0] = {};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Process RunEx(Array<S8_String> s) {
|
||||||
|
S8_String cmd = Merge(s);
|
||||||
|
Process proc = RunEx(cmd);
|
||||||
|
return proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
Process RunEx(Array<S8_String> s, S8_String process_start_dir) {
|
||||||
|
OS_MakeDir(process_start_dir);
|
||||||
|
S8_String working_dir = OS_GetWorkingDir(Perm);
|
||||||
|
OS_SetWorkingDir(process_start_dir);
|
||||||
|
S8_String cmd = Merge(s);
|
||||||
|
Process proc = RunEx(cmd);
|
||||||
|
OS_SetWorkingDir(working_dir);
|
||||||
|
return proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Run(S8_String cmd) {
|
||||||
|
Process process = RunEx(cmd);
|
||||||
|
int result = Wait(&process);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Run(Array<S8_String> cmd) {
|
||||||
|
S8_String cmds = Merge(cmd);
|
||||||
|
int result = Run(cmds);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
138
src/compiler/arena.c
Normal file
138
src/compiler/arena.c
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
#if defined(LC_USE_ADDRESS_SANITIZER)
|
||||||
|
#include <sanitizer/asan_interface.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(ASAN_POISON_MEMORY_REGION)
|
||||||
|
#define LC_ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
|
||||||
|
#define LC_ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
|
||||||
|
#else
|
||||||
|
#define LC_ASAN_POISON_MEMORY_REGION(addr, size) ASAN_POISON_MEMORY_REGION(addr, size)
|
||||||
|
#define LC_ASAN_UNPOISON_MEMORY_REGION(addr, size) ASAN_UNPOISON_MEMORY_REGION(addr, size)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LC_DEFAULT_RESERVE_SIZE LC_GIB(1)
|
||||||
|
#define LC_DEFAULT_ALIGNMENT 8
|
||||||
|
#define LC_COMMIT_ADD_SIZE LC_MIB(4)
|
||||||
|
|
||||||
|
LC_FUNCTION uint8_t *LC_V_AdvanceCommit(LC_VMemory *m, size_t *commit_size, size_t page_size) {
|
||||||
|
size_t aligned_up_commit = LC_AlignUp(*commit_size, page_size);
|
||||||
|
size_t to_be_total_commited_size = aligned_up_commit + m->commit;
|
||||||
|
size_t to_be_total_commited_size_clamped_to_reserve = LC_CLAMP_TOP(to_be_total_commited_size, m->reserve);
|
||||||
|
size_t adjusted_to_boundary_commit = to_be_total_commited_size_clamped_to_reserve - m->commit;
|
||||||
|
LC_Assertf(adjusted_to_boundary_commit, "Reached the virtual memory reserved boundary");
|
||||||
|
*commit_size = adjusted_to_boundary_commit;
|
||||||
|
|
||||||
|
if (adjusted_to_boundary_commit == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint8_t *result = m->data + m->commit;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_PopToPos(LC_Arena *arena, size_t pos) {
|
||||||
|
LC_Assertf(arena->len >= arena->base_len, "Bug: arena->len shouldn't ever be smaller then arena->base_len");
|
||||||
|
pos = LC_CLAMP(pos, arena->base_len, arena->len);
|
||||||
|
size_t size = arena->len - pos;
|
||||||
|
arena->len = pos;
|
||||||
|
LC_ASAN_POISON_MEMORY_REGION(arena->memory.data + arena->len, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_PopSize(LC_Arena *arena, size_t size) {
|
||||||
|
LC_PopToPos(arena, arena->len - size);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_DeallocateArena(LC_Arena *arena) {
|
||||||
|
LC_VDeallocate(&arena->memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_ResetArena(LC_Arena *arena) {
|
||||||
|
LC_PopToPos(arena, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION size_t LC__AlignLen(LC_Arena *a) {
|
||||||
|
size_t align_offset = a->alignment ? LC_GetAlignOffset((uintptr_t)a->memory.data + (uintptr_t)a->len, a->alignment) : 0;
|
||||||
|
size_t aligned = a->len + align_offset;
|
||||||
|
return aligned;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void *LC__PushSizeNonZeroed(LC_Arena *a, size_t size) {
|
||||||
|
size_t align_offset = a->alignment ? LC_GetAlignOffset((uintptr_t)a->memory.data + (uintptr_t)a->len, a->alignment) : 0;
|
||||||
|
size_t aligned_len = a->len + align_offset;
|
||||||
|
size_t size_with_alignment = size + align_offset;
|
||||||
|
|
||||||
|
if (a->len + size_with_alignment > a->memory.commit) {
|
||||||
|
if (a->memory.reserve == 0) {
|
||||||
|
LC_Assertf(0, "Pushing on uninitialized arena with zero initialization turned off");
|
||||||
|
}
|
||||||
|
bool result = LC_VCommit(&a->memory, size_with_alignment + LC_COMMIT_ADD_SIZE);
|
||||||
|
LC_Assertf(result, "%s(%d): Failed to commit memory more memory! reserve: %zu commit: %zu len: %zu size_with_alignment: %zu", LC_BeginTempdSourceLoc.file, LC_BeginTempdSourceLoc.line, a->memory.reserve, a->memory.commit, a->len, size_with_alignment);
|
||||||
|
(void)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *result = a->memory.data + aligned_len;
|
||||||
|
a->len += size_with_alignment;
|
||||||
|
LC_Assertf(a->len <= a->memory.commit, "%s(%d): Reached commit boundary! reserve: %zu commit: %zu len: %zu base_len: %zu alignment: %d size_with_alignment: %zu", LC_BeginTempdSourceLoc.file, LC_BeginTempdSourceLoc.line, a->memory.reserve, a->memory.commit, a->len, a->base_len, a->alignment, size_with_alignment);
|
||||||
|
LC_ASAN_UNPOISON_MEMORY_REGION(result, size);
|
||||||
|
return (void *)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void *LC__PushSize(LC_Arena *arena, size_t size) {
|
||||||
|
void *result = LC__PushSizeNonZeroed(arena, size);
|
||||||
|
LC_MemoryZero(result, size);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Arena LC_PushArena(LC_Arena *arena, size_t size) {
|
||||||
|
LC_Arena result;
|
||||||
|
LC_MemoryZero(&result, sizeof(result));
|
||||||
|
result.memory.data = LC_PushArrayNonZeroed(arena, uint8_t, size);
|
||||||
|
result.memory.commit = size;
|
||||||
|
result.memory.reserve = size;
|
||||||
|
result.alignment = arena->alignment;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Arena *LC_PushArenaP(LC_Arena *arena, size_t size) {
|
||||||
|
LC_Arena *result = LC_PushStruct(arena, LC_Arena);
|
||||||
|
*result = LC_PushArena(arena, size);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_InitArenaEx(LC_Arena *a, size_t reserve) {
|
||||||
|
a->memory = LC_VReserve(reserve);
|
||||||
|
LC_ASAN_POISON_MEMORY_REGION(a->memory.data, a->memory.reserve);
|
||||||
|
a->alignment = LC_DEFAULT_ALIGNMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_InitArena(LC_Arena *a) {
|
||||||
|
LC_InitArenaEx(a, LC_DEFAULT_RESERVE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Arena *LC_BootstrapArena(void) {
|
||||||
|
LC_Arena bootstrap_arena = {0};
|
||||||
|
LC_InitArena(&bootstrap_arena);
|
||||||
|
|
||||||
|
LC_Arena *arena = LC_PushStruct(&bootstrap_arena, LC_Arena);
|
||||||
|
*arena = bootstrap_arena;
|
||||||
|
arena->base_len = arena->len;
|
||||||
|
return arena;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_InitArenaFromBuffer(LC_Arena *arena, void *buffer, size_t size) {
|
||||||
|
arena->memory.data = (uint8_t *)buffer;
|
||||||
|
arena->memory.commit = size;
|
||||||
|
arena->memory.reserve = size;
|
||||||
|
arena->alignment = LC_DEFAULT_ALIGNMENT;
|
||||||
|
LC_ASAN_POISON_MEMORY_REGION(arena->memory.data, arena->memory.reserve);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_TempArena LC_BeginTemp(LC_Arena *arena) {
|
||||||
|
LC_TempArena result;
|
||||||
|
result.pos = arena->len;
|
||||||
|
result.arena = arena;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_EndTemp(LC_TempArena checkpoint) {
|
||||||
|
LC_PopToPos(checkpoint.arena, checkpoint.pos);
|
||||||
|
}
|
||||||
167
src/compiler/ast.c
Normal file
167
src/compiler/ast.c
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
LC_FUNCTION LC_AST *LC_CreateAST(LC_Token *pos, LC_ASTKind kind) {
|
||||||
|
LC_AST *n = LC_PushStruct(L->ast_arena, LC_AST);
|
||||||
|
n->id = ++L->ast_count;
|
||||||
|
n->kind = kind;
|
||||||
|
n->pos = pos;
|
||||||
|
if (L->parser == &L->quick_parser) n->pos = &L->BuiltinToken;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_AST *LC_CreateUnary(LC_Token *pos, LC_TokenKind op, LC_AST *expr) {
|
||||||
|
LC_AST *r = LC_CreateAST(pos, LC_ASTKind_ExprUnary);
|
||||||
|
r->eunary.op = op;
|
||||||
|
r->eunary.expr = expr;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_AST *LC_CreateBinary(LC_Token *pos, LC_AST *left, LC_AST *right, LC_TokenKind op) {
|
||||||
|
LC_AST *r = LC_CreateAST(pos, LC_ASTKind_ExprBinary);
|
||||||
|
r->ebinary.op = op;
|
||||||
|
r->ebinary.left = left;
|
||||||
|
r->ebinary.right = right;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_AST *LC_CreateIndex(LC_Token *pos, LC_AST *left, LC_AST *index) {
|
||||||
|
LC_AST *r = LC_CreateAST(pos, LC_ASTKind_ExprIndex);
|
||||||
|
r->eindex.index = index;
|
||||||
|
r->eindex.base = left;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_SetPointerSizeAndAlign(int size, int align) {
|
||||||
|
L->pointer_align = align;
|
||||||
|
L->pointer_size = size;
|
||||||
|
if (L->tpvoid) {
|
||||||
|
L->tpvoid->size = size;
|
||||||
|
L->tpvoid->align = align;
|
||||||
|
}
|
||||||
|
if (L->tpchar) {
|
||||||
|
L->tpchar->size = size;
|
||||||
|
L->tpchar->align = align;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Type *LC_CreateType(LC_TypeKind kind) {
|
||||||
|
LC_Type *r = LC_PushStruct(L->type_arena, LC_Type);
|
||||||
|
L->type_count += 1;
|
||||||
|
r->kind = kind;
|
||||||
|
r->id = ++L->typeids;
|
||||||
|
if (r->kind == LC_TypeKind_Proc || r->kind == LC_TypeKind_Pointer) {
|
||||||
|
r->size = L->pointer_size;
|
||||||
|
r->align = L->pointer_align;
|
||||||
|
r->is_unsigned = true;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Type *LC_CreateTypedef(LC_Decl *decl, LC_Type *base) {
|
||||||
|
LC_Type *n = LC_CreateType(base->kind);
|
||||||
|
*n = *base;
|
||||||
|
decl->typedef_renamed_type_decl = base->decl;
|
||||||
|
n->decl = decl;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Type *LC_CreatePointerType(LC_Type *type) {
|
||||||
|
uint64_t key = (uint64_t)type;
|
||||||
|
LC_Type *entry = (LC_Type *)LC_MapGetU64(&L->type_map, key);
|
||||||
|
if (entry) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
LC_Type *n = LC_CreateType(LC_TypeKind_Pointer);
|
||||||
|
n->tbase = type;
|
||||||
|
LC_MapInsertU64(&L->type_map, key, n);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Type *LC_CreateArrayType(LC_Type *type, int size) {
|
||||||
|
uint64_t size_key = LC_HashBytes(&size, sizeof(size));
|
||||||
|
uint64_t type_key = LC_HashBytes(type, sizeof(*type));
|
||||||
|
uint64_t key = LC_HashMix(size_key, type_key);
|
||||||
|
LC_Type *entry = (LC_Type *)LC_MapGetU64(&L->type_map, key);
|
||||||
|
if (entry) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
LC_Type *n = LC_CreateType(LC_TypeKind_Array);
|
||||||
|
n->tbase = type;
|
||||||
|
n->tarray.size = size;
|
||||||
|
n->size = type->size * size;
|
||||||
|
n->align = type->align;
|
||||||
|
LC_MapInsertU64(&L->type_map, key, n);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Type *LC_CreateProcType(LC_TypeMemberList args, LC_Type *ret, bool has_vargs, bool has_vargs_any_promotion) {
|
||||||
|
LC_ASSERT(NULL, ret);
|
||||||
|
uint64_t key = LC_HashBytes(ret, sizeof(*ret));
|
||||||
|
key = LC_HashMix(key, LC_HashBytes(&has_vargs, sizeof(has_vargs)));
|
||||||
|
key = LC_HashMix(key, LC_HashBytes(&has_vargs_any_promotion, sizeof(has_vargs_any_promotion)));
|
||||||
|
int procarg_count = 0;
|
||||||
|
LC_TypeFor(it, args.first) {
|
||||||
|
key = LC_HashMix(LC_HashBytes(it->type, sizeof(it->type[0])), key);
|
||||||
|
key = LC_HashMix(LC_HashBytes((char *)it->name, LC_StrLen((char *)it->name)), key);
|
||||||
|
if (it->default_value_expr) {
|
||||||
|
key = LC_HashMix(LC_HashBytes(&it->default_value_expr, sizeof(LC_AST *)), key);
|
||||||
|
}
|
||||||
|
procarg_count += 1;
|
||||||
|
}
|
||||||
|
LC_Type *n = (LC_Type *)LC_MapGetU64(&L->type_map, key);
|
||||||
|
if (n) return n;
|
||||||
|
|
||||||
|
n = LC_CreateType(LC_TypeKind_Proc);
|
||||||
|
n->tproc.args = args;
|
||||||
|
n->tproc.vargs = has_vargs;
|
||||||
|
n->tproc.vargs_any_promotion = has_vargs_any_promotion;
|
||||||
|
n->tproc.ret = ret;
|
||||||
|
LC_MapInsertU64(&L->type_map, key, n);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Type *LC_CreateIncompleteType(LC_Decl *decl) {
|
||||||
|
LC_Type *n = LC_CreateType(LC_TypeKind_Incomplete);
|
||||||
|
n->decl = decl;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Type *LC_CreateUntypedIntEx(LC_Type *base, LC_Decl *decl) {
|
||||||
|
uint64_t hash_base = LC_HashBytes(base, sizeof(*base));
|
||||||
|
uint64_t untyped_int = LC_TypeKind_UntypedInt;
|
||||||
|
uint64_t key = LC_HashMix(hash_base, untyped_int);
|
||||||
|
LC_Type *n = (LC_Type *)LC_MapGetU64(&L->type_map, key);
|
||||||
|
if (n) return n;
|
||||||
|
|
||||||
|
n = LC_CreateType(LC_TypeKind_UntypedInt);
|
||||||
|
n->tbase = base;
|
||||||
|
n->decl = decl;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Type *LC_CreateUntypedInt(LC_Type *base) {
|
||||||
|
LC_Decl *decl = LC_CreateDecl(LC_DeclKind_Type, LC_ILit("UntypedInt"), &L->NullAST);
|
||||||
|
LC_Type *n = LC_CreateUntypedIntEx(base, decl);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_TypeMember *LC_AddTypeToList(LC_TypeMemberList *list, LC_Intern name, LC_Type *type, LC_AST *ast) {
|
||||||
|
LC_TypeFor(it, list->first) {
|
||||||
|
if (name == it->name) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_TypeMember *r = LC_PushStruct(L->arena, LC_TypeMember);
|
||||||
|
r->name = name;
|
||||||
|
r->type = type;
|
||||||
|
r->ast = ast;
|
||||||
|
LC_DLLAdd(list->first, list->last, r);
|
||||||
|
list->count += 1;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Type *LC_StripPointer(LC_Type *type) {
|
||||||
|
if (type->kind == LC_TypeKind_Pointer) {
|
||||||
|
return type->tbase;
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
283
src/compiler/ast_copy.c
Normal file
283
src/compiler/ast_copy.c
Normal file
@@ -0,0 +1,283 @@
|
|||||||
|
LC_FUNCTION LC_AST *LC_CopyAST(LC_Arena *arena, LC_AST *n) {
|
||||||
|
if (n == NULL) return NULL;
|
||||||
|
|
||||||
|
LC_AST *result = LC_PushStruct(arena, LC_AST);
|
||||||
|
result->kind = n->kind;
|
||||||
|
result->id = ++L->ast_count;
|
||||||
|
result->notes = LC_CopyAST(arena, n->notes);
|
||||||
|
result->pos = n->pos;
|
||||||
|
|
||||||
|
switch (n->kind) {
|
||||||
|
case LC_ASTKind_File: {
|
||||||
|
result->afile.x = n->afile.x;
|
||||||
|
|
||||||
|
LC_ASTFor(it, n->afile.fimport) {
|
||||||
|
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||||
|
LC_DLLAdd(result->afile.fimport, result->afile.limport, it_copy);
|
||||||
|
}
|
||||||
|
LC_ASTFor(it, n->afile.fdecl) {
|
||||||
|
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||||
|
LC_DLLAdd(result->afile.fdecl, result->afile.ldecl, it_copy);
|
||||||
|
}
|
||||||
|
LC_ASTFor(it, n->afile.fdiscarded) {
|
||||||
|
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||||
|
LC_DLLAdd(result->afile.fdiscarded, result->afile.ldiscarded, it_copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
result->afile.build_if = n->afile.build_if;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_DeclProc: {
|
||||||
|
result->dbase.name = n->dbase.name;
|
||||||
|
result->dproc.body = LC_CopyAST(arena, n->dproc.body);
|
||||||
|
result->dproc.type = LC_CopyAST(arena, n->dproc.type);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_NoteList: {
|
||||||
|
LC_ASTFor(it, n->anote_list.first) {
|
||||||
|
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||||
|
LC_DLLAdd(result->anote_list.first, result->anote_list.last, it_copy);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_TypespecAggMem:
|
||||||
|
case LC_ASTKind_TypespecProcArg: {
|
||||||
|
result->tproc_arg.name = n->tproc_arg.name;
|
||||||
|
result->tproc_arg.type = LC_CopyAST(arena, n->tproc_arg.type);
|
||||||
|
result->tproc_arg.expr = LC_CopyAST(arena, n->tproc_arg.expr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprNote: {
|
||||||
|
result->enote.expr = LC_CopyAST(arena, n->enote.expr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtSwitch: {
|
||||||
|
LC_ASTFor(it, n->sswitch.first) {
|
||||||
|
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||||
|
LC_DLLAdd(result->sswitch.first, result->sswitch.last, it_copy);
|
||||||
|
}
|
||||||
|
result->sswitch.total_switch_case_count = n->sswitch.total_switch_case_count;
|
||||||
|
result->sswitch.expr = LC_CopyAST(arena, n->sswitch.expr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtSwitchCase: {
|
||||||
|
LC_ASTFor(it, n->scase.first) {
|
||||||
|
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||||
|
LC_DLLAdd(result->scase.first, result->scase.last, it_copy);
|
||||||
|
}
|
||||||
|
result->scase.body = LC_CopyAST(arena, n->scase.body);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtSwitchDefault: {
|
||||||
|
LC_ASTFor(it, n->scase.first) {
|
||||||
|
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||||
|
LC_DLLAdd(result->scase.first, result->scase.last, it_copy);
|
||||||
|
}
|
||||||
|
result->scase.body = LC_CopyAST(arena, n->scase.body);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtIf: {
|
||||||
|
LC_ASTFor(it, n->sif.first) {
|
||||||
|
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||||
|
LC_DLLAdd(result->sswitch.first, result->sswitch.last, it_copy);
|
||||||
|
}
|
||||||
|
result->sif.expr = LC_CopyAST(arena, n->sif.expr);
|
||||||
|
result->sif.body = LC_CopyAST(arena, n->sif.body);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtElse:
|
||||||
|
case LC_ASTKind_StmtElseIf: {
|
||||||
|
result->sif.expr = LC_CopyAST(arena, n->sif.expr);
|
||||||
|
result->sif.body = LC_CopyAST(arena, n->sif.body);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_GlobImport: {
|
||||||
|
result->gimport.name = n->gimport.name;
|
||||||
|
result->gimport.path = n->gimport.path;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_DeclNote: {
|
||||||
|
result->dnote.expr = LC_CopyAST(arena, n->dnote.expr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_DeclUnion:
|
||||||
|
case LC_ASTKind_DeclStruct: {
|
||||||
|
result->dbase.name = n->dbase.name;
|
||||||
|
LC_ASTFor(it, n->dagg.first) {
|
||||||
|
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||||
|
LC_DLLAdd(result->dagg.first, result->dagg.last, it_copy);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_DeclVar: {
|
||||||
|
result->dbase.name = n->dbase.name;
|
||||||
|
result->dvar.type = LC_CopyAST(arena, n->dvar.type);
|
||||||
|
result->dvar.expr = LC_CopyAST(arena, n->dvar.expr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_DeclConst: {
|
||||||
|
result->dbase.name = n->dbase.name;
|
||||||
|
result->dconst.expr = LC_CopyAST(arena, n->dconst.expr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_DeclTypedef: {
|
||||||
|
result->dbase.name = n->dbase.name;
|
||||||
|
result->dtypedef.type = LC_CopyAST(arena, n->dtypedef.type);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprField:
|
||||||
|
case LC_ASTKind_TypespecField: {
|
||||||
|
result->efield.left = LC_CopyAST(arena, n->efield.left);
|
||||||
|
result->efield.right = n->efield.right;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_TypespecIdent: {
|
||||||
|
result->eident.name = n->eident.name;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_TypespecPointer: {
|
||||||
|
result->tpointer.base = LC_CopyAST(arena, n->tpointer.base);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_TypespecArray: {
|
||||||
|
result->tarray.base = LC_CopyAST(arena, n->tarray.base);
|
||||||
|
result->tarray.index = LC_CopyAST(arena, n->tarray.index);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_TypespecProc: {
|
||||||
|
LC_ASTFor(it, n->tproc.first) {
|
||||||
|
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||||
|
LC_DLLAdd(result->tproc.first, result->tproc.last, it_copy);
|
||||||
|
}
|
||||||
|
result->tproc.ret = LC_CopyAST(arena, n->tproc.ret);
|
||||||
|
result->tproc.vargs = n->tproc.vargs;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtBlock: {
|
||||||
|
LC_ASTFor(it, n->sblock.first) {
|
||||||
|
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||||
|
LC_DLLAdd(result->sblock.first, result->sblock.last, it_copy);
|
||||||
|
|
||||||
|
if (it_copy->kind == LC_ASTKind_StmtDefer) {
|
||||||
|
LC_SLLStackAddMod(result->sblock.first_defer, it_copy, sdefer.next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (n->sblock.first_defer) {
|
||||||
|
LC_ASSERT(result, result->sblock.first_defer);
|
||||||
|
}
|
||||||
|
result->sblock.kind = n->sblock.kind;
|
||||||
|
result->sblock.name = n->sblock.name;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtNote: {
|
||||||
|
result->snote.expr = LC_CopyAST(arena, n->snote.expr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtReturn: {
|
||||||
|
result->sreturn.expr = LC_CopyAST(arena, n->sreturn.expr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtBreak: {
|
||||||
|
result->sbreak.name = n->sbreak.name;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtContinue: {
|
||||||
|
result->scontinue.name = n->scontinue.name;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtDefer: {
|
||||||
|
result->sdefer.body = LC_CopyAST(arena, n->sdefer.body);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtFor: {
|
||||||
|
result->sfor.init = LC_CopyAST(arena, n->sfor.init);
|
||||||
|
result->sfor.cond = LC_CopyAST(arena, n->sfor.cond);
|
||||||
|
result->sfor.inc = LC_CopyAST(arena, n->sfor.inc);
|
||||||
|
result->sfor.body = LC_CopyAST(arena, n->sfor.body);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtAssign: {
|
||||||
|
result->sassign.op = n->sassign.op;
|
||||||
|
result->sassign.left = LC_CopyAST(arena, n->sassign.left);
|
||||||
|
result->sassign.right = LC_CopyAST(arena, n->sassign.right);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtExpr: {
|
||||||
|
result->sexpr.expr = LC_CopyAST(arena, n->sexpr.expr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtVar: {
|
||||||
|
result->svar.type = LC_CopyAST(arena, n->svar.type);
|
||||||
|
result->svar.expr = LC_CopyAST(arena, n->svar.expr);
|
||||||
|
result->svar.name = n->svar.name;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtConst: {
|
||||||
|
result->sconst.expr = LC_CopyAST(arena, n->sconst.expr);
|
||||||
|
result->sconst.name = n->sconst.name;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprIdent: {
|
||||||
|
result->eident.name = n->eident.name;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprBool:
|
||||||
|
case LC_ASTKind_ExprFloat:
|
||||||
|
case LC_ASTKind_ExprInt:
|
||||||
|
case LC_ASTKind_ExprString: {
|
||||||
|
result->eatom = n->eatom;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprType: {
|
||||||
|
result->etype.type = LC_CopyAST(arena, n->etype.type);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprAddPtr:
|
||||||
|
case LC_ASTKind_ExprBinary: {
|
||||||
|
result->ebinary.op = n->ebinary.op;
|
||||||
|
result->ebinary.left = LC_CopyAST(arena, n->ebinary.left);
|
||||||
|
result->ebinary.right = LC_CopyAST(arena, n->ebinary.right);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprGetPointerOfValue:
|
||||||
|
case LC_ASTKind_ExprGetValueOfPointer:
|
||||||
|
case LC_ASTKind_ExprUnary: {
|
||||||
|
result->eunary.op = n->eunary.op;
|
||||||
|
result->eunary.expr = LC_CopyAST(arena, n->eunary.expr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprCompoundItem:
|
||||||
|
case LC_ASTKind_ExprCallItem: {
|
||||||
|
result->ecompo_item.name = n->ecompo_item.name;
|
||||||
|
result->ecompo_item.index = LC_CopyAST(arena, n->ecompo_item.index);
|
||||||
|
result->ecompo_item.expr = LC_CopyAST(arena, n->ecompo_item.expr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprBuiltin:
|
||||||
|
case LC_ASTKind_Note:
|
||||||
|
case LC_ASTKind_ExprCall:
|
||||||
|
case LC_ASTKind_ExprCompound: {
|
||||||
|
LC_ASTFor(it, n->ecompo.first) {
|
||||||
|
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||||
|
LC_DLLAdd(result->ecompo.first, result->ecompo.last, it_copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
result->ecompo.size = n->ecompo.size;
|
||||||
|
result->ecompo.name = LC_CopyAST(arena, n->ecompo.name);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprCast: {
|
||||||
|
result->ecast.type = LC_CopyAST(arena, n->ecast.type);
|
||||||
|
result->ecast.expr = LC_CopyAST(arena, n->ecast.expr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprIndex: {
|
||||||
|
result->eindex.index = LC_CopyAST(arena, n->eindex.index);
|
||||||
|
result->eindex.base = LC_CopyAST(arena, n->eindex.base);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: LC_ReportASTError(n, "internal compiler error: failed to LC_CopyAST, got invalid ast kind: %s", LC_ASTKindToString(n->kind));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
316
src/compiler/ast_walk.c
Normal file
316
src/compiler/ast_walk.c
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
LC_FUNCTION void LC_ReserveAST(LC_ASTArray *stack, int size) {
|
||||||
|
if (size > stack->cap) {
|
||||||
|
LC_AST **new_stack = LC_PushArray(stack->arena, LC_AST *, size);
|
||||||
|
|
||||||
|
LC_MemoryCopy(new_stack, stack->data, stack->len * sizeof(LC_AST *));
|
||||||
|
stack->data = new_stack;
|
||||||
|
stack->cap = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_PushAST(LC_ASTArray *stack, LC_AST *ast) {
|
||||||
|
LC_ASSERT(NULL, stack->len <= stack->cap);
|
||||||
|
LC_ASSERT(NULL, stack->arena);
|
||||||
|
if (stack->len == stack->cap) {
|
||||||
|
int new_cap = stack->cap < 16 ? 16 : stack->cap * 2;
|
||||||
|
LC_AST **new_stack = LC_PushArray(stack->arena, LC_AST *, new_cap);
|
||||||
|
|
||||||
|
LC_MemoryCopy(new_stack, stack->data, stack->len * sizeof(LC_AST *));
|
||||||
|
stack->data = new_stack;
|
||||||
|
stack->cap = new_cap;
|
||||||
|
}
|
||||||
|
stack->data[stack->len++] = ast;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_PopAST(LC_ASTArray *stack) {
|
||||||
|
LC_ASSERT(NULL, stack->arena);
|
||||||
|
LC_ASSERT(NULL, stack->len > 0);
|
||||||
|
stack->len -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_AST *LC_GetLastAST(LC_ASTArray *arr) {
|
||||||
|
LC_ASSERT(NULL, arr->len > 0);
|
||||||
|
LC_AST *result = arr->data[arr->len - 1];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_WalkAST(LC_ASTWalker *ctx, LC_AST *n) {
|
||||||
|
if (!ctx->depth_first) {
|
||||||
|
ctx->proc(ctx, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->dont_recurse) {
|
||||||
|
ctx->dont_recurse = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_PushAST(&ctx->stack, n);
|
||||||
|
switch (n->kind) {
|
||||||
|
case LC_ASTKind_TypespecIdent:
|
||||||
|
case LC_ASTKind_ExprIdent:
|
||||||
|
case LC_ASTKind_ExprString:
|
||||||
|
case LC_ASTKind_ExprInt:
|
||||||
|
case LC_ASTKind_ExprFloat:
|
||||||
|
case LC_ASTKind_GlobImport:
|
||||||
|
case LC_ASTKind_ExprBool:
|
||||||
|
case LC_ASTKind_StmtBreak:
|
||||||
|
case LC_ASTKind_StmtContinue: break;
|
||||||
|
|
||||||
|
case LC_ASTKind_Package: {
|
||||||
|
LC_ASTFor(it, n->apackage.ffile) LC_WalkAST(ctx, it);
|
||||||
|
|
||||||
|
ctx->inside_discarded += 1;
|
||||||
|
if (ctx->visit_discarded) LC_ASTFor(it, n->apackage.fdiscarded) LC_WalkAST(ctx, it);
|
||||||
|
ctx->inside_discarded -= 1;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_File: {
|
||||||
|
LC_ASTFor(it, n->afile.fimport) LC_WalkAST(ctx, it);
|
||||||
|
LC_ASTFor(it, n->afile.fdecl) {
|
||||||
|
if (ctx->visit_notes == false && it->kind == LC_ASTKind_DeclNote) continue;
|
||||||
|
LC_WalkAST(ctx, it);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->inside_discarded += 1;
|
||||||
|
if (ctx->visit_discarded) LC_ASTFor(it, n->afile.fdiscarded) LC_WalkAST(ctx, it);
|
||||||
|
ctx->inside_discarded -= 1;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_DeclProc: {
|
||||||
|
LC_WalkAST(ctx, n->dproc.type);
|
||||||
|
if (n->dproc.body) LC_WalkAST(ctx, n->dproc.body);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_NoteList: {
|
||||||
|
if (ctx->visit_notes) LC_ASTFor(it, n->anote_list.first) LC_WalkAST(ctx, it);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_TypespecProcArg: {
|
||||||
|
LC_WalkAST(ctx, n->tproc_arg.type);
|
||||||
|
if (n->tproc_arg.expr) LC_WalkAST(ctx, n->tproc_arg.expr);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_TypespecAggMem: {
|
||||||
|
LC_WalkAST(ctx, n->tproc_arg.type);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprNote: {
|
||||||
|
ctx->inside_note += 1;
|
||||||
|
if (ctx->visit_notes) LC_WalkAST(ctx, n->enote.expr);
|
||||||
|
ctx->inside_note -= 1;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtSwitch: {
|
||||||
|
LC_WalkAST(ctx, n->sswitch.expr);
|
||||||
|
LC_ASTFor(it, n->sswitch.first) LC_WalkAST(ctx, it);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtSwitchCase: {
|
||||||
|
LC_ASTFor(it, n->scase.first) LC_WalkAST(ctx, it);
|
||||||
|
LC_WalkAST(ctx, n->scase.body);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_StmtSwitchDefault: {
|
||||||
|
LC_ASTFor(it, n->scase.first) LC_WalkAST(ctx, it);
|
||||||
|
LC_WalkAST(ctx, n->scase.body);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtIf: {
|
||||||
|
LC_WalkAST(ctx, n->sif.expr);
|
||||||
|
LC_WalkAST(ctx, n->sif.body);
|
||||||
|
LC_ASTFor(it, n->sif.first) LC_WalkAST(ctx, it);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_StmtElse:
|
||||||
|
case LC_ASTKind_StmtElseIf: {
|
||||||
|
if (n->sif.expr) LC_WalkAST(ctx, n->sif.expr);
|
||||||
|
LC_WalkAST(ctx, n->sif.body);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_DeclNote: {
|
||||||
|
ctx->inside_note += 1;
|
||||||
|
if (ctx->visit_notes) LC_WalkAST(ctx, n->dnote.expr);
|
||||||
|
ctx->inside_note -= 1;
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_DeclUnion:
|
||||||
|
case LC_ASTKind_DeclStruct: {
|
||||||
|
LC_ASTFor(it, n->dagg.first) LC_WalkAST(ctx, it);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_DeclVar: {
|
||||||
|
if (n->dvar.type) LC_WalkAST(ctx, n->dvar.type);
|
||||||
|
if (n->dvar.expr) LC_WalkAST(ctx, n->dvar.expr);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_DeclConst: {
|
||||||
|
if (n->dconst.expr) LC_WalkAST(ctx, n->dconst.expr);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_DeclTypedef: {
|
||||||
|
LC_WalkAST(ctx, n->dtypedef.type);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_TypespecField: {
|
||||||
|
LC_WalkAST(ctx, n->efield.left);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_TypespecPointer: {
|
||||||
|
LC_WalkAST(ctx, n->tpointer.base);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_TypespecArray: {
|
||||||
|
LC_WalkAST(ctx, n->tarray.base);
|
||||||
|
if (n->tarray.index) LC_WalkAST(ctx, n->tarray.index);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_TypespecProc: {
|
||||||
|
LC_ASTFor(it, n->tproc.first) LC_WalkAST(ctx, it);
|
||||||
|
if (n->tproc.ret) LC_WalkAST(ctx, n->tproc.ret);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_StmtBlock: {
|
||||||
|
LC_ASTFor(it, n->sblock.first) LC_WalkAST(ctx, it);
|
||||||
|
// hmm should we inline defers or maybe remove them from
|
||||||
|
// the stmt list?
|
||||||
|
// LC_ASTFor(it, n->sblock.first_defer) LC_WalkAST(ctx, it);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtNote: {
|
||||||
|
ctx->inside_note += 1;
|
||||||
|
if (ctx->visit_notes) LC_WalkAST(ctx, n->snote.expr);
|
||||||
|
ctx->inside_note -= 1;
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_StmtReturn: {
|
||||||
|
if (n->sreturn.expr) LC_WalkAST(ctx, n->sreturn.expr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtDefer: {
|
||||||
|
LC_WalkAST(ctx, n->sdefer.body);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_StmtFor: {
|
||||||
|
if (n->sfor.init) LC_WalkAST(ctx, n->sfor.init);
|
||||||
|
if (n->sfor.cond) LC_WalkAST(ctx, n->sfor.cond);
|
||||||
|
if (n->sfor.inc) LC_WalkAST(ctx, n->sfor.inc);
|
||||||
|
LC_WalkAST(ctx, n->sfor.body);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtAssign: {
|
||||||
|
LC_WalkAST(ctx, n->sassign.left);
|
||||||
|
LC_WalkAST(ctx, n->sassign.right);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_StmtExpr: {
|
||||||
|
LC_WalkAST(ctx, n->sexpr.expr);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_StmtVar: {
|
||||||
|
if (n->svar.type) LC_WalkAST(ctx, n->svar.type);
|
||||||
|
if (n->svar.expr) LC_WalkAST(ctx, n->svar.expr);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_StmtConst: {
|
||||||
|
LC_WalkAST(ctx, n->sconst.expr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprType: {
|
||||||
|
LC_WalkAST(ctx, n->etype.type);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_ExprAddPtr:
|
||||||
|
case LC_ASTKind_ExprBinary: {
|
||||||
|
LC_WalkAST(ctx, n->ebinary.left);
|
||||||
|
LC_WalkAST(ctx, n->ebinary.right);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_ExprGetPointerOfValue:
|
||||||
|
case LC_ASTKind_ExprGetValueOfPointer:
|
||||||
|
case LC_ASTKind_ExprUnary: {
|
||||||
|
LC_WalkAST(ctx, n->eunary.expr);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_ExprCompoundItem:
|
||||||
|
case LC_ASTKind_ExprCallItem: {
|
||||||
|
if (n->ecompo_item.index) LC_WalkAST(ctx, n->ecompo_item.index);
|
||||||
|
LC_WalkAST(ctx, n->ecompo_item.expr);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_Note: {
|
||||||
|
ctx->inside_note += 1;
|
||||||
|
if (n->ecompo.name) LC_WalkAST(ctx, n->ecompo.name);
|
||||||
|
LC_ASTFor(it, n->ecompo.first) LC_WalkAST(ctx, it);
|
||||||
|
ctx->inside_note -= 1;
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_ExprBuiltin: {
|
||||||
|
ctx->inside_builtin += 1;
|
||||||
|
if (n->ecompo.name) LC_WalkAST(ctx, n->ecompo.name);
|
||||||
|
LC_ASTFor(it, n->ecompo.first) LC_WalkAST(ctx, it);
|
||||||
|
ctx->inside_builtin -= 1;
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_ExprCall:
|
||||||
|
case LC_ASTKind_ExprCompound: {
|
||||||
|
if (n->ecompo.name) LC_WalkAST(ctx, n->ecompo.name);
|
||||||
|
LC_ASTFor(it, n->ecompo.first) LC_WalkAST(ctx, it);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_ExprCast: {
|
||||||
|
LC_WalkAST(ctx, n->ecast.type);
|
||||||
|
LC_WalkAST(ctx, n->ecast.expr);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_ExprField: {
|
||||||
|
LC_WalkAST(ctx, n->efield.left);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_ExprIndex: {
|
||||||
|
LC_WalkAST(ctx, n->eindex.index);
|
||||||
|
LC_WalkAST(ctx, n->eindex.base);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_Ignore:
|
||||||
|
case LC_ASTKind_Error:
|
||||||
|
default: LC_ReportASTError(n, "internal compiler error: got invalid ast kind during ast walk: %s", LC_ASTKindToString(n->kind));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->visit_notes && n->notes) {
|
||||||
|
LC_WalkAST(ctx, n->notes);
|
||||||
|
}
|
||||||
|
LC_PopAST(&ctx->stack);
|
||||||
|
|
||||||
|
if (ctx->depth_first) {
|
||||||
|
ctx->proc(ctx, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_ASTWalker LC_GetDefaultWalker(LC_Arena *arena, LC_ASTWalkProc *proc) {
|
||||||
|
LC_ASTWalker result = {0};
|
||||||
|
result.stack.arena = arena;
|
||||||
|
result.proc = proc;
|
||||||
|
result.depth_first = true;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void WalkAndFlattenAST(LC_ASTWalker *ctx, LC_AST *n) {
|
||||||
|
LC_ASTArray *array = (LC_ASTArray *)ctx->user_data;
|
||||||
|
LC_PushAST(array, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_ASTArray LC_FlattenAST(LC_Arena *arena, LC_AST *n) {
|
||||||
|
LC_ASTArray array = {arena};
|
||||||
|
LC_ASTWalker walker = LC_GetDefaultWalker(arena, WalkAndFlattenAST);
|
||||||
|
walker.user_data = (void *)&array;
|
||||||
|
LC_WalkAST(&walker, n);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void WalkToFindSizeofLike(LC_ASTWalker *w, LC_AST *n) {
|
||||||
|
if (n->kind == LC_ASTKind_ExprBuiltin) {
|
||||||
|
LC_ASSERT(n, n->ecompo.name->kind == LC_ASTKind_ExprIdent);
|
||||||
|
if (n->ecompo.name->eident.name == L->isizeof || n->ecompo.name->eident.name == L->ialignof || n->ecompo.name->eident.name == L->ioffsetof) {
|
||||||
|
((bool *)w->user_data)[0] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION bool LC_ContainsCBuiltin(LC_AST *n) {
|
||||||
|
LC_TempArena checkpoint = LC_BeginTemp(L->arena);
|
||||||
|
bool found = false;
|
||||||
|
{
|
||||||
|
LC_ASTWalker walker = LC_GetDefaultWalker(L->arena, WalkToFindSizeofLike);
|
||||||
|
walker.depth_first = false;
|
||||||
|
walker.user_data = (void *)&found;
|
||||||
|
LC_WalkAST(&walker, n);
|
||||||
|
}
|
||||||
|
LC_EndTemp(checkpoint);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void SetASTPosOnAll_Walk(LC_ASTWalker *ctx, LC_AST *n) {
|
||||||
|
n->pos = (LC_Token *)ctx->user_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_SetASTPosOnAll(LC_AST *n, LC_Token *pos) {
|
||||||
|
LC_TempArena check = LC_BeginTemp(L->arena);
|
||||||
|
LC_ASTWalker walker = LC_GetDefaultWalker(L->arena, SetASTPosOnAll_Walk);
|
||||||
|
walker.user_data = (void *)pos;
|
||||||
|
LC_WalkAST(&walker, n);
|
||||||
|
LC_EndTemp(check);
|
||||||
|
}
|
||||||
1471
src/compiler/bigint.c
Normal file
1471
src/compiler/bigint.c
Normal file
File diff suppressed because it is too large
Load Diff
198
src/compiler/common.c
Normal file
198
src/compiler/common.c
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
#if __cplusplus
|
||||||
|
#define LC_Alignof(...) alignof(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define LC_Alignof(...) _Alignof(__VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LC_WRAP_AROUND_POWER_OF_2(x, pow2) (((x) & ((pow2)-1llu)))
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#define LC_DebugBreak() (L->breakpoint_on_error && IsDebuggerPresent() && (__debugbreak(), 0))
|
||||||
|
#else
|
||||||
|
#define LC_DebugBreak() (L->breakpoint_on_error && (__builtin_trap(), 0))
|
||||||
|
#endif
|
||||||
|
#define LC_FatalError() (L->breakpoint_on_error ? LC_DebugBreak() : (LC_Exit(1), 0))
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_IgnoreMessage(LC_Token *pos, char *str, int len) {
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_SendErrorMessage(LC_Token *pos, LC_String s8) {
|
||||||
|
if (L->on_message) {
|
||||||
|
L->on_message(pos, s8.str, (int)s8.len);
|
||||||
|
} else {
|
||||||
|
if (pos) {
|
||||||
|
LC_String line = LC_GetTokenLine(pos);
|
||||||
|
LC_String fmt = LC_Format(L->arena, "%s(%d,%d): error: %.*s\n%.*s", (char *)pos->lex->file, pos->line, pos->column, LC_Expand(s8), LC_Expand(line));
|
||||||
|
LC_Print(fmt.str, fmt.len);
|
||||||
|
} else {
|
||||||
|
LC_Print(s8.str, s8.len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LC_DebugBreak();
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_SendErrorMessagef(LC_Lex *x, LC_Token *pos, const char *str, ...) {
|
||||||
|
LC_FORMAT(L->arena, str, s8);
|
||||||
|
LC_SendErrorMessage(pos, s8);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_HandleFatalError(void) {
|
||||||
|
if (L->on_fatal_error) {
|
||||||
|
L->on_fatal_error();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LC_FatalError();
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_MapReserve(LC_Map *map, int size) {
|
||||||
|
LC_Map old_map = *map;
|
||||||
|
|
||||||
|
LC_ASSERT(NULL, LC_IS_POW2(size));
|
||||||
|
map->len = 0;
|
||||||
|
map->cap = size;
|
||||||
|
LC_ASSERT(NULL, map->arena);
|
||||||
|
|
||||||
|
map->entries = LC_PushArray(map->arena, LC_MapEntry, map->cap);
|
||||||
|
|
||||||
|
if (old_map.entries) {
|
||||||
|
for (int i = 0; i < old_map.cap; i += 1) {
|
||||||
|
LC_MapEntry *it = old_map.entries + i;
|
||||||
|
if (it->key) LC_InsertMapEntry(map, it->key, it->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FNV HASH (1a?)
|
||||||
|
LC_FUNCTION uint64_t LC_HashBytes(void *data, uint64_t size) {
|
||||||
|
uint8_t *data8 = (uint8_t *)data;
|
||||||
|
uint64_t hash = (uint64_t)14695981039346656037ULL;
|
||||||
|
for (uint64_t i = 0; i < size; i++) {
|
||||||
|
hash = hash ^ (uint64_t)(data8[i]);
|
||||||
|
hash = hash * (uint64_t)1099511628211ULL;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION uint64_t LC_HashMix(uint64_t x, uint64_t y) {
|
||||||
|
x ^= y;
|
||||||
|
x *= 0xff51afd7ed558ccd;
|
||||||
|
x ^= x >> 32;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION int LC_NextPow2(int v) {
|
||||||
|
v--;
|
||||||
|
v |= v >> 1;
|
||||||
|
v |= v >> 2;
|
||||||
|
v |= v >> 4;
|
||||||
|
v |= v >> 8;
|
||||||
|
v |= v >> 16;
|
||||||
|
v++;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_MapEntry *LC_GetMapEntryEx(LC_Map *map, uint64_t key) {
|
||||||
|
LC_ASSERT(NULL, key);
|
||||||
|
if (map->len * 2 >= map->cap) {
|
||||||
|
LC_MapReserve(map, map->cap * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t hash = LC_HashBytes(&key, sizeof(key));
|
||||||
|
if (hash == 0) hash += 1;
|
||||||
|
uint64_t index = LC_WRAP_AROUND_POWER_OF_2(hash, map->cap);
|
||||||
|
uint64_t i = index;
|
||||||
|
for (;;) {
|
||||||
|
LC_MapEntry *it = map->entries + i;
|
||||||
|
if (it->key == key || it->key == 0) {
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = LC_WRAP_AROUND_POWER_OF_2(i + 1, map->cap);
|
||||||
|
if (i == index) return NULL;
|
||||||
|
}
|
||||||
|
LC_ASSERT(NULL, !"invalid codepath");
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION bool LC_InsertWithoutReplace(LC_Map *map, void *key, void *value) {
|
||||||
|
LC_MapEntry *entry = LC_GetMapEntryEx(map, (uint64_t)key);
|
||||||
|
if (entry->key != 0) return false;
|
||||||
|
|
||||||
|
map->len += 1;
|
||||||
|
entry->key = (uint64_t)key;
|
||||||
|
entry->value = (uint64_t)value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_MapEntry *LC_InsertMapEntry(LC_Map *map, uint64_t key, uint64_t value) {
|
||||||
|
LC_MapEntry *entry = LC_GetMapEntryEx(map, key);
|
||||||
|
if (entry->key == key) {
|
||||||
|
entry->value = value;
|
||||||
|
}
|
||||||
|
if (entry->key == 0) {
|
||||||
|
entry->key = key;
|
||||||
|
entry->value = value;
|
||||||
|
map->len += 1;
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_MapEntry *LC_GetMapEntry(LC_Map *map, uint64_t key) {
|
||||||
|
LC_MapEntry *entry = LC_GetMapEntryEx(map, key);
|
||||||
|
if (entry && entry->key == key) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_MapInsert(LC_Map *map, LC_String keystr, void *value) {
|
||||||
|
uint64_t key = LC_HashBytes(keystr.str, keystr.len);
|
||||||
|
LC_InsertMapEntry(map, key, (uint64_t)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void *LC_MapGet(LC_Map *map, LC_String keystr) {
|
||||||
|
uint64_t key = LC_HashBytes(keystr.str, keystr.len);
|
||||||
|
LC_MapEntry *r = LC_GetMapEntry(map, key);
|
||||||
|
return r ? (void *)r->value : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_MapInsertU64(LC_Map *map, uint64_t key, void *value) {
|
||||||
|
LC_InsertMapEntry(map, key, (uint64_t)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void *LC_MapGetU64(LC_Map *map, uint64_t key) {
|
||||||
|
LC_MapEntry *r = LC_GetMapEntry(map, key);
|
||||||
|
return r ? (void *)r->value : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void *LC_MapGetP(LC_Map *map, void *key) {
|
||||||
|
return LC_MapGetU64(map, (uint64_t)key);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_MapInsertP(LC_Map *map, void *key, void *value) {
|
||||||
|
LC_InsertMapEntry(map, (uint64_t)key, (uint64_t)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_MapClear(LC_Map *map) {
|
||||||
|
if (map->len != 0) LC_MemoryZero(map->entries, map->cap * sizeof(LC_MapEntry));
|
||||||
|
map->len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION size_t LC_GetAlignOffset(size_t size, size_t align) {
|
||||||
|
size_t mask = align - 1;
|
||||||
|
size_t val = size & mask;
|
||||||
|
if (val) {
|
||||||
|
val = align - val;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION size_t LC_AlignUp(size_t size, size_t align) {
|
||||||
|
size_t result = size + LC_GetAlignOffset(size, align);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION size_t LC_AlignDown(size_t size, size_t align) {
|
||||||
|
size += 1; // Make sure when align is 8 doesn't get rounded down to 0
|
||||||
|
size_t result = size - (align - LC_GetAlignOffset(size, align));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
72
src/compiler/extended_passes.c
Normal file
72
src/compiler/extended_passes.c
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
LC_FUNCTION void WalkAndCountDeclRefs(LC_ASTWalker *ctx, LC_AST *n) {
|
||||||
|
LC_Decl *decl = NULL;
|
||||||
|
if (n->kind == LC_ASTKind_ExprIdent || n->kind == LC_ASTKind_TypespecIdent) {
|
||||||
|
if (n->eident.resolved_decl) decl = n->eident.resolved_decl;
|
||||||
|
}
|
||||||
|
if (n->kind == LC_ASTKind_ExprField) {
|
||||||
|
if (n->efield.resolved_decl) decl = n->efield.resolved_decl;
|
||||||
|
}
|
||||||
|
if (decl) {
|
||||||
|
LC_Map *map_of_visits = (LC_Map *)ctx->user_data;
|
||||||
|
intptr_t visited = (intptr_t)LC_MapGetP(map_of_visits, decl);
|
||||||
|
LC_MapInsertP(map_of_visits, decl, (void *)(visited + 1));
|
||||||
|
if (visited == 0 && decl->ast->kind != LC_ASTKind_Null) {
|
||||||
|
LC_WalkAST(ctx, decl->ast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Map LC_CountDeclRefs(LC_Arena *arena) {
|
||||||
|
LC_Map map = {arena};
|
||||||
|
LC_MapReserve(&map, 512);
|
||||||
|
|
||||||
|
LC_AST *package = LC_GetPackageByName(L->first_package);
|
||||||
|
LC_ASTWalker walker = LC_GetDefaultWalker(arena, WalkAndCountDeclRefs);
|
||||||
|
walker.user_data = (void *)↦
|
||||||
|
walker.visit_notes = true;
|
||||||
|
LC_WalkAST(&walker, package);
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_RemoveUnreferencedGlobalDecls(LC_Map *map_of_visits) {
|
||||||
|
for (LC_ASTRef *it = L->ordered_packages.first; it; it = it->next) {
|
||||||
|
for (LC_Decl *decl = it->ast->apackage.first_ordered; decl;) {
|
||||||
|
intptr_t ref_count = (intptr_t)LC_MapGetP(map_of_visits, decl);
|
||||||
|
|
||||||
|
LC_Decl *remove = decl;
|
||||||
|
decl = decl->next;
|
||||||
|
if (ref_count == 0 && remove->foreign_name != LC_ILit("main")) {
|
||||||
|
LC_DLLRemove(it->ast->apackage.first_ordered, it->ast->apackage.last_ordered, remove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_ErrorOnUnreferencedLocals(LC_Map *map_of_visits) {
|
||||||
|
LC_Decl *first = (LC_Decl *)L->decl_arena->memory.data;
|
||||||
|
for (int i = 0; i < L->decl_count; i += 1) {
|
||||||
|
LC_Decl *decl = first + i;
|
||||||
|
if (decl->package == L->builtin_package) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
intptr_t ref_count = (intptr_t)LC_MapGetP(map_of_visits, decl);
|
||||||
|
if (ref_count == 0) {
|
||||||
|
if (LC_IsStmt(decl->ast)) {
|
||||||
|
if (!LC_HasNote(decl->ast, L->iunused)) LC_ReportASTError(decl->ast, "unused local variable '%s'", decl->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_FindUnusedLocalsAndRemoveUnusedGlobalDecls(void) {
|
||||||
|
if (L->errors) return;
|
||||||
|
LC_TempArena check = LC_BeginTemp(L->arena);
|
||||||
|
|
||||||
|
LC_Map map = LC_CountDeclRefs(check.arena);
|
||||||
|
LC_ErrorOnUnreferencedLocals(&map);
|
||||||
|
LC_RemoveUnreferencedGlobalDecls(&map);
|
||||||
|
|
||||||
|
LC_EndTemp(check);
|
||||||
|
}
|
||||||
677
src/compiler/genc.c
Normal file
677
src/compiler/genc.c
Normal file
@@ -0,0 +1,677 @@
|
|||||||
|
const bool LC_GenCInternalGenerateSizeofs = true;
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenCLineDirective(LC_AST *node) {
|
||||||
|
if (L->emit_line_directives) {
|
||||||
|
L->printer.last_line_num = node->pos->line;
|
||||||
|
LC_GenLinef("#line %d", L->printer.last_line_num);
|
||||||
|
LC_Intern file = node->pos->lex->file;
|
||||||
|
if (file != L->printer.last_filename) {
|
||||||
|
L->printer.last_filename = file;
|
||||||
|
LC_Genf(" \"%s\"", (char *)L->printer.last_filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenLastCLineDirective(void) {
|
||||||
|
if (L->emit_line_directives) {
|
||||||
|
LC_Genf("#line %d", L->printer.last_line_num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenCLineDirectiveNum(int num) {
|
||||||
|
if (L->emit_line_directives) {
|
||||||
|
LC_Genf("#line %d", num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION char *LC_GenCTypeParen(char *str, char c) {
|
||||||
|
return c && c != '[' ? LC_Strf("(%s)", str) : str;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION char *LC_GenCType(LC_Type *type, char *str) {
|
||||||
|
switch (type->kind) {
|
||||||
|
case LC_TypeKind_Pointer: {
|
||||||
|
return LC_GenCType(type->tptr.base, LC_GenCTypeParen(LC_Strf("*%s", str), *str));
|
||||||
|
} break;
|
||||||
|
case LC_TypeKind_Array: {
|
||||||
|
if (type->tarray.size == 0) {
|
||||||
|
return LC_GenCType(type->tarray.base, LC_GenCTypeParen(LC_Strf("%s[]", str), *str));
|
||||||
|
} else {
|
||||||
|
return LC_GenCType(type->tarray.base, LC_GenCTypeParen(LC_Strf("%s[%d]", str, type->tarray.size), *str));
|
||||||
|
}
|
||||||
|
|
||||||
|
} break;
|
||||||
|
case LC_TypeKind_Proc: {
|
||||||
|
LC_StringList out = {0};
|
||||||
|
LC_Addf(L->arena, &out, "(*%s)", str);
|
||||||
|
LC_Addf(L->arena, &out, "(");
|
||||||
|
if (type->tagg.mems.count == 0) {
|
||||||
|
LC_Addf(L->arena, &out, "void");
|
||||||
|
} else {
|
||||||
|
int i = 0;
|
||||||
|
for (LC_TypeMember *it = type->tproc.args.first; it; it = it->next) {
|
||||||
|
LC_Addf(L->arena, &out, "%s%s", i == 0 ? "" : ", ", LC_GenCType(it->type, ""));
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type->tproc.vargs) {
|
||||||
|
LC_Addf(L->arena, &out, ", ...");
|
||||||
|
}
|
||||||
|
LC_Addf(L->arena, &out, ")");
|
||||||
|
char *front = LC_MergeString(L->arena, out).str;
|
||||||
|
char *result = LC_GenCType(type->tproc.ret, front);
|
||||||
|
return result;
|
||||||
|
} break;
|
||||||
|
default: return LC_Strf("%s%s%s", type->decl->foreign_name, str[0] ? " " : "", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Intern LC_GetStringFromSingleArgNote(LC_AST *note) {
|
||||||
|
LC_ASSERT(note, note->kind == LC_ASTKind_Note);
|
||||||
|
LC_ASSERT(note, note->ecompo.first == note->ecompo.last);
|
||||||
|
LC_AST *arg = note->ecompo.first;
|
||||||
|
LC_ASSERT(note, arg->kind == LC_ASTKind_ExprCallItem);
|
||||||
|
LC_AST *str = arg->ecompo_item.expr;
|
||||||
|
LC_ASSERT(note, str->kind == LC_ASTKind_ExprString);
|
||||||
|
return str->eatom.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenCCompound(LC_AST *n) {
|
||||||
|
LC_Type *type = n->type;
|
||||||
|
if (LC_IsAggType(type)) {
|
||||||
|
LC_ResolvedCompo *rd = n->ecompo.resolved_items;
|
||||||
|
LC_Genf("{");
|
||||||
|
if (rd->first == NULL) LC_Genf("0");
|
||||||
|
for (LC_ResolvedCompoItem *it = rd->first; it; it = it->next) {
|
||||||
|
LC_Genf(".%s = ", (char *)it->t->name);
|
||||||
|
LC_GenCExpr(it->expr);
|
||||||
|
if (it->next) LC_Genf(", ");
|
||||||
|
}
|
||||||
|
LC_Genf("}");
|
||||||
|
} else if (LC_IsArray(type)) {
|
||||||
|
LC_ResolvedArrayCompo *rd = n->ecompo.resolved_array_items;
|
||||||
|
LC_Genf("{");
|
||||||
|
for (LC_ResolvedCompoArrayItem *it = rd->first; it; it = it->next) {
|
||||||
|
LC_Genf("[%d] = ", it->index);
|
||||||
|
LC_GenCExpr(it->comp->ecompo_item.expr);
|
||||||
|
if (it->next) LC_Genf(", ");
|
||||||
|
}
|
||||||
|
LC_Genf("}");
|
||||||
|
} else {
|
||||||
|
LC_ReportASTError(n, "internal compiler error: got unhandled case in %s", __FUNCTION__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_THREAD_LOCAL bool GC_SpecialCase_GlobalScopeStringDecl;
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenCString(char *s, LC_Type *type) {
|
||||||
|
if (type == L->tstring) {
|
||||||
|
if (!GC_SpecialCase_GlobalScopeStringDecl) LC_Genf("(LC_String)");
|
||||||
|
LC_Genf("{ ");
|
||||||
|
}
|
||||||
|
LC_Genf("\"");
|
||||||
|
for (int i = 0; s[i]; i += 1) {
|
||||||
|
LC_String escape = LC_GetEscapeString(s[i]);
|
||||||
|
if (escape.len) {
|
||||||
|
LC_Genf("%.*s", LC_Expand(escape));
|
||||||
|
} else {
|
||||||
|
LC_Genf("%c", s[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LC_Genf("\"");
|
||||||
|
if (type == L->tstring) LC_Genf(", %d }", (int)LC_StrLen(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION char *LC_GenCVal(LC_TypeAndVal v, LC_Type *type) {
|
||||||
|
char *str = LC_GenLCTypeVal(v);
|
||||||
|
switch (type->kind) {
|
||||||
|
case LC_TypeKind_uchar:
|
||||||
|
case LC_TypeKind_ushort:
|
||||||
|
case LC_TypeKind_uint: str = LC_Strf("%su", str); break;
|
||||||
|
case LC_TypeKind_ulong: str = LC_Strf("%sul", str); break;
|
||||||
|
case LC_TypeKind_Pointer:
|
||||||
|
case LC_TypeKind_Proc:
|
||||||
|
case LC_TypeKind_ullong: str = LC_Strf("%sull", str); break;
|
||||||
|
case LC_TypeKind_long: str = LC_Strf("%sull", str); break;
|
||||||
|
case LC_TypeKind_llong: str = LC_Strf("%sull", str); break;
|
||||||
|
case LC_TypeKind_float: str = LC_Strf("%sf", str); break;
|
||||||
|
case LC_TypeKind_UntypedFloat: str = LC_Strf(" /*utfloat*/%s", str); break;
|
||||||
|
case LC_TypeKind_UntypedInt: str = LC_Strf(" /*utint*/%sull", str); break;
|
||||||
|
default: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (LC_IsUTInt(v.type) && !LC_IsUntyped(type) && type->size < 4) {
|
||||||
|
str = LC_Strf("(%s)%s", LC_GenCType(type, ""), str);
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenCExpr(LC_AST *n) {
|
||||||
|
LC_ASSERT(n, LC_IsExpr(n));
|
||||||
|
intptr_t is_any = (intptr_t)LC_MapGetP(&L->implicit_any, n);
|
||||||
|
if (is_any) LC_Genf("(LC_Any){%d, (%s[]){", n->type->id, LC_GenCType(n->type, ""));
|
||||||
|
|
||||||
|
if (n->const_val.type) {
|
||||||
|
bool contains_sizeof_like = LC_GenCInternalGenerateSizeofs ? LC_ContainsCBuiltin(n) : false;
|
||||||
|
if (!contains_sizeof_like) {
|
||||||
|
if (LC_IsUTStr(n->const_val.type)) {
|
||||||
|
LC_GenCString((char *)n->const_val.name, n->type);
|
||||||
|
} else {
|
||||||
|
char *val = LC_GenCVal(n->const_val, n->type);
|
||||||
|
LC_Genf("%s", val);
|
||||||
|
}
|
||||||
|
if (is_any) LC_Genf("}}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_Type *type = n->type;
|
||||||
|
switch (n->kind) {
|
||||||
|
case LC_ASTKind_ExprIdent: {
|
||||||
|
LC_Genf("%s", (char *)n->eident.resolved_decl->foreign_name);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprCast: {
|
||||||
|
LC_Genf("(");
|
||||||
|
LC_Genf("(%s)", LC_GenCType(type, ""));
|
||||||
|
LC_Genf("(");
|
||||||
|
LC_GenCExpr(n->ecast.expr);
|
||||||
|
LC_Genf(")");
|
||||||
|
LC_Genf(")");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprUnary: {
|
||||||
|
LC_Genf("%s(", LC_TokenKindToOperator(n->eunary.op));
|
||||||
|
LC_GenCExpr(n->eunary.expr);
|
||||||
|
LC_Genf(")");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprAddPtr: {
|
||||||
|
LC_Genf("(");
|
||||||
|
LC_GenCExpr(n->ebinary.left);
|
||||||
|
LC_Genf("+");
|
||||||
|
LC_GenCExpr(n->ebinary.right);
|
||||||
|
LC_Genf(")");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprBinary: {
|
||||||
|
LC_Genf("(");
|
||||||
|
LC_GenCExpr(n->ebinary.left);
|
||||||
|
LC_Genf("%s", LC_TokenKindToOperator(n->ebinary.op));
|
||||||
|
LC_GenCExpr(n->ebinary.right);
|
||||||
|
LC_Genf(")");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprIndex: {
|
||||||
|
LC_Genf("(");
|
||||||
|
LC_GenCExpr(n->eindex.base);
|
||||||
|
LC_Genf("[");
|
||||||
|
LC_GenCExpr(n->eindex.index);
|
||||||
|
LC_Genf("]");
|
||||||
|
LC_Genf(")");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprGetValueOfPointer: {
|
||||||
|
LC_Genf("(*(");
|
||||||
|
LC_GenCExpr(n->eunary.expr);
|
||||||
|
LC_Genf("))");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprGetPointerOfValue: {
|
||||||
|
LC_Genf("(&(");
|
||||||
|
LC_GenCExpr(n->eunary.expr);
|
||||||
|
LC_Genf("))");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprField: {
|
||||||
|
if (n->efield.parent_decl->kind != LC_DeclKind_Import) {
|
||||||
|
LC_Type *left_type = n->efield.left->type;
|
||||||
|
LC_GenCExpr(n->efield.left);
|
||||||
|
if (LC_IsPtr(left_type)) LC_Genf("->");
|
||||||
|
else LC_Genf(".");
|
||||||
|
LC_Genf("%s", (char *)n->efield.right);
|
||||||
|
} else {
|
||||||
|
LC_Genf("%s", (char *)n->efield.resolved_decl->foreign_name);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprCall: {
|
||||||
|
LC_ResolvedCompo *rd = n->ecompo.resolved_items;
|
||||||
|
LC_GenCExpr(n->ecompo.name);
|
||||||
|
LC_Genf("(");
|
||||||
|
for (LC_ResolvedCompoItem *it = rd->first; it; it = it->next) {
|
||||||
|
LC_GenCExpr(it->expr);
|
||||||
|
if (it->next) LC_Genf(", ");
|
||||||
|
}
|
||||||
|
LC_Genf(")");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprCompound: {
|
||||||
|
LC_Genf("(%s)", LC_GenCType(type, ""));
|
||||||
|
LC_GenCCompound(n);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprBuiltin: {
|
||||||
|
LC_ASSERT(n, n->ecompo.name->kind == LC_ASTKind_ExprIdent);
|
||||||
|
if (n->ecompo.name->eident.name == L->isizeof) {
|
||||||
|
LC_Genf("sizeof(");
|
||||||
|
LC_AST *expr = n->ecompo.first->ecompo_item.expr;
|
||||||
|
if (expr->kind == LC_ASTKind_ExprType) {
|
||||||
|
LC_Genf("%s", LC_GenCType(expr->type, ""));
|
||||||
|
} else {
|
||||||
|
LC_GenCExpr(expr);
|
||||||
|
}
|
||||||
|
LC_Genf(")");
|
||||||
|
} else if (n->ecompo.name->eident.name == L->ialignof) {
|
||||||
|
LC_Genf("LC_Alignof(");
|
||||||
|
LC_AST *expr = n->ecompo.first->ecompo_item.expr;
|
||||||
|
if (expr->kind == LC_ASTKind_ExprType) {
|
||||||
|
LC_Genf("%s", LC_GenCType(expr->type, ""));
|
||||||
|
} else {
|
||||||
|
LC_GenCExpr(expr);
|
||||||
|
}
|
||||||
|
LC_Genf(")");
|
||||||
|
} else if (n->ecompo.name->eident.name == L->ioffsetof) {
|
||||||
|
LC_AST *i1 = n->ecompo.first->ecompo_item.expr;
|
||||||
|
LC_AST *i2 = n->ecompo.first->next->ecompo_item.expr;
|
||||||
|
LC_Genf("offsetof(%s, %s)", LC_GenCType(i1->type, ""), (char *)i2->eident.name);
|
||||||
|
} else {
|
||||||
|
LC_ReportASTError(n, "internal compiler error: got unhandled case in %s / LC_ASTKind_ExprBuiltin", __FUNCTION__);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: LC_ReportASTError(n, "internal compiler error: got unhandled case in %s", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_any) LC_Genf("}}");
|
||||||
|
}
|
||||||
|
|
||||||
|
const int GC_Stmt_OmitSemicolonAndNewLine = 1;
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenCNote(LC_AST *note) {
|
||||||
|
if (note->ecompo.name->eident.name == L->ic) {
|
||||||
|
LC_Genf("%s", (char *)LC_GetStringFromSingleArgNote(note));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenCVarExpr(LC_AST *n, bool is_declaration) {
|
||||||
|
if (LC_HasNote(n, L->inot_init)) return;
|
||||||
|
|
||||||
|
LC_AST *e = n->dvar.expr;
|
||||||
|
if (n->kind == LC_ASTKind_StmtVar) e = n->svar.expr;
|
||||||
|
if (e) {
|
||||||
|
LC_Genf(" = ");
|
||||||
|
if (e->kind == LC_ASTKind_ExprNote) {
|
||||||
|
LC_GenCNote(e->enote.expr);
|
||||||
|
} else if (is_declaration && e->kind == LC_ASTKind_ExprCompound) {
|
||||||
|
LC_GenCCompound(e);
|
||||||
|
} else {
|
||||||
|
LC_GenCExpr(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LC_Genf(" = {0}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenCDefers(LC_AST *block) {
|
||||||
|
LC_AST *first = block->sblock.first_defer;
|
||||||
|
if (first == NULL) return;
|
||||||
|
|
||||||
|
int save = L->printer.last_line_num;
|
||||||
|
LC_GenLine();
|
||||||
|
LC_GenLastCLineDirective();
|
||||||
|
|
||||||
|
LC_GenLinef("/*defer*/");
|
||||||
|
for (LC_AST *it = first; it; it = it->sdefer.next) {
|
||||||
|
LC_GenCStmtBlock(it->sdefer.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
L->printer.last_line_num = save + 1;
|
||||||
|
LC_GenLine();
|
||||||
|
LC_GenLastCLineDirective();
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenCDefersLoopBreak(LC_AST *n) {
|
||||||
|
LC_ASSERT(n, n->kind == LC_ASTKind_StmtBreak || n->kind == LC_ASTKind_StmtContinue);
|
||||||
|
LC_AST *it = NULL;
|
||||||
|
for (int i = L->printer.out_block_stack.len - 1; i >= 0; i -= 1) {
|
||||||
|
it = L->printer.out_block_stack.data[i];
|
||||||
|
LC_GenCDefers(it);
|
||||||
|
LC_ASSERT(it, it->sblock.kind != SBLK_Proc);
|
||||||
|
if (it->sblock.kind == SBLK_Loop) {
|
||||||
|
if (!n->sbreak.name) break;
|
||||||
|
if (n->sbreak.name && it->sblock.name == n->sbreak.name) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LC_ASSERT(it, it->sblock.kind == SBLK_Loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenCDefersReturn(LC_AST *n) {
|
||||||
|
LC_ASSERT(n, n->kind == LC_ASTKind_StmtReturn);
|
||||||
|
LC_AST *it = NULL;
|
||||||
|
for (int i = L->printer.out_block_stack.len - 1; i >= 0; i -= 1) {
|
||||||
|
it = L->printer.out_block_stack.data[i];
|
||||||
|
LC_GenCDefers(it);
|
||||||
|
if (it->sblock.kind == SBLK_Proc) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LC_ASSERT(it, it);
|
||||||
|
LC_ASSERT(it, it->sblock.kind == SBLK_Proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenCStmt2(LC_AST *n, int flags) {
|
||||||
|
LC_ASSERT(n, LC_IsStmt(n));
|
||||||
|
bool semicolon = !(flags & GC_Stmt_OmitSemicolonAndNewLine);
|
||||||
|
|
||||||
|
if (semicolon) {
|
||||||
|
LC_GenLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (n->kind) {
|
||||||
|
case LC_ASTKind_StmtVar: {
|
||||||
|
LC_Type *type = n->type;
|
||||||
|
LC_Genf("%s", LC_GenCType(type, (char *)n->svar.name));
|
||||||
|
LC_GenCVarExpr(n, true);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_StmtExpr: LC_GenCExpr(n->sexpr.expr); break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtAssign: {
|
||||||
|
// Assigning to array doesn't work in C so we need to handle that
|
||||||
|
// specific compo case here. :CompoArray
|
||||||
|
if (LC_IsArray(n->type) && n->sassign.right->kind == LC_ASTKind_ExprCompound) {
|
||||||
|
LC_ASSERT(n, n->sassign.op == LC_TokenKind_Assign);
|
||||||
|
LC_AST *expr = n->sassign.right;
|
||||||
|
LC_Genf("memset(");
|
||||||
|
LC_GenCExpr(n->sassign.left);
|
||||||
|
LC_Genf(", 0, sizeof(");
|
||||||
|
LC_GenCExpr(n->sassign.left);
|
||||||
|
LC_Genf("));");
|
||||||
|
|
||||||
|
LC_ResolvedArrayCompo *rd = expr->ecompo.resolved_array_items;
|
||||||
|
for (LC_ResolvedCompoArrayItem *it = rd->first; it; it = it->next) {
|
||||||
|
LC_GenCExpr(n->sassign.left);
|
||||||
|
LC_Genf("[%d] = ", it->index);
|
||||||
|
LC_GenCExpr(it->comp->ecompo_item.expr);
|
||||||
|
LC_Genf(";");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
LC_GenCExpr(n->sassign.left);
|
||||||
|
LC_Genf(" %s ", LC_TokenKindToOperator(n->sassign.op));
|
||||||
|
LC_GenCExpr(n->sassign.right);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
default: LC_ReportASTError(n, "internal compiler error: got unhandled case in %s", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (semicolon) LC_Genf(";");
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenCStmt(LC_AST *n) {
|
||||||
|
LC_ASSERT(n, LC_IsStmt(n));
|
||||||
|
LC_GenCLineDirective(n);
|
||||||
|
switch (n->kind) {
|
||||||
|
case LC_ASTKind_StmtConst:
|
||||||
|
case LC_ASTKind_StmtDefer: break;
|
||||||
|
case LC_ASTKind_StmtNote: {
|
||||||
|
LC_GenLine();
|
||||||
|
LC_GenCNote(n->snote.expr);
|
||||||
|
LC_Genf(";");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtReturn: {
|
||||||
|
LC_GenCDefersReturn(n);
|
||||||
|
LC_GenLinef("return");
|
||||||
|
if (n->sreturn.expr) {
|
||||||
|
LC_Genf(" ");
|
||||||
|
LC_GenCExpr(n->sreturn.expr);
|
||||||
|
}
|
||||||
|
LC_Genf(";");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtContinue:
|
||||||
|
case LC_ASTKind_StmtBreak: {
|
||||||
|
const char *stmt = n->kind == LC_ASTKind_StmtBreak ? "break" : "continue";
|
||||||
|
LC_GenCDefersLoopBreak(n);
|
||||||
|
if (n->sbreak.name) {
|
||||||
|
LC_GenLinef("goto %s_%s;", (char *)n->sbreak.name, stmt);
|
||||||
|
} else {
|
||||||
|
LC_GenLinef("%s;", stmt);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtBlock: {
|
||||||
|
LC_GenLinef("/*block*/");
|
||||||
|
LC_GenCStmtBlock(n);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtSwitch: {
|
||||||
|
LC_GenLinef("switch(");
|
||||||
|
LC_GenCExpr(n->sswitch.expr);
|
||||||
|
LC_Genf(") {");
|
||||||
|
|
||||||
|
L->printer.indent += 1;
|
||||||
|
LC_ASTFor(it, n->sswitch.first) {
|
||||||
|
LC_GenCLineDirective(it);
|
||||||
|
if (it->kind == LC_ASTKind_StmtSwitchCase) {
|
||||||
|
LC_ASTFor(label_it, it->scase.first) {
|
||||||
|
LC_GenLinef("case ");
|
||||||
|
LC_GenCExpr(label_it);
|
||||||
|
LC_Genf(":");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (it->kind == LC_ASTKind_StmtSwitchDefault) {
|
||||||
|
LC_GenLinef("default:");
|
||||||
|
}
|
||||||
|
LC_GenCStmtBlock(it->scase.body);
|
||||||
|
if (LC_HasNote(it, L->ifallthrough)) {
|
||||||
|
LC_Genf(" /*@fallthough*/");
|
||||||
|
} else {
|
||||||
|
LC_Genf(" break;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
L->printer.indent -= 1;
|
||||||
|
LC_GenLinef("}");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtFor: {
|
||||||
|
LC_GenLinef("for (");
|
||||||
|
if (n->sfor.init) LC_GenCStmt2(n->sfor.init, GC_Stmt_OmitSemicolonAndNewLine);
|
||||||
|
LC_Genf(";");
|
||||||
|
if (n->sfor.cond) {
|
||||||
|
LC_Genf(" ");
|
||||||
|
LC_GenCExpr(n->sfor.cond);
|
||||||
|
}
|
||||||
|
LC_Genf(";");
|
||||||
|
if (n->sfor.inc) {
|
||||||
|
LC_Genf(" ");
|
||||||
|
LC_GenCStmt2(n->sfor.inc, GC_Stmt_OmitSemicolonAndNewLine);
|
||||||
|
}
|
||||||
|
LC_Genf(")");
|
||||||
|
LC_GenCStmtBlock(n->sfor.body);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtIf: {
|
||||||
|
LC_GenLinef("if ");
|
||||||
|
LC_GenCExprParen(n->sif.expr);
|
||||||
|
LC_GenCStmtBlock(n->sif.body);
|
||||||
|
LC_ASTFor(it, n->sif.first) {
|
||||||
|
LC_GenCLineDirective(it);
|
||||||
|
LC_GenLinef("else");
|
||||||
|
if (it->kind == LC_ASTKind_StmtElseIf) {
|
||||||
|
LC_Genf(" if ");
|
||||||
|
LC_GenCExprParen(it->sif.expr);
|
||||||
|
}
|
||||||
|
LC_GenCStmtBlock(it->sif.body);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: LC_GenCStmt2(n, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenCExprParen(LC_AST *expr) {
|
||||||
|
bool paren = expr->kind != LC_ASTKind_ExprBinary;
|
||||||
|
if (paren) LC_Genf("(");
|
||||||
|
LC_GenCExpr(expr);
|
||||||
|
if (paren) LC_Genf(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenCStmtBlock(LC_AST *n) {
|
||||||
|
LC_PushAST(&L->printer.out_block_stack, n);
|
||||||
|
LC_ASSERT(n, n->kind == LC_ASTKind_StmtBlock);
|
||||||
|
LC_Genf(" {");
|
||||||
|
L->printer.indent += 1;
|
||||||
|
LC_ASTFor(it, n->sblock.first) {
|
||||||
|
LC_GenCStmt(it);
|
||||||
|
}
|
||||||
|
LC_GenCDefers(n);
|
||||||
|
if (n->sblock.name) LC_GenLinef("%s_continue:;", (char *)n->sblock.name);
|
||||||
|
L->printer.indent -= 1;
|
||||||
|
LC_GenLinef("}");
|
||||||
|
if (n->sblock.name) LC_GenLinef("%s_break:;", (char *)n->sblock.name);
|
||||||
|
LC_PopAST(&L->printer.out_block_stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenCProcDecl(LC_Decl *decl) {
|
||||||
|
LC_StringList out = {0};
|
||||||
|
LC_Type *type = decl->type;
|
||||||
|
LC_AST *n = decl->ast;
|
||||||
|
LC_AST *typespec = n->dproc.type;
|
||||||
|
|
||||||
|
LC_Addf(L->arena, &out, "%s(", (char *)decl->foreign_name);
|
||||||
|
if (type->tagg.mems.count == 0) {
|
||||||
|
LC_Addf(L->arena, &out, "void");
|
||||||
|
} else {
|
||||||
|
int i = 0;
|
||||||
|
LC_ASTFor(it, typespec->tproc.first) {
|
||||||
|
LC_Type *type = it->type;
|
||||||
|
LC_Addf(L->arena, &out, "%s%s", i == 0 ? "" : ", ", LC_GenCType(type, (char *)it->tproc_arg.name));
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type->tproc.vargs) {
|
||||||
|
LC_Addf(L->arena, &out, ", ...");
|
||||||
|
}
|
||||||
|
LC_Addf(L->arena, &out, ")");
|
||||||
|
char *front = LC_MergeString(L->arena, out).str;
|
||||||
|
char *result = LC_GenCType(type->tproc.ret, front);
|
||||||
|
|
||||||
|
LC_GenLine();
|
||||||
|
bool is_public = LC_HasNote(n, L->iapi) || decl->foreign_name == L->imain;
|
||||||
|
if (!is_public) LC_Genf("static ");
|
||||||
|
LC_Genf("%s", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenCAggForwardDecl(LC_Decl *decl) {
|
||||||
|
LC_ASSERT(decl->ast, LC_IsAgg(decl->ast));
|
||||||
|
char *agg = LC_GenLCAggName(decl->type);
|
||||||
|
LC_GenLinef("typedef %s %s %s;", agg, (char *)decl->foreign_name, (char *)decl->foreign_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenCTypeDecl(LC_Decl *decl) {
|
||||||
|
LC_AST *n = decl->ast;
|
||||||
|
LC_ASSERT(n, decl->kind == LC_DeclKind_Type);
|
||||||
|
if (n->kind == LC_ASTKind_DeclTypedef) {
|
||||||
|
LC_Type *type = decl->typedef_renamed_type_decl ? decl->typedef_renamed_type_decl->type : decl->type;
|
||||||
|
LC_GenLinef("typedef %s;", LC_GenCType(type, (char *)decl->foreign_name));
|
||||||
|
} else {
|
||||||
|
LC_Type *type = decl->type;
|
||||||
|
LC_Intern name = decl->foreign_name;
|
||||||
|
{
|
||||||
|
bool packed = LC_HasNote(n, L->ipacked) ? true : false;
|
||||||
|
if (packed) LC_GenLinef("#pragma pack(push, 1)");
|
||||||
|
|
||||||
|
LC_GenLinef("%s %s {", LC_GenLCAggName(type), name ? (char *)name : "");
|
||||||
|
L->printer.indent += 1;
|
||||||
|
for (LC_TypeMember *it = type->tagg.mems.first; it; it = it->next) {
|
||||||
|
LC_GenLinef("%s;", LC_GenCType(it->type, (char *)it->name));
|
||||||
|
}
|
||||||
|
L->printer.indent -= 1;
|
||||||
|
LC_GenLinef("};");
|
||||||
|
if (packed) LC_GenLinef("#pragma pack(pop)");
|
||||||
|
LC_GenLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenCVarFDecl(LC_Decl *decl) {
|
||||||
|
if (!LC_HasNote(decl->ast, L->iapi)) return;
|
||||||
|
LC_Type *type = decl->type; // make string arrays assignable
|
||||||
|
LC_GenLinef("extern ");
|
||||||
|
if (LC_HasNote(decl->ast, L->ithread_local)) LC_Genf("_Thread_local ");
|
||||||
|
LC_Genf("%s;", LC_GenCType(type, (char *)decl->foreign_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenCHeader(LC_AST *package) {
|
||||||
|
// C notes
|
||||||
|
LC_ASTFor(file, package->apackage.ffile) {
|
||||||
|
LC_ASTFor(it, file->afile.fdecl) {
|
||||||
|
if (it->kind != LC_ASTKind_DeclNote) continue;
|
||||||
|
|
||||||
|
LC_AST *note = it->dnote.expr;
|
||||||
|
if (note->ecompo.name->eident.name == L->ic) {
|
||||||
|
LC_GenLinef("%s", (char *)LC_GetStringFromSingleArgNote(note));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// struct forward decls
|
||||||
|
LC_DeclFor(decl, package->apackage.first_ordered) {
|
||||||
|
if (decl->is_foreign) continue;
|
||||||
|
LC_AST *n = decl->ast;
|
||||||
|
if (decl->kind == LC_DeclKind_Type && LC_IsAgg(n)) LC_GenCAggForwardDecl(decl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// type decls
|
||||||
|
LC_GenLine();
|
||||||
|
LC_DeclFor(decl, package->apackage.first_ordered) {
|
||||||
|
if (decl->is_foreign) continue;
|
||||||
|
LC_AST *n = decl->ast;
|
||||||
|
if (decl->kind == LC_DeclKind_Type) LC_GenCTypeDecl(decl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// proc and var forward decls
|
||||||
|
LC_DeclFor(decl, package->apackage.first_ordered) {
|
||||||
|
if (decl->is_foreign) continue;
|
||||||
|
LC_AST *n = decl->ast;
|
||||||
|
if (decl->kind == LC_DeclKind_Var) {
|
||||||
|
LC_GenCVarFDecl(decl);
|
||||||
|
} else if (decl->kind == LC_DeclKind_Proc) {
|
||||||
|
LC_GenCProcDecl(decl);
|
||||||
|
LC_Genf(";");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenCImpl(LC_AST *package) {
|
||||||
|
// implementation of vars
|
||||||
|
LC_DeclFor(decl, package->apackage.first_ordered) {
|
||||||
|
if (decl->kind == LC_DeclKind_Var && !decl->is_foreign) {
|
||||||
|
LC_AST *n = decl->ast;
|
||||||
|
LC_Type *type = decl->type; // make string arrays assignable
|
||||||
|
LC_GenLine();
|
||||||
|
if (!LC_HasNote(n, L->iapi)) LC_Genf("static ");
|
||||||
|
if (LC_HasNote(n, L->ithread_local)) LC_Genf("_Thread_local ");
|
||||||
|
LC_Genf("%s", LC_GenCType(type, (char *)decl->foreign_name));
|
||||||
|
|
||||||
|
GC_SpecialCase_GlobalScopeStringDecl = true;
|
||||||
|
LC_GenCVarExpr(n, true);
|
||||||
|
GC_SpecialCase_GlobalScopeStringDecl = false;
|
||||||
|
LC_Genf(";");
|
||||||
|
LC_GenLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// implementation of procs
|
||||||
|
LC_DeclFor(decl, package->apackage.first_ordered) {
|
||||||
|
LC_AST *n = decl->ast;
|
||||||
|
if (decl->kind == LC_DeclKind_Proc && n->dproc.body && !decl->is_foreign) {
|
||||||
|
LC_GenCLineDirective(n);
|
||||||
|
LC_GenCProcDecl(decl);
|
||||||
|
LC_GenCStmtBlock(n->dproc.body);
|
||||||
|
LC_GenLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
259
src/compiler/init.c
Normal file
259
src/compiler/init.c
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
LC_FUNCTION LC_Lang *LC_LangAlloc(void) {
|
||||||
|
LC_Arena *arena = LC_BootstrapArena();
|
||||||
|
LC_Arena *lex_arena = LC_PushStruct(arena, LC_Arena);
|
||||||
|
LC_Arena *decl_arena = LC_PushStruct(arena, LC_Arena);
|
||||||
|
LC_Arena *ast_arena = LC_PushStruct(arena, LC_Arena);
|
||||||
|
LC_Arena *type_arena = LC_PushStruct(arena, LC_Arena);
|
||||||
|
LC_InitArena(lex_arena);
|
||||||
|
LC_InitArena(decl_arena);
|
||||||
|
LC_InitArena(ast_arena);
|
||||||
|
LC_InitArena(type_arena);
|
||||||
|
|
||||||
|
LC_Lang *l = LC_PushStruct(arena, LC_Lang);
|
||||||
|
l->arena = arena;
|
||||||
|
l->lex_arena = lex_arena;
|
||||||
|
l->decl_arena = decl_arena;
|
||||||
|
l->ast_arena = ast_arena;
|
||||||
|
l->type_arena = type_arena;
|
||||||
|
|
||||||
|
l->emit_line_directives = true;
|
||||||
|
l->breakpoint_on_error = true;
|
||||||
|
l->use_colored_terminal_output = true;
|
||||||
|
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_LangEnd(LC_Lang *lang) {
|
||||||
|
LC_DeallocateArena(lang->lex_arena);
|
||||||
|
LC_DeallocateArena(lang->type_arena);
|
||||||
|
LC_DeallocateArena(lang->decl_arena);
|
||||||
|
LC_DeallocateArena(lang->ast_arena);
|
||||||
|
LC_DeallocateArena(lang->arena);
|
||||||
|
if (L == lang) L = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_LangBegin(LC_Lang *l) {
|
||||||
|
L = l;
|
||||||
|
|
||||||
|
// Init default target settings
|
||||||
|
{
|
||||||
|
if (L->os == LC_OS_Invalid) {
|
||||||
|
L->os = LC_OS_LINUX;
|
||||||
|
#if LC_OPERATING_SYSTEM_WINDOWS
|
||||||
|
L->os = LC_OS_WINDOWS;
|
||||||
|
#elif LC_OPERATING_SYSTEM_MAC
|
||||||
|
L->os = LC_OS_MAC;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (L->arch == LC_ARCH_Invalid) {
|
||||||
|
L->arch = LC_ARCH_X64;
|
||||||
|
}
|
||||||
|
if (L->gen == LC_GEN_Invalid) {
|
||||||
|
L->gen = LC_GEN_C;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Init declared notes, interns and foreign names checker
|
||||||
|
//
|
||||||
|
{
|
||||||
|
L->declared_notes.arena = L->arena;
|
||||||
|
L->interns.arena = L->arena;
|
||||||
|
L->foreign_names.arena = L->arena;
|
||||||
|
L->implicit_any.arena = L->arena;
|
||||||
|
|
||||||
|
LC_MapReserve(&L->declared_notes, 128);
|
||||||
|
LC_MapReserve(&L->interns, 4096);
|
||||||
|
LC_MapReserve(&L->foreign_names, 256);
|
||||||
|
LC_MapReserve(&L->implicit_any, 64);
|
||||||
|
|
||||||
|
#define X(x) l->k##x = LC_InternStrLen(#x, sizeof(#x) - 1);
|
||||||
|
LC_LIST_KEYWORDS
|
||||||
|
#undef X
|
||||||
|
l->first_keyword = l->kfor;
|
||||||
|
l->last_keyword = l->kfalse;
|
||||||
|
|
||||||
|
#define X(x, declare) l->i##x = LC_InternStrLen(#x, sizeof(#x) - 1);
|
||||||
|
LC_LIST_INTERNS
|
||||||
|
#undef X
|
||||||
|
#define X(x, declare) \
|
||||||
|
if (declare) LC_DeclareNote(L->i##x);
|
||||||
|
LC_LIST_INTERNS
|
||||||
|
#undef X
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nulls
|
||||||
|
{
|
||||||
|
L->NullLEX.begin = "builtin declarations";
|
||||||
|
L->NullLEX.file = LC_ILit("builtin declarations");
|
||||||
|
L->BuiltinToken.lex = &L->NullLEX;
|
||||||
|
L->BuiltinToken.str = "builtin declarations";
|
||||||
|
L->BuiltinToken.len = sizeof("builtin declarations") - 1;
|
||||||
|
L->NullAST.pos = &L->BuiltinToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
LC_AST *builtins = LC_CreateAST(0, LC_ASTKind_Package);
|
||||||
|
L->builtin_package = builtins;
|
||||||
|
builtins->apackage.name = LC_ILit("builtins");
|
||||||
|
builtins->apackage.scope = LC_CreateScope(256);
|
||||||
|
LC_AddPackageToList(builtins);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_InitDeclStack(&L->resolver.locals, 128);
|
||||||
|
L->resolver.duplicate_map.arena = L->arena;
|
||||||
|
LC_MapReserve(&L->resolver.duplicate_map, 32);
|
||||||
|
|
||||||
|
L->resolver.stmt_block_stack.arena = L->arena;
|
||||||
|
L->printer.out_block_stack.arena = L->arena;
|
||||||
|
|
||||||
|
LC_PUSH_PACKAGE(L->builtin_package);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Init default type sizes using current platform
|
||||||
|
//
|
||||||
|
// Here we use the sizes of our current platform but
|
||||||
|
// later on it gets swapped based on LC override global variables in
|
||||||
|
// InitTarget
|
||||||
|
//
|
||||||
|
{
|
||||||
|
l->type_map.arena = L->arena;
|
||||||
|
LC_MapReserve(&l->type_map, 256);
|
||||||
|
|
||||||
|
typedef long long llong;
|
||||||
|
typedef unsigned long long ullong;
|
||||||
|
typedef unsigned long ulong;
|
||||||
|
typedef unsigned short ushort;
|
||||||
|
typedef unsigned char uchar;
|
||||||
|
typedef unsigned int uint;
|
||||||
|
|
||||||
|
L->pointer_align = LC_Alignof(void *);
|
||||||
|
L->pointer_size = sizeof(void *);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
#define X(TNAME, IS_UNSIGNED) \
|
||||||
|
l->types[i].kind = LC_TypeKind_##TNAME; \
|
||||||
|
l->types[i].size = sizeof(TNAME); \
|
||||||
|
l->types[i].align = LC_Alignof(TNAME); \
|
||||||
|
l->types[i].is_unsigned = IS_UNSIGNED; \
|
||||||
|
l->t##TNAME = l->types + i++;
|
||||||
|
|
||||||
|
LC_LIST_TYPES
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
//
|
||||||
|
// Overwrite types with target
|
||||||
|
//
|
||||||
|
if (L->arch == LC_ARCH_X64) {
|
||||||
|
LC_SetPointerSizeAndAlign(8, 8);
|
||||||
|
if (L->os == LC_OS_WINDOWS) {
|
||||||
|
L->tlong->size = 4;
|
||||||
|
L->tlong->align = 4;
|
||||||
|
L->tulong->size = 4;
|
||||||
|
L->tulong->align = 4;
|
||||||
|
} else {
|
||||||
|
L->tlong->size = 8;
|
||||||
|
L->tlong->align = 8;
|
||||||
|
L->tulong->size = 8;
|
||||||
|
L->tulong->align = 8;
|
||||||
|
}
|
||||||
|
} else if (L->arch == LC_ARCH_X86) {
|
||||||
|
LC_SetPointerSizeAndAlign(4, 4);
|
||||||
|
L->tlong->size = 4;
|
||||||
|
L->tlong->align = 4;
|
||||||
|
L->tulong->size = 4;
|
||||||
|
L->tulong->align = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
l->types[i].kind = LC_TypeKind_void;
|
||||||
|
l->tvoid = l->types + i++;
|
||||||
|
|
||||||
|
// Init decls for types
|
||||||
|
for (int i = 0; i < T_Count; i += 1) {
|
||||||
|
char *it = (char *)LC_TypeKindToString((LC_TypeKind)i) + 12;
|
||||||
|
LC_Intern intern = LC_ILit(it);
|
||||||
|
LC_Type *t = l->types + i;
|
||||||
|
t->id = ++l->typeids;
|
||||||
|
|
||||||
|
LC_Decl *decl = LC_CreateDecl(LC_DeclKind_Type, intern, &L->NullAST);
|
||||||
|
decl->state = LC_DeclState_Resolved;
|
||||||
|
decl->type = t;
|
||||||
|
t->decl = decl;
|
||||||
|
LC_AddDeclToScope(L->builtin_package->apackage.scope, decl);
|
||||||
|
|
||||||
|
if (t->kind == LC_TypeKind_uchar) decl->foreign_name = LC_ILit("unsigned char");
|
||||||
|
if (t->kind == LC_TypeKind_ushort) decl->foreign_name = LC_ILit("unsigned short");
|
||||||
|
if (t->kind == LC_TypeKind_uint) decl->foreign_name = LC_ILit("unsigned");
|
||||||
|
if (t->kind == LC_TypeKind_ulong) decl->foreign_name = LC_ILit("unsigned long");
|
||||||
|
if (t->kind == LC_TypeKind_llong) decl->foreign_name = LC_ILit("long long");
|
||||||
|
if (t->kind == LC_TypeKind_ullong) decl->foreign_name = LC_ILit("unsigned long long");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
l->tpvoid = LC_CreatePointerType(l->tvoid);
|
||||||
|
l->tpchar = LC_CreatePointerType(l->tchar);
|
||||||
|
|
||||||
|
{
|
||||||
|
l->tuntypedint = LC_CreateUntypedInt(L->tint);
|
||||||
|
l->tuntypedbool = LC_CreateUntypedInt(L->tbool);
|
||||||
|
l->tuntypednil = LC_CreateUntypedInt(L->tullong);
|
||||||
|
|
||||||
|
l->ttuntypedfloat.kind = LC_TypeKind_UntypedFloat;
|
||||||
|
l->ttuntypedfloat.id = ++L->typeids;
|
||||||
|
l->tuntypedfloat = &L->ttuntypedfloat;
|
||||||
|
l->tuntypedfloat->tutdefault = l->tdouble;
|
||||||
|
l->tuntypedfloat->decl = LC_CreateDecl(LC_DeclKind_Type, LC_ILit("UntypedFloat"), &L->NullAST);
|
||||||
|
|
||||||
|
l->ttuntypedstring.kind = LC_TypeKind_UntypedString;
|
||||||
|
l->ttuntypedstring.id = ++L->typeids;
|
||||||
|
l->tuntypedstring = &L->ttuntypedstring;
|
||||||
|
l->tuntypedstring->tutdefault = l->tpchar;
|
||||||
|
l->tuntypedstring->decl = LC_CreateDecl(LC_DeclKind_Type, LC_ILit("UntypedString"), &L->NullAST);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add builtin "String" type
|
||||||
|
{
|
||||||
|
L->ttstring.kind = LC_TypeKind_Incomplete;
|
||||||
|
L->ttstring.id = ++L->typeids;
|
||||||
|
L->tstring = &L->ttstring;
|
||||||
|
|
||||||
|
LC_AST *ast = LC_ParseDeclf("String :: struct { str: *char; len: int; }");
|
||||||
|
LC_Decl *decl = LC_CreateDecl(LC_DeclKind_Type, ast->dbase.name, ast);
|
||||||
|
decl->foreign_name = LC_ILit("LC_String");
|
||||||
|
decl->state = LC_DeclState_Resolved;
|
||||||
|
decl->type = L->tstring;
|
||||||
|
L->tstring->decl = decl;
|
||||||
|
LC_AddDeclToScope(L->builtin_package->apackage.scope, decl);
|
||||||
|
LC_Operand result = LC_ResolveTypeAggregate(ast, decl->type);
|
||||||
|
LC_ASSERT(ast, !LC_IsError(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add builtin "Any" type
|
||||||
|
{
|
||||||
|
L->ttany.kind = LC_TypeKind_Incomplete;
|
||||||
|
L->ttany.id = ++L->typeids;
|
||||||
|
L->tany = &L->ttany;
|
||||||
|
|
||||||
|
LC_AST *ast = LC_ParseDeclf("Any :: struct { type: int; data: *void; }");
|
||||||
|
LC_Decl *decl = LC_CreateDecl(LC_DeclKind_Type, ast->dbase.name, ast);
|
||||||
|
decl->foreign_name = LC_ILit("LC_Any");
|
||||||
|
decl->state = LC_DeclState_Resolved;
|
||||||
|
decl->type = L->tany;
|
||||||
|
L->tany->decl = decl;
|
||||||
|
LC_AddDeclToScope(L->builtin_package->apackage.scope, decl);
|
||||||
|
LC_Operand result = LC_ResolveTypeAggregate(ast, decl->type);
|
||||||
|
LC_ASSERT(ast, !LC_IsError(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_Decl *decl_nil = LC_AddConstIntDecl("nil", 0);
|
||||||
|
decl_nil->type = L->tuntypednil;
|
||||||
|
|
||||||
|
for (int i = LC_ARCH_X64; i < LC_ARCH_Count; i += 1) LC_AddBuiltinConstInt((char *)LC_ARCHToString((LC_ARCH)i), i);
|
||||||
|
for (int i = LC_OS_WINDOWS; i < LC_OS_Count; i += 1) LC_AddBuiltinConstInt((char *)LC_OSToString((LC_OS)i), i);
|
||||||
|
for (int i = LC_GEN_C; i < LC_GEN_Count; i += 1) LC_AddBuiltinConstInt((char *)LC_GENToString((LC_GEN)i), i);
|
||||||
|
LC_AddBuiltinConstInt("LC_ARCH", L->arch);
|
||||||
|
LC_AddBuiltinConstInt("LC_GEN", L->gen);
|
||||||
|
LC_AddBuiltinConstInt("LC_OS", L->os);
|
||||||
|
|
||||||
|
LC_POP_PACKAGE();
|
||||||
|
}
|
||||||
44
src/compiler/intern.c
Normal file
44
src/compiler/intern.c
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
typedef struct {
|
||||||
|
int len;
|
||||||
|
char str[];
|
||||||
|
} INTERN_Entry;
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Intern LC_InternStrLen(char *str, int len) {
|
||||||
|
LC_String key = LC_MakeString(str, len);
|
||||||
|
INTERN_Entry *entry = (INTERN_Entry *)LC_MapGet(&L->interns, key);
|
||||||
|
if (entry == NULL) {
|
||||||
|
LC_ASSERT(NULL, sizeof(INTERN_Entry) == sizeof(int));
|
||||||
|
entry = (INTERN_Entry *)LC_PushSize(L->arena, sizeof(int) + sizeof(char) * (len + 1));
|
||||||
|
entry->len = len;
|
||||||
|
LC_MemoryCopy(entry->str, str, len);
|
||||||
|
entry->str[len] = 0;
|
||||||
|
LC_MapInsert(&L->interns, key, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (uintptr_t)entry->str;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Intern LC_ILit(char *str) {
|
||||||
|
return LC_InternStrLen(str, (int)LC_StrLen(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Intern LC_GetUniqueIntern(const char *name_for_debug) {
|
||||||
|
LC_String name = LC_Format(L->arena, "U%u_%s", ++L->unique_counter, name_for_debug);
|
||||||
|
LC_Intern result = LC_InternStrLen(name.str, (int)name.len);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION char *LC_GetUniqueName(const char *name_for_debug) {
|
||||||
|
LC_String name = LC_Format(L->arena, "U%u_%s", ++L->unique_counter, name_for_debug);
|
||||||
|
return name.str;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_DeclareNote(LC_Intern intern) {
|
||||||
|
LC_MapInsertU64(&L->declared_notes, intern, (void *)intern);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION bool LC_IsNoteDeclared(LC_Intern intern) {
|
||||||
|
void *p = LC_MapGetU64(&L->declared_notes, intern);
|
||||||
|
bool result = p != NULL;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
535
src/compiler/lex.c
Normal file
535
src/compiler/lex.c
Normal file
@@ -0,0 +1,535 @@
|
|||||||
|
LC_FUNCTION void LC_LexingError(LC_Token *pos, const char *str, ...) {
|
||||||
|
LC_FORMAT(L->arena, str, s8);
|
||||||
|
LC_SendErrorMessage(pos, s8);
|
||||||
|
L->errors += 1;
|
||||||
|
pos->kind = LC_TokenKind_Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LC_IF(cond, ...) \
|
||||||
|
do { \
|
||||||
|
if (cond) { \
|
||||||
|
LC_LexingError(t, __VA_ARGS__); \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
LC_FUNCTION bool LC_IsAssign(LC_TokenKind kind) {
|
||||||
|
bool result = kind >= LC_TokenKind_Assign && kind <= LC_TokenKind_RightShiftAssign;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION bool LC_IsHexDigit(char c) {
|
||||||
|
bool result = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION bool LC_IsBinDigit(char c) {
|
||||||
|
bool result = (c >= '0' && c <= '1');
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION uint64_t LC_MapCharToNumber(char c) {
|
||||||
|
// clang-format off
|
||||||
|
switch (c) {
|
||||||
|
case '0': return 0;
|
||||||
|
case '1': return 1;
|
||||||
|
case '2': return 2;
|
||||||
|
case '3': return 3;
|
||||||
|
case '4': return 4;
|
||||||
|
case '5': return 5;
|
||||||
|
case '6': return 6;
|
||||||
|
case '7': return 7;
|
||||||
|
case '8': return 8;
|
||||||
|
case '9': return 9;
|
||||||
|
case 'a': case 'A': return 10;
|
||||||
|
case 'b': case 'B': return 11;
|
||||||
|
case 'c': case 'C': return 12;
|
||||||
|
case 'd': case 'D': return 13;
|
||||||
|
case 'e': case 'E': return 14;
|
||||||
|
case 'f': case 'F': return 15;
|
||||||
|
default: return 255;
|
||||||
|
}
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION uint64_t LC_GetEscapeCode(char c) {
|
||||||
|
switch (c) {
|
||||||
|
case 'a': return '\a';
|
||||||
|
case 'b': return '\b';
|
||||||
|
case 'e': return 0x1B;
|
||||||
|
case 'f': return '\f';
|
||||||
|
case 'n': return '\n';
|
||||||
|
case 'r': return '\r';
|
||||||
|
case 't': return '\t';
|
||||||
|
case 'v': return '\v';
|
||||||
|
case '\\': return '\\';
|
||||||
|
case '\'': return '\'';
|
||||||
|
case '\"': return '\"';
|
||||||
|
case '0': return '\0';
|
||||||
|
default: return UINT64_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_GetEscapeString(char c) {
|
||||||
|
switch (c) {
|
||||||
|
case '\a': return LC_Lit("\\a");
|
||||||
|
case '\b': return LC_Lit("\\b");
|
||||||
|
case 0x1B: return LC_Lit("\\x1B");
|
||||||
|
case '\f': return LC_Lit("\\f");
|
||||||
|
case '\n': return LC_Lit("\\n");
|
||||||
|
case '\r': return LC_Lit("\\r");
|
||||||
|
case '\t': return LC_Lit("\\t");
|
||||||
|
case '\v': return LC_Lit("\\v");
|
||||||
|
case '\\': return LC_Lit("\\\\");
|
||||||
|
case '\'': return LC_Lit("\\'");
|
||||||
|
case '\"': return LC_Lit("\\\"");
|
||||||
|
case '\0': return LC_Lit("\\0");
|
||||||
|
default: return LC_Lit("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_LexAdvance(LC_Lex *x) {
|
||||||
|
if (x->at[0] == 0) {
|
||||||
|
return;
|
||||||
|
} else if (x->at[0] == '\n') {
|
||||||
|
x->line += 1;
|
||||||
|
x->column = 0;
|
||||||
|
}
|
||||||
|
x->column += 1;
|
||||||
|
x->at += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_EatWhitespace(LC_Lex *x) {
|
||||||
|
while (LC_IsWhitespace(x->at[0])) LC_LexAdvance(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_EatIdent(LC_Lex *x) {
|
||||||
|
while (x->at[0] == '_' || LC_IsAlphanumeric(x->at[0])) LC_LexAdvance(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_SetTokenLen(LC_Lex *x, LC_Token *t) {
|
||||||
|
t->len = (int)(x->at - t->str);
|
||||||
|
LC_ASSERT(NULL, t->len < 2000000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_EatUntilIncluding(LC_Lex *x, char c) {
|
||||||
|
while (x->at[0] != 0 && x->at[0] != c) LC_LexAdvance(x);
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo: add temporary allocation + copy at end to perm
|
||||||
|
LC_FUNCTION LC_BigInt LC_LexBigInt(char *string, int len, uint64_t base) {
|
||||||
|
LC_ASSERT(NULL, base >= 2 && base <= 16);
|
||||||
|
LC_BigInt m = LC_Bigint_u64(1);
|
||||||
|
LC_BigInt base_mul = LC_Bigint_u64(base);
|
||||||
|
LC_BigInt result = LC_Bigint_u64(0);
|
||||||
|
|
||||||
|
LC_BigInt tmp = {0};
|
||||||
|
for (int i = len - 1; i >= 0; --i) {
|
||||||
|
uint64_t u = LC_MapCharToNumber(string[i]);
|
||||||
|
LC_ASSERT(NULL, u < base);
|
||||||
|
LC_BigInt val = LC_Bigint_u64(u);
|
||||||
|
LC_Bigint_mul(&tmp, &val, &m);
|
||||||
|
LC_BigInt new_val = tmp;
|
||||||
|
LC_Bigint_add(&tmp, &result, &new_val);
|
||||||
|
result = tmp;
|
||||||
|
LC_Bigint_mul(&tmp, &m, &base_mul);
|
||||||
|
m = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_LexNestedComments(LC_Lex *x, LC_Token *t) {
|
||||||
|
t->kind = LC_TokenKind_Comment;
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
|
||||||
|
if (x->at[0] == '*') {
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
t->kind = LC_TokenKind_DocComment;
|
||||||
|
|
||||||
|
if (x->at[0] == ' ' && x->at[1] == 'f' && x->at[2] == 'i' && x->at[3] == 'l' && x->at[4] == 'e') {
|
||||||
|
t->kind = LC_TokenKind_FileDocComment;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x->at[0] == ' ' && x->at[1] == 'p' && x->at[2] == 'a' && x->at[3] == 'c' && x->at[4] == 'k' && x->at[5] == 'a' && x->at[6] == 'g' && x->at[7] == 'e') {
|
||||||
|
t->kind = LC_TokenKind_PackageDocComment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int counter = 0;
|
||||||
|
for (;;) {
|
||||||
|
if (x->at[0] == '*' && x->at[1] == '/') {
|
||||||
|
if (counter <= 0) break;
|
||||||
|
counter -= 1;
|
||||||
|
} else if (x->at[0] == '/' && x->at[1] == '*') {
|
||||||
|
counter += 1;
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
}
|
||||||
|
LC_IF(x->at[0] == 0, "Unclosed block comment");
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
}
|
||||||
|
t->str += 2;
|
||||||
|
LC_SetTokenLen(x, t);
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_LexStringLiteral(LC_Lex *x, LC_Token *t, LC_TokenKind kind) {
|
||||||
|
t->kind = kind;
|
||||||
|
if (kind == LC_TokenKind_RawString) {
|
||||||
|
LC_EatUntilIncluding(x, '`');
|
||||||
|
} else if (kind == LC_TokenKind_String) {
|
||||||
|
for (;;) {
|
||||||
|
LC_IF(x->at[0] == '\n', "got a new line while parsing a '\"' string literal");
|
||||||
|
LC_IF(x->at[0] == 0, "reached end of file during string lexing");
|
||||||
|
if (x->at[0] == '"') break;
|
||||||
|
if (x->at[0] == '\\' && x->at[1] == '"') LC_LexAdvance(x);
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
}
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
} else {
|
||||||
|
LC_IF(1, "internal compiler error: unhandled case in %s", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_SetTokenLen(x, t);
|
||||||
|
t->len -= 2;
|
||||||
|
t->str += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_LexUnicodeLiteral(LC_Lex *x, LC_Token *t) {
|
||||||
|
t->kind = LC_TokenKind_Unicode;
|
||||||
|
LC_UTF32Result decode = LC_ConvertUTF8ToUTF32(x->at, 4);
|
||||||
|
LC_IF(decode.error, "invalid utf8 sequence");
|
||||||
|
|
||||||
|
uint8_t c[8] = {0};
|
||||||
|
for (int i = 0; i < decode.advance; i += 1) {
|
||||||
|
c[i] = x->at[0];
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
}
|
||||||
|
uint64_t result = *(uint64_t *)&c[0];
|
||||||
|
|
||||||
|
if (result == '\\') {
|
||||||
|
LC_ASSERT(NULL, decode.advance == 1);
|
||||||
|
result = LC_GetEscapeCode(x->at[0]);
|
||||||
|
LC_IF(result == UINT64_MAX, "invalid escape code");
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
}
|
||||||
|
LC_IF(x->at[0] != '\'', "unclosed unicode literal");
|
||||||
|
|
||||||
|
LC_Bigint_init_signed(&t->i, result);
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
LC_SetTokenLen(x, t);
|
||||||
|
t->str += 1;
|
||||||
|
t->len -= 2;
|
||||||
|
|
||||||
|
LC_IF(t->len == 0, "empty unicode literal");
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_LexIntOrFloat(LC_Lex *x, LC_Token *t) {
|
||||||
|
t->kind = LC_TokenKind_Int;
|
||||||
|
for (;;) {
|
||||||
|
if (x->at[0] == '.') {
|
||||||
|
LC_IF(t->kind == LC_TokenKind_Float, "failed to parse a floating point number, invalid format, found multiple '.'");
|
||||||
|
if (t->kind == LC_TokenKind_Int) t->kind = LC_TokenKind_Float;
|
||||||
|
} else if (!LC_IsDigit(x->at[0])) break;
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_SetTokenLen(x, t);
|
||||||
|
if (t->kind == LC_TokenKind_Int) {
|
||||||
|
t->i = LC_LexBigInt(t->str, t->len, 10);
|
||||||
|
} else if (t->kind == LC_TokenKind_Float) {
|
||||||
|
t->f64 = LC_ParseFloat(t->str, t->len);
|
||||||
|
} else {
|
||||||
|
LC_IF(1, "internal compiler error: unhandled case in %s", __FUNCTION__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_LexCase2(LC_Lex *x, LC_Token *t, LC_TokenKind tk0, char c, LC_TokenKind tk1) {
|
||||||
|
t->kind = tk0;
|
||||||
|
if (x->at[0] == c) {
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
t->kind = tk1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_LexCase3(LC_Lex *x, LC_Token *t, LC_TokenKind tk, char c0, LC_TokenKind tk0, char c1, LC_TokenKind tk1) {
|
||||||
|
t->kind = tk;
|
||||||
|
if (x->at[0] == c0) {
|
||||||
|
t->kind = tk0;
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
} else if (x->at[0] == c1) {
|
||||||
|
t->kind = tk1;
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_LexCase4(LC_Lex *x, LC_Token *t, LC_TokenKind tk, char c0, LC_TokenKind tk0, char c1, LC_TokenKind tk1, char c2, LC_TokenKind tk2) {
|
||||||
|
t->kind = tk;
|
||||||
|
if (x->at[0] == c0) {
|
||||||
|
t->kind = tk0;
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
} else if (x->at[0] == c1) {
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
LC_LexCase2(x, t, tk1, c2, tk2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_LexNext(LC_Lex *x, LC_Token *t) {
|
||||||
|
LC_EatWhitespace(x);
|
||||||
|
LC_MemoryZero(t, sizeof(LC_Token));
|
||||||
|
t->str = x->at;
|
||||||
|
t->line = x->line + 1;
|
||||||
|
t->column = x->column;
|
||||||
|
t->lex = x;
|
||||||
|
char *c = x->at;
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
|
||||||
|
switch (c[0]) {
|
||||||
|
case 0: t->kind = LC_TokenKind_EOF; break;
|
||||||
|
case '(': t->kind = LC_TokenKind_OpenParen; break;
|
||||||
|
case ')': t->kind = LC_TokenKind_CloseParen; break;
|
||||||
|
case '{': t->kind = LC_TokenKind_OpenBrace; break;
|
||||||
|
case '}': t->kind = LC_TokenKind_CloseBrace; break;
|
||||||
|
case '[': t->kind = LC_TokenKind_OpenBracket; break;
|
||||||
|
case ']': t->kind = LC_TokenKind_CloseBracket; break;
|
||||||
|
case ',': t->kind = LC_TokenKind_Comma; break;
|
||||||
|
case ':': t->kind = LC_TokenKind_Colon; break;
|
||||||
|
case ';': t->kind = LC_TokenKind_Semicolon; break;
|
||||||
|
case '~': t->kind = LC_TokenKind_Neg; break;
|
||||||
|
case '#': t->kind = LC_TokenKind_Hash; break;
|
||||||
|
case '@': t->kind = LC_TokenKind_Note; break;
|
||||||
|
case '\'': LC_LexUnicodeLiteral(x, t); break;
|
||||||
|
case '"': LC_LexStringLiteral(x, t, LC_TokenKind_String); break;
|
||||||
|
case '`': LC_LexStringLiteral(x, t, LC_TokenKind_RawString); break;
|
||||||
|
case '=': LC_LexCase2(x, t, LC_TokenKind_Assign, '=', LC_TokenKind_Equals); break;
|
||||||
|
case '!': LC_LexCase2(x, t, LC_TokenKind_Not, '=', LC_TokenKind_NotEquals); break;
|
||||||
|
case '*': LC_LexCase2(x, t, LC_TokenKind_Mul, '=', LC_TokenKind_MulAssign); break;
|
||||||
|
case '%': LC_LexCase2(x, t, LC_TokenKind_Mod, '=', LC_TokenKind_ModAssign); break;
|
||||||
|
case '+': LC_LexCase2(x, t, LC_TokenKind_Add, '=', LC_TokenKind_AddAssign); break;
|
||||||
|
case '-': LC_LexCase2(x, t, LC_TokenKind_Sub, '=', LC_TokenKind_SubAssign); break;
|
||||||
|
case '^': LC_LexCase2(x, t, LC_TokenKind_BitXor, '=', LC_TokenKind_BitXorAssign); break;
|
||||||
|
case '&': LC_LexCase3(x, t, LC_TokenKind_BitAnd, '=', LC_TokenKind_BitAndAssign, '&', LC_TokenKind_And); break;
|
||||||
|
case '|': LC_LexCase3(x, t, LC_TokenKind_BitOr, '=', LC_TokenKind_BitOrAssign, '|', LC_TokenKind_Or); break;
|
||||||
|
case '>': LC_LexCase4(x, t, LC_TokenKind_GreaterThen, '=', LC_TokenKind_GreaterThenEq, '>', LC_TokenKind_RightShift, '=', LC_TokenKind_RightShiftAssign); break;
|
||||||
|
case '<': LC_LexCase4(x, t, LC_TokenKind_LesserThen, '=', LC_TokenKind_LesserThenEq, '<', LC_TokenKind_LeftShift, '=', LC_TokenKind_LeftShiftAssign); break;
|
||||||
|
case '.': {
|
||||||
|
t->kind = LC_TokenKind_Dot;
|
||||||
|
if (x->at[0] == '.' && x->at[1] == '.') {
|
||||||
|
t->kind = LC_TokenKind_ThreeDots;
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case '0': {
|
||||||
|
if (x->at[0] == 'x') {
|
||||||
|
t->kind = LC_TokenKind_Int;
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
while (LC_IsHexDigit(x->at[0])) LC_LexAdvance(x);
|
||||||
|
LC_SetTokenLen(x, t);
|
||||||
|
LC_IF(t->len < 3, "invalid hex number");
|
||||||
|
t->i = LC_LexBigInt(t->str + 2, t->len - 2, 16);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (x->at[0] == 'b') {
|
||||||
|
t->kind = LC_TokenKind_Int;
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
while (LC_IsBinDigit(x->at[0])) LC_LexAdvance(x);
|
||||||
|
LC_SetTokenLen(x, t);
|
||||||
|
LC_IF(t->len < 3, "invalid binary number");
|
||||||
|
t->i = LC_LexBigInt(t->str + 2, t->len - 2, 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} // @fallthrough
|
||||||
|
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9': {
|
||||||
|
LC_LexIntOrFloat(x, t);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case 'A':
|
||||||
|
case 'a':
|
||||||
|
case 'B':
|
||||||
|
case 'b':
|
||||||
|
case 'C':
|
||||||
|
case 'c':
|
||||||
|
case 'D':
|
||||||
|
case 'd':
|
||||||
|
case 'E':
|
||||||
|
case 'e':
|
||||||
|
case 'F':
|
||||||
|
case 'f':
|
||||||
|
case 'G':
|
||||||
|
case 'g':
|
||||||
|
case 'H':
|
||||||
|
case 'h':
|
||||||
|
case 'I':
|
||||||
|
case 'i':
|
||||||
|
case 'J':
|
||||||
|
case 'j':
|
||||||
|
case 'K':
|
||||||
|
case 'k':
|
||||||
|
case 'L':
|
||||||
|
case 'l':
|
||||||
|
case 'M':
|
||||||
|
case 'm':
|
||||||
|
case 'N':
|
||||||
|
case 'n':
|
||||||
|
case 'O':
|
||||||
|
case 'o':
|
||||||
|
case 'P':
|
||||||
|
case 'p':
|
||||||
|
case 'Q':
|
||||||
|
case 'q':
|
||||||
|
case 'R':
|
||||||
|
case 'r':
|
||||||
|
case 'S':
|
||||||
|
case 's':
|
||||||
|
case 'T':
|
||||||
|
case 't':
|
||||||
|
case 'U':
|
||||||
|
case 'u':
|
||||||
|
case 'V':
|
||||||
|
case 'v':
|
||||||
|
case 'W':
|
||||||
|
case 'w':
|
||||||
|
case 'X':
|
||||||
|
case 'x':
|
||||||
|
case 'Y':
|
||||||
|
case 'y':
|
||||||
|
case 'Z':
|
||||||
|
case 'z':
|
||||||
|
case '_': {
|
||||||
|
t->kind = LC_TokenKind_Ident;
|
||||||
|
LC_EatIdent(x);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case '/': {
|
||||||
|
t->kind = LC_TokenKind_Div;
|
||||||
|
if (x->at[0] == '=') {
|
||||||
|
t->kind = LC_TokenKind_DivAssign;
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
} else if (x->at[0] == '/') {
|
||||||
|
t->kind = LC_TokenKind_Comment;
|
||||||
|
LC_LexAdvance(x);
|
||||||
|
while (x->at[0] != '\n' && x->at[0] != 0) LC_LexAdvance(x);
|
||||||
|
LC_SetTokenLen(x, t);
|
||||||
|
} else if (x->at[0] == '*') {
|
||||||
|
LC_LexNestedComments(x, t);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: LC_IF(1, "invalid character");
|
||||||
|
}
|
||||||
|
if (t->len == 0 && t->kind != LC_TokenKind_String && t->kind != LC_TokenKind_RawString) LC_SetTokenLen(x, t);
|
||||||
|
if (t->kind == LC_TokenKind_Comment) LC_LexNext(x, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Lex *LC_LexStream(char *file, char *str, int line) {
|
||||||
|
LC_Lex *x = LC_PushStruct(L->lex_arena, LC_Lex);
|
||||||
|
x->begin = str;
|
||||||
|
x->at = str;
|
||||||
|
x->file = LC_ILit(file);
|
||||||
|
x->line = line;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
LC_Token *t = LC_PushStruct(L->lex_arena, LC_Token);
|
||||||
|
if (!x->tokens) x->tokens = t;
|
||||||
|
x->token_count += 1;
|
||||||
|
|
||||||
|
LC_LexNext(x, t);
|
||||||
|
if (t->kind == LC_TokenKind_EOF) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_GetTokenLine(LC_Token *token) {
|
||||||
|
LC_Lex *x = token->lex;
|
||||||
|
LC_String content = LC_MakeFromChar(x->begin);
|
||||||
|
LC_StringList lines = LC_Split(L->arena, content, LC_Lit("\n"), 0);
|
||||||
|
|
||||||
|
LC_String l[3] = {LC_MakeEmptyString()};
|
||||||
|
|
||||||
|
int line = 1;
|
||||||
|
for (LC_StringNode *it = lines.first; it; it = it->next) {
|
||||||
|
LC_String sline = it->string;
|
||||||
|
if (token->line - 1 == line) {
|
||||||
|
l[0] = LC_Format(L->arena, "> %.*s\n", LC_Expand(sline));
|
||||||
|
}
|
||||||
|
if (token->line + 1 == line) {
|
||||||
|
l[2] = LC_Format(L->arena, "> %.*s\n", LC_Expand(sline));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (token->line == line) {
|
||||||
|
int begin = (int)(token->str - sline.str);
|
||||||
|
LC_String left = LC_GetPrefix(sline, begin);
|
||||||
|
LC_String past_left = LC_Skip(sline, begin);
|
||||||
|
LC_String mid = LC_GetPrefix(past_left, token->len);
|
||||||
|
LC_String right = LC_Skip(past_left, token->len);
|
||||||
|
|
||||||
|
char *green = "\033[32m";
|
||||||
|
char *reset = "\033[0m";
|
||||||
|
if (!L->use_colored_terminal_output) {
|
||||||
|
green = ">>>>";
|
||||||
|
reset = "<<<<";
|
||||||
|
}
|
||||||
|
l[1] = LC_Format(L->arena, "> %.*s%s%.*s%s%.*s\n", LC_Expand(left), green, LC_Expand(mid), reset, LC_Expand(right));
|
||||||
|
}
|
||||||
|
line += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_String result = LC_Format(L->arena, "%.*s%.*s%.*s", LC_Expand(l[0]), LC_Expand(l[1]), LC_Expand(l[2]));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_InternTokens(LC_Lex *x) {
|
||||||
|
// @todo: add scratch, we can dump the LC_PushArray strings
|
||||||
|
for (int i = 0; i < x->token_count; i += 1) {
|
||||||
|
LC_Token *t = x->tokens + i;
|
||||||
|
if (t->kind == LC_TokenKind_String) {
|
||||||
|
int string_len = 0;
|
||||||
|
char *string = LC_PushArray(L->arena, char, t->len);
|
||||||
|
for (int i = 0; i < t->len; i += 1) {
|
||||||
|
char c0 = t->str[i];
|
||||||
|
char c1 = t->str[i + 1];
|
||||||
|
if (i + 1 >= t->len) c1 = 0;
|
||||||
|
|
||||||
|
if (c0 == '\\') {
|
||||||
|
uint64_t code = LC_GetEscapeCode(c1);
|
||||||
|
if (code == UINT64_MAX) {
|
||||||
|
LC_LexingError(t, "invalid escape code in string '%c%c'", c0, c1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
c0 = (char)code;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
string[string_len++] = c0;
|
||||||
|
}
|
||||||
|
t->ident = LC_InternStrLen(string, string_len);
|
||||||
|
}
|
||||||
|
if (t->kind == LC_TokenKind_Note || t->kind == LC_TokenKind_Ident || t->kind == LC_TokenKind_RawString) {
|
||||||
|
t->ident = LC_InternStrLen(t->str, t->len);
|
||||||
|
}
|
||||||
|
if (t->kind == LC_TokenKind_Ident) {
|
||||||
|
bool is_keyword = t->ident >= L->first_keyword && t->ident <= L->last_keyword;
|
||||||
|
if (is_keyword) {
|
||||||
|
t->kind = LC_TokenKind_Keyword;
|
||||||
|
if (L->kaddptr == t->ident) t->kind = LC_TokenKind_AddPtr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef LC_IF
|
||||||
95
src/compiler/lib_compiler.c
Normal file
95
src/compiler/lib_compiler.c
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
#include "lib_compiler.h"
|
||||||
|
|
||||||
|
#if __clang__
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wswitch"
|
||||||
|
#pragma clang diagnostic ignored "-Wwritable-strings"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LC_ParseFloat // @override
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define LC_ParseFloat(str, len) strtod(str, NULL)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LC_Print // @override
|
||||||
|
#include <stdio.h>
|
||||||
|
#define LC_Print(str, len) printf("%.*s", (int)len, str)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LC_Exit // @override
|
||||||
|
#include <stdio.h>
|
||||||
|
#define LC_Exit(x) exit(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LC_MemoryZero // @override
|
||||||
|
#include <string.h>
|
||||||
|
#define LC_MemoryZero(p, size) memset(p, 0, size)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LC_MemoryCopy // @override
|
||||||
|
#include <string.h>
|
||||||
|
#define LC_MemoryCopy(dst, src, size) memcpy(dst, src, size);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LC_vsnprintf // @override
|
||||||
|
#include <stdio.h>
|
||||||
|
#define LC_vsnprintf vsnprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LC_THREAD_LOCAL LC_Lang *L;
|
||||||
|
|
||||||
|
#include "unicode.c"
|
||||||
|
#include "string.c"
|
||||||
|
#include "to_string.c"
|
||||||
|
#include "common.c"
|
||||||
|
#include "intern.c"
|
||||||
|
#include "lex.c"
|
||||||
|
#include "bigint.c"
|
||||||
|
#include "value.c"
|
||||||
|
#include "ast.c"
|
||||||
|
#include "ast_walk.c"
|
||||||
|
#include "ast_copy.c"
|
||||||
|
#include "resolver.c"
|
||||||
|
#include "resolve.c"
|
||||||
|
#include "parse.c"
|
||||||
|
#include "printer.c"
|
||||||
|
#include "genc.c"
|
||||||
|
#include "extended_passes.c"
|
||||||
|
#include "packages.c"
|
||||||
|
#include "init.c"
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#include "win32_filesystem.c"
|
||||||
|
#elif __linux__ || __APPLE__ || __unix__
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
#include "unix_filesystem.c"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LC_USE_CUSTOM_ARENA
|
||||||
|
#include "arena.c"
|
||||||
|
#if _WIN32
|
||||||
|
#include "win32_arena.c"
|
||||||
|
#elif __linux__ || __APPLE__ || __unix__
|
||||||
|
#include "unix_arena.c"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __clang__
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
1696
src/compiler/lib_compiler.h
Normal file
1696
src/compiler/lib_compiler.h
Normal file
File diff suppressed because it is too large
Load Diff
363
src/compiler/packages.c
Normal file
363
src/compiler/packages.c
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
LC_FUNCTION LC_Operand LC_ImportPackage(LC_AST *import, LC_AST *dst, LC_AST *src) {
|
||||||
|
DeclScope *dst_scope = dst->apackage.scope;
|
||||||
|
int scope_size = LC_NextPow2(src->apackage.scope->len * 2 + 1);
|
||||||
|
if (import && import->gimport.name) {
|
||||||
|
LC_PUSH_PACKAGE(dst);
|
||||||
|
LC_Decl *decl = LC_CreateDecl(LC_DeclKind_Import, import->gimport.name, import);
|
||||||
|
decl->scope = LC_CreateScope(scope_size);
|
||||||
|
LC_PutGlobalDecl(decl);
|
||||||
|
import->gimport.resolved_decl = decl;
|
||||||
|
LC_POP_PACKAGE();
|
||||||
|
dst_scope = decl->scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < src->apackage.scope->cap; i += 1) {
|
||||||
|
LC_MapEntry entry = src->apackage.scope->entries[i];
|
||||||
|
if (entry.key != 0) {
|
||||||
|
LC_Decl *decl = (LC_Decl *)entry.value;
|
||||||
|
if (decl->package != src) continue;
|
||||||
|
LC_Decl *existing = (LC_Decl *)LC_MapGetU64(dst_scope, decl->name);
|
||||||
|
if (existing && decl->package == L->builtin_package) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (existing) {
|
||||||
|
LC_MarkDeclError(existing);
|
||||||
|
LC_MarkDeclError(decl);
|
||||||
|
return LC_ReportASTErrorEx(decl->ast, existing->ast, "name colission while importing '%s' into '%s', there are 2 decls with the same name '%s'", src->apackage.name, dst->apackage.name, decl->name);
|
||||||
|
}
|
||||||
|
LC_MapInsertU64(dst_scope, decl->name, decl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (import && import->gimport.name) {
|
||||||
|
LC_ASSERT(import, dst_scope->cap == scope_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (import) import->gimport.resolved = true;
|
||||||
|
return LC_OPNull;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Intern LC_MakePackageNameFromPath(LC_String path) {
|
||||||
|
if (path.str[path.len - 1] == '/') path = LC_Chop(path, 1);
|
||||||
|
LC_String s8name = LC_SkipToLastSlash(path);
|
||||||
|
if (!LC_IsDir(L->arena, path) && LC_EndsWith(path, LC_Lit(".lc"), true)) {
|
||||||
|
s8name = LC_ChopLastPeriod(s8name);
|
||||||
|
}
|
||||||
|
if (s8name.len == 0) {
|
||||||
|
L->errors += 1;
|
||||||
|
LC_SendErrorMessagef(NULL, NULL, "failed to extract name from path %.*s", LC_Expand(path));
|
||||||
|
return LC_GetUniqueIntern("invalid_package_name");
|
||||||
|
}
|
||||||
|
LC_Intern result = LC_InternStrLen(s8name.str, (int)s8name.len);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION bool LC_PackageNameValid(LC_Intern name) {
|
||||||
|
char *str = (char *)name;
|
||||||
|
if (LC_IsDigit(str[0])) return false;
|
||||||
|
for (int i = 0; str[i]; i += 1) {
|
||||||
|
bool is_valid = LC_IsIdent(str[i]) || LC_IsDigit(str[i]);
|
||||||
|
if (!is_valid) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION bool LC_PackageNameDuplicate(LC_Intern name) {
|
||||||
|
LC_ASTFor(it, L->fpackage) {
|
||||||
|
if (it->apackage.name == name) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_AddPackageToList(LC_AST *n) {
|
||||||
|
LC_Intern name = n->apackage.name;
|
||||||
|
if (LC_PackageNameDuplicate(name)) {
|
||||||
|
LC_SendErrorMessagef(NULL, NULL, "found 2 packages with the same name: '%s' / '%.*s'\n", name, LC_Expand(n->apackage.path));
|
||||||
|
L->errors += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!LC_PackageNameValid(name)) {
|
||||||
|
LC_SendErrorMessagef(NULL, NULL, "invalid package name, please change the name of the package directory: '%s'\n", name);
|
||||||
|
L->errors += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LC_DLLAdd(L->fpackage, L->lpackage, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_AST *LC_RegisterPackage(LC_String path) {
|
||||||
|
LC_ASSERT(NULL, path.len != 0);
|
||||||
|
LC_AST *n = LC_CreateAST(NULL, LC_ASTKind_Package);
|
||||||
|
n->apackage.name = LC_MakePackageNameFromPath(path);
|
||||||
|
n->apackage.path = path;
|
||||||
|
LC_AddPackageToList(n);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_AST *LC_FindImportInRefList(LC_ASTRefList *arr, LC_Intern path) {
|
||||||
|
for (LC_ASTRef *it = arr->first; it; it = it->next) {
|
||||||
|
if (it->ast->gimport.path == path) return it->ast;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_AddASTToRefList(LC_ASTRefList *refs, LC_AST *ast) {
|
||||||
|
LC_ASTRef *ref = LC_PushStruct(L->arena, LC_ASTRef);
|
||||||
|
ref->ast = ast;
|
||||||
|
LC_DLLAdd(refs->first, refs->last, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_ASTRefList LC_GetPackageImports(LC_AST *package) {
|
||||||
|
LC_ASSERT(package, package->kind == LC_ASTKind_Package);
|
||||||
|
|
||||||
|
LC_ASTRefList refs = {0};
|
||||||
|
LC_ASTFor(file, package->apackage.ffile) {
|
||||||
|
LC_ASTFor(import, file->afile.fimport) {
|
||||||
|
LC_AST *found = LC_FindImportInRefList(&refs, import->gimport.path);
|
||||||
|
if (found) {
|
||||||
|
LC_ReportASTErrorEx(import, found, "duplicate import of: '%s', into package '%s'\n", import->gimport.path, package->apackage.name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
LC_AddASTToRefList(&refs, import);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return refs;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_RegisterPackageDir(char *dir) {
|
||||||
|
LC_String sdir = LC_MakeFromChar(dir);
|
||||||
|
if (!LC_IsDir(L->arena, sdir)) {
|
||||||
|
LC_SendErrorMessagef(NULL, NULL, "dir with name '%s', doesn't exist\n", dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LC_AddNode(L->arena, &L->package_dirs, sdir);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_AST *LC_GetPackageByName(LC_Intern name) {
|
||||||
|
LC_ASTFor(it, L->fpackage) {
|
||||||
|
if (it->apackage.name == name) return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_AST *result = NULL;
|
||||||
|
for (LC_StringNode *it = L->package_dirs.first; it; it = it->next) {
|
||||||
|
LC_String s = it->string;
|
||||||
|
LC_String path = LC_Format(L->arena, "%.*s/%s", LC_Expand(s), (char *)name);
|
||||||
|
if (LC_IsDir(L->arena, path)) {
|
||||||
|
if (result != NULL) {
|
||||||
|
LC_SendErrorMessagef(NULL, NULL, "found 2 directories with the same name: '%.*s', '%.*s'\n", LC_Expand(path), LC_Expand(result->apackage.path));
|
||||||
|
L->errors += 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result = LC_RegisterPackage(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_StringList LC_ListFilesInPackage(LC_Arena *arena, LC_String path) {
|
||||||
|
LC_StringList result = LC_MakeEmptyList();
|
||||||
|
for (LC_FileIter it = LC_IterateFiles(arena, path); LC_IsValid(it); LC_Advance(&it)) {
|
||||||
|
if (LC_EndsWith(it.absolute_path, LC_Lit(".lc"), LC_IgnoreCase)) {
|
||||||
|
LC_AddNode(arena, &result, it.absolute_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LoadedFile LC_ReadFileHook(LC_AST *package, LC_String path) {
|
||||||
|
LoadedFile result = {path};
|
||||||
|
if (L->on_file_load) {
|
||||||
|
L->on_file_load(package, &result);
|
||||||
|
} else {
|
||||||
|
result.content = LC_ReadFile(L->arena, result.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_ParsePackage(LC_AST *n) {
|
||||||
|
LC_ASSERT(n, n->kind == LC_ASTKind_Package);
|
||||||
|
LC_ASSERT(n, n->apackage.scope == NULL);
|
||||||
|
n->apackage.scope = LC_CreateScope(256);
|
||||||
|
|
||||||
|
LC_StringList files = n->apackage.injected_filepaths;
|
||||||
|
if (files.node_count == 0) {
|
||||||
|
files = LC_ListFilesInPackage(L->arena, n->apackage.path);
|
||||||
|
if (files.first == NULL) {
|
||||||
|
LC_SendErrorMessagef(NULL, NULL, "no valid .lc files in '%.*s'", LC_Expand(n->apackage.path));
|
||||||
|
n->apackage.state = LC_DeclState_Error;
|
||||||
|
L->errors += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (LC_StringNode *it = files.first; it; it = it->next) {
|
||||||
|
LoadedFile file = LC_ReadFileHook(n, it->string);
|
||||||
|
if (file.content.str == NULL) file.content.str = "";
|
||||||
|
|
||||||
|
LC_AST *ast_file = LC_ParseFile(n, file.path.str, file.content.str, file.line);
|
||||||
|
if (!ast_file) {
|
||||||
|
n->apackage.state = LC_DeclState_Error;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_ParsePackagesUsingRegistry(LC_Intern name) {
|
||||||
|
LC_AST *n = LC_GetPackageByName(name);
|
||||||
|
if (!n) {
|
||||||
|
LC_SendErrorMessagef(NULL, NULL, "no package with name '%s'\n", name);
|
||||||
|
L->errors += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (n->apackage.scope) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LC_ParsePackage(n);
|
||||||
|
LC_ASTRefList imports = LC_GetPackageImports(n);
|
||||||
|
for (LC_ASTRef *it = imports.first; it; it = it->next) {
|
||||||
|
LC_ParsePackagesUsingRegistry(it->ast->gimport.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_AddOrderedPackageToRefList(LC_AST *n) {
|
||||||
|
LC_ASTRefList *ordered = &L->ordered_packages;
|
||||||
|
for (LC_ASTRef *it = ordered->first; it; it = it->next) {
|
||||||
|
if (it->ast->apackage.name == n->apackage.name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LC_AddASTToRefList(ordered, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here we use import statements to produce a list of ordered packages.
|
||||||
|
// While we are at it we also resolve most top level declarations. I say
|
||||||
|
// most because aggregations are handled a bit differently, their resolution
|
||||||
|
// is deffered. This is added because a pointer doesn't require full typeinfo of
|
||||||
|
// an aggregate. It's just a number.
|
||||||
|
LC_FUNCTION LC_AST *LC_OrderPackagesAndBasicResolve(LC_AST *pos, LC_Intern name) {
|
||||||
|
LC_AST *n = LC_GetPackageByName(name);
|
||||||
|
if (n->apackage.state == LC_DeclState_Error) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (n->apackage.state == LC_DeclState_Resolved) {
|
||||||
|
// This function can be called multiple times, I assume user might
|
||||||
|
// want to use type information to generate something. Pattern:
|
||||||
|
// typecheck -> generate -> typecheck is expected!
|
||||||
|
LC_PackageDecls(n);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
if (n->apackage.state == LC_DeclState_Resolving) {
|
||||||
|
LC_ReportASTError(pos, "circular import '%s'", name);
|
||||||
|
n->apackage.state = LC_DeclState_Error;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
LC_ASSERT(pos, n->apackage.state == LC_DeclState_Unresolved);
|
||||||
|
n->apackage.state = LC_DeclState_Resolving;
|
||||||
|
|
||||||
|
LC_Operand op = LC_ImportPackage(NULL, n, L->builtin_package);
|
||||||
|
LC_ASSERT(pos, !LC_IsError(op));
|
||||||
|
|
||||||
|
// Resolve all imports regardless of errors.
|
||||||
|
// If current package has wrong import it means it's also
|
||||||
|
// wrong but it should still look into all imports
|
||||||
|
// despite this.
|
||||||
|
int wrong_import = 0;
|
||||||
|
LC_ASTRefList refs = LC_GetPackageImports(n);
|
||||||
|
for (LC_ASTRef *it = refs.first; it; it = it->next) {
|
||||||
|
LC_AST *import = LC_OrderPackagesAndBasicResolve(it->ast, it->ast->gimport.path);
|
||||||
|
if (!import) {
|
||||||
|
n->apackage.state = LC_DeclState_Error;
|
||||||
|
wrong_import += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_Operand op = LC_ImportPackage(it->ast, n, import);
|
||||||
|
if (LC_IsError(op)) {
|
||||||
|
n->apackage.state = LC_DeclState_Error;
|
||||||
|
wrong_import += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wrong_import) return NULL;
|
||||||
|
|
||||||
|
LC_PackageDecls(n);
|
||||||
|
LC_AddOrderedPackageToRefList(n);
|
||||||
|
n->apackage.state = LC_DeclState_Resolved;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_OrderAndResolveTopLevelDecls(LC_Intern name) {
|
||||||
|
L->first_package = name;
|
||||||
|
LC_OrderPackagesAndBasicResolve(NULL, name);
|
||||||
|
|
||||||
|
// Resolve still incomplete aggregate types, this operates on all packages
|
||||||
|
// that didn't have errors so even if something broke in package ordering
|
||||||
|
// it should still be fine to go forward with this and also proc body analysis
|
||||||
|
for (LC_ASTRef *it = L->ordered_packages.first; it; it = it->next) {
|
||||||
|
LC_AST *package = it->ast;
|
||||||
|
LC_ASSERT(package, package->apackage.state == LC_DeclState_Resolved);
|
||||||
|
LC_ResolveIncompleteTypes(package);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_ResolveAllProcBodies(void) {
|
||||||
|
// We don't need to check errors, only valid packages should have been put into
|
||||||
|
// the list.
|
||||||
|
for (LC_ASTRef *it = L->ordered_packages.first; it; it = it->next) {
|
||||||
|
LC_AST *package = it->ast;
|
||||||
|
LC_ASSERT(package, package->apackage.state == LC_DeclState_Resolved);
|
||||||
|
LC_ResolveProcBodies(package);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_ASTRefList LC_ResolvePackageByName(LC_Intern name) {
|
||||||
|
LC_ParsePackagesUsingRegistry(name);
|
||||||
|
LC_ASTRefList empty = {0};
|
||||||
|
if (L->errors) return empty;
|
||||||
|
|
||||||
|
LC_OrderAndResolveTopLevelDecls(name);
|
||||||
|
LC_ResolveAllProcBodies();
|
||||||
|
return L->ordered_packages;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_GenerateUnityBuild(LC_ASTRefList packages) {
|
||||||
|
if (L->errors) return LC_MakeEmptyString();
|
||||||
|
|
||||||
|
LC_BeginStringGen(L->arena);
|
||||||
|
|
||||||
|
LC_GenLinef("#include <stdbool.h>");
|
||||||
|
LC_GenLinef("#include <stddef.h>");
|
||||||
|
LC_GenLinef("#ifndef LC_String_IMPL");
|
||||||
|
LC_GenLinef("#define LC_String_IMPL");
|
||||||
|
LC_GenLinef("typedef struct { char *str; long long len; } LC_String;");
|
||||||
|
LC_GenLinef("#endif");
|
||||||
|
LC_GenLinef("#ifndef LC_Any_IMPL");
|
||||||
|
LC_GenLinef("#define LC_Any_IMPL");
|
||||||
|
LC_GenLinef("typedef struct { int type; void *data; } LC_Any;");
|
||||||
|
LC_GenLinef("#endif");
|
||||||
|
|
||||||
|
LC_GenLinef("#ifndef LC_Alignof");
|
||||||
|
LC_GenLinef("#if defined(__TINYC__)");
|
||||||
|
LC_GenLinef("#define LC_Alignof(...) __alignof__(__VA_ARGS__)");
|
||||||
|
LC_GenLinef("#else");
|
||||||
|
LC_GenLinef("#define LC_Alignof(...) _Alignof(__VA_ARGS__)");
|
||||||
|
LC_GenLinef("#endif");
|
||||||
|
LC_GenLinef("#endif");
|
||||||
|
LC_GenLinef("void *memset(void *, int, size_t);");
|
||||||
|
|
||||||
|
for (LC_ASTRef *it = packages.first; it; it = it->next) LC_GenCHeader(it->ast);
|
||||||
|
for (LC_ASTRef *it = packages.first; it; it = it->next) LC_GenCImpl(it->ast);
|
||||||
|
LC_String s = LC_EndStringGen(L->arena);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_AddSingleFilePackage(LC_Intern name, LC_String path) {
|
||||||
|
LC_AST *n = LC_CreateAST(0, LC_ASTKind_Package);
|
||||||
|
n->apackage.name = name;
|
||||||
|
n->apackage.path = path;
|
||||||
|
LC_AddNode(L->arena, &n->apackage.injected_filepaths, path);
|
||||||
|
LC_AddPackageToList(n);
|
||||||
|
}
|
||||||
1079
src/compiler/parse.c
Normal file
1079
src/compiler/parse.c
Normal file
File diff suppressed because it is too large
Load Diff
477
src/compiler/printer.c
Normal file
477
src/compiler/printer.c
Normal file
@@ -0,0 +1,477 @@
|
|||||||
|
LC_FUNCTION LC_StringList *LC_BeginStringGen(LC_Arena *arena) {
|
||||||
|
L->printer.list = LC_MakeEmptyList();
|
||||||
|
L->printer.arena = arena;
|
||||||
|
L->printer.last_filename = 0;
|
||||||
|
L->printer.last_line_num = 0;
|
||||||
|
L->printer.indent = 0;
|
||||||
|
return &L->printer.list;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_EndStringGen(LC_Arena *arena) {
|
||||||
|
LC_String result = LC_MergeString(arena, L->printer.list);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenIndent(void) {
|
||||||
|
LC_String s = LC_Lit(" ");
|
||||||
|
for (int i = 0; i < L->printer.indent; i++) {
|
||||||
|
LC_AddNode(L->printer.arena, &L->printer.list, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION char *LC_Strf(const char *str, ...) {
|
||||||
|
LC_FORMAT(L->arena, str, s8);
|
||||||
|
return s8.str;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenLine(void) {
|
||||||
|
LC_Genf("\n");
|
||||||
|
LC_GenIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION char *LC_GenLCType(LC_Type *type) {
|
||||||
|
LC_StringList out = {0};
|
||||||
|
for (LC_Type *it = type; it;) {
|
||||||
|
if (it->kind == LC_TypeKind_Pointer) {
|
||||||
|
LC_Addf(L->arena, &out, "*");
|
||||||
|
it = it->tptr.base;
|
||||||
|
} else if (it->kind == LC_TypeKind_Array) {
|
||||||
|
LC_Addf(L->arena, &out, "[%d]", it->tarray.size);
|
||||||
|
it = it->tarray.base;
|
||||||
|
} else if (it->kind == LC_TypeKind_Proc) {
|
||||||
|
LC_Addf(L->arena, &out, "proc(");
|
||||||
|
LC_TypeFor(mem, it->tproc.args.first) {
|
||||||
|
LC_Addf(L->arena, &out, "%s: %s", (char *)mem->name, LC_GenLCType(mem->type));
|
||||||
|
if (mem->default_value_expr) LC_Addf(L->arena, &out, "/*has default value*/");
|
||||||
|
if (mem->next) LC_Addf(L->arena, &out, ", ");
|
||||||
|
}
|
||||||
|
if (it->tproc.vargs) LC_Addf(L->arena, &out, "..");
|
||||||
|
LC_Addf(L->arena, &out, ")");
|
||||||
|
if (it->tproc.ret->kind != LC_TypeKind_void) LC_Addf(L->arena, &out, ": %s", LC_GenLCType(it->tproc.ret));
|
||||||
|
break;
|
||||||
|
} else if (it->decl) {
|
||||||
|
LC_Decl *decl = it->decl;
|
||||||
|
LC_ASSERT(decl->ast, decl);
|
||||||
|
LC_Addf(L->arena, &out, "%s", (char *)decl->name);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
LC_SendErrorMessagef(NULL, NULL, "internal compiler error: unhandled type kind in %s", __FUNCTION__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LC_String s = LC_MergeString(L->arena, out);
|
||||||
|
return s.str;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION char *LC_GenLCTypeVal(LC_TypeAndVal v) {
|
||||||
|
if (LC_IsInt(v.type) || LC_IsPtr(v.type) || LC_IsProc(v.type)) {
|
||||||
|
return LC_Bigint_str(&v.i, 10);
|
||||||
|
}
|
||||||
|
if (LC_IsFloat(v.type)) {
|
||||||
|
LC_String s = LC_Format(L->arena, "%f", v.d);
|
||||||
|
return s.str;
|
||||||
|
}
|
||||||
|
LC_ASSERT(NULL, !"invalid codepath");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION char *LC_GenLCAggName(LC_Type *t) {
|
||||||
|
if (t->kind == LC_TypeKind_Struct) return "struct";
|
||||||
|
if (t->kind == LC_TypeKind_Union) return "union";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_GenLCNode(LC_AST *n) {
|
||||||
|
switch (n->kind) {
|
||||||
|
case LC_ASTKind_Package: {
|
||||||
|
LC_ASTFor(it, n->apackage.ffile) {
|
||||||
|
LC_GenLCNode(it);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_File: {
|
||||||
|
LC_ASTFor(it, n->afile.fimport) {
|
||||||
|
LC_GenLCNode(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_ASTFor(it, n->afile.fdecl) {
|
||||||
|
LC_GenLCNode(it);
|
||||||
|
}
|
||||||
|
// @todo: we need to do something with notes so we can generate them in order!
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_GlobImport: {
|
||||||
|
LC_GenLinef("import %s \"%s\";", (char *)n->gimport.name, (char *)n->gimport.path);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_DeclProc: {
|
||||||
|
LC_GenLinef("%s :: ", (char *)n->dbase.name);
|
||||||
|
LC_GenLCNode(n->dproc.type);
|
||||||
|
if (n->dproc.body) {
|
||||||
|
LC_Genf(" ");
|
||||||
|
LC_GenLCNode(n->dproc.body);
|
||||||
|
} else {
|
||||||
|
LC_Genf(";");
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_DeclUnion:
|
||||||
|
case LC_ASTKind_DeclStruct: {
|
||||||
|
const char *agg = n->kind == LC_ASTKind_DeclUnion ? "union" : "struct";
|
||||||
|
LC_GenLinef("%s :: %s {", (char *)n->dbase.name, agg);
|
||||||
|
L->printer.indent += 1;
|
||||||
|
LC_ASTFor(it, n->dagg.first) {
|
||||||
|
LC_GenLine();
|
||||||
|
LC_GenLCNode(it);
|
||||||
|
LC_Genf(";");
|
||||||
|
}
|
||||||
|
L->printer.indent -= 1;
|
||||||
|
LC_GenLinef("}");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_TypespecAggMem: {
|
||||||
|
LC_Genf("%s: ", (char *)n->tagg_mem.name);
|
||||||
|
LC_GenLCNode(n->tagg_mem.type);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_DeclVar: {
|
||||||
|
LC_GenLinef("%s ", (char *)n->dbase.name);
|
||||||
|
if (n->dvar.type) {
|
||||||
|
LC_Genf(": ");
|
||||||
|
LC_GenLCNode(n->dvar.type);
|
||||||
|
if (n->dvar.expr) {
|
||||||
|
LC_Genf("= ");
|
||||||
|
LC_GenLCNode(n->dvar.expr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LC_Genf(":= ");
|
||||||
|
LC_GenLCNode(n->dvar.expr);
|
||||||
|
}
|
||||||
|
LC_Genf(";");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_DeclConst: {
|
||||||
|
LC_GenLinef("%s :: ", (char *)n->dbase.name);
|
||||||
|
LC_GenLCNode(n->dconst.expr);
|
||||||
|
LC_Genf(";");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_DeclTypedef: {
|
||||||
|
LC_GenLinef("%s :: typedef ", (char *)n->dbase.name);
|
||||||
|
LC_GenLCNode(n->dtypedef.type);
|
||||||
|
LC_Genf(";");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprIdent:
|
||||||
|
case LC_ASTKind_TypespecIdent: {
|
||||||
|
LC_Genf("%s", (char *)n->eident.name);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprField:
|
||||||
|
case LC_ASTKind_TypespecField: {
|
||||||
|
LC_GenLCNode(n->efield.left);
|
||||||
|
LC_Genf(".%s", (char *)n->efield.right);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_TypespecPointer: {
|
||||||
|
LC_Genf("*");
|
||||||
|
LC_GenLCNode(n->tpointer.base);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_TypespecArray: {
|
||||||
|
LC_Genf("[");
|
||||||
|
if (n->tarray.index) LC_GenLCNode(n->tarray.index);
|
||||||
|
LC_Genf("]");
|
||||||
|
LC_GenLCNode(n->tpointer.base);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_TypespecProc: {
|
||||||
|
LC_Genf("proc(");
|
||||||
|
LC_ASTFor(it, n->tproc.first) {
|
||||||
|
LC_GenLCNode(it);
|
||||||
|
if (it != n->tproc.last) LC_Genf(", ");
|
||||||
|
}
|
||||||
|
if (n->tproc.vargs) {
|
||||||
|
LC_Genf(", ...");
|
||||||
|
if (n->tproc.vargs_any_promotion) LC_Genf("Any");
|
||||||
|
}
|
||||||
|
LC_Genf(")");
|
||||||
|
if (n->tproc.ret) {
|
||||||
|
LC_Genf(": ");
|
||||||
|
LC_GenLCNode(n->tproc.ret);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_TypespecProcArg: {
|
||||||
|
LC_Genf("%s: ", (char *)n->tproc_arg.name);
|
||||||
|
LC_GenLCNode(n->tproc_arg.type);
|
||||||
|
if (n->tproc_arg.expr) {
|
||||||
|
LC_Genf(" = ");
|
||||||
|
LC_GenLCNode(n->tproc_arg.expr);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtBlock: {
|
||||||
|
if (n->sblock.name && n->sblock.kind != SBLK_Loop) LC_Genf("%s: ", (char *)n->sblock.name);
|
||||||
|
LC_Genf("{");
|
||||||
|
L->printer.indent += 1;
|
||||||
|
LC_ASTFor(it, n->sblock.first) {
|
||||||
|
LC_GenLine();
|
||||||
|
LC_GenLCNode(it);
|
||||||
|
if (it->kind != LC_ASTKind_StmtBlock && it->kind != LC_ASTKind_StmtDefer && it->kind != LC_ASTKind_StmtFor && it->kind != LC_ASTKind_StmtIf && it->kind != LC_ASTKind_StmtSwitch) LC_Genf(";");
|
||||||
|
}
|
||||||
|
L->printer.indent -= 1;
|
||||||
|
LC_GenLinef("}");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtReturn: {
|
||||||
|
LC_Genf("return");
|
||||||
|
if (n->sreturn.expr) {
|
||||||
|
LC_Genf(" ");
|
||||||
|
LC_GenLCNode(n->sreturn.expr);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtBreak: {
|
||||||
|
LC_Genf("break");
|
||||||
|
if (n->sbreak.name) LC_Genf(" %s", (char *)n->sbreak.name);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtContinue: {
|
||||||
|
LC_Genf("continue");
|
||||||
|
if (n->scontinue.name) LC_Genf(" %s", (char *)n->scontinue.name);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtDefer: {
|
||||||
|
LC_Genf("defer ");
|
||||||
|
LC_GenLCNode(n->sdefer.body);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtFor: {
|
||||||
|
LC_StmtBlock *sblock = &n->sfor.body->sblock;
|
||||||
|
if (sblock->name && sblock->kind == SBLK_Loop) {
|
||||||
|
LC_Genf("%s: ", (char *)sblock->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_Genf("for ");
|
||||||
|
if (n->sfor.init) {
|
||||||
|
LC_GenLCNode(n->sfor.init);
|
||||||
|
if (n->sfor.cond) LC_Genf("; ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n->sfor.cond) {
|
||||||
|
LC_GenLCNode(n->sfor.cond);
|
||||||
|
if (n->sfor.inc) {
|
||||||
|
LC_Genf("; ");
|
||||||
|
LC_GenLCNode(n->sfor.inc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_Genf(" ");
|
||||||
|
LC_GenLCNode(n->sfor.body);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtElseIf:
|
||||||
|
LC_Genf("else ");
|
||||||
|
case LC_ASTKind_StmtIf: {
|
||||||
|
LC_Genf("if ");
|
||||||
|
LC_GenLCNode(n->sif.expr);
|
||||||
|
LC_GenLCNode(n->sif.body);
|
||||||
|
LC_ASTFor(it, n->sif.first) {
|
||||||
|
LC_GenLCNode(it);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtElse: {
|
||||||
|
LC_Genf("else ");
|
||||||
|
LC_GenLCNode(n->sif.body);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtSwitch: {
|
||||||
|
LC_Genf("switch ");
|
||||||
|
LC_GenLCNode(n->sswitch.expr);
|
||||||
|
LC_Genf("{");
|
||||||
|
L->printer.indent += 1;
|
||||||
|
LC_ASTFor(it, n->sswitch.first) {
|
||||||
|
LC_GenLine();
|
||||||
|
LC_GenLCNode(it);
|
||||||
|
}
|
||||||
|
L->printer.indent -= 1;
|
||||||
|
LC_Genf("}");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtSwitchCase: {
|
||||||
|
LC_Genf("case ");
|
||||||
|
LC_ASTFor(it, n->scase.first) {
|
||||||
|
LC_GenLCNode(it);
|
||||||
|
if (it != n->scase.last) LC_Genf(", ");
|
||||||
|
}
|
||||||
|
LC_Genf(": ");
|
||||||
|
LC_GenLCNode(n->scase.body);
|
||||||
|
} break;
|
||||||
|
case LC_ASTKind_StmtSwitchDefault: {
|
||||||
|
LC_Genf("default: ");
|
||||||
|
LC_GenLCNode(n->scase.body);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtAssign: {
|
||||||
|
LC_GenLCNode(n->sassign.left);
|
||||||
|
LC_Genf(" %s ", LC_TokenKindToOperator(n->sassign.op));
|
||||||
|
LC_GenLCNode(n->sassign.right);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtExpr: {
|
||||||
|
LC_GenLCNode(n->sexpr.expr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtVar: {
|
||||||
|
LC_Genf("%s", (char *)n->svar.name);
|
||||||
|
if (n->svar.type) {
|
||||||
|
LC_Genf(": ");
|
||||||
|
LC_GenLCNode(n->svar.type);
|
||||||
|
if (n->svar.expr) {
|
||||||
|
LC_Genf(" = ");
|
||||||
|
LC_GenLCNode(n->svar.expr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LC_Genf(" := ");
|
||||||
|
LC_GenLCNode(n->svar.expr);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtConst: {
|
||||||
|
LC_GenLinef("%s :: ", (char *)n->sconst.name);
|
||||||
|
LC_GenLCNode(n->sconst.expr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprString: {
|
||||||
|
LC_Genf("`%s`", (char *)n->eatom.name);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprInt: {
|
||||||
|
LC_Genf("%s", LC_Bigint_str(&n->eatom.i, 10));
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprFloat: {
|
||||||
|
LC_Genf("%f", n->eatom.d);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprBool: {
|
||||||
|
int64_t value = LC_Bigint_as_unsigned(&n->eatom.i);
|
||||||
|
if (value) {
|
||||||
|
LC_Genf("true");
|
||||||
|
} else {
|
||||||
|
LC_Genf("false");
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprType: {
|
||||||
|
LC_Genf(":");
|
||||||
|
LC_GenLCNode(n->etype.type);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprBinary: {
|
||||||
|
LC_Genf("(");
|
||||||
|
LC_GenLCNode(n->ebinary.left);
|
||||||
|
LC_Genf("%s", LC_TokenKindToOperator(n->ebinary.op));
|
||||||
|
LC_GenLCNode(n->ebinary.right);
|
||||||
|
LC_Genf(")");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprUnary: {
|
||||||
|
LC_Genf("%s(", LC_TokenKindToOperator(n->eunary.op));
|
||||||
|
LC_GenLCNode(n->eunary.expr);
|
||||||
|
LC_Genf(")");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_StmtNote: {
|
||||||
|
LC_Genf("#");
|
||||||
|
LC_GenLCNode(n->snote.expr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprNote: {
|
||||||
|
LC_Genf("#");
|
||||||
|
LC_GenLCNode(n->enote.expr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_DeclNote: {
|
||||||
|
LC_GenLinef("#");
|
||||||
|
LC_GenLCNode(n->dnote.expr);
|
||||||
|
LC_Genf(";");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_Note:
|
||||||
|
case LC_ASTKind_ExprBuiltin:
|
||||||
|
case LC_ASTKind_ExprCall: {
|
||||||
|
LC_GenLCNode(n->ecompo.name);
|
||||||
|
LC_Genf("(");
|
||||||
|
LC_ASTFor(it, n->ecompo.first) {
|
||||||
|
LC_GenLCNode(it);
|
||||||
|
if (it != n->ecompo.last) LC_Genf(", ");
|
||||||
|
}
|
||||||
|
LC_Genf(")");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprCompoundItem:
|
||||||
|
case LC_ASTKind_ExprCallItem: {
|
||||||
|
if (n->ecompo_item.name) {
|
||||||
|
LC_Genf("%s = ", (char *)n->ecompo_item.name);
|
||||||
|
}
|
||||||
|
if (n->ecompo_item.index) {
|
||||||
|
LC_Genf("[");
|
||||||
|
LC_GenLCNode(n->ecompo_item.index);
|
||||||
|
LC_Genf("] = ");
|
||||||
|
}
|
||||||
|
LC_GenLCNode(n->ecompo_item.expr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprCompound: {
|
||||||
|
if (n->ecompo.name) LC_GenLCNode(n->ecompo.name);
|
||||||
|
LC_Genf("{");
|
||||||
|
LC_ASTFor(it, n->ecompo.first) {
|
||||||
|
LC_GenLCNode(it);
|
||||||
|
if (it != n->ecompo.last) LC_Genf(", ");
|
||||||
|
}
|
||||||
|
LC_Genf("}");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprCast: {
|
||||||
|
LC_Genf(":");
|
||||||
|
LC_GenLCNode(n->ecast.type);
|
||||||
|
LC_Genf("(");
|
||||||
|
LC_GenLCNode(n->ecast.expr);
|
||||||
|
LC_Genf(")");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprIndex: {
|
||||||
|
LC_Genf("(");
|
||||||
|
LC_GenLCNode(n->eindex.base);
|
||||||
|
LC_Genf("[");
|
||||||
|
LC_GenLCNode(n->eindex.index);
|
||||||
|
LC_Genf("]");
|
||||||
|
LC_Genf(")");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprAddPtr: {
|
||||||
|
LC_Genf("addptr(");
|
||||||
|
LC_GenLCNode(n->ebinary.left);
|
||||||
|
LC_Genf(", ");
|
||||||
|
LC_GenLCNode(n->ebinary.right);
|
||||||
|
LC_Genf(")");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprGetValueOfPointer: {
|
||||||
|
LC_Genf("*(");
|
||||||
|
LC_GenLCNode(n->eunary.expr);
|
||||||
|
LC_Genf(")");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case LC_ASTKind_ExprGetPointerOfValue: {
|
||||||
|
LC_Genf("&(");
|
||||||
|
LC_GenLCNode(n->eunary.expr);
|
||||||
|
LC_Genf(")");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: LC_ReportASTError(n, "internal compiler error: unhandled ast kind in %s", __FUNCTION__);
|
||||||
|
}
|
||||||
|
}
|
||||||
1496
src/compiler/resolve.c
Normal file
1496
src/compiler/resolve.c
Normal file
File diff suppressed because it is too large
Load Diff
192
src/compiler/resolver.c
Normal file
192
src/compiler/resolver.c
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
// clang-format off
|
||||||
|
#define LC_PUSH_COMP_ARRAY_SIZE(SIZE) int PREV_SIZE = L->resolver.compo_context_array_size; L->resolver.compo_context_array_size = SIZE;
|
||||||
|
#define LC_POP_COMP_ARRAY_SIZE() L->resolver.compo_context_array_size = PREV_SIZE
|
||||||
|
#define LC_PUSH_SCOPE(SCOPE) DeclScope *PREV_SCOPE = L->resolver.active_scope; L->resolver.active_scope = SCOPE
|
||||||
|
#define LC_POP_SCOPE() L->resolver.active_scope = PREV_SCOPE
|
||||||
|
#define LC_PUSH_LOCAL_SCOPE() int LOCAL_LEN = L->resolver.locals.len
|
||||||
|
#define LC_POP_LOCAL_SCOPE() L->resolver.locals.len = LOCAL_LEN
|
||||||
|
#define LC_PUSH_PACKAGE(PKG) LC_AST *PREV_PKG = L->resolver.package; L->resolver.package = PKG; LC_PUSH_SCOPE(PKG->apackage.scope)
|
||||||
|
#define LC_POP_PACKAGE() L->resolver.package = PREV_PKG; LC_POP_SCOPE()
|
||||||
|
#define LC_PROP_ERROR(OP, n, ...) OP = __VA_ARGS__; if (LC_IsError(OP)) { n->kind = LC_ASTKind_Error; return OP; }
|
||||||
|
#define LC_DECL_PROP_ERROR(OP, ...) OP = __VA_ARGS__; if (LC_IsError(OP)) { LC_MarkDeclError(decl); return OP; }
|
||||||
|
|
||||||
|
#define LC_IF(COND, N, ...) if (COND) { LC_Operand R_ = LC_ReportASTError(N, __VA_ARGS__); N->kind = LC_ASTKind_Error; return R_; }
|
||||||
|
#define LC_DECL_IF(COND, ...) if (COND) { LC_MarkDeclError(decl); return LC_ReportASTError(__VA_ARGS__); }
|
||||||
|
#define LC_TYPE_IF(COND, ...) if (COND) { LC_MarkDeclError(decl); type->kind = LC_TypeKind_Error; return LC_ReportASTError(__VA_ARGS__); }
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_AddDecl(LC_DeclStack *scope, LC_Decl *decl) {
|
||||||
|
if (scope->len + 1 > scope->cap) {
|
||||||
|
LC_ASSERT(NULL, scope->cap);
|
||||||
|
int new_cap = scope->cap * 2;
|
||||||
|
LC_Decl **new_stack = LC_PushArray(L->arena, LC_Decl *, new_cap);
|
||||||
|
LC_MemoryCopy(new_stack, scope->stack, scope->len * sizeof(LC_Decl *));
|
||||||
|
scope->stack = new_stack;
|
||||||
|
scope->cap = new_cap;
|
||||||
|
}
|
||||||
|
scope->stack[scope->len++] = decl;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_InitDeclStack(LC_DeclStack *stack, int size) {
|
||||||
|
stack->stack = LC_PushArray(L->arena, LC_Decl *, size);
|
||||||
|
stack->cap = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_DeclStack *LC_CreateDeclStack(int size) {
|
||||||
|
LC_DeclStack *stack = LC_PushStruct(L->arena, LC_DeclStack);
|
||||||
|
LC_InitDeclStack(stack, size);
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Decl *LC_FindDeclOnStack(LC_DeclStack *scp, LC_Intern name) {
|
||||||
|
for (int i = 0; i < scp->len; i += 1) {
|
||||||
|
LC_Decl *it = scp->stack[i];
|
||||||
|
if (it->name == name) {
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_MarkDeclError(LC_Decl *decl) {
|
||||||
|
if (decl) {
|
||||||
|
decl->kind = LC_DeclKind_Error;
|
||||||
|
decl->state = LC_DeclState_Error;
|
||||||
|
if (decl->ast) decl->ast->kind = LC_ASTKind_Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Decl *LC_CreateDecl(LC_DeclKind kind, LC_Intern name, LC_AST *n) {
|
||||||
|
LC_Decl *decl = LC_PushStruct(L->decl_arena, LC_Decl);
|
||||||
|
L->decl_count += 1;
|
||||||
|
|
||||||
|
decl->name = name;
|
||||||
|
decl->kind = kind;
|
||||||
|
decl->ast = n;
|
||||||
|
decl->package = L->resolver.package;
|
||||||
|
LC_ASSERT(n, decl->package);
|
||||||
|
|
||||||
|
LC_AST *note = LC_HasNote(n, L->iforeign);
|
||||||
|
if (note) {
|
||||||
|
decl->is_foreign = true;
|
||||||
|
if (note->anote.first) {
|
||||||
|
if (note->anote.size != 1) LC_ReportASTError(note, "invalid format of @foreign(...), more then 1 argument");
|
||||||
|
LC_AST *expr = note->anote.first->ecompo_item.expr;
|
||||||
|
if (expr->kind == LC_ASTKind_ExprIdent) decl->foreign_name = expr->eident.name;
|
||||||
|
if (expr->kind != LC_ASTKind_ExprIdent) LC_ReportASTError(note, "invalid format of @foreign(...), expected identifier");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!decl->foreign_name) decl->foreign_name = decl->name;
|
||||||
|
return decl;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Operand LC_ThereIsNoDecl(DeclScope *scp, LC_Decl *decl, bool check_locals) {
|
||||||
|
LC_Decl *r = (LC_Decl *)LC_MapGetU64(scp, decl->name);
|
||||||
|
if (check_locals && !r) {
|
||||||
|
r = LC_FindDeclOnStack(&L->resolver.locals, decl->name);
|
||||||
|
}
|
||||||
|
if (r) {
|
||||||
|
LC_MarkDeclError(r);
|
||||||
|
LC_MarkDeclError(decl);
|
||||||
|
return LC_ReportASTErrorEx(decl->ast, r->ast, "there are 2 decls with the same name '%s'", decl->name);
|
||||||
|
}
|
||||||
|
return LC_OPNull;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Operand LC_AddDeclToScope(DeclScope *scp, LC_Decl *decl) {
|
||||||
|
LC_Operand LC_DECL_PROP_ERROR(op, LC_ThereIsNoDecl(scp, decl, false));
|
||||||
|
LC_MapInsertU64(scp, decl->name, decl);
|
||||||
|
return LC_OPDecl(decl);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION DeclScope *LC_CreateScope(int size) {
|
||||||
|
DeclScope *scope = LC_PushStruct(L->arena, DeclScope);
|
||||||
|
scope->arena = L->arena;
|
||||||
|
LC_MapReserve(scope, size);
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Decl *LC_FindDeclInScope(DeclScope *scope, LC_Intern name) {
|
||||||
|
LC_Decl *decl = (LC_Decl *)LC_MapGetU64(scope, name);
|
||||||
|
return decl;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Decl *LC_GetLocalOrGlobalDecl(LC_Intern name) {
|
||||||
|
LC_Decl *decl = LC_FindDeclInScope(L->resolver.active_scope, name);
|
||||||
|
if (!decl && L->resolver.package->apackage.scope == L->resolver.active_scope) {
|
||||||
|
decl = LC_FindDeclOnStack(&L->resolver.locals, name);
|
||||||
|
}
|
||||||
|
return decl;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Operand LC_PutGlobalDecl(LC_Decl *decl) {
|
||||||
|
LC_Operand LC_DECL_PROP_ERROR(op, LC_AddDeclToScope(L->resolver.package->apackage.scope, decl));
|
||||||
|
|
||||||
|
// :Mangle global scope name
|
||||||
|
if (!decl->is_foreign && decl->package != L->builtin_package) {
|
||||||
|
bool mangle = true;
|
||||||
|
if (LC_HasNote(decl->ast, L->idont_mangle)) mangle = false;
|
||||||
|
if (LC_HasNote(decl->ast, L->iapi)) mangle = false;
|
||||||
|
if (decl->name == L->imain) {
|
||||||
|
if (L->first_package) {
|
||||||
|
if (L->first_package == decl->package->apackage.name) {
|
||||||
|
mangle = false;
|
||||||
|
}
|
||||||
|
} else mangle = false;
|
||||||
|
}
|
||||||
|
if (mangle) {
|
||||||
|
LC_String name = LC_Format(L->arena, "lc_%s_%s", (char *)decl->package->apackage.name, (char *)decl->name);
|
||||||
|
decl->foreign_name = LC_InternStrLen(name.str, (int)name.len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_Decl *conflict = (LC_Decl *)LC_MapGetU64(&L->foreign_names, decl->foreign_name);
|
||||||
|
if (conflict && !decl->is_foreign) {
|
||||||
|
LC_ReportASTErrorEx(decl->ast, conflict->ast, "found two global declarations with the same foreign name: %s", decl->foreign_name);
|
||||||
|
} else {
|
||||||
|
LC_MapInsertU64(&L->foreign_names, decl->foreign_name, decl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return op;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Operand LC_CreateLocalDecl(LC_DeclKind kind, LC_Intern name, LC_AST *ast) {
|
||||||
|
LC_Decl *decl = LC_CreateDecl(kind, name, ast);
|
||||||
|
decl->state = LC_DeclState_Resolving;
|
||||||
|
LC_Operand LC_DECL_PROP_ERROR(operr0, LC_ThereIsNoDecl(L->resolver.package->apackage.scope, decl, true));
|
||||||
|
LC_AddDecl(&L->resolver.locals, decl);
|
||||||
|
return LC_OPDecl(decl);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Decl *LC_AddConstIntDecl(char *key, int64_t value) {
|
||||||
|
LC_Intern intern = LC_ILit(key);
|
||||||
|
LC_Decl *decl = LC_CreateDecl(LC_DeclKind_Const, intern, &L->NullAST);
|
||||||
|
decl->state = LC_DeclState_Resolved;
|
||||||
|
decl->type = L->tuntypedint;
|
||||||
|
LC_Bigint_init_signed(&decl->v.i, value);
|
||||||
|
LC_AddDeclToScope(L->resolver.package->apackage.scope, decl);
|
||||||
|
return decl;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_Decl *LC_GetBuiltin(LC_Intern name) {
|
||||||
|
LC_Decl *decl = (LC_Decl *)LC_MapGetU64(L->builtin_package->apackage.scope, name);
|
||||||
|
return decl;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_AddBuiltinConstInt(char *key, int64_t value) {
|
||||||
|
LC_PUSH_PACKAGE(L->builtin_package);
|
||||||
|
LC_AddConstIntDecl(key, value);
|
||||||
|
LC_POP_PACKAGE();
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_AST *LC_HasNote(LC_AST *ast, LC_Intern i) {
|
||||||
|
if (ast && ast->notes) {
|
||||||
|
LC_ASTFor(it, ast->notes->anote_list.first) {
|
||||||
|
LC_ASSERT(it, "internal compiler error: note is not an identifier");
|
||||||
|
if (it->anote.name->eident.name == i) {
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
422
src/compiler/string.c
Normal file
422
src/compiler/string.c
Normal file
@@ -0,0 +1,422 @@
|
|||||||
|
LC_FUNCTION int64_t LC__ClampTop(int64_t val, int64_t max) {
|
||||||
|
if (val > max) val = max;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION char LC_ToLowerCase(char a) {
|
||||||
|
if (a >= 'A' && a <= 'Z') a += 32;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION char LC_ToUpperCase(char a) {
|
||||||
|
if (a >= 'a' && a <= 'z') a -= 32;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION bool LC_IsWhitespace(char w) {
|
||||||
|
bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r';
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION bool LC_IsAlphabetic(char a) {
|
||||||
|
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION bool LC_IsIdent(char a) {
|
||||||
|
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || a == '_';
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION bool LC_IsDigit(char a) {
|
||||||
|
bool result = a >= '0' && a <= '9';
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION bool LC_IsAlphanumeric(char a) {
|
||||||
|
bool result = LC_IsDigit(a) || LC_IsAlphabetic(a);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION bool LC_AreEqual(LC_String a, LC_String b, unsigned ignore_case) {
|
||||||
|
if (a.len != b.len) return false;
|
||||||
|
for (int64_t i = 0; i < a.len; i++) {
|
||||||
|
char A = a.str[i];
|
||||||
|
char B = b.str[i];
|
||||||
|
if (ignore_case) {
|
||||||
|
A = LC_ToLowerCase(A);
|
||||||
|
B = LC_ToLowerCase(B);
|
||||||
|
}
|
||||||
|
if (A != B)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION bool LC_EndsWith(LC_String a, LC_String end, unsigned ignore_case) {
|
||||||
|
LC_String a_end = LC_GetPostfix(a, end.len);
|
||||||
|
bool result = LC_AreEqual(end, a_end, ignore_case);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION bool LC_StartsWith(LC_String a, LC_String start, unsigned ignore_case) {
|
||||||
|
LC_String a_start = LC_GetPrefix(a, start.len);
|
||||||
|
bool result = LC_AreEqual(start, a_start, ignore_case);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_MakeString(char *str, int64_t len) {
|
||||||
|
LC_String result;
|
||||||
|
result.str = (char *)str;
|
||||||
|
result.len = len;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_CopyString(LC_Arena *allocator, LC_String string) {
|
||||||
|
char *copy = (char *)LC_PushSize(allocator, sizeof(char) * (string.len + 1));
|
||||||
|
LC_MemoryCopy(copy, string.str, string.len);
|
||||||
|
copy[string.len] = 0;
|
||||||
|
LC_String result = LC_MakeString(copy, string.len);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_CopyChar(LC_Arena *allocator, char *s) {
|
||||||
|
int64_t len = LC_StrLen(s);
|
||||||
|
char *copy = (char *)LC_PushSize(allocator, sizeof(char) * (len + 1));
|
||||||
|
LC_MemoryCopy(copy, s, len);
|
||||||
|
copy[len] = 0;
|
||||||
|
LC_String result = LC_MakeString(copy, len);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_NormalizePath(LC_Arena *allocator, LC_String s) {
|
||||||
|
LC_String copy = LC_CopyString(allocator, s);
|
||||||
|
for (int64_t i = 0; i < copy.len; i++) {
|
||||||
|
if (copy.str[i] == '\\')
|
||||||
|
copy.str[i] = '/';
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_NormalizePathUnsafe(LC_String s) {
|
||||||
|
for (int64_t i = 0; i < s.len; i++) {
|
||||||
|
if (s.str[i] == '\\')
|
||||||
|
s.str[i] = '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_Chop(LC_String string, int64_t len) {
|
||||||
|
len = LC__ClampTop(len, string.len);
|
||||||
|
LC_String result = LC_MakeString(string.str, string.len - len);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_Skip(LC_String string, int64_t len) {
|
||||||
|
len = LC__ClampTop(len, string.len);
|
||||||
|
int64_t remain = string.len - len;
|
||||||
|
LC_String result = LC_MakeString(string.str + len, remain);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_GetPostfix(LC_String string, int64_t len) {
|
||||||
|
len = LC__ClampTop(len, string.len);
|
||||||
|
int64_t remain_len = string.len - len;
|
||||||
|
LC_String result = LC_MakeString(string.str + remain_len, len);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_GetPrefix(LC_String string, int64_t len) {
|
||||||
|
len = LC__ClampTop(len, string.len);
|
||||||
|
LC_String result = LC_MakeString(string.str, len);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_GetNameNoExt(LC_String s) {
|
||||||
|
return LC_SkipToLastSlash(LC_ChopLastPeriod(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_Slice(LC_String string, int64_t first_index, int64_t one_past_last_index) {
|
||||||
|
if (one_past_last_index < 0) one_past_last_index = string.len + one_past_last_index + 1;
|
||||||
|
if (first_index < 0) first_index = string.len + first_index;
|
||||||
|
LC_ASSERT(NULL, first_index < one_past_last_index && "LC_Slice, first_index is bigger then one_past_last_index");
|
||||||
|
LC_ASSERT(NULL, string.len > 0 && "Slicing string of length 0! Might be an error!");
|
||||||
|
LC_String result = string;
|
||||||
|
if (string.len > 0) {
|
||||||
|
if (one_past_last_index > first_index) {
|
||||||
|
first_index = LC__ClampTop(first_index, string.len - 1);
|
||||||
|
one_past_last_index = LC__ClampTop(one_past_last_index, string.len);
|
||||||
|
result.str += first_index;
|
||||||
|
result.len = one_past_last_index - first_index;
|
||||||
|
} else {
|
||||||
|
result.len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION bool LC_Seek(LC_String string, LC_String find, LC_FindFlag flags, int64_t *index_out) {
|
||||||
|
bool ignore_case = flags & LC_FindFlag_IgnoreCase ? true : false;
|
||||||
|
bool result = false;
|
||||||
|
if (flags & LC_FindFlag_MatchFindLast) {
|
||||||
|
for (int64_t i = string.len; i != 0; i--) {
|
||||||
|
int64_t index = i - 1;
|
||||||
|
LC_String substring = LC_Slice(string, index, index + find.len);
|
||||||
|
if (LC_AreEqual(substring, find, ignore_case)) {
|
||||||
|
if (index_out)
|
||||||
|
*index_out = index;
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int64_t i = 0; i < string.len; i++) {
|
||||||
|
LC_String substring = LC_Slice(string, i, i + find.len);
|
||||||
|
if (LC_AreEqual(substring, find, ignore_case)) {
|
||||||
|
if (index_out)
|
||||||
|
*index_out = i;
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION int64_t LC_Find(LC_String string, LC_String find, LC_FindFlag flag) {
|
||||||
|
int64_t result = -1;
|
||||||
|
LC_Seek(string, find, flag, &result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_StringList LC_Split(LC_Arena *allocator, LC_String string, LC_String find, LC_SplitFlag flags) {
|
||||||
|
LC_StringList result = LC_MakeEmptyList();
|
||||||
|
int64_t index = 0;
|
||||||
|
|
||||||
|
LC_FindFlag find_flag = flags & LC_SplitFlag_IgnoreCase ? LC_FindFlag_IgnoreCase : LC_FindFlag_None;
|
||||||
|
while (LC_Seek(string, find, find_flag, &index)) {
|
||||||
|
LC_String before_match = LC_MakeString(string.str, index);
|
||||||
|
LC_AddNode(allocator, &result, before_match);
|
||||||
|
if (flags & LC_SplitFlag_SplitInclusive) {
|
||||||
|
LC_String match = LC_MakeString(string.str + index, find.len);
|
||||||
|
LC_AddNode(allocator, &result, match);
|
||||||
|
}
|
||||||
|
string = LC_Skip(string, index + find.len);
|
||||||
|
}
|
||||||
|
if (string.len) LC_AddNode(allocator, &result, string);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_MergeWithSeparator(LC_Arena *allocator, LC_StringList list, LC_String separator) {
|
||||||
|
if (list.node_count == 0) return LC_MakeEmptyString();
|
||||||
|
if (list.char_count == 0) return LC_MakeEmptyString();
|
||||||
|
|
||||||
|
int64_t base_size = (list.char_count + 1);
|
||||||
|
int64_t sep_size = (list.node_count - 1) * separator.len;
|
||||||
|
int64_t size = base_size + sep_size;
|
||||||
|
char *buff = (char *)LC_PushSize(allocator, sizeof(char) * (size + 1));
|
||||||
|
LC_String string = LC_MakeString(buff, 0);
|
||||||
|
for (LC_StringNode *it = list.first; it; it = it->next) {
|
||||||
|
LC_ASSERT(NULL, string.len + it->string.len <= size);
|
||||||
|
LC_MemoryCopy(string.str + string.len, it->string.str, it->string.len);
|
||||||
|
string.len += it->string.len;
|
||||||
|
if (it != list.last) {
|
||||||
|
LC_MemoryCopy(string.str + string.len, separator.str, separator.len);
|
||||||
|
string.len += separator.len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LC_ASSERT(NULL, string.len == size - 1);
|
||||||
|
string.str[size] = 0;
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_MergeString(LC_Arena *allocator, LC_StringList list) {
|
||||||
|
return LC_MergeWithSeparator(allocator, list, LC_Lit(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_ChopLastSlash(LC_String s) {
|
||||||
|
LC_String result = s;
|
||||||
|
LC_Seek(s, LC_Lit("/"), LC_FindFlag_MatchFindLast, &result.len);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_ChopLastPeriod(LC_String s) {
|
||||||
|
LC_String result = s;
|
||||||
|
LC_Seek(s, LC_Lit("."), LC_FindFlag_MatchFindLast, &result.len);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_SkipToLastSlash(LC_String s) {
|
||||||
|
int64_t pos;
|
||||||
|
LC_String result = s;
|
||||||
|
if (LC_Seek(s, LC_Lit("/"), LC_FindFlag_MatchFindLast, &pos)) {
|
||||||
|
result = LC_Skip(result, pos + 1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_SkipToLastPeriod(LC_String s) {
|
||||||
|
int64_t pos;
|
||||||
|
LC_String result = s;
|
||||||
|
if (LC_Seek(s, LC_Lit("."), LC_FindFlag_MatchFindLast, &pos)) {
|
||||||
|
result = LC_Skip(result, pos + 1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION int64_t LC_StrLen(char *string) {
|
||||||
|
int64_t len = 0;
|
||||||
|
while (*string++ != 0)
|
||||||
|
len++;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_MakeFromChar(char *string) {
|
||||||
|
LC_String result;
|
||||||
|
result.str = (char *)string;
|
||||||
|
result.len = LC_StrLen(string);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_MakeEmptyString(void) {
|
||||||
|
return LC_MakeString(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_StringList LC_MakeEmptyList(void) {
|
||||||
|
LC_StringList result;
|
||||||
|
result.first = 0;
|
||||||
|
result.last = 0;
|
||||||
|
result.char_count = 0;
|
||||||
|
result.node_count = 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_FormatV(LC_Arena *allocator, const char *str, va_list args1) {
|
||||||
|
va_list args2;
|
||||||
|
va_copy(args2, args1);
|
||||||
|
int64_t len = LC_vsnprintf(0, 0, str, args2);
|
||||||
|
va_end(args2);
|
||||||
|
|
||||||
|
char *result = (char *)LC_PushSize(allocator, sizeof(char) * (len + 1));
|
||||||
|
LC_vsnprintf(result, (int)(len + 1), str, args1);
|
||||||
|
LC_String res = LC_MakeString(result, len);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_Format(LC_Arena *allocator, const char *str, ...) {
|
||||||
|
LC_FORMAT(allocator, str, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_StringNode *LC_CreateNode(LC_Arena *allocator, LC_String string) {
|
||||||
|
LC_StringNode *result = (LC_StringNode *)LC_PushSize(allocator, sizeof(LC_StringNode));
|
||||||
|
result->string = string;
|
||||||
|
result->next = 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_ReplaceNodeString(LC_StringList *list, LC_StringNode *node, LC_String new_string) {
|
||||||
|
list->char_count -= node->string.len;
|
||||||
|
list->char_count += new_string.len;
|
||||||
|
node->string = new_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_AddExistingNode(LC_StringList *list, LC_StringNode *node) {
|
||||||
|
if (list->first) {
|
||||||
|
list->last->next = node;
|
||||||
|
list->last = list->last->next;
|
||||||
|
} else {
|
||||||
|
list->first = list->last = node;
|
||||||
|
}
|
||||||
|
list->node_count += 1;
|
||||||
|
list->char_count += node->string.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_AddArray(LC_Arena *allocator, LC_StringList *list, char **array, int count) {
|
||||||
|
for (int i = 0; i < count; i += 1) {
|
||||||
|
LC_String s = LC_MakeFromChar(array[i]);
|
||||||
|
LC_AddNode(allocator, list, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_AddArrayWithPrefix(LC_Arena *allocator, LC_StringList *list, char *prefix, char **array, int count) {
|
||||||
|
for (int i = 0; i < count; i += 1) {
|
||||||
|
LC_Addf(allocator, list, "%s%s", prefix, array[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_StringList LC_MakeList(LC_Arena *allocator, LC_String a) {
|
||||||
|
LC_StringList result = LC_MakeEmptyList();
|
||||||
|
LC_AddNode(allocator, &result, a);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_StringList LC_CopyList(LC_Arena *allocator, LC_StringList a) {
|
||||||
|
LC_StringList result = LC_MakeEmptyList();
|
||||||
|
for (LC_StringNode *it = a.first; it; it = it->next) LC_AddNode(allocator, &result, it->string);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_StringList LC_ConcatLists(LC_Arena *allocator, LC_StringList a, LC_StringList b) {
|
||||||
|
LC_StringList result = LC_MakeEmptyList();
|
||||||
|
for (LC_StringNode *it = a.first; it; it = it->next) LC_AddNode(allocator, &result, it->string);
|
||||||
|
for (LC_StringNode *it = b.first; it; it = it->next) LC_AddNode(allocator, &result, it->string);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_StringNode *LC_AddNode(LC_Arena *allocator, LC_StringList *list, LC_String string) {
|
||||||
|
LC_StringNode *node = LC_CreateNode(allocator, string);
|
||||||
|
LC_AddExistingNode(list, node);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_StringNode *LC_Add(LC_Arena *allocator, LC_StringList *list, LC_String string) {
|
||||||
|
LC_String copy = LC_CopyString(allocator, string);
|
||||||
|
LC_StringNode *node = LC_CreateNode(allocator, copy);
|
||||||
|
LC_AddExistingNode(list, node);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_Addf(LC_Arena *allocator, LC_StringList *list, const char *str, ...) {
|
||||||
|
LC_FORMAT(allocator, str, result);
|
||||||
|
LC_AddNode(allocator, list, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String16 LC_ToWidecharEx(LC_Arena *allocator, LC_String string) {
|
||||||
|
LC_ASSERT(NULL, sizeof(wchar_t) == 2);
|
||||||
|
wchar_t *buffer = (wchar_t *)LC_PushSize(allocator, sizeof(wchar_t) * (string.len + 1));
|
||||||
|
int64_t size = LC_CreateWidecharFromChar(buffer, string.len + 1, string.str, string.len);
|
||||||
|
LC_String16 result = {buffer, size};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION wchar_t *LC_ToWidechar(LC_Arena *allocator, LC_String string) {
|
||||||
|
LC_String16 result = LC_ToWidecharEx(allocator, string);
|
||||||
|
return result.str;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_FromWidecharEx(LC_Arena *allocator, wchar_t *wstring, int64_t wsize) {
|
||||||
|
LC_ASSERT(NULL, sizeof(wchar_t) == 2);
|
||||||
|
|
||||||
|
int64_t buffer_size = (wsize + 1) * 2;
|
||||||
|
char *buffer = (char *)LC_PushSize(allocator, buffer_size);
|
||||||
|
int64_t size = LC_CreateCharFromWidechar(buffer, buffer_size, wstring, wsize);
|
||||||
|
LC_String result = LC_MakeString(buffer, size);
|
||||||
|
|
||||||
|
LC_ASSERT(NULL, size < buffer_size);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION int64_t LC_WideLength(wchar_t *string) {
|
||||||
|
int64_t len = 0;
|
||||||
|
while (*string++ != 0)
|
||||||
|
len++;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_FromWidechar(LC_Arena *allocator, wchar_t *wstring) {
|
||||||
|
int64_t size = LC_WideLength(wstring);
|
||||||
|
LC_String result = LC_FromWidecharEx(allocator, wstring, size);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
277
src/compiler/to_string.c
Normal file
277
src/compiler/to_string.c
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
LC_FUNCTION const char *LC_OSToString(LC_OS os) {
|
||||||
|
switch (os) {
|
||||||
|
case LC_OS_WINDOWS: return "OS_WINDOWS";
|
||||||
|
case LC_OS_LINUX: return "OS_LINUX";
|
||||||
|
case LC_OS_MAC: return "OS_MAC";
|
||||||
|
default: return "UNKNOWN_OPERATING_SYSTEM";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION const char *LC_GENToString(LC_GEN os) {
|
||||||
|
switch (os) {
|
||||||
|
case LC_GEN_C: return "GEN_C";
|
||||||
|
default: return "UNKNOWN_GENERATOR";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION const char *LC_ARCHToString(LC_ARCH arch) {
|
||||||
|
switch (arch) {
|
||||||
|
case LC_ARCH_X86: return "ARCH_X86";
|
||||||
|
case LC_ARCH_X64: return "ARCH_X64";
|
||||||
|
default: return "UNKNOWN_ARCHITECTURE";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION const char *LC_ASTKindToString(LC_ASTKind kind) {
|
||||||
|
static const char *strs[] = {
|
||||||
|
"ast null",
|
||||||
|
"ast error",
|
||||||
|
"ast note",
|
||||||
|
"ast note list",
|
||||||
|
"ast file",
|
||||||
|
"ast package",
|
||||||
|
"ast ignore",
|
||||||
|
"typespec procdure argument",
|
||||||
|
"typespec aggregate member",
|
||||||
|
"expr call item",
|
||||||
|
"expr compound item",
|
||||||
|
"expr note",
|
||||||
|
"stmt switch case",
|
||||||
|
"stmt switch default",
|
||||||
|
"stmt else if",
|
||||||
|
"stmt else",
|
||||||
|
"import",
|
||||||
|
"global note",
|
||||||
|
"decl proc",
|
||||||
|
"decl struct",
|
||||||
|
"decl union",
|
||||||
|
"decl var",
|
||||||
|
"decl const",
|
||||||
|
"decl typedef",
|
||||||
|
"typespec ident",
|
||||||
|
"typespec field",
|
||||||
|
"typespec pointer",
|
||||||
|
"typespec array",
|
||||||
|
"typespec proc",
|
||||||
|
"stmt block",
|
||||||
|
"stmt note",
|
||||||
|
"stmt return",
|
||||||
|
"stmt break",
|
||||||
|
"stmt continue",
|
||||||
|
"stmt defer",
|
||||||
|
"stmt for",
|
||||||
|
"stmt if",
|
||||||
|
"stmt switch",
|
||||||
|
"stmt assign",
|
||||||
|
"stmt expr",
|
||||||
|
"stmt var",
|
||||||
|
"stmt const",
|
||||||
|
"expr ident",
|
||||||
|
"expr string",
|
||||||
|
"expr int",
|
||||||
|
"expr float",
|
||||||
|
"expr bool",
|
||||||
|
"expr type",
|
||||||
|
"expr binary",
|
||||||
|
"expr unary",
|
||||||
|
"expr builtin",
|
||||||
|
"expr call",
|
||||||
|
"expr compound",
|
||||||
|
"expr cast",
|
||||||
|
"expr field",
|
||||||
|
"expr index",
|
||||||
|
"expr pointerindex",
|
||||||
|
"expr getvalueofpointer",
|
||||||
|
"expr getpointerofvalue",
|
||||||
|
};
|
||||||
|
if (kind < 0 || kind >= LC_ASTKind_Count) {
|
||||||
|
return "<invalid_ast_kind>";
|
||||||
|
}
|
||||||
|
return strs[kind];
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION const char *LC_TypeKindToString(LC_TypeKind kind) {
|
||||||
|
static const char *strs[] = {
|
||||||
|
"LC_TypeKind_char",
|
||||||
|
"LC_TypeKind_uchar",
|
||||||
|
"LC_TypeKind_short",
|
||||||
|
"LC_TypeKind_ushort",
|
||||||
|
"LC_TypeKind_bool",
|
||||||
|
"LC_TypeKind_int",
|
||||||
|
"LC_TypeKind_uint",
|
||||||
|
"LC_TypeKind_long",
|
||||||
|
"LC_TypeKind_ulong",
|
||||||
|
"LC_TypeKind_llong",
|
||||||
|
"LC_TypeKind_ullong",
|
||||||
|
"LC_TypeKind_float",
|
||||||
|
"LC_TypeKind_double",
|
||||||
|
"LC_TypeKind_void",
|
||||||
|
"LC_TypeKind_Struct",
|
||||||
|
"LC_TypeKind_Union",
|
||||||
|
"LC_TypeKind_Pointer",
|
||||||
|
"LC_TypeKind_Array",
|
||||||
|
"LC_TypeKind_Proc",
|
||||||
|
"LC_TypeKind_UntypedInt",
|
||||||
|
"LC_TypeKind_UntypedFloat",
|
||||||
|
"LC_TypeKind_UntypedString",
|
||||||
|
"LC_TypeKind_Incomplete",
|
||||||
|
"LC_TypeKind_Completing",
|
||||||
|
"LC_TypeKind_Error",
|
||||||
|
};
|
||||||
|
if (kind < 0 || kind >= T_TotalCount) {
|
||||||
|
return "<invalid_type_kind>";
|
||||||
|
}
|
||||||
|
return strs[kind];
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION const char *LC_DeclKindToString(LC_DeclKind decl_kind) {
|
||||||
|
static const char *strs[] = {
|
||||||
|
"declaration of error kind",
|
||||||
|
"type declaration",
|
||||||
|
"const declaration",
|
||||||
|
"variable declaration",
|
||||||
|
"procedure declaration",
|
||||||
|
"import declaration",
|
||||||
|
};
|
||||||
|
if (decl_kind < 0 || decl_kind >= LC_DeclKind_Count) {
|
||||||
|
return "<invalid_decl_kind>";
|
||||||
|
}
|
||||||
|
return strs[decl_kind];
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION const char *LC_TokenKindToString(LC_TokenKind token_kind) {
|
||||||
|
static const char *strs[] = {
|
||||||
|
"end of file",
|
||||||
|
"token error",
|
||||||
|
"comment",
|
||||||
|
"doc comment",
|
||||||
|
"file doc comment",
|
||||||
|
"package doc comment",
|
||||||
|
"note '@'",
|
||||||
|
"hash '#'",
|
||||||
|
"identifier",
|
||||||
|
"keyword",
|
||||||
|
"string literal",
|
||||||
|
"raw string literal",
|
||||||
|
"integer literal",
|
||||||
|
"float literal",
|
||||||
|
"unicode literal",
|
||||||
|
"open paren '('",
|
||||||
|
"close paren ')'",
|
||||||
|
"open brace '{'",
|
||||||
|
"close brace '}'",
|
||||||
|
"open bracket '['",
|
||||||
|
"close bracket ']'",
|
||||||
|
"comma ','",
|
||||||
|
"question mark '?'",
|
||||||
|
"semicolon ';'",
|
||||||
|
"period '.'",
|
||||||
|
"three dots '...'",
|
||||||
|
"colon ':'",
|
||||||
|
"multiply '*'",
|
||||||
|
"divide '/'",
|
||||||
|
"modulo '%'",
|
||||||
|
"left shift '<<'",
|
||||||
|
"right shift '>>'",
|
||||||
|
"add '+'",
|
||||||
|
"subtract '-'",
|
||||||
|
"equals '=='",
|
||||||
|
"lesser then '<'",
|
||||||
|
"greater then '>'",
|
||||||
|
"lesser then or equal '<='",
|
||||||
|
"greater then or equal '>='",
|
||||||
|
"not equal '!='",
|
||||||
|
"bit and '&'",
|
||||||
|
"bit or '|'",
|
||||||
|
"bit xor '^'",
|
||||||
|
"and '&&'",
|
||||||
|
"or '||'",
|
||||||
|
"addptr keyword",
|
||||||
|
"negation '~'",
|
||||||
|
"exclamation '!'",
|
||||||
|
"assignment '='",
|
||||||
|
"assignment '/='",
|
||||||
|
"assignment '*='",
|
||||||
|
"assignment '%='",
|
||||||
|
"assignment '-='",
|
||||||
|
"assignment '+='",
|
||||||
|
"assignment '&='",
|
||||||
|
"assignment '|='",
|
||||||
|
"assignment '^='",
|
||||||
|
"assignment '<<='",
|
||||||
|
"assignment '>>='",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (token_kind < 0 || token_kind >= LC_TokenKind_Count) {
|
||||||
|
return "<invalid_token_kind>";
|
||||||
|
}
|
||||||
|
return strs[token_kind];
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION const char *LC_TokenKindToOperator(LC_TokenKind token_kind) {
|
||||||
|
static const char *strs[] = {
|
||||||
|
"end of file",
|
||||||
|
"token error",
|
||||||
|
"comment",
|
||||||
|
"doc comment",
|
||||||
|
"file doc comment",
|
||||||
|
"package doc comment",
|
||||||
|
"@",
|
||||||
|
"#",
|
||||||
|
"identifier",
|
||||||
|
"keyword",
|
||||||
|
"string literal",
|
||||||
|
"raw string literal",
|
||||||
|
"integer literal",
|
||||||
|
"float literal",
|
||||||
|
"unicode literal",
|
||||||
|
"(",
|
||||||
|
")",
|
||||||
|
"{",
|
||||||
|
"}",
|
||||||
|
"[",
|
||||||
|
"]",
|
||||||
|
",",
|
||||||
|
"?",
|
||||||
|
";",
|
||||||
|
".",
|
||||||
|
"...",
|
||||||
|
":",
|
||||||
|
"*",
|
||||||
|
"/",
|
||||||
|
"%",
|
||||||
|
"<<",
|
||||||
|
">>",
|
||||||
|
"+",
|
||||||
|
"-",
|
||||||
|
"==",
|
||||||
|
"<",
|
||||||
|
">",
|
||||||
|
"<=",
|
||||||
|
">=",
|
||||||
|
"!=",
|
||||||
|
"&",
|
||||||
|
"|",
|
||||||
|
"^",
|
||||||
|
"&&",
|
||||||
|
"||",
|
||||||
|
"+",
|
||||||
|
"~",
|
||||||
|
"!",
|
||||||
|
"=",
|
||||||
|
"/=",
|
||||||
|
"*=",
|
||||||
|
"%=",
|
||||||
|
"-=",
|
||||||
|
"+=",
|
||||||
|
"&=",
|
||||||
|
"|=",
|
||||||
|
"^=",
|
||||||
|
"<<=",
|
||||||
|
">>=",
|
||||||
|
};
|
||||||
|
if (token_kind < 0 || token_kind >= LC_TokenKind_Count) {
|
||||||
|
return "<invalid_token_operator>";
|
||||||
|
}
|
||||||
|
return strs[token_kind];
|
||||||
|
}
|
||||||
158
src/compiler/unicode.c
Normal file
158
src/compiler/unicode.c
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
LC_FUNCTION LC_UTF32Result LC_ConvertUTF16ToUTF32(uint16_t *c, int max_advance) {
|
||||||
|
LC_UTF32Result result;
|
||||||
|
LC_MemoryZero(&result, sizeof(result));
|
||||||
|
if (max_advance >= 1) {
|
||||||
|
result.advance = 1;
|
||||||
|
result.out_str = c[0];
|
||||||
|
if (c[0] >= 0xD800 && c[0] <= 0xDBFF && c[1] >= 0xDC00 && c[1] <= 0xDFFF) {
|
||||||
|
if (max_advance >= 2) {
|
||||||
|
result.out_str = 0x10000;
|
||||||
|
result.out_str += (uint32_t)(c[0] & 0x03FF) << 10u | (c[1] & 0x03FF);
|
||||||
|
result.advance = 2;
|
||||||
|
} else
|
||||||
|
result.error = 2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.error = 1;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_UTF8Result LC_ConvertUTF32ToUTF8(uint32_t codepoint) {
|
||||||
|
LC_UTF8Result result;
|
||||||
|
LC_MemoryZero(&result, sizeof(result));
|
||||||
|
|
||||||
|
if (codepoint <= 0x7F) {
|
||||||
|
result.len = 1;
|
||||||
|
result.out_str[0] = (char)codepoint;
|
||||||
|
} else if (codepoint <= 0x7FF) {
|
||||||
|
result.len = 2;
|
||||||
|
result.out_str[0] = 0xc0 | (0x1f & (codepoint >> 6));
|
||||||
|
result.out_str[1] = 0x80 | (0x3f & codepoint);
|
||||||
|
} else if (codepoint <= 0xFFFF) { // 16 bit word
|
||||||
|
result.len = 3;
|
||||||
|
result.out_str[0] = 0xe0 | (0xf & (codepoint >> 12)); // 4 bits
|
||||||
|
result.out_str[1] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits
|
||||||
|
result.out_str[2] = 0x80 | (0x3f & codepoint); // 6 bits
|
||||||
|
} else if (codepoint <= 0x10FFFF) { // 21 bit word
|
||||||
|
result.len = 4;
|
||||||
|
result.out_str[0] = 0xf0 | (0x7 & (codepoint >> 18)); // 3 bits
|
||||||
|
result.out_str[1] = 0x80 | (0x3f & (codepoint >> 12)); // 6 bits
|
||||||
|
result.out_str[2] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits
|
||||||
|
result.out_str[3] = 0x80 | (0x3f & codepoint); // 6 bits
|
||||||
|
} else {
|
||||||
|
result.error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_UTF32Result LC_ConvertUTF8ToUTF32(char *c, int max_advance) {
|
||||||
|
LC_UTF32Result result;
|
||||||
|
LC_MemoryZero(&result, sizeof(result));
|
||||||
|
|
||||||
|
if ((c[0] & 0x80) == 0) { // Check if leftmost zero of first byte is unset
|
||||||
|
if (max_advance >= 1) {
|
||||||
|
result.out_str = c[0];
|
||||||
|
result.advance = 1;
|
||||||
|
} else result.error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((c[0] & 0xe0) == 0xc0) {
|
||||||
|
if ((c[1] & 0xc0) == 0x80) { // Continuation byte required
|
||||||
|
if (max_advance >= 2) {
|
||||||
|
result.out_str = (uint32_t)(c[0] & 0x1f) << 6u | (c[1] & 0x3f);
|
||||||
|
result.advance = 2;
|
||||||
|
} else result.error = 2;
|
||||||
|
} else result.error = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((c[0] & 0xf0) == 0xe0) {
|
||||||
|
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80) { // Two continuation bytes required
|
||||||
|
if (max_advance >= 3) {
|
||||||
|
result.out_str = (uint32_t)(c[0] & 0xf) << 12u | (uint32_t)(c[1] & 0x3f) << 6u | (c[2] & 0x3f);
|
||||||
|
result.advance = 3;
|
||||||
|
} else result.error = 3;
|
||||||
|
} else result.error = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((c[0] & 0xf8) == 0xf0) {
|
||||||
|
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80 && (c[3] & 0xc0) == 0x80) { // Three continuation bytes required
|
||||||
|
if (max_advance >= 4) {
|
||||||
|
result.out_str = (uint32_t)(c[0] & 0xf) << 18u | (uint32_t)(c[1] & 0x3f) << 12u | (uint32_t)(c[2] & 0x3f) << 6u | (uint32_t)(c[3] & 0x3f);
|
||||||
|
result.advance = 4;
|
||||||
|
} else result.error = 4;
|
||||||
|
} else result.error = 4;
|
||||||
|
} else result.error = 4;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_UTF16Result LC_ConvertUTF32ToUTF16(uint32_t codepoint) {
|
||||||
|
LC_UTF16Result result;
|
||||||
|
LC_MemoryZero(&result, sizeof(result));
|
||||||
|
if (codepoint < 0x10000) {
|
||||||
|
result.out_str[0] = (uint16_t)codepoint;
|
||||||
|
result.out_str[1] = 0;
|
||||||
|
result.len = 1;
|
||||||
|
} else if (codepoint <= 0x10FFFF) {
|
||||||
|
uint32_t code = (codepoint - 0x10000);
|
||||||
|
result.out_str[0] = (uint16_t)(0xD800 | (code >> 10));
|
||||||
|
result.out_str[1] = (uint16_t)(0xDC00 | (code & 0x3FF));
|
||||||
|
result.len = 2;
|
||||||
|
} else {
|
||||||
|
result.error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LC__HANDLE_DECODE_ERROR(question_mark) \
|
||||||
|
{ \
|
||||||
|
if (outlen < buffer_size - 1) buffer[outlen++] = (question_mark); \
|
||||||
|
break; \
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION int64_t LC_CreateCharFromWidechar(char *buffer, int64_t buffer_size, wchar_t *in, int64_t inlen) {
|
||||||
|
int64_t outlen = 0;
|
||||||
|
for (int64_t i = 0; i < inlen && in[i];) {
|
||||||
|
LC_UTF32Result decode = LC_ConvertUTF16ToUTF32((uint16_t *)(in + i), (int)(inlen - i));
|
||||||
|
if (!decode.error) {
|
||||||
|
i += decode.advance;
|
||||||
|
LC_UTF8Result encode = LC_ConvertUTF32ToUTF8(decode.out_str);
|
||||||
|
if (!encode.error) {
|
||||||
|
for (int64_t j = 0; j < encode.len; j++) {
|
||||||
|
if (outlen < buffer_size - 1) {
|
||||||
|
buffer[outlen++] = encode.out_str[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else LC__HANDLE_DECODE_ERROR('?');
|
||||||
|
} else LC__HANDLE_DECODE_ERROR('?');
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[outlen] = 0;
|
||||||
|
return outlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION int64_t LC_CreateWidecharFromChar(wchar_t *buffer, int64_t buffer_size, char *in, int64_t inlen) {
|
||||||
|
int64_t outlen = 0;
|
||||||
|
for (int64_t i = 0; i < inlen;) {
|
||||||
|
LC_UTF32Result decode = LC_ConvertUTF8ToUTF32(in + i, (int)(inlen - i));
|
||||||
|
if (!decode.error) {
|
||||||
|
i += decode.advance;
|
||||||
|
LC_UTF16Result encode = LC_ConvertUTF32ToUTF16(decode.out_str);
|
||||||
|
if (!encode.error) {
|
||||||
|
for (int64_t j = 0; j < encode.len; j++) {
|
||||||
|
if (outlen < buffer_size - 1) {
|
||||||
|
buffer[outlen++] = encode.out_str[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else LC__HANDLE_DECODE_ERROR(0x003f);
|
||||||
|
} else LC__HANDLE_DECODE_ERROR(0x003f);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[outlen] = 0;
|
||||||
|
return outlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef LC__HANDLE_DECODE_ERROR
|
||||||
30
src/compiler/unix_arena.c
Normal file
30
src/compiler/unix_arena.c
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#define LC_V_UNIX_PAGE_SIZE 4096
|
||||||
|
|
||||||
|
LC_FUNCTION LC_VMemory LC_VReserve(size_t size) {
|
||||||
|
LC_VMemory result = {};
|
||||||
|
size_t size_aligned = LC_AlignUp(size, LC_V_UNIX_PAGE_SIZE);
|
||||||
|
result.data = (uint8_t *)mmap(0, size_aligned, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||||
|
LC_Assertf(result.data, "Failed to reserve memory using mmap!!");
|
||||||
|
if (result.data) {
|
||||||
|
result.reserve = size_aligned;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION bool LC_VCommit(LC_VMemory *m, size_t commit) {
|
||||||
|
uint8_t *pointer = LC_V_AdvanceCommit(m, &commit, LC_V_UNIX_PAGE_SIZE);
|
||||||
|
if (pointer) {
|
||||||
|
int mprotect_result = mprotect(pointer, commit, PROT_READ | PROT_WRITE);
|
||||||
|
LC_Assertf(mprotect_result == 0, "Failed to commit more memory using mmap");
|
||||||
|
if (mprotect_result == 0) {
|
||||||
|
m->commit += commit;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_VDeallocate(LC_VMemory *m) {
|
||||||
|
int result = munmap(m->data, m->reserve);
|
||||||
|
LC_Assertf(result == 0, "Failed to release virtual memory using munmap");
|
||||||
|
}
|
||||||
89
src/compiler/unix_filesystem.c
Normal file
89
src/compiler/unix_filesystem.c
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
LC_FUNCTION bool LC_EnableTerminalColors(void) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION bool LC_IsDir(LC_Arena *arena, LC_String path) {
|
||||||
|
bool result = false;
|
||||||
|
LC_TempArena ch = LC_BeginTemp(arena);
|
||||||
|
LC_String copy = LC_CopyString(arena, path);
|
||||||
|
|
||||||
|
struct stat s;
|
||||||
|
if (stat(copy.str, &s) != 0) {
|
||||||
|
result = false;
|
||||||
|
} else {
|
||||||
|
result = S_ISDIR(s.st_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_EndTemp(ch);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_GetAbsolutePath(LC_Arena *arena, LC_String relative) {
|
||||||
|
LC_String copy = LC_CopyString(arena, relative);
|
||||||
|
char *buffer = (char *)LC_PushSizeNonZeroed(arena, PATH_MAX);
|
||||||
|
realpath((char *)copy.str, buffer);
|
||||||
|
LC_String result = LC_MakeFromChar(buffer);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION bool LC_IsValid(LC_FileIter it) {
|
||||||
|
return it.is_valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION void LC_Advance(LC_FileIter *it) {
|
||||||
|
struct dirent *file = 0;
|
||||||
|
while ((file = readdir((DIR *)it->dir)) != NULL) {
|
||||||
|
if (file->d_name[0] == '.' && file->d_name[1] == '.' && file->d_name[2] == 0) continue;
|
||||||
|
if (file->d_name[0] == '.' && file->d_name[1] == 0) continue;
|
||||||
|
|
||||||
|
it->is_directory = file->d_type == DT_DIR;
|
||||||
|
it->filename = LC_CopyChar(it->arena, file->d_name);
|
||||||
|
|
||||||
|
const char *dir_char_ending = it->is_directory ? "/" : "";
|
||||||
|
const char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/";
|
||||||
|
it->relative_path = LC_Format(it->arena, "%.*s%s%s%s", LC_Expand(it->path), separator, file->d_name, dir_char_ending);
|
||||||
|
it->absolute_path = LC_GetAbsolutePath(it->arena, it->relative_path);
|
||||||
|
if (it->is_directory) it->absolute_path = LC_Format(it->arena, "%.*s/", LC_Expand(it->absolute_path));
|
||||||
|
it->is_valid = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
it->is_valid = false;
|
||||||
|
closedir((DIR *)it->dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_FileIter LC_IterateFiles(LC_Arena *arena, LC_String path) {
|
||||||
|
LC_FileIter it = {0};
|
||||||
|
it.arena = arena;
|
||||||
|
it.path = path = LC_CopyString(arena, path);
|
||||||
|
it.dir = (void *)opendir((char *)path.str);
|
||||||
|
if (!it.dir) return it;
|
||||||
|
|
||||||
|
LC_Advance(&it);
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
LC_FUNCTION LC_String LC_ReadFile(LC_Arena *arena, LC_String path) {
|
||||||
|
LC_String result = LC_MakeEmptyString();
|
||||||
|
|
||||||
|
// ftell returns insane size if file is
|
||||||
|
// a directory **on some machines** KEKW
|
||||||
|
if (LC_IsDir(arena, path)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = LC_CopyString(arena, path);
|
||||||
|
FILE *f = fopen(path.str, "rb");
|
||||||
|
if (f) {
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
result.len = ftell(f);
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
|
||||||
|
result.str = (char *)LC_PushSizeNonZeroed(arena, result.len + 1);
|
||||||
|
fread(result.str, result.len, 1, f);
|
||||||
|
result.str[result.len] = 0;
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user