Skip to content

Introduction#

The Nest C API allows you to write C libraries for the Nest Programming Language or to use Nest inside your C or C++ program. These documents explain all the functions, macros and structs defined inside the library.

Type definitions#

Instead of using standard C types, the file typedefs.h defines some clearer types inspired by Rust:

Type name C Equivalent
i8 char
u8 unsigned char
i16 short
u16 unsigned short
i32 long
u32 unsigned long
i64 long long
u64 unsigned long long
f32 float
f64 double
usize size_t
isize ptrdiff_t

Code style#

Line length#

Each line can be at most 79 characters long, it can be exceeded only be one punctuation character that is not a comment.

/* -------------------- This line is 79 characters long -------------------- */
                                                                             //
// This is good                                                              //
bool res = Nst_translate_cp(                                                 //
    &Nst_cp_utf8,                                                            //
    &Nst_cp_utf16,                                                           //
    (void *)str, len,                                                        //
    (void **)&out_str, NULL);                                                //
                                                                             //
// This is also good                                                         //
NstEXP bool NstC Nst_translate_cp(Nst_CP *from, Nst_CP *to, void *from_buf,  //
                                  usize from_len, void **to_buf, usize *to_len);
                                                                             //
// This is too long                                                          //
bool res = Nst_translate_cp(&Nst_cp_utf8, &Nst_cp_utf16, (void *)str, len, (void **)&out_str, NULL);

If the line is too long you can reduce it in various ways:

  • put the arguments of a function call on separate lines, though you can put related arguments on the same one
  • put the arguments of a function definition on different lines that are all indented to be aligned with the first argument
  • split the accessing of fields in structs before the dot or the arrow
    // This is good
    Nst_state.idx = Nst_fstack_peek()
        .func->body
        .bytecode->total_size;
    
    // This is wrong
    Nst_state.idx = Nst_fstack_peek().
        func->body.
        bytecode->total_size;
  • maybe you have too many operations on one line and you should split them up

Indentation#

Each level of indentation must be of exactly four spaces, not tabs. There is no limit to how many levels of indentation you can have.

Type names#

When there is a corresponding type, use the type definition inside typedefs.h. You can use built-in types when there are type warnings.

Function declarations and definitions#

The function definition should keep the return type and the function name on the same line, the arguments, if they don't fit into the line, can span multiple lines and are always indented to the first one.

// This is good
NstEXP Nst_LList *NstC nst_tokenizef(i8 *filename, bool force_cp1252,
                                     i32 *opt_level, bool *no_default,
                                     Nst_SourceText *src_text,
                                     Nst_Error *error);

// This is wrong
NstEXP Nst_LList *NstC
nst_tokenizef(i8 *filename, bool force_cp1252, i32 *opt_level, bool *no_default,
              Nst_SourceText *src_text, Nst_Error *error);

// This is also wrong
NstEXP Nst_LList *NstC nst_tokenizef(
    i8 *filename,
    bool force_cp1252,
    i32 *opt_level,
    bool *no_default,
    Nst_SourceText *src_text,
    Nst_Error *error
);

Furthermore, when implementing the function the curly braces should always appear in their own lines.

// This is good
i8 *Nst_wchar_t_to_char(wchar_t *str, usize len)
{
    ...
}

// This is wrong
i8 *Nst_wchar_t_to_char(wchar_t *str, usize len) {
    ...
}

Nomenclature#

All exported names should begin with either Nst_ or _Nst_, with the exception of type casts defined to make the code shorter and more readable. Which one to use depends on whether the function is intended for public or internal use.

Macros, aside from Nst_ and _Nst_, should use uppercase with the words separated by underscores (Ex. Nst_RETURN_COND).

Functions, aside from Nst_ and _Nst_, should use snake case, with lowercase words separated by underscores (Ex. Nst_buffer_expand_by).

Types (including structs, enums, typedefs and unions), aside from Nst_ or _Nst_ should use pascal case where each word has its first letter capitalized (Ex. Nst_StrObj).

When declaring a struct it should always be in this format:

typedef struct _Nst_StructName {
    ...
} Nst_StructName;

where StructName is the name of the struct. This means that any structure in Nest can be accessed using the name of the alias preceded by an underscore: struct _Nst_Obj and Nst_Obj are equivalent.

Function calls#

When calling a functions there should be no whitespace between the name and the opening bracket, before the commas separating the arguments or around the closing bracket. There should always be one space after the comma. If the line is too long, arguments can be split on multiple lines that end with the commas that separate them. The closing bracket and semicolon should be on the same line as the last argument. You may put relate arguments on the same line, otherwise each argument goes on a separate line.

// This is good
Nst_calloc_c(_Nst_MAP_MIN_SIZE, Nst_MapNode, NULL);

