mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-23 22:25:00 -07:00
901 lines
15 KiB
Odin
901 lines
15 KiB
Odin
// Demo 002
|
|
#load "basic.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 :: type 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 :: type enum {
|
|
APPLE, // 0
|
|
BANANA, // 1
|
|
PEAR, // 2
|
|
}
|
|
|
|
f := Fruit.APPLE
|
|
// g12: int = Fruit.BANANA
|
|
g: int = Fruit.BANANA as int
|
|
// However, you can use enums are index values as _any_ integer allowed
|
|
}
|
|
{
|
|
Fruit1 :: type enum int {
|
|
APPLE,
|
|
BANANA,
|
|
PEAR,
|
|
}
|
|
|
|
Fruit2 :: type enum u8 {
|
|
APPLE,
|
|
BANANA,
|
|
PEAR,
|
|
}
|
|
|
|
Fruit3 :: type 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 i := 0; i < len(args); i++ {
|
|
if i > 0 {
|
|
print_string(", ")
|
|
}
|
|
print_int(args[i])
|
|
}
|
|
}
|
|
|
|
print_ints(); // nl()
|
|
print_ints(1); nl()
|
|
print_ints(1, 2, 3); nl()
|
|
|
|
print_prefix_f32s :: proc(prefix: string, args: ..f32) {
|
|
print_string(prefix)
|
|
print_string(": ")
|
|
for i := 0; i < len(args); i++ {
|
|
if i > 0 {
|
|
print_string(", ")
|
|
}
|
|
print_f32(args[i])
|
|
}
|
|
}
|
|
|
|
print_prefix_f32s("a"); nl()
|
|
print_prefix_f32s("b", 1); nl()
|
|
7 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 := new_slice(int, 12)
|
|
c := new_slice(int, 12, 16)
|
|
|
|
defer delete(a)
|
|
defer delete(b)
|
|
defer delete(c)
|
|
|
|
// NOTE(bill): These use the current context's allocator not the default allocator
|
|
// see runtime.odin
|
|
|
|
// Q: Should this be `free` rather than `delete` 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 delete(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 = null;
|
|
y := ptr_offset(x, 100)
|
|
z := ptr_sub(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_int(min(a, b)); nl()
|
|
print_int(max(a, b)); nl()
|
|
print_int(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_int(C); nl()
|
|
print_int(D); nl()
|
|
print_int(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_string("1!\n")
|
|
// break by default
|
|
|
|
case 2:
|
|
s := "2!\n"; // Each case has its own scope
|
|
print_string(s)
|
|
break // explicit break
|
|
|
|
case 3, 4: // multiple cases
|
|
print_string("3 or 4!\n")
|
|
|
|
case 5:
|
|
print_string("5!\n")
|
|
fallthrough // explicit fallthrough
|
|
|
|
default:
|
|
print_string("default!\n")
|
|
}
|
|
|
|
|
|
|
|
match x := 1.5; x {
|
|
case 1.5:
|
|
print_string("1.5!\n")
|
|
// break by default
|
|
case MATH_TAU:
|
|
print_string("τ!\n")
|
|
default:
|
|
print_string("default!\n")
|
|
}
|
|
|
|
|
|
|
|
match x := "Hello"; x {
|
|
case "Hello":
|
|
print_string("greeting\n")
|
|
// break by default
|
|
case "Goodbye":
|
|
print_string("farewell\n")
|
|
default:
|
|
print_string("???\n")
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
a := 53
|
|
match {
|
|
case a == 1:
|
|
print_string("one\n")
|
|
case a == 2:
|
|
print_string("a couple\n")
|
|
case a < 7, a == 7:
|
|
print_string("a few\n")
|
|
case a < 12: // intentional bug
|
|
print_string("several\n")
|
|
case a >= 12 && a < 100:
|
|
print_string("dozens\n")
|
|
case a >= 100 && a < 1000:
|
|
print_string("hundreds\n")
|
|
default:
|
|
print_string("a fuck ton\n")
|
|
}
|
|
|
|
// Identical to this
|
|
|
|
b := 53
|
|
if b == 1 {
|
|
print_string("one\n")
|
|
} else if b == 2 {
|
|
print_string("a couple\n")
|
|
} else if b < 7 || b == 7 {
|
|
print_string("a few\n")
|
|
} else if b < 12 { // intentional bug
|
|
print_string("several\n")
|
|
} else if b >= 12 && b < 100 {
|
|
print_string("dozens\n")
|
|
} else if b >= 100 && b < 1000 {
|
|
print_string("hundreds\n")
|
|
} else {
|
|
print_string("a fuck ton\n")
|
|
}
|
|
|
|
// However, match statements allow for `break` and `fallthrough` unlike
|
|
// an if statement
|
|
}
|
|
}
|
|
|
|
Vector3 :: type struct {
|
|
x, y, z: f32
|
|
}
|
|
|
|
print_floats :: proc(args: ..f32) {
|
|
for i := 0; i < len(args); i++ {
|
|
if i > 0 {
|
|
print_string(", ")
|
|
}
|
|
print_f32(args[i])
|
|
}
|
|
print_nl()
|
|
}
|
|
|
|
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 :: type struct {
|
|
Guid :: type int
|
|
Nested :: type struct {
|
|
MyInt :: type 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_int(e.guid as int); nl()
|
|
print_string(e.name); nl()
|
|
}
|
|
|
|
{
|
|
using e: Entity
|
|
guid = 78456
|
|
name = "Thing"
|
|
|
|
print_int(e.guid as int); nl()
|
|
print_string(e.name); nl()
|
|
}
|
|
}
|
|
|
|
{
|
|
Entity :: type struct {
|
|
Guid :: type int
|
|
Nested :: type struct {
|
|
MyInt :: type 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 :: type struct {
|
|
position: Vector3
|
|
}
|
|
|
|
print_pos_1 :: proc(entity: ^Entity) {
|
|
print_string("print_pos_1: ")
|
|
print_floats(entity.position.x, entity.position.y, entity.position.z)
|
|
}
|
|
|
|
print_pos_2 :: proc(entity: ^Entity) {
|
|
using entity
|
|
print_string("print_pos_2: ")
|
|
print_floats(position.x, position.y, position.z)
|
|
}
|
|
|
|
print_pos_3 :: proc(using entity: ^Entity) {
|
|
print_string("print_pos_3: ")
|
|
print_floats(position.x, position.y, position.z)
|
|
}
|
|
|
|
print_pos_4 :: proc(using entity: ^Entity) {
|
|
using position
|
|
print_string("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 :: type struct {
|
|
position: Vector3
|
|
}
|
|
|
|
Frog :: type 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 :: type struct {
|
|
position: Vector3
|
|
}
|
|
|
|
Frog :: type struct {
|
|
using entity: Entity
|
|
jump_height: f32
|
|
}
|
|
|
|
f: Frog
|
|
f.position = Vector3{1, 2, 3}
|
|
|
|
|
|
print_pos :: proc(using entity: Entity) {
|
|
print_string("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 :: type struct {
|
|
position: Vector3
|
|
}
|
|
|
|
Frog :: type 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_string("print_pos: ")
|
|
print_floats(position.x, position.y, position.z)
|
|
}
|
|
|
|
print_pos(f.entity)
|
|
print_pos(^f)
|
|
print_pos(f)
|
|
}
|
|
|
|
{
|
|
// More efficient subtyping
|
|
|
|
Entity :: type struct {
|
|
position: Vector3
|
|
}
|
|
|
|
Frog :: type 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 :: type struct {
|
|
position: Vector3
|
|
}
|
|
|
|
Frog :: type struct {
|
|
jump_height: f32
|
|
using entity: Entity
|
|
}
|
|
|
|
f: Frog
|
|
f.jump_height = 564
|
|
e := ^f.entity
|
|
|
|
frog := e down_cast ^Frog
|
|
print_string("down_cast: ")
|
|
print_f32(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 :: type struct {
|
|
position: Vector3
|
|
}
|
|
Climber :: type struct {
|
|
speed: f32
|
|
}
|
|
|
|
Frog :: type struct {
|
|
using entity: Entity
|
|
using climber: Climber
|
|
}
|
|
}
|
|
}
|
|
|
|
tagged_unions :: proc() {
|
|
{
|
|
EntityKind :: type enum {
|
|
INVALID,
|
|
FROG,
|
|
GIRAFFE,
|
|
HELICOPTER,
|
|
}
|
|
|
|
Entity :: type 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 :: type 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
|
|
}
|
|
}
|
|
|
|
using Entity
|
|
f1: Frog = Frog{12, 0xff9900}
|
|
f2: Entity = Frog{12, 0xff9900} // Implicit cast
|
|
f3 := Frog{12, 0xff9900} as Entity // 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 type x : ^f {
|
|
case Frog:
|
|
print_string("Frog!\n")
|
|
print_f32(x.jump_height); nl()
|
|
x.jump_height = 3
|
|
print_f32(x.jump_height); nl()
|
|
case Giraffe:
|
|
print_string("Giraffe!\n")
|
|
case Helicopter:
|
|
print_string("ROFLCOPTER!\n")
|
|
default:
|
|
print_string("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 := ^f as ^Frog // Unsafe
|
|
print_f32(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 :: type struct {}
|
|
Scope :: type struct {}
|
|
Token :: type struct {}
|
|
AstNode :: type struct {}
|
|
ExactValue :: type struct {}
|
|
|
|
EntityKind :: type enum {
|
|
Invalid,
|
|
Constant,
|
|
Variable,
|
|
UsingVariable,
|
|
TypeName,
|
|
Procedure,
|
|
Builtin,
|
|
Count,
|
|
}
|
|
|
|
Entity :: type struct {
|
|
Guid :: type i64
|
|
|
|
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 :: type struct {}
|
|
Scope :: type struct {}
|
|
Token :: type struct {}
|
|
AstNode :: type struct {}
|
|
ExactValue :: type struct {}
|
|
|
|
|
|
Entity :: type union {
|
|
Base :: type struct {
|
|
Guid :: type i64
|
|
guid: Guid
|
|
|
|
scope: ^Scope
|
|
token: Token
|
|
type_: ^Type
|
|
}
|
|
|
|
|
|
Constant: struct {
|
|
using base: Base
|
|
value: ExactValue
|
|
}
|
|
Variable: struct {
|
|
using base: Base
|
|
visited: bool // Cycle detection
|
|
used: bool // Variable is used
|
|
is_field: bool // Is struct field
|
|
anonymous: bool // Variable is an anonymous
|
|
}
|
|
UsingVariable: struct {
|
|
using base: Base
|
|
}
|
|
TypeName: struct {
|
|
using base: Base
|
|
}
|
|
Procedure: struct {
|
|
using base: Base
|
|
used: bool
|
|
}
|
|
Builtin: struct {
|
|
using base: Base
|
|
id: int
|
|
}
|
|
}
|
|
|
|
using Entity
|
|
|
|
e: Entity
|
|
|
|
e = Variable{
|
|
base = Base{},
|
|
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 :: type raw_union {
|
|
using xy_: struct { x, y: f32 }
|
|
e: [2]f32
|
|
v: {2}f32
|
|
}
|
|
|
|
Vector3 :: type raw_union {
|
|
using xyz_: struct { x, y, z: f32 }
|
|
xy: Vector2
|
|
e: [3]f32
|
|
v: {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() { print_nl() }
|