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

293
examples/_demo1.core Normal file
View File

@@ -0,0 +1,293 @@
/* EXTRA CREDITS (bottom of README.md on github)
* https://bitwise.handmade.network/ - series by Per Vognsen where he actually creates a C like language, very helpful, very hands on!
* https://hero.handmade.network/episode/code/day206/ - this episode of handmade hero started me on the entire compiler journey a long, long time ago.
* https://www.youtube.com/watch?v=TH9VCN6UkyQ&list=PLmV5I2fxaiCKfxMBrNsU1kgKJXD3PkyxO - I have 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.
* 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.
* 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
*/
//
// #1
//
// * Inspired by: Per Vognsen(Ion), Jonathan Blow(Jai)
// * Standard programming constructs
// * No forward declarations needed
// * Named function arguments
// * Enum values are nicely namespaced
#import "Multimedia.core"
main :: (): int
StartMultimedia(title = "Hello people!")
DrawYellowFill()
DrawCoolGradient()
DrawCircleWithGradient()
AnimateTransmorphingCircle()
RaymarchSphere()
DrawYellowFill :: ()
for y := 0, y < Mu.window.y, y+=1
for x := 0, x < Mu.window.x, x+=1
Mu.screen[x + y*Mu.window.x] = 0xFFFFFF00
for UpdateMultimedia()
if Mu.key[Key.Escape].down
Mu.key[Key.Escape].down = false
break
//
// #2
//
// * Modules
// * User names the imports
// * Aliasing
// * Operator overloads
F :: #import "MathF32.core"
V3 :: #import "MathVec3.core"
V2 :: #import "MathVec2.core"
Vec3 :: V3.Vec3
DrawCoolGradient :: ()
for y := 0, y < Mu.window.y, y+=1
for x := 0, x < Mu.window.x, x+=1
// Normalize to range {0, 1}
uv := V2.Vec2{x->F32, y->F32} / Mu.window.sizef
// Move to range {-1, 1}
uv = uv*2->F32 - 1->F32
Assert(uv.x >= -1 && uv.x <= 1)
Assert(uv.y >= -1 && uv.y <= 1)
color := Vec3{uv.x, uv.y, 1}
Mu.screen[x + y*Mu.window.x] = V3.ConvertToARGB(color)
for UpdateMultimedia()
if Mu.key[Key.Escape].down
Mu.key[Key.Escape].down = false
break
//
// #3
//
// * The ';;' operator
// * The ';' operator
// * Compound expressions, named parameters!
DrawCircleWithGradient :: ()
default_color := V3.ConvertToARGB({x = 1, y = 1, z = 1})
ratio := Mu.window.sizef.x / Mu.window.sizef.y
for y := 0, y < Mu.window.y, y+=1
for x := 0, x < Mu.window.x, x+=1
// Normalize to range {0, 1}
uv := V2.Vec2{x->F32, y->F32} / Mu.window.sizef
// Move to range {-1, 1}
uv = uv*2->F32 - 1->F32
uv.x *= ratio
if uv.x*uv.x + uv.y*uv.y < 0.5
color := Vec3{uv.x, uv.y, 1}
Mu.screen[x + y*Mu.window.x] = V3.ConvertToARGB(color)
else;; Mu.screen[x + y*Mu.window.x] = default_color
for UpdateMultimedia()
if Mu.key[Key.Escape].down
Mu.key[Key.Escape].down = false
break
//
// #4
//
AnimateTransmorphingCircle :: ()
for UpdateMultimedia()
if Mu.key[Key.Escape].down
Mu.key[Key.Escape].down = false
break
default_color := V3.ConvertToARGB({x = 1, y = 1, z = 1})
ratio := Mu.window.sizef.x / Mu.window.sizef.y
for y := 0, y < Mu.window.y, y+=1
for x := 0, x < Mu.window.x, x+=1
// Normalize to range {0, 1}
uv := V2.Vec2{x->F32, y->F32} / Mu.window.sizef
// Move to range {-1, 1}
uv = uv*2->F32 - 1->F32
uv.x *= ratio
if F.Cos(Mu.time.total->F32)*uv.x*uv.x + F.Sin(Mu.time.total->F32)*uv.y*uv.y < 0.5
color := Vec3{uv.x, uv.y, 1}
Mu.screen[x + y*Mu.window.x] = V3.ConvertToARGB(color)
else ;; Mu.screen[x + y*Mu.window.x] = default_color
//
// #5
//
Epsilon :: 0.00001
SphereSDF :: (pos: Vec3): F32
result := V3.Length(pos) - 1.0
return result
RaymarchSphere :: ()
for UpdateMultimedia()
if Mu.key[Key.Escape].down
Mu.key[Key.Escape].down = false
break
up := Vec3{0, 1, 0}
forward := Vec3{0, 0, -1}
side := V3.Normalize(V3.Cross(forward, up))
LightPos := Vec3{2,4,2}
LightPos.x = F.Cos(Mu.time.total->F32)*4
LightPos.y = F.Sin(Mu.time.total->F32)*4
ambient_color := Vec3{0.2,0.2,0.2}
diffuse_color := Vec3{0.7,0.2,0.2}
eye := Vec3{0, 0, 2}
light_intensity :: 1.2->F32
Xf := 1 / Mu.window.sizef.x
Yf := 1 / Mu.window.sizef.y
ratio := Mu.window.sizef.x / Mu.window.sizef.y
for y := 0, y < Mu.window.y, y+=1
for x := 0, x < Mu.window.x, x+=1
uv := Vec3{x->F32 * Xf * 2 - 1, y->F32 * Yf * 2 - 1, 1.0}
uv.x *= ratio
dir := V3.Normalize(Vec3{V3.Dot(side, uv), V3.Dot(up, uv), V3.Dot(forward, uv)})
t: F32
end: F32 = 100.0
hit := true
p: Vec3
for i := 0, i < 255, i+=1
p = eye + dir*t
distance := SphereSDF(p)
if distance < Epsilon
break
t += distance
if distance >= end
hit = false
break
if hit
normal := V3.Normalize(Vec3{
SphereSDF({p.x + Epsilon, p.y, p.z}) - SphereSDF({p.x - Epsilon, p.y, p.z}),
SphereSDF({p.x, p.y + Epsilon, p.z}) - SphereSDF({p.x, p.y - Epsilon, p.z}),
SphereSDF({p.x, p.y, p.z + Epsilon}) - SphereSDF({p.x, p.y, p.z - Epsilon}),
})
light_to_point := V3.Normalize(LightPos - p)
ambient :: 0.2->F32
diffuse := V3.Dot(normal, light_to_point)
color := ambient_color*ambient->F32
if diffuse > Epsilon
color = color + diffuse_color*diffuse
color = color * light_intensity
// Gamma correction
color.x = F.SquareRoot(color.x)
color.y = F.SquareRoot(color.y)
color.z = F.SquareRoot(color.z)
Mu.screen[x + y*Mu.window.x] = V3.ConvertToARGB(color)
else;; Mu.screen[x + y*Mu.window.x] = 0

