:date: 2026-07-25 17:22
The last post introduced SnowC. Let's take a look at how it works.
In the realm of possible C programs, there is technically valid C and there is a subset of that which is good C. If you are writing good C then there are two major sources of redundancy that can be safely removed. The first is is that if you are indenting your code properly, you do not need curly braces to convey your nesting intent. The second main principle is that if you are using one sensible line of code per actual line in your source file, you can also consider C's semicolon terminator redundant with the actual end of line character.
Let's look at the easier one first. This is a valid line of C code. Technically it is also a complete C "statement".
xed.ch += snowc;
In SnowC the semicolon is simply dropped.
xed.ch += snowc
That was pretty easy, right? Ah, not so fast! C programmers usually put one statement per line in the source file but a lot of perfectly reasonable C code violates this. What is more prevalent — the one statement per line convention or the exceptions? This is not a trick question! The problem is that C must assume that you will not do the thing that's normally done. SnowC assumes you will.
C assumes you'll do something wild like this.
xed.ch
+=
snowc
;
Note that no extra syntax was required. In actual practice, there may be valid reasons for something like this but since this is the less common case, SnowC needs you to to really signal your intention here by escaping all the line endings that do not want a semicolon automatically reinstated. Here is the valid SnowC if you must do something like this.
xed.ch\
+=\
snowc
Note that there is no semicolon. One will be added to the last line because it is not escaped with a backslash. The escaping backslashes will get removed, and we'll be back to normal equivalent C.
Here is an example of some SnowC taken from its own source code showing a typical case where C's assumption that you will be casually adding line breaks is completely sensible.
for (int i=e; i>s; i--)
if (m[i] == mPRE\
|| m[i] == mCOM\
|| m[i] == mSTR)
return 0
Note the two lines ending in backslashes. The backslash basically means: "While I usually want you to automatically fill in the necessary semicolon at the end of most lines, this is not one of those lines."
This example is a good one to move on to C's other major redundancy:
curly braces. Note in that example that when the SnowC is converted to
C we want that final return 0 line to become return 0; as
discussed. However, this example is interesting because it is the
only line where we want an implicit traditional semicolon. What
about all the other lines?
It is important to appreciate that SnowC is not a proper compiler. It does not understand anything about your code (well, very little). All it does is look for certain style elements and makes simple conversions. Here is this example converted from SnowC back to ordinary C.
for (int i=e; i>s; i--) {
if (m[i] == mPRE
|| m[i] == mCOM
|| m[i] == mSTR) {
return 0;
}
}
The escapes are gone since, as we just covered, C assumes this kind of less common code. The return statement is reunited with its semicolon. And there are braces. Braces are what allow C to style that entire block in one line like this.
for(int i=e;i>s;i--){if(m[i]==mPRE||m[i]==mCOM||m[i]==mSTR){return 0;}}
Coding like this is kind of scary but, hey, maybe you've got a good reason. SnowC can let you do this kind of thing if you really must. Here is the same line in SnowC.
for(int i=e;i>s;i--){if(m[i]==mPRE||m[i]==mCOM||m[i]==mSTR){return 0;}}\
Basically add a backslash escape and SnowC will just assume you know what you're doing and largely leave the line alone.
However, normal SnowC tends not to have many curly braces. They are automatically reintroduced when converting to C based on indentation. To understand how this example works, let's rewrite it slightly the way SnowC actually sees it.
for (int i=e; i>s; i--)
if (m[i] == mPRE || m[i] == mCOM || m[i] == mSTR)
return 0
With the escaped lines treated as one we can see the clear structure here. It is hopefully visually apparent why this converts to the following C code.
for (int i=e; i>s; i--) {
if (m[i] == mPRE || m[i] == mCOM || m[i] == mSTR) {
return 0;
}
}
If you misalign one of the indentations in the C code by one space, that is obnoxious, but C doesn't care. In SnowC that is an error!
C is complicated. But if you already know C then SnowC is not. These basic rules are pretty much all you need. Converting from C to SnowC is very challenging (because C is very challenging) but SnowC is quite structured and follows simple rules and is easy to convert back to C.
During development I created the following (SnowC) pseudocode to summarize the logic of converting from SnowC to C. It turned out to be slightly more complex, but it is a very reasonable and succinct approximation of how SnowC works. It is a good way for a programmer to understand it conceptually.
// NLRI = Next Line's Relative Indent
// LEC = Last Effective Character, i.e. not trailing whitespace or comments.
// Generally ignore all preprocessor directives and comments!
if (NLRI > 0) // Indented.
add_opening_brace()
else // Not indented.
if (LEC == ':' || LEC == ';' || LEC == '}')
{/* pass - leave alone labels and user supplied C syntax */}
else if (LEC == '\\')
remove_escape_character() // And otherwise leave alone.
else
add_semicolon()
if (NLRI < 0) // Outdented.
add_closing_braces(NLRI)
This basically tells you all you need to know to effectively use SnowC. To get endless useful examples of SnowC, simply try converting some C you're familiar with and see what the results look like.
There is a lot of gruesome complexity under the hood to make this happen when wild C is involved but the beauty of SnowC is that if you have highly structured code (and most of us do most of the time!) then the redundant extra syntax that C requires can be added simply and automatically.