Refresh the repo
This commit is contained in:
226
README.md
226
README.md
@@ -1,22 +1,43 @@
|
|||||||
# The Core Language
|
# The Core Language
|
||||||
|
|
||||||
A statically typed systems programming language that **you shouldn't use**.
|
Core is an experimental systems programming language and compiler I built as one of my first "full" language projects.
|
||||||
I mark it as complete, it's pretty buggy but I had a lot of fun making it.
|
|
||||||
Learned a lot about language design and implementation, very worthwhile experience.
|
|
||||||
If you are intersted in programming languages, checkout bottom of the readme (there are useful resources there!).
|
|
||||||
The language is usable,
|
|
||||||
it lacks a macro system/generics for full convenience. It can be combined with C preprocessor
|
|
||||||
or python but where is fun in that?
|
|
||||||
Generics seem easy but I wanted something more powerful with compile time tree rewriting capabilities
|
|
||||||
but thats complicated, I dont have good design for that. :(
|
|
||||||
|
|
||||||
|
It is intentionally kept here as a learning artifact: useful to read, fun to tinker with, and definitely not production-ready.
|
||||||
|
|
||||||
The basic premise of the language is simplicity and debuggability,
|
## What this project is
|
||||||
reinforcing already useful ideas from C,C++,Go,Odin,Jai and shaving off round edges.
|
|
||||||
|
|
||||||
## Simple drawing to window example
|
- A compiler written in C++
|
||||||
|
- A language with `.core` source files
|
||||||
|
- A C code generator backend (Core -> C)
|
||||||
|
- A small standard-module set in `modules/`
|
||||||
|
- A collection of examples in `examples/`
|
||||||
|
|
||||||
``` odin
|
The compiler aims for simple syntax, explicit behavior, and debuggable output.
|
||||||
|
|
||||||
|
## Current status
|
||||||
|
|
||||||
|
- Archived hobby project / historical snapshot
|
||||||
|
- Works in many cases, but has known bugs and rough edges
|
||||||
|
- Best viewed as a language implementation study, not a stable toolchain
|
||||||
|
- I have made modifications over time and have not recently re-verified a clean build, so the project may not build out of the box
|
||||||
|
|
||||||
|
If you are new to compilers, this repo can still be valuable as a real-world "first serious compiler" codebase.
|
||||||
|
|
||||||
|
## Language highlights
|
||||||
|
|
||||||
|
- Static typing with strict checks
|
||||||
|
- Untyped compile-time constants (with big-int support in constant evaluation)
|
||||||
|
- Order-independent declarations
|
||||||
|
- Modules via `#import`
|
||||||
|
- Conditional loading (for example OS-specific modules)
|
||||||
|
- Slices, arrays, structs, unions, enums
|
||||||
|
- Multiple return values
|
||||||
|
- Operator overloading
|
||||||
|
- Runtime type information (`Type`, `GetTypeInfo`, `Any`)
|
||||||
|
|
||||||
|
## Tiny syntax taste
|
||||||
|
|
||||||
|
```core
|
||||||
#import "Multimedia.core"
|
#import "Multimedia.core"
|
||||||
|
|
||||||
main :: (): int
|
main :: (): int
|
||||||
@@ -25,118 +46,111 @@ main :: (): int
|
|||||||
if Mu.key[Key.Escape].down
|
if Mu.key[Key.Escape].down
|
||||||
Mu.quit = true
|
Mu.quit = true
|
||||||
|
|
||||||
for y := 0, y < Mu.window.y, y+=1
|
for y := 0, y < Mu.window.y, y += 1
|
||||||
for x := 0, x < Mu.window.x, x+=1
|
for x := 0, x < Mu.window.x, x += 1
|
||||||
Mu.screen[x + y*Mu.window.x] = 0xFFFFFF00
|
Mu.screen[x + y * Mu.window.x] = 0xFFFFFF00
|
||||||
|
|
||||||
|
return 0
|
||||||
```
|
```
|
||||||
|
|
||||||
## Features
|
For more examples, see:
|
||||||
|
|
||||||
* **Debuggers**(Visual Studio, Remedybg) **fully work** with the language, you can step through the program
|
- `examples/language_basics.core`
|
||||||
* **No external dependencies**, you just setup clang and call build.bat
|
- `examples/arrays_and_slices.core`
|
||||||
* Compiles to C code, in the future it will also compile to bytecode and hopefully a raw x64 executable
|
- `examples/operator_overloading.core`
|
||||||
* Very strict Go like type system with untyped literals
|
- `examples/runtime_type_information.core`
|
||||||
* **Order independent declarations**
|
|
||||||
* Module system
|
|
||||||
* Tree shaking (unused code is not compiled)
|
|
||||||
* **Windows and Linux**(only tested on Ubuntu) support
|
|
||||||
* Conditional compilation
|
|
||||||
* Runtime type reflection
|
|
||||||
* Typesafe variadic arguments
|
|
||||||
* Operator overloading
|
|
||||||
* Slices
|
|
||||||
* Multiple return values
|
|
||||||
|
|
||||||
## What's missing ?
|
## Compiler pipeline
|
||||||
|
|
||||||
- [x] Typechecking - very strict typesystem, inspired by Go, no implicit conversions outside of special types like void or Any.
|
At a high level:
|
||||||
- [x] Constant evaluation and constant folding - Folding and precomputing every expression that can be calculated at compile time.
|
|
||||||
- [x] Untyped types - Context dependent type assignment of constant expressions, this is a feature I really loved in Go, it makes constants work very well with a very strict type system and it makes errors like overflow of constants in C due to bad size specifier impossible
|
|
||||||
- [x] Infinite precision integers in constants using big ints! No more overflows or underflows (at least at compile time).
|
|
||||||
|
|
||||||
- [x] Module system
|
1. Lex + parse `.core` source into AST
|
||||||
- [x] Lazy evaluation of modules (unused code is not compiled or typechecked)
|
2. Resolve symbols and types
|
||||||
- [x] Import module, load project file distinction
|
3. Generate C code (`generated_main.c`)
|
||||||
- [x] Linking to libraries through importing a module, module calls ```#link``` and that is added to linked libraries.
|
4. Optionally compile generated C with `clang`
|
||||||
|
|
||||||
- [x] Order independent declarations - The ordering of functions in code files or modules does not matter, compiler figures all that stuff out for you. "main" can be wherever you want it to be and all functions should be available without problems
|
One design goal was debugger friendliness, so generated C includes line synchronization for stepping through source.
|
||||||
|
|
||||||
- [x] Synchronize generated C code with original source using line directives so that debuggers work
|
## Building the compiler
|
||||||
- [x] Fix overshoots when debugger goes to brace in c code
|
|
||||||
|
|
||||||
- [x] Expressions
|
### Windows
|
||||||
- [x] Compounds with named fields and numbered fields
|
|
||||||
- [x] Functions calls with named arguments
|
|
||||||
- [x] All the standard binary, unary expressions
|
|
||||||
- [x] Pointer arithmetic and pointer as array
|
|
||||||
- [x] Dot access expression needs a redesign because it doesn't handle expressions after the dot, requires identifiers
|
|
||||||
- [ ] Casting might need a redesign not sure. From '->' to 'cast', and maybe more options like Odin ```transmute```.
|
|
||||||
|
|
||||||
- [x] Runtime reflection
|
```bat
|
||||||
- [x] Package of type and pointer which enables dynamic typing
|
build.bat
|
||||||
- [x] Types are values holding type ids, this way we can do if typeof(value) == S64 ;; blah
|
```
|
||||||
- [x] Typesafe variadic arguments using []Any slice (no more var args!)
|
|
||||||
- [x] Any semantics that make it easy to use (automatic unpacking to pointer and type)
|
|
||||||
- [x] Optional type information dump which allows an application to recursively serialize the data. Something you would need special tools for in C++.
|
|
||||||
- [ ] Is the design of this correct? That's a lot of data.
|
|
||||||
|
|
||||||
- [ ] Builtin data structures
|
This builds the compiler executable in `build/`.
|
||||||
- [x] Arrays
|
|
||||||
- [x] Slices
|
|
||||||
- [ ] Dynamic arrays. (Do we want this?)
|
|
||||||
- [ ] Hash tables. (Do we want this?)
|
|
||||||
|
|
||||||
- [x] Multiple return values
|
### Linux
|
||||||
|
|
||||||
- [ ] Polymorphism
|
`build_unix.sh` exists, but the repo layout changed over time. If it fails, build manually:
|
||||||
- [x] C like void type
|
|
||||||
- [x] Dynamic typing using Any and operator overloading
|
|
||||||
- [ ] Generics aka compile time polymorphism aka parametric polymorphism
|
|
||||||
- [ ] Something akin to inheritence
|
|
||||||
- [ ] Something akin to C unions or Rust's enums
|
|
||||||
|
|
||||||
- [x] Operator overloading
|
```bash
|
||||||
- [x] Binary operators
|
clang src/language/core_main.cpp -O0 -Wall -Wno-unused-function -fno-exceptions -fdiagnostics-absolute-paths -g -o core.out
|
||||||
- [x] Unary operators
|
```
|
||||||
- [x] Bulletproof
|
|
||||||
- [ ] Assignment expressions?
|
|
||||||
|
|
||||||
- [x] Conditional compilation, you can include files based on a pattern:
|
## Running it
|
||||||
- [x] "$os_multimedia.core" expands to "win32_multimedia.core" or "unix_multimedia.core" depending on the platform.
|
|
||||||
|
|
||||||
- [x] Platforms
|
Compile one source file to C:
|
||||||
- [x] Conditional compilation
|
|
||||||
- [x] Windows
|
|
||||||
- [x] Fully working
|
|
||||||
- [x] Multimedia library
|
|
||||||
- [x] Linux (Only tested on ubuntu)
|
|
||||||
- [x] Paths
|
|
||||||
- [x] Reading files
|
|
||||||
- [x] Listing files
|
|
||||||
- [x] Virtual memory
|
|
||||||
- [ ] Multimedia library (maybe using SDL)
|
|
||||||
|
|
||||||
- [ ] Language constructs
|
```bash
|
||||||
- [x] Standard constructs like if, for loop etc.
|
./core.out examples/language_basics.core
|
||||||
- [ ] Jai like using statement
|
```
|
||||||
- [ ] Defer statement
|
|
||||||
- [ ] Unions (or something like unions)
|
|
||||||
- [ ] Unnamed blocks
|
|
||||||
|
|
||||||
## Building
|
or on Windows:
|
||||||
|
|
||||||
1. Install **Visual Studio** and **Clang**
|
```bat
|
||||||
1. Run **build.bat**
|
build\main.exe examples\language_basics.core
|
||||||
|
```
|
||||||
|
|
||||||
## Resources
|
This produces `generated_main.c`.
|
||||||
|
|
||||||
Stuff that helped me a lot programming the compiler. Hopefully they also will be helpful to you!
|
Then compile the generated C yourself (example):
|
||||||
|
|
||||||
* https://bitwise.handmade.network/ - series by Per Vognsen where he actually creates a C like language, very helpful, very hands on!
|
```bash
|
||||||
* https://hero.handmade.network/episode/code/day206/ - this episode of handmade hero started me on the entire compiler journey a long, long time ago.
|
clang generated_main.c -Wall -Wno-unused-function -Wno-parentheses-equality -g -o a.out
|
||||||
* https://www.youtube.com/watch?v=TH9VCN6UkyQ&list=PLmV5I2fxaiCKfxMBrNsU1kgKJXD3PkyxO - I have rewatched 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.
|
|
||||||
* 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.
|
There is also a test mode:
|
||||||
* https://github.com/c3lang/c3c - I sometimes looked at C3 compiler as a reference, the author also let me use his big int library he wrote sometime in the past! Thanks a lot!
|
|
||||||
* https://go.dev/blog/constants - article on golang type system, untyped types, constants that kind of stuff.
|
```bash
|
||||||
* https://github.com/gingerbill/odin - I sometimes peeked at the compiler to figure stuff out when I was confused about stuff. Also we get a free code indexing and syntax highlighting using odin sublime text plugin ! :D
|
./core.out -testing
|
||||||
|
```
|
||||||
|
|
||||||
|
which iterates through `examples/` (and `tests/` if present), compiles, and runs them.
|
||||||
|
|
||||||
|
## Repository map
|
||||||
|
|
||||||
|
- `src/language/` - compiler front/mid/back-end implementation
|
||||||
|
- `src/base/` - memory, strings, containers, utilities
|
||||||
|
- `src/os/` - OS abstraction layer (Windows/Linux)
|
||||||
|
- `modules/` - language modules and platform bindings
|
||||||
|
- `examples/` - sample Core programs
|
||||||
|
- `tools/meta.py` - meta tables used to generate token/keyword boilerplate
|
||||||
|
|
||||||
|
## Known rough edges
|
||||||
|
|
||||||
|
- Project paths changed over time; some scripts may need adjustment
|
||||||
|
- Windows support is more mature than Linux in some areas
|
||||||
|
- No polished package manager or full modern tooling around the language
|
||||||
|
- Feature set is incomplete (for example, no finished generics system)
|
||||||
|
|
||||||
|
## Why keep this repo public?
|
||||||
|
|
||||||
|
Because complete-but-imperfect projects are useful. This codebase captures:
|
||||||
|
|
||||||
|
- early language design decisions
|
||||||
|
- practical compiler architecture tradeoffs
|
||||||
|
- how a transpile-to-C strategy can speed up experimentation
|
||||||
|
|
||||||
|
If you are building your own language, you may find useful ideas here (and just as many cautionary tales).
|
||||||
|
|
||||||
|
## Learning resources that inspired this project
|
||||||
|
|
||||||
|
- https://bitwise.handmade.network/
|
||||||
|
- https://hero.handmade.network/episode/code/day206/
|
||||||
|
- https://www.youtube.com/watch?v=TH9VCN6UkyQ&list=PLmV5I2fxaiCKfxMBrNsU1kgKJXD3PkyxO
|
||||||
|
- A Retargetable C Compiler: Design and Implementation (Fraser/Hanson)
|
||||||
|
- https://github.com/c3lang/c3c
|
||||||
|
- https://go.dev/blog/constants
|
||||||
|
- https://github.com/gingerbill/odin
|
||||||
|
|||||||
16
build.bat
16
build.bat
@@ -1,10 +1,14 @@
|
|||||||
@echo off
|
@echo off
|
||||||
call "..\misc\compile_setup.bat"
|
|
||||||
|
|
||||||
bld --dont_compile_core
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
core_main.exe rtsgame/main.core
|
|
||||||
rem core_main.exe examples/unions.core
|
cl ../src/language/core_main.cpp -Zi -nologo -W3 -wd4200 -wd4267 -wd4244 -diagnostics:column -Fe:main.exe user32.lib
|
||||||
bld --dont_compile_core --link=vendor/raylib/windows/raylibdll.lib
|
|
||||||
rem build\generated_main.exe
|
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
|
rem cd build\pathfind_visualizer
|
||||||
|
|
||||||
|
rem call build.bat
|
||||||
|
|
||||||
|
rem cd ..\..
|
||||||
|
|||||||
@@ -1,249 +0,0 @@
|
|||||||
#import "raylib.core"
|
|
||||||
#import "LibC.core"
|
|
||||||
A :: #import "array.core"
|
|
||||||
MAP :: #load "map.core"
|
|
||||||
sqrtf :: #foreign (value: F32): F32
|
|
||||||
|
|
||||||
V2I :: struct
|
|
||||||
x: int
|
|
||||||
y: int
|
|
||||||
|
|
||||||
ANI_SetTile :: struct
|
|
||||||
set: bool
|
|
||||||
p: V2I
|
|
||||||
t: F32
|
|
||||||
|
|
||||||
WinX := 1280
|
|
||||||
WinY := 720
|
|
||||||
MouseX := 0
|
|
||||||
MouseY := 0
|
|
||||||
MouseP: Vector2
|
|
||||||
Mode := 0
|
|
||||||
RectX :: 16
|
|
||||||
RectY :: 16
|
|
||||||
Dt: F32
|
|
||||||
ANI_SetTiles: A.Array(ANI_SetTile)
|
|
||||||
|
|
||||||
MouseSelecting := false
|
|
||||||
MouseSelectionPivot: Vector2
|
|
||||||
MouseSelectionBox: Rectangle
|
|
||||||
MouseSelectedActors: A.Array(*MAP.Actor) // @todo: ids
|
|
||||||
|
|
||||||
main :: (): int
|
|
||||||
MAP.Init()
|
|
||||||
|
|
||||||
// InitAudioDevice()
|
|
||||||
// sound := LoadSound("catune - Pass the town, and to the C.mp3")
|
|
||||||
// SetMasterVolume(0.01)
|
|
||||||
// PlaySound(sound)
|
|
||||||
|
|
||||||
InitWindow(WinX, WinY, "Testing")
|
|
||||||
SetTargetFPS(60)
|
|
||||||
|
|
||||||
orange := ORANGE
|
|
||||||
orange.a = 255/2
|
|
||||||
|
|
||||||
brown := BROWN
|
|
||||||
brown.a = 255/2
|
|
||||||
|
|
||||||
actor_color := DARKGREEN
|
|
||||||
actor_color.a = 255/2
|
|
||||||
|
|
||||||
past_actor_color := BLUE
|
|
||||||
past_actor_color.a = 255/2
|
|
||||||
|
|
||||||
target_color := RED
|
|
||||||
target_color.a = 255/2
|
|
||||||
|
|
||||||
COLOR_SelectionBox := GREEN
|
|
||||||
COLOR_SelectionBox.a = 255/2
|
|
||||||
|
|
||||||
COLOR_Selected := COLOR_SelectionBox
|
|
||||||
|
|
||||||
testing := 0
|
|
||||||
for !WindowShouldClose()
|
|
||||||
defer ;; testing += 1
|
|
||||||
WinX = GetScreenWidth()
|
|
||||||
WinY = GetScreenHeight()
|
|
||||||
MouseX = GetMouseX()
|
|
||||||
MouseY = GetMouseY()
|
|
||||||
MouseP = GetMousePosition()
|
|
||||||
Dt = GetFrameTime()
|
|
||||||
map := &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
|
|
||||||
MAP.MoveTowardsTarget(it)
|
|
||||||
MAP.PathFindUpdate(map)
|
|
||||||
|
|
||||||
if IsKeyPressed(KEY_F4)
|
|
||||||
MAP.RandomizeActors()
|
|
||||||
|
|
||||||
|
|
||||||
BeginDrawing()
|
|
||||||
ClearBackground(RAYWHITE)
|
|
||||||
|
|
||||||
map_rectangle := Rectangle{0, 0, map.x->F32 * RectX, map.y->F32 * 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{x->F32 * RectX, y->F32 * 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)
|
|
||||||
A.Add(&ANI_SetTiles, {true, {x,y}})
|
|
||||||
if colliding && IsMouseButtonDown(MOUSE_BUTTON_RIGHT)
|
|
||||||
A.Add(&ANI_SetTiles, {false, {x,y}})
|
|
||||||
if colliding == true ;; color = {a = 100}
|
|
||||||
|
|
||||||
DrawRectangleRec(r2, color)
|
|
||||||
|
|
||||||
for tile_i := 0, tile_i < ANI_SetTiles.len, tile_i += 1
|
|
||||||
tile_it := ANI_SetTiles.data + tile_i
|
|
||||||
remove := false
|
|
||||||
|
|
||||||
t := tile_it.t
|
|
||||||
if tile_it.set == false
|
|
||||||
t = 1 - t
|
|
||||||
|
|
||||||
|
|
||||||
x := tile_it.p.x->F32 * RectX + 1
|
|
||||||
y := tile_it.p.y->F32 * RectY + 1
|
|
||||||
w: F32 = (RectX - 2)
|
|
||||||
h: F32 = (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 |= MAP.TILE_BLOCKER
|
|
||||||
else ;; *map_tile &= ~MAP.TILE_BLOCKER
|
|
||||||
remove = true
|
|
||||||
|
|
||||||
tile_it.t += Dt*8
|
|
||||||
if remove
|
|
||||||
A.UnorderedRemove(&ANI_SetTiles, tile_it)
|
|
||||||
tile_i -= 1
|
|
||||||
|
|
||||||
for i := 0, i < map.actors.len, i += 1
|
|
||||||
actor_it := map.actors.data + i
|
|
||||||
target_r := MAP.Rect(actor_it.target_p)
|
|
||||||
|
|
||||||
main_p := MAP.Circle(actor_it.p)
|
|
||||||
DrawCircleV(main_p, RectX/2, actor_color)
|
|
||||||
DrawRectangleRec(target_r, target_color)
|
|
||||||
|
|
||||||
smaller_the_further: F32 = 0
|
|
||||||
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 := MAP.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 := MAP.Rect(path_it.p)
|
|
||||||
DrawRectangleRec(path_r, orange)
|
|
||||||
s := TextFormat("%d", sqrtf(path_it.value_to_sort_by->F32)->int)
|
|
||||||
DrawText(s, path_r.x->int, path_r.y->int, 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 := MAP.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 := MAP.Circle(path_it.came_from)
|
|
||||||
p1 := MAP.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 := MAP.Rect(actor_it.p)
|
|
||||||
DrawRectangleRec(actor_box, COLOR_Selected)
|
|
||||||
|
|
||||||
if IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)
|
|
||||||
p := MAP.ScreenToMap(MouseP)
|
|
||||||
MAP.SetTargetP(actor_it, p)
|
|
||||||
|
|
||||||
if MouseSelecting
|
|
||||||
A.Reset(&MouseSelectedActors)
|
|
||||||
|
|
||||||
for actor_i := 0, actor_i < map.actors.len, actor_i += 1
|
|
||||||
actor_it := map.actors.data + actor_i
|
|
||||||
actor_box := MAP.Rect(actor_it.p)
|
|
||||||
|
|
||||||
if CheckCollisionRecs(actor_box, MouseSelectionBox)
|
|
||||||
A.Add(&MouseSelectedActors, actor_it)
|
|
||||||
|
|
||||||
DrawRectangleRec(MouseSelectionBox, COLOR_SelectionBox)
|
|
||||||
|
|
||||||
|
|
||||||
menu_open := false
|
|
||||||
if menu_open
|
|
||||||
text_size := 24
|
|
||||||
text_p := 4
|
|
||||||
text_y := WinY - text_size
|
|
||||||
|
|
||||||
DrawText("Space :: PathFind", text_p, text_y, text_size, GRAY)
|
|
||||||
text_y -= text_size
|
|
||||||
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
|
|
||||||
@@ -1,213 +0,0 @@
|
|||||||
CurrentMap: Map
|
|
||||||
|
|
||||||
PATH_SEARCHING :: 0
|
|
||||||
PATH_REACHED :: 1
|
|
||||||
PATH_UNREACHABLE :: 2
|
|
||||||
|
|
||||||
TILE_BLOCKER :: 1
|
|
||||||
TILE_ACTOR_IS_STANDING :: 2
|
|
||||||
|
|
||||||
Tile :: int
|
|
||||||
Map :: struct
|
|
||||||
data: *Tile
|
|
||||||
x: int
|
|
||||||
y: int
|
|
||||||
actors: A.Array(Actor)
|
|
||||||
|
|
||||||
Actor :: struct
|
|
||||||
p: V2I
|
|
||||||
target_p: V2I
|
|
||||||
map: *Map
|
|
||||||
|
|
||||||
open_paths: A.Array(Path)
|
|
||||||
close_paths: A.Array(Path)
|
|
||||||
tiles_visited: A.Array(V2I)
|
|
||||||
history: A.Array(Path)
|
|
||||||
|
|
||||||
Path :: struct
|
|
||||||
value_to_sort_by: int // distance from target
|
|
||||||
p: V2I
|
|
||||||
came_from: V2I
|
|
||||||
|
|
||||||
Rect :: (p: V2I): Rectangle
|
|
||||||
result := Rectangle{p.x->F32 * RectX, p.y->F32 * RectY, RectX, RectY}
|
|
||||||
return result
|
|
||||||
|
|
||||||
Circle :: (p: V2I): Vector2
|
|
||||||
result := Vector2{p.x->F32 * RectX + RectX/2, p.y->F32 * RectY + RectY/2}
|
|
||||||
return result
|
|
||||||
|
|
||||||
ScreenToMap :: (p: Vector2): V2I
|
|
||||||
p0 := p.x / RectX
|
|
||||||
p1 := p.y / RectY
|
|
||||||
result := V2I{p0->int, p1->int}
|
|
||||||
return result
|
|
||||||
|
|
||||||
AddActor :: (map: *Map, p: V2I): *Actor
|
|
||||||
A.Add(&map.actors, {p, p, map})
|
|
||||||
Assert(map.data[p.x + p.y * map.x] == 0)
|
|
||||||
map.data[p.x + p.y * map.x] |= TILE_ACTOR_IS_STANDING
|
|
||||||
actor := A.GetLast(&CurrentMap.actors)
|
|
||||||
return actor
|
|
||||||
|
|
||||||
ActorSetP :: (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
|
|
||||||
|
|
||||||
A.Reset(&actor.tiles_visited)
|
|
||||||
A.Reset(&actor.history)
|
|
||||||
A.Reset(&actor.open_paths)
|
|
||||||
A.Reset(&actor.close_paths)
|
|
||||||
|
|
||||||
SetTargetP :: (s: *Actor, p: V2I)
|
|
||||||
s.target_p = p
|
|
||||||
A.Reset(&s.tiles_visited)
|
|
||||||
A.Reset(&s.history)
|
|
||||||
A.Reset(&s.open_paths)
|
|
||||||
A.Reset(&s.close_paths)
|
|
||||||
|
|
||||||
GetRandomP :: (m: *Map): V2I
|
|
||||||
result: V2I = {GetRandomValue(0, CurrentMap.x - 1), GetRandomValue(0, CurrentMap.y - 1)}
|
|
||||||
return result
|
|
||||||
|
|
||||||
GetRandomUnblockedP :: (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(false, "Invalid codepath")
|
|
||||||
return {}
|
|
||||||
|
|
||||||
Init :: ()
|
|
||||||
CurrentMap.x = WinX / RectX
|
|
||||||
CurrentMap.y = WinY / RectY
|
|
||||||
bytes := sizeof(Tile) * CurrentMap.x->U64 * CurrentMap.y->U64
|
|
||||||
CurrentMap.data = malloc(bytes)
|
|
||||||
memset(CurrentMap.data, 0, bytes)
|
|
||||||
|
|
||||||
actor := AddActor(&CurrentMap, GetRandomUnblockedP(&CurrentMap))
|
|
||||||
actor.target_p = GetRandomUnblockedP(&CurrentMap)
|
|
||||||
|
|
||||||
actor2 := AddActor(&CurrentMap, GetRandomUnblockedP(&CurrentMap))
|
|
||||||
actor2.target_p = GetRandomUnblockedP(&CurrentMap)
|
|
||||||
|
|
||||||
RandomizeActors :: ()
|
|
||||||
map := &CurrentMap
|
|
||||||
for i := 0, i < map.actors.len, i += 1
|
|
||||||
it := map.actors.data + i
|
|
||||||
p := GetRandomUnblockedP(&CurrentMap)
|
|
||||||
ActorSetP(it, p)
|
|
||||||
it.target_p = GetRandomUnblockedP(&CurrentMap)
|
|
||||||
|
|
||||||
InsertOpenPath :: (s: *Actor, p: V2I, came_from: V2I, ignore_blocks: bool = false)
|
|
||||||
if p.x < 0 || p.x >= s.map.x ;; return
|
|
||||||
if p.y < 0 || p.y >= s.map.y ;; return
|
|
||||||
if ignore_blocks == false && s.map.data[p.x + p.y * s.map.x] != 0 ;; return
|
|
||||||
|
|
||||||
for i := 0, i < s.close_paths.len, i += 1
|
|
||||||
it := s.close_paths.data + i
|
|
||||||
if it.p.x == p.x && it.p.y == p.y ;; return
|
|
||||||
|
|
||||||
for i := 0, i < s.open_paths.len, i += 1
|
|
||||||
it := s.open_paths.data + i
|
|
||||||
if it.p.x == p.x && it.p.y == p.y ;; return
|
|
||||||
|
|
||||||
dx := s.target_p.x - p.x
|
|
||||||
dy := s.target_p.y - p.y
|
|
||||||
d := dx*dx + dy*dy
|
|
||||||
A.InsertSortedDecreasing(&s.open_paths, {d, p, came_from})
|
|
||||||
|
|
||||||
GetCloseP :: (s: *Actor, p: V2I): *Path
|
|
||||||
for i := 0, i < s.close_paths.len, i += 1
|
|
||||||
it := s.close_paths.data + i
|
|
||||||
if it.p.x == p.x && it.p.y == p.y ;; return it
|
|
||||||
|
|
||||||
Assert(false, "Invalid codepath")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
RecomputeHistory :: (s: *Actor)
|
|
||||||
if s.close_paths.len > 1
|
|
||||||
A.Reset(&s.history)
|
|
||||||
it := A.GetLast(&s.close_paths)
|
|
||||||
A.Add(&s.history, *it)
|
|
||||||
for i := 0,,i += 1
|
|
||||||
if it.p.x == s.p.x && it.p.y == s.p.y ;; break
|
|
||||||
if i > 512
|
|
||||||
A.Reset(&s.history)
|
|
||||||
break
|
|
||||||
it = GetCloseP(s, it.came_from)
|
|
||||||
A.Add(&s.history, *it)
|
|
||||||
A.Pop(&s.history)
|
|
||||||
|
|
||||||
MoveTowardsTarget :: (s: *Actor)
|
|
||||||
tile := s.map.data + s.p.x + s.p.y * s.map.x
|
|
||||||
if s.history.len > 0
|
|
||||||
step := A.Pop(&s.history)
|
|
||||||
new_tile := s.map.data + step.p.x + step.p.y * s.map.x
|
|
||||||
if *new_tile == 0
|
|
||||||
A.Add(&s.tiles_visited, s.p)
|
|
||||||
s.p = step.p
|
|
||||||
*tile &= ~TILE_ACTOR_IS_STANDING
|
|
||||||
*new_tile |= TILE_ACTOR_IS_STANDING
|
|
||||||
|
|
||||||
PathFindUpdate :: (map: *Map)
|
|
||||||
for actor_i := 0, actor_i < map.actors.len, actor_i += 1
|
|
||||||
s := map.actors.data + actor_i
|
|
||||||
for i := 0, i < s.history.len, i += 1
|
|
||||||
it := s.history.data + i
|
|
||||||
|
|
||||||
tile := s.map.data[it.p.x + it.p.y * s.map.x]
|
|
||||||
if tile != 0
|
|
||||||
A.Reset(&s.open_paths)
|
|
||||||
A.Reset(&s.close_paths)
|
|
||||||
A.Reset(&s.history)
|
|
||||||
break
|
|
||||||
|
|
||||||
PathFind(s)
|
|
||||||
|
|
||||||
PathFindStep :: (s: *Actor, compute_history: bool = true): bool
|
|
||||||
if s.open_paths.len == 0
|
|
||||||
// Reset if we didnt find solution
|
|
||||||
if s.close_paths.len != 0
|
|
||||||
last := A.GetLast(&s.close_paths)
|
|
||||||
reached_target := last.p.x == s.target_p.x && last.p.y == s.target_p.y
|
|
||||||
if reached_target == false
|
|
||||||
A.Reset(&s.open_paths)
|
|
||||||
A.Reset(&s.close_paths)
|
|
||||||
A.Reset(&s.history)
|
|
||||||
|
|
||||||
InsertOpenPath(s, s.p, s.p, ignore_blocks = true)
|
|
||||||
|
|
||||||
if s.close_paths.len != 0
|
|
||||||
last := A.GetLast(&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 := A.Pop(&s.open_paths)
|
|
||||||
A.Add(&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 :: (s: *Actor)
|
|
||||||
for i := 0, i < 32, i += 1
|
|
||||||
done := PathFindStep(s, false)
|
|
||||||
if done ;; break
|
|
||||||
RecomputeHistory(s)
|
|
||||||
1
examples/pathfind_visualizer
Submodule
1
examples/pathfind_visualizer
Submodule
Submodule examples/pathfind_visualizer added at caff4aaa2e
306
src/base/array.hpp
Normal file
306
src/base/array.hpp
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
// #define ARRAY_ALLOCATOR_TYPE Allocator
|
||||||
|
|
||||||
|
#ifndef ARRAY_PRIVATE_FUNCTION
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define ARRAY_PRIVATE_FUNCTION __attribute__((unused)) static
|
||||||
|
#else
|
||||||
|
#define ARRAY_PRIVATE_FUNCTION static
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ARRAY_ALLOCATE
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define ARRAY_ALLOCATE(allocator, size) malloc(size)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ARRAY_DEALLOCATE
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define ARRAY_DEALLOCATE(allocator, p) free(p)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ARRAY_ASSERT
|
||||||
|
#include <assert.h>
|
||||||
|
#define ARRAY_ASSERT(x) assert(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ARRAY_MemoryMove
|
||||||
|
#include <string.h>
|
||||||
|
#define ARRAY_MemoryMove(dst, src, size) memmove(dst, src, size)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ARRAY_SET_DEFAULT_ALLOCATOR
|
||||||
|
#define ARRAY_SET_DEFAULT_ALLOCATOR
|
||||||
|
// Example:
|
||||||
|
// #define ARRAY_SET_DEFAULT_ALLOCATOR if (!allocator) allocator = global_heap;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef DEFER_HEADER
|
||||||
|
#define ForArrayRemovable(a) for (int __i = 0; __i < (a).len; __i += 1)
|
||||||
|
#define ForArrayRemovablePrepare(a) \
|
||||||
|
auto &it = (a)[__i]; \
|
||||||
|
bool remove_it = false; \
|
||||||
|
Defer { \
|
||||||
|
if (remove_it) { \
|
||||||
|
(a).ordered_remove(it); \
|
||||||
|
__i -= 1; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
#define ForArrayRemovableDeclare() (remove_it = true)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(ARRAY_ALLOCATOR_CODE)
|
||||||
|
#if defined(ARRAY_ALLOCATOR_TYPE)
|
||||||
|
#define ARRAY_ALLOCATOR_CODE(x) x
|
||||||
|
#define ARRAY_ALLOCATOR_PARAM ARRAY_ALLOCATOR_TYPE *allocator,
|
||||||
|
#define ARRAY_ALLOCATOR_PASS allocator,
|
||||||
|
#define ARRAY__PASS(ctx, name) (ctx)->name,
|
||||||
|
#else
|
||||||
|
#define ARRAY_ALLOCATOR_CODE(x)
|
||||||
|
#define ARRAY_ALLOCATOR_PARAM
|
||||||
|
#define ARRAY_ALLOCATOR_PASS
|
||||||
|
#define ARRAY__PASS(ctx, name)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(For)
|
||||||
|
#define For2(a,it) for(auto &it : (a))
|
||||||
|
#define For(x) For2(x,it)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct Array {
|
||||||
|
ARRAY_ALLOCATOR_CODE(ARRAY_ALLOCATOR_TYPE *allocator;)
|
||||||
|
T *data;
|
||||||
|
int cap, len;
|
||||||
|
|
||||||
|
T &operator[](int index) {
|
||||||
|
ARRAY_ASSERT(index >= 0 && index < len);
|
||||||
|
return data[index];
|
||||||
|
}
|
||||||
|
T &operator[](long long index) {
|
||||||
|
ARRAY_ASSERT(index >= 0 && index < len);
|
||||||
|
return data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_first(T &item) { return &item == first(); }
|
||||||
|
bool is_last(T &item) { return &item == last(); }
|
||||||
|
|
||||||
|
bool contains(T *item) {
|
||||||
|
bool result = item >= data && item < data + len;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_index(T &item) {
|
||||||
|
ARRAY_ASSERT((data <= &item) && ((data + len) > &item));
|
||||||
|
size_t offset = &item - data;
|
||||||
|
return (int)offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(T item) {
|
||||||
|
try_growing();
|
||||||
|
data[len++] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Struct needs to have 'value_to_sort_by' field
|
||||||
|
void sorted_insert_decreasing(T item) {
|
||||||
|
int insert_index = -1;
|
||||||
|
For(*this) {
|
||||||
|
if (it.value_to_sort_by <= item.value_to_sort_by) {
|
||||||
|
insert_index = get_index(it);
|
||||||
|
insert(item, insert_index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insert_index == -1) {
|
||||||
|
add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bounded_add(T item) {
|
||||||
|
ARRAY_ASSERT(len + 1 <= cap);
|
||||||
|
try_growing(); // in case of error
|
||||||
|
data[len++] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *alloc(const T &item) {
|
||||||
|
try_growing();
|
||||||
|
T *ref = data + len++;
|
||||||
|
*ref = item;
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *alloc() {
|
||||||
|
try_growing();
|
||||||
|
T *ref = data + len++;
|
||||||
|
*ref = {};
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_array(T *items, int item_count) {
|
||||||
|
for (int i = 0; i < item_count; i += 1) {
|
||||||
|
add(items[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void reserve(int size) {
|
||||||
|
ARRAY_ASSERT(size == 0 || (size != 0 && size > cap));
|
||||||
|
ARRAY_SET_DEFAULT_ALLOCATOR;
|
||||||
|
|
||||||
|
void *p = ARRAY_ALLOCATE(allocator, size * sizeof(T));
|
||||||
|
ARRAY_ASSERT(p);
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
ARRAY_MemoryMove(p, data, len * sizeof(T));
|
||||||
|
ARRAY_DEALLOCATE(allocator, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
data = (T *)p;
|
||||||
|
cap = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(ARRAY_ALLOCATOR_PARAM int size) {
|
||||||
|
len = 0;
|
||||||
|
cap = 0;
|
||||||
|
data = 0;
|
||||||
|
ARRAY_ALLOCATOR_CODE(this->allocator = allocator;)
|
||||||
|
reserve(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
T pop() {
|
||||||
|
ARRAY_ASSERT(len > 0);
|
||||||
|
return data[--len];
|
||||||
|
}
|
||||||
|
|
||||||
|
void unordered_remove(T &item) { // DONT USE IN LOOPS !!!!
|
||||||
|
ARRAY_ASSERT(len > 0);
|
||||||
|
ARRAY_ASSERT(&item >= begin() && &item < end());
|
||||||
|
item = data[--len];
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_index(const T &item) {
|
||||||
|
ptrdiff_t index = (ptrdiff_t)(&item - data);
|
||||||
|
ARRAY_ASSERT(index >= 0 && index < len);
|
||||||
|
// ARRAY_ASSERT(index > INT_MIN && index < INT_MAX);
|
||||||
|
return (int)index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ordered_remove(T &item) { // DONT USE IN LOOPS !!!
|
||||||
|
ARRAY_ASSERT(len > 0);
|
||||||
|
ARRAY_ASSERT(&item >= begin() && &item < end());
|
||||||
|
int index = get_index(item);
|
||||||
|
ARRAY_ASSERT(index >= 0 && index < len);
|
||||||
|
|
||||||
|
int right_len = len - index - 1;
|
||||||
|
ARRAY_MemoryMove(data + index, data + index + 1, right_len * sizeof(T));
|
||||||
|
len -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(T item, int index) {
|
||||||
|
if (index == len) {
|
||||||
|
add(item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ARRAY_ASSERT(index < len);
|
||||||
|
ARRAY_ASSERT(index >= 0);
|
||||||
|
|
||||||
|
try_growing();
|
||||||
|
int right_len = len - index;
|
||||||
|
ARRAY_MemoryMove(data + index + 1, data + index, sizeof(T) * right_len);
|
||||||
|
data[index] = item;
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dealloc() {
|
||||||
|
if (data) ARRAY_DEALLOCATE(allocator, data);
|
||||||
|
data = 0;
|
||||||
|
len = cap = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<T> exact_copy(ARRAY_ALLOCATOR_CODE(ARRAY_ALLOCATOR_TYPE *allocator)) {
|
||||||
|
Array result = {};
|
||||||
|
ARRAY_ALLOCATOR_CODE(result.allocator = allocator;)
|
||||||
|
result.reserve(cap);
|
||||||
|
|
||||||
|
ARRAY_MemoryMove(result.data, data, sizeof(T) * len);
|
||||||
|
result.len = len;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<T> tight_copy(ARRAY_ALLOCATOR_CODE(ARRAY_ALLOCATOR_TYPE *allocator)) {
|
||||||
|
Array result = {};
|
||||||
|
ARRAY_ALLOCATOR_CODE(result.allocator = allocator;)
|
||||||
|
result.reserve(len);
|
||||||
|
|
||||||
|
ARRAY_MemoryMove(result.data, data, sizeof(T) * len);
|
||||||
|
result.len = len;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *first() {
|
||||||
|
ARRAY_ASSERT(len > 0);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
T *last() {
|
||||||
|
ARRAY_ASSERT(len > 0);
|
||||||
|
return data + len - 1;
|
||||||
|
}
|
||||||
|
T *front() {
|
||||||
|
ARRAY_ASSERT(len > 0);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
T *back() {
|
||||||
|
ARRAY_ASSERT(len > 0);
|
||||||
|
return data + len - 1;
|
||||||
|
}
|
||||||
|
T *begin() { return data; }
|
||||||
|
T *end() { return data + len; }
|
||||||
|
|
||||||
|
struct Reverse_Iter {
|
||||||
|
T *data;
|
||||||
|
Array<T> *arr;
|
||||||
|
|
||||||
|
Reverse_Iter operator++(int) {
|
||||||
|
Reverse_Iter ret = *this;
|
||||||
|
data -= 1;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Reverse_Iter &operator++() {
|
||||||
|
data -= 1;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
T &operator*() { return data[0]; }
|
||||||
|
T *operator->() { return data; }
|
||||||
|
|
||||||
|
friend bool operator==(const Reverse_Iter &a, const Reverse_Iter &b) { return a.data == b.data; };
|
||||||
|
friend bool operator!=(const Reverse_Iter &a, const Reverse_Iter &b) { return a.data != b.data; };
|
||||||
|
|
||||||
|
Reverse_Iter begin() { return Reverse_Iter{arr->end() - 1, arr}; }
|
||||||
|
Reverse_Iter end() { return Reverse_Iter{arr->begin() - 1, arr}; }
|
||||||
|
};
|
||||||
|
|
||||||
|
Reverse_Iter reverse() { return {end() - 1, this}; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void try_growing() {
|
||||||
|
if (len + 1 > cap) {
|
||||||
|
int new_size = cap * 2;
|
||||||
|
if (new_size < 16) new_size = 16;
|
||||||
|
|
||||||
|
reserve(new_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void try_growing_to_fit_item_count(int item_count) {
|
||||||
|
if (len + item_count > cap) {
|
||||||
|
int new_size = max(16, (cap + item_count) * 2);
|
||||||
|
reserve(new_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -126,7 +126,10 @@ typedef double F64;
|
|||||||
#define global static
|
#define global static
|
||||||
#define assert(x) \
|
#define assert(x) \
|
||||||
do { \
|
do { \
|
||||||
if (!(x)) Breakpoint; \
|
if (!(x)) { \
|
||||||
|
printf("Assertion! %s", #x); \
|
||||||
|
Breakpoint; \
|
||||||
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
#define assert_message(x, ...) \
|
#define assert_message(x, ...) \
|
||||||
do { \
|
do { \
|
||||||
@@ -29,11 +29,13 @@ CORE_Static B32 os_decommit_pos(OS_Memory *m, size_t pos);
|
|||||||
global const size_t default_reserve_size = gib(4);
|
global const size_t default_reserve_size = gib(4);
|
||||||
global const size_t default_alignment = 8;
|
global const size_t default_alignment = 8;
|
||||||
global const size_t additional_commit_size = mib(1);
|
global const size_t additional_commit_size = mib(1);
|
||||||
|
global U32 global_arena_ids;
|
||||||
struct Arena : Allocator {
|
struct Arena : Allocator {
|
||||||
OS_Memory memory;
|
OS_Memory memory;
|
||||||
size_t alignment;
|
size_t alignment;
|
||||||
size_t len;
|
size_t len;
|
||||||
String debug_string;
|
String debug_string;
|
||||||
|
U32 id;
|
||||||
};
|
};
|
||||||
|
|
||||||
CORE_Static void
|
CORE_Static void
|
||||||
@@ -59,6 +61,7 @@ arena_clear(Arena *arena) {
|
|||||||
arena_pop_pos(arena, 0);
|
arena_pop_pos(arena, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static float GLOBAL_VIS_VALUE;
|
||||||
CORE_Static void *
|
CORE_Static void *
|
||||||
arena_push_size(Arena *a, size_t size) {
|
arena_push_size(Arena *a, size_t size) {
|
||||||
size_t generous_size = size + a->alignment;
|
size_t generous_size = size + a->alignment;
|
||||||
@@ -90,6 +93,7 @@ push_arena(Allocator *allocator, size_t size, String debug_name) {
|
|||||||
result.memory.data = (U8 *)allocate_size(allocator, size);
|
result.memory.data = (U8 *)allocate_size(allocator, size);
|
||||||
result.memory.reserve = size;
|
result.memory.reserve = size;
|
||||||
result.alignment = default_alignment;
|
result.alignment = default_alignment;
|
||||||
|
result.id = ++global_arena_ids;
|
||||||
result.debug_string = debug_name;
|
result.debug_string = debug_name;
|
||||||
result.allocate = (Allocator::Allocate *)arena_push_size;
|
result.allocate = (Allocator::Allocate *)arena_push_size;
|
||||||
result.deallocate = (Allocator::Deallocate *)deallocate_stub;
|
result.deallocate = (Allocator::Deallocate *)deallocate_stub;
|
||||||
@@ -100,6 +104,7 @@ CORE_Static void
|
|||||||
arena_init(Arena *a, String debug_name) {
|
arena_init(Arena *a, String debug_name) {
|
||||||
a->memory = os_reserve(default_reserve_size);
|
a->memory = os_reserve(default_reserve_size);
|
||||||
a->alignment = default_alignment;
|
a->alignment = default_alignment;
|
||||||
|
a->id = ++global_arena_ids;
|
||||||
a->debug_string = debug_name;
|
a->debug_string = debug_name;
|
||||||
a->allocate = (Allocator::Allocate *)arena_push_size;
|
a->allocate = (Allocator::Allocate *)arena_push_size;
|
||||||
a->deallocate = (Allocator::Deallocate *)deallocate_stub;
|
a->deallocate = (Allocator::Deallocate *)deallocate_stub;
|
||||||
@@ -114,6 +119,7 @@ arena_sub(Arena *base, size_t size, String debug_name) {
|
|||||||
result.alignment = default_alignment;
|
result.alignment = default_alignment;
|
||||||
result.allocate = (Allocator::Allocate *)arena_push_size;
|
result.allocate = (Allocator::Allocate *)arena_push_size;
|
||||||
result.deallocate = (Allocator::Deallocate *)deallocate_stub;
|
result.deallocate = (Allocator::Deallocate *)deallocate_stub;
|
||||||
|
result.id = ++global_arena_ids;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,6 +132,7 @@ arena_from_buffer(void *buffer, size_t size) {
|
|||||||
result.alignment = default_alignment;
|
result.alignment = default_alignment;
|
||||||
result.allocate = (Allocator::Allocate *)arena_push_size;
|
result.allocate = (Allocator::Allocate *)arena_push_size;
|
||||||
result.deallocate = (Allocator::Deallocate *)deallocate_stub;
|
result.deallocate = (Allocator::Deallocate *)deallocate_stub;
|
||||||
|
result.id = ++global_arena_ids;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
#include "core/defer.hpp"
|
#include "../base/defer.hpp"
|
||||||
#define ARRAY_ALLOCATOR_TYPE Allocator
|
#define ARRAY_ALLOCATOR_TYPE Allocator
|
||||||
#define ARRAY_ASSERT assert
|
#define ARRAY_ASSERT assert
|
||||||
#define ARRAY_ALLOCATE(allocator, size) allocate_size(allocator, size)
|
#define ARRAY_ALLOCATE(allocator, size) allocate_size(allocator, size)
|
||||||
#define ARRAY_DEALLOCATE(allocator, p) deallocate(allocator, p)
|
#define ARRAY_DEALLOCATE(allocator, p) deallocate(allocator, p)
|
||||||
#include "core/array.hpp"
|
#include "../base/array.hpp"
|
||||||
#include "core/linked_list.h"
|
#include "../base/linked_list.h"
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Map
|
// Map
|
||||||
@@ -71,8 +71,7 @@ map_insert(Map *map, U64 key, void *val) {
|
|||||||
map->data[i].key = key;
|
map->data[i].key = key;
|
||||||
map->data[i].value = val;
|
map->data[i].value = val;
|
||||||
return;
|
return;
|
||||||
}
|
} else if (map->data[i].key == key) {
|
||||||
else if (map->data[i].key == key) {
|
|
||||||
map->data[i].value = val;
|
map->data[i].value = val;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -95,8 +94,7 @@ map_base_get(Map *map, U64 key) {
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
if (map->data[i].key == key) {
|
if (map->data[i].key == key) {
|
||||||
return map->data + i;
|
return map->data + i;
|
||||||
}
|
} else if (map->data[i].key == 0) {
|
||||||
else if (map->data[i].key == 0) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
21
src/base/defer.hpp
Normal file
21
src/base/defer.hpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#define DEFER_HEADER
|
||||||
|
template <typename T>
|
||||||
|
struct DEFER_ExitScope {
|
||||||
|
T lambda;
|
||||||
|
DEFER_ExitScope(T lambda) : lambda(lambda) {}
|
||||||
|
~DEFER_ExitScope() { lambda(); }
|
||||||
|
DEFER_ExitScope(const DEFER_ExitScope &i) : lambda(i.lambda){};
|
||||||
|
|
||||||
|
private:
|
||||||
|
DEFER_ExitScope &operator=(const DEFER_ExitScope &);
|
||||||
|
};
|
||||||
|
|
||||||
|
class DEFER_ExitScopeHelp {
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
DEFER_ExitScope<T> operator+(T t) { return t; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DEFER_CONCAT_INTERNAL(x, y) x##y
|
||||||
|
#define DEFER_CONCAT(x, y) DEFER_CONCAT_INTERNAL(x, y)
|
||||||
|
#define Defer const auto DEFER_CONCAT(defer__, __LINE__) = DEFER_ExitScopeHelp() + [&]()
|
||||||
131
src/base/linked_list.h
Normal file
131
src/base/linked_list.h
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
#ifndef LINKED_LIST_HEADER
|
||||||
|
#define LINKED_LIST_HEADER
|
||||||
|
|
||||||
|
#define GET_ARRAY_SIZE(x) ((sizeof(x) / sizeof((x)[0])))
|
||||||
|
|
||||||
|
#define SLL_QUEUE_ADD_MOD(f, l, n, next) \
|
||||||
|
do { \
|
||||||
|
(n)->next = 0; \
|
||||||
|
if ((f) == 0) { \
|
||||||
|
(f) = (l) = (n); \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
(l) = (l)->next = (n); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#define SLL_QUEUE_ADD(f, l, n) SLL_QUEUE_ADD_MOD(f, l, n, next)
|
||||||
|
|
||||||
|
#define SLL_QUEUE_POP_FIRST_MOD(f, l, next) \
|
||||||
|
do { \
|
||||||
|
if ((f) == (l)) { \
|
||||||
|
(f) = (l) = 0; \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
(f) = (f)->next; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#define SLL_QUEUE_POP_FIRST(f, l) SLL_QUEUE_POP_FIRST_MOD(f, l, next)
|
||||||
|
|
||||||
|
#define SLL_STACK_ADD_MOD(stack_base, new_stack_base, next) \
|
||||||
|
do { \
|
||||||
|
(new_stack_base)->next = (stack_base); \
|
||||||
|
(stack_base) = (new_stack_base); \
|
||||||
|
} while (0)
|
||||||
|
#define SLL_STACK_ADD(stack_base, new_stack_base) \
|
||||||
|
SLL_STACK_ADD_MOD(stack_base, new_stack_base, next)
|
||||||
|
|
||||||
|
#define SLL_STACK_POP_AND_STORE(stack_base, out_node) \
|
||||||
|
do { \
|
||||||
|
if (stack_base) { \
|
||||||
|
(out_node) = (stack_base); \
|
||||||
|
(stack_base) = (stack_base)->next; \
|
||||||
|
(out_node)->next = 0; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define DLL_QUEUE_ADD_MOD(f, l, node, next, prev) \
|
||||||
|
do { \
|
||||||
|
if ((f) == 0) { \
|
||||||
|
(f) = (l) = (node); \
|
||||||
|
(node)->prev = 0; \
|
||||||
|
(node)->next = 0; \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
(l)->next = (node); \
|
||||||
|
(node)->prev = (l); \
|
||||||
|
(node)->next = 0; \
|
||||||
|
(l) = (node); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#define DLL_QUEUE_ADD(f, l, node) DLL_QUEUE_ADD_MOD(f, l, node, next, prev)
|
||||||
|
#define DLL_QUEUE_REMOVE_MOD(first, last, node, next, prev) \
|
||||||
|
do { \
|
||||||
|
if ((first) == (last)) { \
|
||||||
|
IO_Assertf((node) == (first), "Not you are trying to remove is not in the list"); \
|
||||||
|
(first) = (last) = 0; \
|
||||||
|
} \
|
||||||
|
else if ((last) == (node)) { \
|
||||||
|
(last) = (last)->prev; \
|
||||||
|
(last)->next = 0; \
|
||||||
|
} \
|
||||||
|
else if ((first) == (node)) { \
|
||||||
|
(first) = (first)->next; \
|
||||||
|
(first)->prev = 0; \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
(node)->prev->next = (node)->next; \
|
||||||
|
(node)->next->prev = (node)->prev; \
|
||||||
|
} \
|
||||||
|
if (node) { \
|
||||||
|
(node)->prev = 0; \
|
||||||
|
(node)->next = 0; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#define DLL_QUEUE_REMOVE(first, last, node) DLL_QUEUE_REMOVE_MOD(first, last, node, next, prev)
|
||||||
|
|
||||||
|
#define DLL_STACK_ADD_MOD(first, node, next, prev) \
|
||||||
|
do { \
|
||||||
|
(node)->next = (first); \
|
||||||
|
if ((first)) \
|
||||||
|
(first)->prev = (node); \
|
||||||
|
(first) = (node); \
|
||||||
|
(node)->prev = 0; \
|
||||||
|
} while (0)
|
||||||
|
#define DLL_STACK_ADD(first, node) DLL_STACK_ADD_MOD(first, node, next, prev)
|
||||||
|
#define DLL_STACK_REMOVE_MOD(first, node, next, prev) \
|
||||||
|
do { \
|
||||||
|
if ((node) == (first)) { \
|
||||||
|
(first) = (first)->next; \
|
||||||
|
if ((first)) \
|
||||||
|
(first)->prev = 0; \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
(node)->prev->next = (node)->next; \
|
||||||
|
if ((node)->next) \
|
||||||
|
(node)->next->prev = (node)->prev; \
|
||||||
|
} \
|
||||||
|
if (node) { \
|
||||||
|
(node)->prev = 0; \
|
||||||
|
(node)->next = 0; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#define DLL_STACK_REMOVE(first, node) DLL_STACK_REMOVE_MOD(first, node, next, prev)
|
||||||
|
|
||||||
|
#define DLL_INSERT_NEXT_MOD(base, new, next, prev) \
|
||||||
|
do { \
|
||||||
|
if ((base) == 0) { \
|
||||||
|
(base) = (new); \
|
||||||
|
(new)->next = 0; \
|
||||||
|
(new)->prev = 0; \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
(new)->next = (base)->next; \
|
||||||
|
(base)->next = (new); \
|
||||||
|
(new)->prev = (base); \
|
||||||
|
if ((new)->next) (new)->next->prev = (new); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#define DLL_INSERT_NEXT(base, new) DLL_INSERT_NEXT_MOD(base, new, next, prev)
|
||||||
|
#define DLL_INSERT_PREV(base, new) DLL_INSERT_NEXT_MOD(base, new, next, prev)
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
#include "base.cpp"
|
#include "../base/base.cpp"
|
||||||
#define STB_SPRINTF_IMPLEMENTATION
|
#define STB_SPRINTF_IMPLEMENTATION
|
||||||
#include "stb_sprintf.h"
|
#include "../base/stb_sprintf.h"
|
||||||
#include "base_unicode.cpp"
|
#include "../base/base_unicode.cpp"
|
||||||
#include "base_arena.cpp"
|
#include "../base/base_arena.cpp"
|
||||||
#include "base_data_structures.cpp"
|
#include "../base/base_data_structures.cpp"
|
||||||
#include "base_string.cpp"
|
#include "../base/base_string.cpp"
|
||||||
|
|
||||||
#include "os.h"
|
#include "../os/os.h"
|
||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
#include "os_windows.cpp"
|
#include "../os/os_windows.cpp"
|
||||||
#elif OS_LINUX
|
#elif OS_LINUX
|
||||||
#include "os_linux.cpp"
|
#include "../os/os_linux.cpp"
|
||||||
#else
|
#else
|
||||||
#error Couldnt figure out OS using macros
|
#error Couldnt figure out OS using macros
|
||||||
#endif
|
#endif
|
||||||
@@ -94,7 +94,8 @@ static void compile_file(Allocator *allocator, String filename, U32 compile_flag
|
|||||||
F64 end = os_time();
|
F64 end = os_time();
|
||||||
|
|
||||||
if (is_flag_set(compile_flags, COMPILE_PRINT_STATS)) {
|
if (is_flag_set(compile_flags, COMPILE_PRINT_STATS)) {
|
||||||
printf("total = %f\n", os_time() - pctx->time.start);
|
F64 total = os_time() - pctx->time.start;
|
||||||
|
printf("total = %f\n", total);
|
||||||
printf("core_total = %f\n", pctx->time.total);
|
printf("core_total = %f\n", pctx->time.total);
|
||||||
if (!is_flag_set(compile_flags, DONT_USE_C_COMPILER)) printf("clang = %f\n", end - begin);
|
if (!is_flag_set(compile_flags, DONT_USE_C_COMPILER)) printf("clang = %f\n", end - begin);
|
||||||
printf("parsing = %f\n", pctx->time.parsing);
|
printf("parsing = %f\n", pctx->time.parsing);
|
||||||
Reference in New Issue
Block a user