Add examples

This commit is contained in:
Ginger Bill
2017-08-27 23:27:12 +01:00
parent cf3c5a878a
commit 7cb8016df3
16 changed files with 4862 additions and 27 deletions
+337
View File
@@ -0,0 +1,337 @@
#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")
}
+892
View File
@@ -0,0 +1,892 @@
// 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(); }
+66
View File
@@ -0,0 +1,66 @@
#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
}
+284
View File
@@ -0,0 +1,284 @@
#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
}
+318
View File
@@ -0,0 +1,318 @@
#import "atomic.odin";
#import "hash.odin";
#import "mem.odin";
#import "opengl.odin";
#import "strconv.odin";
#import "sync.odin";
#import win32 "sys/windows.odin";
#import "fmt.odin";
#import "os.odin";
#import "math.odin";
main :: proc() {
when true {
/*
Added:
* Unexported entities and fields using an underscore prefix
- See `sync.odin` and explain
Removed:
* Maybe/option types
* Remove `type` keyword and other "reserved" keywords
* ..< and ... removed and replace with .. (half-closed range)
Changed:
* `compile_assert` and `assert` return the value of the condition for semantic reasons
* thread_local -> #thread_local
* #include -> #load
* Files only get checked if they are actually used
* match x in y {} // For type match statements
* Version numbering now starts from 0.1.0 and uses the convention:
- major.minor.patch
* Core library additions to Windows specific stuff
*/
{
Fruit :: enum {
APPLE,
BANANA,
COCONUT,
}
fmt.println(Fruit.names);
}
{
A :: struct {x, y: f32};
B :: struct #align 16 {x, y: f32};
fmt.println("align_of(A) =", align_of(A));
fmt.println("align_of(B) =", align_of(B));
}
{
// Removal of ..< and ...
for i in 0..16 {
}
// Is similar to
for _i := 0; _i < 16; _i++ { immutable i := _i;
}
}
{
thing: for i in 0..10 {
for j in i+1..10 {
if j == 2 {
fmt.println(i, j);
continue thing;
}
if j == 3 {
break thing;
}
}
}
// Works with, `for`, `for in`, `match`, `match in`
// NOTE(bill): This solves most of the problems I need `goto` for
}
{
t := type_info(int);
using Type_Info;
match i in t {
case Integer, Float:
fmt.println("It's a number");
}
x: any = 123;
foo match i in x {
case int, f32:
fmt.println("It's an int or f32");
break foo;
}
}
{
cond := true;
x: int;
if cond {
x = 3;
} else {
x = 4;
}
// Ternary operator
y := cond ? 3 : 4;
FOO :: true ? 123 : 432; // Constant ternary expression
fmt.println("Ternary values:", y, FOO);
}
{
// Slices now store a capacity
buf: [256]byte;
s: []byte;
s = buf[..0]; // == buf[0..0];
fmt.println("count =", s.count);
fmt.println("capacity =", s.capacity);
append(s, 1, 2, 3);
fmt.println(s);
s = buf[1..2..3];
fmt.println("count =", s.count);
fmt.println("capacity =", s.capacity);
fmt.println(s);
clear(s); // Sets count to zero
s.count = 0; // Equivalent
}
{
Foo :: struct {
x, y, z: f32,
ok: bool,
flags: u32,
}
foo_array: [256]Foo;
foo_as_bytes: []byte = slice_to_bytes(foo_array[..]);
// Useful for things like
// os.write(handle, foo_as_bytes);
foo_slice := slice_ptr(cast(^Foo)foo_as_bytes.data, foo_as_bytes.count/size_of(Foo), foo_as_bytes.capacity/size_of(Foo));
// Question: Should there be a bytes_to_slice procedure or is it clearer to do this even if it is error prone?
// And if so what would the syntax be?
// slice_transmute([]Foo, foo_as_bytes);
}
{
Vec3 :: [vector 3]f32;
x := Vec3{1, 2, 3};
y := Vec3{4, 5, 6};
fmt.println(x < y);
fmt.println(x + y);
fmt.println(x - y);
fmt.println(x * y);
fmt.println(x / y);
for i in x {
fmt.println(i);
}
compile_assert(size_of([vector 7]bool) == size_of([7]bool));
compile_assert(size_of([vector 7]i32) == size_of([7]i32));
// align_of([vector 7]i32) != align_of([7]i32) // this may be the case
}
{
// fmt.* changes
// bprint* returns `int` (bytes written)
// sprint* returns `string` (bytes written as a string)
data: [256]byte;
str := fmt.sprintf(data[..0], "Hellope %d %s %c", 123, "others", '!');
fmt.println(str);
buf := data[..0];
count := fmt.bprintf(^buf, "Hellope %d %s %c", 321, "y'all", '!');
fmt.println(cast(string)buf[..count]);
// NOTE(bill): We may change this but because this is a library feature, I am not that bothered yet
}
{
x: [dynamic]f64;
reserve(x, 16);
defer free(x); // `free` is overloaded for numerous types
// Number literals can have underscores in them for readability
append(x, 2_000_000.500_000, 123, 5, 7); // variadic append
for p, i in x {
if i > 0 { fmt.print(", "); }
fmt.print(p);
}
fmt.println();
}
{
// Dynamic array "literals"
x := [dynamic]f64{2_000_000.500_000, 3, 5, 7};
defer free(x);
fmt.println(x); // fmt.print* supports printing of dynamic types
clear(x);
fmt.println(x);
}
{
m: map[f32]int;
reserve(m, 16);
defer free(m);
m[1.0] = 1278;
m[2.0] = 7643;
m[3.0] = 564;
_, ok := m[3.0];
c := m[3.0];
assert(ok && c == 564);
fmt.print("map[");
i := 0;
for val, key in m {
if i > 0 {
fmt.print(", ");
}
fmt.printf("%v=%v", key, val);
i += 1;
}
fmt.println("]");
}
{
m := map[string]u32{
"a" = 56,
"b" = 13453,
"c" = 7654,
};
defer free(m);
c := m["c"];
_, ok := m["c"];
assert(ok && c == 7654);
fmt.println(m);
delete(m, "c"); // deletes entry with key "c"
_, found := m["c"];
assert(!found);
fmt.println(m);
clear(m);
fmt.println(m);
// NOTE: Fixed size maps are planned but we have not yet implemented
// them as we have had no need for them as of yet
}
{
Vector3 :: struct{x, y, z: f32};
Quaternion :: struct{x, y, z, w: f32};
Entity :: union {
// Common Fields
id: u64,
name: string,
using position: Vector3,
orientation: Quaternion,
flags: u32,
// Variants
Frog{
ribbit_volume: f32,
jump_height: f32,
},
Door{
openness: f32,
},
Map{
width, height: f32,
place_positions: []Vector3,
place_names: []string,
},
}
entity: Entity;
// implicit conversion from variant to base type
entity = Entity.Frog{
id = 1337,
ribbit_volume = 0.5,
jump_height = 2.1,
/*other data */
};
entity.name = "Frank";
entity.position = Vector3{1, 4, 9};
using Entity;
match e in entity {
case Frog:
fmt.println("Ribbit");
case Door:
fmt.println("Creak");
case Map:
fmt.println("Rustle");
default:
fmt.println("Just a normal entity");
}
if frog, ok := union_cast(Frog)entity; ok {
fmt.printf("The frog jumps %f feet high at %v\n", frog.jump_height, frog.position);
}
// Panics if not the correct type
frog: Frog;
frog = union_cast(Frog)entity;
frog, _ = union_cast(Frog)entity; // ignore error and force cast
}
}
}
+412
View File
@@ -0,0 +1,412 @@
#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()
}