View File

@@ -0,0 +1,98 @@
VariadicArguments :: (string: *char, args: []Any): Any
return args[0]
AnyArguments :: (values: []Any)
for values
Assert(it.type == int)
Assert(*(values[0].data->*int) == 10)
Assert(*(values[1].data->*int) == 20)
/**
* C++ version 0.4 char* style "itoa":
* Written by Lukás Chmela
* Released under GPLv3.
*/
IntegerToString :: (value: int, result: *U8, base: int): *U8
// check that the base if valid
if (base < 2) || (base > 36)
*result = 0 // '
return result
ptr := result
ptr1 := result
tmp_char: U8
tmp_value: int
str := "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"
for value != 0
tmp_value = value
value /= base
*ptr++ = str[35 + (tmp_value - value * base)]
// Apply negative sign
if tmp_value < 0
*ptr++ = '-'
*ptr-- = 0
for ptr1 < ptr
tmp_char = *ptr
*ptr-- = *ptr1
*ptr1++ = tmp_char
return result
StringToDouble :: (s: String): F64
sign: F64 = 1.0
i := 0
if s[i] == '-'
sign = -1
i += 1
elif s[i] == '+'
i += 1
for , i < Len(s), i+= 1
pass // BACKUP
return 0
FormatString :: (buffer: *U8, buffer_len: U64, string: String, args: []Any)
// @todo(krzosa): Add consideration of buffer SIZE! Add some function to handle this OutStr or something
arg_counter := 0
out_buffer_len := 0
for i := 0, i < Len(string), i+=1
if string[i] == '%'
Assert(arg_counter < Len(args), "Passed too many [%] to the string formating function")
arg := args[arg_counter++]
if arg.type == int
value := *(arg.data->*int)
itoa_buff: [64]U8
p := IntegerToString(value, &itoa_buff[0], 10)
for *p != 0
buffer[out_buffer_len++] = *p++
else;; Assert(false)
else
buffer[out_buffer_len++] = string[i]
main :: (): int
a := 10
b := 20
values := []Any{a, b}
for values
Assert(it.type == int)
AnyArguments({a,b})
c := VariadicArguments("Test", args = {a+b,b})
Assert(*(c.data->*int) == 30)
d := VariadicArguments("Test", {b,a})
Assert(*(d.data->*int) == b)
Assert(*(values[0].data->*int) == 10)
Assert(*(values[1].data->*int) == 20)
buf: [128]U8
FormatString(&buf[0], Len(buf), "Test % %", {32, 156})
return 0

