@@ -34,14 +34,21 @@ however, a common problem with libraries is the proliferation of inordinately long and hard-to-type function names such as `SuperWidget_Widget_Label_Font_Size_Set()`. this may be tolerable in IDEs with robust auto-complete or when referencing a highly-specific, sparsely-used library; it is however completely intolerable in the case of a core library with heavily used functionality. therefore, libk uses two slightly different naming conventions: the **short** convention, for core functions the user will call frequently, and the **full** convention, for less-commonly used functions. the inconvenience of remembering which is which will hopefully be outweighed by the keystrokes (and bytes) saved. -in the **full** convention, a function's name is prefixed with its module name followed by an underscore. thus, `kfile/open.c` will be invoked as `kfile_open()`. +in the **full** convention, an identifier's name is prefixed with its module name followed by an underscore. thus, `kgraft/list.c` is invoked as `kgraft_list()`. -in the **short** convention, the function name is prefixed by the letter `k` followed by the module's "glyph" -- a one- or two-letter sequence that represents the module, usually the first one or two characters. therefore, `kio/write.c` is invoked as `kiowrite`. +in the **short** convention, identifiers are prefixed by the letter `k` followed by the module's "glyph" -- a one- or two-letter sequence that represents the module, usually the first one or two characters. therefore, `kfile/open.c` is invoked as `kfopen`. which naming convention a module uses should be specified at the top of its documentation. if it uses the short convention, its glyph should be specified as well +in both naming conventions, the following rules apply: + + 1. the possible values of enumeration types are always preceded by the name of the enumeration type and an underscore. for instance, the enum `kschain_kind` has a value named `kschain_kind_block`. **exception:** an enum named `_kind`, where `` is a struct type, may simply use the prefix `_`. + 2. macros begin with the uppercase letter `K` -- e.g. `Kmacro`. macros that can be defined by the user to alter the behavior of the api should begin with `KF` if they are on/off flags, or `KV` otherwise. + 3. capital letters are only used in macro prefixes. + 4. low-level function names are prefixed with the API they call into. for example, the function that performs the POSIX syscall `write` is named `kio_posix_fd_write`.a wrapper around the Windows function `CreateProcess()` might be called `kproc_win_createprocess`. + ### atoms libk uses the concept of "atoms" (small, regular strings of text) to standardize common references, such as operating systems or processor architectures. @@ -74,8 +81,10 @@ ## macros libk will not in any circumstance use macros to encode magic numbers, instead using typedef'd enums. all libk macros begin with the uppercase letter `K` -- e.g. `Kmacro`. macros that can be defined by the user to alter the behavior of the api should begin with `KF` if they are on/off flags, or `KV` otherwise. **macros should only be defined by the libk headers if the flag `KFclean` is *not* defined at the time of inclusion.** +include guards take the form of the bare module name prefixed by `KI`. so to test if `k/term.h` has been included, you could write `#ifdef KIterm`. + ## languages libk uses only three languages: C (\*.c, \*.h), yasm (\*.s), and make (makefile). @@ -93,8 +102,10 @@ each module directory should contain a makefile that can build that module. see **makefiles** below. all makefiles should be named `makefile` (**not** `Makefile`). each module should contain a markdown file. this file's name should be the name of the parent directory suffixed with `.md`; for instance, `kterm` should contain the file `kterm/kterm.md`. this file should document the module as thoroughly as possible + +each module may contain any number of files of the name `exe.*.c`. this files will be treated as *tools* by the build system and compiled as executables, rather than libraries. they should be compiled to `out/$module.$tool` the repository root and each module may also contain the directory `misc`. this directory may be used to store miscellaneous data such as ABI references, developer discussions, and roadmaps. if the `misc` directory is deleted, this must not affect the library or build system's function in any way - that is, nothing outside a `misc` folder may reference a `misc` folder or anything inside it, including documentation. the `misc` directory should be removed when its contents are no longer needed. in most cases, the repository wiki and forum should be used instead of the `misc` folder. the folder `arch` in the root of the repository contains syscall tables and ABI implementations for various architectures. @@ -106,13 +117,41 @@ each rule should be prefixed with ${OUT}, to allow retargeting of the build-dir with the OUT environment variable. this is particularly important since the makefiles chain. the rest is TBD. +## design principles + +there are four overriding principles that guide the design of libk. + + 1. it should be easy to write code that uses it. + 2. it should be easy to read code that uses it. + 3. the simple, obvious way of using libk should produce the most optimal code. + 4. code that uses libk should be idiomatic C. + +for these reasons, the codebase follows a number of strict rules. + +### booleans are banned +there are a number of reasons for this. + +the first is simply that the boolean type in C is a bit messy and libk headers are intended to import as few extra files as possible. + +the second is that boolean-using code can be hard to read. consider a struct declaration of the form `rule r = { 10, buf, true, false, true }`: the meaning of this declaration is opaque unless you've memorized the structure's definition. + +instead, libk uses enums liberally. so the above might be rewritten as e.g.: + + rule r = { 10, buf, + rule_kind_undialectical, + rule_action_expropriate, + rule_target_bourgeoisie + }; + +this makes code much more legible and has the added benefit of making the definitions easier to expand at a later date if new functionality values is needed without breaking the API or ABI. + ## build process libk has a number of targets. all files generated by a `make` invocation will be stored in the folder "out" at the root of the repository. this directory may be deleted entirely to clean the repository. -**defs** will create the directory `out/k/` and populate it with module header files. the `k/` directory shall be suitable to copy to `/usr/include` or similar. these header files will copied by building the `defs` target of each module's makefile. +**defs** will create the directory `out/k/` and populate it with module header files. the `k/` directory shall be suitable to copy to `/usr/include` or similar. these header files will copied by building the `${OUT}/$(module).h` target of each module's makefile. **libk.so** will build the dynamically linked form of libk, according to the build variables set **libk.a** will build the statically linked form of libk, according to the build variables set