This is a rough guide to the coding style used for the C source code in c3c.
- Constants use upper snake case, e.g.
THIS_IS_A_CONSTANT
- Macros use upper snake case, e.g.
MY_MACRO(123)
- User defined types use capitalized camel case. e.g.
MyLittleType
- Function names use snake case, e.g.
my_little_function(123)
- Variables use snake case, e.g.
context
In addition, function names that act on a particular object, should
be prefixed with the type name in snake case, with the name of
the function following e.g. type_info_new
(and not "new_type_info").
The source Allman style:
if (foo)
{
... code here ...
}
Goto labels have no indentation:
int foo()
{
x = 0;
LABEL1:
if (foo)
{
... code ...
}
}
Case statements have one tab indentation:
switch foo()
{
case 1:
do_something();
break;
case 2:
do_something_else();
FALLTHROUGH;
default:
do_default();
}
Spaces between control statements and ()
:
if (foo) ...
while (foo) ...
Space after ,
and between expressions:
if (foo && bar) return (Baz) { 1, 2, 3 };
Space around assignment:
a += b;
No space inside parenthesis:
int x = c * (foo(b) + b);
Use tabs for indentation, no CRLF in the source.
Any if
statement with else
should use braces.
if (foo)
{
...
}
else
{
...
}
Single line if
statements are allowed without braces.
if (this_is_fine()) return true;
Otherwise use braces.
General principles for coding.
Keep fairly large headers that can be used across multiple files, e.g. sema_internal.h
instead of fragmenting into one header file per c file.
Don't add things "just because", because this requires more code to maintain.
Code that works without testing are rare things. Test your code and preferably add more tests as you go along.
External libraries has maintainability issues. Try to depend on as few libraries as possible. Currently, c3c only depends on LLVM and libc with an optional dependency on libcurl.
Do use rewrites of subsets of other libraries to bring in functionality, but avoid copying in libraries that needs to be updated separately.
Some notes about the internals.
The compiler uses an arena allocator that isn't released until the compiler closes.
In addition to this general allocator, there are allocators for certain types,
such as Decl
Ast
etc. These are discarded before code generation. Consequently
you need to make sure that none of those are used in the code generation phase.
Dynamic arrays are used throughout the compiler. They use the arena allocator and
thus will use up memory until the compiler ends. To create a dynamic array, just
declare a null pointer to the type and use vec_add
to add elements:
Foo *foos = NULL;
vec_add(foos, (Foo) { 1, 2 });
Iterating over the elements are done using VECEACH
.
There is a scratch buffer for strings in the global_context
prefer using that
one with related functions when working on temporary strings.
When contributing to the standard librairy please to your best to follow the following style requirements as to ensure a consistent style in the stdlib and also make accepting PRs more quickly.
NO:
fn void foo(String bar) {
@pool() {
...
};
}
YES:
fn void foo(String bar)
{
@pool()
{
...
};
}
Use tab for indentation, not spaces, no CRLF in the sources
Use PascalCase
not Ada_Case
for type names.
YES:
enum MyEnum
{
ABC,
DEF
}
NO:
enum My_Enum
{
ABC,
DEF
}
When doing bindings (for instance, adding declarations referring to Win32 APIs), try to retain the original name when possible. If it isn't possible use (consistently) one of two options:
- Prefix:
HANDLE
->Win32_HANDLE
- Change the first letter to upper case:
mode_t
->Mode_t
Use snake_case
, not camelCase
.
YES:
int some_global = 1;
fn void open_file(String special_file)
{
...
}
NO:
int someGlobal = 1;
fn void openFile(String specialFile)
{
...
}
When doing bindings (for instance, adding declarations referring to Win32 APIs), try to retain the original name when possible. If it isn't possible use (consistently) one of two options:
- Prefix:
win32_GetWindowLongPtrW
. However, this is usually only recommended if it is builtin. - Change first character to lower case:
GetWindowLongPtrW
->getWindowLongPtrW
Unless there is a strong reason not to, use self
for the first parameter in a method.
Prefer always calling the allocator parameter allocator
, and make it the first regular
argument.
If you add or fix things, then there should always be tests in test/unit/stdlib
to verify
the functionality.