Static if statement
This one is really needed. Sometimes we want to include or exclude parts of code depending on various conditions (for example target platform, build type, etc.). Biscuit does not have this concept yet, we're just including whole files with platform depending code during compilation.
Example:
1 2 3 4 5 6 | ... WINDOWS :: true; #if WINDOWS { // compile this only on windows platform } ... |
Switch statement
We can use "wall of ifs" now, but the switch is sometimes more elegant. Syntax for switches is not defined yet and we are open to new ideas.
Concept:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ... switch a { case A: // implicit fall through when case is empty? case B: break; // explicit end of the switch branch case C: // declarations without need of explicit body '{}' i := 10; case D: print("a is D!!!\n"); // no need for explicit break when case is not empty case E: print("a is E!!!\n"); default: // default case like in C } ... |
Function overloading
We have no possibility to have the same function name twice in BL for now, but future plan is provide function overloading based on argument types.
Concept:
1 2 3 4 5 6 7 8 9 10 11 | ... minimum :: fn (a: u32, b: u32) u32 #inline { if a < b { return a; } return b; }; minimum :: fn (a: s32, b: s32) s32 #inline { if a < b { return a; } return b; }; ... |
Structure inheritance
The BL is never going to be OO language but we can provide kind of inheritance for structures with some additional features. Common way to simulate inheritance in C is composition:
1 2 3 4 5 6 7 8 9 10 | struct Entity { v3 position; quat rotation; }; struct Player { struct Entity base; const char *name; int age; }; |
Since Player's 'base' member of type 'struct Entity' is listed first we can safely cast player instance pointer to entity pointer and build some abstraction on it, but accessing to the entity's members from player requires go through base. player_ptr->base.position. This is quite OK for one level of composition depth but what if we have even more specific structure based on Player, then access to the position looks like this: more_spec_player_ptr->base.base.position, and this is quite annoying...
BL version of the same concept should be something like this:
1 2 3 4 5 6 7 8 9 | Entity :: struct { position: v3, rotation: quat }; Player :: struct #extends Entity { name: string, age: s32 }; |
With hash directive #extends we're saying to the compiler that content of Entity structure should be implicitly inserted into the Player structure. Using Entity's position from player does not require going through base member then.
1 2 3 | player: Player; player.name = "Travis"; player.position = {:v3: 0}; |
Since compiler has an information about connection between player and entity we can allow implicit casting from pointer to the player to pointer to the entity like this:
1 2 3 4 | entity_update :: fn (entity: *Entity) { ... }; player: Player; entity_update(&player); // no explicit cast needed here |
Operator overloading
We've considered this feature as needed after few days of GL-math programming. Explicit calling of methods for vector calculations is not so comfortable.
Basic idea is enable users to define custom operator behaviour for custom types.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | v3 :: struct { x: f32, y: f32, z: f32 }; #operator + :: fn (a: v3, b: v3) v3 { return {:v3: a.x + b.x, a.y + b.y, a.z + b.z}; }; #operator + :: fn (a: v3, b: f32) v3 { return {:v3: a.x + b, a.y + b, a.z + b}; }; // usage vec_a: v3; vec_b: v3; // implicit operator handler call vec_a + vec_b; vec_a + 10.f; |
But we have some open issues here still:
- We don't want to pass larger structures by value into the operator handlers.
- Problems with order of expressions in binary operations vec_a + 10.f; is not the same as 10.f + vec_a;. Do we need to specify handlers for both variants?
- Should we enable overloading of unary operators? -vec_a;