View File

@@ -0,0 +1,55 @@
/*
Static arrays are exactly like c arrays, difference is
we can easily then get a slice of that array.
Slices are static arrays + length, they simplify array handling.
This allows us to pass an array into a function easily, then that function
can still access that length information easily.
Passing a pointer to array + length is a common pattern in C. So it would
be nice if handling of that was simplified.
*/
main :: (): int
static_array: [8]int
// We can get size of array using Length builtin
#Assert(Len(static_array) == 8)
// Accessing values is like in C
// Variables are zeroed by default
Assert(static_array[1] == 0)
element2 := static_array[2]
element0: int = static_array[0]
Assert(element0 == 0 && element2 == 0)
// We can loop through arrays
// this implicitly defines 'it' variable
for static_array
*it = 1
// We set all variables to 1 so
Assert(static_array[6] == 1)
// This is how slice is defined, no [] index in between brackets
// slice is array pointer + length
// Other then that it works exactly like regular array
slice: []int = static_array
// We can't do a compile time Assert anymore
Assert(Len(slice) == 8)
Assert(slice[4] == 1)
// After we loop and reassign slice values
// old static_array gets changed
//
// In this example, operator ';;' is used
// it inserts a new line, allows to write this
// example in a single line
for slice;; *it = 2
Assert(static_array[2] == 2)
return 0

View File

@@ -0,0 +1,40 @@
// We can bind module to a name
M :: #import "Multimedia.core"
// You can bind struct to a name
MU :: M.MU
// We can bind a lambda to a name
Start :: M.StartMultimedia
Update :: M.UpdateMultimedia
// Other example of binding a lambda to a name
SomeOtherLambda :: () ;; pass
AliasOf :: SomeOtherLambda
// We can bind a simple type to name, this type is
// exactly the same as int, typechecker doesn't complain
NewInt :: int
// We can force it to complain using a '#strict' directive
// this makes a new type that requires casting, good for ids
// and stuff like that, normal int operations still work!
StrictInt :: #strict int
SomeStruct :: struct ;; a: int
StructAlias :: SomeStruct
main :: (): int
some_struct: SomeStruct = {a = 10}
struct_alias: StructAlias = some_struct
Assert(struct_alias.a == 10)
#Assert(SomeStruct == StructAlias)
#Assert(NewInt == int)
#Assert(StrictInt != int)
Start(x = 1280, y = 720)
Update()
return 0

View File

@@ -0,0 +1,76 @@
#import "Base.core"
#import "Arena.core"
#import "KERNEL32.core"
#import "GDI32.core"
#import "USER32.core"
WinMain :: (hInstance: HINSTANCE, hPrevInstance: HINSTANCE, lpCmdLine: LPSTR, nShowCmd: int): int
screen_size_x: int = 1280
screen_size_y: int = 720
arena: Arena
window_name := StringToString16(&arena, "Have a wonderful day!")
w := WNDCLASSW{
lpfnWndProc = WindowProc,
hInstance = hInstance,
lpszClassName = window_name.str,
}
Assert(RegisterClassW(&w) != 0)
window := CreateWindowExW(
dwExStyle = 0, hWndParent = 0, hMenu = 0, lpParam = 0,
X = CW_USEDEFAULT, Y = CW_USEDEFAULT, nWidth = screen_size_x, nHeight = screen_size_y,
lpClassName = window_name.str,
lpWindowName = window_name.str,
dwStyle = WS_OVERLAPPEDWINDOW,
hInstance = hInstance
)
Assert(window != 0)
ShowWindow(window, nShowCmd)
window_dc := GetDC(window)
header_size: U32 = sizeof(BITMAPINFOHEADER)
Assert(header_size == 40)
bminfo := BITMAPINFO{
BITMAPINFOHEADER{
biSize = header_size,
biWidth = screen_size_x->LONG,
biHeight = screen_size_y->LONG,
biPlanes = 1,
biBitCount = 32,
biCompression = BI_RGB,
biXPelsPerMeter = 1,
biYPelsPerMeter = 1,
}
}
pixels: *U32
bitmap_dib := CreateDIBSection(window_dc, &bminfo, DIB_RGB_COLORS, (&pixels)->**void, 0, 0)
bitmap_hdc := CreateCompatibleDC(window_dc)
for AppIsRunning
msg: MSG
for PeekMessageW(&msg, window, 0, 0, PM_REMOVE) > 0
TranslateMessage(&msg)
DispatchMessageW(&msg)
for y := 0->int, y < screen_size_y, y+=1
for x := 0->int, x < screen_size_x, x+=1
pixels[x + y*screen_size_x] = 0xFFFF0000
SelectObject(bitmap_hdc, bitmap_dib)
BitBlt(window_dc, 0, 0, screen_size_x, screen_size_y, bitmap_hdc, 0, 0, SRCCOPY)
Sleep(100)
if CStringCompare(lpCmdLine, "testing")
return 0
return 0
AppIsRunning := true
WindowProc :: (hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM): LRESULT
if msg == WM_DESTROY
PostQuitMessage(0)
AppIsRunning = false
return 0
else;; return DefWindowProcW(hwnd, msg, wparam, lparam)

