diff --git a/STYLE b/STYLE index 5322661..aa233a2 100644 --- a/STYLE +++ b/STYLE @@ -1,11 +1,26 @@ The following guidelines are conducive to clear and readable code that is consistent with the style of the rest of the Bonsai Computer System. -0. Braces are mandatory for all control flow. +0. Use: + - a single line for control flow statements short enough to be easily +understood at a glance: -1. Nested indentation should be kept to a minimum. + if !(argc < 0) { usage(program_name); } -2. Empty lines should be placed between different kinds of statements: +This applies to C switch cases, as well. + + switch (value) { /* aligning stuff to make it easier to read is fine */ + case possibility: variable = foo; break; + default: variable = NULL; break; + } + + - as little nested logic as possible (within reason). + - braces in control flow, when their inclusion is left optional by a +programming language (in, for example, C). + + if (condition) { statement; } + + - empty lines between different kinds of statements: int t; @@ -25,56 +40,66 @@ consistent with the style of the rest of the Bonsai Computer System. return io; -3. Each block of code should be indented once more than the keyword which -initiated the block: - - switch (c) { - case 'e': mode |= EQUAL; break; - case 'g': mode |= GREATER; break; - case 'l': mode |= LESS; break; - default: return usage(s); - } - -4. In C, spaces should be placed in control flow statements after the keyword -and before the opening brace: - - for (i = 2; i < argc; ++i) { - -5. If a function, a C control flow statement, or a Rust macro has arguments that -cause the statement to be broken into multiple lines, this should be done by -placing the arguments on a new line inside the parentheses: + - compiler options that yield the most useful warnings, such as -Wpedantic in +a lot of C compilers. Fix the warnings, too. See [0]. + - fixed bounds for loops; see [0]. + - one more level of indentation and one line per argument, when a function +call or statement header is too long to fit on one line: let usage = format!( "Usage: {} [-d delimiter] index command [args...]", argv[0], ); -6. If Rust function arguments or fields are on their own lines, they should -always have a trailing comma: + - one more level of indentation than the keyword that initiated a block. + + if (condition) { + statement; + statement; + } + + - the return value of all non-void functions, or explicitly ignore them (like +casting to void in C): + + if ((a = malloc(sizeof char)) == NULL) { /* handle this error */ + (void)fprintf(stderr, "oh noes!"); /* explicitly ignore this one */ + return EX_OSERR; /* ...because the program is exiting anyway */ + } + +See [0]. + + - the smallest possible scope for data; see [0]. + - (C) comments noting all the symbols and macros used from a header, next to +its include macro: + + #include /* close(2), getopt(3), lseek(2), read(2), write(2), + (space-aligned) * optarg, optind, STDIN_FILENO, STDOUT_FILENO */ + + - (C) one more level of indentation within switch cases. + + switch (value) { + case possibility: + statement; + default: + statement; + } + + - (C) spaces in control flow statements, after the keyword and before the +opening brace: + + for (i = 2; i < argc; ++i) { + + - (Cpp) as little of the preprocessor as possible; see [0]. + - (Rust) a trailing comma on all arguments or fields that are on their own +lines: return Err(EvaluationError { message: format!("{}: Invalid token", i), code: EX_DATAERR, }) -7. If text is on the same line as a brace, spaces should be placed after an -opening curly brace and before a closing one: - - use sysexits::{ EX_DATAERR, EX_IOERR, EX_UNAVAILABLE, EX_USAGE }; - -8. If a control flow statement is short enough to be easily understood in a -glance, it may be placed on a single line: - - if !(argc < 0) { usage(program_name); } - -9. In C, note everything you use from a library in a comment subsequent to its -#include statement: - - #include /* close(2), getopt(3), lseek(2), read(2), write(2), - * optarg, optind, STDIN_FILENO, STDOUT_FILENO */ - -10. In Rust, place extern statements after use statements that include standard -library crates. Group alike statements: + - (Rust) extern statements after use statements that include standard library +crates. Group like statements: use std::fs::Path; @@ -84,40 +109,74 @@ library crates. Group alike statements: use strerror::StrError; use sysexits::{ EX_OSERR, EX_USAGE }; -11. Do not use do while loops in C. + - (Rust) if text is on the same line as a brace, spaces after an opening curly +brace and before a closing one: -12. Adhere to the following rules from the paper The Power of 10: Rules for -Developing Safety-Critical Code [0]: - 1. Avoid complex flow constructs, such as goto and recursion. - 2. All loops must have fixed bounds. This prevents runaway code. - 3. Avoid heap memory allocation. - 4. Restrict functions to the length of a single printed page. + use sysexits::{ EX_DATAERR, EX_IOERR, EX_UNAVAILABLE, EX_USAGE }; - 6. Restrict the scope of data to the smallest possible. - 7. Check the return value of all non-void functions, or cast to void to - indicate the return value is useless (such as in the case of using - fprintf(3p) to print to the standard error). - 8. Use the preprocessor sparingly. - 9. Limit pointer use to a single dereference, and do not use function - pointers. - 10. Compile with all possible warnings active; all warnings should then be - addressed before release of the software (for C compilers, compile with - -Wpedantic). + - (Rust) one more level of indentation within match arms. -13. Remember this quote from The Elements of Programming Style by Brian -Kernighan: - Everyone knows that debugging is twice as hard as writing a program in the - first place. So if you're as clever as you can be when you write it, how - will you ever debug it? +1. Avoid: + - function pointers; see [0]. + - heap memory allocation; see [0]. + - too many levels of dereferences; see [0]. + /* do not do this */ + for (size_t i = 0; i < sizeof a / sizeof *a; ++i) { + if (a[i].id == MATCH) { a[i].val = 0; } + } + + /* do this */ + for (struct MadeUp *s = &a[0]; *s != NULL; s = &s[1]) { + if (s->id == MATCH) { s->val = 0; } + } + +2. Do not use: + - more than the length of one printed page for a function; see [0]. + - recursion, as it's complex and can unexpectedly overflow the stack; see [0] +and section 2 of this document. + - (C) any language features not in C99. + - (C) do-while loops, as they're unique to the language and confusing for +casual C programmers. + - (C) gotos; use sensible flow control, see [0]. + - (C) pointer arithmetic, as it tends to be confusing and unnecessary; use +index-reference patterns like &p[1] instead of p + 1. &p[n] is the address at +p + sizeof p * n, not p + n, like pointer arithmetic suggests. + - (C) struct bitfields in unions, to access certain bits of bigger data types, +as it's poorly defined in the C standards; use bit arithmetic. + - (C) trigraphs. + - (Cpp) inclusions in C header files, to prevent multiple file inclusions. + - (Cpp) variables to prevent multiple inclusions of the same file, such as: + + #ifdef _FILE + #define _FILE + /* file body */ + #endif /* ifdef _FILE */ + +Instead, take the time to ensure other files aren't including the any files +twice. + + - (libc) any functionality not in the latest POSIX or C99. + - (libc) gets(3p) from , as it's impossible to prevent buffer +overflows when it's used; use fgets(3p) from . + - (libc) scanf(3p) from ; see [1]. + - (Make) any functionality not described in make(1p) from the latest POSIX. + +2. Keep the following in mind: + - Everyone knows that debugging is twice as hard as writing a program in the +first place. So if you're as clever as you can be when you write it, how will +you ever debug it? + -- Brian Kernighan, in The Elements of Programming Style References ========== [0] +[1] -- Copyright © 2024 Emma Tebibyte +Copyright © 2024 DTB Copyright © Wikipedia contributors This work is licensed under CC BY-SA 4.0. To view a copy of this license, visit