-
-
Notifications
You must be signed in to change notification settings - Fork 638
/
demo.odin
2628 lines (2164 loc) · 62.3 KB
/
demo.odin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#+vet !using-stmt !using-param
package main
import "core:fmt"
import "core:mem"
import "core:os"
import "core:thread"
import "core:time"
import "core:reflect"
import "base:runtime"
import "base:intrinsics"
import "core:math/big"
/*
Odin is a general-purpose programming language with distinct typing built
for high performance, modern systems and data-oriented programming.
Odin is the C alternative for the Joy of Programming.
# Installing Odin
Getting Started - https://odin-lang.org/docs/install/
Instructions for downloading and install the Odin compiler and libraries.
# Learning Odin
Getting Started - https://odin-lang.org/docs/install/
Getting Started with Odin. Downloading, installing, and getting your
first program to compile and run.
Overview of Odin - https://odin-lang.org/docs/overview/
An overview of the Odin programming language and its features.
Frequently Asked Questions (FAQ) - https://odin-lang.org/docs/faq/
Answers to common questions about Odin.
Packages - https://pkg.odin-lang.org/
Documentation for all the official packages part of the
core and vendor library collections.
Nightly Builds - https://odin-lang.org/docs/nightly/
Get the latest nightly builds of Odin.
More Odin Examples - https://github.com/odin-lang/examples
This repository contains examples of how certain things can be accomplished
in idiomatic Odin, allowing you learn its semantics, as well as how to use
parts of the core and vendor package collections.
*/
the_basics :: proc() {
fmt.println("\n# the basics")
{ // The Basics
// os.args holds the path to the current executable and any arguments passed to it.
if len(os.args) == 1 {
fmt.printf("Hellope from %v.\n", os.args[0])
} else if len(os.args) > 2 {
fmt.printf("%v, %v! from %v.\n", os.args[1], os.args[2], os.args[0])
}
// Lexical elements and literals
// A comment
my_integer_variable: int // A comment for documentaton
// Multi-line comments begin with /* and end with */. Multi-line comments can
// also be nested (unlike in C):
/*
You can have any text or code here and
have it be commented.
/*
NOTE: comments can be nested!
*/
*/
// String literals are enclosed in double quotes and character literals in single quotes.
// Special characters are escaped with a backslash \
some_string := "This is a string"
_ = 'A' // unicode codepoint literal
_ = '\n'
_ = "C:\\Windows\\notepad.exe"
// Raw string literals are enclosed with single back ticks
_ = `C:\Windows\notepad.exe`
// The length of a string in bytes can be found using the built-in `len` procedure:
_ = len("Foo")
_ = len(some_string)
// Numbers
// Numerical literals are written similar to most other programming languages.
// A useful feature in Odin is that underscores are allowed for better
// readability: 1_000_000_000 (one billion). A number that contains a dot is a
// floating point literal: 1.0e9 (one billion). If a number literal is suffixed
// with i, is an imaginary number literal: 2i (2 multiply the square root of -1).
// Binary literals are prefixed with 0b, octal literals with 0o, and hexadecimal
// literals 0x. A leading zero does not produce an octal constant (unlike C).
// In Odin, if a numeric constant can be represented by a type without
// precision loss, it will automatically convert to that type.
x: int = 1.0 // A float literal but it can be represented by an integer without precision loss
// Constant literals are “untyped” which means that they can implicitly convert to a type.
y: int // `y` is typed of type `int`
y = 1 // `1` is an untyped integer literal which can implicitly convert to `int`
z: f64 // `z` is typed of type `f64` (64-bit floating point number)
z = 1 // `1` is an untyped integer literal which can be implicitly converted to `f64`
// No need for any suffixes or decimal places like in other languages
// (with the exception of negative zero, which must be given as `-0.0`)
// CONSTANTS JUST WORK!!!
// Assignment statements
h: int = 123 // declares a new variable `h` with type `int` and assigns a value to it
h = 637 // assigns a new value to `h`
// `=` is the assignment operator
// You can assign multiple variables with it:
a, b := 1, "hello" // declares `a` and `b` and infers the types from the assignments
b, a = "byte", 0
// Note: `:=` is two tokens, `:` and `=`. The following are equivalent,
/*
i: int = 123
i: = 123
i := 123
*/
// Constant declarations
// Constants are entities (symbols) which have an assigned value.
// The constant’s value cannot be changed.
// The constant’s value must be able to be evaluated at compile time:
X :: "what" // constant `X` has the untyped string value "what"
// Constants can be explicitly typed like a variable declaration:
Y : int : 123
Z :: Y + 7 // constant computations are possible
_ = my_integer_variable
_ = x
}
}
control_flow :: proc() {
fmt.println("\n# control flow")
{ // Control flow
// For loop
// Odin has only one loop statement, the `for` loop
// Basic for loop
for i := 0; i < 10; i += 1 {
fmt.println(i)
}
// NOTE: Unlike other languages like C, there are no parentheses `( )` surrounding the three components.
// Braces `{ }` or a `do` are always required
for i := 0; i < 10; i += 1 { }
// for i := 0; i < 10; i += 1 do fmt.print()
// The initial and post statements are optional
i := 0
for ; i < 10; {
i += 1
}
// These semicolons can be dropped. This `for` loop is equivalent to C's `while` loop
i = 0
for i < 10 {
i += 1
}
// If the condition is omitted, an infinite loop is produced:
for {
break
}
// Range-based for loop
// The basic for loop
for j := 0; j < 10; j += 1 {
fmt.println(j)
}
// can also be written
for j in 0..<10 {
fmt.println(j)
}
for j in 0..=9 {
fmt.println(j)
}
// Certain built-in types can be iterated over
some_string := "Hello, 世界"
for character in some_string { // Strings are assumed to be UTF-8
fmt.println(character)
}
some_array := [3]int{1, 4, 9}
for value in some_array {
fmt.println(value)
}
some_slice := []int{1, 4, 9}
for value in some_slice {
fmt.println(value)
}
some_dynamic_array := [dynamic]int{1, 4, 9}
defer delete(some_dynamic_array)
for value in some_dynamic_array {
fmt.println(value)
}
some_map := map[string]int{"A" = 1, "C" = 9, "B" = 4}
defer delete(some_map)
for key in some_map {
fmt.println(key)
}
// Alternatively a second index value can be added
for character, index in some_string {
fmt.println(index, character)
}
for value, index in some_array {
fmt.println(index, value)
}
for value, index in some_slice {
fmt.println(index, value)
}
for value, index in some_dynamic_array {
fmt.println(index, value)
}
for key, value in some_map {
fmt.println(key, value)
}
// The iterated values are copies and cannot be written to.
// The following idiom is useful for iterating over a container in a by-reference manner:
for _, idx in some_slice {
some_slice[idx] = (idx+1)*(idx+1)
}
// If statements
x := 123
if x >= 0 {
fmt.println("x is positive")
}
if y := -34; y < 0 {
fmt.println("y is negative")
}
if y := 123; y < 0 {
fmt.println("y is negative")
} else if y == 0 {
fmt.println("y is zero")
} else {
fmt.println("y is positive")
}
// Switch statement
// A switch statement is another way to write a sequence of if-else statements.
// In Odin, the default case is denoted as a case without any expression.
#partial switch arch := ODIN_ARCH; arch {
case .i386:
fmt.println("32-bit")
case .amd64:
fmt.println("64-bit")
case: // default
fmt.println("Unsupported architecture")
}
// Odin’s `switch` is like one in C or C++, except that Odin only runs the selected case.
// This means that a `break` statement is not needed at the end of each case.
// Another important difference is that the case values need not be integers nor constants.
// To achieve a C-like fall through into the next case block, the keyword `fallthrough` can be used.
one_angry_dwarf :: proc() -> int {
fmt.println("one_angry_dwarf was called")
return 1
}
switch j := 0; j {
case 0:
case one_angry_dwarf():
}
// A switch statement without a condition is the same as `switch true`.
// This can be used to write a clean and long if-else chain and have the
// ability to break if needed
switch {
case x < 0:
fmt.println("x is negative")
case x == 0:
fmt.println("x is zero")
case:
fmt.println("x is positive")
}
// A `switch` statement can also use ranges like a range-based loop:
switch c := 'j'; c {
case 'A'..='Z', 'a'..='z', '0'..='9':
fmt.println("c is alphanumeric")
}
switch x {
case 0..<10:
fmt.println("units")
case 10..<13:
fmt.println("pre-teens")
case 13..<20:
fmt.println("teens")
case 20..<30:
fmt.println("twenties")
}
}
{ // Defer statement
// A defer statement defers the execution of a statement until the end of
// the scope it is in.
// The following will print 4 then 234:
{
x := 123
defer fmt.println(x)
{
defer x = 4
x = 2
}
fmt.println(x)
x = 234
}
// You can defer an entire block too:
{
bar :: proc() {}
defer {
fmt.println("1")
fmt.println("2")
}
cond := false
defer if cond {
bar()
}
}
// Defer statements are executed in the reverse order that they were declared:
{
defer fmt.println("1")
defer fmt.println("2")
defer fmt.println("3")
}
// Will print 3, 2, and then 1.
if false {
f, err := os.open("my_file.txt")
if err != nil {
// handle error
}
defer os.close(f)
// rest of code
}
}
{ // When statement
/*
The when statement is almost identical to the if statement but with some differences:
* Each condition must be a constant expression as a when
statement is evaluated at compile time.
* The statements within a branch do not create a new scope
* The compiler checks the semantics and code only for statements
that belong to the first condition that is true
* An initial statement is not allowed in a when statement
* when statements are allowed at file scope
*/
// Example
when ODIN_ARCH == .i386 {
fmt.println("32 bit")
} else when ODIN_ARCH == .amd64 {
fmt.println("64 bit")
} else {
fmt.println("Unknown architecture")
}
// The when statement is very useful for writing platform specific code.
// This is akin to the #if construct in C’s preprocessor however, in Odin,
// it is type checked.
}
{ // Branch statements
cond, cond1, cond2 := false, false, false
one_step :: proc() { fmt.println("one_step") }
beyond :: proc() { fmt.println("beyond") }
// Break statement
for cond {
switch {
case:
if cond {
break // break out of the `switch` statement
}
}
break // break out of the `for` statement
}
loop: for cond1 {
for cond2 {
break loop // leaves both loops
}
}
// Continue statement
for cond {
if cond2 {
continue
}
fmt.println("Hellope")
}
// Fallthrough statement
// Odin’s switch is like one in C or C++, except that Odin only runs the selected
// case. This means that a break statement is not needed at the end of each case.
// Another important difference is that the case values need not be integers nor
// constants.
// fallthrough can be used to explicitly fall through into the next case block:
switch i := 0; i {
case 0:
one_step()
fallthrough
case 1:
beyond()
}
}
}
named_proc_return_parameters :: proc() {
fmt.println("\n# named proc return parameters")
foo0 :: proc() -> int {
return 123
}
foo1 :: proc() -> (a: int) {
a = 123
return
}
foo2 :: proc() -> (a, b: int) {
// Named return values act like variables within the scope
a = 321
b = 567
return b, a
}
fmt.println("foo0 =", foo0()) // 123
fmt.println("foo1 =", foo1()) // 123
fmt.println("foo2 =", foo2()) // 567 321
}
variadic_procedures :: proc() {
fmt.println("\n# variadic procedures")
sum :: proc(nums: ..int, init_value:= 0) -> (result: int) {
result = init_value
for n in nums {
result += n
}
return
}
fmt.println("sum(()) =", sum())
fmt.println("sum(1, 2) =", sum(1, 2))
fmt.println("sum(1, 2, 3, 4, 5) =", sum(1, 2, 3, 4, 5))
fmt.println("sum(1, 2, 3, 4, 5, init_value = 5) =", sum(1, 2, 3, 4, 5, init_value = 5))
// pass a slice as varargs
odds := []int{1, 3, 5}
fmt.println("odds =", odds)
fmt.println("sum(..odds) =", sum(..odds))
fmt.println("sum(..odds, init_value = 5) =", sum(..odds, init_value = 5))
}
explicit_procedure_overloading :: proc() {
fmt.println("\n# explicit procedure overloading")
add_ints :: proc(a, b: int) -> int {
x := a + b
fmt.println("add_ints", x)
return x
}
add_floats :: proc(a, b: f32) -> f32 {
x := a + b
fmt.println("add_floats", x)
return x
}
add_numbers :: proc(a: int, b: f32, c: u8) -> int {
x := int(a) + int(b) + int(c)
fmt.println("add_numbers", x)
return x
}
add :: proc{add_ints, add_floats, add_numbers}
add(int(1), int(2))
add(f32(1), f32(2))
add(int(1), f32(2), u8(3))
add(1, 2) // untyped ints coerce to int tighter than f32
add(1.0, 2.0) // untyped floats coerce to f32 tighter than int
add(1, 2, 3) // three parameters
// Ambiguous answers
// add(1.0, 2)
// add(1, 2.0)
}
struct_type :: proc() {
fmt.println("\n# struct type")
// A struct is a record type in Odin. It is a collection of fields.
// Struct fields are accessed by using a dot:
{
Vector2 :: struct {
x: f32,
y: f32,
}
v := Vector2{1, 2}
v.x = 4
fmt.println(v.x)
// Struct fields can be accessed through a struct pointer:
v = Vector2{1, 2}
p := &v
p.x = 1335
fmt.println(v)
// We could write p^.x, however, it is nice to abstract the ability
// to not explicitly dereference the pointer. This is very useful when
// refactoring code to use a pointer rather than a value, and vice versa.
}
{
// A struct literal can be denoted by providing the struct’s type
// followed by {}. A struct literal must either provide all the
// arguments or none:
Vector3 :: struct {
x, y, z: f32,
}
v: Vector3
v = Vector3{} // Zero value
v = Vector3{1, 4, 9}
// You can list just a subset of the fields if you specify the
// field by name (the order of the named fields does not matter):
v = Vector3{z=1, y=2}
assert(v.x == 0)
assert(v.y == 2)
assert(v.z == 1)
}
{
// Structs can tagged with different memory layout and alignment requirements:
a :: struct #align(4) {} // align to 4 bytes
b :: struct #packed {} // remove padding between fields
c :: struct #raw_union {} // all fields share the same offset (0). This is the same as C's union
}
}
union_type :: proc() {
fmt.println("\n# union type")
{
val: union{int, bool}
val = 137
if i, ok := val.(int); ok {
fmt.println(i)
}
val = true
fmt.println(val)
val = nil
switch 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
switch v in val {
case int: fmt.println("int", v)
case bool: fmt.println("bool", v)
case: fmt.println("nil")
}
}
Vector3 :: distinct [3]f32
Quaternion :: distinct quaternion128
// 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: typeid) -> ^Entity {
t := new(T)
t.derived = t^
return t
}
entity := new_entity(Monster)
switch e in entity.derived {
case Frog:
fmt.println("Ribbit")
case Monster:
if e.is_robot { fmt.println("Robotic") }
if e.is_zombie { fmt.println("Grrrr!") }
fmt.println("I'm a monster")
}
}
{
// 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: typeid) -> ^Entity {
t := new(Entity)
t.derived = T{entity = t}
return t
}
entity := new_entity(Monster)
switch e in entity.derived {
case Frog:
fmt.println("Ribbit")
case Monster:
if e.is_robot { fmt.println("Robotic") }
if e.is_zombie { 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
}
*/
}
}
using_statement :: proc() {
fmt.println("\n# using statement")
// using can used to bring entities declared in a scope/namespace
// into the current scope. This can be applied to import names, struct
// fields, procedure fields, and struct values.
Vector3 :: struct{x, y, z: f32}
{
Entity :: struct {
position: Vector3,
orientation: quaternion128,
}
// It can used like this:
foo0 :: proc(entity: ^Entity) {
fmt.println(entity.position.x, entity.position.y, entity.position.z)
}
// The entity members can be brought into the procedure scope by using it:
foo1 :: proc(entity: ^Entity) {
using entity
fmt.println(position.x, position.y, position.z)
}
// The using can be applied to the parameter directly:
foo2 :: proc(using entity: ^Entity) {
fmt.println(position.x, position.y, position.z)
}
// It can also be applied to sub-fields:
foo3 :: proc(entity: ^Entity) {
using entity.position
fmt.println(x, y, z)
}
}
{
// We can also apply the using statement to the struct fields directly,
// making all the fields of position appear as if they on Entity itself:
Entity :: struct {
using position: Vector3,
orientation: quaternion128,
}
foo :: proc(entity: ^Entity) {
fmt.println(entity.x, entity.y, entity.z)
}
// Subtype polymorphism
// It is possible to get subtype polymorphism, similar to inheritance-like
// functionality in C++, but without the requirement of vtables or unknown
// struct layout:
Colour :: struct {r, g, b, a: u8}
Frog :: struct {
ribbit_volume: f32,
using entity: Entity,
colour: Colour,
}
frog: Frog
// Both work
foo(&frog.entity)
foo(&frog)
frog.x = 123
// Note: using can be applied to arbitrarily many things, which allows
// the ability to have multiple subtype polymorphism (but also its issues).
// Note: using’d fields can still be referred by name.
}
}
implicit_context_system :: proc() {
fmt.println("\n# implicit context system")
// In each scope, there is an implicit value named context. This
// context variable is local to each scope and is implicitly passed
// by pointer to any procedure call in that scope (if the procedure
// has the Odin calling convention).
// The main purpose of the implicit context system is for the ability
// to intercept third-party code and libraries and modify their
// functionality. One such case is modifying how a library allocates
// something or logs something. In C, this was usually achieved with
// the library defining macros which could be overridden so that the
// user could define what he wanted. However, not many libraries
// supported this in many languages by default which meant intercepting
// third-party code to see what it does and to change how it does it is
// not possible.
c := context // copy the current scope's context
context.user_index = 456
{
context.allocator = my_custom_allocator()
context.user_index = 123
what_a_fool_believes() // the `context` for this scope is implicitly passed to `what_a_fool_believes`
}
// `context` value is local to the scope it is in
assert(context.user_index == 456)
what_a_fool_believes :: proc() {
c := context // this `context` is the same as the parent procedure that it was called from
// From this example, context.user_index == 123
// A context.allocator is assigned to the return value of `my_custom_allocator()`
assert(context.user_index == 123)
// The memory management procedure use the `context.allocator` by
// default unless explicitly specified otherwise
china_grove := new(int)
free(china_grove)
_ = c
}
my_custom_allocator :: mem.nil_allocator
_ = c
// By default, the context value has default values for its parameters which is
// decided in the package runtime. What the defaults are are compiler specific.
// To see what the implicit context value contains, please see the following
// definition in package runtime.
}
parametric_polymorphism :: proc() {
fmt.println("\n# parametric polymorphism")
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: typeid) -> ^T {
t := cast(^T)mem.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_Slot :: struct($Key, $Value: typeid) {
occupied: bool,
hash: u32,
key: Key,
value: Value,
}
TABLE_SIZE_MIN :: 32
Table :: struct($Key, $Value: typeid) {
count: int,
allocator: mem.Allocator,
slots: []Table_Slot(Key, Value),
}
// Only allow types that are specializations of a (polymorphic) slice
make_slice :: proc($T: typeid/[]$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 {
c.allocator = table.allocator
}
context = c
table.slots = make_slice(type_of(table.slots), max(capacity, TABLE_SIZE_MIN))
}
expand :: proc(table: ^$T/Table) {
c := context
if table.allocator.procedure != nil {
c.allocator = table.allocator
}
context = c
old_slots := table.slots
defer delete(old_slots)
cap := max(2*len(table.slots), TABLE_SIZE_MIN)
allocate(table, cap)
for s in old_slots {
if s.occupied {
put(table, s.key, s.value)
}
}
}
// 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(len(table.slots)) {
expand(table)
}
assert(table.count <= len(table.slots))
index = int(hash % u32(len(table.slots)))
for table.slots[index].occupied {
if index += 1; index >= len(table.slots) {
index = 0
}
}
table.count += 1
}