View File

@@ -0,0 +1,55 @@
/*
Language enables implementing dynamic typing using operator overloads and
the Any type. Any type is a bundle of typeid and a pointer to value.
Current semantics of the Any dictate that values that get assigned to
Any values, they get implicitly converted to a pointer and a type.
a: Any = 10 // This sentence allocates 10 on the stack
a: Any = a + 10 // This also allocates on stack
a: Any = a // Does not allocate, takes pointer to a
a: Any = array[1] // Does not allocate, takes pointer to value in array
The general picture you can take from this is that if we are referencing
something it will take a pointer to it.
If the operation results in a new value it will allocate the result on stack
and make a pointer out of it. Have to be mindful of the lifetime.
*/
storage: [32]int
len : int
"+" :: (a: Any, b: Any): Any
result: Any = storage[len++]
if a.type == int && b.type == int
*(result.data->*int) = *(a.data->*int) + *(b.data->*int)
return result
"+" :: (a: Any, b: int): Any
result: Any = storage[len++]
if a.type == int
*(result.data->*int) = *(a.data->*int) + b
return result
"==" :: (a: Any, b: int): bool
result := false
if a.type == int
result = *(a.data->*int) == b
return result
"==" :: (a: Any, b: Any): bool
result := false
if a.type == int && b.type == int
result = *(a.data->*int) == *(b.data->*int)
return result
main :: (): int
a: Any = 10
b: Any = 20
c := a + b
Assert(c.type == int && c == 30)
Assert(a+b+a==c+(5+5))
return 0

View File

@@ -0,0 +1,103 @@
main :: (): int
// Language has a bunch of standard builtin types:
// Signed integer types
s64val: S64 = 0
s32val: S32 = 0
s16val: S16 = 0
s8val : S8 = 0
intval: int = 0
// Unsigned integer types = U64, U32, U16, U8,
u64val: U64 = 0
u32val: U32 = 0
u16val: U16 = 0
u8val : U8 = 0
// Floating point types = F64, F32
f64val: F64 = 0
f32val: F32 = 0
// String type = String
string_val: String = "String type"
cstring_val: *char = "CString type"
// This is how we can assign variables
// There is no need for prefixes, compiler figures
// out the format by itself
signed_variable: S32 = 10
unsigned_variable: U32 = 10
// We can also tell the compiler to infer the type
this_is_s64_by_default := 10
this_is_f32_by_default := 10.1251
this_is_string_by_default := "Thing"
// Reassigning values is exactly like in other languages
this_is_s64_by_default = 20
this_is_string_by_default = "Other_Thing"
this_is_f32_by_default = 15.1255
// @todo: Add type_of operator!!!
// Assert(type_of(this_is_string_by_default) == String)
// Assert(type_of(this_is_s64_by_default) == S64)
// There are also constant bindings in the language.
// You can bind all sorts of constants to names this way.
INT_VALUE :: 10
FLOAT_VALUE :: 124.125
// For constants we can mix and match different types
COMBINE_VALUE :: INT_VALUE + FLOAT_VALUE
// When it comes to runtime variables it's a bit different
// To do this we need a cast
combining_types := this_is_s64_by_default->F32 + this_is_f32_by_default
// Compound statements
// Struct is at the bottom of the file!
data1 := Data{
a = 1,
d = 2
}
data2: Data = {
a = 4,
b = 2,
}
size0 := sizeof(Data)
size1 := sizeof(data1)
align0 := alignof(Data)
align1 := alignof(data1)
type0 := typeof(Data)
type1 := typeof(data1)
Assert(s64val == 0 && s32val == 0 && s16val == 0 && s8val == 0 && intval == 0 && u64val == 0 && u32val == 0 && u16val == 0 && u8val == 0 && f64val == 0 && f32val == 0)
Assert(string_val[0] == 'S')
Assert(cstring_val[0] == 'C')
Assert(signed_variable == 10 && unsigned_variable == 10)
Assert(INT_VALUE == 10)
Assert(FLOAT_VALUE == 124.125)
Assert(this_is_f32_by_default == 15.1255)
Assert(combining_types == 15.1255 + 20)
Assert(data1.a == 1)
Assert(data1.b == 0)
Assert(data1.c == 0)
Assert(data1.d == 2)
Assert(data2.a == 4)
Assert(data2.b == 2)
Assert(data2.c == 0)
Assert(data2.d == 0)
Assert(size0 == size1)
Assert(align0 == align1)
Assert(type0 == type1)
Assert(typeof(data2) == Data)
return 0
Data :: struct
a: S64
b: S32
c: S32
d: S32

