Concepts
Namespaces
Namespaces are an organization feature, to reduce repetition and easier navigation around code. Here's an example:
namespace example {
void code(void) { ... }
}
And your other file that includes that one could do:
#namespace example:code;
// Both are valid
code();
example:code();
You can also have file-global namespaces. So instead of wrapping your entire file in a namespace, adding another unnecessary tab, you can just:
namespace example;
void code(void) { ... }
Of course, they can be nested for further organization. Those are hard to navigate if you have too many of them, make sure to only use them if you have insane levels of indentation. Namespace colons are treated as underscores for C compatibility, make sure to not do a silly like:
namespace a {
namespace b {
void c();
}
void b_c();
}
I'll probably make this a "symbol collision error", you're not allowed to do that.
Namespace types
Namespaces have a special property. A type or function called "~" takes the namespace's name. Here's an example:
namespace person;
struct ~ {
int number;
char* name
}
void print_person(~* p) { ... }
To use that function and struct, you do:
person guy;
person:print_person(&guy);
Constructors
Suppose you want to automatically initialize a struct upon its creation. Here's how you'd implement it:
namespace person;
struct ~ {
int number;
char* name;
}
result init(~* p, int number, char* person_name) {
return ~ { number, name = person_name };
}
And here's how you'd use it:
person p = 27 "idk what example to give";
And this variable is now fully initialized, passing "27" and that string into "result init()".
Error handling
Ripped from rust, basically.
Optionstruct option {
void* ptr; // is NULL if there is nothing
}
struct result {
int error; // can be 0 for success, 1 for failure, and whatever else you want to have as an error
void* ptr;
}
Traits
Ripped from rust, again. I love them and I want them back. Imagine we want the person to print their number, but make other structs do that too:
namespace person;
trait print_number() {
void ~(person* p); // "~" refers to "print_number" now, don't mix them up!
}
impl print_number for ~ { // "~" is now "person"... maybe I regret adding this feature
void ~(person* p) { // i think it's print_number again idk
io:printf("%i\n", p->i);
}
}
I don't know how to implement the "loop over structs that implement the same traits and run the functions", so I'll keep traits as an organization/naming consistency feature rather than a true rust reimplementation.
You may have noticed the braces in the trait initialization.
You basically put the traits' names the trait depends on in a comma separated list, the previous example could've been:
trait print_number(get_number, print) { ... }
If we needed those traits within print_number.
Preprocessor thingies
Include blocks don't take up 27 lines anymore! You can just do:
#include : io, : mem, , "vec.h"
And use them like this:
io:printf(...);
mem:memcpy(...);
sqrt(...);
vec:init(...); // the file contains it's own namespace, no need to add one
Adding a namespace around a file is optional, but useful if you're working with C libraries that have no C+ namespaces.
Standards
Standard file extensions:
- Source: .p (For the "plus" in C+, I'm not seriously using "cp")
- Header: .u (For "universal header", it can be transpiled back into C, so it can be used by both languages)
What I still have to think about
Random special C words
Things like "static", "extern", or whatever else I'm missing. I don't know what any of those mean, I don't know whether or not I need those in the first place, but I'll do some research on them.
Whether or not I should rename types to be objectively better
Examples include:
- int -> i32
- unsigned int -> u32
- size_t -> int // dont kill me for mixing up the "int"
- ssize_t -> uint
Signed size_t equivalent
I know ssize_t exists, but it relies on "sys/types.h". And besides, it can easily be implemented alongside all the other stuff.