mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-04 06:38:47 +00:00
Replace import_load with using import .
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
set exe_name=odin.exe
|
set exe_name=odin.exe
|
||||||
|
|
||||||
:: Debug = 0, Release = 1
|
:: Debug = 0, Release = 1
|
||||||
set release_mode=1
|
set release_mode=0
|
||||||
set compiler_flags= -nologo -Oi -TP -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR-
|
set compiler_flags= -nologo -Oi -TP -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR-
|
||||||
|
|
||||||
if %release_mode% EQU 0 ( rem Debug
|
if %release_mode% EQU 0 ( rem Debug
|
||||||
@@ -43,7 +43,7 @@ del *.ilk > NUL 2> NUL
|
|||||||
|
|
||||||
cl %compiler_settings% "src\main.cpp" ^
|
cl %compiler_settings% "src\main.cpp" ^
|
||||||
/link %linker_settings% -OUT:%exe_name% ^
|
/link %linker_settings% -OUT:%exe_name% ^
|
||||||
&& odin run code/demo.odin -opt=0
|
&& odin run examples/punity.odin -opt=0
|
||||||
rem && odin docs core/fmt.odin
|
rem && odin docs core/fmt.odin
|
||||||
|
|
||||||
del *.obj > NUL 2> NUL
|
del *.obj > NUL 2> NUL
|
||||||
|
|||||||
-601
@@ -1,601 +0,0 @@
|
|||||||
|
|
||||||
import (
|
|
||||||
"fmt.odin";
|
|
||||||
"strconv.odin";
|
|
||||||
"mem.odin";
|
|
||||||
"thread.odin" when ODIN_OS == "windows";
|
|
||||||
win32 "sys/windows.odin" when ODIN_OS == "windows";
|
|
||||||
|
|
||||||
/*
|
|
||||||
"atomics.odin";
|
|
||||||
"bits.odin";
|
|
||||||
"hash.odin";
|
|
||||||
"math.odin";
|
|
||||||
"opengl.odin";
|
|
||||||
"os.odin";
|
|
||||||
"raw.odin";
|
|
||||||
"sort.odin";
|
|
||||||
"strings.odin";
|
|
||||||
"sync.odin";
|
|
||||||
"types.odin";
|
|
||||||
"utf8.odin";
|
|
||||||
"utf16.odin";
|
|
||||||
*/
|
|
||||||
)
|
|
||||||
|
|
||||||
general_stuff :: proc() {
|
|
||||||
{ // `do` for inline statmes rather than block
|
|
||||||
foo :: proc() do fmt.println("Foo!");
|
|
||||||
if false do foo();
|
|
||||||
for false do foo();
|
|
||||||
when false do foo();
|
|
||||||
|
|
||||||
if false do foo();
|
|
||||||
else do foo();
|
|
||||||
}
|
|
||||||
|
|
||||||
{ // Removal of `++` and `--` (again)
|
|
||||||
x: int;
|
|
||||||
x += 1;
|
|
||||||
x -= 1;
|
|
||||||
}
|
|
||||||
{ // Casting syntaxes
|
|
||||||
i := i32(137);
|
|
||||||
ptr := &i;
|
|
||||||
|
|
||||||
fp1 := (^f32)(ptr);
|
|
||||||
// ^f32(ptr) == ^(f32(ptr))
|
|
||||||
fp2 := cast(^f32)ptr;
|
|
||||||
|
|
||||||
f1 := (^f32)(ptr)^;
|
|
||||||
f2 := (cast(^f32)ptr)^;
|
|
||||||
|
|
||||||
// Questions: Should there be two ways to do it?
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Remove *_val_of built-in procedures
|
|
||||||
* size_of, align_of, offset_of
|
|
||||||
* type_of, type_info_of
|
|
||||||
*/
|
|
||||||
|
|
||||||
{ // `expand_to_tuple` built-in procedure
|
|
||||||
Foo :: struct {
|
|
||||||
x: int;
|
|
||||||
b: bool;
|
|
||||||
}
|
|
||||||
f := Foo{137, true};
|
|
||||||
x, b := expand_to_tuple(f);
|
|
||||||
fmt.println(f);
|
|
||||||
fmt.println(x, b);
|
|
||||||
fmt.println(expand_to_tuple(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// .. half-closed range
|
|
||||||
// ... open range
|
|
||||||
|
|
||||||
for in 0..2 {} // 0, 1
|
|
||||||
for in 0...2 {} // 0, 1, 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nested_struct_declarations :: proc() {
|
|
||||||
{
|
|
||||||
FooInteger :: int;
|
|
||||||
Foo :: struct {
|
|
||||||
i: FooInteger;
|
|
||||||
};
|
|
||||||
f := Foo{FooInteger(137)};
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Foo :: struct {
|
|
||||||
Integer :: int;
|
|
||||||
|
|
||||||
i: Integer;
|
|
||||||
}
|
|
||||||
f := Foo{Foo.Integer(137)};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default_struct_values :: proc() {
|
|
||||||
{
|
|
||||||
Vector3 :: struct {
|
|
||||||
x: f32;
|
|
||||||
y: f32;
|
|
||||||
z: f32;
|
|
||||||
}
|
|
||||||
v: Vector3;
|
|
||||||
fmt.println(v);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// Default values must be constants
|
|
||||||
Vector3 :: struct {
|
|
||||||
x: f32 = 1;
|
|
||||||
y: f32 = 4;
|
|
||||||
z: f32 = 9;
|
|
||||||
}
|
|
||||||
v: Vector3;
|
|
||||||
fmt.println(v);
|
|
||||||
|
|
||||||
v = Vector3{};
|
|
||||||
fmt.println(v);
|
|
||||||
|
|
||||||
// Uses the same semantics as a default values in a procedure
|
|
||||||
v = Vector3{137};
|
|
||||||
fmt.println(v);
|
|
||||||
|
|
||||||
v = Vector3{z = 137};
|
|
||||||
fmt.println(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Vector3 :: struct {
|
|
||||||
x := 1.0;
|
|
||||||
y := 4.0;
|
|
||||||
z := 9.0;
|
|
||||||
}
|
|
||||||
stack_default: Vector3;
|
|
||||||
stack_literal := Vector3{};
|
|
||||||
heap_one := new(Vector3); defer free(heap_one);
|
|
||||||
heap_two := new_clone(Vector3{}); defer free(heap_two);
|
|
||||||
|
|
||||||
fmt.println("stack_default - ", stack_default);
|
|
||||||
fmt.println("stack_literal - ", stack_literal);
|
|
||||||
fmt.println("heap_one - ", heap_one^);
|
|
||||||
fmt.println("heap_two - ", heap_two^);
|
|
||||||
|
|
||||||
|
|
||||||
N :: 4;
|
|
||||||
stack_array: [N]Vector3;
|
|
||||||
heap_array := new([N]Vector3); defer free(heap_array);
|
|
||||||
heap_slice := make([]Vector3, N); defer free(heap_slice);
|
|
||||||
fmt.println("stack_array[1] - ", stack_array[1]);
|
|
||||||
fmt.println("heap_array[1] - ", heap_array[1]);
|
|
||||||
fmt.println("heap_slice[1] - ", heap_slice[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
union_type :: proc() {
|
|
||||||
{
|
|
||||||
val: union{int, bool};
|
|
||||||
val = 137;
|
|
||||||
if i, ok := val.(int); ok {
|
|
||||||
fmt.println(i);
|
|
||||||
}
|
|
||||||
val = true;
|
|
||||||
fmt.println(val);
|
|
||||||
|
|
||||||
val = nil;
|
|
||||||
|
|
||||||
match v in val {
|
|
||||||
case int: fmt.println("int", v);
|
|
||||||
case bool: fmt.println("bool", v);
|
|
||||||
case: fmt.println("nil");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// There is a duality between `any` and `union`
|
|
||||||
// An `any` has a pointer to the data and allows for any type (open)
|
|
||||||
// A `union` has as binary blob to store the data and allows only certain types (closed)
|
|
||||||
// The following code is with `any` but has the same syntax
|
|
||||||
val: any;
|
|
||||||
val = 137;
|
|
||||||
if i, ok := val.(int); ok {
|
|
||||||
fmt.println(i);
|
|
||||||
}
|
|
||||||
val = true;
|
|
||||||
fmt.println(val);
|
|
||||||
|
|
||||||
val = nil;
|
|
||||||
|
|
||||||
match v in val {
|
|
||||||
case int: fmt.println("int", v);
|
|
||||||
case bool: fmt.println("bool", v);
|
|
||||||
case: fmt.println("nil");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3 :: struct {
|
|
||||||
x, y, z: f32;
|
|
||||||
};
|
|
||||||
Quaternion :: struct {
|
|
||||||
x, y, z: f32;
|
|
||||||
w: f32 = 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
// More realistic examples
|
|
||||||
{
|
|
||||||
// NOTE(bill): For the above basic examples, you may not have any
|
|
||||||
// particular use for it. However, my main use for them is not for these
|
|
||||||
// simple cases. My main use is for hierarchical types. Many prefer
|
|
||||||
// subtyping, embedding the base data into the derived types. Below is
|
|
||||||
// an example of this for a basic game Entity.
|
|
||||||
|
|
||||||
Entity :: struct {
|
|
||||||
id: u64;
|
|
||||||
name: string;
|
|
||||||
position: Vector3;
|
|
||||||
orientation: Quaternion;
|
|
||||||
|
|
||||||
derived: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
Frog :: struct {
|
|
||||||
using entity: Entity;
|
|
||||||
jump_height: f32;
|
|
||||||
}
|
|
||||||
|
|
||||||
Monster :: struct {
|
|
||||||
using entity: Entity;
|
|
||||||
is_robot: bool;
|
|
||||||
is_zombie: bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
// See `parametric_polymorphism` procedure for details
|
|
||||||
new_entity :: proc(T: type) -> ^Entity {
|
|
||||||
t := new(T);
|
|
||||||
t.derived = t^;
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
entity := new_entity(Monster);
|
|
||||||
|
|
||||||
match e in entity.derived {
|
|
||||||
case Frog:
|
|
||||||
fmt.println("Ribbit");
|
|
||||||
case Monster:
|
|
||||||
if e.is_robot do fmt.println("Robotic");
|
|
||||||
if e.is_zombie do fmt.println("Grrrr!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// NOTE(bill): A union can be used to achieve something similar. Instead
|
|
||||||
// of embedding the base data into the derived types, the derived data
|
|
||||||
// in embedded into the base type. Below is the same example of the
|
|
||||||
// basic game Entity but using an union.
|
|
||||||
|
|
||||||
Entity :: struct {
|
|
||||||
id: u64;
|
|
||||||
name: string;
|
|
||||||
position: Vector3;
|
|
||||||
orientation: Quaternion;
|
|
||||||
|
|
||||||
derived: union {Frog, Monster};
|
|
||||||
}
|
|
||||||
|
|
||||||
Frog :: struct {
|
|
||||||
using entity: ^Entity;
|
|
||||||
jump_height: f32;
|
|
||||||
}
|
|
||||||
|
|
||||||
Monster :: struct {
|
|
||||||
using entity: ^Entity;
|
|
||||||
is_robot: bool;
|
|
||||||
is_zombie: bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
// See `parametric_polymorphism` procedure for details
|
|
||||||
new_entity :: proc(T: type) -> ^Entity {
|
|
||||||
t := new(Entity);
|
|
||||||
t.derived = T{entity = t};
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
entity := new_entity(Monster);
|
|
||||||
|
|
||||||
match e in entity.derived {
|
|
||||||
case Frog:
|
|
||||||
fmt.println("Ribbit");
|
|
||||||
case Monster:
|
|
||||||
if e.is_robot do fmt.println("Robotic");
|
|
||||||
if e.is_zombie do fmt.println("Grrrr!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE(bill): As you can see, the usage code has not changed, only its
|
|
||||||
// memory layout. Both approaches have their own advantages but they can
|
|
||||||
// be used together to achieve different results. The subtyping approach
|
|
||||||
// can allow for a greater control of the memory layout and memory
|
|
||||||
// allocation, e.g. storing the derivatives together. However, this is
|
|
||||||
// also its disadvantage. You must either preallocate arrays for each
|
|
||||||
// derivative separation (which can be easily missed) or preallocate a
|
|
||||||
// bunch of "raw" memory; determining the maximum size of the derived
|
|
||||||
// types would require the aid of metaprogramming. Unions solve this
|
|
||||||
// particular problem as the data is stored with the base data.
|
|
||||||
// Therefore, it is possible to preallocate, e.g. [100]Entity.
|
|
||||||
|
|
||||||
// It should be noted that the union approach can have the same memory
|
|
||||||
// layout as the any and with the same type restrictions by using a
|
|
||||||
// pointer type for the derivatives.
|
|
||||||
|
|
||||||
/*
|
|
||||||
Entity :: struct {
|
|
||||||
...
|
|
||||||
derived: union{^Frog, ^Monster};
|
|
||||||
}
|
|
||||||
|
|
||||||
Frog :: struct {
|
|
||||||
using entity: Entity;
|
|
||||||
...
|
|
||||||
}
|
|
||||||
Monster :: struct {
|
|
||||||
using entity: Entity;
|
|
||||||
...
|
|
||||||
|
|
||||||
}
|
|
||||||
new_entity :: proc(T: type) -> ^Entity {
|
|
||||||
t := new(T);
|
|
||||||
t.derived = t;
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parametric_polymorphism :: proc() {
|
|
||||||
print_value :: proc(value: $T) {
|
|
||||||
fmt.printf("print_value: %T %v\n", value, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
v1: int = 1;
|
|
||||||
v2: f32 = 2.1;
|
|
||||||
v3: f64 = 3.14;
|
|
||||||
v4: string = "message";
|
|
||||||
|
|
||||||
print_value(v1);
|
|
||||||
print_value(v2);
|
|
||||||
print_value(v3);
|
|
||||||
print_value(v4);
|
|
||||||
|
|
||||||
fmt.println();
|
|
||||||
|
|
||||||
add :: proc(p, q: $T) -> T {
|
|
||||||
x: T = p + q;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
a := add(3, 4);
|
|
||||||
fmt.printf("a: %T = %v\n", a, a);
|
|
||||||
|
|
||||||
b := add(3.2, 4.3);
|
|
||||||
fmt.printf("b: %T = %v\n", b, b);
|
|
||||||
|
|
||||||
// This is how `new` is implemented
|
|
||||||
alloc_type :: proc(T: type) -> ^T {
|
|
||||||
t := cast(^T)alloc(size_of(T), align_of(T));
|
|
||||||
t^ = T{}; // Use default initialization value
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
copy_slice :: proc(dst, src: []$T) -> int {
|
|
||||||
n := min(len(dst), len(src));
|
|
||||||
if n > 0 {
|
|
||||||
mem.copy(&dst[0], &src[0], n*size_of(T));
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
double_params :: proc(a: $A, b: $B) -> A {
|
|
||||||
return a + A(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.println(double_params(12, 1.345));
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{ // Polymorphic Types and Type Specialization
|
|
||||||
Table :: struct(Key, Value: type) {
|
|
||||||
Slot :: struct {
|
|
||||||
occupied: bool;
|
|
||||||
hash: u32;
|
|
||||||
key: Key;
|
|
||||||
value: Value;
|
|
||||||
}
|
|
||||||
SIZE_MIN :: 32;
|
|
||||||
|
|
||||||
count: int;
|
|
||||||
allocator: Allocator;
|
|
||||||
slots: []Slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only allow types that are specializations of a (polymorphic) slice
|
|
||||||
make_slice :: proc(T: type/[]$E, len: int) -> T {
|
|
||||||
return make(T, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Only allow types that are specializations of `Table`
|
|
||||||
allocate :: proc(table: ^$T/Table, capacity: int) {
|
|
||||||
c := context;
|
|
||||||
if table.allocator.procedure != nil do c.allocator = table.allocator;
|
|
||||||
|
|
||||||
push_context c {
|
|
||||||
table.slots = make_slice([]T.Slot, max(capacity, T.SIZE_MIN));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
expand :: proc(table: ^$T/Table) {
|
|
||||||
c := context;
|
|
||||||
if table.allocator.procedure != nil do c.allocator = table.allocator;
|
|
||||||
|
|
||||||
push_context c {
|
|
||||||
old_slots := table.slots;
|
|
||||||
|
|
||||||
cap := max(2*cap(table.slots), T.SIZE_MIN);
|
|
||||||
allocate(table, cap);
|
|
||||||
|
|
||||||
for s in old_slots do if s.occupied {
|
|
||||||
put(table, s.key, s.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(old_slots);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Polymorphic determination of a polymorphic struct
|
|
||||||
// put :: proc(table: ^$T/Table, key: T.Key, value: T.Value) {
|
|
||||||
put :: proc(table: ^Table($Key, $Value), key: Key, value: Value) {
|
|
||||||
hash := get_hash(key); // Ad-hoc method which would fail in a different scope
|
|
||||||
index := find_index(table, key, hash);
|
|
||||||
if index < 0 {
|
|
||||||
if f64(table.count) >= 0.75*f64(cap(table.slots)) {
|
|
||||||
expand(table);
|
|
||||||
}
|
|
||||||
assert(table.count <= cap(table.slots));
|
|
||||||
|
|
||||||
hash := get_hash(key);
|
|
||||||
index = int(hash % u32(cap(table.slots)));
|
|
||||||
|
|
||||||
for table.slots[index].occupied {
|
|
||||||
if index += 1; index >= cap(table.slots) {
|
|
||||||
index = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table.count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
slot := &table.slots[index];
|
|
||||||
slot.occupied = true;
|
|
||||||
slot.hash = hash;
|
|
||||||
slot.key = key;
|
|
||||||
slot.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// find :: proc(table: ^$T/Table, key: T.Key) -> (T.Value, bool) {
|
|
||||||
find :: proc(table: ^Table($Key, $Value), key: Key) -> (Value, bool) {
|
|
||||||
hash := get_hash(key);
|
|
||||||
index := find_index(table, key, hash);
|
|
||||||
if index < 0 {
|
|
||||||
return Value{}, false;
|
|
||||||
}
|
|
||||||
return table.slots[index].value, true;
|
|
||||||
}
|
|
||||||
|
|
||||||
find_index :: proc(table: ^Table($Key, $Value), key: Key, hash: u32) -> int {
|
|
||||||
if cap(table.slots) <= 0 do return -1;
|
|
||||||
|
|
||||||
index := int(hash % u32(cap(table.slots)));
|
|
||||||
for table.slots[index].occupied {
|
|
||||||
if table.slots[index].hash == hash {
|
|
||||||
if table.slots[index].key == key {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if index += 1; index >= cap(table.slots) {
|
|
||||||
index = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
get_hash :: proc(s: string) -> u32 { // fnv32a
|
|
||||||
h: u32 = 0x811c9dc5;
|
|
||||||
for i in 0..len(s) {
|
|
||||||
h = (h ~ u32(s[i])) * 0x01000193;
|
|
||||||
}
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
table: Table(string, int);
|
|
||||||
|
|
||||||
for i in 0..36 do put(&table, "Hellope", i);
|
|
||||||
for i in 0..42 do put(&table, "World!", i);
|
|
||||||
|
|
||||||
found, _ := find(&table, "Hellope");
|
|
||||||
fmt.printf("`found` is %v\n", found);
|
|
||||||
|
|
||||||
found, _ = find(&table, "World!");
|
|
||||||
fmt.printf("`found` is %v\n", found);
|
|
||||||
|
|
||||||
// I would not personally design a hash table like this in production
|
|
||||||
// but this is a nice basic example
|
|
||||||
// A better approach would either use a `u64` or equivalent for the key
|
|
||||||
// and let the user specify the hashing function or make the user store
|
|
||||||
// the hashing procedure with the table
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
prefix_table := [...]string{
|
|
||||||
"White",
|
|
||||||
"Red",
|
|
||||||
"Green",
|
|
||||||
"Blue",
|
|
||||||
"Octarine",
|
|
||||||
"Black",
|
|
||||||
};
|
|
||||||
|
|
||||||
threading_example :: proc() {
|
|
||||||
when ODIN_OS == "windows" {
|
|
||||||
unordered_remove :: proc(array: ^[]$T, index: int, loc := #caller_location) {
|
|
||||||
__bounds_check_error_loc(loc, index, len(array));
|
|
||||||
array[index] = array[len(array)-1];
|
|
||||||
pop(array);
|
|
||||||
}
|
|
||||||
ordered_remove :: proc(array: ^[]$T, index: int, loc := #caller_location) {
|
|
||||||
__bounds_check_error_loc(loc, index, len(array));
|
|
||||||
copy(array[index..], array[index+1..]);
|
|
||||||
pop(array);
|
|
||||||
}
|
|
||||||
|
|
||||||
worker_proc :: proc(t: ^thread.Thread) -> int {
|
|
||||||
for iteration in 1...5 {
|
|
||||||
fmt.printf("Thread %d is on iteration %d\n", t.user_index, iteration);
|
|
||||||
fmt.printf("`%s`: iteration %d\n", prefix_table[t.user_index], iteration);
|
|
||||||
// win32.sleep(1);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
threads := make([]^thread.Thread, 0, len(prefix_table));
|
|
||||||
defer free(threads);
|
|
||||||
|
|
||||||
for i in 0..len(prefix_table) {
|
|
||||||
if t := thread.create(worker_proc); t != nil {
|
|
||||||
t.init_context = context;
|
|
||||||
t.use_init_context = true;
|
|
||||||
t.user_index = len(threads);
|
|
||||||
append(&threads, t);
|
|
||||||
thread.start(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for len(threads) > 0 {
|
|
||||||
for i := 0; i < len(threads); {
|
|
||||||
if t := threads[i]; thread.is_done(t) {
|
|
||||||
fmt.printf("Thread %d is done\n", t.user_index);
|
|
||||||
thread.destroy(t);
|
|
||||||
|
|
||||||
ordered_remove(&threads, i);
|
|
||||||
} else {
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
main :: proc() {
|
|
||||||
when true {
|
|
||||||
fmt.println("\n# general_stuff"); general_stuff();
|
|
||||||
fmt.println("\n# nested_struct_declarations"); nested_struct_declarations();
|
|
||||||
fmt.println("\n# default_struct_values"); default_struct_values();
|
|
||||||
fmt.println("\n# union_type"); union_type();
|
|
||||||
fmt.println("\n# parametric_polymorphism"); parametric_polymorphism();
|
|
||||||
fmt.println("\n# threading_example"); threading_example();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,430 +0,0 @@
|
|||||||
import (
|
|
||||||
"fmt.odin";
|
|
||||||
"atomics.odin";
|
|
||||||
"bits.odin";
|
|
||||||
"decimal.odin";
|
|
||||||
"hash.odin";
|
|
||||||
"math.odin";
|
|
||||||
"mem.odin";
|
|
||||||
"opengl.odin";
|
|
||||||
"os.odin";
|
|
||||||
"raw.odin";
|
|
||||||
"strconv.odin";
|
|
||||||
"strings.odin";
|
|
||||||
"sync.odin";
|
|
||||||
"sort.odin";
|
|
||||||
"types.odin";
|
|
||||||
"utf8.odin";
|
|
||||||
"utf16.odin";
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
general_stuff :: proc() {
|
|
||||||
// Complex numbers
|
|
||||||
a := 3 + 4i;
|
|
||||||
b: complex64 = 3 + 4i;
|
|
||||||
c: complex128 = 3 + 4i;
|
|
||||||
d := complex(2, 3);
|
|
||||||
|
|
||||||
e := a / conj(a);
|
|
||||||
fmt.println("(3+4i)/(3-4i) =", e);
|
|
||||||
fmt.println(real(e), "+", imag(e), "i");
|
|
||||||
|
|
||||||
|
|
||||||
// C-style variadic procedures
|
|
||||||
foreign __llvm_core {
|
|
||||||
// The variadic part allows for extra type checking too which C does not provide
|
|
||||||
c_printf :: proc(fmt: ^u8, #c_vararg args: ...any) -> i32 #link_name "printf" ---;
|
|
||||||
}
|
|
||||||
str := "%d\n\x00";
|
|
||||||
// c_printf(&str[0], i32(789456123));
|
|
||||||
|
|
||||||
|
|
||||||
Foo :: struct {
|
|
||||||
x: int;
|
|
||||||
y: f32;
|
|
||||||
z: string;
|
|
||||||
}
|
|
||||||
foo := Foo{123, 0.513, "A string"};
|
|
||||||
x, y, z := expand_to_tuple(foo);
|
|
||||||
fmt.println(x, y, z);
|
|
||||||
compile_assert(type_of(x) == int);
|
|
||||||
compile_assert(type_of(y) == f32);
|
|
||||||
compile_assert(type_of(z) == string);
|
|
||||||
|
|
||||||
|
|
||||||
// By default, all variables are zeroed
|
|
||||||
// This can be overridden with the "uninitialized value"
|
|
||||||
// This is similar to `nil` but applied to everything
|
|
||||||
undef_int: int = ---;
|
|
||||||
|
|
||||||
|
|
||||||
// Context system is now implemented using Implicit Parameter Passing (IPP)
|
|
||||||
// The previous implementation was Thread Local Storage (TLS)
|
|
||||||
// IPP has the advantage that it works on systems without TLS and that you can
|
|
||||||
// link the context to the stack frame and thus look at previous contexts
|
|
||||||
//
|
|
||||||
// It does mean that a pointer is implicitly passed procedures with the default
|
|
||||||
// Odin calling convention (#cc_odin)
|
|
||||||
// This can be overridden with something like #cc_contextless or #cc_c if performance
|
|
||||||
// is worried about
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
foreign_blocks :: proc() {
|
|
||||||
// See sys/windows.odin
|
|
||||||
}
|
|
||||||
|
|
||||||
default_arguments :: proc() {
|
|
||||||
hello :: proc(a: int = 9, b: int = 9) do fmt.printf("a is %d; b is %d\n", a, b);
|
|
||||||
fmt.println("\nTesting default arguments:");
|
|
||||||
hello(1, 2);
|
|
||||||
hello(1);
|
|
||||||
hello();
|
|
||||||
}
|
|
||||||
|
|
||||||
named_arguments :: proc() {
|
|
||||||
Colour :: enum {
|
|
||||||
Red,
|
|
||||||
Orange,
|
|
||||||
Yellow,
|
|
||||||
Green,
|
|
||||||
Blue,
|
|
||||||
Octarine,
|
|
||||||
};
|
|
||||||
using Colour;
|
|
||||||
|
|
||||||
make_character :: proc(name, catch_phrase: string, favourite_colour, least_favourite_colour: Colour) {
|
|
||||||
fmt.println();
|
|
||||||
fmt.printf("My name is %v and I like %v. %v\n", name, favourite_colour, catch_phrase);
|
|
||||||
}
|
|
||||||
|
|
||||||
make_character("Frank", "¡Ay, caramba!", Blue, Green);
|
|
||||||
|
|
||||||
|
|
||||||
// As the procedures have more and more parameters, it is very easy
|
|
||||||
// to get many of the arguments in the wrong order especialy if the
|
|
||||||
// types are the same
|
|
||||||
make_character("¡Ay, caramba!", "Frank", Green, Blue);
|
|
||||||
|
|
||||||
// Named arguments help to disambiguate this problem
|
|
||||||
make_character(catch_phrase = "¡Ay, caramba!", name = "Frank",
|
|
||||||
least_favourite_colour = Green, favourite_colour = Blue);
|
|
||||||
|
|
||||||
|
|
||||||
// The named arguments can be specifed in any order.
|
|
||||||
make_character(favourite_colour = Octarine, catch_phrase = "U wot m8!",
|
|
||||||
least_favourite_colour = Green, name = "Dennis");
|
|
||||||
|
|
||||||
|
|
||||||
// NOTE: You cannot mix named arguments with normal values
|
|
||||||
/*
|
|
||||||
make_character("Dennis",
|
|
||||||
favourite_colour = Octarine, catch_phrase = "U wot m8!",
|
|
||||||
least_favourite_colour = Green);
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// Named arguments can also aid with default arguments
|
|
||||||
numerous_things :: proc(s: string, a := 1, b := 2, c := 3.14,
|
|
||||||
d := "The Best String!", e := false, f := 10.3/3.1, g := false) {
|
|
||||||
g_str := g ? "true" : "false";
|
|
||||||
fmt.printf("How many?! %s: %v\n", s, g_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
numerous_things("First");
|
|
||||||
numerous_things(s = "Second", g = true);
|
|
||||||
|
|
||||||
|
|
||||||
// Default values can be placed anywhere, not just at the end like in other languages
|
|
||||||
weird :: proc(pre: string, mid: int = 0, post: string) {
|
|
||||||
fmt.println(pre, mid, post);
|
|
||||||
}
|
|
||||||
|
|
||||||
weird("How many things", 42, "huh?");
|
|
||||||
weird(pre = "Prefix", post = "Pat");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
default_return_values :: proc() {
|
|
||||||
foo :: proc(x: int) -> (first: string = "Hellope", second := "world!") {
|
|
||||||
match x {
|
|
||||||
case 0: return;
|
|
||||||
case 1: return "Goodbye";
|
|
||||||
case 2: return "Goodbye", "cruel world...";
|
|
||||||
case 3: return second = "cruel world...", first = "Goodbye";
|
|
||||||
}
|
|
||||||
|
|
||||||
return second = "my old friend.";
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.printf("%s %s\n", foo(0));
|
|
||||||
fmt.printf("%s %s\n", foo(1));
|
|
||||||
fmt.printf("%s %s\n", foo(2));
|
|
||||||
fmt.printf("%s %s\n", foo(3));
|
|
||||||
fmt.printf("%s %s\n", foo(4));
|
|
||||||
fmt.println();
|
|
||||||
|
|
||||||
|
|
||||||
// A more "real" example
|
|
||||||
Error :: enum {
|
|
||||||
None,
|
|
||||||
WhyTheNumberThree,
|
|
||||||
TenIsTooBig,
|
|
||||||
};
|
|
||||||
|
|
||||||
Entity :: struct {
|
|
||||||
name: string;
|
|
||||||
id: u32;
|
|
||||||
}
|
|
||||||
|
|
||||||
some_thing :: proc(input: int) -> (result: ^Entity = nil, err := Error.None) {
|
|
||||||
match {
|
|
||||||
case input == 3: return err = Error.WhyTheNumberThree;
|
|
||||||
case input >= 10: return err = Error.TenIsTooBig;
|
|
||||||
}
|
|
||||||
|
|
||||||
e := new(Entity);
|
|
||||||
e.id = u32(input);
|
|
||||||
|
|
||||||
return result = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
call_location :: proc() {
|
|
||||||
amazing :: proc(n: int, using loc := #caller_location) {
|
|
||||||
fmt.printf("%s(%d:%d) just asked to do something amazing.\n",
|
|
||||||
fully_pathed_filename, line, column);
|
|
||||||
fmt.printf("Normal -> %d\n", n);
|
|
||||||
fmt.printf("Amazing -> %d\n", n+1);
|
|
||||||
fmt.println();
|
|
||||||
}
|
|
||||||
|
|
||||||
loc := #location(main);
|
|
||||||
fmt.println("`main` is located at", loc);
|
|
||||||
|
|
||||||
fmt.println("This line is located at", #location());
|
|
||||||
fmt.println();
|
|
||||||
|
|
||||||
amazing(3);
|
|
||||||
amazing(4, #location(call_location));
|
|
||||||
|
|
||||||
// See _preload.odin for the implementations of `assert` and `panic`
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
explicit_parametric_polymorphic_procedures :: proc() {
|
|
||||||
// This is how `new` is actually implemented, see _preload.odin
|
|
||||||
alloc_type :: proc(T: type) -> ^T do return cast(^T)alloc(size_of(T), align_of(T));
|
|
||||||
|
|
||||||
int_ptr := alloc_type(int);
|
|
||||||
defer free(int_ptr);
|
|
||||||
int_ptr^ = 137;
|
|
||||||
fmt.println(int_ptr, int_ptr^);
|
|
||||||
|
|
||||||
// Named arguments work too!
|
|
||||||
another_ptr := alloc_type(T = f32);
|
|
||||||
defer free(another_ptr);
|
|
||||||
|
|
||||||
|
|
||||||
add :: proc(T: type, args: ...T) -> T {
|
|
||||||
res: T;
|
|
||||||
for arg in args do res += arg;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.println("add =", add(int, 1, 2, 3, 4, 5, 6));
|
|
||||||
|
|
||||||
swap :: proc(T: type, a, b: ^T) {
|
|
||||||
tmp := a^;
|
|
||||||
a^ = b^;
|
|
||||||
b^ = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, b: int = 3, 4;
|
|
||||||
fmt.println("Pre-swap:", a, b);
|
|
||||||
swap(int, &a, &b);
|
|
||||||
fmt.println("Post-swap:", a, b);
|
|
||||||
a, b = b, a; // Or use this syntax for this silly example case
|
|
||||||
|
|
||||||
|
|
||||||
Vector2 :: struct {x, y: f32;};
|
|
||||||
{
|
|
||||||
// A more complicated example using subtyping
|
|
||||||
// Something like this could be used in a game
|
|
||||||
|
|
||||||
Entity :: struct {
|
|
||||||
using position: Vector2;
|
|
||||||
flags: u64;
|
|
||||||
id: u64;
|
|
||||||
derived: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
Rock :: struct {
|
|
||||||
using entity: Entity;
|
|
||||||
heavy: bool;
|
|
||||||
}
|
|
||||||
Door :: struct {
|
|
||||||
using entity: Entity;
|
|
||||||
open: bool;
|
|
||||||
}
|
|
||||||
Monster :: struct {
|
|
||||||
using entity: Entity;
|
|
||||||
is_robot: bool;
|
|
||||||
is_zombie: bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
new_entity :: proc(T: type, x, y: f32) -> ^T {
|
|
||||||
result := new(T);
|
|
||||||
result.derived = result^;
|
|
||||||
result.x = x;
|
|
||||||
result.y = y;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
entities: [dynamic]^Entity;
|
|
||||||
|
|
||||||
rock := new_entity(Rock, 3, 5);
|
|
||||||
|
|
||||||
// Named arguments work too!
|
|
||||||
door := new_entity(T = Door, x = 3, y = 6);
|
|
||||||
|
|
||||||
// And named arguments can be any order
|
|
||||||
monster := new_entity(
|
|
||||||
y = 1,
|
|
||||||
x = 2,
|
|
||||||
T = Monster,
|
|
||||||
);
|
|
||||||
|
|
||||||
append(&entities, rock, door, monster);
|
|
||||||
|
|
||||||
fmt.println("Subtyping");
|
|
||||||
for entity in entities {
|
|
||||||
match e in entity.derived {
|
|
||||||
case Rock: fmt.println("Rock", e.x, e.y);
|
|
||||||
case Door: fmt.println("Door", e.x, e.y);
|
|
||||||
case Monster: fmt.println("Monster", e.x, e.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Entity :: struct {
|
|
||||||
using position: Vector2;
|
|
||||||
flags: u64;
|
|
||||||
id: u64;
|
|
||||||
variant: union { Rock, Door, Monster };
|
|
||||||
}
|
|
||||||
|
|
||||||
Rock :: struct {
|
|
||||||
using entity: ^Entity;
|
|
||||||
heavy: bool;
|
|
||||||
}
|
|
||||||
Door :: struct {
|
|
||||||
using entity: ^Entity;
|
|
||||||
open: bool;
|
|
||||||
}
|
|
||||||
Monster :: struct {
|
|
||||||
using entity: ^Entity;
|
|
||||||
is_robot: bool;
|
|
||||||
is_zombie: bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
new_entity :: proc(T: type, x, y: f32) -> ^T {
|
|
||||||
result := new(Entity);
|
|
||||||
result.variant = T{entity = result};
|
|
||||||
result.x = x;
|
|
||||||
result.y = y;
|
|
||||||
|
|
||||||
return cast(^T)&result.variant;
|
|
||||||
}
|
|
||||||
|
|
||||||
entities: [dynamic]^Entity;
|
|
||||||
|
|
||||||
rock := new_entity(Rock, 3, 5);
|
|
||||||
|
|
||||||
// Named arguments work too!
|
|
||||||
door := new_entity(T = Door, x = 3, y = 6);
|
|
||||||
|
|
||||||
// And named arguments can be any order
|
|
||||||
monster := new_entity(
|
|
||||||
y = 1,
|
|
||||||
x = 2,
|
|
||||||
T = Monster,
|
|
||||||
);
|
|
||||||
|
|
||||||
append(&entities, rock, door, monster);
|
|
||||||
|
|
||||||
fmt.println("Union");
|
|
||||||
for entity in entities {
|
|
||||||
match e in entity.variant {
|
|
||||||
case Rock: fmt.println("Rock", e.x, e.y);
|
|
||||||
case Door: fmt.println("Door", e.x, e.y);
|
|
||||||
case Monster: fmt.println("Monster", e.x, e.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
implicit_polymorphic_assignment :: proc() {
|
|
||||||
yep :: proc(p: proc(x: int)) {
|
|
||||||
p(123);
|
|
||||||
}
|
|
||||||
|
|
||||||
frank :: proc(x: $T) do fmt.println("frank ->", x);
|
|
||||||
tim :: proc(x, y: $T) do fmt.println("tim ->", x, y);
|
|
||||||
yep(frank);
|
|
||||||
// yep(tim);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
main :: proc() {
|
|
||||||
/*
|
|
||||||
foo :: proc(x: i64, y: f32) do fmt.println("#1", x, y);
|
|
||||||
foo :: proc(x: type, y: f32) do fmt.println("#2", type_info(x), y);
|
|
||||||
foo :: proc(x: type) do fmt.println("#3", type_info(x));
|
|
||||||
|
|
||||||
f :: foo;
|
|
||||||
|
|
||||||
f(y = 3785.1546, x = 123);
|
|
||||||
f(x = int, y = 897.513);
|
|
||||||
f(x = f32);
|
|
||||||
|
|
||||||
general_stuff();
|
|
||||||
foreign_blocks();
|
|
||||||
default_arguments();
|
|
||||||
named_arguments();
|
|
||||||
default_return_values();
|
|
||||||
call_location();
|
|
||||||
explicit_parametric_polymorphic_procedures();
|
|
||||||
implicit_polymorphic_assignment();
|
|
||||||
|
|
||||||
|
|
||||||
// Command line argument(s)!
|
|
||||||
// -opt=0,1,2,3
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
program := "+ + * - /";
|
|
||||||
accumulator := 0;
|
|
||||||
|
|
||||||
for token in program {
|
|
||||||
match token {
|
|
||||||
case '+': accumulator += 1;
|
|
||||||
case '-': accumulator -= 1;
|
|
||||||
case '*': accumulator *= 2;
|
|
||||||
case '/': accumulator /= 2;
|
|
||||||
case: // Ignore everything else
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.printf("The program \"%s\" calculates the value %d\n",
|
|
||||||
program, accumulator);
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
-222
@@ -1,222 +0,0 @@
|
|||||||
import win32 "sys/windows.odin" when ODIN_OS == "windows";
|
|
||||||
import wgl "sys/wgl.odin" when ODIN_OS == "windows";
|
|
||||||
import "fmt.odin";
|
|
||||||
import "math.odin";
|
|
||||||
import "os.odin";
|
|
||||||
import gl "opengl.odin";
|
|
||||||
|
|
||||||
const TWO_HEARTS = '💕';
|
|
||||||
|
|
||||||
var win32_perf_count_freq = win32.get_query_performance_frequency();
|
|
||||||
proc time_now() -> f64 {
|
|
||||||
assert(win32_perf_count_freq != 0);
|
|
||||||
|
|
||||||
var counter: i64;
|
|
||||||
win32.query_performance_counter(&counter);
|
|
||||||
return f64(counter) / f64(win32_perf_count_freq);
|
|
||||||
}
|
|
||||||
proc win32_print_last_error() {
|
|
||||||
var err_code = win32.get_last_error();
|
|
||||||
if err_code != 0 {
|
|
||||||
fmt.println("get_last_error: ", err_code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Yuk!
|
|
||||||
proc to_c_string(s: string) -> []u8 {
|
|
||||||
var c_str = make([]u8, len(s)+1);
|
|
||||||
copy(c_str, []u8(s));
|
|
||||||
c_str[len(s)] = 0;
|
|
||||||
return c_str;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
type Window struct {
|
|
||||||
width, height: int,
|
|
||||||
wc: win32.WndClassExA,
|
|
||||||
dc: win32.Hdc,
|
|
||||||
hwnd: win32.Hwnd,
|
|
||||||
opengl_context, rc: wgl.Hglrc,
|
|
||||||
c_title: []u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
proc make_window(title: string, msg, height: int, window_proc: win32.WndProc) -> (Window, bool) {
|
|
||||||
using win32;
|
|
||||||
|
|
||||||
var w: Window;
|
|
||||||
w.width, w.height = msg, height;
|
|
||||||
|
|
||||||
var class_name = "Win32-Odin-Window\x00";
|
|
||||||
var c_class_name = &class_name[0];
|
|
||||||
if title[len(title)-1] != 0 {
|
|
||||||
w.c_title = to_c_string(title);
|
|
||||||
} else {
|
|
||||||
w.c_title = []u8(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
var instance = get_module_handle_a(nil);
|
|
||||||
|
|
||||||
w.wc = WndClassExA{
|
|
||||||
size = size_of(WndClassExA),
|
|
||||||
style = CS_VREDRAW | CS_HREDRAW,
|
|
||||||
instance = Hinstance(instance),
|
|
||||||
class_name = c_class_name,
|
|
||||||
wnd_proc = window_proc,
|
|
||||||
};
|
|
||||||
|
|
||||||
if register_class_ex_a(&w.wc) == 0 {
|
|
||||||
win32_print_last_error();
|
|
||||||
return w, false;
|
|
||||||
}
|
|
||||||
|
|
||||||
w.hwnd = create_window_ex_a(0,
|
|
||||||
c_class_name, &w.c_title[0],
|
|
||||||
WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
|
|
||||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
||||||
i32(w.width), i32(w.height),
|
|
||||||
nil, nil, instance, nil);
|
|
||||||
|
|
||||||
if w.hwnd == nil {
|
|
||||||
win32_print_last_error();
|
|
||||||
return w, false;
|
|
||||||
}
|
|
||||||
|
|
||||||
w.dc = get_dc(w.hwnd);
|
|
||||||
|
|
||||||
{
|
|
||||||
var pfd = PixelFormatDescriptor{
|
|
||||||
size = size_of(PixelFormatDescriptor),
|
|
||||||
version = 1,
|
|
||||||
flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
|
|
||||||
pixel_type = PFD_TYPE_RGBA,
|
|
||||||
color_bits = 32,
|
|
||||||
alpha_bits = 8,
|
|
||||||
depth_bits = 24,
|
|
||||||
stencil_bits = 8,
|
|
||||||
layer_type = PFD_MAIN_PLANE,
|
|
||||||
};
|
|
||||||
|
|
||||||
set_pixel_format(w.dc, choose_pixel_format(w.dc, &pfd), nil);
|
|
||||||
w.opengl_context = wgl.create_context(w.dc);
|
|
||||||
wgl.make_current(w.dc, w.opengl_context);
|
|
||||||
|
|
||||||
var attribs = [8]i32{
|
|
||||||
wgl.CONTEXT_MAJOR_VERSION_ARB, 2,
|
|
||||||
wgl.CONTEXT_MINOR_VERSION_ARB, 1,
|
|
||||||
wgl.CONTEXT_PROFILE_MASK_ARB, wgl.CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
|
||||||
0, // NOTE(bill): tells the proc that this is the end of attribs
|
|
||||||
};
|
|
||||||
|
|
||||||
var wgl_str = "wglCreateContextAttribsARB\x00";
|
|
||||||
var wglCreateContextAttribsARB = wgl.CreateContextAttribsARBType(wgl.get_proc_address(&wgl_str[0]));
|
|
||||||
w.rc = wglCreateContextAttribsARB(w.dc, nil, &attribs[0]);
|
|
||||||
wgl.make_current(w.dc, w.rc);
|
|
||||||
swap_buffers(w.dc);
|
|
||||||
}
|
|
||||||
|
|
||||||
return w, true;
|
|
||||||
}
|
|
||||||
|
|
||||||
proc destroy_window(w: ^Window) {
|
|
||||||
free(w.c_title);
|
|
||||||
}
|
|
||||||
|
|
||||||
proc display_window(w: ^Window) {
|
|
||||||
win32.swap_buffers(w.dc);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
proc run() {
|
|
||||||
using math;
|
|
||||||
|
|
||||||
proc win32_proc(hwnd: win32.Hwnd, msg: u32, wparam: win32.Wparam, lparam: win32.Lparam) -> win32.Lresult #no_inline {
|
|
||||||
using win32;
|
|
||||||
if msg == WM_DESTROY || msg == WM_CLOSE || msg == WM_QUIT {
|
|
||||||
os.exit(0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return def_window_proc_a(hwnd, msg, wparam, lparam);
|
|
||||||
}
|
|
||||||
|
|
||||||
var window, window_success = make_window("Odin Language Demo", 854, 480, win32.WndProc(win32_proc));
|
|
||||||
if !window_success {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
defer destroy_window(&window);
|
|
||||||
|
|
||||||
gl.init();
|
|
||||||
|
|
||||||
using win32;
|
|
||||||
|
|
||||||
var prev_time = time_now();
|
|
||||||
var running = true;
|
|
||||||
|
|
||||||
var pos = Vec2{100, 100};
|
|
||||||
|
|
||||||
for running {
|
|
||||||
var curr_time = time_now();
|
|
||||||
var dt = f32(curr_time - prev_time);
|
|
||||||
prev_time = curr_time;
|
|
||||||
|
|
||||||
var msg: Msg;
|
|
||||||
for peek_message_a(&msg, nil, 0, 0, PM_REMOVE) > 0 {
|
|
||||||
if msg.message == WM_QUIT {
|
|
||||||
running = false;
|
|
||||||
}
|
|
||||||
translate_message(&msg);
|
|
||||||
dispatch_message_a(&msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if is_key_down(KeyCode.Escape) {
|
|
||||||
running = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const SPEED = 500;
|
|
||||||
var v: Vec2;
|
|
||||||
|
|
||||||
if is_key_down(KeyCode.Right) { v[0] += 1; }
|
|
||||||
if is_key_down(KeyCode.Left) { v[0] -= 1; }
|
|
||||||
if is_key_down(KeyCode.Up) { v[1] += 1; }
|
|
||||||
if is_key_down(KeyCode.Down) { v[1] -= 1; }
|
|
||||||
|
|
||||||
v = norm(v);
|
|
||||||
|
|
||||||
pos += v * Vec2{SPEED * dt};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
gl.ClearColor(0.5, 0.7, 1.0, 1.0);
|
|
||||||
gl.Clear(gl.COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
gl.LoadIdentity();
|
|
||||||
gl.Ortho(0, f64(window.width),
|
|
||||||
0, f64(window.height), 0, 1);
|
|
||||||
|
|
||||||
proc draw_rect(x, y, w, h: f32) {
|
|
||||||
gl.Begin(gl.TRIANGLES);
|
|
||||||
defer gl.End();
|
|
||||||
|
|
||||||
gl.Color3f(1, 0, 0); gl.Vertex3f(x, y, 0);
|
|
||||||
gl.Color3f(0, 1, 0); gl.Vertex3f(x+w, y, 0);
|
|
||||||
gl.Color3f(0, 0, 1); gl.Vertex3f(x+w, y+h, 0);
|
|
||||||
|
|
||||||
gl.Color3f(0, 0, 1); gl.Vertex3f(x+w, y+h, 0);
|
|
||||||
gl.Color3f(1, 1, 0); gl.Vertex3f(x, y+h, 0);
|
|
||||||
gl.Color3f(1, 0, 0); gl.Vertex3f(x, y, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
draw_rect(pos.x, pos.y, 50, 50);
|
|
||||||
|
|
||||||
display_window(&window);
|
|
||||||
var ms_to_sleep = i32(16 - 1000*dt);
|
|
||||||
if ms_to_sleep > 0 {
|
|
||||||
win32.sleep(ms_to_sleep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
proc main() {
|
|
||||||
run();
|
|
||||||
}
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
import "fmt.odin";
|
|
||||||
|
|
||||||
foreign_system_library ws2 "Ws2_32.lib" when ODIN_OS == "windows";
|
|
||||||
|
|
||||||
|
|
||||||
type SOCKET uint;
|
|
||||||
const INVALID_SOCKET = ~SOCKET(0);
|
|
||||||
|
|
||||||
type AF enum i32 {
|
|
||||||
UNSPEC = 0, // unspecified
|
|
||||||
UNIX = 1, // local to host (pipes, portals)
|
|
||||||
INET = 2, // internetwork: UDP, TCP, etc.
|
|
||||||
IMPLINK = 3, // arpanet imp addresses
|
|
||||||
PUP = 4, // pup protocols: e.g. BSP
|
|
||||||
CHAOS = 5, // mit CHAOS protocols
|
|
||||||
NS = 6, // XEROX NS protocols
|
|
||||||
ISO = 7, // ISO protocols
|
|
||||||
OSI = ISO, // OSI is ISO
|
|
||||||
ECMA = 8, // european computer manufacturers
|
|
||||||
DATAKIT = 9, // datakit protocols
|
|
||||||
CCITT = 10, // CCITT protocols, X.25 etc
|
|
||||||
SNA = 11, // IBM SNA
|
|
||||||
DECnet = 12, // DECnet
|
|
||||||
DLI = 13, // Direct data link interface
|
|
||||||
LAT = 14, // LAT
|
|
||||||
HYLINK = 15, // NSC Hyperchannel
|
|
||||||
APPLETALK = 16, // AppleTalk
|
|
||||||
ROUTE = 17, // Internal Routing Protocol
|
|
||||||
LINK = 18, // Link layer interface
|
|
||||||
XTP = 19, // eXpress Transfer Protocol (no AF)
|
|
||||||
COIP = 20, // connection-oriented IP, aka ST II
|
|
||||||
CNT = 21, // Computer Network Technology
|
|
||||||
RTIP = 22, // Help Identify RTIP packets
|
|
||||||
IPX = 23, // Novell Internet Protocol
|
|
||||||
SIP = 24, // Simple Internet Protocol
|
|
||||||
PIP = 25, // Help Identify PIP packets
|
|
||||||
MAX = 26,
|
|
||||||
};
|
|
||||||
|
|
||||||
const (
|
|
||||||
SOCK_STREAM = 1;
|
|
||||||
SOCKET_ERROR = -1;
|
|
||||||
IPPROTO_TCP = 6;
|
|
||||||
AI_PASSIVE = 0x0020;
|
|
||||||
SOMAXCONN = 128;
|
|
||||||
)
|
|
||||||
const (
|
|
||||||
SD_RECEIVE = 0;
|
|
||||||
SD_SEND = 1;
|
|
||||||
SD_BOTH = 2;
|
|
||||||
)
|
|
||||||
|
|
||||||
const WSADESCRIPTION_LEN = 256;
|
|
||||||
const WSASYS_STATUS_LEN = 128;
|
|
||||||
type WSADATA struct #ordered {
|
|
||||||
version: i16,
|
|
||||||
high_version: i16,
|
|
||||||
|
|
||||||
|
|
||||||
// NOTE(bill): This is x64 ordering
|
|
||||||
max_sockets: u16,
|
|
||||||
max_udp_dg: u16,
|
|
||||||
vendor_info: ^u8,
|
|
||||||
description: [WSADESCRIPTION_LEN+1]u8,
|
|
||||||
system_status: [WSASYS_STATUS_LEN+1]u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
type addrinfo struct #ordered {
|
|
||||||
flags: i32,
|
|
||||||
family: i32,
|
|
||||||
socktype: i32,
|
|
||||||
protocol: i32,
|
|
||||||
addrlen: uint,
|
|
||||||
canonname: ^u8,
|
|
||||||
addr: ^sockaddr,
|
|
||||||
next: ^addrinfo,
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddr struct #ordered {
|
|
||||||
family: u16,
|
|
||||||
data: [14]u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
foreign ws2 {
|
|
||||||
proc WSAStartup (version_requested: i16, data: ^WSADATA) -> i32;
|
|
||||||
proc WSACleanup () -> i32;
|
|
||||||
proc getaddrinfo (node_name, service_name: ^u8, hints: ^addrinfo, result: ^^addrinfo) -> i32;
|
|
||||||
proc freeaddrinfo (ai: ^addrinfo);
|
|
||||||
proc socket (af, type_, protocol: i32) -> SOCKET;
|
|
||||||
proc closesocket (s: SOCKET) -> i32;
|
|
||||||
proc bind (s: SOCKET, name: ^sockaddr, name_len: i32) -> i32;
|
|
||||||
proc listen (s: SOCKET, back_log: i32) -> i32;
|
|
||||||
proc accept (s: SOCKET, addr: ^sockaddr, addr_len: i32) -> SOCKET;
|
|
||||||
proc recv (s: SOCKET, buf: ^u8, len: i32, flags: i32) -> i32;
|
|
||||||
proc send (s: SOCKET, buf: ^u8, len: i32, flags: i32) -> i32;
|
|
||||||
proc shutdown (s: SOCKET, how: i32) -> i32;
|
|
||||||
proc WSAGetLastError() -> i32;
|
|
||||||
}
|
|
||||||
proc to_c_string(s: string) -> ^u8 {
|
|
||||||
var c_str = make([]u8, len(s)+1);
|
|
||||||
copy(c_str, []u8(s));
|
|
||||||
c_str[len(s)] = 0;
|
|
||||||
return &c_str[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
proc run() {
|
|
||||||
var (
|
|
||||||
wsa: WSADATA;
|
|
||||||
res: ^addrinfo = nil;
|
|
||||||
hints: addrinfo;
|
|
||||||
s, client: SOCKET;
|
|
||||||
)
|
|
||||||
|
|
||||||
if WSAStartup(2 | (2 << 8), &wsa) != 0 {
|
|
||||||
fmt.println("WSAStartup failed: ", WSAGetLastError());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
defer WSACleanup();
|
|
||||||
|
|
||||||
hints.family = i32(AF.INET);
|
|
||||||
hints.socktype = SOCK_STREAM;
|
|
||||||
hints.protocol = IPPROTO_TCP;
|
|
||||||
hints.flags = AI_PASSIVE;
|
|
||||||
|
|
||||||
if getaddrinfo(nil, to_c_string("8080"), &hints, &res) != 0 {
|
|
||||||
fmt.println("getaddrinfo failed: ", WSAGetLastError());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
defer freeaddrinfo(res);
|
|
||||||
|
|
||||||
s = socket(res.family, res.socktype, res.protocol);
|
|
||||||
if s == INVALID_SOCKET {
|
|
||||||
fmt.println("socket failed: ", WSAGetLastError());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
defer closesocket(s);
|
|
||||||
|
|
||||||
bind(s, res.addr, i32(res.addrlen));
|
|
||||||
listen(s, SOMAXCONN);
|
|
||||||
|
|
||||||
client = accept(s, nil, 0);
|
|
||||||
if client == INVALID_SOCKET {
|
|
||||||
fmt.println("socket failed: ", WSAGetLastError());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
defer closesocket(client);
|
|
||||||
|
|
||||||
var html =
|
|
||||||
`HTTP/1.1 200 OK
|
|
||||||
Connection: close
|
|
||||||
Content-type: text/html
|
|
||||||
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Demo Title</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1 style="color: orange;">Odin Server Demo</h1>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`;
|
|
||||||
|
|
||||||
var buf: [1024]u8;
|
|
||||||
for {
|
|
||||||
var bytes = recv(client, &buf[0], i32(len(buf)), 0);
|
|
||||||
if bytes > 0 {
|
|
||||||
// fmt.println(string(buf[0..<bytes]))
|
|
||||||
var bytes_sent = send(client, &html[0], i32(len(html)-1), 0);
|
|
||||||
if bytes_sent == SOCKET_ERROR {
|
|
||||||
fmt.println("send failed: ", WSAGetLastError());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} else if bytes == 0 {
|
|
||||||
fmt.println("Connection closing...");
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
fmt.println("recv failed: ", WSAGetLastError());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shutdown(client, SD_SEND);
|
|
||||||
}
|
|
||||||
@@ -1,337 +0,0 @@
|
|||||||
#import "fmt.odin";
|
|
||||||
#import "os.odin";
|
|
||||||
#import "mem.odin";
|
|
||||||
// #import "http_test.odin" as ht;
|
|
||||||
// #import "game.odin" as game;
|
|
||||||
// #import "punity.odin" as pn;
|
|
||||||
|
|
||||||
main :: proc() {
|
|
||||||
struct_padding();
|
|
||||||
bounds_checking();
|
|
||||||
type_introspection();
|
|
||||||
any_type();
|
|
||||||
crazy_introspection();
|
|
||||||
namespaces_and_files();
|
|
||||||
miscellany();
|
|
||||||
|
|
||||||
/*
|
|
||||||
ht.run();
|
|
||||||
game.run();
|
|
||||||
{
|
|
||||||
init :: proc(c: ^pn.Core) {}
|
|
||||||
step :: proc(c: ^pn.Core) {}
|
|
||||||
|
|
||||||
pn.run(init, step);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
struct_padding :: proc() {
|
|
||||||
{
|
|
||||||
A :: struct {
|
|
||||||
a: u8,
|
|
||||||
b: u32,
|
|
||||||
c: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
B :: struct {
|
|
||||||
a: [7]u8,
|
|
||||||
b: [3]u16,
|
|
||||||
c: u8,
|
|
||||||
d: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.println("size_of(A):", size_of(A));
|
|
||||||
fmt.println("size_of(B):", size_of(B));
|
|
||||||
|
|
||||||
// n.b. http://cbloomrants.blogspot.co.uk/2012/07/07-23-12-structs-are-not-what-you-want.html
|
|
||||||
}
|
|
||||||
{
|
|
||||||
A :: struct #ordered {
|
|
||||||
a: u8,
|
|
||||||
b: u32,
|
|
||||||
c: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
B :: struct #ordered {
|
|
||||||
a: [7]u8,
|
|
||||||
b: [3]u16,
|
|
||||||
c: u8,
|
|
||||||
d: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.println("size_of(A):", size_of(A));
|
|
||||||
fmt.println("size_of(B):", size_of(B));
|
|
||||||
|
|
||||||
// C-style structure layout
|
|
||||||
}
|
|
||||||
{
|
|
||||||
A :: struct #packed {
|
|
||||||
a: u8,
|
|
||||||
b: u32,
|
|
||||||
c: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
B :: struct #packed {
|
|
||||||
a: [7]u8,
|
|
||||||
b: [3]u16,
|
|
||||||
c: u8,
|
|
||||||
d: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.println("size_of(A):", size_of(A));
|
|
||||||
fmt.println("size_of(B):", size_of(B));
|
|
||||||
|
|
||||||
// Useful for explicit layout
|
|
||||||
}
|
|
||||||
|
|
||||||
// Member sorting by priority
|
|
||||||
// Alignment desc.
|
|
||||||
// Size desc.
|
|
||||||
// source order asc.
|
|
||||||
|
|
||||||
/*
|
|
||||||
A :: struct {
|
|
||||||
a: u8
|
|
||||||
b: u32
|
|
||||||
c: u16
|
|
||||||
}
|
|
||||||
|
|
||||||
B :: struct {
|
|
||||||
a: [7]u8
|
|
||||||
b: [3]u16
|
|
||||||
c: u8
|
|
||||||
d: u16
|
|
||||||
}
|
|
||||||
|
|
||||||
Equivalent too
|
|
||||||
|
|
||||||
A :: struct #ordered {
|
|
||||||
b: u32
|
|
||||||
c: u16
|
|
||||||
a: u8
|
|
||||||
}
|
|
||||||
|
|
||||||
B :: struct #ordered {
|
|
||||||
b: [3]u16
|
|
||||||
d: u16
|
|
||||||
a: [7]u8
|
|
||||||
c: u8
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
bounds_checking :: proc() {
|
|
||||||
x: [4]int;
|
|
||||||
// x[-1] = 0; // Compile Time
|
|
||||||
// x[4] = 0; // Compile Time
|
|
||||||
|
|
||||||
{
|
|
||||||
a, b := -1, 4;
|
|
||||||
// x[a] = 0; // Runtime Time
|
|
||||||
// x[b] = 0; // Runtime Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// Works for arrays, strings, slices, and related procedures & operations
|
|
||||||
|
|
||||||
{
|
|
||||||
base: [10]int;
|
|
||||||
s := base[2..6];
|
|
||||||
a, b := -1, 6;
|
|
||||||
|
|
||||||
#no_bounds_check {
|
|
||||||
s[a] = 0;
|
|
||||||
// #bounds_check s[b] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#no_bounds_check
|
|
||||||
if s[a] == 0 {
|
|
||||||
// Do whatever
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bounds checking can be toggled explicit
|
|
||||||
// on a per statement basis.
|
|
||||||
// _any statement_
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type_introspection :: proc() {
|
|
||||||
{
|
|
||||||
info: ^Type_Info;
|
|
||||||
x: int;
|
|
||||||
|
|
||||||
info = type_info(int); // by type
|
|
||||||
info = type_info_of_val(x); // by value
|
|
||||||
// See: runtime.odin
|
|
||||||
|
|
||||||
match i in info {
|
|
||||||
case Type_Info.Integer:
|
|
||||||
fmt.println("integer!");
|
|
||||||
case Type_Info.Float:
|
|
||||||
fmt.println("float!");
|
|
||||||
default:
|
|
||||||
fmt.println("potato!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unsafe cast
|
|
||||||
integer_info := cast(^Type_Info.Integer)cast(rawptr)info;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Vector2 :: struct { x, y: f32 }
|
|
||||||
Vector3 :: struct { x, y, z: f32 }
|
|
||||||
|
|
||||||
v1: Vector2;
|
|
||||||
v2: Vector3;
|
|
||||||
v3: Vector3;
|
|
||||||
|
|
||||||
t1 := type_info_of_val(v1);
|
|
||||||
t2 := type_info_of_val(v2);
|
|
||||||
t3 := type_info_of_val(v3);
|
|
||||||
|
|
||||||
fmt.println();
|
|
||||||
fmt.print("Type of v1 is:\n\t", t1);
|
|
||||||
|
|
||||||
fmt.println();
|
|
||||||
fmt.print("Type of v2 is:\n\t", t2);
|
|
||||||
|
|
||||||
fmt.println("\n");
|
|
||||||
fmt.println("t1 == t2:", t1 == t2);
|
|
||||||
fmt.println("t2 == t3:", t2 == t3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
any_type :: proc() {
|
|
||||||
a: any;
|
|
||||||
|
|
||||||
x: int = 123;
|
|
||||||
y: f64 = 6.28;
|
|
||||||
z: string = "Yo-Yo Ma";
|
|
||||||
// All types can be implicit cast to `any`
|
|
||||||
a = x;
|
|
||||||
a = y;
|
|
||||||
a = z;
|
|
||||||
a = a; // This the "identity" type, it doesn't get converted
|
|
||||||
|
|
||||||
a = 123; // Literals are copied onto the stack first
|
|
||||||
|
|
||||||
// any has two members
|
|
||||||
// data - rawptr to the data
|
|
||||||
// type_info - pointer to the type info
|
|
||||||
|
|
||||||
fmt.println(x, y, z);
|
|
||||||
// See: fmt.odin
|
|
||||||
// For variadic any procedures in action
|
|
||||||
}
|
|
||||||
|
|
||||||
crazy_introspection :: proc() {
|
|
||||||
{
|
|
||||||
Fruit :: enum {
|
|
||||||
APPLE,
|
|
||||||
BANANA,
|
|
||||||
GRAPE,
|
|
||||||
MELON,
|
|
||||||
PEACH,
|
|
||||||
TOMATO,
|
|
||||||
}
|
|
||||||
|
|
||||||
s: string;
|
|
||||||
// s = enum_to_string(Fruit.PEACH);
|
|
||||||
fmt.println(s);
|
|
||||||
|
|
||||||
f := Fruit.GRAPE;
|
|
||||||
// s = enum_to_string(f);
|
|
||||||
fmt.println(s);
|
|
||||||
|
|
||||||
fmt.println(f);
|
|
||||||
// See: runtime.odin
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
// NOTE(bill): This is not safe code and I would not recommend this at all
|
|
||||||
// I'd recommend you use `match type` to get the subtype rather than
|
|
||||||
// casting pointers
|
|
||||||
|
|
||||||
Fruit :: enum {
|
|
||||||
APPLE,
|
|
||||||
BANANA,
|
|
||||||
GRAPE,
|
|
||||||
MELON,
|
|
||||||
PEACH,
|
|
||||||
TOMATO,
|
|
||||||
}
|
|
||||||
|
|
||||||
fruit_ti := type_info(Fruit);
|
|
||||||
name := (union_cast(^Type_Info.Named)fruit_ti).name; // Unsafe casts
|
|
||||||
info, _ := union_cast(^Type_Info.Enum)type_info_base(fruit_ti); // Unsafe casts
|
|
||||||
|
|
||||||
fmt.printf("%s :: enum %T {\n", name, info.base);
|
|
||||||
for i := 0; i < len(info.values); i++ {
|
|
||||||
fmt.printf("\t%s\t= %v,\n", info.names[i], info.values[i]);
|
|
||||||
}
|
|
||||||
fmt.printf("}\n");
|
|
||||||
|
|
||||||
// NOTE(bill): look at that type-safe printf!
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Vector3 :: struct {x, y, z: f32}
|
|
||||||
|
|
||||||
a := Vector3{x = 1, y = 4, z = 9};
|
|
||||||
fmt.println(a);
|
|
||||||
b := Vector3{x = 9, y = 3, z = 1};
|
|
||||||
fmt.println(b);
|
|
||||||
|
|
||||||
// NOTE(bill): See fmt.odin
|
|
||||||
}
|
|
||||||
|
|
||||||
// n.b. This pretty much "solves" serialization (to strings)
|
|
||||||
}
|
|
||||||
|
|
||||||
// #import "test.odin"
|
|
||||||
|
|
||||||
namespaces_and_files :: proc() {
|
|
||||||
|
|
||||||
// test.thing()
|
|
||||||
// test.format.println()
|
|
||||||
// test.println()
|
|
||||||
/*
|
|
||||||
// Non-exporting import
|
|
||||||
#import "file.odin"
|
|
||||||
#import "file.odin" as file
|
|
||||||
#import "file.odin" as .
|
|
||||||
#import "file.odin" as _
|
|
||||||
|
|
||||||
// Exporting import
|
|
||||||
#include "file.odin"
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Talk about scope rules and diagram
|
|
||||||
}
|
|
||||||
|
|
||||||
miscellany :: proc() {
|
|
||||||
/*
|
|
||||||
win32 `__imp__` prefix
|
|
||||||
#dll_import
|
|
||||||
#dll_export
|
|
||||||
|
|
||||||
Change exported name/symbol for linking
|
|
||||||
#link_name
|
|
||||||
|
|
||||||
Custom calling conventions
|
|
||||||
#stdcall
|
|
||||||
#fastcall
|
|
||||||
|
|
||||||
Runtime stuff
|
|
||||||
#shared_global_scope
|
|
||||||
*/
|
|
||||||
|
|
||||||
// assert(false)
|
|
||||||
// compile_assert(false)
|
|
||||||
// panic("Panic message goes here")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,892 +0,0 @@
|
|||||||
// Demo 002
|
|
||||||
#load "fmt.odin";
|
|
||||||
#load "math.odin";
|
|
||||||
// #load "game.odin"
|
|
||||||
|
|
||||||
#thread_local tls_int: int;
|
|
||||||
|
|
||||||
main :: proc() {
|
|
||||||
// Forenotes
|
|
||||||
|
|
||||||
// Semicolons are now optional
|
|
||||||
// Rule for when a semicolon is expected after a statement
|
|
||||||
// - If the next token is not on the same line
|
|
||||||
// - if the next token is a closing brace }
|
|
||||||
// - Otherwise, a semicolon is needed
|
|
||||||
//
|
|
||||||
// Expections:
|
|
||||||
// for, if, match
|
|
||||||
// if x := thing(); x < 123 {}
|
|
||||||
// for i := 0; i < 123; i++ {}
|
|
||||||
|
|
||||||
// Q: Should I use the new rule or go back to the old one without optional semicolons?
|
|
||||||
|
|
||||||
|
|
||||||
// #thread_local - see runtime.odin and above at `tls_int`
|
|
||||||
// #foreign_system_library - see win32.odin
|
|
||||||
|
|
||||||
// struct_compound_literals();
|
|
||||||
// enumerations();
|
|
||||||
// variadic_procedures();
|
|
||||||
// new_builtins();
|
|
||||||
// match_statement();
|
|
||||||
// namespacing();
|
|
||||||
// subtyping();
|
|
||||||
// tagged_unions();
|
|
||||||
}
|
|
||||||
|
|
||||||
struct_compound_literals :: proc() {
|
|
||||||
Thing :: struct {
|
|
||||||
id: int,
|
|
||||||
x: f32,
|
|
||||||
name: string,
|
|
||||||
};
|
|
||||||
{
|
|
||||||
t1: Thing;
|
|
||||||
t1.id = 1;
|
|
||||||
|
|
||||||
t3 := Thing{};
|
|
||||||
t4 := Thing{1, 2, "Fred"};
|
|
||||||
// t5 := Thing{1, 2};
|
|
||||||
|
|
||||||
t6 := Thing{
|
|
||||||
name = "Tom",
|
|
||||||
x = 23,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enumerations :: proc() {
|
|
||||||
{
|
|
||||||
Fruit :: enum {
|
|
||||||
APPLE, // 0
|
|
||||||
BANANA, // 1
|
|
||||||
PEAR, // 2
|
|
||||||
};
|
|
||||||
|
|
||||||
f := Fruit.APPLE;
|
|
||||||
// g12: int = Fruit.BANANA
|
|
||||||
g: int = cast(int)Fruit.BANANA;
|
|
||||||
// However, you can use enums are index values as _any_ integer allowed
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Fruit1 :: enum int {
|
|
||||||
APPLE,
|
|
||||||
BANANA,
|
|
||||||
PEAR,
|
|
||||||
}
|
|
||||||
|
|
||||||
Fruit2 :: enum u8 {
|
|
||||||
APPLE,
|
|
||||||
BANANA,
|
|
||||||
PEAR,
|
|
||||||
}
|
|
||||||
|
|
||||||
Fruit3 :: enum u8 {
|
|
||||||
APPLE = 1,
|
|
||||||
BANANA, // 2
|
|
||||||
PEAR = 5,
|
|
||||||
TOMATO, // 6
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Q: remove the need for `type` if it's a record (struct/enum/raw_union/union)?
|
|
||||||
}
|
|
||||||
|
|
||||||
variadic_procedures :: proc() {
|
|
||||||
print_ints :: proc(args: ..int) {
|
|
||||||
for arg, i in args {
|
|
||||||
if i > 0 {
|
|
||||||
print(", ");
|
|
||||||
}
|
|
||||||
print(arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print_ints(); // nl()
|
|
||||||
print_ints(1); nl();
|
|
||||||
print_ints(1, 2, 3); nl();
|
|
||||||
|
|
||||||
print_prefix_f32s :: proc(prefix: string, args: ..f32) {
|
|
||||||
print(prefix);
|
|
||||||
print(": ");
|
|
||||||
for arg, i in args {
|
|
||||||
if i > 0 {
|
|
||||||
print(", ");
|
|
||||||
}
|
|
||||||
print(arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print_prefix_f32s("a"); nl();
|
|
||||||
print_prefix_f32s("b", 1); nl();
|
|
||||||
print_prefix_f32s("c", 1, 2, 3); nl();
|
|
||||||
|
|
||||||
// Internally, the variadic procedures get allocated to an array on the stack,
|
|
||||||
// and this array is passed a slice
|
|
||||||
|
|
||||||
// This is first step for a `print` procedure but I do not have an `any` type
|
|
||||||
// yet as this requires a few other things first - i.e. introspection
|
|
||||||
|
|
||||||
// NOTE(bill): I haven't yet added the feature of expanding a slice or array into
|
|
||||||
// a variadic a parameter but it's pretty trivial to add
|
|
||||||
}
|
|
||||||
|
|
||||||
new_builtins :: proc() {
|
|
||||||
{
|
|
||||||
a := new(int);
|
|
||||||
b := make([]int, 12);
|
|
||||||
c := make([]int, 12, 16);
|
|
||||||
|
|
||||||
defer free(a);
|
|
||||||
defer free(b);
|
|
||||||
defer free(c);
|
|
||||||
|
|
||||||
// NOTE(bill): These use the current context's allocator not the default allocator
|
|
||||||
// see runtime.odin
|
|
||||||
|
|
||||||
// Q: Should this be `free` rather than `free` and should I overload it for slices too?
|
|
||||||
|
|
||||||
{
|
|
||||||
prev_context := context;
|
|
||||||
defer __context = prev_context;
|
|
||||||
// Q: Should I add a `push_context` feature to the language?
|
|
||||||
|
|
||||||
__context.allocator = default_allocator();
|
|
||||||
|
|
||||||
a := new(int);
|
|
||||||
defer free(a);
|
|
||||||
|
|
||||||
// Do whatever
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
a: int = 123;
|
|
||||||
b: type_of_val(a) = 321;
|
|
||||||
|
|
||||||
// NOTE(bill): This matches the current naming scheme
|
|
||||||
// size_of
|
|
||||||
// align_of
|
|
||||||
// offset_of
|
|
||||||
//
|
|
||||||
// size_of_val
|
|
||||||
// align_of_val
|
|
||||||
// offset_of_val
|
|
||||||
// type_of_val
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// Compile time assert
|
|
||||||
COND :: true;
|
|
||||||
compile_assert(COND);
|
|
||||||
// compile_assert(!COND)
|
|
||||||
|
|
||||||
// Runtime assert
|
|
||||||
x := true;
|
|
||||||
assert(x);
|
|
||||||
// assert(!x);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
x: ^u32 = nil;
|
|
||||||
y := x+100;
|
|
||||||
z := y-x;
|
|
||||||
w := slice_ptr(x, 12);
|
|
||||||
t := slice_ptr(x, 12, 16);
|
|
||||||
|
|
||||||
// NOTE(bill): These are here because I've removed:
|
|
||||||
// pointer arithmetic
|
|
||||||
// pointer indexing
|
|
||||||
// pointer slicing
|
|
||||||
|
|
||||||
// Reason
|
|
||||||
|
|
||||||
a: [16]int;
|
|
||||||
a[1] = 1;
|
|
||||||
b := ^a;
|
|
||||||
// Auto pointer deref
|
|
||||||
// consistent with record members
|
|
||||||
assert(b[1] == 1);
|
|
||||||
|
|
||||||
// Q: Should I add them back in at the cost of inconsitency?
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
a, b := -1, 2;
|
|
||||||
print(min(a, b)); nl();
|
|
||||||
print(max(a, b)); nl();
|
|
||||||
print(abs(a)); nl();
|
|
||||||
|
|
||||||
// These work at compile time too
|
|
||||||
A :: -1;
|
|
||||||
B :: 2;
|
|
||||||
C :: min(A, B);
|
|
||||||
D :: max(A, B);
|
|
||||||
E :: abs(A);
|
|
||||||
|
|
||||||
print(C); nl();
|
|
||||||
print(D); nl();
|
|
||||||
print(E); nl();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
match_statement :: proc() {
|
|
||||||
// NOTE(bill): `match` statements are similar to `switch` statements
|
|
||||||
// in other languages but there are few differences
|
|
||||||
|
|
||||||
{
|
|
||||||
match x := 5; x {
|
|
||||||
case 1: // cases must be constant expression
|
|
||||||
print("1!\n");
|
|
||||||
// break by default
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
s := "2!\n"; // Each case has its own scope
|
|
||||||
print(s);
|
|
||||||
break; // explicit break
|
|
||||||
|
|
||||||
case 3, 4: // multiple cases
|
|
||||||
print("3 or 4!\n");
|
|
||||||
|
|
||||||
case 5:
|
|
||||||
print("5!\n");
|
|
||||||
fallthrough; // explicit fallthrough
|
|
||||||
|
|
||||||
default:
|
|
||||||
print("default!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
match x := 1.5; x {
|
|
||||||
case 1.5:
|
|
||||||
print("1.5!\n");
|
|
||||||
// break by default
|
|
||||||
case TAU:
|
|
||||||
print("τ!\n");
|
|
||||||
default:
|
|
||||||
print("default!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
match x := "Hello"; x {
|
|
||||||
case "Hello":
|
|
||||||
print("greeting\n");
|
|
||||||
// break by default
|
|
||||||
case "Goodbye":
|
|
||||||
print("farewell\n");
|
|
||||||
default:
|
|
||||||
print("???\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
a := 53;
|
|
||||||
match {
|
|
||||||
case a == 1:
|
|
||||||
print("one\n");
|
|
||||||
case a == 2:
|
|
||||||
print("a couple\n");
|
|
||||||
case a < 7, a == 7:
|
|
||||||
print("a few\n");
|
|
||||||
case a < 12: // intentional bug
|
|
||||||
print("several\n");
|
|
||||||
case a >= 12 && a < 100:
|
|
||||||
print("dozens\n");
|
|
||||||
case a >= 100 && a < 1000:
|
|
||||||
print("hundreds\n");
|
|
||||||
default:
|
|
||||||
print("a fuck ton\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Identical to this
|
|
||||||
|
|
||||||
b := 53;
|
|
||||||
if b == 1 {
|
|
||||||
print("one\n");
|
|
||||||
} else if b == 2 {
|
|
||||||
print("a couple\n");
|
|
||||||
} else if b < 7 || b == 7 {
|
|
||||||
print("a few\n");
|
|
||||||
} else if b < 12 { // intentional bug
|
|
||||||
print("several\n");
|
|
||||||
} else if b >= 12 && b < 100 {
|
|
||||||
print("dozens\n");
|
|
||||||
} else if b >= 100 && b < 1000 {
|
|
||||||
print("hundreds\n");
|
|
||||||
} else {
|
|
||||||
print("a fuck ton\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// However, match statements allow for `break` and `fallthrough` unlike
|
|
||||||
// an if statement
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3 :: struct {x, y, z: f32}
|
|
||||||
|
|
||||||
print_floats :: proc(args: ..f32) {
|
|
||||||
for arg, i in args {
|
|
||||||
if i > 0 {
|
|
||||||
print(", ");
|
|
||||||
}
|
|
||||||
print(arg);
|
|
||||||
}
|
|
||||||
println();
|
|
||||||
}
|
|
||||||
|
|
||||||
namespacing :: proc() {
|
|
||||||
{
|
|
||||||
Thing :: #type struct {
|
|
||||||
x: f32,
|
|
||||||
name: string,
|
|
||||||
};
|
|
||||||
|
|
||||||
a: Thing;
|
|
||||||
a.x = 3;
|
|
||||||
{
|
|
||||||
Thing :: #type struct {
|
|
||||||
y: int,
|
|
||||||
test: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
b: Thing; // Uses this scope's Thing
|
|
||||||
b.test = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
{
|
|
||||||
Entity :: struct {
|
|
||||||
Guid :: int
|
|
||||||
Nested :: struct {
|
|
||||||
MyInt :: int
|
|
||||||
i: int
|
|
||||||
}
|
|
||||||
|
|
||||||
CONSTANT :: 123
|
|
||||||
|
|
||||||
|
|
||||||
guid: Guid
|
|
||||||
name: string
|
|
||||||
pos: Vector3
|
|
||||||
vel: Vector3
|
|
||||||
nested: Nested
|
|
||||||
}
|
|
||||||
|
|
||||||
guid: Entity.Guid = Entity.CONSTANT
|
|
||||||
i: Entity.Nested.MyInt
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
using Entity
|
|
||||||
guid: Guid = CONSTANT
|
|
||||||
using Nested
|
|
||||||
i: MyInt
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
using Entity.Nested
|
|
||||||
guid: Entity.Guid = Entity.CONSTANT
|
|
||||||
i: MyInt
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
e: Entity
|
|
||||||
using e
|
|
||||||
guid = 27832
|
|
||||||
name = "Bob"
|
|
||||||
|
|
||||||
print(e.guid as int); nl()
|
|
||||||
print(e.name); nl()
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
using e: Entity
|
|
||||||
guid = 78456
|
|
||||||
name = "Thing"
|
|
||||||
|
|
||||||
print(e.guid as int); nl()
|
|
||||||
print(e.name); nl()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Entity :: struct {
|
|
||||||
Guid :: int
|
|
||||||
Nested :: struct {
|
|
||||||
MyInt :: int
|
|
||||||
i: int
|
|
||||||
}
|
|
||||||
|
|
||||||
CONSTANT :: 123
|
|
||||||
|
|
||||||
|
|
||||||
guid: Guid
|
|
||||||
name: string
|
|
||||||
using pos: Vector3
|
|
||||||
vel: Vector3
|
|
||||||
using nested: ^Nested
|
|
||||||
}
|
|
||||||
|
|
||||||
e := Entity{nested = new(Entity.Nested)}
|
|
||||||
e.x = 123
|
|
||||||
e.i = Entity.CONSTANT
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
{
|
|
||||||
Entity :: struct {
|
|
||||||
position: Vector3
|
|
||||||
}
|
|
||||||
|
|
||||||
print_pos_1 :: proc(entity: ^Entity) {
|
|
||||||
print("print_pos_1: ");
|
|
||||||
print_floats(entity.position.x, entity.position.y, entity.position.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
print_pos_2 :: proc(entity: ^Entity) {
|
|
||||||
using entity;
|
|
||||||
print("print_pos_2: ");
|
|
||||||
print_floats(position.x, position.y, position.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
print_pos_3 :: proc(using entity: ^Entity) {
|
|
||||||
print("print_pos_3: ");
|
|
||||||
print_floats(position.x, position.y, position.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
print_pos_4 :: proc(using entity: ^Entity) {
|
|
||||||
using position;
|
|
||||||
print("print_pos_4: ");
|
|
||||||
print_floats(x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
e := Entity{position = Vector3{1, 2, 3}};
|
|
||||||
print_pos_1(^e);
|
|
||||||
print_pos_2(^e);
|
|
||||||
print_pos_3(^e);
|
|
||||||
print_pos_4(^e);
|
|
||||||
|
|
||||||
// This is similar to C++'s `this` pointer that is implicit and only available in methods
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
subtyping :: proc() {
|
|
||||||
{
|
|
||||||
// C way for subtyping/subclassing
|
|
||||||
|
|
||||||
Entity :: struct {
|
|
||||||
position: Vector3,
|
|
||||||
}
|
|
||||||
|
|
||||||
Frog :: struct {
|
|
||||||
entity: Entity,
|
|
||||||
jump_height: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
f: Frog;
|
|
||||||
f.entity.position = Vector3{1, 2, 3};
|
|
||||||
|
|
||||||
using f.entity;
|
|
||||||
position = Vector3{1, 2, 3};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// C++ way for subtyping/subclassing
|
|
||||||
|
|
||||||
Entity :: struct {
|
|
||||||
position: Vector3
|
|
||||||
}
|
|
||||||
|
|
||||||
Frog :: struct {
|
|
||||||
using entity: Entity,
|
|
||||||
jump_height: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
f: Frog;
|
|
||||||
f.position = Vector3{1, 2, 3};
|
|
||||||
|
|
||||||
|
|
||||||
print_pos :: proc(using entity: Entity) {
|
|
||||||
print("print_pos: ");
|
|
||||||
print_floats(position.x, position.y, position.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
print_pos(f.entity);
|
|
||||||
// print_pos(f);
|
|
||||||
|
|
||||||
// Subtype Polymorphism
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// More than C++ way for subtyping/subclassing
|
|
||||||
|
|
||||||
Entity :: struct {
|
|
||||||
position: Vector3,
|
|
||||||
}
|
|
||||||
|
|
||||||
Frog :: struct {
|
|
||||||
jump_height: f32,
|
|
||||||
using entity: ^Entity, // Doesn't have to be first member!
|
|
||||||
}
|
|
||||||
|
|
||||||
f: Frog;
|
|
||||||
f.entity = new(Entity);
|
|
||||||
f.position = Vector3{1, 2, 3};
|
|
||||||
|
|
||||||
|
|
||||||
print_pos :: proc(using entity: ^Entity) {
|
|
||||||
print("print_pos: ");
|
|
||||||
print_floats(position.x, position.y, position.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
print_pos(f.entity);
|
|
||||||
// print_pos(^f);
|
|
||||||
// print_pos(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// More efficient subtyping
|
|
||||||
|
|
||||||
Entity :: struct {
|
|
||||||
position: Vector3,
|
|
||||||
}
|
|
||||||
|
|
||||||
Frog :: struct {
|
|
||||||
jump_height: f32,
|
|
||||||
using entity: ^Entity,
|
|
||||||
}
|
|
||||||
|
|
||||||
MAX_ENTITES :: 64;
|
|
||||||
entities: [MAX_ENTITES]Entity;
|
|
||||||
entity_count := 0;
|
|
||||||
|
|
||||||
next_entity :: proc(entities: []Entity, entity_count: ^int) -> ^Entity {
|
|
||||||
e := ^entities[entity_count^];
|
|
||||||
entity_count^++;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
f: Frog;
|
|
||||||
f.entity = next_entity(entities[..], ^entity_count);
|
|
||||||
f.position = Vector3{3, 4, 6};
|
|
||||||
|
|
||||||
using f.position;
|
|
||||||
print_floats(x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// Down casting
|
|
||||||
|
|
||||||
Entity :: struct {
|
|
||||||
position: Vector3,
|
|
||||||
}
|
|
||||||
|
|
||||||
Frog :: struct {
|
|
||||||
jump_height: f32,
|
|
||||||
using entity: Entity,
|
|
||||||
}
|
|
||||||
|
|
||||||
f: Frog;
|
|
||||||
f.jump_height = 564;
|
|
||||||
e := ^f.entity;
|
|
||||||
|
|
||||||
frog := down_cast(^Frog)e;
|
|
||||||
print("down_cast: ");
|
|
||||||
print(frog.jump_height); nl();
|
|
||||||
|
|
||||||
// NOTE(bill): `down_cast` is unsafe and there are not check are compile time or run time
|
|
||||||
// Q: Should I completely remove `down_cast` as I added it in about 30 minutes
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// Multiple "inheritance"/subclassing
|
|
||||||
|
|
||||||
Entity :: struct {
|
|
||||||
position: Vector3,
|
|
||||||
}
|
|
||||||
Climber :: struct {
|
|
||||||
speed: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
Frog :: struct {
|
|
||||||
using entity: Entity,
|
|
||||||
using climber: Climber,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tagged_unions :: proc() {
|
|
||||||
{
|
|
||||||
EntityKind :: enum {
|
|
||||||
INVALID,
|
|
||||||
FROG,
|
|
||||||
GIRAFFE,
|
|
||||||
HELICOPTER,
|
|
||||||
}
|
|
||||||
|
|
||||||
Entity :: struct {
|
|
||||||
kind: EntityKind
|
|
||||||
using data: raw_union {
|
|
||||||
frog: struct {
|
|
||||||
jump_height: f32,
|
|
||||||
colour: u32,
|
|
||||||
},
|
|
||||||
giraffe: struct {
|
|
||||||
neck_length: f32,
|
|
||||||
spot_count: int,
|
|
||||||
},
|
|
||||||
helicopter: struct {
|
|
||||||
blade_count: int,
|
|
||||||
weight: f32,
|
|
||||||
pilot_name: string,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
e: Entity;
|
|
||||||
e.kind = EntityKind.FROG;
|
|
||||||
e.frog.jump_height = 12;
|
|
||||||
|
|
||||||
f: type_of_val(e.frog);
|
|
||||||
|
|
||||||
// But this is very unsafe and extremely cumbersome to write
|
|
||||||
// In C++, I use macros to alleviate this but it's not a solution
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Entity :: union {
|
|
||||||
Frog{
|
|
||||||
jump_height: f32,
|
|
||||||
colour: u32,
|
|
||||||
},
|
|
||||||
Giraffe{
|
|
||||||
neck_length: f32,
|
|
||||||
spot_count: int,
|
|
||||||
},
|
|
||||||
Helicopter{
|
|
||||||
blade_count: int,
|
|
||||||
weight: f32,
|
|
||||||
pilot_name: string,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
using Entity;
|
|
||||||
f1: Frog = Frog{12, 0xff9900};
|
|
||||||
f2: Entity = Frog{12, 0xff9900}; // Implicit cast
|
|
||||||
f3 := cast(Entity)Frog{12, 0xff9900}; // Explicit cast
|
|
||||||
|
|
||||||
// f3.Frog.jump_height = 12 // There are "members" of a union
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
e, f, g, h: Entity;
|
|
||||||
f = Frog{12, 0xff9900};
|
|
||||||
g = Giraffe{2.1, 23};
|
|
||||||
h = Helicopter{4, 1000, "Frank"};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Requires a pointer to the union
|
|
||||||
// `x` will be a pointer to type of the case
|
|
||||||
|
|
||||||
match x in ^f {
|
|
||||||
case Frog:
|
|
||||||
print("Frog!\n");
|
|
||||||
print(x.jump_height); nl();
|
|
||||||
// x.jump_height = 3;
|
|
||||||
print(x.jump_height); nl();
|
|
||||||
case Giraffe:
|
|
||||||
print("Giraffe!\n");
|
|
||||||
case Helicopter:
|
|
||||||
print("ROFLCOPTER!\n");
|
|
||||||
default:
|
|
||||||
print("invalid entity\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Q: Allow for a non pointer version with takes a copy instead?
|
|
||||||
// Or it takes the pointer the data and not a copy
|
|
||||||
|
|
||||||
|
|
||||||
// fp := cast(^Frog)^f; // Unsafe
|
|
||||||
// print(fp.jump_height); nl();
|
|
||||||
|
|
||||||
|
|
||||||
// Internals of a tagged union
|
|
||||||
/*
|
|
||||||
struct {
|
|
||||||
data: [size_of_biggest_tag]u8,
|
|
||||||
tag_index: int,
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
// This is to allow for pointer casting if needed
|
|
||||||
|
|
||||||
|
|
||||||
// Advantage over subtyping version
|
|
||||||
MAX_ENTITES :: 64;
|
|
||||||
entities: [MAX_ENTITES]Entity;
|
|
||||||
|
|
||||||
entities[0] = Frog{};
|
|
||||||
entities[1] = Helicopter{};
|
|
||||||
// etc.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
// Transliteration of code from this actual compiler
|
|
||||||
// Some stuff is missing
|
|
||||||
Type :: struct {};
|
|
||||||
Scope :: struct {};
|
|
||||||
Token :: struct {};
|
|
||||||
AstNode :: struct {};
|
|
||||||
ExactValue :: struct {};
|
|
||||||
|
|
||||||
EntityKind :: enum {
|
|
||||||
Invalid,
|
|
||||||
Constant,
|
|
||||||
Variable,
|
|
||||||
UsingVariable,
|
|
||||||
TypeName,
|
|
||||||
Procedure,
|
|
||||||
Builtin,
|
|
||||||
Count,
|
|
||||||
}
|
|
||||||
|
|
||||||
Guid :: i64;
|
|
||||||
Entity :: struct {
|
|
||||||
|
|
||||||
kind: EntityKind,
|
|
||||||
guid: Guid,
|
|
||||||
|
|
||||||
scope: ^Scope,
|
|
||||||
token: Token,
|
|
||||||
type_: ^Type,
|
|
||||||
|
|
||||||
using data: raw_union {
|
|
||||||
Constant: struct {
|
|
||||||
value: ExactValue,
|
|
||||||
},
|
|
||||||
Variable: struct {
|
|
||||||
visited: bool, // Cycle detection
|
|
||||||
used: bool, // Variable is used
|
|
||||||
is_field: bool, // Is struct field
|
|
||||||
anonymous: bool, // Variable is an anonymous
|
|
||||||
},
|
|
||||||
UsingVariable: struct {
|
|
||||||
},
|
|
||||||
TypeName: struct {
|
|
||||||
},
|
|
||||||
Procedure: struct {
|
|
||||||
used: bool,
|
|
||||||
},
|
|
||||||
Builtin: struct {
|
|
||||||
id: int,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Plus all the constructing procedures that go along with them!!!!
|
|
||||||
// It's a nightmare
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Type :: struct {};
|
|
||||||
Scope :: struct {};
|
|
||||||
Token :: struct {};
|
|
||||||
AstNode :: struct {};
|
|
||||||
ExactValue :: struct {};
|
|
||||||
|
|
||||||
|
|
||||||
Guid :: i64;
|
|
||||||
Entity_Base :: struct {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Entity :: union {
|
|
||||||
guid: Guid,
|
|
||||||
|
|
||||||
scope: ^Scope,
|
|
||||||
token: Token,
|
|
||||||
type_: ^Type,
|
|
||||||
|
|
||||||
Constant{
|
|
||||||
value: ExactValue,
|
|
||||||
},
|
|
||||||
Variable{
|
|
||||||
visited: bool, // Cycle detection
|
|
||||||
used: bool, // Variable is used
|
|
||||||
is_field: bool, // Is struct field
|
|
||||||
anonymous: bool, // Variable is an anonymous
|
|
||||||
},
|
|
||||||
UsingVariable{
|
|
||||||
},
|
|
||||||
TypeName{
|
|
||||||
},
|
|
||||||
Procedure{
|
|
||||||
used: bool,
|
|
||||||
},
|
|
||||||
Builtin{
|
|
||||||
id: int,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
using Entity;
|
|
||||||
|
|
||||||
e: Entity;
|
|
||||||
|
|
||||||
e = Variable{
|
|
||||||
used = true,
|
|
||||||
anonymous = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Q: Allow a "base" type to be added to a union?
|
|
||||||
// Or even `using` on union to get the same properties?
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
// `Raw` unions still have uses, especially for mathematic types
|
|
||||||
|
|
||||||
Vector2 :: raw_union {
|
|
||||||
using xy_: struct { x, y: f32 },
|
|
||||||
e: [2]f32,
|
|
||||||
v: [vector 2]f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3 :: raw_union {
|
|
||||||
using xyz_: struct { x, y, z: f32 },
|
|
||||||
xy: Vector2,
|
|
||||||
e: [3]f32,
|
|
||||||
v: [vector 3]f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
v2: Vector2;
|
|
||||||
v2.x = 1;
|
|
||||||
v2.e[0] = 1;
|
|
||||||
v2.v[0] = 1;
|
|
||||||
|
|
||||||
v3: Vector3;
|
|
||||||
v3.x = 1;
|
|
||||||
v3.e[0] = 1;
|
|
||||||
v3.v[0] = 1;
|
|
||||||
v3.xy.x = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nl :: proc() { println(); }
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
#import "fmt.odin";
|
|
||||||
#import "utf8.odin";
|
|
||||||
#import "hash.odin";
|
|
||||||
#import "mem.odin";
|
|
||||||
|
|
||||||
main :: proc() {
|
|
||||||
{ // New Standard Library stuff
|
|
||||||
s := "Hello";
|
|
||||||
fmt.println(s,
|
|
||||||
utf8.valid_string(s),
|
|
||||||
hash.murmur64(cast([]byte)s));
|
|
||||||
|
|
||||||
// utf8.odin
|
|
||||||
// hash.odin
|
|
||||||
// - crc, fnv, fnva, murmur
|
|
||||||
// mem.odin
|
|
||||||
// - Custom allocators
|
|
||||||
// - Helpers
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
arena: mem.Arena;
|
|
||||||
mem.init_arena_from_context(^arena, mem.megabytes(16)); // Uses default allocator
|
|
||||||
defer mem.free_arena(^arena);
|
|
||||||
|
|
||||||
push_allocator mem.arena_allocator(^arena) {
|
|
||||||
x := new(int);
|
|
||||||
x^ = 1337;
|
|
||||||
|
|
||||||
fmt.println(x^);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
push_allocator x {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
is equivalent to:
|
|
||||||
|
|
||||||
{
|
|
||||||
prev_allocator := __context.allocator
|
|
||||||
__context.allocator = x
|
|
||||||
defer __context.allocator = prev_allocator
|
|
||||||
|
|
||||||
...
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// You can also "push" a context
|
|
||||||
|
|
||||||
c := context; // Create copy of the allocator
|
|
||||||
c.allocator = mem.arena_allocator(^arena);
|
|
||||||
|
|
||||||
push_context c {
|
|
||||||
x := new(int);
|
|
||||||
x^ = 365;
|
|
||||||
|
|
||||||
fmt.println(x^);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backend improvements
|
|
||||||
// - Minimal dependency building (only build what is needed)
|
|
||||||
// - Numerous bugs fixed
|
|
||||||
// - Mild parsing recovery after bad syntax error
|
|
||||||
}
|
|
||||||
@@ -1,284 +0,0 @@
|
|||||||
#import "fmt.odin";
|
|
||||||
#import "utf8.odin";
|
|
||||||
// #import "atomic.odin";
|
|
||||||
// #import "hash.odin";
|
|
||||||
// #import "math.odin";
|
|
||||||
// #import "mem.odin";
|
|
||||||
// #import "opengl.odin";
|
|
||||||
// #import "os.odin";
|
|
||||||
// #import "sync.odin";
|
|
||||||
// #import win32 "sys/windows.odin";
|
|
||||||
|
|
||||||
main :: proc() {
|
|
||||||
// syntax();
|
|
||||||
procedure_overloading();
|
|
||||||
}
|
|
||||||
|
|
||||||
syntax :: proc() {
|
|
||||||
// Cyclic type checking
|
|
||||||
// Uncomment to see the error
|
|
||||||
// A :: struct {b: B};
|
|
||||||
// B :: struct {a: A};
|
|
||||||
|
|
||||||
x: int;
|
|
||||||
y := cast(f32)x;
|
|
||||||
z := transmute(u32)y;
|
|
||||||
// down_cast, union_cast are similar too
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Basic directives
|
|
||||||
fmt.printf("Basic directives = %s(%d): %s\n", #file, #line, #procedure);
|
|
||||||
// NOTE: new and improved `printf`
|
|
||||||
// TODO: It does need accurate float printing
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// record fields use the same syntax a procedure signatures
|
|
||||||
Thing1 :: struct {
|
|
||||||
x: f32,
|
|
||||||
y: int,
|
|
||||||
z: ^[]int,
|
|
||||||
};
|
|
||||||
Thing2 :: struct {x: f32, y: int, z: ^[]int};
|
|
||||||
|
|
||||||
// Slice interals are now just a `ptr+len+cap`
|
|
||||||
slice: []int; compile_assert(size_of_val(slice) == 3*size_of(int));
|
|
||||||
|
|
||||||
// Helper type - Help the reader understand what it is quicker
|
|
||||||
My_Int :: #type int;
|
|
||||||
My_Proc :: #type proc(int) -> f32;
|
|
||||||
|
|
||||||
|
|
||||||
// All declarations with : are either variable or constant
|
|
||||||
// To make these declarations syntactically consistent
|
|
||||||
v_variable := 123;
|
|
||||||
c_constant :: 123;
|
|
||||||
c_type1 :: int;
|
|
||||||
c_type2 :: []int;
|
|
||||||
c_proc :: proc() { /* code here */ };
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
x += 1;
|
|
||||||
x -= 1;
|
|
||||||
// ++ and -- have been removed
|
|
||||||
// x++;
|
|
||||||
// x--;
|
|
||||||
// Question: Should they be added again?
|
|
||||||
// They were removed as they are redundant and statements, not expressions
|
|
||||||
// like in C/C++
|
|
||||||
*/
|
|
||||||
|
|
||||||
// You can now build files as a `.dll`
|
|
||||||
// `odin build_dll demo.odin`
|
|
||||||
|
|
||||||
|
|
||||||
// New vector syntax
|
|
||||||
u, v: [vector 3]f32;
|
|
||||||
v[0] = 123;
|
|
||||||
v.x = 123; // valid for all vectors with count 1 to 4
|
|
||||||
|
|
||||||
// Next part
|
|
||||||
prefixes();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Prefix_Type :: struct {x: int, y: f32, z: rawptr};
|
|
||||||
|
|
||||||
#thread_local my_tls: Prefix_Type;
|
|
||||||
|
|
||||||
prefixes :: proc() {
|
|
||||||
using var: Prefix_Type;
|
|
||||||
immutable const := Prefix_Type{1, 2, nil};
|
|
||||||
var.x = 123;
|
|
||||||
x = 123;
|
|
||||||
// const.x = 123; // const is immutable
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
foo :: proc(using immutable pt: Prefix_Type, immutable int_ptr: ^int) {
|
|
||||||
// int_ptr = nil; // Not valid
|
|
||||||
// int_ptr^ = 123; // Not valid
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Same as C99's `restrict`
|
|
||||||
bar :: proc(no_alias a, b: ^int) {
|
|
||||||
// Assumes a never equals b so it can perform optimizations with that fact
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
when_statements();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
when_statements :: proc() {
|
|
||||||
X :: 123 + 12;
|
|
||||||
Y :: X/5;
|
|
||||||
COND :: Y > 0;
|
|
||||||
|
|
||||||
when COND {
|
|
||||||
fmt.println("Y > 0");
|
|
||||||
} else {
|
|
||||||
fmt.println("Y <= 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
when false {
|
|
||||||
this_code_does_not_exist(123, 321);
|
|
||||||
but_its_syntax_is_valid();
|
|
||||||
x :: ^^^^int;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreign_procedures();
|
|
||||||
}
|
|
||||||
|
|
||||||
#foreign_system_library win32_user "user32.lib" when ODIN_OS == "windows";
|
|
||||||
// NOTE: This is done on purpose for two reasons:
|
|
||||||
// * Makes it clear where the platform specific stuff is
|
|
||||||
// * Removes the need to solve the travelling salesman problem when importing files :P
|
|
||||||
|
|
||||||
foreign_procedures :: proc() {
|
|
||||||
ShowWindow :: proc(hwnd: rawptr, cmd_show: i32) -> i32 #foreign win32_user;
|
|
||||||
show_window :: proc(hwnd: rawptr, cmd_show: i32) -> i32 #foreign win32_user "ShowWindow";
|
|
||||||
// NOTE: If that library doesn't get used, it doesn't get linked with
|
|
||||||
// NOTE: There is not link checking yet to see if that procedure does come from that library
|
|
||||||
|
|
||||||
// See sys/windows.odin for more examples
|
|
||||||
|
|
||||||
special_expressions();
|
|
||||||
}
|
|
||||||
|
|
||||||
special_expressions :: proc() {
|
|
||||||
/*
|
|
||||||
// Block expression
|
|
||||||
x := {
|
|
||||||
a: f32 = 123;
|
|
||||||
b := a-123;
|
|
||||||
c := b/a;
|
|
||||||
give c;
|
|
||||||
}; // semicolon is required as it's an expression
|
|
||||||
|
|
||||||
y := if x < 50 {
|
|
||||||
give x;
|
|
||||||
} else {
|
|
||||||
// TODO: Type cohesion is not yet finished
|
|
||||||
give 123;
|
|
||||||
}; // semicolon is required as it's an expression
|
|
||||||
*/
|
|
||||||
|
|
||||||
// This is allows for inline blocks of code and will be a useful feature to have when
|
|
||||||
// macros will be implemented into the language
|
|
||||||
|
|
||||||
loops();
|
|
||||||
}
|
|
||||||
|
|
||||||
loops :: proc() {
|
|
||||||
// The C-style for loop
|
|
||||||
for i := 0; i < 123; i += 1 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
for i := 0; i < 123; {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
for false {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in 0..123 { // 123 exclusive
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in 0..123-1 { // 122 inclusive
|
|
||||||
}
|
|
||||||
|
|
||||||
for val, idx in 12..16 {
|
|
||||||
fmt.println(val, idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
primes := [..]int{2, 3, 5, 7, 11, 13, 17, 19};
|
|
||||||
|
|
||||||
for p in primes {
|
|
||||||
fmt.println(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pointers to arrays, slices, or strings are allowed
|
|
||||||
for _ in ^primes {
|
|
||||||
// ignore the value and just iterate across it
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
name := "你好,世界";
|
|
||||||
fmt.println(name);
|
|
||||||
for r in name {
|
|
||||||
compile_assert(type_of_val(r) == rune);
|
|
||||||
fmt.printf("%r\n", r);
|
|
||||||
}
|
|
||||||
|
|
||||||
when false {
|
|
||||||
for i, size := 0; i < name.count; i += size {
|
|
||||||
r: rune;
|
|
||||||
r, size = utf8.decode_rune(name[i..]);
|
|
||||||
fmt.printf("%r\n", r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
procedure_overloading();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
procedure_overloading :: proc() {
|
|
||||||
THINGF :: 14451.1;
|
|
||||||
THINGI :: 14451;
|
|
||||||
|
|
||||||
foo :: proc() {
|
|
||||||
fmt.printf("Zero args\n");
|
|
||||||
}
|
|
||||||
foo :: proc(i: int) {
|
|
||||||
fmt.printf("int arg, i=%d\n", i);
|
|
||||||
}
|
|
||||||
foo :: proc(f: f64) {
|
|
||||||
i := cast(int)f;
|
|
||||||
fmt.printf("f64 arg, f=%d\n", i);
|
|
||||||
}
|
|
||||||
|
|
||||||
foo();
|
|
||||||
foo(THINGF);
|
|
||||||
// foo(THINGI); // 14451 is just a number so it could go to either procedures
|
|
||||||
foo(cast(int)THINGI);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
foo :: proc(x: ^i32) -> (int, int) {
|
|
||||||
fmt.println("^int");
|
|
||||||
return 123, cast(int)(x^);
|
|
||||||
}
|
|
||||||
foo :: proc(x: rawptr) {
|
|
||||||
fmt.println("rawptr");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
a: i32 = 123;
|
|
||||||
b: f32;
|
|
||||||
c: rawptr;
|
|
||||||
fmt.println(foo(^a));
|
|
||||||
foo(^b);
|
|
||||||
foo(c);
|
|
||||||
// foo(nil); // nil could go to numerous types thus the ambiguity
|
|
||||||
|
|
||||||
f: proc();
|
|
||||||
f = foo; // The correct `foo` to chosen
|
|
||||||
f();
|
|
||||||
|
|
||||||
|
|
||||||
// See math.odin and atomic.odin for more examples
|
|
||||||
}
|
|
||||||
@@ -1,412 +0,0 @@
|
|||||||
#include "win32.odin"
|
|
||||||
|
|
||||||
assume :: proc(cond: bool) #foreign "llvm.assume"
|
|
||||||
|
|
||||||
__debug_trap :: proc() #foreign "llvm.debugtrap"
|
|
||||||
__trap :: proc() #foreign "llvm.trap"
|
|
||||||
read_cycle_counter :: proc() -> u64 #foreign "llvm.readcyclecounter"
|
|
||||||
|
|
||||||
bit_reverse16 :: proc(b: u16) -> u16 #foreign "llvm.bitreverse.i16"
|
|
||||||
bit_reverse32 :: proc(b: u32) -> u32 #foreign "llvm.bitreverse.i32"
|
|
||||||
bit_reverse64 :: proc(b: u64) -> u64 #foreign "llvm.bitreverse.i64"
|
|
||||||
|
|
||||||
byte_swap16 :: proc(b: u16) -> u16 #foreign "llvm.bswap.i16"
|
|
||||||
byte_swap32 :: proc(b: u32) -> u32 #foreign "llvm.bswap.i32"
|
|
||||||
byte_swap64 :: proc(b: u64) -> u64 #foreign "llvm.bswap.i64"
|
|
||||||
|
|
||||||
fmuladd_f32 :: proc(a, b, c: f32) -> f32 #foreign "llvm.fmuladd.f32"
|
|
||||||
fmuladd_f64 :: proc(a, b, c: f64) -> f64 #foreign "llvm.fmuladd.f64"
|
|
||||||
|
|
||||||
// TODO(bill): make custom heap procedures
|
|
||||||
heap_alloc :: proc(len: int) -> rawptr #foreign "malloc"
|
|
||||||
heap_dealloc :: proc(ptr: rawptr) #foreign "free"
|
|
||||||
|
|
||||||
memory_zero :: proc(data: rawptr, len: int) {
|
|
||||||
d := slice_ptr(data as ^byte, len)
|
|
||||||
for i := 0; i < len; i++ {
|
|
||||||
d[i] = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
memory_compare :: proc(dst, src: rawptr, len: int) -> int {
|
|
||||||
s1, s2: ^byte = dst, src
|
|
||||||
for i := 0; i < len; i++ {
|
|
||||||
a := ptr_offset(s1, i)^
|
|
||||||
b := ptr_offset(s2, i)^
|
|
||||||
if a != b {
|
|
||||||
return (a - b) as int
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
memory_copy :: proc(dst, src: rawptr, n: int) #inline {
|
|
||||||
if dst == src {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
v128b :: type {4}u32
|
|
||||||
compile_assert(align_of(v128b) == 16)
|
|
||||||
|
|
||||||
d, s: ^byte = dst, src
|
|
||||||
|
|
||||||
for ; s as uint % 16 != 0 && n != 0; n-- {
|
|
||||||
d^ = s^
|
|
||||||
d, s = ptr_offset(d, 1), ptr_offset(s, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if d as uint % 16 == 0 {
|
|
||||||
for ; n >= 16; d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16 {
|
|
||||||
(d as ^v128b)^ = (s as ^v128b)^
|
|
||||||
}
|
|
||||||
|
|
||||||
if n&8 != 0 {
|
|
||||||
(d as ^u64)^ = (s as ^u64)^
|
|
||||||
d, s = ptr_offset(d, 8), ptr_offset(s, 8)
|
|
||||||
}
|
|
||||||
if n&4 != 0 {
|
|
||||||
(d as ^u32)^ = (s as ^u32)^;
|
|
||||||
d, s = ptr_offset(d, 4), ptr_offset(s, 4)
|
|
||||||
}
|
|
||||||
if n&2 != 0 {
|
|
||||||
(d as ^u16)^ = (s as ^u16)^
|
|
||||||
d, s = ptr_offset(d, 2), ptr_offset(s, 2)
|
|
||||||
}
|
|
||||||
if n&1 != 0 {
|
|
||||||
d^ = s^
|
|
||||||
d, s = ptr_offset(d, 1), ptr_offset(s, 1)
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// IMPORTANT NOTE(bill): Little endian only
|
|
||||||
LS :: proc(a, b: u32) -> u32 #inline { return a << b }
|
|
||||||
RS :: proc(a, b: u32) -> u32 #inline { return a >> b }
|
|
||||||
/* NOTE(bill): Big endian version
|
|
||||||
LS :: proc(a, b: u32) -> u32 #inline { return a >> b; }
|
|
||||||
RS :: proc(a, b: u32) -> u32 #inline { return a << b; }
|
|
||||||
*/
|
|
||||||
|
|
||||||
w, x: u32
|
|
||||||
|
|
||||||
if d as uint % 4 == 1 {
|
|
||||||
w = (s as ^u32)^
|
|
||||||
d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
|
|
||||||
d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
|
|
||||||
d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
|
|
||||||
n -= 3
|
|
||||||
|
|
||||||
for n > 16 {
|
|
||||||
d32 := d as ^u32
|
|
||||||
s32 := ptr_offset(s, 1) as ^u32
|
|
||||||
x = s32^; d32^ = LS(w, 24) | RS(x, 8)
|
|
||||||
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
|
|
||||||
w = s32^; d32^ = LS(x, 24) | RS(w, 8)
|
|
||||||
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
|
|
||||||
x = s32^; d32^ = LS(w, 24) | RS(x, 8)
|
|
||||||
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
|
|
||||||
w = s32^; d32^ = LS(x, 24) | RS(w, 8)
|
|
||||||
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
|
|
||||||
|
|
||||||
d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if d as uint % 4 == 2 {
|
|
||||||
w = (s as ^u32)^
|
|
||||||
d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
|
|
||||||
d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
|
|
||||||
n -= 2
|
|
||||||
|
|
||||||
for n > 17 {
|
|
||||||
d32 := d as ^u32
|
|
||||||
s32 := ptr_offset(s, 2) as ^u32
|
|
||||||
x = s32^; d32^ = LS(w, 16) | RS(x, 16)
|
|
||||||
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
|
|
||||||
w = s32^; d32^ = LS(x, 16) | RS(w, 16)
|
|
||||||
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
|
|
||||||
x = s32^; d32^ = LS(w, 16) | RS(x, 16)
|
|
||||||
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
|
|
||||||
w = s32^; d32^ = LS(x, 16) | RS(w, 16)
|
|
||||||
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
|
|
||||||
|
|
||||||
d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if d as uint % 4 == 3 {
|
|
||||||
w = (s as ^u32)^
|
|
||||||
d^ = s^
|
|
||||||
n -= 1
|
|
||||||
|
|
||||||
for n > 18 {
|
|
||||||
d32 := d as ^u32
|
|
||||||
s32 := ptr_offset(s, 3) as ^u32
|
|
||||||
x = s32^; d32^ = LS(w, 8) | RS(x, 24)
|
|
||||||
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
|
|
||||||
w = s32^; d32^ = LS(x, 8) | RS(w, 24)
|
|
||||||
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
|
|
||||||
x = s32^; d32^ = LS(w, 8) | RS(x, 24)
|
|
||||||
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
|
|
||||||
w = s32^; d32^ = LS(x, 8) | RS(w, 24)
|
|
||||||
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
|
|
||||||
|
|
||||||
d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if n&16 != 0 {
|
|
||||||
(d as ^v128b)^ = (s as ^v128b)^
|
|
||||||
d, s = ptr_offset(d, 16), ptr_offset(s, 16)
|
|
||||||
}
|
|
||||||
if n&8 != 0 {
|
|
||||||
(d as ^u64)^ = (s as ^u64)^
|
|
||||||
d, s = ptr_offset(d, 8), ptr_offset(s, 8)
|
|
||||||
}
|
|
||||||
if n&4 != 0 {
|
|
||||||
(d as ^u32)^ = (s as ^u32)^;
|
|
||||||
d, s = ptr_offset(d, 4), ptr_offset(s, 4)
|
|
||||||
}
|
|
||||||
if n&2 != 0 {
|
|
||||||
(d as ^u16)^ = (s as ^u16)^
|
|
||||||
d, s = ptr_offset(d, 2), ptr_offset(s, 2)
|
|
||||||
}
|
|
||||||
if n&1 != 0 {
|
|
||||||
d^ = s^
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
memory_move :: proc(dst, src: rawptr, n: int) #inline {
|
|
||||||
d, s: ^byte = dst, src
|
|
||||||
if d == s {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if d >= ptr_offset(s, n) || ptr_offset(d, n) <= s {
|
|
||||||
memory_copy(d, s, n)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(bill): Vectorize the shit out of this
|
|
||||||
if d < s {
|
|
||||||
if s as int % size_of(int) == d as int % size_of(int) {
|
|
||||||
for d as int % size_of(int) != 0 {
|
|
||||||
if n == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
n--
|
|
||||||
d^ = s^
|
|
||||||
d, s = ptr_offset(d, 1), ptr_offset(s, 1)
|
|
||||||
}
|
|
||||||
di, si := d as ^int, s as ^int
|
|
||||||
for n >= size_of(int) {
|
|
||||||
di^ = si^
|
|
||||||
di, si = ptr_offset(di, 1), ptr_offset(si, 1)
|
|
||||||
n -= size_of(int)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for ; n > 0; n-- {
|
|
||||||
d^ = s^
|
|
||||||
d, s = ptr_offset(d, 1), ptr_offset(s, 1)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if s as int % size_of(int) == d as int % size_of(int) {
|
|
||||||
for ptr_offset(d, n) as int % size_of(int) != 0 {
|
|
||||||
if n == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
n--
|
|
||||||
d^ = s^
|
|
||||||
d, s = ptr_offset(d, 1), ptr_offset(s, 1)
|
|
||||||
}
|
|
||||||
for n >= size_of(int) {
|
|
||||||
n -= size_of(int)
|
|
||||||
di := ptr_offset(d, n) as ^int
|
|
||||||
si := ptr_offset(s, n) as ^int
|
|
||||||
di^ = si^
|
|
||||||
}
|
|
||||||
for ; n > 0; n-- {
|
|
||||||
d^ = s^
|
|
||||||
d, s = ptr_offset(d, 1), ptr_offset(s, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for n > 0 {
|
|
||||||
n--
|
|
||||||
dn := ptr_offset(d, n)
|
|
||||||
sn := ptr_offset(s, n)
|
|
||||||
dn^ = sn^
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__string_eq :: proc(a, b: string) -> bool {
|
|
||||||
if len(a) != len(b) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if ^a[0] == ^b[0] {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return memory_compare(^a[0], ^b[0], len(a)) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
__string_cmp :: proc(a, b : string) -> int {
|
|
||||||
min_len := len(a)
|
|
||||||
if len(b) < min_len {
|
|
||||||
min_len = len(b)
|
|
||||||
}
|
|
||||||
for i := 0; i < min_len; i++ {
|
|
||||||
x := a[i]
|
|
||||||
y := b[i]
|
|
||||||
if x < y {
|
|
||||||
return -1
|
|
||||||
} else if x > y {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(a) < len(b) {
|
|
||||||
return -1
|
|
||||||
} else if len(a) > len(b) {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
__string_ne :: proc(a, b : string) -> bool #inline { return !__string_eq(a, b) }
|
|
||||||
__string_lt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) < 0 }
|
|
||||||
__string_gt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) > 0 }
|
|
||||||
__string_le :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) <= 0 }
|
|
||||||
__string_ge :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) >= 0 }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Allocation_Mode :: type enum {
|
|
||||||
ALLOC,
|
|
||||||
DEALLOC,
|
|
||||||
DEALLOC_ALL,
|
|
||||||
RESIZE,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Allocator_Proc :: type proc(allocator_data: rawptr, mode: Allocation_Mode,
|
|
||||||
size, alignment: int,
|
|
||||||
old_memory: rawptr, old_size: int, flags: u64) -> rawptr
|
|
||||||
|
|
||||||
Allocator :: type struct {
|
|
||||||
procedure: Allocator_Proc;
|
|
||||||
data: rawptr
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Context :: type struct {
|
|
||||||
thread_ptr: rawptr
|
|
||||||
|
|
||||||
user_data: rawptr
|
|
||||||
user_index: int
|
|
||||||
|
|
||||||
allocator: Allocator
|
|
||||||
}
|
|
||||||
|
|
||||||
#thread_local context: Context
|
|
||||||
|
|
||||||
DEFAULT_ALIGNMENT :: 2*size_of(int)
|
|
||||||
|
|
||||||
|
|
||||||
__check_context :: proc() {
|
|
||||||
if context.allocator.procedure == null {
|
|
||||||
context.allocator = __default_allocator()
|
|
||||||
}
|
|
||||||
if context.thread_ptr == null {
|
|
||||||
// TODO(bill):
|
|
||||||
// context.thread_ptr = current_thread_pointer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
alloc :: proc(size: int) -> rawptr #inline { return alloc_align(size, DEFAULT_ALIGNMENT) }
|
|
||||||
|
|
||||||
alloc_align :: proc(size, alignment: int) -> rawptr #inline {
|
|
||||||
__check_context()
|
|
||||||
a := context.allocator
|
|
||||||
return a.procedure(a.data, Allocation_Mode.ALLOC, size, alignment, null, 0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
dealloc :: proc(ptr: rawptr) #inline {
|
|
||||||
__check_context()
|
|
||||||
a := context.allocator
|
|
||||||
_ = a.procedure(a.data, Allocation_Mode.DEALLOC, 0, 0, ptr, 0, 0)
|
|
||||||
}
|
|
||||||
dealloc_all :: proc(ptr: rawptr) #inline {
|
|
||||||
__check_context()
|
|
||||||
a := context.allocator
|
|
||||||
_ = a.procedure(a.data, Allocation_Mode.DEALLOC_ALL, 0, 0, ptr, 0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
resize :: proc(ptr: rawptr, old_size, new_size: int) -> rawptr #inline { return resize_align(ptr, old_size, new_size, DEFAULT_ALIGNMENT) }
|
|
||||||
resize_align :: proc(ptr: rawptr, old_size, new_size, alignment: int) -> rawptr #inline {
|
|
||||||
__check_context()
|
|
||||||
a := context.allocator
|
|
||||||
return a.procedure(a.data, Allocation_Mode.RESIZE, new_size, alignment, ptr, old_size, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int) -> rawptr {
|
|
||||||
if old_memory == null {
|
|
||||||
return alloc_align(new_size, alignment)
|
|
||||||
}
|
|
||||||
|
|
||||||
if new_size == 0 {
|
|
||||||
dealloc(old_memory)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if new_size == old_size {
|
|
||||||
return old_memory
|
|
||||||
}
|
|
||||||
|
|
||||||
new_memory := alloc_align(new_size, alignment)
|
|
||||||
if new_memory == null {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
memory_copy(new_memory, old_memory, min(old_size, new_size));
|
|
||||||
dealloc(old_memory)
|
|
||||||
return new_memory
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
__default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocation_Mode,
|
|
||||||
size, alignment: int,
|
|
||||||
old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
|
|
||||||
using Allocation_Mode
|
|
||||||
match mode {
|
|
||||||
case ALLOC:
|
|
||||||
return heap_alloc(size)
|
|
||||||
case RESIZE:
|
|
||||||
return default_resize_align(old_memory, old_size, size, alignment)
|
|
||||||
case DEALLOC:
|
|
||||||
heap_dealloc(old_memory)
|
|
||||||
case DEALLOC_ALL:
|
|
||||||
// NOTE(bill): Does nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
__default_allocator :: proc() -> Allocator {
|
|
||||||
return Allocator{
|
|
||||||
__default_allocator_proc,
|
|
||||||
null,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
__assert :: proc(msg: string) {
|
|
||||||
file_write(file_get_standard(File_Standard.ERROR), msg as []byte)
|
|
||||||
// TODO(bill): Which is better?
|
|
||||||
// __trap()
|
|
||||||
__debug_trap()
|
|
||||||
}
|
|
||||||
@@ -1,498 +0,0 @@
|
|||||||
import (
|
|
||||||
win32 "sys/windows.odin";
|
|
||||||
"fmt.odin";
|
|
||||||
"os.odin";
|
|
||||||
"mem.odin";
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
CANVAS_WIDTH = 128;
|
|
||||||
CANVAS_HEIGHT = 128;
|
|
||||||
CANVAS_SCALE = 3;
|
|
||||||
FRAME_TIME = 1.0/30.0;
|
|
||||||
WINDOW_TITLE = "Punity\x00";
|
|
||||||
)
|
|
||||||
|
|
||||||
const _ = compile_assert(CANVAS_WIDTH % 16 == 0);
|
|
||||||
|
|
||||||
const (
|
|
||||||
WINDOW_WIDTH = CANVAS_WIDTH * CANVAS_SCALE;
|
|
||||||
WINDOW_HEIGHT = CANVAS_HEIGHT * CANVAS_SCALE;
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
STACK_CAPACITY = 1<<20;
|
|
||||||
STORAGE_CAPACITY = 1<<20;
|
|
||||||
|
|
||||||
DRAW_LIST_RESERVE = 128;
|
|
||||||
|
|
||||||
MAX_KEYS = 256;
|
|
||||||
)
|
|
||||||
|
|
||||||
type Core struct {
|
|
||||||
stack: ^Bank,
|
|
||||||
storage: ^Bank,
|
|
||||||
|
|
||||||
running: bool,
|
|
||||||
key_modifiers: u32,
|
|
||||||
key_states: [MAX_KEYS]u8,
|
|
||||||
key_deltas: [MAX_KEYS]u8,
|
|
||||||
|
|
||||||
perf_frame,
|
|
||||||
perf_frame_inner,
|
|
||||||
perf_step,
|
|
||||||
perf_audio,
|
|
||||||
perf_blit,
|
|
||||||
perf_blit_cvt,
|
|
||||||
perf_blit_gdi: Perf_Span,
|
|
||||||
|
|
||||||
frame: i64,
|
|
||||||
|
|
||||||
canvas: Canvas,
|
|
||||||
draw_list: ^Draw_List,
|
|
||||||
}
|
|
||||||
|
|
||||||
type Perf_Span struct {
|
|
||||||
stamp: f64,
|
|
||||||
delta: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
type Bank struct {
|
|
||||||
memory: []u8,
|
|
||||||
cursor: int,
|
|
||||||
}
|
|
||||||
|
|
||||||
type Bank_State struct {
|
|
||||||
state: Bank,
|
|
||||||
bank: ^Bank,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
type Color raw_union {
|
|
||||||
using channels: struct{a, b, g, r: u8},
|
|
||||||
rgba: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
type Palette struct {
|
|
||||||
colors: [256]Color,
|
|
||||||
colors_count: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
type Rect raw_union {
|
|
||||||
using minmax: struct {min_x, min_y, max_x, max_y: int},
|
|
||||||
using pos: struct {left, top, right, bottom: int},
|
|
||||||
e: [4]int,
|
|
||||||
}
|
|
||||||
|
|
||||||
type Bitmap struct {
|
|
||||||
pixels: []u8,
|
|
||||||
width: int,
|
|
||||||
height: int,
|
|
||||||
}
|
|
||||||
|
|
||||||
type Font struct {
|
|
||||||
using bitmap: Bitmap,
|
|
||||||
char_width: int,
|
|
||||||
char_height: int,
|
|
||||||
}
|
|
||||||
|
|
||||||
type Canvas struct {
|
|
||||||
using bitmap: ^Bitmap,
|
|
||||||
palette: Palette,
|
|
||||||
translate_x: int,
|
|
||||||
translate_y: int,
|
|
||||||
clip: Rect,
|
|
||||||
font: ^Font,
|
|
||||||
}
|
|
||||||
|
|
||||||
type DrawFlag enum {
|
|
||||||
NONE = 0,
|
|
||||||
FLIP_H = 1<<0,
|
|
||||||
FLIP_V = 1<<1,
|
|
||||||
MASK = 1<<2,
|
|
||||||
}
|
|
||||||
|
|
||||||
type Draw_Item struct {}
|
|
||||||
type Draw_List struct {
|
|
||||||
items: []Draw_Item,
|
|
||||||
}
|
|
||||||
|
|
||||||
type Key enum {
|
|
||||||
ModShift = 0x0001,
|
|
||||||
ModControl = 0x0002,
|
|
||||||
ModAlt = 0x0004,
|
|
||||||
ModSuper = 0x0008,
|
|
||||||
|
|
||||||
|
|
||||||
Unknown =-1,
|
|
||||||
Invalid =-2,
|
|
||||||
|
|
||||||
|
|
||||||
Lbutton = 1,
|
|
||||||
Rbutton = 2,
|
|
||||||
Cancel = 3,
|
|
||||||
Mbutton = 4,
|
|
||||||
|
|
||||||
|
|
||||||
Back = 8,
|
|
||||||
Tab = 9,
|
|
||||||
Clear = 12,
|
|
||||||
Return = 13,
|
|
||||||
Shift = 16,
|
|
||||||
Control = 17,
|
|
||||||
Menu = 18,
|
|
||||||
Pause = 19,
|
|
||||||
Capital = 20,
|
|
||||||
Kana = 0x15,
|
|
||||||
Hangeul = 0x15,
|
|
||||||
Hangul = 0x15,
|
|
||||||
Junja = 0x17,
|
|
||||||
Final = 0x18,
|
|
||||||
Hanja = 0x19,
|
|
||||||
Kanji = 0x19,
|
|
||||||
Escape = 0x1B,
|
|
||||||
Convert = 0x1C,
|
|
||||||
NonConvert = 0x1D,
|
|
||||||
Accept = 0x1E,
|
|
||||||
ModeChange = 0x1F,
|
|
||||||
Space = 32,
|
|
||||||
Prior = 33,
|
|
||||||
Next = 34,
|
|
||||||
End = 35,
|
|
||||||
Home = 36,
|
|
||||||
Left = 37,
|
|
||||||
Up = 38,
|
|
||||||
Right = 39,
|
|
||||||
Down = 40,
|
|
||||||
Select = 41,
|
|
||||||
Print = 42,
|
|
||||||
Exec = 43,
|
|
||||||
Snapshot = 44,
|
|
||||||
Insert = 45,
|
|
||||||
Delete = 46,
|
|
||||||
Help = 47,
|
|
||||||
Lwin = 0x5B,
|
|
||||||
Rwin = 0x5C,
|
|
||||||
Apps = 0x5D,
|
|
||||||
Sleep = 0x5F,
|
|
||||||
Numpad0 = 0x60,
|
|
||||||
Numpad1 = 0x61,
|
|
||||||
Numpad2 = 0x62,
|
|
||||||
Numpad3 = 0x63,
|
|
||||||
Numpad4 = 0x64,
|
|
||||||
Numpad5 = 0x65,
|
|
||||||
Numpad6 = 0x66,
|
|
||||||
Numpad7 = 0x67,
|
|
||||||
Numpad8 = 0x68,
|
|
||||||
Numpad9 = 0x69,
|
|
||||||
Multiply = 0x6A,
|
|
||||||
Add = 0x6B,
|
|
||||||
Separator = 0x6C,
|
|
||||||
Subtract = 0x6D,
|
|
||||||
Decimal = 0x6E,
|
|
||||||
Divide = 0x6F,
|
|
||||||
F1 = 0x70,
|
|
||||||
F2 = 0x71,
|
|
||||||
F3 = 0x72,
|
|
||||||
F4 = 0x73,
|
|
||||||
F5 = 0x74,
|
|
||||||
F6 = 0x75,
|
|
||||||
F7 = 0x76,
|
|
||||||
F8 = 0x77,
|
|
||||||
F9 = 0x78,
|
|
||||||
F10 = 0x79,
|
|
||||||
F11 = 0x7A,
|
|
||||||
F12 = 0x7B,
|
|
||||||
F13 = 0x7C,
|
|
||||||
F14 = 0x7D,
|
|
||||||
F15 = 0x7E,
|
|
||||||
F16 = 0x7F,
|
|
||||||
F17 = 0x80,
|
|
||||||
F18 = 0x81,
|
|
||||||
F19 = 0x82,
|
|
||||||
F20 = 0x83,
|
|
||||||
F21 = 0x84,
|
|
||||||
F22 = 0x85,
|
|
||||||
F23 = 0x86,
|
|
||||||
F24 = 0x87,
|
|
||||||
Numlock = 0x90,
|
|
||||||
Scroll = 0x91,
|
|
||||||
Lshift = 0xA0,
|
|
||||||
Rshift = 0xA1,
|
|
||||||
Lcontrol = 0xA2,
|
|
||||||
Rcontrol = 0xA3,
|
|
||||||
Lmenu = 0xA4,
|
|
||||||
Rmenu = 0xA5,
|
|
||||||
|
|
||||||
|
|
||||||
Apostrophe = 39, /* ' */
|
|
||||||
Comma = 44, /* , */
|
|
||||||
Minus = 45, /* - */
|
|
||||||
Period = 46, /* . */
|
|
||||||
Slash = 47, /* / */
|
|
||||||
Num0 = 48,
|
|
||||||
Num1 = 49,
|
|
||||||
Num2 = 50,
|
|
||||||
Num3 = 51,
|
|
||||||
Num4 = 52,
|
|
||||||
Num5 = 53,
|
|
||||||
Num6 = 54,
|
|
||||||
Num7 = 55,
|
|
||||||
Num8 = 56,
|
|
||||||
Num9 = 57,
|
|
||||||
Semicolon = 59, /* ; */
|
|
||||||
Equal = 61, /* = */
|
|
||||||
A = 65,
|
|
||||||
B = 66,
|
|
||||||
C = 67,
|
|
||||||
D = 68,
|
|
||||||
E = 69,
|
|
||||||
F = 70,
|
|
||||||
G = 71,
|
|
||||||
H = 72,
|
|
||||||
I = 73,
|
|
||||||
J = 74,
|
|
||||||
K = 75,
|
|
||||||
L = 76,
|
|
||||||
M = 77,
|
|
||||||
N = 78,
|
|
||||||
O = 79,
|
|
||||||
P = 80,
|
|
||||||
Q = 81,
|
|
||||||
R = 82,
|
|
||||||
S = 83,
|
|
||||||
T = 84,
|
|
||||||
U = 85,
|
|
||||||
V = 86,
|
|
||||||
W = 87,
|
|
||||||
X = 88,
|
|
||||||
Y = 89,
|
|
||||||
Z = 90,
|
|
||||||
LeftBracket = 91, /* [ */
|
|
||||||
Backslash = 92, /* \ */
|
|
||||||
RightBracket = 93, /* ] */
|
|
||||||
GraveAccent = 96, /* ` */
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
proc key_down(k: Key) -> bool {
|
|
||||||
return _core.key_states[k] != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
proc key_pressed(k: Key) -> bool {
|
|
||||||
return (_core.key_deltas[k] != 0) && key_down(k);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let win32_perf_count_freq = win32.get_query_performance_frequency();
|
|
||||||
proc time_now() -> f64 {
|
|
||||||
assert(win32_perf_count_freq != 0);
|
|
||||||
|
|
||||||
var counter: i64;
|
|
||||||
win32.query_performance_counter(&counter);
|
|
||||||
return f64(counter) / f64(win32_perf_count_freq);
|
|
||||||
}
|
|
||||||
|
|
||||||
var _core: Core;
|
|
||||||
|
|
||||||
proc run(user_init, user_step: proc(c: ^Core)) {
|
|
||||||
using win32;
|
|
||||||
|
|
||||||
_core.running = true;
|
|
||||||
|
|
||||||
proc win32_proc(hwnd: win32.Hwnd, msg: u32, wparam: win32.Wparam, lparam: win32.Lparam) -> win32.Lresult #no_inline #cc_c {
|
|
||||||
proc win32_app_key_mods() -> u32 {
|
|
||||||
var mods: u32 = 0;
|
|
||||||
|
|
||||||
if is_key_down(KeyCode.Shift) {
|
|
||||||
mods |= u32(Key.ModShift);
|
|
||||||
}
|
|
||||||
if is_key_down(KeyCode.Control) {
|
|
||||||
mods |= u32(Key.ModControl);
|
|
||||||
}
|
|
||||||
if is_key_down(KeyCode.Menu) {
|
|
||||||
mods |= u32(Key.ModAlt);
|
|
||||||
}
|
|
||||||
if is_key_down(KeyCode.Lwin) || is_key_down(KeyCode.Rwin) {
|
|
||||||
mods |= u32(Key.ModSuper);
|
|
||||||
}
|
|
||||||
|
|
||||||
return mods;
|
|
||||||
}
|
|
||||||
|
|
||||||
match msg {
|
|
||||||
case WM_KEYDOWN:
|
|
||||||
_core.key_modifiers = win32_app_key_mods();
|
|
||||||
if wparam < MAX_KEYS {
|
|
||||||
_core.key_states[wparam] = 1;
|
|
||||||
_core.key_deltas[wparam] = 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
case WM_KEYUP:
|
|
||||||
_core.key_modifiers = win32_app_key_mods();
|
|
||||||
if wparam < MAX_KEYS {
|
|
||||||
_core.key_states[wparam] = 0;
|
|
||||||
_core.key_deltas[wparam] = 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
case WM_CLOSE:
|
|
||||||
post_quit_message(0);
|
|
||||||
_core.running = false;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return def_window_proc_a(hwnd, msg, wparam, lparam);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var class_name = "Punity\x00";
|
|
||||||
var window_class = WndClassExA{
|
|
||||||
class_name = &class_name[0],
|
|
||||||
size = size_of(WndClassExA),
|
|
||||||
style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
|
|
||||||
instance = Hinstance(get_module_handle_a(nil)),
|
|
||||||
wnd_proc = win32_proc,
|
|
||||||
// wnd_proc = DefWindowProcA,
|
|
||||||
background = Hbrush(get_stock_object(BLACK_BRUSH)),
|
|
||||||
};
|
|
||||||
|
|
||||||
if register_class_ex_a(&window_class) == 0 {
|
|
||||||
fmt.fprintln(os.stderr, "register_class_ex_a failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var screen_width = get_system_metrics(SM_CXSCREEN);
|
|
||||||
var screen_height = get_system_metrics(SM_CYSCREEN);
|
|
||||||
|
|
||||||
var rc: Rect;
|
|
||||||
rc.left = (screen_width - WINDOW_WIDTH) / 2;
|
|
||||||
rc.top = (screen_height - WINDOW_HEIGHT) / 2;
|
|
||||||
rc.right = rc.left + WINDOW_WIDTH;
|
|
||||||
rc.bottom = rc.top + WINDOW_HEIGHT;
|
|
||||||
|
|
||||||
var style: u32 = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
|
|
||||||
assert(adjust_window_rect(&rc, style, 0) != 0);
|
|
||||||
|
|
||||||
var wt = WINDOW_TITLE;
|
|
||||||
|
|
||||||
var win32_window = create_window_ex_a(0,
|
|
||||||
window_class.class_name,
|
|
||||||
&wt[0],
|
|
||||||
style,
|
|
||||||
rc.left, rc.top,
|
|
||||||
rc.right-rc.left, rc.bottom-rc.top,
|
|
||||||
nil, nil, window_class.instance,
|
|
||||||
nil);
|
|
||||||
|
|
||||||
if win32_window == nil {
|
|
||||||
fmt.fprintln(os.stderr, "create_window_ex_a failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var window_bmi: BitmapInfo;
|
|
||||||
window_bmi.size = size_of(BitmapInfoHeader);
|
|
||||||
window_bmi.width = CANVAS_WIDTH;
|
|
||||||
window_bmi.height = CANVAS_HEIGHT;
|
|
||||||
window_bmi.planes = 1;
|
|
||||||
window_bmi.bit_count = 32;
|
|
||||||
window_bmi.compression = BI_RGB;
|
|
||||||
|
|
||||||
|
|
||||||
user_init(&_core);
|
|
||||||
|
|
||||||
show_window(win32_window, SW_SHOW);
|
|
||||||
|
|
||||||
var window_buffer = make([]u32, CANVAS_WIDTH * CANVAS_HEIGHT);
|
|
||||||
defer free(window_buffer);
|
|
||||||
|
|
||||||
for _, i in window_buffer {
|
|
||||||
window_buffer[i] = 0xff00ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
dt: f64;
|
|
||||||
prev_time = time_now();
|
|
||||||
curr_time = time_now();
|
|
||||||
total_time : f64 = 0;
|
|
||||||
offset_x = 0;
|
|
||||||
offset_y = 0;
|
|
||||||
)
|
|
||||||
|
|
||||||
var message: Msg;
|
|
||||||
for _core.running {
|
|
||||||
curr_time = time_now();
|
|
||||||
dt = curr_time - prev_time;
|
|
||||||
prev_time = curr_time;
|
|
||||||
total_time += dt;
|
|
||||||
|
|
||||||
offset_x += 1;
|
|
||||||
offset_y += 2;
|
|
||||||
|
|
||||||
{
|
|
||||||
var buf: [128]u8;
|
|
||||||
var s = fmt.bprintf(buf[..], "Punity: %.4f ms\x00", dt*1000);
|
|
||||||
win32.set_window_text_a(win32_window, &s[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for var y = 0; y < CANVAS_HEIGHT; y++ {
|
|
||||||
for var x = 0; x < CANVAS_WIDTH; x++ {
|
|
||||||
var g = (x % 32) * 8;
|
|
||||||
var b = (y % 32) * 8;
|
|
||||||
window_buffer[x + y*CANVAS_WIDTH] = u32(g << 8 | b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mem.zero(&_core.key_deltas[0], size_of(_core.key_deltas));
|
|
||||||
|
|
||||||
for peek_message_a(&message, nil, 0, 0, PM_REMOVE) != 0 {
|
|
||||||
if message.message == WM_QUIT {
|
|
||||||
_core.running = false;
|
|
||||||
}
|
|
||||||
translate_message(&message);
|
|
||||||
dispatch_message_a(&message);
|
|
||||||
}
|
|
||||||
|
|
||||||
user_step(&_core);
|
|
||||||
|
|
||||||
var dc = get_dc(win32_window);
|
|
||||||
stretch_dibits(dc,
|
|
||||||
0, 0, CANVAS_WIDTH * CANVAS_SCALE, CANVAS_HEIGHT * CANVAS_SCALE,
|
|
||||||
0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
|
|
||||||
&window_buffer[0],
|
|
||||||
&window_bmi,
|
|
||||||
DIB_RGB_COLORS,
|
|
||||||
SRCCOPY);
|
|
||||||
release_dc(win32_window, dc);
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
var delta = time_now() - prev_time;
|
|
||||||
var ms = i32((FRAME_TIME - delta) * 1000);
|
|
||||||
if ms > 0 {
|
|
||||||
win32.sleep(ms);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_core.frame++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
proc main() {
|
|
||||||
proc user_init(c: ^Core) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
proc user_step(c: ^Core) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
run(user_init, user_step);
|
|
||||||
}
|
|
||||||
+5
-6
@@ -1,11 +1,10 @@
|
|||||||
#shared_global_scope;
|
#shared_global_scope;
|
||||||
|
|
||||||
import (
|
import "os.odin";
|
||||||
"os.odin";
|
import "fmt.odin"; // TODO(bill): Remove the need for `fmt` here
|
||||||
"fmt.odin"; // TODO(bill): Remove the need for `fmt` here
|
import "utf8.odin";
|
||||||
"utf8.odin";
|
import "raw.odin";
|
||||||
"raw.odin";
|
|
||||||
)
|
|
||||||
// Naming Conventions:
|
// Naming Conventions:
|
||||||
// In general, Ada_Case for types and snake_case for values
|
// In general, Ada_Case for types and snake_case for values
|
||||||
//
|
//
|
||||||
|
|||||||
+6
-8
@@ -1,11 +1,9 @@
|
|||||||
import (
|
import "os.odin";
|
||||||
"os.odin";
|
import "mem.odin";
|
||||||
"mem.odin";
|
import "utf8.odin";
|
||||||
"utf8.odin";
|
import "types.odin";
|
||||||
"types.odin";
|
import "strconv.odin";
|
||||||
"strconv.odin";
|
import "raw.odin";
|
||||||
"raw.odin";
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
_BUFFER_SIZE :: 1<<12;
|
_BUFFER_SIZE :: 1<<12;
|
||||||
|
|||||||
+4
-5
@@ -1,8 +1,7 @@
|
|||||||
import (
|
import "fmt.odin";
|
||||||
"fmt.odin";
|
import "os.odin";
|
||||||
"os.odin";
|
import "raw.odin";
|
||||||
"raw.odin";
|
|
||||||
)
|
|
||||||
foreign __llvm_core {
|
foreign __llvm_core {
|
||||||
swap :: proc(b: u16) -> u16 #link_name "llvm.bswap.i16" ---;
|
swap :: proc(b: u16) -> u16 #link_name "llvm.bswap.i16" ---;
|
||||||
swap :: proc(b: u32) -> u32 #link_name "llvm.bswap.i32" ---;
|
swap :: proc(b: u32) -> u32 #link_name "llvm.bswap.i32" ---;
|
||||||
|
|||||||
+3
-5
@@ -2,11 +2,9 @@ foreign_system_library (
|
|||||||
lib "opengl32.lib" when ODIN_OS == "windows";
|
lib "opengl32.lib" when ODIN_OS == "windows";
|
||||||
lib "gl" when ODIN_OS == "linux";
|
lib "gl" when ODIN_OS == "linux";
|
||||||
)
|
)
|
||||||
import (
|
import win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||||
win32 "sys/windows.odin" when ODIN_OS == "windows";
|
import "sys/wgl.odin" when ODIN_OS == "windows";
|
||||||
"sys/wgl.odin" when ODIN_OS == "windows";
|
using import . "opengl_constants.odin";
|
||||||
)
|
|
||||||
import_load "opengl_constants.odin";
|
|
||||||
|
|
||||||
_ := compile_assert(ODIN_OS != "osx");
|
_ := compile_assert(ODIN_OS != "osx");
|
||||||
|
|
||||||
|
|||||||
+3
-5
@@ -1,8 +1,6 @@
|
|||||||
import_load (
|
using import . "os_windows.odin" when ODIN_OS == "windows";
|
||||||
"os_windows.odin" when ODIN_OS == "windows";
|
using import . "os_x.odin" when ODIN_OS == "osx";
|
||||||
"os_x.odin" when ODIN_OS == "osx";
|
using import . "os_linux.odin" when ODIN_OS == "linux";
|
||||||
"os_linux.odin" when ODIN_OS == "linux";
|
|
||||||
)
|
|
||||||
|
|
||||||
write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
|
write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
|
||||||
return write(fd, cast([]u8)str);
|
return write(fd, cast([]u8)str);
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import . "decimal.odin";
|
using import "decimal.odin";
|
||||||
|
|
||||||
Int_Flag :: enum {
|
Int_Flag :: enum {
|
||||||
Prefix = 1<<0,
|
Prefix = 1<<0,
|
||||||
|
|||||||
+2
-4
@@ -1,4 +1,2 @@
|
|||||||
import_load (
|
using import . "sync_windows.odin" when ODIN_OS == "windows";
|
||||||
"sync_windows.odin" when ODIN_OS == "windows";
|
using import . "sync_linux.odin" when ODIN_OS == "linux";
|
||||||
"sync_linux.odin" when ODIN_OS == "linux";
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import (
|
import "atomics.odin";
|
||||||
"atomics.odin";
|
import "os.odin";
|
||||||
"os.odin";
|
|
||||||
)
|
|
||||||
|
|
||||||
Semaphore :: struct {
|
Semaphore :: struct {
|
||||||
// _handle: win32.Handle;
|
// _handle: win32.Handle;
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import (
|
import win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||||
win32 "sys/windows.odin" when ODIN_OS == "windows";
|
import "atomics.odin";
|
||||||
"atomics.odin";
|
|
||||||
)
|
|
||||||
|
|
||||||
Semaphore :: struct {
|
Semaphore :: struct {
|
||||||
_handle: win32.Handle;
|
_handle: win32.Handle;
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
foreign_system_library "opengl32.lib" when ODIN_OS == "windows";
|
foreign_system_library "opengl32.lib" when ODIN_OS == "windows";
|
||||||
import . "windows.odin";
|
using import "windows.odin";
|
||||||
|
|
||||||
|
|
||||||
CONTEXT_MAJOR_VERSION_ARB :: 0x2091;
|
CONTEXT_MAJOR_VERSION_ARB :: 0x2091;
|
||||||
|
|||||||
@@ -457,7 +457,7 @@ foreign gdi32 {
|
|||||||
stretch_dibits :: proc(hdc: Hdc,
|
stretch_dibits :: proc(hdc: Hdc,
|
||||||
x_dst, y_dst, width_dst, height_dst: i32,
|
x_dst, y_dst, width_dst, height_dst: i32,
|
||||||
x_src, y_src, width_src, header_src: i32,
|
x_src, y_src, width_src, header_src: i32,
|
||||||
bits: rawptr, bits_info: ^BitmapInfo,
|
bits: rawptr, bits_info: ^Bitmap_Info,
|
||||||
usage: u32,
|
usage: u32,
|
||||||
rop: u32) -> i32 #cc_std #link_name "StretchDIBits" ---;
|
rop: u32) -> i32 #cc_std #link_name "StretchDIBits" ---;
|
||||||
|
|
||||||
@@ -572,7 +572,7 @@ Bitmap_Info_Header :: struct #ordered {
|
|||||||
clr_used: u32;
|
clr_used: u32;
|
||||||
clr_important: u32;
|
clr_important: u32;
|
||||||
}
|
}
|
||||||
BitmapInfo :: struct #ordered {
|
Bitmap_Info :: struct #ordered {
|
||||||
using header: Bitmap_Info_Header;
|
using header: Bitmap_Info_Header;
|
||||||
colors: [1]Rgb_Quad;
|
colors: [1]Rgb_Quad;
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-42
@@ -1942,27 +1942,21 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
|
|||||||
}
|
}
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
|
case_ast_node(id, ImportDecl, decl);
|
||||||
|
if (!c->context.scope->is_file) {
|
||||||
|
error(decl, "import declarations are only allowed in the file scope");
|
||||||
|
// NOTE(bill): _Should_ be caught by the parser
|
||||||
|
// TODO(bill): Better error handling if it isn't
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
DelayedDecl di = {c->context.scope, decl};
|
||||||
|
array_add(&c->delayed_imports, di);
|
||||||
|
case_end;
|
||||||
|
|
||||||
case_ast_node(gd, GenDecl, decl);
|
case_ast_node(gd, GenDecl, decl);
|
||||||
for_array(i, gd->specs) {
|
for_array(i, gd->specs) {
|
||||||
AstNode *spec = gd->specs[i];
|
AstNode *spec = gd->specs[i];
|
||||||
switch (gd->token.kind) {
|
switch (gd->token.kind) {
|
||||||
case Token_import:
|
|
||||||
case Token_import_load: {
|
|
||||||
ast_node(ts, ImportSpec, spec);
|
|
||||||
if (!c->context.scope->is_file) {
|
|
||||||
if (ts->is_import) {
|
|
||||||
error(decl, "import declarations are only allowed in the file scope");
|
|
||||||
} else {
|
|
||||||
error(decl, "import_load declarations are only allowed in the file scope");
|
|
||||||
}
|
|
||||||
// NOTE(bill): _Should_ be caught by the parser
|
|
||||||
// TODO(bill): Better error handling if it isn't
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
DelayedDecl di = {c->context.scope, spec};
|
|
||||||
array_add(&c->delayed_imports, di);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case Token_foreign_library:
|
case Token_foreign_library:
|
||||||
case Token_foreign_system_library: {
|
case Token_foreign_system_library: {
|
||||||
ast_node(fl, ForeignLibrarySpec, spec);
|
ast_node(fl, ForeignLibrarySpec, spec);
|
||||||
@@ -2202,7 +2196,7 @@ void import_graph_node_set_remove(ImportGraphNodeSet *s, ImportGraphNode *n) {
|
|||||||
|
|
||||||
struct ImportGraphNode {
|
struct ImportGraphNode {
|
||||||
Scope * scope;
|
Scope * scope;
|
||||||
Array<AstNode *> specs;
|
Array<AstNode *> decls; // AstNodeImportDecl *
|
||||||
String path;
|
String path;
|
||||||
isize file_id;
|
isize file_id;
|
||||||
ImportGraphNodeSet pred;
|
ImportGraphNodeSet pred;
|
||||||
@@ -2216,7 +2210,7 @@ ImportGraphNode *import_graph_node_create(gbAllocator a, Scope *scope) {
|
|||||||
n->scope = scope;
|
n->scope = scope;
|
||||||
n->path = scope->file->tokenizer.fullpath;
|
n->path = scope->file->tokenizer.fullpath;
|
||||||
n->file_id = scope->file->id;
|
n->file_id = scope->file->id;
|
||||||
array_init(&n->specs, heap_allocator());
|
array_init(&n->decls, heap_allocator());
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2224,6 +2218,7 @@ ImportGraphNode *import_graph_node_create(gbAllocator a, Scope *scope) {
|
|||||||
void import_graph_node_destroy(ImportGraphNode *n, gbAllocator a) {
|
void import_graph_node_destroy(ImportGraphNode *n, gbAllocator a) {
|
||||||
import_graph_node_set_destroy(&n->pred);
|
import_graph_node_set_destroy(&n->pred);
|
||||||
import_graph_node_set_destroy(&n->succ);
|
import_graph_node_set_destroy(&n->succ);
|
||||||
|
array_free(&n->decls);
|
||||||
gb_free(a, n);
|
gb_free(a, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2282,7 +2277,7 @@ Array<ImportGraphNode *> generate_import_dependency_graph(Checker *c, Map<Scope
|
|||||||
AstNode *decl = c->delayed_imports[i].decl;
|
AstNode *decl = c->delayed_imports[i].decl;
|
||||||
GB_ASSERT(parent->is_file);
|
GB_ASSERT(parent->is_file);
|
||||||
|
|
||||||
ast_node(id, ImportSpec, decl);
|
ast_node(id, ImportDecl, decl);
|
||||||
|
|
||||||
String path = id->fullpath;
|
String path = id->fullpath;
|
||||||
HashKey key = hash_string(path);
|
HashKey key = hash_string(path);
|
||||||
@@ -2310,10 +2305,9 @@ Array<ImportGraphNode *> generate_import_dependency_graph(Checker *c, Map<Scope
|
|||||||
GB_ASSERT(found_node != nullptr);
|
GB_ASSERT(found_node != nullptr);
|
||||||
n = *found_node;
|
n = *found_node;
|
||||||
|
|
||||||
array_add(&m->specs, decl);
|
array_add(&m->decls, decl);
|
||||||
|
|
||||||
bool is_dot_or_load = id->import_name.string == ".";
|
if (id->is_using) {
|
||||||
if (is_dot_or_load) {
|
|
||||||
import_graph_node_set_add(&n->pred, m);
|
import_graph_node_set_add(&n->pred, m);
|
||||||
import_graph_node_set_add(&m->succ, n);
|
import_graph_node_set_add(&m->succ, n);
|
||||||
ptr_set_add(&m->scope->imported, n->scope);
|
ptr_set_add(&m->scope->imported, n->scope);
|
||||||
@@ -2326,7 +2320,7 @@ Array<ImportGraphNode *> generate_import_dependency_graph(Checker *c, Map<Scope
|
|||||||
for_array(i, M.entries) {
|
for_array(i, M.entries) {
|
||||||
auto *entry = &M.entries[i];
|
auto *entry = &M.entries[i];
|
||||||
ImportGraphNode *n = entry->value;
|
ImportGraphNode *n = entry->value;
|
||||||
gb_sort_array(n->specs.data, n->specs.count, ast_node_cmp);
|
gb_sort_array(n->decls.data, n->decls.count, ast_node_cmp);
|
||||||
array_add(&G, n);
|
array_add(&G, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2340,7 +2334,7 @@ Array<ImportGraphNode *> generate_import_dependency_graph(Checker *c, Map<Scope
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Array<Scope *> find_import_path(Map<Scope *> *map, Scope *start, Scope *end, PtrSet<Scope *> *visited = nullptr) {
|
Array<Scope *> find_import_path(Map<Scope *> *file_scopes, Scope *start, Scope *end, PtrSet<Scope *> *visited = nullptr) {
|
||||||
PtrSet<Scope *> visited_ = {};
|
PtrSet<Scope *> visited_ = {};
|
||||||
bool made_visited = false;
|
bool made_visited = false;
|
||||||
if (visited == nullptr) {
|
if (visited == nullptr) {
|
||||||
@@ -2361,7 +2355,7 @@ Array<Scope *> find_import_path(Map<Scope *> *map, Scope *start, Scope *end, Ptr
|
|||||||
|
|
||||||
String path = start->file->tokenizer.fullpath;
|
String path = start->file->tokenizer.fullpath;
|
||||||
HashKey key = hash_string(path);
|
HashKey key = hash_string(path);
|
||||||
Scope **found = map_get(map, key);
|
Scope **found = map_get(file_scopes, key);
|
||||||
if (found) {
|
if (found) {
|
||||||
Scope *scope = *found;
|
Scope *scope = *found;
|
||||||
for_array(i, scope->imported.entries) {
|
for_array(i, scope->imported.entries) {
|
||||||
@@ -2372,7 +2366,7 @@ Array<Scope *> find_import_path(Map<Scope *> *map, Scope *start, Scope *end, Ptr
|
|||||||
array_add(&path, dep);
|
array_add(&path, dep);
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
Array<Scope *> next_path = find_import_path(map, dep, end, visited);
|
Array<Scope *> next_path = find_import_path(file_scopes, dep, end, visited);
|
||||||
if (next_path.count > 0) {
|
if (next_path.count > 0) {
|
||||||
array_add(&next_path, dep);
|
array_add(&next_path, dep);
|
||||||
return next_path;
|
return next_path;
|
||||||
@@ -2412,6 +2406,7 @@ void check_import_entities(Checker *c, Map<Scope *> *file_scopes) {
|
|||||||
defer (array_free(&path));
|
defer (array_free(&path));
|
||||||
|
|
||||||
if (path.count > 0) {
|
if (path.count > 0) {
|
||||||
|
// TODO(bill): This needs better TokenPos finding
|
||||||
auto const mt = [](Scope *s) -> Token {
|
auto const mt = [](Scope *s) -> Token {
|
||||||
Token token = {};
|
Token token = {};
|
||||||
token.pos = token_pos(s->file->tokenizer.fullpath, 1, 1);
|
token.pos = token_pos(s->file->tokenizer.fullpath, 1, 1);
|
||||||
@@ -2453,9 +2448,9 @@ void check_import_entities(Checker *c, Map<Scope *> *file_scopes) {
|
|||||||
for_array(file_index, file_order) {
|
for_array(file_index, file_order) {
|
||||||
ImportGraphNode *node = file_order[file_index];
|
ImportGraphNode *node = file_order[file_index];
|
||||||
Scope *parent_scope = node->scope;
|
Scope *parent_scope = node->scope;
|
||||||
for_array(i, node->specs) {
|
for_array(i, node->decls) {
|
||||||
AstNode *spec = node->specs[i];
|
AstNode *decl = node->decls[i];
|
||||||
ast_node(id, ImportSpec, spec);
|
ast_node(id, ImportDecl, decl);
|
||||||
Token token = id->relpath;
|
Token token = id->relpath;
|
||||||
|
|
||||||
GB_ASSERT(parent_scope->is_file);
|
GB_ASSERT(parent_scope->is_file);
|
||||||
@@ -2496,34 +2491,30 @@ void check_import_entities(Checker *c, Map<Scope *> *file_scopes) {
|
|||||||
|
|
||||||
scope->has_been_imported = true;
|
scope->has_been_imported = true;
|
||||||
|
|
||||||
if (id->import_name.string == ".") {
|
if (id->is_using) {
|
||||||
if (parent_scope->is_global) {
|
if (parent_scope->is_global) {
|
||||||
error(id->import_name, "#shared_global_scope imports cannot use .");
|
error(id->import_name, "#shared_global_scope imports cannot use using");
|
||||||
} else {
|
} else {
|
||||||
// NOTE(bill): Add imported entities to this file's scope
|
// NOTE(bill): Add imported entities to this file's scope
|
||||||
for_array(elem_index, scope->elements.entries) {
|
for_array(elem_index, scope->elements.entries) {
|
||||||
Entity *e = scope->elements.entries[elem_index].value;
|
Entity *e = scope->elements.entries[elem_index].value;
|
||||||
if (e->scope == parent_scope) {
|
if (e->scope == parent_scope) continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_entity_kind_exported(e->kind)) {
|
if (!is_entity_kind_exported(e->kind)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (id->is_import) {
|
if (id->import_name.string == ".") {
|
||||||
|
add_entity(c, parent_scope, e->identifier, e);
|
||||||
|
} else {
|
||||||
if (is_entity_exported(e)) {
|
if (is_entity_exported(e)) {
|
||||||
// TODO(bill): Should these entities be imported but cause an error when used?
|
// TODO(bill): Should these entities be imported but cause an error when used?
|
||||||
bool ok = add_entity(c, parent_scope, e->identifier, e);
|
bool ok = add_entity(c, parent_scope, e->identifier, e);
|
||||||
if (ok) {
|
if (ok) map_set(&parent_scope->implicit, hash_entity(e), true);
|
||||||
map_set(&parent_scope->implicit, hash_entity(e), true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
add_entity(c, parent_scope, e->identifier, e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if (id->import_name.string != ".") {
|
||||||
String import_name = path_to_entity_name(id->import_name.string, id->fullpath);
|
String import_name = path_to_entity_name(id->import_name.string, id->fullpath);
|
||||||
if (is_blank_ident(import_name)) {
|
if (is_blank_ident(import_name)) {
|
||||||
error(token, "File name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string));
|
error(token, "File name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string));
|
||||||
|
|||||||
@@ -96,7 +96,6 @@ void print_declaration(AstNode *decl) {
|
|||||||
AstNode *spec = gd->specs[spec_index];
|
AstNode *spec = gd->specs[spec_index];
|
||||||
switch(gd->token.kind) {
|
switch(gd->token.kind) {
|
||||||
case Token_import:
|
case Token_import:
|
||||||
case Token_import_load:
|
|
||||||
break;
|
break;
|
||||||
case Token_foreign_library:
|
case Token_foreign_library:
|
||||||
case Token_foreign_system_library:
|
case Token_foreign_system_library:
|
||||||
|
|||||||
+145
-115
@@ -20,8 +20,8 @@ struct CommentGroup {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
enum ImportedFileKind {
|
enum ImportedFileKind
|
||||||
ImportedFile_Normal,
|
{ ImportedFile_Normal,
|
||||||
ImportedFile_Shared,
|
ImportedFile_Shared,
|
||||||
ImportedFile_Init,
|
ImportedFile_Init,
|
||||||
};
|
};
|
||||||
@@ -352,8 +352,9 @@ AST_NODE_KIND(_DeclBegin, "", i32) \
|
|||||||
CommentGroup docs; \
|
CommentGroup docs; \
|
||||||
CommentGroup comment; \
|
CommentGroup comment; \
|
||||||
}) \
|
}) \
|
||||||
AST_NODE_KIND(ImportSpec, "import specification", struct { \
|
AST_NODE_KIND(ImportDecl, "import declaration", struct { \
|
||||||
bool is_import; \
|
Token token; \
|
||||||
|
bool is_using; \
|
||||||
Token relpath; \
|
Token relpath; \
|
||||||
String fullpath; \
|
String fullpath; \
|
||||||
Token import_name; \
|
Token import_name; \
|
||||||
@@ -582,7 +583,7 @@ Token ast_node_token(AstNode *node) {
|
|||||||
|
|
||||||
case AstNode_GenDecl: return node->GenDecl.token;
|
case AstNode_GenDecl: return node->GenDecl.token;
|
||||||
case AstNode_ValueDecl: return ast_node_token(node->ValueDecl.names[0]);
|
case AstNode_ValueDecl: return ast_node_token(node->ValueDecl.names[0]);
|
||||||
case AstNode_ImportSpec: return node->ImportSpec.import_name;
|
case AstNode_ImportDecl: return node->ImportDecl.token;
|
||||||
|
|
||||||
case AstNode_ForeignBlockDecl: return node->ForeignBlockDecl.token;
|
case AstNode_ForeignBlockDecl: return node->ForeignBlockDecl.token;
|
||||||
|
|
||||||
@@ -1547,15 +1548,16 @@ AstNode *ast_value_decl(AstFile *f, Array<AstNode *> names, AstNode *type, Array
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
AstNode *ast_import_spec(AstFile *f, bool is_import, Token relpath, Token import_name, AstNode *cond,
|
AstNode *ast_import_decl(AstFile *f, Token token, bool is_using, Token relpath, Token import_name, AstNode *cond,
|
||||||
CommentGroup docs, CommentGroup comment) {
|
CommentGroup docs, CommentGroup comment) {
|
||||||
AstNode *result = make_ast_node(f, AstNode_ImportSpec);
|
AstNode *result = make_ast_node(f, AstNode_ImportDecl);
|
||||||
result->ImportSpec.is_import = is_import;
|
result->ImportDecl.token = token;
|
||||||
result->ImportSpec.relpath = relpath;
|
result->ImportDecl.is_using = is_using;
|
||||||
result->ImportSpec.import_name = import_name;
|
result->ImportDecl.relpath = relpath;
|
||||||
result->ImportSpec.cond = cond;
|
result->ImportDecl.import_name = import_name;
|
||||||
result->ImportSpec.docs = docs;
|
result->ImportDecl.cond = cond;
|
||||||
result->ImportSpec.comment = comment;
|
result->ImportDecl.docs = docs;
|
||||||
|
result->ImportDecl.comment = comment;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1899,7 +1901,6 @@ void expect_semicolon(AstFile *f, AstNode *s) {
|
|||||||
if (s->kind == AstNode_GenDecl) {
|
if (s->kind == AstNode_GenDecl) {
|
||||||
switch (s->GenDecl.token.kind) {
|
switch (s->GenDecl.token.kind) {
|
||||||
case Token_import:
|
case Token_import:
|
||||||
case Token_import_load:
|
|
||||||
node_string = str_lit("import declaration");
|
node_string = str_lit("import declaration");
|
||||||
break;
|
break;
|
||||||
case Token_foreign_library:
|
case Token_foreign_library:
|
||||||
@@ -3060,61 +3061,61 @@ AstNode *parse_gen_decl(AstFile *f, Token token, ParseSpecFunc *func) {
|
|||||||
return ast_gen_decl(f, token, open, close, specs, docs);
|
return ast_gen_decl(f, token, open, close, specs, docs);
|
||||||
}
|
}
|
||||||
|
|
||||||
PARSE_SPEC_FUNC(parse_import_spec) {
|
// PARSE_SPEC_FUNC(parse_import_spec) {
|
||||||
AstNode *spec = nullptr;
|
// AstNode *spec = nullptr;
|
||||||
if (token.kind == Token_import) {
|
// if (token.kind == Token_import) {
|
||||||
AstNode *cond = nullptr;
|
// AstNode *cond = nullptr;
|
||||||
Token import_name = {};
|
// Token import_name = {};
|
||||||
|
|
||||||
switch (f->curr_token.kind) {
|
// switch (f->curr_token.kind) {
|
||||||
case Token_Period:
|
// case Token_Period:
|
||||||
import_name = advance_token(f);
|
// import_name = advance_token(f);
|
||||||
import_name.kind = Token_Ident;
|
// import_name.kind = Token_Ident;
|
||||||
break;
|
// break;
|
||||||
case Token_Ident:
|
// case Token_Ident:
|
||||||
import_name = advance_token(f);
|
// import_name = advance_token(f);
|
||||||
break;
|
// break;
|
||||||
default:
|
// default:
|
||||||
import_name.pos = f->curr_token.pos;
|
// import_name.pos = f->curr_token.pos;
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (is_blank_ident(import_name)) {
|
// if (is_blank_ident(import_name)) {
|
||||||
syntax_error(import_name, "Illegal import name: `_`");
|
// syntax_error(import_name, "Illegal import name: `_`");
|
||||||
}
|
// }
|
||||||
|
|
||||||
Token file_path = expect_token_after(f, Token_String, "import");
|
// Token file_path = expect_token_after(f, Token_String, "import");
|
||||||
if (allow_token(f, Token_when)) {
|
// if (allow_token(f, Token_when)) {
|
||||||
cond = parse_expr(f, false);
|
// cond = parse_expr(f, false);
|
||||||
}
|
// }
|
||||||
|
|
||||||
expect_semicolon(f, nullptr);
|
// expect_semicolon(f, nullptr);
|
||||||
if (f->curr_proc != nullptr) {
|
// if (f->curr_proc != nullptr) {
|
||||||
syntax_error(import_name, "You cannot use `import` within a procedure. This must be done at the file scope");
|
// syntax_error(import_name, "You cannot use `import` within a procedure. This must be done at the file scope");
|
||||||
spec = ast_bad_decl(f, import_name, file_path);
|
// spec = ast_bad_decl(f, import_name, file_path);
|
||||||
} else {
|
// } else {
|
||||||
spec = ast_import_spec(f, true, file_path, import_name, cond, docs, f->line_comment);
|
// spec = ast_import_decl(f, true, file_path, import_name, cond, docs, f->line_comment);
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
AstNode *cond = nullptr;
|
// AstNode *cond = nullptr;
|
||||||
Token file_path = expect_token_after(f, Token_String, "import_load");
|
// Token file_path = expect_token_after(f, Token_String, "import_load");
|
||||||
Token import_name = file_path;
|
// Token import_name = file_path;
|
||||||
import_name.string = str_lit(".");
|
// import_name.string = str_lit(".");
|
||||||
|
|
||||||
if (allow_token(f, Token_when)) {
|
// if (allow_token(f, Token_when)) {
|
||||||
cond = parse_expr(f, false);
|
// cond = parse_expr(f, false);
|
||||||
}
|
// }
|
||||||
|
|
||||||
expect_semicolon(f, nullptr);
|
// expect_semicolon(f, nullptr);
|
||||||
if (f->curr_proc != nullptr) {
|
// if (f->curr_proc != nullptr) {
|
||||||
syntax_error(import_name, "You cannot use `import_load` within a procedure. This must be done at the file scope");
|
// syntax_error(import_name, "You cannot use `import_load` within a procedure. This must be done at the file scope");
|
||||||
spec = ast_bad_decl(f, import_name, file_path);
|
// spec = ast_bad_decl(f, import_name, file_path);
|
||||||
} else {
|
// } else {
|
||||||
spec = ast_import_spec(f, false, file_path, import_name, cond, docs, f->line_comment);
|
// spec = ast_import_decl(f, false, file_path, import_name, cond, docs, f->line_comment);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return spec;
|
// return spec;
|
||||||
}
|
// }
|
||||||
|
|
||||||
PARSE_SPEC_FUNC(parse_foreign_library_spec) {
|
PARSE_SPEC_FUNC(parse_foreign_library_spec) {
|
||||||
AstNode *spec = nullptr;
|
AstNode *spec = nullptr;
|
||||||
@@ -3205,10 +3206,9 @@ void parse_foreign_block_decl(AstFile *f, Array<AstNode *> *decls) {
|
|||||||
AstNode *parse_decl(AstFile *f) {
|
AstNode *parse_decl(AstFile *f) {
|
||||||
ParseSpecFunc *func = nullptr;
|
ParseSpecFunc *func = nullptr;
|
||||||
switch (f->curr_token.kind) {
|
switch (f->curr_token.kind) {
|
||||||
case Token_import:
|
// case Token_import:
|
||||||
case Token_import_load:
|
// func = parse_import_spec;
|
||||||
func = parse_import_spec;
|
// break;
|
||||||
break;
|
|
||||||
|
|
||||||
case Token_foreign_library:
|
case Token_foreign_library:
|
||||||
case Token_foreign_system_library:
|
case Token_foreign_system_library:
|
||||||
@@ -4524,7 +4524,45 @@ AstNode *parse_asm_stmt(AstFile *f) {
|
|||||||
return ast_asm_stmt(f, token, is_volatile, open, close, code_string,
|
return ast_asm_stmt(f, token, is_volatile, open, close, code_string,
|
||||||
output_list, input_list, clobber_list,
|
output_list, input_list, clobber_list,
|
||||||
output_count, input_count, clobber_count);
|
output_count, input_count, clobber_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AstNode *parse_import_decl(AstFile *f, bool is_using) {
|
||||||
|
CommentGroup docs = f->lead_comment;
|
||||||
|
Token token = expect_token(f, Token_import);
|
||||||
|
AstNode *cond = nullptr;
|
||||||
|
Token import_name = {};
|
||||||
|
|
||||||
|
switch (f->curr_token.kind) {
|
||||||
|
case Token_Ident:
|
||||||
|
import_name = advance_token(f);
|
||||||
|
break;
|
||||||
|
case Token_Period:
|
||||||
|
import_name = advance_token(f);
|
||||||
|
import_name.kind = Token_Ident;
|
||||||
|
if (is_using) break;
|
||||||
|
syntax_error(import_name, "`import .` is not allowed. Did you mean `using import`?");
|
||||||
|
/* fallthrough */
|
||||||
|
default:
|
||||||
|
import_name.pos = f->curr_token.pos;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_blank_ident(import_name)) {
|
||||||
|
syntax_error(import_name, "Illegal import name: `_`");
|
||||||
|
}
|
||||||
|
|
||||||
|
Token file_path = expect_token_after(f, Token_String, "import");
|
||||||
|
if (allow_token(f, Token_when)) {
|
||||||
|
cond = parse_expr(f, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect_semicolon(f, nullptr);
|
||||||
|
if (f->curr_proc != nullptr) {
|
||||||
|
syntax_error(import_name, "You cannot use `import` within a procedure. This must be done at the file scope");
|
||||||
|
return ast_bad_decl(f, import_name, file_path);
|
||||||
|
}
|
||||||
|
return ast_import_decl(f, token, is_using, file_path, import_name, cond, docs, f->line_comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -4552,12 +4590,7 @@ AstNode *parse_stmt(AstFile *f) {
|
|||||||
expect_semicolon(f, s);
|
expect_semicolon(f, s);
|
||||||
return s;
|
return s;
|
||||||
|
|
||||||
// case Token_var:
|
|
||||||
// case Token_const:
|
|
||||||
// case Token_proc:
|
|
||||||
// case Token_type:
|
|
||||||
case Token_import:
|
|
||||||
case Token_import_load:
|
|
||||||
case Token_foreign:
|
case Token_foreign:
|
||||||
case Token_foreign_library:
|
case Token_foreign_library:
|
||||||
case Token_foreign_system_library:
|
case Token_foreign_system_library:
|
||||||
@@ -4565,6 +4598,8 @@ AstNode *parse_stmt(AstFile *f) {
|
|||||||
expect_semicolon(f, s);
|
expect_semicolon(f, s);
|
||||||
return s;
|
return s;
|
||||||
|
|
||||||
|
case Token_import:
|
||||||
|
return parse_import_decl(f, false);
|
||||||
|
|
||||||
case Token_if: return parse_if_stmt(f);
|
case Token_if: return parse_if_stmt(f);
|
||||||
case Token_when: return parse_when_stmt(f);
|
case Token_when: return parse_when_stmt(f);
|
||||||
@@ -4573,7 +4608,6 @@ AstNode *parse_stmt(AstFile *f) {
|
|||||||
case Token_defer: return parse_defer_stmt(f);
|
case Token_defer: return parse_defer_stmt(f);
|
||||||
case Token_asm: return parse_asm_stmt(f);
|
case Token_asm: return parse_asm_stmt(f);
|
||||||
case Token_return: return parse_return_stmt(f);
|
case Token_return: return parse_return_stmt(f);
|
||||||
// case Token_give: return parse_give_stmt(f);
|
|
||||||
|
|
||||||
case Token_break:
|
case Token_break:
|
||||||
case Token_continue:
|
case Token_continue:
|
||||||
@@ -4592,6 +4626,10 @@ AstNode *parse_stmt(AstFile *f) {
|
|||||||
case Token_using: {
|
case Token_using: {
|
||||||
CommentGroup docs = f->lead_comment;
|
CommentGroup docs = f->lead_comment;
|
||||||
Token token = expect_token(f, Token_using);
|
Token token = expect_token(f, Token_using);
|
||||||
|
if (f->curr_token.kind == Token_import) {
|
||||||
|
return parse_import_decl(f, true);
|
||||||
|
}
|
||||||
|
|
||||||
AstNode *decl = nullptr;
|
AstNode *decl = nullptr;
|
||||||
Array<AstNode *> list = parse_lhs_expr_list(f);
|
Array<AstNode *> list = parse_lhs_expr_list(f);
|
||||||
if (list.count == 0) {
|
if (list.count == 0) {
|
||||||
@@ -4909,49 +4947,41 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<AstNod
|
|||||||
node->kind != AstNode_EmptyStmt) {
|
node->kind != AstNode_EmptyStmt) {
|
||||||
// NOTE(bill): Sanity check
|
// NOTE(bill): Sanity check
|
||||||
syntax_error(node, "Only declarations are allowed at file scope %.*s", LIT(ast_node_strings[node->kind]));
|
syntax_error(node, "Only declarations are allowed at file scope %.*s", LIT(ast_node_strings[node->kind]));
|
||||||
|
} else if (node->kind == AstNode_ImportDecl) {
|
||||||
|
ast_node(id, ImportDecl, node);
|
||||||
|
String collection_name = {};
|
||||||
|
String oirignal_string = id->relpath.string;
|
||||||
|
String file_str = id->relpath.string;
|
||||||
|
gbAllocator a = heap_allocator(); // TODO(bill): Change this allocator
|
||||||
|
String import_file = {};
|
||||||
|
String rel_path = {};
|
||||||
|
|
||||||
|
if (!is_import_path_valid(file_str)) {
|
||||||
|
syntax_error(node, "Invalid import path: `%.*s`", LIT(file_str));
|
||||||
|
// NOTE(bill): It's a naughty name
|
||||||
|
decls[i] = ast_bad_decl(f, id->relpath, id->relpath);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_mutex_lock(&p->file_decl_mutex);
|
||||||
|
defer (gb_mutex_unlock(&p->file_decl_mutex));
|
||||||
|
|
||||||
|
rel_path = get_fullpath_relative(a, base_dir, file_str);
|
||||||
|
import_file = rel_path;
|
||||||
|
if (!gb_file_exists(cast(char *)rel_path.text)) { // NOTE(bill): This should be null terminated
|
||||||
|
String abs_path = get_fullpath_core(a, file_str);
|
||||||
|
if (gb_file_exists(cast(char *)abs_path.text)) {
|
||||||
|
import_file = abs_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
import_file = string_trim_whitespace(import_file);
|
||||||
|
|
||||||
|
id->fullpath = import_file;
|
||||||
|
try_add_import_path(p, import_file, file_str, ast_node_token(node).pos);
|
||||||
} else if (node->kind == AstNode_GenDecl) {
|
} else if (node->kind == AstNode_GenDecl) {
|
||||||
ast_node(gd, GenDecl, node);
|
ast_node(gd, GenDecl, node);
|
||||||
if (gd->token.kind == Token_import ||
|
if (gd->token.kind == Token_foreign_library ||
|
||||||
gd->token.kind == Token_import_load) {
|
|
||||||
for_array(spec_index, gd->specs) {
|
|
||||||
AstNode *spec = gd->specs[spec_index];
|
|
||||||
ast_node(id, ImportSpec, spec);
|
|
||||||
String collection_name = {};
|
|
||||||
String oirignal_string = id->relpath.string;
|
|
||||||
String file_str = id->relpath.string;
|
|
||||||
gbAllocator a = heap_allocator(); // TODO(bill): Change this allocator
|
|
||||||
String import_file = {};
|
|
||||||
String rel_path = {};
|
|
||||||
|
|
||||||
if (!is_import_path_valid(file_str)) {
|
|
||||||
if (id->is_import) {
|
|
||||||
syntax_error(node, "Invalid import path: `%.*s`", LIT(file_str));
|
|
||||||
} else {
|
|
||||||
syntax_error(node, "Invalid include path: `%.*s`", LIT(file_str));
|
|
||||||
}
|
|
||||||
// NOTE(bill): It's a naughty name
|
|
||||||
decls[i] = ast_bad_decl(f, id->relpath, id->relpath);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
gb_mutex_lock(&p->file_decl_mutex);
|
|
||||||
defer (gb_mutex_unlock(&p->file_decl_mutex));
|
|
||||||
|
|
||||||
rel_path = get_fullpath_relative(a, base_dir, file_str);
|
|
||||||
import_file = rel_path;
|
|
||||||
if (!gb_file_exists(cast(char *)rel_path.text)) { // NOTE(bill): This should be null terminated
|
|
||||||
String abs_path = get_fullpath_core(a, file_str);
|
|
||||||
if (gb_file_exists(cast(char *)abs_path.text)) {
|
|
||||||
import_file = abs_path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
import_file = string_trim_whitespace(import_file);
|
|
||||||
|
|
||||||
id->fullpath = import_file;
|
|
||||||
try_add_import_path(p, import_file, file_str, ast_node_token(node).pos);
|
|
||||||
}
|
|
||||||
} else if (gd->token.kind == Token_foreign_library ||
|
|
||||||
gd->token.kind == Token_foreign_system_library) {
|
gd->token.kind == Token_foreign_system_library) {
|
||||||
for_array(spec_index, gd->specs) {
|
for_array(spec_index, gd->specs) {
|
||||||
AstNode *spec = gd->specs[spec_index];
|
AstNode *spec = gd->specs[spec_index];
|
||||||
|
|||||||
@@ -85,7 +85,6 @@ TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \
|
|||||||
\
|
\
|
||||||
TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
|
TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
|
||||||
TOKEN_KIND(Token_import, "import"), \
|
TOKEN_KIND(Token_import, "import"), \
|
||||||
TOKEN_KIND(Token_import_load, "import_load"), \
|
|
||||||
TOKEN_KIND(Token_foreign, "foreign"), \
|
TOKEN_KIND(Token_foreign, "foreign"), \
|
||||||
TOKEN_KIND(Token_foreign_library, "foreign_library"), \
|
TOKEN_KIND(Token_foreign_library, "foreign_library"), \
|
||||||
TOKEN_KIND(Token_foreign_system_library, "foreign_system_library"), \
|
TOKEN_KIND(Token_foreign_system_library, "foreign_system_library"), \
|
||||||
|
|||||||
Reference in New Issue
Block a user