View File

@@ -0,0 +1,26 @@
Vec3 :: struct;; x: F32; y: F32; z: F32
// We can define operator overloads for arbitrary types
// these are just regular lambdas/functions
"+" :: (a: Vec3, b: Vec3): Vec3
return {a.x+b.x, a.y+b.y, a.z+b.z}
// We can make a one liner out of these using ';;' operator
// which functions as a new line with indent
"-" :: (a: Vec3, b: Vec3): Vec3 ;; return {a.x-b.x, a.y-b.y, a.z-b.z}
"-" :: (a: Vec3): Vec3 ;; return {-a.x, -a.y, -a.z}
main :: (): int
a := Vec3{1,1,1}
b := Vec3{2,3,4}
// The expressions are replaced with the defined lambdas
c := a + b
Assert(c.x == 3 && c.y == 4 && c.z == 5)
d := -c
Assert(d.x == -3 && d.y == -4 && d.z == -5)
e := c - d
Assert(e.x == 6 && e.y == 8 && e.z == 10)
return 0

View File

@@ -0,0 +1,74 @@
/*
Biggest problems with C/C++ are the header files and the fact that
top level declarations are required to be ordered.
In C++ you simply cannot write code like this:
struct Asset{
Asset_Tag asset_tag;
};
enum Asset_Tag{
ASSET_SOUND,
ASSET_IMAGE,
};
Even though it makes more sense to do that, I would rather first learn
about what Asset is rather then what tags it can have. C/C++ force on you
an order of declarations that doesn't match what you have in your head.
The next problem are the header files. Frequently on top of writing code,
in C/C++ there is a need to maintain a header file. Header files contain
all the forward declarations of the functions that you are implementing.
Problem with this is that if you are changing something, potentially
you now need to change that in multiple places. It would be nice if I could
write a module that doesn't yet have any concrete spec but the functions
are available everywhere, immediately. On top of that those functions would
be nicely localized in a single file without thinking about what depends
on what, how to fit it in the stack of includes etc. It would be just nice
to tell the compiler to solve all those issues for us.
*/
// We can have main at the top level, this is something
// that is sometimes pretty tricky in C++
main :: (): int
// Even though Asset is beneath main, this still works
// this feature is even nicer when we think about modules etc.
asset1 := make_sound()
asset2 := make_sound()
asset1.next_asset = &asset2
return 0
// @todo: This does not work
// asset := []Asset{make_sound(), make_sound()}
// asset[0].next_sound = &asset[1]
// Recursion is a pain point for this kinds of algorithms
// As can be seen here it works very nicely
make_sound :: (should_exit_recursion: bool = false): Asset
if should_exit_recursion == true
asset: Asset
asset.tag = Asset_Tag.Sound
return asset
return make_sound(true)
Asset :: struct
tag: Asset_Tag
// Pointers to self work as expected
// this is always a pain point for these kinds of
// algorithms
next_asset: *Asset
// enum #flag assigns default values differently from normal enums.
// It assigns a unique bit of a value to each enumeration.
// Default values go: 1, 2, 4, 8, 16, 32, 64
Asset_Tag :: enum #flag
Sound
Image
Text

Submodule examples/pathfind_visualizer added at caff4aaa2e

View File

