# Contributing to Teeny Scheme

## Code Style

Code style used for C files is Linux Kernel style.
So tabs, minimized empty/bracket-only lines, and overall readability.
With one exception: procedure names should start lines.
This is for searchability.
You can do `grep "^[a-z].*(" scheme.c` and find all the procedure prototypes in Teeny.
`make indent` is generally enough to format the code, 
though you might need to re-run it one extra time to make it converge on a minimal set of changes.

### Syntax Sugar

Teeny Scheme also uses a lot of syntax sugar to make the code more readable to an average human.
In particular, [nicer logical operators from iso646.h](https://en.cppreference.com/c/header/iso646) 
(`and`, `or`, `not`) are used extensively.
The only place where it is allowed to not use them is `#if` macros, because these do not support spelled-out logicals.
In all the other instances, just use iso646.h macros.

In addition to these, a bunch of macros is defined for more readable code:
``` c
#define isnt !=
#define is ==
#define eq ==
```

The heuristic for use of these is:
- `=` and `!=` should only be used on numbers.
- In all other cases (including characters,) use `is` and `isnt`.
- In case using `is` feels slightly off, because things _aren’t_, but _are equivalent_—use `eq`.
- You do not need to put the constant first in the comparison, which is customary in C. You can safely write `var is 0xDEAD` because `is` is unambigously an equality operator.

### Standard Libraries

- Use `bool`-eans from `<stdbool.h>`: `true` and `false`. Never use 1 / 0 / whatever number as boolean.
  - If you notice some piece of code doing that, send a fix.
  - Unfortunately, not many compilers detect misuse of numbers as booleans. `clang-tidy` might help.
- Use `nullptr` for all null pointers. Do not use `NULL` unless strongly justified.
- Use `size_t` instead of `int` for lengths and sizes. Some parts of the codebase (like `list_length` function) still use `int` for this, because they allow negative (!) lengths. I / someone will eventually fix that.
- Use fixed-width integer types from `<stdint.h>` as much as possible. The size and domain of everything should be rigorously defined.

## Zero Warnings, Zero Hazards

There’s a lot of emphasis on making code correct and using compilers’ help for that.
Make sure that your code compiles without warnings.
Preferably even with additional checks listed in the makefile.
Maybe—but not necessarily—using a C++ compiler (they are known to be stricter.)
And, in case the code does compile without warnings, then make it break and fix it.
Warning-less code is nice, but code that’s guaranteed to be working by the compiler is even better.
Make sure the compiler can guarantee that future changes to the code don’t break it.

### Good Guarantees Example

A good example of this policy is the structure of opcodes.
Removing any handler will result in compilation error:

```
scheme.c:5017:54: error: ‘DO_OP_LOAD’ undeclared here (not in a function); did you mean ‘DO_OP_LOG’?
 5017 | #define _OP_DEF(NAME, MINARITY, MAXARITY, TYPES, OP) DO_##OP,
      |                                                      ^~~
scheme-ops.h:2:1: note: in expansion of macro ‘_OP_DEF’
    2 | _OP_DEF("load",               1,  1,       TST_STRING,               OP_LOAD)
      | ^~~~~~~
```

While removing its opcode metadata results in unused handler:

```
scheme.c:2793:24: warning: ‘DO_OP_LOAD’ defined but not used [-Wunused-function]
 2793 |         static pointer DO_##op(__VA_ARGS__)
      |                        ^~~
scheme.c:2795:1: note: in expansion of macro ‘DEFHANDLER’
 2795 | DEFHANDLER(OP_LOAD, "load", scheme *sc)
      | ^~~~~~~~~~
```

### Bad Guarantees Example

`TST_*` test structure is brittle, which even the original code comments around it prove:
```
/* Correspond carefully with following defines! */
static struct {
	test_predicate fct;
	const char *kind;
} tests[] = {
	{nullptr, nullptr},		/* unused */
	{is_any, nullptr},
	{is_string, "string"},
    /* ... */
};

// correspond with preceding struct "tests"
#define TST_NONE nullptr
#define TST_ANY "\001"
#define TST_STRING "\002"
/* ... */
```

The number of `tests` should coincide with the uppermost `TST_*` constant value.
It’s extremely brittle and in need of a change.
I’m yet to come up with a way to change that, though.

## LLM et al. Contributions

Teeny Scheme does not accept Generative AI contributions.
At all.
Zero.
Null.
Nigil.