// This is also good
bool res = Nst_translate_cp(
    &Nst_cp_utf8,
    &Nst_cp_utf16,
    (void *)str, len,
    (void **)&out_str, NULL);

// This is wrong
bool res = Nst_translate_cp(
    &Nst_cp_utf8,
    &Nst_cp_utf16,
    (void *)str, len,
    (void **)&out_str, NULL
);

// This is also wrong
Nst_calloc_c (_Nst_MAP_MIN_SIZE, Nst_MapNode, NULL);
Nst_calloc_c( _Nst_MAP_MIN_SIZE, Nst_MapNode, NULL );
Nst_calloc_c(_Nst_MAP_MIN_SIZE , Nst_MapNode , NULL);

Macros#

A macro should always require a semicolon when put on its own line. When defining a macro, if it spans multiple lines the backslashes should be put as the last character of the line. If the macro needs to be wrapped inside a do { ... } while (0), the do { should be in the same line as the name of the macro and } while (0) on the last line, at the same indentation of the rest of the macro's body using always a zero 0 and not false. The body itself should be ad one level of indentation. If the name of the macro with the arguments is too long to fit into one line, or the do { would exceed the 79 character length, you should consider making the macro a function.

// This is good
#define Nst_OBJ_IS_TRACKED(obj) (GGC_OBJ(obj)->ggc_list != NULL)

// This is also good
#define Nst_GGC_OBJ_INIT(obj, trav_func, track_function) do {                 \
    obj->ggc_prev = NULL;                                                     \
    obj->ggc_next = NULL;                                                     \
    obj->ggc_list = NULL;                                                     \
    obj->traverse_func = (void (*)(Nst_Obj *))(trav_func);                    \
    obj->track_func = (void (*)(Nst_Obj *))(track_function);                  \
    Nst_FLAG_SET(obj, Nst_FLAG_GGC_IS_SUPPORTED);                             \
    } while (0)

// This is wrong
#define Nst_GGC_OBJ_INIT(obj, trav_func, track_function)                      \
    do {                                                                      \
        obj->ggc_prev = NULL;                                                 \
        obj->ggc_next = NULL;                                                 \
        obj->ggc_list = NULL;                                                 \
        obj->traverse_func = (void (*)(Nst_Obj *))(trav_func);                \
        obj->track_func = (void (*)(Nst_Obj *))(track_function);              \
        Nst_FLAG_SET(obj, Nst_FLAG_GGC_IS_SUPPORTED);                         \
    } while (0)

#define Nst_GGC_OBJ_INIT(obj, trav_func, track_function) do {                 \
    obj->ggc_prev = NULL;                                                     \
    obj->ggc_next = NULL;                                                     \
    obj->ggc_list = NULL;                                                     \
    obj->traverse_func = (void (*)(Nst_Obj *))(trav_func);                    \
    obj->track_func = (void (*)(Nst_Obj *))(track_function);                  \
    Nst_FLAG_SET(obj, Nst_FLAG_GGC_IS_SUPPORTED);                             \
    } while (false)

#define Nst_GGC_OBJ_INIT(obj, trav_func, track_function) do { \
    obj->ggc_prev = NULL; \
    obj->ggc_next = NULL; \
    obj->ggc_list = NULL; \
    obj->traverse_func = (void (*)(Nst_Obj *))(trav_func); \
    obj->track_func = (void (*)(Nst_Obj *))(track_function); \
    Nst_FLAG_SET(obj, Nst_FLAG_GGC_IS_SUPPORTED); \
    } while (0)

Documenting#

The header file should always contain a short description of what the file contains and the author in this format.

/**
 * @file filename.h
 *
 * @brief Brief description
 *
 * @author Author's name or Github
 */

Above each public definition there must be an exhaustive description of how the thing works and behaves. If one line is sufficient, use C89-style comments. Always put a space after /* and before */.

/* Nst_CheckBytesFunc for ASCII */
NstEXP i32 NstC Nst_check_ascii_bytes(u8 *str, usize len);

When the description does not fit into one line but the function is not complex, you should use a multi line comment with the following format:

/**
 * @brief Sets the global operation error creating a string object from the
 * given message and using "Syntax Error" as the name.
 */
NstEXP void NstC Nst_set_syntax_error_c(const i8 *msg);

If the function is more complex you can use a more complete form of documentation that explains the parameters, the return value and gives some specifications:

/**
 * Compiles the AST.
 *
 * @brief Both ast and error are expected to be not NULL.
 *
 * @param ast: the AST to compile, will be freed by the function
 * @param is_module: whether the AST is of an imported module or of the main
 * file
 * @param error: the error set if one occurs
 *
 * @return The function returns the compiled Nst_InstList or NULL if an error
 * occurred.
 */
NstEXP Nst_InstList *NstC Nst_compile(Nst_Node *ast, bool is_module,
                                      Nst_Error *error);