// This could become: proc (buffer: [] u8, $Elem: type_expr) -> PoolAllocator(Elem)
// when that feature is implemented.
+//
+// I think I'm going to veto that idea because when the buffer is a slice of Elem
+// its guaranteed that the size allocated for the buffer is a multiple of the size
+// of Elem.
make :: (buffer: [] $Elem) -> PoolAllocator(Elem) {
assert(sizeof Elem >= sizeof rawptr, "Cannot have a pool allocator of a type less than a rawptr in size.");
func = #solidify pool_allocator_proc { Elem = Elem },
data = pool,
};
-}
\ No newline at end of file
+}
return true;
},
- (va: vararg, $T: type_expr, default: T = 0) -> T {
- if va.count <= 0 do return default;
+ (va: vararg, $T: type_expr, default: T = 0) -> (bool, T) {
+ if va.count <= 0 do return false, default;
ret := *cast(^T) va.data;
va.data = cast(rawptr) (cast(^u8) va.data + sizeof T);
va.count -= 1;
- return ret;
+ return true, ret;
}
}
}
[ ] There is a segfault when passing an anonymous struct literal to an untyped vararg.
+ Should just need to detect this case and provide an error message since this doesn't
+ make any sense semantically.
vararg_proc :: (va: ...) {
...
The problem is that 'T' is an unresolved symbol because the context of the function type
does not have the solved value of T in it.
+[ ] Top level implicit typed arrays/struct literals are not correctly being marked as compile-time.
+
+ Vec2 :: struct { x, y: i32; }
+
+ Hex_Directions := Vec2.[
+ .{ 1, 0 }, .{ 1, -1 }, .{ 0, -1 },
+ .{ -1, 0 }, .{ -1, 1 }, .{ 0, 1 },
+ ];
+
List of things to change:
[X] Currently, there is no way to use the initialized members of a structure without using a struct literal.
The standard library will be rewritten to use these variables to conditionally include file
that are only supported on a particular system.
-For the moment, I think that the choice of backend will be given by compiler flags. It will
+For the moment, I think that the choice of runtime will be given by compiler flags. It will
set the variables described below to their value.
There should be an #error directive that when hit just produces a compile time error and prevents
- This means identifiers can be compared using ptr comparison, instead of string comparison
- This mean no more token_toggle_end!! Woo!!
- [ ] 'when' statements
+ [X] 'when' statements
- Compile time conditions
- Only evalutate code blocks that evaluate to be true
+// Variadic procedures are a very useful feature in a lot of programming languages
+// today so you have probably come across them at some point. If you haven't,
+// "variadic" just means that the number of arguments the procedure takes varies.
+// This is a slightly different concept from overloaded procedures, where there are
+// multiple versions of a procedure and which one is chosen depends on the arguments
+// given. Variadic procedures can consume as many arguments as they want.
+
+// In Onyx, there are two kinds of variadic procedures: fixed type, and untyped. In
+// fixed type variadic procedures, all variadic procedures need to be of a specified type.
+// In untyped variadic procedures, each of the variadic arguments can be of any type,
+// similar to how they work in C.
+
+// Some little things to note about the current implementation of variadic arguments:
+// - There can be at most 1 variadic argument
+// - 'variadic argument' is shortened to 'vararg' in many situtations
+
+
#load "core/std"
use package core
main :: (args: [] cstr) {
+
+ // This is how you specify a fixed type variadic procedure. 'args' can be thought
+ // of as a slice of integers, with data and count fields. You can iterate over the
+ // varargs using a for-loop like any other array-like structure. Note, do not
+ // take a pointer to anything in the varargs because they are placed on the stack
+ // and will "disappear" when the procedure returns.
+ typed_varargs :: (args: ..i32) {
+ print("Typed variadic arguments: ");
+ for x: args {
+ printf("%i ", x);
+ }
+ print("\n");
+ }
+
+ typed_varargs(1, 2, 3, 4);
+
+ // This is how you specify an untyped variadic procedure. Notice the extra '.'
+ // in the type. The access the arguments, you need to use the builtin procedure,
+ // 'vararg_get'. This procedure has two overloads that you can use:
+ //
+ // vararg_get :: (va: vararg, res: ^$T) -> bool
+ // vararg_get :: (va: vararg, $T: type_expr, default: T) -> (bool, T)
+ //
+ // The 'bool' returned is whether or not retrieving the vararg was successful. This
+ // can be achieved because in Onyx, untyped varargs know how many varargs are passed
+ // through a runtime count. This means if you call vararg_get more times than there
+ // are varargs, you will get empty or default results back.
+ untyped_varargs :: (args: ...) {
+ print("Untyped variadic arguments: ");
+
+ x: i32;
+ while vararg_get(args, ^x) {
+ printf("%p ", x);
+ }
+
+ print("\n");
+ }
+
+ untyped_varargs(1.0f, 2.0f, 3.0f, 4.0f);
+
+
+
+
+ // Most of the time with untyped varargs, you will have some kind of format
+ // specifier to indicate how many and of what type the varargs are. For example
+ // 'printf' uses this:
+ //
+ // printf :: (format: str, va: ...) -> void
+ // printf("%i %f %l %d", int, float, long, double);
+ //
+ // There are plans for the future to have better runtime type information available,
+ // which would allow you to introspect the types of each vararg at runtime. This
+ // would mean that instead of using '%i' style specifiers in printf, you could use
+ // '{}' to indicate the next variadic argument, whose type is automatically determined.
+ // If I'm not explaining this very well I apologize and hope that when the feature
+ // is implemented, it will make sense why it is a good idea.
}
+// Controlling the symbols in scope is important to maintaining well structured
+// and factored code. In Onyx, the most powerful feature available to do this is
+// the 'use' keyword. It allows you to bring in symbols from another namespace into
+// the currently scope. In Onyx, a "namespace" simply referers to something contains
+// names, generally something you can use the "." operator on. Some of the examples
+// include:
+// - structs with static (::) members
+// - struct members
+// - variables with a struct or pointer to a struct type
+// - packages
+// - enums
+
+// Please note, 'use' is a rather "alpha-tier" feature at the moment, meaning it works in
+// most cases, but can easily break and require you to write the code without it. This
+// is being address and hopefully in the next couple months it will be much more robust.
+
#load "core/std"
use package core
main :: (args: [] cstr) {
+ // Here are the uses of 'use' currently.
+
+ {
+ // You can 'use' a package. You hopefully already knew this.
+ // This brings in all the symbols from the core.alloc package into
+ // this scope.
+ use package core.alloc
+
+ // You can also restrict and partially rename the symbols that collide
+ use package core.string { free as string_free }
+ }
+
+ {
+ // You can also 'use' a package that is a subpackage of another package. Here, 'string'
+ // is a subpackage of 'core' and the above 'use package core' makes this package
+ // available for you to write code like 'string.equal(...)'. However, because it is
+ // a namespace, you can 'use' it.
+ use string
+
+ s := " Some string ...";
+ t := strip_leading_whitespace(s); // This would have to be 'string.strip_leading_whitespace' otherwise.
+ println(t);
+ }
+
+ {
+ // You can 'use' parameters to a function. This effectively makes this
+ // procedure behave like a 'method' for Dummy. You can access all members
+ // of dummy with having to say 'dummy.' everywhere. Note, this is one of the
+ // most alpha-tier parts of 'use', so if you are running into error, report
+ // an issue and (sadly) go back to writing without the 'use'.
+ //
+ // One side note, you can 'use' more than one parameter, and it doesn't have to
+ // be the first parameter. This can create decent self-documenting code because
+ // a procedure with two used parameters implies that it is a 'cross-cutting'
+ // procedure between two areas of concern.
+
+ basically_a_method :: (use dummy: ^Dummy) {
+ printf("Dummy with name '%s', and age '%i'.\n", name, age);
+ }
+
+ dummy := Dummy.{ "Phil", 15 };
+ basically_a_method(^dummy);
+
+ Dummy :: struct {
+ name : str;
+ age : i32;
+ }
+ }
+
+ {
+ // You can 'use' an enum. This is great if you have a burried enum in some other namespace
+ // and want to do a switch over that enum.
+
+ Player :: struct {
+ Color :: enum {
+ Undefined;
+ Red; Green; Blue;
+ }
+
+ color := Color.Red;
+ }
+
+ value := Player.{};
+
+ use Player.Color;
+ switch value.color {
+ case Red do println("The color is red!");
+ case Green do println("The color is green!");
+ case Blue do println("The color is blue!");
+ case #default do println("The color is unknown...");
+ }
+ }
+
+ {
+ // You can 'use' struct members that are structures.
+ // This is already explained in the struct example.
+ }
}
return mem;
}
-compress :: (arr: [$N] $T, f : proc (T, T) -> T) -> T {
+compress :: (arr: [$N] $T, f: (T, T) -> T) -> T {
val := arr[0];
for i: 1..N do val = f(val, arr[i]);
return val;
if (bh_arr_length(eh->entities) == 0) return;
static const char* state_colors[] = {
- "\e[91m",
- "\e[93m",
- "\e[97m",
- "\e[93m",
- "\e[94m",
- "\e[95m",
- "\e[94m",
- "\e[95m",
- "\e[96m",
- "\e[92m",
+ "\e[91m", "\e[93m", "\e[97m", "\e[93m", "\e[94m",
+ "\e[95m", "\e[94m", "\e[95m", "\e[96m", "\e[92m",
};
printf("\e[2;1H");
for (i32 i = 0; i < Entity_State_Count - 1; i++) {
- if (i % 4 == 0) printf("\n");
- printf("%s \xe2\x96\x88 %s", state_colors[i], entity_state_strings[i]);
+ if (i % 2 == 0) printf("\n");
+ printf("%s %25s \xe2\x96\x88 ", state_colors[i], entity_state_strings[i]);
}
printf("\n\n");
for (i32 i = 0; i < Entity_Type_Count; i++) {
- if (eh->type_count[i] == 0) {
- printf("\e[90m");
- } else {
- printf("\e[97m");
- }
+ if (eh->type_count[i] == 0) printf("\e[90m");
+ else if ((i32) eh->entities[0]->type == i) printf("\e[92m");
+ else printf("\e[97m");
printf("%25s (%4d) | ", entity_type_strings[i], eh->type_count[i]);
printf(state_colors[j]);
i32 count = (eh->all_count[j][i] >> 5) + 1;
- for (i32 c = 0; c < count * 2; c++) {
- printf("\xe2\x96\x88");
- }
+ for (i32 c = 0; c < count * 2; c++) printf("\xe2\x96\x88");
printf("\e[0m");
}
while (!bh_arr_is_empty(context.entities.entities)) {
Entity* ent = entity_heap_top(&context.entities);
- entity_heap_remove_top(&context.entities);
#if defined(_BH_LINUX)
if (context.options->fun_output) {
usleep(1000);
}
#endif
-
+
+ entity_heap_remove_top(&context.entities);
b32 changed = process_entity(ent);
// NOTE: VERY VERY dumb cycle breaking. Basically, remember the first entity that did
AstPolyProc* new_pp = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyProc), Ast_Kind_Polymorphic_Proc);
new_pp->token = pp->token; // TODO: Change this to be the solidify->token
new_pp->base_func = pp->base_func;
- new_pp->poly_scope = new_pp->poly_scope;
+ new_pp->poly_scope = new_pp->poly_scope; // CLEANUP: This doesn't seem correct??
new_pp->flags = pp->flags;
new_pp->poly_params = pp->poly_params;
//
// Polymorphic Structures
//
+//
+// Currently, I am not very happy about how polymorphic structure generation works. My biggest problem
+// with it is that it is very different from the polymorhic procedure generation. Also, it needs to
+// completely generate and check the structure right away, which means there is a lot of up-front work
+// done here that could probably be done elsewhere. This really relates to a large problem in the compiler
+// that types need to be known completely by the time symbol resolution is done, even though that
+// information shouldn't need to be known until right before the types are checked.
+//
+
char* build_poly_struct_name(AstPolyStructType* ps_type, Type* cs_type) {
char name_buf[256];
fori (i, 0, 256) name_buf[i] = 0;
Vec2(100, 1000)
Array of u32.
63001
+2D-array from an allocation to a function.
+Vec2(1000, 2000)
+Vec2(1001, 2001)
+Vec2(1002, 2004)
+Vec2(1003, 2009)
+Vec2(1004, 2016)
+Vec2(1005, 2025)
+Vec2(1006, 2036)
+Vec2(1007, 2049)
println(nums[50]);
}
+
+ {
+ println("2D-array from an allocation to a function.");
+
+ vecs_data: [2][4] Vec2;
+ vecs := ^vecs_data; // equivalent of heap allocating it
+
+ set_vecs :: (vecs: ^[2][4] Vec2) {
+ i := 0;
+ for ^row: *vecs {
+ for ^v: *row {
+ *v = .{ 1000 + i, 2000 + i * i };
+ i += 1;
+ }
+ }
+ }
+
+ set_vecs(vecs);
+
+ print_vecs :: (vecs: ^[4] Vec2) {
+ for ^v: *vecs do println(*v);
+ }
+
+ print_vecs(^(*vecs)[0]);
+ print_vecs(^(*vecs)[1]);
+ }
}