Browse Source

Write demo for v0.5.0

Ginger Bill 8 years ago
parent
commit
260089431e
4 changed files with 443 additions and 20 deletions
  1. 408 0
      code/demo.odin
  2. 13 1
      src/check_decl.cpp
  3. 20 16
      src/check_expr.cpp
  4. 2 3
      src/checker.cpp

+ 408 - 0
code/demo.odin

@@ -1,5 +1,411 @@
 import "fmt.odin";
 
+proc general_stuff() {
+	// Complex numbers
+	var a = 3 + 4i;
+	var b: complex64 = 3 + 4i;
+	var c: complex128 = 3 + 4i;
+	var d = complex(2, 3);
+
+	var e = a / conj(a);
+	fmt.println("(3+4i)/(3-4i) =", e);
+	fmt.println(real(e), "+", imag(e), "i");
+
+
+	// C-style variadic procedures
+	foreign __llvm_core {
+		// The variadic part allows for extra type checking too which C does not provide
+		proc c_printf(fmt: ^u8, #c_vararg args: ..any) -> i32 #link_name "printf";
+	}
+
+
+	type Foo struct {
+		x: int,
+		y: f32,
+		z: string,
+	}
+	var foo = Foo{123, 0.513, "A string"};
+	var x, y, z = expand_to_tuple(foo);
+	fmt.println(x, y, z);
+
+
+	// By default, all variables are zeroed
+	// This can be overridden with the "uninitialized value"
+	var undef_int: int = ---;
+
+
+	// Context system is now implemented using Implicit Parameter Passing (IPP)
+	// The previous implementation was Thread Local Storage (TLS)
+	// IPP has the advantage that it works on systems without TLS and that you can
+	// link the context to the stack frame and thus look at previous contexts
+	//
+	// It does mean that a pointer is implicitly passed procedures with the default
+	// Odin calling convention (#cc_odin)
+	// This can be overridden with something like #cc_contextless or #cc_c
+
+}
+
+proc foreign_blocks() {
+	// See sys/windows.odin
+}
+
+
+proc default_arguments() {
+	proc hello(a: int = 9, b: int = 9) {
+		fmt.printf("a is %d; b is %d\n", a, b);
+	}
+	fmt.println("\nTesting default arguments:");
+	hello(1, 2);
+	hello(1);
+	hello();
+}
+
+proc named_arguments() {
+	type Colour enum {
+		Red,
+		Orange,
+		Yellow,
+		Green,
+		Blue,
+		Octarine,
+	};
+	using Colour;
+
+	proc make_character(name, catch_phrase: string, favorite_color, least_favorite_color: Colour) {
+	    fmt.println();
+	    fmt.printf("My name is %v and I like %v.  %v\n", name, favorite_color, catch_phrase);
+	}
+
+	make_character("Frank", "¡Ay, caramba!", Blue, Green);
+
+
+	// As the procedures have more and more parameters, it is very easy
+	// to get many of the arguments in the wrong order
+	make_character("¡Ay, caramba!", "Frank", Green, Blue);
+
+	// Named arguments help to disambiguate this problem
+	make_character(catch_phrase = "¡Ay, caramba!", name = "Frank",
+	               least_favorite_color = Green, favorite_color = Blue);
+
+
+	// The named arguments can be specifed in any order.
+	make_character(favorite_color = Octarine, catch_phrase = "U wot m8!",
+	               least_favorite_color = Green, name = "Dennis");
+
+
+	// NOTE: You cannot mix named arguments with normal values
+	/*
+	make_character("Dennis",
+	               favorite_color = Octarine, catch_phrase = "U wot m8!",
+	               least_favorite_color = Green);
+	*/
+
+
+	// Named arguments can also aid with default arguments
+	proc numerous_things(s : string, a = 1, b = 2, c = 3.14, d = "The Best String!", e = false, f = 10.3/3.1, g = false) {
+		var g_str = g ? "true" : "false";
+		fmt.printf("How many?! %s: %v\n", s, g_str);
+	}
+
+	numerous_things("First");
+	numerous_things(s = "Second", g = true);
+
+
+	// Default values can be placed anywhere, not just at the end like in other languages
+	proc weird(pre: string, mid: int = 0, post: string) {
+		fmt.println(pre, mid, post);
+	}
+
+	weird("How many things", 42, "huh?");
+	weird(pre = "Prefix", post = "Pat");
+
+}
+
+
+proc default_return_values() {
+	proc foo(x: int) -> (first: string = "Hellope", second = "world!") {
+		match x {
+		case 0: return;
+		case 1: return "Goodbye";
+		case 2: return "Goodbye", "cruel world...";
+		case 3: return second = "cruel world...", first = "Goodbye";
+		}
+
+		return second = "my old friend.";
+	}
+
+	fmt.printf("%s %s\n", foo(0));
+	fmt.printf("%s %s\n", foo(1));
+	fmt.printf("%s %s\n", foo(2));
+	fmt.printf("%s %s\n", foo(3));
+	fmt.printf("%s %s\n", foo(4));
+	fmt.println();
+
+
+	// A more "real" example
+	type Error enum {
+		None,
+		WhyTheNumberThree,
+		TenIsTooBig,
+	};
+
+	type Entity struct {
+		name: string,
+		id:   u32,
+	}
+
+	proc some_thing(input: int) -> (result: ^Entity = nil, err = Error.None) {
+		match {
+		case input == 3: return err = Error.WhyTheNumberThree;
+		case input >= 10: return err = Error.TenIsTooBig;
+		}
+
+		var e = new(Entity);
+		e.id = u32(input);
+
+		return result = e;
+	}
+}
+
+proc call_location() {
+	proc amazing(n: int, using loc = #caller_location) {
+		fmt.printf("%s(%d:%d) just asked to do something amazing to %d.\n",
+		           fully_pathed_filename, line, column);
+		fmt.printf("Amazing -> %d\n", n+1);
+	}
+
+	var loc = #location(main);
+	fmt.println("`main` is located at", loc);
+
+	fmt.println("This line is located at", #location());
+	fmt.println();
+
+	amazing(3);
+	amazing(4, #location(call_location));
+
+	// See _preload.odin for the implementations of `assert` and `panic`
+
+}
+
+
+proc explicit_parametric_polymorphic_procedures() {
+	// This is how `new` is actually implemented, see _preload.odin
+	proc alloc_type(T: type) -> ^T {
+		return ^T(alloc(size_of(T), align_of(T)));
+	}
+
+	var int_ptr = alloc_type(int);
+	defer free(int_ptr);
+	int_ptr^ = 137;
+	fmt.println(int_ptr, int_ptr^);
+
+	// Named arguments work too!
+	var another_ptr = alloc_type(T = f32);
+	defer free(another_ptr);
+
+
+	proc add(T: type, args: ..T) -> T {
+		var res: T;
+		for arg in args {
+			res += arg;
+		}
+		return res;
+	}
+
+	fmt.println("add =", add(int, 1, 2, 3, 4, 5, 6));
+
+	proc swap(T: type, a, b: ^T) {
+		var tmp = a^;
+		a^ = b^;
+		b^ = tmp;
+	}
+
+	var a, b: int = 3, 4;
+	fmt.println("Pre-swap:", a, b);
+	swap(int, &a, &b);
+	fmt.println("Post-swap:", a, b);
+	a, b = b, a; // Or use this syntax for this silly example case
+
+
+
+
+
+
+	// A more complicated example using subtyping
+	// Something like this could be used a game
+	type Vector2 struct {x, y: f32};
+
+	type Entity struct {
+		using position: Vector2,
+		flags:          u64,
+		id:             u64,
+		batch_index:    u32,
+		slot_index:     u32,
+		portable_id:    u32,
+		derived:        any,
+	}
+
+	type Rock struct {
+		using entity: ^Entity,
+		heavy: bool,
+	}
+	type Door struct {
+		using entity: ^Entity,
+		open:         bool,
+	}
+	type Monster struct {
+		using entity: ^Entity,
+		is_robot:     bool,
+		is_zombie:    bool,
+	}
+
+	type EntityManager struct {
+		batches: [dynamic]^EntityBatch,
+		next_portable_id: u32,
+	}
+
+	const ENTITIES_PER_BATCH = 16;
+	type EntityBatch struct {
+		data:        [ENTITIES_PER_BATCH]Entity,
+		occupied:    [ENTITIES_PER_BATCH]bool,
+		batch_index: u32,
+	}
+
+	proc use_empty_slot(manager: ^EntityManager, batch: ^EntityBatch) -> ^Entity {
+		for ok, i in batch.occupied {
+			if ok -> continue;
+			batch.occupied[i] = true;
+
+			var e = &batch.data[i];
+			e.batch_index = u32(batch.batch_index);
+			e.slot_index  = u32(i);
+			e.portable_id = manager.next_portable_id;
+			manager.next_portable_id++;
+			return e;
+		}
+		return nil;
+	}
+
+	proc gen_new_entity(manager: ^EntityManager) -> ^Entity {
+		for b in manager.batches {
+			var e = use_empty_slot(manager, b);
+			if e != nil -> return e;
+		}
+
+		var new_batch = new(EntityBatch);
+		append(manager.batches, new_batch);
+		new_batch.batch_index = u32(len(manager.batches)-1);
+
+		return use_empty_slot(manager, new_batch);
+	}
+
+
+
+	proc new_entity(manager: ^EntityManager, Type: type, x, y: int) -> ^Type {
+		var result = new(Type);
+		result.entity = gen_new_entity(manager);
+		result.derived.data = result;
+		result.derived.type_info = type_info(Type);
+
+		result.position.x = f32(x);
+		result.position.y = f32(y);
+
+		return result;
+	}
+
+	var manager: EntityManager;
+	var entities: [dynamic]^Entity;
+
+	var rock    = new_entity(&manager, Rock, 3, 5);
+
+	// Named arguments work too!
+	var door    = new_entity(manager = &manager, Type = Door, x = 3, y = 6);
+
+	// And named arguments can be any order
+	var monster = new_entity(
+		y = 1,
+		x = 2,
+		manager = &manager,
+		Type = Monster,
+	);
+
+	append(entities, rock, door, monster);
+
+	// An alternative to `union`s
+	for entity in entities {
+		match e in entity.derived {
+		case Rock:    fmt.println("Rock",    e.portable_id);
+		case Door:    fmt.println("Door",    e.portable_id);
+		case Monster: fmt.println("Monster", e.portable_id);
+		}
+	}
+}
+
+proc main() {
+	general_stuff();
+	foreign_blocks();
+	default_arguments();
+	named_arguments();
+	default_return_values();
+	call_location();
+	explicit_parametric_polymorphic_procedures();
+
+	// Command line argument(s)!
+	// -opt=0,1,2,3
+
+
+	/*************/
+	/* Questions */
+	/*************/
+
+	/*
+		I'm questioning if I should change the declaration syntax back to Jai-like
+		as I've found solutions to the problems I had with it before.
+
+		Should I change back to Jai-like declarations or keep with the Pascal-like?
+
+		Jai-like
+
+		x: int;
+		x: int = 123;
+		x := 123;
+
+		foo : int : 123;
+		foo :: 123;
+
+		MyInt :: int;
+		BarType :: proc();
+
+		bar :: proc() {
+		}
+
+		foreign lib {
+			foreign_bar :: proc() ---;
+		}
+
+		Pascal-like
+
+		var x: int;
+		var x: int = 123;
+		var x = 123;
+
+		const foo: int = 123;
+		const foo = 123;
+
+		type MyInt int;
+		type BarType proc();
+
+		proc bar() {
+		}
+
+		foreign lib {
+			proc foreign_bar();
+		}
+	 */
+
+}
+
+/*
 proc main() {
 	var program = "+ + * - /";
 	var accumulator = 0;
@@ -17,3 +423,5 @@ proc main() {
 	fmt.printf("The program \"%s\" calculates the value %d\n",
 	           program, accumulator);
 }
+*/
+

+ 13 - 1
src/check_decl.cpp

@@ -45,6 +45,11 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
 			}
 			t = default_type(t);
 		}
+		if (is_type_gen_proc(t)) {
+			error(e->token, "Invalid use of a generic procedure in %.*s", LIT(context_name));
+			e->type = t_invalid;
+			return NULL;
+		}
 		if (is_type_bit_field_value(t)) {
 			t = default_bit_field_value_type(t);
 		}
@@ -361,7 +366,12 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 
 	if (pt->is_generic) {
 		if (pd->body == NULL) {
-			error(e->token, "Generic procedures must have a body");
+			error(e->token, "Polymorphic procedures must have a body");
+		}
+
+		if (is_foreign) {
+			error(e->token, "A foreign procedures cannot be a polymorphic");
+			return;
 		}
 	}
 
@@ -387,6 +397,8 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 		pt->require_results = is_require_results;
 	}
 