@@ -0,0 +1,92 @@
/* @todo
QueueAddSLL(list: $List, node: $Node, $first = first, $last = last, $next = next)
if list.first == 0
list.first = list.last = node
else
list.last = list.last.next = node
*/
Array :: struct($T: Type)
data: *T
len: int
cap: int
Tuple :: struct($A: Type, $B: Type)
a: A
b: B
Triple :: struct($A: Type, $B: Type, $C: Type)
a: A
b: B
c: C
Variant :: union($A: Type, $B: Type, $C: Type)
a: A
b: B
c: C
MakeArray :: (a: *int, count: int): Array(int)
result := Array(int) {
data = a,
len = count,
cap = count
}
return result
MultipleArgs :: (): Tuple(int, F32)
return {32, 32}
PolyLambda :: ($T: Type = *int): T
return 0
PolyType :: (a: $T): T
return a
GetCount :: (a: int): int
return a
C :: #import "LibC.core"
Add :: (arr: *Array($T), val: T)
if arr.cap == 0
arr.cap = 16
arr.data = C.malloc(sizeof(T)->U64 * arr.cap->U64)
arr.data[arr.len++] = val
main :: (argc: int, argv: **char): int
buff: *int
array: Array(S64)
second_array: Array(int)
third_array: Array(int)
fourth: Array(F32)
fifth: Array(F32)
sixth: Array(Array(F32))
seventh: Variant(int, F32, S64)
test_a := int
test := *int
Assert(test_a != test)
// c := MakeArray(buff, GetCount(GetCount(32)))
a, b := MultipleArgs()
Assert(a == 32 && b == 32)
Add(&array, 32)
Add(&second_array, 32)
Add(&third_array, 32)
Add(&fourth, 32)
Add(&fifth, 32)
Add(&sixth, {})
value := PolyLambda(**int)
PolyType_r1 := PolyType(10)
PolyType_r2 := PolyType(int)
PolyType_r3 := PolyType(test)
PolyType_r4 := PolyType(test_a)
PolyType_r5 := PolyType(sixth)
PolyType_r6 := PolyType(seventh)
return 0

15
examples/push_struct.core Normal file
View File

@@ -0,0 +1,15 @@
MA :: #import "Arena.core"
PushStruct :: (a: *MA.Arena, $K: Type, $T: Type): *T
size := sizeof(T)
result := MA.PushSize(a, size->U64)
return result->*T
main :: (argc: int, argv: **char): int
arena: MA.Arena
a: *int = PushStruct(&arena, int, int)
b: *F32 = PushStruct(&arena, int, F32)
padding := sizeof(int)
Assert(arena.len->int == (sizeof(int) + sizeof(F32) + padding))
return 0

210
examples/raymarcher.core Normal file
View File

