mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-20 04:35:00 -07:00
893 lines
14 KiB
Odin
893 lines
14 KiB
Odin
// 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(); }
|