+
+
 	if (is_foreign) {
 		String name = e->token.string;
 		if (pd->link_name.len > 0) {

+ 20 - 16
src/check_expr.cpp

@@ -12,7 +12,6 @@ enum CallArgumentError {
 	CallArgumentError_ParameterNotFound,
 	CallArgumentError_ParameterMissing,
 	CallArgumentError_DuplicateParameter,
-	CallArgumentError_GenericProcedureNotSupported,
 };
 
 enum CallArgumentErrorMode {
@@ -285,9 +284,11 @@ i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) {
 
 
 	if (is_type_any(dst)) {
-		// NOTE(bill): Anything can cast to `Any`
-		add_type_info_type(c, s);
-		return 10;
+		if (!is_type_gen_proc(src)) {
+			// NOTE(bill): Anything can cast to `Any`
+			add_type_info_type(c, s);
+			return 10;
+		}
 	}
 
 
@@ -4241,13 +4242,17 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 
 
 	case BuiltinProc_type_of:
-		// proc type_of_val(val: Type) -> type(Type)
-		check_assignment(c, operand, NULL, str_lit("argument of `type_of_val`"));
+		// proc type_of(val: Type) -> type(Type)
+		check_assignment(c, operand, NULL, str_lit("argument of `type_of`"));
 		if (operand->mode == Addressing_Invalid || operand->mode == Addressing_Builtin) {
 			return false;
 		}
-		if (operand->type == NULL || operand->type == t_invalid || is_type_gen_proc(operand->type)) {
-			error(operand->expr, "Invalid argument to `type_of_val`");
+		if (operand->type == NULL || operand->type == t_invalid) {
+			error(operand->expr, "Invalid argument to `type_of`");
+			return false;
+		}
+		if (is_type_gen_proc(operand->type)) {
+			error(operand->expr, "`type_of` of generic procedure cannot be determined");
 			return false;
 		}
 		operand->mode = Addressing_Type;
@@ -4999,10 +5004,10 @@ Entity *find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity,
 	Type *final_proc_type = make_type_proc(c->allocator, c->context.scope, NULL, 0, NULL, 0, false, pt->calling_convention);
 	check_procedure_type(c, final_proc_type, pt->node, operands);
 
-	auto *found = map_get(&c->info.gen_procs, hash_pointer(base_entity->identifier));
-	if (found) {
-		for_array(i, *found) {
-			Entity *other = (*found)[i];
+	auto *found_gen_procs = map_get(&c->info.gen_procs, hash_pointer(base_entity->identifier));
+	if (found_gen_procs) {
+		for_array(i, *found_gen_procs) {
+			Entity *other = (*found_gen_procs)[i];
 			if (are_types_identical(other->type, final_proc_type)) {
 			// NOTE(bill): This scope is not needed any more, destroy it
 				destroy_scope(scope);
@@ -5041,13 +5046,13 @@ Entity *find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity,
 	proc_info.body  = pd->body;
 	proc_info.tags  = tags;
 
-	if (found) {
-		array_add(found, entity);
+	if (found_gen_procs) {
+		array_add(found_gen_procs, entity);
 	} else {
 		Array<Entity *> array = {};
 		array_init(&array, heap_allocator());
 		array_add(&array, entity);
-		map_set(&c->info.gen_procs, hash_pointer(entity->identifier), array);
+		map_set(&c->info.gen_procs, hash_pointer(base_entity->identifier), array);
 	}
 
 	GB_ASSERT(entity != NULL);
@@ -5373,7 +5378,6 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
 
 	Entity *gen_entity = NULL;
 	if (pt->is_generic && err == CallArgumentError_None) {
-		// err = CallArgumentError_GenericProcedureNotSupported;
 		ProcedureInfo proc_info = {};
 		gen_entity = find_or_generate_polymorphic_procedure(c, entity, &ordered_operands, &proc_info);
 		if (gen_entity != NULL) {

+ 2 - 3
src/checker.cpp

@@ -87,7 +87,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	{STR_LIT("size_of"),          1, false, Expr_Expr},
 	{STR_LIT("align_of"),         1, false, Expr_Expr},
 	{STR_LIT("offset_of"),        2, false, Expr_Expr},
-	{STR_LIT("type_of_val"),      1, false, Expr_Expr},
+	{STR_LIT("type_of"),          1, false, Expr_Expr},
 	{STR_LIT("type_info"),        1, false, Expr_Expr},
 
 	{STR_LIT("compile_assert"),   1, false, Expr_Expr},
@@ -1456,7 +1456,7 @@ void check_procedure_overloading(Checker *c, Entity *e) {
 				TypeProc *ptq = &base_type(q->type)->Proc;
 				if (ptq->is_generic) {
 					q->type = t_invalid;
-					error(q->token, "Generic procedure `%.*s` cannot be overloaded", LIT(name));
+					error(q->token, "Polymorphic procedure `%.*s` cannot be overloaded", LIT(name));
 					continue;
 				}
 			}
@@ -2269,7 +2269,6 @@ void check_parsed_files(Checker *c) {
 			if (pi->decl->gen_proc_type == NULL) {
 				continue;
 			}
-			// gb_printf_err("Generic procedure `%.*s` -> %s\n", LIT(pi->token.string), type_to_string(pi->decl->gen_proc_type));
 		}
 
 		add_curr_ast_file(c, pi->file);