@@ -0,0 +1,210 @@
F :: #import "MathF32.core"
V3 :: #import "MathVec3.core"; Vec3 :: V3.Vec3
V2 :: #import "MathVec2.core"; Vec2 :: V2.Vec2
Epsilon :: 0.00001
Screen : *U32
X : int
Y : int
TotalTime: F64
LightPos := Vec3{2,4,2}
SphereSDF :: (pos: Vec3): F32
result := V3.Length(pos) - 1.0
return result
Raymarcher_Update :: ()
up := Vec3{0, 1, 0}
forward := Vec3{0, 0, -1}
side := V3.Normalize(V3.Cross(forward, up))
LightPos.x = F.Cos(TotalTime->F32)*4
LightPos.y = F.Sin(TotalTime->F32)*4
ambient_color := Vec3{0.2,0.2,0.2}
diffuse_color := Vec3{0.7,0.2,0.2}
specular_color := Vec3{1,1,1}
eye := Vec3{0, 0, 2}
light_intensity :: 1.2->F32
Xf := 1 / X->F32
Yf := 1 / Y->F32
ratio := X->F32 / Y->F32
for y := 0, y < Y, y+=1
for x := 0, x < X, x+=1
uv := Vec3{x->F32 * Xf * 2 - 1, y->F32 * Yf * 2 - 1, 1.0}
uv.x *= ratio
dir := V3.Normalize(Vec3{V3.Dot(side, uv), V3.Dot(up, uv), V3.Dot(forward, uv)})
t: F32
end: F32 = 100.0
hit := true
p: Vec3
for i := 0, i < 255, i+=1
p = eye + dir*t
distance := SphereSDF(p)
if distance < Epsilon
break
t += distance
if distance >= end
hit = false
break
if hit
normal := V3.Normalize(Vec3{
SphereSDF({p.x + Epsilon, p.y, p.z}) - SphereSDF({p.x - Epsilon, p.y, p.z}),
SphereSDF({p.x, p.y + Epsilon, p.z}) - SphereSDF({p.x, p.y - Epsilon, p.z}),
SphereSDF({p.x, p.y, p.z + Epsilon}) - SphereSDF({p.x, p.y, p.z - Epsilon}),
})
light_to_point := V3.Normalize(LightPos - p)
eye_to_point := V3.Normalize(eye - p)
reflected_light := V3.Normalize(V3.Reflect(V3.Negate(light_to_point), normal))
ambient :: 0.2->F32
diffuse := V3.Dot(normal, light_to_point)
color := ambient_color*ambient->F32
if diffuse > Epsilon
color = color + diffuse_color*diffuse
specular := V3.Dot(reflected_light, eye_to_point)
if specular > Epsilon
specular = specular*specular*specular*specular
color = color + specular_color*specular*0.2->F32
color = color * light_intensity
// Gamma correction
color.x = F.SquareRoot(color.x)
color.y = F.SquareRoot(color.y)
color.z = F.SquareRoot(color.z)
Screen[x + y*X] = V3.ConvertToARGB(color)
else;; Screen[x + y*X] = 0
/////////////////////////////////////
//
// Windows specific code
//
#import "Base.core"
#import "Arena.core"
#import "OS$OS.core"
#import "KERNEL32.core"
#import "GDI32.core"
#import "USER32.core"
#import "WINMM.core"
AppIsRunning := true
WindowProc :: (hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM): LRESULT
if msg == WM_DESTROY
PostQuitMessage(0)
AppIsRunning = false
return 0
else;; return DefWindowProcW(hwnd, msg, wparam, lparam)
WinMain :: (hInstance: HINSTANCE, hPrevInstance: HINSTANCE, lpCmdLine: LPSTR, nShowCmd: int): int
if good_scheduling := false, timeBeginPeriod(1) == TIMERR_NOERROR
good_scheduling = true
dpi_aware_set := SetProcessDPIAware()
Assert(dpi_aware_set != 0)
arena: Arena
window_name := StringToString16(&arena, "Have a wonderful day! 你好世界 ")
w := WNDCLASSW{
lpfnWndProc = WindowProc,
hInstance = hInstance,
lpszClassName = window_name.str,
}
Assert(RegisterClassW(&w) != 0)
screen_size: V2.Vec2I = {1280, 720}
window := CreateWindowExW(
dwExStyle = 0, hWndParent = 0, hMenu = 0, lpParam = 0,
X = CW_USEDEFAULT, Y = CW_USEDEFAULT, nWidth = screen_size.x->int, nHeight = screen_size.y->int,
lpClassName = window_name.str,
lpWindowName = window_name.str,
dwStyle = WS_OVERLAPPEDWINDOW,
hInstance = hInstance
)
Assert(window != 0)
ShowWindow(window, nShowCmd)
window_dc := GetDC(window)
bitmap := CreateBitmap(screen_size)
requested_time_per_frame: F64 = 1.0 / 60.0
frame_start_time := Time()
frame_number: int
for AppIsRunning
msg: MSG
for PeekMessageW(&msg, window, 0, 0, PM_REMOVE) > 0
TranslateMessage(&msg)
DispatchMessageW(&msg)
Screen = bitmap.data; X = bitmap.size.x; Y = bitmap.size.y
Raymarcher_Update()
SelectObject(bitmap.hdc, bitmap.dib)
BitBlt(window_dc, 0, 0, (bitmap.size.x)->int, (bitmap.size.y)->int, bitmap.hdc, 0, 0, SRCCOPY)
frame_time := Time() - frame_start_time
frame_number += 1
TotalTime += frame_time
if frame_time < requested_time_per_frame
if good_scheduling
time_to_sleep := (requested_time_per_frame - frame_time) * 1000
if time_to_sleep > 0
time_to_sleep_dword := time_to_sleep->DWORD
// @check if time_to_sleep_dword truncates down
Sleep(time_to_sleep_dword)
new_frame_time := Time()
for new_frame_time < requested_time_per_frame
new_frame_time = Time() - frame_start_time
frame_time = new_frame_time
if CStringCompare(lpCmdLine, "testing")
return 0
return 0
Windows_Bitmap :: struct
size: V2.Vec2I
data: *U32
hdc: HDC
dib: HBITMAP
CreateBitmap :: (size: V2.Vec2I, bottom_up: bool = true): Windows_Bitmap
result: Windows_Bitmap = {size = size}
if bottom_up == false
result.size.y = -result.size.y
header_size: U32 = sizeof(BITMAPINFOHEADER)
Assert(header_size == 40)
bminfo := BITMAPINFO{
BITMAPINFOHEADER{
biSize = header_size,
biWidth = size.x->LONG,
biHeight = size.y->LONG,
biPlanes = 1,
biBitCount = 32,
biCompression = BI_RGB,
biXPelsPerMeter = 1,
biYPelsPerMeter = 1,
}
}
hdc := GetDC(0)
result.dib = CreateDIBSection(hdc, &bminfo, DIB_RGB_COLORS, (&result.data)->**void, 0, 0)
result.hdc = CreateCompatibleDC(hdc)
return result

