Browse Source

Merge pull request #799 from odin-lang/parser-experiments

Add experimental `-insert-semicolon` functionality to tokenizer and parser
gingerBill 4 years ago
parent
commit
1eb1bffd89

+ 1 - 1
core/math/math.odin

@@ -10,7 +10,7 @@ Float_Class :: enum {
 	Neg_Zero,  // the negative zero
 	NaN,       // Not-A-Number (NaN)
 	Inf,       // positive infinity
-	Neg_Inf    // negative infinity
+	Neg_Inf,   // negative infinity
 };
 
 TAU          :: 6.28318530717958647692528676655900576;

+ 1 - 1
core/mem/allocators.odin

@@ -935,7 +935,7 @@ Small_Allocator :: struct(BUFFER_SIZE: int)
 	start:      uintptr,
 	curr:       uintptr,
 	end:        uintptr,
-	chunk_size: int
+	chunk_size: int,
 }
 
 small_allocator :: proc(s: ^$S/Small_Allocator, backing := context.allocator) -> (a: Allocator) {

+ 4 - 4
core/os/stat_windows.odin

@@ -114,10 +114,10 @@ cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
 	buf = buf[:N];
 
 	if len(buf) >= 4 {
-		if buf[0] == '\\'
-		&& buf[1] == '\\'
-		&& buf[2] == '?'
-		&& buf[3] == '\\' {
+		if buf[0] == '\\' &&
+		   buf[1] == '\\' &&
+		   buf[2] == '?'  &&
+		   buf[3] == '\\' {
 			buf = buf[4:];
 		}
 	}

+ 2 - 2
core/runtime/core.odin

@@ -71,7 +71,7 @@ Type_Info_Boolean    :: struct {};
 Type_Info_Any        :: struct {};
 Type_Info_Type_Id    :: struct {};
 Type_Info_Pointer :: struct {
-	elem: ^Type_Info // nil -> rawptr
+	elem: ^Type_Info, // nil -> rawptr
 };
 Type_Info_Procedure :: struct {
 	params:     ^Type_Info, // Type_Info_Tuple
@@ -312,7 +312,7 @@ Logger_Option :: enum {
 	Line,
 	Procedure,
 	Terminal_Color,
-	Thread_Id
+	Thread_Id,
 }
 
 Logger_Options :: bit_set[Logger_Option];

+ 1 - 2
core/sys/windows/kernel32.odin

@@ -183,8 +183,7 @@ foreign kernel32 {
 		lpOverlapped: LPOVERLAPPED,
 	) -> BOOL ---
 	CloseHandle :: proc(hObject: HANDLE) -> BOOL ---
-	MoveFileExW :: proc(lpExistingFileName: LPCWSTR, lpNewFileName: LPCWSTR, dwFlags: DWORD)
-	-> BOOL ---
+	MoveFileExW :: proc(lpExistingFileName: LPCWSTR, lpNewFileName: LPCWSTR, dwFlags: DWORD) -> BOOL ---
 	SetFilePointerEx :: proc(
 		hFile: HANDLE,
 		liDistanceToMove: LARGE_INTEGER,

+ 8 - 8
core/sys/windows/types.odin

@@ -118,12 +118,12 @@ SYNCHRONIZE: DWORD : 0x00100000;
 GENERIC_READ: DWORD : 0x80000000;
 GENERIC_WRITE: DWORD : 0x40000000;
 STANDARD_RIGHTS_WRITE: DWORD : READ_CONTROL;
-FILE_GENERIC_WRITE: DWORD : STANDARD_RIGHTS_WRITE
-	| FILE_WRITE_DATA
-	| FILE_WRITE_ATTRIBUTES
-	| FILE_WRITE_EA
-	| FILE_APPEND_DATA
-	| SYNCHRONIZE;
+FILE_GENERIC_WRITE: DWORD : STANDARD_RIGHTS_WRITE |
+	FILE_WRITE_DATA |
+	FILE_WRITE_ATTRIBUTES |
+	FILE_WRITE_EA |
+	FILE_APPEND_DATA |
+	SYNCHRONIZE;
 
 FILE_FLAG_OPEN_REPARSE_POINT: DWORD : 0x00200000;
 FILE_FLAG_BACKUP_SEMANTICS: DWORD : 0x02000000;
@@ -722,7 +722,7 @@ SYSTEM_INFO :: struct {
 
 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw
 OSVERSIONINFOEXW :: struct {
-    dwOSVersionInfoSize: ULONG, 
+    dwOSVersionInfoSize: ULONG,
     dwMajorVersion:      ULONG,
     dwMinorVersion:      ULONG,
     dwBuildNumber:       ULONG,
@@ -733,4 +733,4 @@ OSVERSIONINFOEXW :: struct {
     wSuiteMask:          USHORT,
     wProductType:        UCHAR,
     wReserved:           UCHAR,
-};
+};

+ 2024 - 0
examples/demo_insert_semicolon/demo.odin

@@ -0,0 +1,2024 @@
+package main
+
+import "core:fmt"
+import "core:mem"
+import "core:os"
+import "core:thread"
+import "core:time"
+import "core:reflect"
+import "core:runtime"
+import "intrinsics"
+
+/*
+	The Odin programming language is fast, concise, readable, pragmatic and open sourced.
+	It is designed with the intent of replacing C with the following goals:
+	 * simplicity
+	 * high performance
+	 * built for modern systems
+	 * 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
+	Overview of Odin - https://odin-lang.org/docs/overview/
+		An overview of the Odin programming language.
+	Frequently Asked Questions (FAQ) - https://odin-lang.org/docs/faq/
+		Answers to common questions about Odin.
+*/
+
+the_basics :: proc() {
+	fmt.println("\n# the basics")
+
+	{ // The Basics
+		fmt.println("Hellope")
+
+		// 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 number constant is possible to 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 literals which can be implicity conver to `f64`
+				// No need for any suffixes or decimal places like in other languages
+				// 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, this produces an infinite loop:
+		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.
+
+		switch arch := ODIN_ARCH; arch {
+		case "386":
+			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 != 0 {
+				// 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 == "386" {
+			fmt.println("32 bit")
+		} else when ODIN_ARCH == "amd64" {
+			fmt.println("64 bit")
+		} else {
+			fmt.println("Unsupported 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
+}
+
+
+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 to nice 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.
+	}
+	{ // using on an enum declaration
+
+		using Foo :: enum {A, B, C}
+
+		f0 := A
+		f1 := B
+		f2 := C
+		fmt.println(f0, f1, f2)
+		fmt.println(len(Foo))
+	}
+}
+
+
+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
+		// An 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)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
+			}
+
+			slot := &table.slots[index]
+			slot.occupied = true
+			slot.hash     = hash
+			slot.key      = key
+			slot.value    = value
+		}
+
+
+		// find :: proc(table: ^$T/Table, key: T.Key) -> (T.Value, bool) {
+		find :: proc(table: ^Table($Key, $Value), key: Key) -> (Value, bool) {
+			hash := get_hash(key)
+			index := find_index(table, key, hash)
+			if index < 0 {
+				return Value{}, false
+			}
+			return table.slots[index].value, true
+		}
+
+		find_index :: proc(table: ^Table($Key, $Value), key: Key, hash: u32) -> int {
+			if len(table.slots) <= 0 {
+				return -1
+			}
+
+			index := int(hash % u32(len(table.slots)))
+			for table.slots[index].occupied {
+				if table.slots[index].hash == hash {
+					if table.slots[index].key == key {
+						return index
+					}
+				}
+
+				if index += 1; index >= len(table.slots) {
+					index = 0
+				}
+			}
+
+			return -1
+		}
+
+		get_hash :: proc(s: string) -> u32 { // fnv32a
+			h: u32 = 0x811c9dc5
+			for i in 0..<len(s) {
+				h = (h ~ u32(s[i])) * 0x01000193
+			}
+			return h
+		}
+
+
+		table: Table(string, int)
+
+		for i in 0..36 { put(&table, "Hellope", i) }
+		for i in 0..42 { put(&table, "World!",  i) }
+
+		found, _ := find(&table, "Hellope")
+		fmt.printf("`found` is %v\n", found)
+
+		found, _ = find(&table, "World!")
+		fmt.printf("`found` is %v\n", found)
+
+		// I would not personally design a hash table like this in production
+		// but this is a nice basic example
+		// A better approach would either use a `u64` or equivalent for the key
+		// and let the user specify the hashing function or make the user store
+		// the hashing procedure with the table
+	}
+
+	{ // Parametric polymorphic union
+		Error :: enum {
+			Foo0,
+			Foo1,
+			Foo2,
+			Foo3,
+		}
+		Para_Union :: union(T: typeid) {T, Error}
+		r: Para_Union(int)
+		fmt.println(typeid_of(type_of(r)))
+
+		fmt.println(r)
+		r = 123
+		fmt.println(r)
+		r = Error.Foo0 // r = .Foo0; is allow too, see implicit selector expressions below
+		fmt.println(r)
+	}
+
+	{ // Polymorphic names
+		foo :: proc($N: $I, $T: typeid) -> (res: [N]T) {
+			// `N` is the constant value passed
+			// `I` is the type of N
+			// `T` is the type passed
+			fmt.printf("Generating an array of type %v from the value %v of type %v\n",
+					   typeid_of(type_of(res)), N, typeid_of(I))
+			for i in 0..<N {
+				res[i] = T(i*i)
+			}
+			return
+		}
+
+		T :: int
+		array := foo(4, T)
+		for v, i in array {
+			assert(v == T(i*i))
+		}
+
+		// Matrix multiplication
+		mul :: proc(a: [$M][$N]$T, b: [N][$P]T) -> (c: [M][P]T) {
+			for i in 0..<M {
+				for j in 0..<P {
+					for k in 0..<N {
+						c[i][j] += a[i][k] * b[k][j]
+					}
+				}
+			}
+			return
+		}
+
+		x := [2][3]f32{
+			{1, 2, 3},
+			{3, 2, 1},
+		}
+		y := [3][2]f32{
+			{0, 8},
+			{6, 2},
+			{8, 4},
+		}
+		z := mul(x, y)
+		assert(z == {{36, 24}, {20, 32}})
+	}
+}
+
+
+prefix_table := [?]string{
+	"White",
+	"Red",
+	"Green",
+	"Blue",
+	"Octarine",
+	"Black",
+}
+
+threading_example :: proc() {
+	fmt.println("\n# threading_example")
+
+	{ // Basic Threads
+		fmt.println("\n## Basic Threads")
+			worker_proc :: proc(t: ^thread.Thread) {
+			for iteration in 1..5 {
+				fmt.printf("Thread %d is on iteration %d\n", t.user_index, iteration)
+				fmt.printf("`%s`: iteration %d\n", prefix_table[t.user_index], iteration)
+				time.sleep(1 * time.Millisecond)
+			}
+		}
+
+		threads := make([dynamic]^thread.Thread, 0, len(prefix_table))
+		defer delete(threads)
+
+		for in prefix_table {
+			if t := thread.create(worker_proc); t != nil {
+				t.init_context = context
+				t.user_index = len(threads)
+				append(&threads, t)
+				thread.start(t)
+			}
+		}
+
+		for len(threads) > 0 {
+			for i := 0; i < len(threads); /**/ {
+				if t := threads[i]; thread.is_done(t) {
+					fmt.printf("Thread %d is done\n", t.user_index)
+					thread.destroy(t)
+
+					ordered_remove(&threads, i)
+				} else {
+					i += 1
+				}
+			}
+		}
+	}
+
+	{ // Thread Pool
+		fmt.println("\n## Thread Pool")
+		task_proc :: proc(t: ^thread.Task) {
+			index := t.user_index % len(prefix_table)
+			for iteration in 1..5 {
+				fmt.printf("Worker Task %d is on iteration %d\n", t.user_index, iteration)
+				fmt.printf("`%s`: iteration %d\n", prefix_table[index], iteration)
+				time.sleep(1 * time.Millisecond)
+			}
+		}
+
+		pool: thread.Pool
+		thread.pool_init(pool=&pool, thread_count=3)
+		defer thread.pool_destroy(&pool)
+
+
+		for i in 0..<30 {
+			thread.pool_add_task(pool=&pool, procedure=task_proc, data=nil, user_index=i)
+		}
+
+		thread.pool_start(&pool)
+		thread.pool_wait_and_process(&pool)
+	}
+}
+
+
+array_programming :: proc() {
+	fmt.println("\n# array programming")
+	{
+		a := [3]f32{1, 2, 3}
+		b := [3]f32{5, 6, 7}
+		c := a * b
+		d := a + b
+		e := 1 +  (c - d) / 2
+		fmt.printf("%.1f\n", e) // [0.5, 3.0, 6.5]
+	}
+
+	{
+		a := [3]f32{1, 2, 3}
+		b := swizzle(a, 2, 1, 0)
+		assert(b == [3]f32{3, 2, 1})
+
+		c := swizzle(a, 0, 0)
+		assert(c == [2]f32{1, 1})
+		assert(c == 1)
+	}
+
+	{
+		Vector3 :: distinct [3]f32
+		a := Vector3{1, 2, 3}
+		b := Vector3{5, 6, 7}
+		c := (a * b)/2 + 1
+		d := c.x + c.y + c.z
+		fmt.printf("%.1f\n", d) // 22.0
+
+		cross :: proc(a, b: Vector3) -> Vector3 {
+			i := swizzle(a, 1, 2, 0) * swizzle(b, 2, 0, 1)
+			j := swizzle(a, 2, 0, 1) * swizzle(b, 1, 2, 0)
+			return i - j
+		}
+
+		blah :: proc(a: Vector3) -> f32 {
+			return a.x + a.y + a.z
+		}
+
+		x := cross(a, b)
+		fmt.println(x)
+		fmt.println(blah(x))
+	}
+}
+
+map_type :: proc() {
+	fmt.println("\n# map type")
+
+	m := make(map[string]int)
+	defer delete(m)
+
+	m["Bob"] = 2
+	m["Ted"] = 5
+	fmt.println(m["Bob"])
+
+	delete_key(&m, "Ted")
+
+	// If an element of a key does not exist, the zero value of the
+	// element will be returned. To check to see if an element exists
+	// can be done in two ways:
+	elem, ok := m["Bob"]
+	exists := "Bob" in m
+	_, _ = elem, ok
+	_ = exists
+}
+
+implicit_selector_expression :: proc() {
+	fmt.println("\n# implicit selector expression")
+
+	Foo :: enum {A, B, C}
+
+	f: Foo
+	f = Foo.A
+	f = .A
+
+	BAR :: bit_set[Foo]{.B, .C}
+
+	switch f {
+	case .A:
+		fmt.println("HITHER")
+	case .B:
+		fmt.println("NEVER")
+	case .C:
+		fmt.println("FOREVER")
+	}
+
+	my_map := make(map[Foo]int)
+	defer delete(my_map)
+
+	my_map[.A] = 123
+	my_map[Foo.B] = 345
+
+	fmt.println(my_map[.A] + my_map[Foo.B] + my_map[.C])
+}
+
+
+partial_switch :: proc() {
+	fmt.println("\n# partial_switch")
+	{ // enum
+		Foo :: enum {
+			A,
+			B,
+			C,
+			D,
+		}
+
+		f := Foo.A
+		switch f {
+		case .A: fmt.println("A")
+		case .B: fmt.println("B")
+		case .C: fmt.println("C")
+		case .D: fmt.println("D")
+		case:    fmt.println("?")
+		}
+
+		#partial switch f {
+		case .A: fmt.println("A")
+		case .D: fmt.println("D")
+		}
+	}
+	{ // union
+		Foo :: union {int, bool}
+		f: Foo = 123
+		switch in f {
+		case int:  fmt.println("int")
+		case bool: fmt.println("bool")
+		case:
+		}
+
+		#partial switch in f {
+		case bool: fmt.println("bool")
+		}
+	}
+}
+
+cstring_example :: proc() {
+	fmt.println("\n# cstring_example")
+
+	W :: "Hellope"
+	X :: cstring(W)
+	Y :: string(X)
+
+	w := W
+	_ = w
+	x: cstring = X
+	y: string = Y
+	z := string(x)
+	fmt.println(x, y, z)
+	fmt.println(len(x), len(y), len(z))
+	fmt.println(len(W), len(X), len(Y))
+	// IMPORTANT NOTE for cstring variables
+	// len(cstring) is O(N)
+	// cast(string)cstring is O(N)
+}
+
+bit_set_type :: proc() {
+	fmt.println("\n# bit_set type")
+
+	{
+		using Day :: enum {
+			Sunday,
+			Monday,
+			Tuesday,
+			Wednesday,
+			Thursday,
+			Friday,
+			Saturday,
+		}
+
+		Days :: distinct bit_set[Day]
+		WEEKEND :: Days{Sunday, Saturday}
+
+		d: Days
+		d = {Sunday, Monday}
+		e := d | WEEKEND
+		e |= {Monday}
+		fmt.println(d, e)
+
+		ok := Saturday in e // `in` is only allowed for `map` and `bit_set` types
+		fmt.println(ok)
+		if Saturday in e {
+			fmt.println("Saturday in", e)
+		}
+		X :: Saturday in WEEKEND // Constant evaluation
+		fmt.println(X)
+		fmt.println("Cardinality:", card(e))
+	}
+	{
+		x: bit_set['A'..'Z']
+		#assert(size_of(x) == size_of(u32))
+		y: bit_set[0..8; u16]
+		fmt.println(typeid_of(type_of(x))) // bit_set[A..Z]
+		fmt.println(typeid_of(type_of(y))) // bit_set[0..8; u16]
+
+		incl(&x, 'F')
+		assert('F' in x)
+		excl(&x, 'F')
+		assert('F' not_in x)
+
+		y |= {1, 4, 2}
+		assert(2 in y)
+	}
+	{
+		Letters :: bit_set['A'..'Z']
+		a := Letters{'A', 'B'}
+		b := Letters{'A', 'B', 'C', 'D', 'F'}
+		c := Letters{'A', 'B'}
+
+		assert(a <= b) // 'a' is a subset of 'b'
+		assert(b >= a) // 'b' is a superset of 'a'
+		assert(a < b)  // 'a' is a strict subset of 'b'
+		assert(b > a)  // 'b' is a strict superset of 'a'
+
+		assert(!(a < c)) // 'a' is a not strict subset of 'c'
+		assert(!(c > a)) // 'c' is a not strict superset of 'a'
+	}
+}
+
+deferred_procedure_associations :: proc() {
+	fmt.println("\n# deferred procedure associations")
+
+	@(deferred_out=closure)
+	open :: proc(s: string) -> bool {
+		fmt.println(s)
+		return true
+	}
+
+	closure :: proc(ok: bool) {
+		fmt.println("Goodbye?", ok)
+	}
+
+	if open("Welcome") {
+		fmt.println("Something in the middle, mate.")
+	}
+}
+
+reflection :: proc() {
+	fmt.println("\n# reflection")
+
+	Foo :: struct {
+		x: int    `tag1`,
+		y: string `json:"y_field"`,
+		z: bool, // no tag
+	}
+
+	id := typeid_of(Foo)
+	names := reflect.struct_field_names(id)
+	types := reflect.struct_field_types(id)
+	tags  := reflect.struct_field_tags(id)
+
+	assert(len(names) == len(types) && len(names) == len(tags))
+
+	fmt.println("Foo :: struct {")
+	for tag, i in tags {
+		name, type := names[i], types[i]
+		if tag != "" {
+			fmt.printf("\t%s: %T `%s`,\n", name, type, tag)
+		} else {
+			fmt.printf("\t%s: %T,\n", name, type)
+		}
+	}
+	fmt.println("}")
+
+
+	for tag, i in tags {
+		if val, ok := reflect.struct_tag_lookup(tag, "json"); ok {
+			fmt.printf("json: %s -> %s\n", names[i], val)
+		}
+	}
+}
+
+quaternions :: proc() {
+	// Not just an April Fool's Joke any more, but a fully working thing!
+	fmt.println("\n# quaternions")
+
+	{ // Quaternion operations
+		q := 1 + 2i + 3j + 4k
+		r := quaternion(5, 6, 7, 8)
+		t := q * r
+		fmt.printf("(%v) * (%v) = %v\n", q, r, t)
+		v := q / r
+		fmt.printf("(%v) / (%v) = %v\n", q, r, v)
+		u := q + r
+		fmt.printf("(%v) + (%v) = %v\n", q, r, u)
+		s := q - r
+		fmt.printf("(%v) - (%v) = %v\n", q, r, s)
+	}
+	{ // The quaternion types
+		q128: quaternion128 // 4xf32
+		q256: quaternion256 // 4xf64
+		q128 = quaternion(1, 0, 0, 0)
+		q256 = 1 // quaternion(1, 0, 0, 0)
+	}
+	{ // Built-in procedures
+		q := 1 + 2i + 3j + 4k
+		fmt.println("q =", q)
+		fmt.println("real(q) =", real(q))
+		fmt.println("imag(q) =", imag(q))
+		fmt.println("jmag(q) =", jmag(q))
+		fmt.println("kmag(q) =", kmag(q))
+		fmt.println("conj(q) =", conj(q))
+		fmt.println("abs(q)  =", abs(q))
+	}
+	{ // Conversion of a complex type to a quaternion type
+		c := 1 + 2i
+		q := quaternion256(c)
+		fmt.println(c)
+		fmt.println(q)
+	}
+	{ // Memory layout of Quaternions
+		q := 1 + 2i + 3j + 4k
+		a := transmute([4]f64)q
+		fmt.println("Quaternion memory layout: xyzw/(ijkr)")
+		fmt.println(q) // 1.000+2.000i+3.000j+4.000k
+		fmt.println(a) // [2.000, 3.000, 4.000, 1.000]
+	}
+}
+
+inline_for_statement :: proc() {
+	fmt.println("\n#inline for statements")
+
+	// 'inline for' works the same as if the 'inline' prefix did not
+	// exist but these ranged loops are explicitly unrolled which can
+	// be very very useful for certain optimizations
+
+	fmt.println("Ranges")
+	inline for x, i in 1..<4 {
+		fmt.println(x, i)
+	}
+
+	fmt.println("Strings")
+	inline for r, i in "Hello, 世界" {
+		fmt.println(r, i)
+	}
+
+	fmt.println("Arrays")
+	inline for elem, idx in ([4]int{1, 4, 9, 16}) {
+		fmt.println(elem, idx)
+	}
+
+
+	Foo_Enum :: enum {
+		A = 1,
+		B,
+		C = 6,
+		D,
+	}
+	fmt.println("Enum types")
+	inline for elem, idx in Foo_Enum {
+		fmt.println(elem, idx)
+	}
+}
+
+where_clauses :: proc() {
+	fmt.println("\n#procedure 'where' clauses")
+
+	{ // Sanity checks
+		simple_sanity_check :: proc(x: [2]int)
+			where len(x) > 1,
+				  type_of(x) == [2]int {
+			fmt.println(x)
+		}
+	}
+	{ // Parametric polymorphism checks
+		cross_2d :: proc(a, b: $T/[2]$E) -> E
+			where intrinsics.type_is_numeric(E) {
+			return a.x*b.y - a.y*b.x
+		}
+		cross_3d :: proc(a, b: $T/[3]$E) -> T
+			where intrinsics.type_is_numeric(E) {
+			x := a.y*b.z - a.z*b.y
+			y := a.z*b.x - a.x*b.z
+			z := a.x*b.y - a.y*b.z
+			return T{x, y, z}
+		}
+
+		a := [2]int{1, 2}
+		b := [2]int{5, -3}
+		fmt.println(cross_2d(a, b))
+
+		x := [3]f32{1, 4, 9}
+		y := [3]f32{-5, 0, 3}
+		fmt.println(cross_3d(x, y))
+
+		// Failure case
+		// i := [2]bool{true, false}
+		// j := [2]bool{false, true}
+		// fmt.println(cross_2d(i, j))
+
+	}
+
+	{ // Procedure groups usage
+		foo :: proc(x: [$N]int) -> bool
+			where N > 2 {
+			fmt.println(#procedure, "was called with the parameter", x)
+			return true
+		}
+
+		bar :: proc(x: [$N]int) -> bool
+			where 0 < N,
+				  N <= 2 {
+			fmt.println(#procedure, "was called with the parameter", x)
+			return false
+		}
+
+		baz :: proc{foo, bar}
+
+		x := [3]int{1, 2, 3}
+		y := [2]int{4, 9}
+		ok_x := baz(x)
+		ok_y := baz(y)
+		assert(ok_x == true)
+		assert(ok_y == false)
+	}
+
+	{ // Record types
+		Foo :: struct(T: typeid, N: int)
+			where intrinsics.type_is_integer(T),
+				  N > 2 {
+			x: [N]T,
+			y: [N-2]T,
+		}
+
+		T :: i32
+		N :: 5
+		f: Foo(T, N)
+		#assert(size_of(f) == (N+N-2)*size_of(T))
+	}
+}
+
+
+when ODIN_OS == "windows" {
+	foreign import kernel32 "system:kernel32.lib"
+}
+
+foreign_system :: proc() {
+	fmt.println("\n#foreign system")
+	when ODIN_OS == "windows" {
+		// It is sometimes necessarily to interface with foreign code,
+		// such as a C library. In Odin, this is achieved through the
+		// foreign system. You can “import” a library into the code
+		// using the same semantics as a normal import declaration.
+
+		// This foreign import declaration will create a
+		// “foreign import name” which can then be used to associate
+		// entities within a foreign block.
+
+		foreign kernel32 {
+			ExitProcess :: proc "stdcall" (exit_code: u32) ---
+		}
+
+		// Foreign procedure declarations have the cdecl/c calling
+		// convention by default unless specified otherwise. Due to
+		// foreign procedures do not have a body declared within this
+		// code, you need append the --- symbol to the end to distinguish
+		// it as a procedure literal without a body and not a procedure type.
+
+		// The attributes system can be used to change specific properties
+		// of entities declared within a block:
+
+		@(default_calling_convention = "std")
+		foreign kernel32 {
+			@(link_name="GetLastError") get_last_error :: proc() -> i32 ---
+		}
+
+		// Example using the link_prefix attribute
+		@(default_calling_convention = "std")
+		@(link_prefix = "Get")
+		foreign kernel32 {
+			LastError :: proc() -> i32 ---
+		}
+	}
+}
+
+ranged_fields_for_array_compound_literals :: proc() {
+	fmt.println("\n#ranged fields for array compound literals")
+	{ // Normal Array Literal
+		foo := [?]int{1, 4, 9, 16}
+		fmt.println(foo)
+	}
+	{ // Indexed
+		foo := [?]int{
+			3 = 16,
+			1 = 4,
+			2 = 9,
+			0 = 1,
+		}
+		fmt.println(foo)
+	}
+	{ // Ranges
+		i := 2
+		foo := [?]int {
+			0 = 123,
+			5..9 = 54,
+			10..<16 = i*3 + (i-1)*2,
+		}
+		#assert(len(foo) == 16)
+		fmt.println(foo); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8]
+	}
+	{ // Slice and Dynamic Array support
+		i := 2
+		foo_slice := []int {
+			0 = 123,
+			5..9 = 54,
+			10..<16 = i*3 + (i-1)*2,
+		}
+		assert(len(foo_slice) == 16)
+		fmt.println(foo_slice); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8]
+
+		foo_dynamic_array := [dynamic]int {
+			0 = 123,
+			5..9 = 54,
+			10..<16 = i*3 + (i-1)*2,
+		}
+		assert(len(foo_dynamic_array) == 16)
+		fmt.println(foo_dynamic_array); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8]
+	}
+}
+
+deprecated_attribute :: proc() {
+	@(deprecated="Use foo_v2 instead")
+	foo_v1 :: proc(x: int) {
+		fmt.println("foo_v1")
+	}
+	foo_v2 :: proc(x: int) {
+		fmt.println("foo_v2")
+	}
+
+	// NOTE: Uncomment to see the warning messages
+	// foo_v1(1)
+}
+
+range_statements_with_multiple_return_values :: proc() {
+	// IMPORTANT NOTE(bill, 2019-11-02): This feature is subject to be changed/removed
+	fmt.println("\n#range statements with multiple return values")
+	My_Iterator :: struct {
+		index: int,
+		data:  []i32,
+	}
+	make_my_iterator :: proc(data: []i32) -> My_Iterator {
+		return My_Iterator{data = data}
+	}
+	my_iterator :: proc(it: ^My_Iterator) -> (val: i32, idx: int, cond: bool) {
+		if cond = it.index < len(it.data); cond {
+			val = it.data[it.index]
+			idx = it.index
+			it.index += 1
+		}
+		return
+	}
+
+	data := make([]i32, 6)
+	for _, i in data {
+		data[i] = i32(i*i)
+	}
+
+	{
+		it := make_my_iterator(data)
+		for val in my_iterator(&it) {
+			fmt.println(val)
+		}
+	}
+	{
+		it := make_my_iterator(data)
+		for val, idx in my_iterator(&it) {
+			fmt.println(val, idx)
+		}
+	}
+	{
+		it := make_my_iterator(data)
+		for {
+			val, _, cond := my_iterator(&it)
+			if !cond {
+				break
+			}
+			fmt.println(val)
+		}
+	}
+}
+
+
+soa_struct_layout :: proc() {
+	// IMPORTANT NOTE(bill, 2019-11-03): This feature is subject to be changed/removed
+	// NOTE(bill): Most likely #soa [N]T
+	fmt.println("\n#SOA Struct Layout")
+
+	{
+		Vector3 :: struct {x, y, z: f32}
+
+		N :: 2
+		v_aos: [N]Vector3
+		v_aos[0].x = 1
+		v_aos[0].y = 4
+		v_aos[0].z = 9
+
+		fmt.println(len(v_aos))
+		fmt.println(v_aos[0])
+		fmt.println(v_aos[0].x)
+		fmt.println(&v_aos[0].x)
+
+		v_aos[1] = {0, 3, 4}
+		v_aos[1].x = 2
+		fmt.println(v_aos[1])
+		fmt.println(v_aos)
+
+		v_soa: #soa[N]Vector3
+
+		v_soa[0].x = 1
+		v_soa[0].y = 4
+		v_soa[0].z = 9
+
+
+		// Same syntax as AOS and treat as if it was an array
+		fmt.println(len(v_soa))
+		fmt.println(v_soa[0])
+		fmt.println(v_soa[0].x)
+		fmt.println(&v_soa[0].x)
+		v_soa[1] = {0, 3, 4}
+		v_soa[1].x = 2
+		fmt.println(v_soa[1])
+
+		// Can use SOA syntax if necessary
+		v_soa.x[0] = 1
+		v_soa.y[0] = 4
+		v_soa.z[0] = 9
+		fmt.println(v_soa.x[0])
+
+		// Same pointer addresses with both syntaxes
+		assert(&v_soa[0].x == &v_soa.x[0])
+
+
+		// Same fmt printing
+		fmt.println(v_aos)
+		fmt.println(v_soa)
+	}
+	{
+		// Works with arrays of length <= 4 which have the implicit fields xyzw/rgba
+		Vector3 :: distinct [3]f32
+
+		N :: 2
+		v_aos: [N]Vector3
+		v_aos[0].x = 1
+		v_aos[0].y = 4
+		v_aos[0].z = 9
+
+		v_soa: #soa[N]Vector3
+
+		v_soa[0].x = 1
+		v_soa[0].y = 4
+		v_soa[0].z = 9
+	}
+	{
+		// SOA Slices
+		// Vector3 :: struct {x, y, z: f32}
+		Vector3 :: struct {x: i8, y: i16, z: f32}
+
+		N :: 3
+		v: #soa[N]Vector3
+		v[0].x = 1
+		v[0].y = 4
+		v[0].z = 9
+
+		s: #soa[]Vector3
+		s = v[:]
+		assert(len(s) == N)
+		fmt.println(s)
+		fmt.println(s[0].x)
+
+		a := s[1:2]
+		assert(len(a) == 1)
+		fmt.println(a)
+
+		d: #soa[dynamic]Vector3
+
+		append_soa(&d, Vector3{1, 2, 3}, Vector3{4, 5, 9}, Vector3{-4, -4, 3})
+		fmt.println(d)
+		fmt.println(len(d))
+		fmt.println(cap(d))
+		fmt.println(d[:])
+	}
+}
+
+constant_literal_expressions :: proc() {
+	fmt.println("\n#constant literal expressions")
+
+	Bar :: struct {x, y: f32}
+	Foo :: struct {a, b: int, using c: Bar}
+
+	FOO_CONST :: Foo{b = 2, a = 1, c = {3, 4}}
+
+
+	fmt.println(FOO_CONST.a)
+	fmt.println(FOO_CONST.b)
+	fmt.println(FOO_CONST.c)
+	fmt.println(FOO_CONST.c.x)
+	fmt.println(FOO_CONST.c.y)
+	fmt.println(FOO_CONST.x); // using works as expected
+	fmt.println(FOO_CONST.y)
+
+	fmt.println("-------")
+
+	ARRAY_CONST :: [3]int{1 = 4, 2 = 9, 0 = 1}
+
+	fmt.println(ARRAY_CONST[0])
+	fmt.println(ARRAY_CONST[1])
+	fmt.println(ARRAY_CONST[2])
+
+	fmt.println("-------")
+
+	FOO_ARRAY_DEFAULTS :: [3]Foo{{}, {}, {}}
+	fmt.println(FOO_ARRAY_DEFAULTS[2].x)
+
+	fmt.println("-------")
+
+	Baz :: enum{A=5, B, C, D}
+	ENUM_ARRAY_CONST :: [Baz]int{.A .. .C = 1, .D = 16}
+
+	fmt.println(ENUM_ARRAY_CONST[.A])
+	fmt.println(ENUM_ARRAY_CONST[.B])
+	fmt.println(ENUM_ARRAY_CONST[.C])
+	fmt.println(ENUM_ARRAY_CONST[.D])
+
+	fmt.println("-------")
+
+	Partial_Baz :: enum{A=5, B, C, D=16}
+	#assert(len(Partial_Baz) < len(#partial [Partial_Baz]int))
+	PARTIAL_ENUM_ARRAY_CONST :: #partial [Partial_Baz]int{.A .. .C = 1, .D = 16}
+
+	fmt.println(PARTIAL_ENUM_ARRAY_CONST[.A])
+	fmt.println(PARTIAL_ENUM_ARRAY_CONST[.B])
+	fmt.println(PARTIAL_ENUM_ARRAY_CONST[.C])
+	fmt.println(PARTIAL_ENUM_ARRAY_CONST[.D])
+
+	fmt.println("-------")
+
+
+	STRING_CONST :: "Hellope!"
+
+	fmt.println(STRING_CONST[0])
+	fmt.println(STRING_CONST[2])
+	fmt.println(STRING_CONST[3])
+
+	fmt.println(STRING_CONST[0:5])
+	fmt.println(STRING_CONST[3:][:4])
+}
+
+union_maybe :: proc() {
+	fmt.println("\n#union #maybe")
+
+	Maybe :: union(T: typeid) #maybe {T}
+
+	i: Maybe(u8)
+	p: Maybe(^u8); // No tag is stored for pointers, nil is the sentinel value
+
+	#assert(size_of(i) == size_of(u8) + size_of(u8))
+	#assert(size_of(p) == size_of(^u8))
+
+	i = 123
+	x := i.?
+	y, y_ok := p.?
+	p = &x
+	z, z_ok := p.?
+
+	fmt.println(i, p)
+	fmt.println(x, &x)
+	fmt.println(y, y_ok)
+	fmt.println(z, z_ok)
+}
+
+dummy_procedure :: proc() {
+	fmt.println("dummy_procedure")
+}
+
+explicit_context_definition :: proc "c" () {
+	// Try commenting the following statement out below
+	context = runtime.default_context()
+
+	fmt.println("\n#explicit context definition")
+	dummy_procedure()
+}
+
+relative_data_types :: proc() {
+	fmt.println("\n#relative data types")
+
+	x: int = 123
+	ptr: #relative(i16) ^int
+	ptr = &x
+	fmt.println(ptr^)
+
+	arr := [3]int{1, 2, 3}
+	s := arr[:]
+	rel_slice: #relative(i16) []int
+	rel_slice = s
+	fmt.println(rel_slice)
+	fmt.println(rel_slice[:])
+	fmt.println(rel_slice[1])
+}
+
+pure_procedures :: proc() {
+	fmt.println("\n#pure procedures")
+
+	square :: proc "pure" (x: int) -> int {
+		return x*x + 1
+	}
+
+	do_math :: proc "pure" (x: int) -> int {
+		// Only "pure" procedure calls are allowed within a "pure" procedure
+		return square(x) + 1
+	}
+
+	x := do_math(5)
+	fmt.println(x)
+}
+
+main :: proc() {
+	when true {
+		the_basics()
+		control_flow()
+		named_proc_return_parameters()
+		explicit_procedure_overloading()
+		struct_type()
+		union_type()
+		using_statement()
+		implicit_context_system()
+		parametric_polymorphism()
+		array_programming()
+		map_type()
+		implicit_selector_expression()
+		partial_switch()
+		cstring_example()
+		bit_set_type()
+		deferred_procedure_associations()
+		reflection()
+		quaternions()
+		inline_for_statement()
+		where_clauses()
+		foreign_system()
+		ranged_fields_for_array_compound_literals()
+		deprecated_attribute()
+		range_statements_with_multiple_return_values()
+		threading_example()
+		soa_struct_layout()
+		constant_literal_expressions()
+		union_maybe()
+		explicit_context_definition()
+		relative_data_types()
+		pure_procedures()
+	}
+}

