debian/: Ship `symbols' file for better downstream dependency versioning.
[sod] / STYLE
diff --git a/STYLE b/STYLE
index c1eb705..e85be96 100644 (file)
--- a/STYLE
+++ b/STYLE
@@ -1,19 +1,51 @@
-Notes on Lisp style
+Notes on coding style
 
 
-* Language subset and extensions
+* General
+
+** Layout
+
+Lines are 77 characters at most, except for strange special effects.
+Don't ask.  This is not negotiable, though.  Don't try to tell me that
+your monitor is very wide so you can read longer lines.  My monitor is
+likely at least as wide.  On the other hand, most lines are easily short
+enough to fit in my narrow columns, so the right hand side of a wide
+window would be mostly blank.  This seems wasteful to me, when I could
+fill that space with more code.
+
+Horizontal whitespace for layout purposes -- i.e., indentation and
+alignment, rather than just separating words -- consists of as many tabs
+as possible, followed by as many spaces as necessary to reach the target
+column.  Tab stops occur at every eight columns.  You can tell this
+because when you cat a file to your terminal, that's how the tabs
+appear.  Editors which disagree about this are simply wrong.
+
+My indentation quantum is usually two columns.  It seems that some
+modern editors are deeply confused, and think that tab width and
+indentation quantum are the same thing, but they aren't.  Such broken
+editors will make a hopeless mess of my code.  If you have the
+misfortune to use such an editor, maybe you could contribute patches to
+fix it.
+
+
+* Lisp style
+
+** Language subset and extensions
 
 None of ANSI Common Lisp is off-limits.
 
 I think my Lisp style is rather more imperative in flavour than most
 modern Lisp programmers.  It's probably closer to historical Lisp
 
 None of ANSI Common Lisp is off-limits.
 
 I think my Lisp style is rather more imperative in flavour than most
 modern Lisp programmers.  It's probably closer to historical Lisp
-practice in that regard, even though I wasn't writing Lisp back then.
+practice in that regard, even though I wasn't writing Lisp back then.  A
+lot of this is because I don't assume that the Lisp implementation
+handles tail calls properly: Common Lisp is not Scheme.
 
 I make extensive use of CLOS, and macros.  On a couple of occasions I've
 made macros which use CLOS generic function dispatch to compute their
 expansions.  The parser language is probably the best example of this in
 the codebase.
 
 
 I make extensive use of CLOS, and macros.  On a couple of occasions I've
 made macros which use CLOS generic function dispatch to compute their
 expansions.  The parser language is probably the best example of this in
 the codebase.
 
-I like hairy ~format~ strings.
+I like hairy ~format~ strings.  I've intentionally opted to leave them
+as challenges to the reader rather than explain them.
 
 I've avoided hairy ~loop~ for the most part, not because I dislike it
 strongly but because others do and I don't find that it wins big enough
 
 I've avoided hairy ~loop~ for the most part, not because I dislike it
 strongly but because others do and I don't find that it wins big enough
@@ -50,8 +82,7 @@ suite, which makes additional use of ~xlunit~.  Running the test suite
 isn't essential to getting the translator built, so this isn't as much
 of a problem.
 
 isn't essential to getting the translator built, so this isn't as much
 of a problem.
 
-
-* Layout
+** Layout
 
 I pretty much let Emacs indent my code for me, based on information
 collected by SLIME.  Some exceptions:
 
 I pretty much let Emacs indent my code for me, based on information
 collected by SLIME.  Some exceptions:
@@ -64,13 +95,28 @@ collected by SLIME.  Some exceptions:
     fixing.  Since I don't use hairy ~loop~ much, this isn't a major
     problem.
 
     fixing.  Since I don't use hairy ~loop~ much, this isn't a major
     problem.
 
-Lines are 77 characters at most, except for strange special effects.
-Don't ask.  This is not negotiable, though.  Don't try to tell me that
-your monitor is very wide so you can read longer lines.  My monitor is
-likely at least as wide.  On the other hand, most lines are easily short
-enough to fit in my narrow columns, so the right hand side of a wide
-window would be mostly blank.  This seems wasteful to me, when I could
-fill that space with more code.
+  + Emacs indents lambda lists really badly.  I often prefer to put the
+    entire lambda list on its own line than to split it.  If I have to
+    split a simple lambda list, without lambda-list keywords, I just
+    align the start of each subsequent line with the start of the first
+    argument.  I break hairy lambda lists before lambda-list keywords,
+    and the start of a subsequent line aligns with the first argument
+    name following the lambda-list keyword which begins the group, so
+    that the lambda-list keyword stands out.
+
+    : (defun many-arguments (first second third
+    :                       fourth fifth)
+    :   ...)
+
+    : (defun hairy-arguments (first second third
+    :                        &optional fourth fifth
+    :                                  sixth
+    :                        &rest others)
+    :   ...)
+
+    I don't know what I'd do if I had a hairy lambda list with so many
+    mandatory positional arguments that I had to split them.  So far,
+    this situation hasn't come up.
 
 Lisp code does have a tendency to march across to the right quite
 rapidly given a chance.  I have a number of strategies for dealing with
 
 Lisp code does have a tendency to march across to the right quite
 rapidly given a chance.  I have a number of strategies for dealing with
@@ -86,8 +132,7 @@ this.
   + Shrug my shoulders and let code dribble down the right hand side for
     a bit.
 
   + Shrug my shoulders and let code dribble down the right hand side for
     a bit.
 
-
-* Packages and exporting
+** Packages and exporting
 
 A package collects symbols which are given meanings in one or more
 source files.  If a package's code is all in one file, then the package
 
 A package collects symbols which are given meanings in one or more
 source files.  If a package's code is all in one file, then the package
@@ -119,8 +164,7 @@ See ~doc/list-symbols.lisp~ for more sophisticated reporting.  (In
 particular, this identifies what kind of thing(s) each external symbol
 names.)
 
 particular, this identifies what kind of thing(s) each external symbol
 names.)
 
-
-* Comments and file structuring
+** Comments and file structuring
 
 A file starts with a big ~;;;~ comment bearing the Emacs ~-*-lisp-*-~
 marker, a quick description, and copyright and licensing boilerplate.  I
 
 A file starts with a big ~;;;~ comment bearing the Emacs ~-*-lisp-*-~
 marker, a quick description, and copyright and licensing boilerplate.  I
@@ -129,7 +173,8 @@ special effects.
 
 Then there's package stuff.  There may be a ~cl:defpackage~ form (with
 explicit package qualifier) if the relevant package doesn't have its own
 
 Then there's package stuff.  There may be a ~cl:defpackage~ form (with
 explicit package qualifier) if the relevant package doesn't have its own
-package definition file.
+package definition file.  I use gensyms to name packages: strings don't
+seem right, and symbols would leak into some unrelated package.
 
 Then there's ~cl:in-package~.  Like ~defpackage~, I use a gensym to name
 the package.  I can't think offhand of a good reason to have a file with
 
 Then there's ~cl:in-package~.  Like ~defpackage~, I use a gensym to name
 the package.  I can't think offhand of a good reason to have a file with
@@ -171,8 +216,7 @@ Files end, as a result of long tradition, with a comment
 
 : ;;;----- That's all, folks --------------------------------------------------
 
 
 : ;;;----- That's all, folks --------------------------------------------------
 
-
-* Macro style
+** Macro style
 
 I don't mind complicated macros if they're doing something worthwhile.
 They need to have good documentation strings, though.
 
 I don't mind complicated macros if they're doing something worthwhile.
 They need to have good documentation strings, though.
@@ -185,8 +229,7 @@ It's extremely bad taste for a macro to evaluate its evaluable
 parameters in any order other than strictly left to right, or to
 evaluate them more than once.
 
 parameters in any order other than strictly left to right, or to
 evaluate them more than once.
 
-
-* Data structures
+** Data structures
 
 I've tended to be happy with plain lists for homogeneous-ish
 collections.  Strongly heterogeneous collections (other than input
 
 I've tended to be happy with plain lists for homogeneous-ish
 collections.  Strongly heterogeneous collections (other than input
@@ -242,8 +285,7 @@ making a new structure type.  I tend to tidy up a few rough edges.
     to roll up your sleeves and mess with ~&aux~ parameters to pull it
     off.
 
     to roll up your sleeves and mess with ~&aux~ parameters to pull it
     off.
 
-
-* Naming
+** Naming
 
 I'm a traditionalist in some ways, and one of the reasons I like Lisp is
 the richness of its history and tradition.
 
 I'm a traditionalist in some ways, and one of the reasons I like Lisp is
 the richness of its history and tradition.
@@ -257,16 +299,15 @@ I've also tended to go for fairly prosaic names, taking my inspiration
 from the CLOS MOP.  While I mourn the loss of whimsical names like
 ~haulong~ and ~haipart~, I've tried to avoid inventing more of them.
 
 from the CLOS MOP.  While I mourn the loss of whimsical names like
 ~haulong~ and ~haipart~, I've tried to avoid inventing more of them.
 
-There's a convention, which I think comes from ML, of using ~_~ in a
-where a binding occurrence of a variable name is expected, to signify
-that that the corresponding value is to be discarded.  Common Lisp,
-alas, doesn't have such a convention.  Instead, there's a sequence of
-silly names used with the same intention, and the bindings are then
-explicitly ignored with a declaration.  The names begin ~hunoz~,
-~hukairz~, and (I think) ~huaskt~.
+There's a convention, which I think comes from ML, of using ~_~ where a
+binding occurrence of a variable name is expected, to signify that that
+the corresponding value is to be discarded.  Common Lisp, alas, doesn't
+have such a convention.  Instead, there's a sequence of silly names used
+with the same intention, and the bindings are then explicitly ignored
+with a declaration.  The names begin ~hunoz~, ~hukairz~, and (I think)
+~huaskt~.
 
 
-
-* Declarations
+** Declarations
 
 The code is light on declarations, other than ~ignore~ and similar used
 to muffle warnings.  The macros try to do sensible things with
 
 The code is light on declarations, other than ~ignore~ and similar used
 to muffle warnings.  The macros try to do sensible things with
@@ -276,16 +317,279 @@ actual correctness, declarations provided by the caller need to be split
 up into a number of different parts of the expansion, which in turn
 requires figuring out what the declarations mean and which bindings
 they're referring to.  That's not completely impossible, assuming that
 up into a number of different parts of the expansion, which in turn
 requires figuring out what the declarations mean and which bindings
 they're referring to.  That's not completely impossible, assuming that
-there aren't implementation-specific declarations which crazy syntax
+there aren't implementation-specific declarations with crazy syntax
 mixed in there, but it's more work than seems worthwhile.
 
 
 mixed in there, but it's more work than seems worthwhile.
 
 
+* C style
+
+** Language subset and extensions
+
+I'm trying to support C89 still.  There are few really worthwhile
+features in C99 and later, though there are some.  For now, I want Sod
+to continue working if built with a C89 compiler, even if some things --
+e.g., most notably the macro sugar for varargs messages -- are
+unavailable.
+
+Similarly, I'll use compiler-specific features if they don't adversely
+affect portability.  For example, I'll use GCC attributes to improve
+compiler diagnostics, but they're wrapped up in preprocessor hacking so
+that they won't be noticed by compilers which don't understand them.
+I'm generally happy to accept contributions which make similar
+improvements for other compilers.
+
+Sod is supposed to have minimal dependencies.  It should be able to work
+in what the ISO C standard names a `freestanding environment', without
+most of the standard C library.  The keyword-argument library is
+carefully split into a piece which is fully portable and a piece which
+depends on features which are only available in hosted environments,
+like being able to print stuff to ~stderr~, so that users targetting
+embedded systems have an easy porting job.
+
+** Naming
+
+I usually give local variables, arguments, and structure members very
+short names, just one or two characters long.  I find that longer names
+are harder to distinguish, and take up horizontal space.  Besides,
+mathematicians have been using single-letter variable names quite
+successfully for hundreds of years.
+
+I usually choose variable names to match their types in an informal way.
+Loop counters are often called ~i~, ~j~, ~k~; generic pointers, and
+pointers to bytes or characters, are usually ~p~ or ~q~; a character is
+often ~ch~; a ~FILE~ pointer is ~fp~ following long tradition; sizes of
+things, in bytes, are ~sz~, while lengths of vectors, in elements, are
+~n~.  I often name values of, or pointers to, structures or custom types
+with the first letter of the type.  If I have two things of the same
+kind, I'll often double the name of one of them; e.g., if I have two
+pointers to ~whatsit~ structures, I might call them ~w~ and ~ww~.
+
+I don't (any more) give ~typedef~ names to structures or unions.  This
+makes it possible to have a variable with the same name as the structure
+tag without serious trouble.
+
+In variable names, I tend to just squash pieces of words together; in
+longer names, sometimes I'll put in underscores to split things up a
+bit.  Camel case is bletcherous.
+
+File-scope names with /internal/ linkage -- i.e., things marked ~static~
+-- generally deserve somewhat longer names.  I don't give them other
+kind of marking; e.g., I'd probably name the pointer to the head of a
+list of ~foo~ things something like ~foohead~.
+
+Names with /external/ linkage want more care because they're playing in
+a shared global namespace.
+
+** Layout
+
+The C indent quantum is two columns.
+
+Declarations go at the top of functions.  I don't put declarations in
+inner blocks, and I certainly don't scatter declarations throughout a
+block.  I find that having the declarations all in one place makes it
+easier for me to keep track of what things the function is going to be
+thinking about.
+
+If I can't set a variable to its proper value immediately, I'll leave it
+uninitialized until I can.  That way, the compiler will warn me if I
+forget.
+
+Most of my style is an attempt to get as much interesting code on the
+screen at a time, and still be able to read it.  The short variable
+names keep things distinct while keeping statements short; short
+statements don't need to be split across multiple lines.  And keeping
+the overall line length limit low means I can fit more /columns/ of code
+on my screen.
+
+If there are several related variables with the same declaration
+specifiers, I'll usually write a single declaration for all of them --
+even if they have different actual types.  For example,
+
+: struct foo f, *fp = &f;
+
+Note that a ~*~ declarator operator has a space to its left, but never
+to its right.  (Stroustrup's style horribly misrepresents the underlying
+syntax.)
+
+I will often write multiple statements on a single line, usually to
+indicate that these things are part of the same thought, and they
+shouldn't be separated.  For example, if I'm working through an array of
+things, I might have a pointer ~p~ to the element I'm hacking on, and a
+count ~n~ of things left to hack, I'll have a loop
+
+: while (n) {
+:   /* hack on *p */
+:   p++; n--;
+: }
+
+so that the two updates don't get separated.
+
+I don't wrap braces around individual statements that fit on a single
+line.  For example, I'll write
+
+: while (*p == ' ') p++;
+
+On the other hand, if a single substatement is going to take more than
+one line then it gets wrapped in braces.
+
+I don't write blocks which aren't part of larger compound statements,
+e.g., ~if~ or ~while~.  I'll write a compound statementon a single line
+if I can; but I'll split ~if~ with an ~else~ over two lines.  For
+example,
+
+: if (a == 1) x = 0;
+: else if (b == 3) { y = 2; z = 1; }
+: else w = 15;
+
+On the other hand, if I can't write all of the branches of an
+~if~\relax/\relax ~else if~ ladder like this, then /all/ of the
+substatements get their own lines.  (I write ~do~\relax/\relax ~while~
+loops in the same way, but this comes up much less frequently.)
+
+If I can't write a block on the same line, then the opening brace goes
+on the same line as the statement head, and the closing brace gets its
+own line.  A trailing ~else~ or ~while~ goes on the same line as the
+previous closing brace, if there is one.
+
+I don't write spaces inside parentheses or square brackets, or between
+unary operators and their operands.  I always write ~sizeof~ as if it
+were a function, even though I know it isn't.  I write a single space
+either side of non-multiplicative binary operators -- i.e., other than
+~*~, ~/~, ~%~, and ~&~; I don't write spaces around multiplicative
+operators any more.  The comma operator is special, and gets a space
+after, but not before.
+
+If I'm breaking a long line at a binary operator, the break comes
+/after/ the operator, not before.
+
+** Common conventions
+
+A /predicate/ is a function which answers a yes/no question -- and has
+no side-effects.  I don't use ~bool~ or similar; predicates return
+~int~, such that zero is false and nonzero is true.  Predicates usually
+have names ending in ~p~ or ~_p~.  (Note that function names
+beginning ~is...~ are reserved for future ~<ctype.h>~ macros.)
+
+On the other hand, an /operation/ is a function whose main purpose is to
+have an effect -- maybe create a thing, or update some state.  In the
+absence of better ideas, operations also return ~int~, but zero
+indicates success, and nonzero -- usually $-1$ -- indicates failure.
+
+** Error handling and resource management
+
+I've tried many techniques.  I think the following is the best approach
+so far.
+
+I try to arrange that every type which represents some resource which
+might need releasing has an easily recognizable `inert' value which
+indicates that the resource has not been acquired.  At the top of a
+function, I initialize all of the variables which might hold onto
+resources to their inert values.  At the end of the function, I place a
+label, ~end~ or ~fail~.  An ~end~ label is for common cleanup; a ~fail~
+label is for cleanup that's only needed on unsuccessful completion.
+
+
+** Miscellaneous style issues
+
+I write ~0~, not ~NULL~.  Doing this prevents a common error in
+null-terminated variable-length argument lists, e.g., ~execlp~, where
+~NULL~ is actually an integer ~0~ in disguise and ends up being an ~int~
+where a pointer was wanted.
+
+I don't usually write redundant comparisons against ~0~, or ~NULL~, or
+well-known return codes indicating success.  Again, this helps with
+compression.  I'll write
+
+: rc = do_something(foo, bar); if (rc) goto end;
+
+(yes, one line) rather than comparing ~rc~ against some ~STATUS_SUCCESS~
+code or similar.  Exception: I still haven't decided whether I prefer
+leaving the explicit relational in ~strcmp~ and similar tests.
+
+I always write parentheses around the expression in a ~return~
+statement.
+
+In declarations, storage classes come first (e.g., ~static~, ~extern~,
+~typedef~), followed by qualifiers (~const~, ~volatile~; I never use
+~restrict~), and then the type specifiers, signedness indicators first
+where they aren't redundant (so maybe ~signed char~ for special effects,
+but never ~signed int~), then length indicators, then the base type.  I
+omit ~int~ if there are other type specifiers, so ~unsigned~ or ~long~,
+rather than ~unsigned int~ or ~long int~.
+
+The full declarator syntax for function pointer is pretty ugly.  I often
+simplify it by defining a ~typedef~ for the /function/ type, not the
+function pointer type.  For example
+
+: typedef int callbackfn(struct thing */*t*/, void */*p*/);
+
+I'd then use variables (structure members, arguments, etc.) of type
+~callbackfn *~.
+
+In header files, I comment out argument names to prevent problems with
+macros defined by client translation units.  Also, I explicitly mark
+function declarations as being ~extern~.
+
+** Comments and file structuring
+
+I never use C++-style ~//~ comments except for temporary special
+effects.
+
+If a comment fits on one line, then its closing ~*/~ is on the same
+line; otherwise, the ending ~*/~ is on a line by itself, and there's a
+spine of ~*~ characters in a column on the left.
+
+A file starts with a big comment bearing the Emacs ~-*-c-*-~ marker, a
+quick description, and copyright and licensing boilerplate.
+
+Header files are wrapped up with multiple-inclusion and C++ guards, with
+
+: #ifndef HEADER_H
+: #define HEADER_H
+:
+: #ifdef __cplusplus
+:   extern "C" {
+: #endif
+
+at the top.
+
+The rest of the file consists of C code.  I don't use page boundaries
+~^L~ to split files up.  Instead, I use big banner comments for this:
+
+: /*----- Section title -----------------------------------------------------*/
+
+Following long tradition, functions and macros are documented in a
+preceding comment which looks like this.
+
+: /* --- @name@ --- *
+:  *
+:  * Arguments:        @type fmm@ = a five-minute argument
+:  *           @type fhh@ = the full half-hour
+:  *
+:  * Returns:  A return value.
+:  *
+:  * Use:      It does a thing.  Otherwise I wouldn't have bothered.
+:  */
+
+Sometimes (rarely) the description of the return value explains
+sufficiently what the thing does.  If so, the `Use' part can be omitted.
+Fragments of C code in this comment are surrounded by ~@~ characters.
+There can also be \LaTeX\ maths in here, in ~%$~...\relax ~$%~.
+
+Files end, as a result of long tradition, with a comment
+
+: /*----- That's all, folks -------------------------------------------------*/
+
+The closing ~#endif~ of a header file comes after this final comment.
+
+
 * COMMENT Emacs cruft
 
 #+LATEX_CLASS: strayman
 
 ## LocalWords:  CLOS ish destructure destructured accessor specializers
 * COMMENT Emacs cruft
 
 #+LATEX_CLASS: strayman
 
 ## LocalWords:  CLOS ish destructure destructured accessor specializers
-## LocalWords:  accessors DSLs gensym
+## LocalWords:  accessors DSLs gensym gensyms bletcherous Stroustrup
+## LocalWords:  Stroustrup's signedness
 
 ## Local variables:
 ## mode: org
 
 ## Local variables:
 ## mode: org