Refresh the repo

This commit is contained in:
Krzosa Karol
2026-03-20 08:35:18 +01:00
parent 771e9b59b3
commit 6e18bb6641
77 changed files with 27788 additions and 27766 deletions

220
README.md
View File

@@ -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
@@ -28,115 +49,108 @@ main :: (): int
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

View File

@@ -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 ..\..

View File

@@ -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

View File

@@ -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)

Submodule examples/pathfind_visualizer added at caff4aaa2e

View File

@@ -1,2 +0,0 @@
cloc *.hpp *.cpp *.py *.h
git rev-list --all --count

306
src/base/array.hpp Normal file
View 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);
}
}
};

View File

@@ -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 { \

View File

@@ -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;
} }

View File

@@ -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
View 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
View 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

View File

@@ -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

View File

@@ -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);

View File