+ 1 - 0
src/build_settings.cpp

@@ -192,6 +192,7 @@ struct BuildContext {
 	bool   different_os;
 	bool   keep_object_files;
 	bool   disallow_do;
+	bool   insert_semicolon;
 
 	bool   use_llvm_api;
 

+ 7 - 1
src/main.cpp

@@ -588,6 +588,7 @@ enum BuildFlagKind {
 
 	BuildFlag_DisallowDo,
 	BuildFlag_DefaultToNilAllocator,
+	BuildFlag_InsertSemicolon,
 
 	BuildFlag_Compact,
 	BuildFlag_GlobalDefinitions,
@@ -697,7 +698,7 @@ bool parse_build_flags(Array<String> args) {
 
 	add_flag(&build_flags, BuildFlag_DisallowDo,            str_lit("disallow-do"),              BuildFlagParam_None, Command__does_check);
 	add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check);
-
+	add_flag(&build_flags, BuildFlag_InsertSemicolon,       str_lit("insert-semicolon"),         BuildFlagParam_None, Command__does_check);
 	add_flag(&build_flags, BuildFlag_Compact,           str_lit("compact"),            BuildFlagParam_None, Command_query);
 	add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None, Command_query);
 	add_flag(&build_flags, BuildFlag_GoToDefinitions,   str_lit("go-to-definitions"),  BuildFlagParam_None, Command_query);
@@ -706,6 +707,7 @@ bool parse_build_flags(Array<String> args) {
 	add_flag(&build_flags, BuildFlag_AllPackages,   str_lit("all-packages"), BuildFlagParam_None, Command_doc);
 
 
+
 #if defined(GB_SYSTEM_WINDOWS)
 	add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"),  BuildFlagParam_None, Command__does_build);
 	add_flag(&build_flags, BuildFlag_ResourceFile,   str_lit("resource"),          BuildFlagParam_String, Command__does_build);
@@ -1161,6 +1163,10 @@ bool parse_build_flags(Array<String> args) {
 							build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR = true;
 							break;
 
+						case BuildFlag_InsertSemicolon:
+							build_context.insert_semicolon = true;
+							break;
+
 						case BuildFlag_Compact:
 							if (!build_context.query_data_set_settings.ok) {
 								gb_printf_err("Invalid use of -compact flag, only allowed with 'odin query'\n");

+ 104 - 10
src/parser.cpp

@@ -1264,11 +1264,69 @@ bool peek_token_kind(AstFile *f, TokenKind kind) {
 	return false;
 }
 
+Token peek_token(AstFile *f) {
+	for (isize i = f->curr_token_index+1; i < f->tokens.count; i++) {
+		Token tok = f->tokens[i];
+		if (tok.kind == Token_Comment) {
+			continue;
+		}
+		return tok;
+	}
+	return {};
+}
+
+
+bool token_is_newline(Token const &tok) {
+	return tok.kind == Token_Semicolon && tok.string == "\n";
+}
+
+bool skip_possible_newline(AstFile *f) {
+	if ((f->tokenizer.flags & TokenizerFlag_InsertSemicolon) == 0) {
+		return false;
+	}
+	Token *prev = &f->curr_token;
+	if (prev->kind == Token_Semicolon && prev->string == "\n") {
+		advance_token(f);
+		return true;
+	}
+	return false;
+}
+
+bool skip_possible_newline_for_literal(AstFile *f) {
+	if ((f->tokenizer.flags & TokenizerFlag_InsertSemicolon) == 0) {
+		return false;
+	}
+	TokenPos curr_pos = f->curr_token.pos;
+	if (token_is_newline(f->curr_token)) {
+		Token next = peek_token(f);
+		if (curr_pos.line+1 >= next.pos.line) {
+			switch (next.kind) {
+			case Token_OpenBrace:
+			case Token_else:
+			case Token_where:
+				advance_token(f);
+				return true;
+			}
+		}
+	}
+
+	return false;
+}
+
+String token_to_string(Token const &tok) {
+	String p = token_strings[tok.kind];
+	if (token_is_newline(tok)) {
+		p = str_lit("newline");
+	}
+	return p;
+}
+
+
 Token expect_token(AstFile *f, TokenKind kind) {
 	Token prev = f->curr_token;
 	if (prev.kind != kind) {
 		String c = token_strings[kind];
-		String p = token_strings[prev.kind];
+		String p = token_to_string(prev);
 		syntax_error(f->curr_token, "Expected '%.*s', got '%.*s'", LIT(c), LIT(p));
 		if (prev.kind == Token_EOF) {
 			gb_exit(1);
@@ -1282,7 +1340,7 @@ Token expect_token(AstFile *f, TokenKind kind) {
 Token expect_token_after(AstFile *f, TokenKind kind, char const *msg) {
 	Token prev = f->curr_token;
 	if (prev.kind != kind) {
-		String p = token_strings[prev.kind];
+		String p = token_to_string(prev);
 		syntax_error(f->curr_token, "Expected '%.*s' after %s, got '%.*s'",
 		             LIT(token_strings[kind]),
 		             msg,
@@ -1313,11 +1371,13 @@ Token expect_operator(AstFile *f) {
 	} else if (prev.kind == Token_if || prev.kind == Token_when) {
 		// okay
 	} else if (!gb_is_between(prev.kind, Token__OperatorBegin+1, Token__OperatorEnd-1)) {
+		String p = token_to_string(prev);
 		syntax_error(f->curr_token, "Expected an operator, got '%.*s'",
-		             LIT(token_strings[prev.kind]));
+		             LIT(p));
 	} else if (!f->allow_range && is_token_range(prev)) {
+		String p = token_to_string(prev);
 		syntax_error(f->curr_token, "Expected an non-range operator, got '%.*s'",
-		             LIT(token_strings[prev.kind]));
+		             LIT(p));
 	}
 	advance_token(f);
 	return prev;
@@ -1326,8 +1386,9 @@ Token expect_operator(AstFile *f) {
 Token expect_keyword(AstFile *f) {
 	Token prev = f->curr_token;
 	if (!gb_is_between(prev.kind, Token__KeywordBegin+1, Token__KeywordEnd-1)) {
+		String p = token_to_string(prev);
 		syntax_error(f->curr_token, "Expected a keyword, got '%.*s'",
-		             LIT(token_strings[prev.kind]));
+		             LIT(p));
 	}
 	advance_token(f);
 	return prev;
@@ -1487,7 +1548,22 @@ void expect_semicolon(AstFile *f, Ast *s) {
 
 
 	if (s != nullptr) {
-		if (prev_token.pos.line != f->curr_token.pos.line) {
+		bool insert_semi = (f->tokenizer.flags & TokenizerFlag_InsertSemicolon) != 0;
+		if (insert_semi) {
+			switch (f->curr_token.kind) {
+			case Token_CloseBrace:
+			case Token_CloseParen:
+			case Token_else:
+			case Token_EOF:
+				return;
+
+			default:
+				if (is_semicolon_optional_for_node(f, s)) {
+					return;
+				}
+				break;
+			}
+		} else if (prev_token.pos.line != f->curr_token.pos.line) {
 			if (is_semicolon_optional_for_node(f, s)) {
 				return;
 			}
@@ -1505,14 +1581,16 @@ void expect_semicolon(AstFile *f, Ast *s) {
 			}
 		}
 		String node_string = ast_strings[s->kind];
+		String p = token_to_string(f->curr_token);
 		syntax_error(prev_token, "Expected ';' after %.*s, got %.*s",
-		             LIT(node_string), LIT(token_strings[f->curr_token.kind]));
+		             LIT(node_string), LIT(p));
 	} else {
 		switch (f->curr_token.kind) {
 		case Token_EOF:
 			return;
 		}
-		syntax_error(prev_token, "Expected ';'");
+		String p = token_to_string(f->curr_token);
+		syntax_error(prev_token, "Expected ';', got %.*s", LIT(p));
 	}
 	fix_advance_to_next_stmt(f);
 }
@@ -2013,6 +2091,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 		Token where_token = {};
 		Array<Ast *> where_clauses = {};
 		u64 tags = 0;
+		skip_possible_newline_for_literal(f);
 
 		if (f->curr_token.kind == Token_where) {
 			where_token = expect_token(f, Token_where);
@@ -2189,6 +2268,8 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 		Token where_token = {};
 		Array<Ast *> where_clauses = {};
 
+		skip_possible_newline_for_literal(f);
+
 		if (f->curr_token.kind == Token_where) {
 			where_token = expect_token(f, Token_where);
 			isize prev_level = f->expr_level;
@@ -2260,6 +2341,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 			syntax_error(f->curr_token, "#maybe and #no_nil cannot be applied together");
 		}
 
+		skip_possible_newline_for_literal(f);
 
 		Token where_token = {};
 		Array<Ast *> where_clauses = {};
@@ -3390,7 +3472,8 @@ bool parse_expect_field_separator(AstFile *f, Ast *param) {
 		return true;
 	}
 	if (token.kind == Token_Semicolon) {
-		syntax_error(f->curr_token, "Expected a comma, got a semicolon");
+		String p = token_to_string(token);
+		syntax_error(f->curr_token, "Expected a comma, got a %.*s", LIT(p));
 		advance_token(f);
 		return true;
 	}
@@ -3713,6 +3796,7 @@ Ast *parse_if_stmt(AstFile *f) {
 		}
 	} else {
 		body = parse_block_stmt(f, false);
+		skip_possible_newline_for_literal(f);
 	}
 
 	if (allow_token(f, Token_else)) {
@@ -3768,6 +3852,7 @@ Ast *parse_when_stmt(AstFile *f) {
 		}
 	} else {
 		body = parse_block_stmt(f, true);
+		skip_possible_newline_for_literal(f);
 	}
 
 	if (allow_token(f, Token_else)) {
@@ -3873,6 +3958,7 @@ Ast *parse_for_stmt(AstFile *f) {
 				}
 			} else {
 				body = parse_block_stmt(f, false);
+				skip_possible_newline_for_literal(f);
 			}
 			return ast_range_stmt(f, token, nullptr, nullptr, in_token, rhs, body);
 		}
@@ -3908,6 +3994,7 @@ Ast *parse_for_stmt(AstFile *f) {
 		}
 	} else {
 		body = parse_block_stmt(f, false);
+		skip_possible_newline_for_literal(f);
 	}
 
 	if (is_range) {
@@ -4189,6 +4276,8 @@ Ast *parse_attribute(AstFile *f, Token token, TokenKind open_kind, TokenKind clo
 	}
 	Ast *attribute = ast_attribute(f, token, open, close, elems);
 
+	skip_possible_newline(f);
+
 	Ast *decl = parse_stmt(f);
 	if (decl->kind == Ast_ValueDecl) {
 		array_add(&decl->ValueDecl.attributes, attribute);
@@ -4257,6 +4346,7 @@ Ast *parse_stmt(AstFile *f) {
 				}
 			} else {
 				body = parse_block_stmt(f, false);
+				skip_possible_newline_for_literal(f);
 			}
 			if (bad_stmt) {
 				return ast_bad_stmt(f, inline_token, f->curr_token);
@@ -4461,7 +4551,11 @@ ParseFileError init_ast_file(AstFile *f, String fullpath, TokenPos *err_pos) {
 	if (!string_ends_with(f->fullpath, str_lit(".odin"))) {
 		return ParseFile_WrongExtension;
 	}
-	TokenizerInitError err = init_tokenizer(&f->tokenizer, f->fullpath);
+	TokenizerFlags tokenizer_flags = TokenizerFlag_None;
+	if (build_context.insert_semicolon) {
+		tokenizer_flags = TokenizerFlag_InsertSemicolon;
+	}
+	TokenizerInitError err = init_tokenizer(&f->tokenizer, f->fullpath, tokenizer_flags);
 	if (err != TokenizerInit_None) {
 		switch (err) {
 		case TokenizerInit_Empty:

+ 101 - 14
src/tokenizer.cpp

@@ -525,6 +525,12 @@ struct TokenizerState {
 	u8 *  read_curr;   // pos from start
 	u8 *  line;        // current line pos
 	isize line_count;
+	bool  insert_semicolon;
+};
+
+enum TokenizerFlags {
+	TokenizerFlag_None = 0,
+	TokenizerFlag_InsertSemicolon = 1<<0,
 };
 
 struct Tokenizer {
@@ -540,6 +546,9 @@ struct Tokenizer {
 
 	isize error_count;
 	Array<String> allocated_strings;
+
+	TokenizerFlags flags;
+	bool insert_semicolon;
 };
 
 
@@ -550,15 +559,17 @@ TokenizerState save_tokenizer_state(Tokenizer *t) {
 	state.read_curr  = t->read_curr;
 	state.line       = t->line;
 	state.line_count = t->line_count;
+	state.insert_semicolon = t->insert_semicolon;
 	return state;
 }
 
 void restore_tokenizer_state(Tokenizer *t, TokenizerState *state) {
-	 t->curr_rune  = state->curr_rune;
-	 t->curr       = state->curr;
-	 t->read_curr  = state->read_curr;
-	 t->line       = state->line;
-	 t->line_count = state->line_count;
+	t->curr_rune  = state->curr_rune;
+	t->curr       = state->curr;
+	t->read_curr  = state->read_curr;
+	t->line       = state->line;
+	t->line_count = state->line_count;
+	t->insert_semicolon = state->insert_semicolon;
 }
 
 
@@ -613,7 +624,7 @@ void advance_to_next_rune(Tokenizer *t) {
 	}
 }
 
-TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) {
+TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath, TokenizerFlags flags = TokenizerFlag_None) {
 	TokenizerInitError err = TokenizerInit_None;
 
 	char *c_str = alloc_cstring(heap_allocator(), fullpath);
@@ -623,6 +634,7 @@ TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) {
 	gbFileContents fc = gb_file_read_contents(heap_allocator(), true, c_str);
 	gb_zero_item(t);
 
+	t->flags = flags;
 	t->fullpath = fullpath;
 	t->line_count = 1;
 
@@ -886,9 +898,13 @@ void tokenizer_get_token(Tokenizer *t, Token *token) {
 	// Skip whitespace
 	for (;;) {
 		switch (t->curr_rune) {
+		case '\n':
+			if (t->insert_semicolon) {
+				break;
+			}
+			/*fallthrough*/
 		case ' ':
 		case '\t':
-		case '\n':
 		case '\r':
 			advance_to_next_rune(t);
 			continue;
@@ -905,6 +921,8 @@ void tokenizer_get_token(Tokenizer *t, Token *token) {
 	token->pos.offset = t->curr - t->start;
 	token->pos.column = t->curr - t->line + 1;
 
+	bool insert_semicolon = false;
+
 	Rune curr_rune = t->curr_rune;
 	if (rune_is_letter(curr_rune)) {
 		token->kind = Token_Ident;
@@ -928,19 +946,51 @@ void tokenizer_get_token(Tokenizer *t, Token *token) {
 				}
 			}
 		}
+
+		switch (token->kind) {
+		case Token_Ident:
+		case Token_context:
+		case Token_typeid: // Dunno?
+		case Token_break:
+		case Token_continue:
+		case Token_fallthrough:
+		case Token_return:
+			insert_semicolon = true;
+			break;
+		}
+
+
+		if (t->flags & TokenizerFlag_InsertSemicolon) {
+			t->insert_semicolon = insert_semicolon;
+		}
 		return;
 
 	} else if (gb_is_between(curr_rune, '0', '9')) {
+		insert_semicolon = true;
 		scan_number_to_token(t, token, false);
 	} else {
 		advance_to_next_rune(t);
 		switch (curr_rune) {
 		case GB_RUNE_EOF:
 			token->kind = Token_EOF;
+			if (t->insert_semicolon) {
+				t->insert_semicolon = false; // EOF consumed
+				token->string = str_lit("\n");
+				token->kind = Token_Semicolon;
+				return;
+			}
 			break;
 
+		case '\n':
+			t->insert_semicolon = false;
+			token->string = str_lit("\n");
+			token->kind = Token_Semicolon;
+			return;
+
 		case '\'': // Rune Literal
 		{
+			insert_semicolon = true;
+
 			token->kind = Token_Rune;
 			Rune quote = curr_rune;
 			bool valid = true;
@@ -976,12 +1026,19 @@ void tokenizer_get_token(Tokenizer *t, Token *token) {
 			} else {
 				tokenizer_err(t, "Invalid rune literal");
 			}
+
+			if (t->flags & TokenizerFlag_InsertSemicolon) {
+				t->insert_semicolon = insert_semicolon;
+			}
+
 			return;
 		} break;
 
 		case '`': // Raw String Literal
 		case '"': // String Literal
 		{
+			insert_semicolon = true;
+
 			bool has_carriage_return = false;
 			i32 success;
 			Rune quote = curr_rune;
@@ -1026,6 +1083,11 @@ void tokenizer_get_token(Tokenizer *t, Token *token) {
 			} else {
 				tokenizer_err(t, "Invalid string literal");
 			}
+
+			if (t->flags & TokenizerFlag_InsertSemicolon) {
+				t->insert_semicolon = insert_semicolon;
+			}
+
 			return;
 		} break;
 
@@ -1046,17 +1108,32 @@ void tokenizer_get_token(Tokenizer *t, Token *token) {
 
 		case '@':  token->kind = Token_At;           break;
 		case '$':  token->kind = Token_Dollar;       break;
-		case '?':  token->kind = Token_Question;     break;
-		case '^':  token->kind = Token_Pointer;      break;
+		case '?':
+			insert_semicolon = true;
+			token->kind = Token_Question;
+			break;
+		case '^':
+			insert_semicolon = true;
+			token->kind = Token_Pointer;
+			break;
 		case ';':  token->kind = Token_Semicolon;    break;
 		case ',':  token->kind = Token_Comma;        break;
 		case ':':  token->kind = Token_Colon;        break;
 		case '(':  token->kind = Token_OpenParen;    break;
-		case ')':  token->kind = Token_CloseParen;   break;
-		case '[':  token->kind = Token_OpenBracket;  break;
-		case ']':  token->kind = Token_CloseBracket; break;
+		case ')':
+			insert_semicolon = true;
+			token->kind = Token_CloseParen;
+			break;
+		case '[': token->kind = Token_OpenBracket;  break;
+		case ']':
+			insert_semicolon = true;
+			token->kind = Token_CloseBracket;
+			break;
 		case '{':  token->kind = Token_OpenBrace;    break;
-		case '}':  token->kind = Token_CloseBrace;   break;
+		case '}':
+			insert_semicolon = true;
+			token->kind = Token_CloseBrace;
+			break;
 		case '\\': token->kind = Token_BackSlash;    break;
 
 		case '%':
@@ -1129,10 +1206,12 @@ void tokenizer_get_token(Tokenizer *t, Token *token) {
 
 		case '#':
 			if (t->curr_rune == '!') {
+				insert_semicolon = t->insert_semicolon;
+				token->kind = Token_Comment;
+
 				while (t->curr_rune != '\n' && t->curr_rune != GB_RUNE_EOF) {
 					advance_to_next_rune(t);
 				}
-				token->kind = Token_Comment;
 			} else {
 				token->kind = Token_Hash;
 			}
@@ -1142,6 +1221,7 @@ void tokenizer_get_token(Tokenizer *t, Token *token) {
 		case '/': {
 			token->kind = Token_Quo;
 			if (t->curr_rune == '/') {
+				insert_semicolon = t->insert_semicolon;
 				token->kind = Token_Comment;
 
 				while (t->curr_rune != '\n' && t->curr_rune != GB_RUNE_EOF) {
@@ -1253,11 +1333,18 @@ void tokenizer_get_token(Tokenizer *t, Token *token) {
 				int len = cast(int)gb_utf8_encode_rune(str, curr_rune);
 				tokenizer_err(t, "Illegal character: %.*s (%d) ", len, str, curr_rune);
 			}
+			insert_semicolon = t->insert_semicolon; // Preserve insert_semicolon info
 			token->kind = Token_Invalid;
 			break;
 		}
 	}
 
+	if (t->flags & TokenizerFlag_InsertSemicolon) {
+		t->insert_semicolon = insert_semicolon;
+	}
+
 	token->string.len = t->curr - token->string.text;
+
+
 	return;
 }