View File

@@ -0,0 +1,65 @@
main :: (): int
// Let's say we have a type
some_type := S64
// this function call allows us to get extensive type information
// about this type. For example this information allows us to walk
// the type tree, pretty print all values in that type, along with their sizes.
// Other use cases allow us to do a type safe printf
type_info: *Type_Info = GetTypeInfo(some_type)
// It can be null, requiring the type information would be a bit unwise
// this is a lot of data
if !type_info
return 1
if type_info.type == S64
// We can use sizeof and alignof operators
// to figure out the type alignment and it's size
Assert(type_info.size == sizeof(S64))
Assert(type_info.align == alignof(S64))
else;; Assert(false, "We expected S64 here! What a boomer!")
//
// @todo: This should work
//
// any_thing: Any = 10
//
// Any type allows us to combine a value pointer and
// it's corresponding type_id
//
value_to_be_wrapped := 10
any_value: Any = value_to_be_wrapped
if any_value.type == int
*(any_value.data->*int) = 20
else ;; Assert(false, "No bueno")
Assert(*(any_value.data->*int) == 20)
letter := GetFirstLetterOfType(value_to_be_wrapped)
Assert(letter == 'I')
return 0
GetFirstLetterOfType :: (a: Any): U8
type_info := GetTypeInfo(a.type)
if !type_info
return '-'
result: U8
switch type_info.kind
Type_Info_Kind.S64, Type_Info_Kind.S32, Type_Info_Kind.S16, Type_Info_Kind.S8, Type_Info_Kind.INT
result = 'I'
Type_Info_Kind.U64, Type_Info_Kind.U32, Type_Info_Kind.U16, Type_Info_Kind.U8
result = 'U'
Type_Info_Kind.F64
result = 'F'
Type_Info_Kind.POINTER
result = '*'
default;; result = '-'
return result

View File

@@ -0,0 +1,48 @@
main :: (): int
// Types can be evaluated at compile time for equality
#Assert(int == int)
#Assert(int != char)
#Assert(*char == *char)
// They can also be evaluated at runtime, they basically get
// replaced with type ids, which are just unique integers assigned
// to each type
Assert(int == int)
Assert(int != char)
Assert(*char == *char)
// We can assign types to compile time variable constants
New_Type :: int
// This is a loose association
thing: int = 10
new_type_thing: New_Type = thing
#Assert(New_Type == int)
// to force typechecker to treat$
// both of these types as different we need to add a #strict directive
Strict_Type :: #strict int
// new_strict_type_thing: Strict_Type = thing // This produces a compile time type error
// But this works
strict_thing: Strict_Type = 10
#Assert(Strict_Type != int)
// If we want to use those types together we need to cast
Assert(new_type_thing + strict_thing->int != 0)
// We can also assign types to runtime variables, there is a special type for that
some_type: Type = int
some_type_implicit := char
// These can be checked for equality using ifs and switches
if some_type == char
pass
elif some_type_implicit == char
pass
return 0

40
examples/unions.core Normal file
View File

@@ -0,0 +1,40 @@
U :: union
a: F64
b: F32
C :: struct
a: int
b: int
main :: (argc: int, argv: **char): int
memes: U
memes.b = 10
Assert(memes.b == 10)
Assert(memes.a != 0)
compound: U = {b = 10.0}
Assert(compound.b == 10)
t := U
ti := GetTypeInfo(t)
Assert(ti.size == sizeof(U))
for ti.struct_members
Assert(it.offset == 0)
ti_it := GetTypeInfo(it.type)
Assert(ti_it.size != 0)
/* @reproduction @todo
```
examples/unions.core - Error! Couldn't infer type of compound expression
c = {10}
```
c: C
c = {10}
*/
return 0

View File

@@ -0,0 +1,12 @@
#import "Multimedia.core"
main :: (): int
StartMultimedia(title = "Hello people!")
for UpdateMultimedia()
if Mu.key[Key.Escape].down ;; Mu.quit = true
for y := 0, y < Mu.window.y, y+=1
for x := 0, x < Mu.window.x, x+=1
Mu.screen[x + y*Mu.window.x] = 0xFFFFFF00
return 0