<?xml version="1.0"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><atom:link href="http://xed.ch/rss.xml" rel="self" type="application/rss+xml" /><title>Xed's Blog</title><link>http://xed.ch/</link><description>A blog dedicated to the enrichment of Chris X. Edwards.</description><language>en-us</language><image><url>http://xed.ch/xedlogo.gif</url>         <title>Xed's Blog</title>                        <link>http://xed.ch/</link></image><generator>Chris just threw together a Bash script</generator>
<item><title>SnowC - A Demonstration</title><link>http://xed.ch/blog/2026/0731.html</link><guid>http://xed.ch/blog/2026/0731.html</guid><pubDate>Fri, 31 Jul 2026 20:10 EDT</pubDate><description>&lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;In previous posts I &lt;a href=&quot;https://xed.ch/b/2026/0724.html&quot;&gt;introduced SnowC&lt;/a&gt; and explained &lt;a href=&quot;https://xed.ch/b/2026/0725.html&quot;&gt;how it works&lt;/a&gt; and what &lt;a href=&quot;https://xed.ch/b/2026/0727.html&quot;&gt;challenges&lt;/a&gt; had to be overcome. In this post let&amp;#8217;s look at SnowC in action and see what it&amp;#8217;s capable of.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Recall that the most challenging project goal was to be able to convert wild C source code into sensible SnowC. That was phase one. Phase two was relatively easy, just following the SnowC rules to convert SnowC back to C. Let&amp;#8217;s have a closer look at phase three which was to get both of these programs working well enough that the SnowC conversion programs&apos; source code themselves could be both converted.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;We can start by downloading the SnowC project from&amp;#8230;&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://xed.ch/project/snowc/snowc.tgz&quot;&gt;https://xed.ch/project/snowc/snowc.tgz&lt;/a&gt;&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Here is a good way to get to a directory with the SnowC source code.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;$ cd /tmp/ $ wget -qO- https://xed.ch/project/snowc/snowc.tgz | tar -xzf - $ cd snowc-*&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;To test the round trip conversions, I&amp;#8217;ve created a script. The script is pretty illustrative and kind of interesting in a mind bending kind of way, but you can also ignore its details and skip ahead to just focus on the big picture.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;#!/bin/bash # Enter number of rounds as an argument or default to 3. MAX_ROUNDS=${1:-3} # === Initialization (Round 0) === echo &quot;=== Round 0: Initial Compilation ===&quot; gcc -Wall -o c2snowc c2snowc.c gcc -Wall -o snowc2c snowc2c.c # Generate initial SnowC files (.cno) from original C. ./c2snowc c2snowc.c &amp;gt; c2snowc.cno ./c2snowc snowc2c.c &amp;gt; snowc2c.cno # Convert initial SnowC back to C (Round 1 output). ./snowc2c c2snowc.cno &amp;gt; C2SNOWC-01.c ./snowc2c snowc2c.cno &amp;gt; SNOWC2C-01.c # Compile Round 1 C files. gcc -Wall -o C2SNOWC-01 C2SNOWC-01.c gcc -Wall -o SNOWC2C-01 SNOWC2C-01.c # Initial check (Round 1 verification). echo &quot;=== Round 1 Verification ===&quot; diff &amp;lt;(./c2snowc C2SNOWC-01.c) &amp;lt;(./C2SNOWC-01 C2SNOWC-01.c) md5sum &amp;lt;(./c2snowc C2SNOWC-01.c) &amp;lt;(./C2SNOWC-01 C2SNOWC-01.c) | cut -b-32 # === Main Loop (Previous Round = 1 to MAX_ROUNDS - 1) === for PR in $(seq -f&apos;%02.0f&apos; $(( ${MAX_ROUNDS} - 1 )) ); do R=$(printf &quot;%02d&quot; $((10#$PR + 1))) # This round = PR++. echo &quot;=== Round $R ===&quot; # 1. Create (R-1) SnowC from (R-1) C. # Using the executables from the previous round (PR). ./C2SNOWC-$PR C2SNOWC-$PR.c &amp;gt; C2SNOWC-$PR.cno ./C2SNOWC-$PR SNOWC2C-$PR.c &amp;gt; SNOWC2C-$PR.cno # 2. Create (R) C from (R-1) SnowC. # Using the SNOWC2C executable from the previous round. ./SNOWC2C-$PR C2SNOWC-$PR.cno &amp;gt; C2SNOWC-$R.c ./SNOWC2C-$PR SNOWC2C-$PR.cno &amp;gt; SNOWC2C-$R.c # 3. Compile (R) C to (R) executables. gcc -Wall -o C2SNOWC-$R C2SNOWC-$R.c gcc -Wall -o SNOWC2C-$R SNOWC2C-$R.c # 4. Verification: Compare (R) executable output vs (R-1) C input. echo &quot;Verifying Round $R...&quot; diff &amp;lt;(./C2SNOWC-$R C2SNOWC-$PR.c) &amp;lt;(./C2SNOWC-$R C2SNOWC-$R.c) md5sum &amp;lt;(./C2SNOWC-$R C2SNOWC-$PR.c) &amp;lt;(./C2SNOWC-$R C2SNOWC-$R.c) | cut -b-32 done&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Basically this script compiles the SnowC conversion programs from the original C source code (the code I originally wrote) and then uses those executables to convert the same source code to SnowC and then back to C. After a new set of C source code files has been generated, you can start the process again with the new files. You can specify how many times you want to make this round trip.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;If I run this with a single round trip, it works. The outputs of the new version and the original version match.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;$ ./roundtrip 1 === Round 0: Initial Compilation === === Round 1 Verification === 13133dd985944b14d2482971baa3c9a1 13133dd985944b14d2482971baa3c9a1&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;This means the newly generated C source code behaves exactly like the original source code. And if I run the script 99 times and check the 99th executable, it also functions identically to the first.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;$ md5sum &amp;lt;(./c2snowc c2snowc.c) &amp;lt;(./C2SNOWC-99 c2snowc.c) | cut -b-32 ba07eb908e548a6e6ceb36b70cd087b3 ba07eb908e548a6e6ceb36b70cd087b3&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Hmm, shouldn&amp;#8217;t those md5 summaries be the same? Probably, it would be nice for sure. When the script is run with a high number of cycles you can easily see the problem. Those &lt;code&gt;diff&lt;/code&gt; checks show that trailing comments are somehow getting pushed to the right by one space every iteration.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;&lt;span class=&quot;image&quot;&gt; &lt;img src=&quot;http://xed.ch/blog/2026/i/0801-f34a-drift.gif&quot; alt=&quot;drift.gif&quot; /&gt; &lt;/span&gt;&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;It looks like when braces are removed to make the SnowC, all the spaces around them stay, and then when a brace is added back to make the C again, it gets a polite space to format/separate it from the previous characters but all the spaces from the last round are still there too. So the line grows. An interesting bug for sure but minor enough to really be a serious issue only if you&amp;#8217;re doing hundreds of round trip conversions!&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;I&amp;#8217;ll fix this bug but it does not affect the critical goal of phase three. With the code supporting a functional round trip, it is now reasonable from here on to be able &lt;strong&gt;to continue development in SnowC&lt;/strong&gt;. Hopefully we&amp;#8217;ll see how that turns out sometime in the future.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Getting to phase three where a round trip is possible was the main original goal and now that I have done that, I&amp;#8217;ll probably take a break from this project. Besides the issue I just mentioned, I have some known bugs I&amp;#8217;d like to fix and I would like to try working directly in SnowC to see what that&amp;#8217;s actually like. But what is next? What is phase four?&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;I think the next major milestone is improving and proving the robustness of the system by having it try to do round trips with C code in the wild. My main target is the &lt;a href=&quot;https://kernel.org&quot;&gt;Linux kernel&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Ignoring header files (for now) the 7.1.5 Linux kernel is distributed with 36684 C source code files. I used &lt;code&gt;c2snowc&lt;/code&gt; to convert all but 11 of them. In just a quick inspection of those, I discovered a nasty configuration of nugatory else matching (see &lt;a href=&quot;https://xed.ch/b/2026/0730.html&quot;&gt;yesterday&amp;#8217;s post&lt;/a&gt;). Since those 11 all hung (and not, say, a seg fault) I&amp;#8217;m hoping they might all have the same problem. That&amp;#8217;s only the first half of the mission. I also converted the 36673 Linux kernel SnowC files back into C. No trouble there. But Linux isn&amp;#8217;t just some test code. Does that generated C compile? No. Even &lt;code&gt;make tinyconfig&lt;/code&gt; dies pretty quickly into the process. But it&amp;#8217;s a superb platform for catching C code that is difficult to translate. So I&amp;#8217;ll probably work on whatever deficiencies that strategy reveals.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;One thing converting 36673 files did answer is &lt;strong&gt;how fast is SnowC&lt;/strong&gt;? Converting the C to SnowC took 2min 55sec or &lt;strong&gt;4.8ms&lt;/strong&gt; per file. With its simple algorithm, converting those SnowC files back to C was 25% quicker at 2min 12sec or &lt;strong&gt;3.6ms&lt;/strong&gt; per file. For reference, on my computer just a simple &lt;code&gt;ls&lt;/code&gt; or &lt;code&gt;cal&lt;/code&gt; take about 12ms. Just showing me the &lt;code&gt;date&lt;/code&gt; takes about twice as long as converting SnowC code. So I&amp;#8217;m pretty happy with the performance. If you come up with a much quicker way to pull the inherent redundancy out of well written C code, I&amp;#8217;d love to see it!&lt;/p&gt;&lt;/div&gt; &lt;svg width=&quot;4in&quot; height=&quot;4in&quot; viewBox=&quot;0 0 100 77.3&quot;&gt; &lt;path style=&quot;fill:#000000;fill-opacity:1;stroke:none&quot; d=&quot;m 25,-4 -2.87,4.94 2.14,3.71 -3.70,-1.00 -3.91,6.83 12.98,3.47 1.40,2.41 -11.02,-2.94 -7.50,4.18 0.10,8.59 8.06,8.06 h -2.81 l -9.50,-9.48 -3.91,6.77 2.71,2.70 h -4.28 l -2.87,5.00 2.87,4.94 h 4.28 l -2.71,2.76 3.91,6.77 9.50,-9.54 h 2.81 l -8.06,8.06 -0.10,8.65 7.50,4.18 11.02,-2.94 -1.40,2.41 -12.98,3.47 3.91,6.83 3.70,-1.00 -2.14,3.71 2.87,4.94 h 5.74 l 2.14,-3.71 0.99,3.71 h 7.83 l -3.47,-12.95 1.40,-2.47 2.95,11.07 7.40,4.35 7.39,-4.35 2.95,-11.07 1.40,2.47 -3.47,12.95 h 7.83 l 0.99,-3.71 2.14,3.71 h 5.74 l 2.87,-4.94 -2.14,-3.71 3.70,1.00 3.91,-6.83 -12.98,-3.47 -1.40,-2.41 11.02,2.94 7.50,-4.18 -0.10,-8.65 -8.06,-8.06 h 2.81 l 9.50,9.54 3.91,-6.77 -2.71,-2.76 h 4.28 l 2.87,-4.94 -2.87,-5.00 h -4.28 l 2.71,-2.70 -3.91,-6.77 -9.50,9.48 h -2.81 l 8.06,-8.06 0.10,-8.59 -7.50,-4.18 -11.02,2.94 1.40,-2.41 12.98,-3.47 -3.91,-6.83 -3.70,1.00 2.14,-3.71 -2.87,-4.94 h -5.74 l -2.14,3.71 -0.99,-3.71 h -7.83 l 3.47,12.95 -1.40,2.41 -2.95,-11.01 -7.40,-4.35 -7.39,4.35 -2.95,11.01 -1.40,-2.41 3.47,-12.95 h -7.83 l -0.99,3.71 -2.14,-3.71 z m 24.99,6.53 4.62,17.19 c 4.30,0.86 7.84,2.72 10.56,5.65 l 16.64,-4.47 -13.43,13.42 -10.99,0.10 c 0,-6.81 -14.15,-10.00 -14.15,4.84 0.01,15.13 14.15,11.66 14.15,4.94 h 10.96 l 13.46,13.42 -16.65,-4.47 c -2.78,2.92 -6.21,4.78 -10.29,5.59 l -4.89,17.25 -4.65,-17.37 c -4.04,-0.99 -7.48,-2.69 -9.79,-5.65 l -17.38,4.65 13.34,-13.30 c -0.64,-3.76 -0.47,-7.03 -0.01,-10.12 l -13.33,-13.30 17.37,4.65 c 3.03,-3.05 6.26,-5.11 9.80,-5.65 z&quot;/&gt; &lt;/svg&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;[If you&amp;#8217;ve made it this far, thank you for reading all this! Or any of it! I hope you learned something, even if only about my ability to get a little too obsessive about my projects!]&lt;/p&gt;&lt;/div&gt;</description></item>
<item><title>SnowC - Challenges - Some Final Pointers</title><link>http://xed.ch/blog/2026/0730.html</link><guid>http://xed.ch/blog/2026/0730.html</guid><pubDate>Thu, 30 Jul 2026 16:33 EDT</pubDate><description>&lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;There is actually a point to this series on challenges I faced when creating a decent implementation of SnowC. I think the SnowC idea is very sensible and it is worth pursuing but while I&amp;#8217;m proud of what I&amp;#8217;ve accomplished, I&amp;#8217;m not claiming to be our species&apos; best candidate to &lt;em&gt;implement&lt;/em&gt; the idea. And one day in the future&amp;#8201;&amp;#8212;&amp;#8201;but not today!&amp;#8201;&amp;#8212;&amp;#8201;maybe our robot friends can even improve on what I&amp;#8217;ve done. So these posts serve as a guide for what someone will need to think about if they want to create a SnowC conversion system of their own. This post will round out some of the other random challenges I ran into. Some were expected, some not so much.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;&lt;strong&gt;Literals&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Let&amp;#8217;s start with the first thing you need to start with. It may come as a slight surprise that the first thing to deal with is character and string literals. I take a &lt;a href=&quot;https://xed.ch/b/2025/0614.html&quot;&gt;special interest in specifying text in programming languages&lt;/a&gt; so it came as no surprise to me to discover that strings must be isolated and dealt with first. It&amp;#8217;s pretty easy to see why if we imagine a string &lt;em&gt;talking about&lt;/em&gt; the syntax of specifying strings.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;char *help = &quot;Use \&quot; to specify strings in C: \&quot;a string\&quot;&quot;;&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Obviously that&amp;#8217;s annoying but really SnowC doesn&amp;#8217;t care what your string says, it just needs your strings to not confuse things. That could happen in an example like this.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;char vista = &quot;char /*{ick}*/ *b = \&quot;b\&quot;; int c = 0; &quot;; int c = 0;&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;You can use the &lt;code&gt;c2snowc&lt;/code&gt; map diagnostic option (&lt;code&gt;-m&lt;/code&gt;) to show how the characters of this line get classified, or mapped into general categories.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;char vista = &quot;char /*{ick}*/ *b = \&quot;b\&quot;; int c = 0; &quot;; int c = 0; CCCC_CC_C_$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$;_CCC_C_C_C;+&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;With the characters of the string properly identified it&amp;#8217;s much easier to see that there are only two proper semicolons here to worry about. The same idea applies to this line involving character literals.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;char c = &apos;;&apos; == &apos;\&apos;&apos; + &apos;;&apos;; c++; CCCC_C_C_&apos;&apos;&apos;_CC_&apos;&apos;&apos;&apos;_C_&apos;&apos;&apos;;_CCC;+&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;It may seem odd, but since strings can say any crazy thing, they must be neutralized before anything else is touched. And although the fail cases are subtle, character literals should be immediately neutralized next.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;&lt;strong&gt;Comments&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;The next thing to isolate and neutralize must be modern double slash comments. This was originally a C++ thing and looks like this.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;char *comments = &quot;can get messy&quot;; // So true!&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;This (everything after &lt;code&gt;//&lt;/code&gt;) is the primary comment style that takes precedence over a secondary comment style. Old school K&amp;amp;R comments (&lt;code&gt;/* which look like this */&lt;/code&gt;) are very clever. Too clever. Probably the biggest source of real problems is C&amp;#8217;s classic comment style. It&amp;#8217;s bad enough looking for the last &lt;em&gt;meaningful&lt;/em&gt; character of a line knowing that any random junk can follow in the form of a comment. For example, the last meaningful character here is the &lt;em&gt;first&lt;/em&gt; semicolon.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;x /= 3; //= 3; ///= 3; /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore This is totally valid C code because FML. */&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;But the problems get infinitely worse when you consider that classic C commenting can be mischievously injected &lt;em&gt;anywhere&lt;/em&gt;. Looking for a thing that needs to follow a thing? Well, you better count on it following a near random collection of obfuscating garbage in the form of a comment.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;So identify all the characters that are double slash comments and then identify all the other comment characters. Then don&amp;#8217;t forget about them, because they could possibly be causing mischief damn near &lt;em&gt;anywhere&lt;/em&gt;!&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Just &lt;a href=&quot;https://xed.ch/b/2026/0523.html&quot;&gt;remember that you can comment out preprocessor syntax&lt;/a&gt;, so deal with comments first. Which brings us to preprocessor problems.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;&lt;strong&gt;Preprocessor&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Comments are hard because they can lurk anywhere muddling important semantic details. But preprocessor directives have a different problem. They are often used to swap in chunks of real C code at the last minute. I actually did not do a super deep dive into insane preprocessor abuse. I am pretty tame with my usage personally and, well, it was a lower priority for me. Sorry.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Basically if the first significant character of a line is a &lt;code&gt;#&lt;/code&gt; I&amp;#8217;m calling it a preprocessor directive and it gets ignored much like a comment. If there is some kind of &lt;code&gt;#if&lt;/code&gt;, &lt;code&gt;#ifdef&lt;/code&gt;, &lt;code&gt;#elif&lt;/code&gt;, &lt;code&gt;#else&lt;/code&gt; or similar construction with some ugly confusing code hiding inside, well, you&amp;#8217;ll probably need to take special care.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;One conceptual problem is that the SnowC to C conversion injects the missing C syntax (braces, semicolons) back into the code. If a preprocessor range isn&amp;#8217;t anticipating this, there can be problems. Here&amp;#8217;s a simplified example equivalent to something I found in the Linux kernel starting with the original C.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;void fn() { for (q1) { if (q2) { a(); #if (FANCY_FEATURE == ENABLED) fancy_feature(); #endif } } }&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;The SnowC is fine.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;void fn() for (q1) if (q2) a() #if (FANCY_FEATURE == ENABLED) fancy_feature() #endif&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;But look what happens when it goes back to C.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;void fn() { for (q1) { if (q2) { a(); #if (FANCY_FEATURE == ENABLED) fancy_feature(); } } } #endif&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Leaving out all the closing braces when the fancy feature is not enabled will be a serious problem. Comments also can be moved to undesired locations in this way but that&amp;#8217;s less problematic.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;I could have all closing brace generation wait until after any preprocessor lines (and comments) but this is sometimes not the right choice either. I suspect it is the more common choice and I may switch the default to it. I&amp;#8217;m starting to think that the correct solution is for SnowC to insist you place your preprocessor statement&amp;#8217;s level intentions the same way as everything else&amp;#8201;&amp;#8212;&amp;#8201;with indent level. Consider it on the to do list.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;&lt;strong&gt;Labels&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;C has a quirky syntax for &quot;labels&quot; which are targets for the &lt;code&gt;goto&lt;/code&gt; statement. Using &lt;code&gt;goto&lt;/code&gt; in the first place is not really encouraged so labels aren&amp;#8217;t even all that common. But they&amp;#8217;re common enough that they need to be dealt with.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;In C, a label is &lt;em&gt;generally&lt;/em&gt; a word followed by a colon. Like this.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;found:&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Easy, right? Well, not so fast! Have a look at some of my test code where I try to anticipate label problems.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;label1: label_with_leading_spcs: label_with_trailing_spcs: label_with_intercolon_spcs : /*precomment*/label_with_precomment: label_with_post_comment:// Post comment. label_with_post_comment_trailings:/* Post comment w trailing. content/ current/ i/ ideas/ Xedtheme/ label_with_intercomment/* Inter comment.*/: _yes_a_label: _yes_2_label: 3_not_a_label: // Can&apos;t start with a number. not%a_label: not^a_label: default_plus_is_label: default_ : // Is a label. default: // Technically not a label despite appearances.&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;C has a way of making you think it&amp;#8217;s simple and obvious. And then you dig into it and find out, yikes, a lot can be taken for granted by the programmer.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;So why exactly do we need to know what text is a label? Because SnowC needs to mostly ignore them sometimes. For example when they are between a singleton control word and the resolving statement.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Unfortunately C has &lt;em&gt;another&lt;/em&gt; use for colons. &lt;a href=&quot;https://en.wikipedia.org/wiki/Bit_field#C&quot;&gt;A couple in fact&lt;/a&gt;. Look at this syntax&amp;#8201;&amp;#8212;&amp;#8201;which I checked does actually compile.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt; int not_a_label = 0; | int not_a_label = 0 x2 = x? | x2 = x?\ not_a_label: | not_a_label: 99; | 99&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Fortunately, despite failing to realize that &lt;code&gt;not_a_label:&lt;/code&gt; is not a label, the SnowC conversion just happens to work because of, well, luck. I am walking away from this one.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;&lt;strong&gt;Nugatory&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;If you don&amp;#8217;t know what the word &quot;nugatory&quot; means, I suggest you look it up (&lt;a href=&quot;https://www.merriam-webster.com/dictionary/nugatory&quot;&gt;click here!&lt;/a&gt;) and start using it. It&amp;#8217;s a great word!&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;You can probably figure it out from my test file called &lt;code&gt;nugatory.c&lt;/code&gt; which gave me a surprising amount of grief. Here it is with the generated SnowC on the right.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt; //TEST: Blank statements and blocks. | //TEST: Blank statements and blocks. if (q0) {} // Empty block. | if (q0) {}\ // Empty block. if (q1) ; // Blank statement. | if (q1) // Blank statement. else if (q2) ; | else if (q2) else if (q3) { | else if (q3) {\ }else if (q4) { // Some spaces. | }else if (q4) {\ // Some spaces. } else if (q5) // Ick. | }else if (q5)\ // Ick. { } | {}\ else if (q6) { | else if (q6) {\ /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore Pass. content/ current/ i/ ideas/ Xedtheme/ | /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore Pass. content/ current/ i/ ideas/ Xedtheme/ } else {// Uncomment for diagnostic. | }else {\// Uncomment for diagnostic. //printf(&quot;Error:\n&quot;); /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore ERROR content/ current/ i/ ideas/ Xedtheme/ | //printf(&quot;Error:\n&quot;); /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore ERROR content/ current/ i/ ideas/ Xedtheme/ } | }\ x++; | x++ |&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Dang that&amp;#8217;s gnarly! But if you follow the simple SnowC rules to convert that back to C, it will produce semantically equivalent C code.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Note the final line is blank. I also have several tests for blank lines at the beginning and end and other inconvenient places. It&amp;#8217;s little things like that which can be a real pain in the ass.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;I&amp;#8217;m sure there are other challenges I could highlight but that concludes my tour of the main problems that one must deal with when trying to pull the redundancy out of C code. It may not be easy, but for practical use, I have demonstrated that it is possible.&lt;/p&gt;&lt;/div&gt; &lt;svg width=&quot;4in&quot; height=&quot;4in&quot; viewBox=&quot;0 0 100 77.3&quot;&gt; &lt;path style=&quot;fill:#000000;fill-opacity:1;stroke:none&quot; d=&quot;m 25,-4 -2.87,4.94 2.14,3.71 -3.70,-1.00 -3.91,6.83 12.98,3.47 1.40,2.41 -11.02,-2.94 -7.50,4.18 0.10,8.59 8.06,8.06 h -2.81 l -9.50,-9.48 -3.91,6.77 2.71,2.70 h -4.28 l -2.87,5.00 2.87,4.94 h 4.28 l -2.71,2.76 3.91,6.77 9.50,-9.54 h 2.81 l -8.06,8.06 -0.10,8.65 7.50,4.18 11.02,-2.94 -1.40,2.41 -12.98,3.47 3.91,6.83 3.70,-1.00 -2.14,3.71 2.87,4.94 h 5.74 l 2.14,-3.71 0.99,3.71 h 7.83 l -3.47,-12.95 1.40,-2.47 2.95,11.07 7.40,4.35 7.39,-4.35 2.95,-11.07 1.40,2.47 -3.47,12.95 h 7.83 l 0.99,-3.71 2.14,3.71 h 5.74 l 2.87,-4.94 -2.14,-3.71 3.70,1.00 3.91,-6.83 -12.98,-3.47 -1.40,-2.41 11.02,2.94 7.50,-4.18 -0.10,-8.65 -8.06,-8.06 h 2.81 l 9.50,9.54 3.91,-6.77 -2.71,-2.76 h 4.28 l 2.87,-4.94 -2.87,-5.00 h -4.28 l 2.71,-2.70 -3.91,-6.77 -9.50,9.48 h -2.81 l 8.06,-8.06 0.10,-8.59 -7.50,-4.18 -11.02,2.94 1.40,-2.41 12.98,-3.47 -3.91,-6.83 -3.70,1.00 2.14,-3.71 -2.87,-4.94 h -5.74 l -2.14,3.71 -0.99,-3.71 h -7.83 l 3.47,12.95 -1.40,2.41 -2.95,-11.01 -7.40,-4.35 -7.39,4.35 -2.95,11.01 -1.40,-2.41 3.47,-12.95 h -7.83 l -0.99,3.71 -2.14,-3.71 z m 24.99,6.53 4.62,17.19 c 4.30,0.86 7.84,2.72 10.56,5.65 l 16.64,-4.47 -13.43,13.42 -10.99,0.10 c 0,-6.81 -14.15,-10.00 -14.15,4.84 0.01,15.13 14.15,11.66 14.15,4.94 h 10.96 l 13.46,13.42 -16.65,-4.47 c -2.78,2.92 -6.21,4.78 -10.29,5.59 l -4.89,17.25 -4.65,-17.37 c -4.04,-0.99 -7.48,-2.69 -9.79,-5.65 l -17.38,4.65 13.34,-13.30 c -0.64,-3.76 -0.47,-7.03 -0.01,-10.12 l -13.33,-13.30 17.37,4.65 c 3.03,-3.05 6.26,-5.11 9.80,-5.65 z&quot;/&gt; &lt;/svg&gt;</description></item>
<item><title>SnowC - Challenges - Else</title><link>http://xed.ch/blog/2026/0729.html</link><guid>http://xed.ch/blog/2026/0729.html</guid><pubDate>Wed, 29 Jul 2026 05:59 EDT</pubDate><description>&lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;In &lt;a href=&quot;https://xed.ch/b/2026/0728.html&quot;&gt;the last post&lt;/a&gt; we looked at how challenging it was to just track level depth. To understand the problem better let&amp;#8217;s review how singletons can cause mischief. The classic singleton is something like this conceptual code.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;SINGLETON_KEYWORD (optional_conditions) resolving_statement;&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;The SINGLETON_KEYWORD can be &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;for&lt;/code&gt;, &lt;code&gt;while&lt;/code&gt;, &lt;code&gt;switch&lt;/code&gt;, &lt;code&gt;do&lt;/code&gt;, or &lt;code&gt;else&lt;/code&gt;. When the resolving statement completes, the singleton is complete. But what if the resolving_statement is also another SINGLETON_KEYWORD?&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt; SINGLETON_KEYWORD SINGLETON_KEYWORD SINGLETON_KEYWORD SINGLETON_KEYWORD resolving_statement;&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;The resolving statement closes out the nearest singleton which then closes out the next and the whole chain unwinds. Now that I&amp;#8217;ve explained it to you, you&amp;#8217;re ready to believe that&amp;#8217;s how it truly works, right? If you don&amp;#8217;t know anything about C you are certainly ready to believe this. Even if you are a professional C programmer, you might still be fooled because no sane person ever writes code like this!&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;The resolving statement unraveling all the nested singletons is a nice neat concept, but unfortunately, it is not C&amp;#8217;s nice neat concept. Let&amp;#8217;s see how C &lt;em&gt;really&lt;/em&gt; behaves. Check out this singleton puzzle paying attention to the indent levels.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt; if (L1) for (L2;;) if (L3) resolve(3,2,1); if (L1) // Previous resolve fully resets level. resolve(1);&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;That does look like the neat algorithm I described. The level of the first &lt;code&gt;if&lt;/code&gt; has been fully reset because the entire chain of singletons has been resolved. Seems reasonable, right? C then puts you in the uncomfortable position of thinking the following code is also reasonable even though there is a kind of inconsistency to it.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt; if (L1) for (L2;0;) if (L3) resolve(only3); else // Previous resolves only one level! resolve(3,2,1); // Finally resolves.&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;The &lt;code&gt;else&lt;/code&gt; resolves with a single statement clearly making it a singleton keyword. But there is more to it. It turns out that &lt;code&gt;else&lt;/code&gt; is a &lt;em&gt;special&lt;/em&gt; singleton keyword that has its own ideas about indent level.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;This inconsistent feature of C&amp;#8217;s control keywords introduces the need to track if the last singleton keyword was specifically an &lt;code&gt;if&lt;/code&gt; and then on closing out that &lt;code&gt;if&lt;/code&gt;, the levels get adjusted differently if a look ahead can find an &lt;code&gt;else&lt;/code&gt; nearby. Good times.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;If you think all that sounds challenging, I&amp;#8217;ve got some bad news for you because it turns out to be a lot worse than that. I realized that any &lt;code&gt;else&lt;/code&gt; must align with any &lt;em&gt;inner&lt;/em&gt; &lt;code&gt;if&lt;/code&gt; regardless of either executing a braced clause or not. This actually requires a &lt;a href=&quot;https://en.wikipedia.org/wiki/Stack_(abstract_data_type)&quot;&gt;FILO stack&lt;/a&gt; to keep track of any pending &lt;code&gt;if&lt;/code&gt; that might possibly go with an &lt;code&gt;else&lt;/code&gt; at &lt;em&gt;every&lt;/em&gt; level of brace depth. And these must be politely dropped once there is no possibility for an &lt;code&gt;else&lt;/code&gt; at that brace level. Don&amp;#8217;t worry if that takes you several days to get your head around; been there.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;At this point I was seriously starting to question my judgment at taking on this project. But I pressed on and figured out a way. The need for this quirky syntax tracking should almost never arise in any sane modern production code. For that reason each tracked level on the stack gets a leisurely heap allocation and I&amp;#8217;m not even going to apologize.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;When I finally implemented that, my test setup caught &lt;em&gt;another&lt;/em&gt; error in the &lt;a href=&quot;https://colorcomputerarchive.com/repo/Documents/Books/The%20C%20Programming%20Language%20(Kernighan%20Ritchie).pdf&quot;&gt;K&amp;amp;R sample from page 132&lt;/a&gt;! There already was a missing semicolon in that code but also I realized line 20 is incorrectly indented! Fortunately SnowC caught it nicely. And of course it must be said that in C, this is not technically an &lt;strong&gt;error&lt;/strong&gt;&amp;#8201;&amp;#8212;&amp;#8201;it does compile fine. But it sure is a faux pas!&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;(KR132-cat.c)&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt; #include &amp;lt;stdio.h&amp;gt; | #include &amp;lt;stdio.h&amp;gt; /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore cat: concatenate files, version 1 content/ current/ i/ ideas/ Xedtheme/ | /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore cat: concatenate files, version 1 content/ current/ i/ ideas/ Xedtheme/ main(int argc, char *argv[]) | main(int argc, char *argv[]) { | FILE *fp FILE *fp; | void filecopy(FILE *, FILE *)\ //[Original missing ;] void filecopy(FILE *, FILE *) //[Original missing ;] | if (argc == 1) /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore no args; copy standard input content/ current/ i/ ideas/ Xedtheme/ if (argc == 1) /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore no args; copy standard input content/ current/ i/ ideas/ Xedtheme/ | filecopy(stdin, stdout) filecopy(stdin, stdout); | else else | while(--argc &amp;gt; 0) while(--argc &amp;gt; 0) | if ((fp = fopen(*++argv, &quot;r&quot;)) == NULL) if ((fp = fopen(*++argv, &quot;r&quot;)) == NULL) { | printf(&quot;cat: can&apos;t open %s\n&quot;, *argv) printf(&quot;cat: can&apos;t open %s\n&quot;, *argv); | return 1 return 1; | else } else { | filecopy(fp, stdout) filecopy(fp, stdout); | fclose(fp) fclose(fp); | return 0 //[Original indent wrong!] } | /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore filecopy: copy file ifp to file ofp content/ current/ i/ ideas/ Xedtheme/ return 0; //[Original indent wrong!] | void filecopy(FILE *ifp, FILE *ofp) } | int c /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore filecopy: copy file ifp to file ofp content/ current/ i/ ideas/ Xedtheme/ | while ((c = getc(ifp)) != EOF) void filecopy(FILE *ifp, FILE *ofp) | putc(c, ofp) { | int c; | while ((c = getc(ifp)) != EOF) | putc(c, ofp); | } |&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;I &lt;a href=&quot;https://xed.ch/b/2026/0724.html&quot;&gt;already noted their page 51 example&lt;/a&gt; which highlights this exact problem deliberately. The SnowC on the right is automatically corrected.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt; if (n &amp;gt; 0) | if (n &amp;gt; 0) for (i = 0; i &amp;lt; n; i++) | for (i = 0; i &amp;lt; n; i++) if (s[i] &amp;gt; 0) { | if (s[i] &amp;gt; 0) printf(&quot;...&quot;); | printf(&quot;...&quot;) return i; | return i } | else /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore WRONG content/ current/ i/ ideas/ Xedtheme/ else /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore WRONG content/ current/ i/ ideas/ Xedtheme/ | printf(&quot;error -- n is negative\n&quot;) printf(&quot;error -- n is negative\n&quot;); |&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Hopefully you now have a better understanding of just how wrong I was when I originally thought of the SnowC concept and I thought it surely couldn&amp;#8217;t be all that hard. It was a hell of a challenge but I&amp;#8217;m glad I stuck with it and I am very happy with the results.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;I&amp;#8217;ll leave you with a couple of extra awful conversion tests that I used during development, starting with an example of singletons, braces, and a mix. Remember, SnowC is ignoring all the (possibly spurious) indentation of the original and recreating it with structural rigor.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;(nested_if_else.c)&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt; int main(int argc, char** argv) { | int main(int argc, char** argv) // All singletons. | // All singletons. if (q1) | if (q1) if (q2) | if (q2) if (q3) | if (q3) if (q4) | if (q4) a4(); | a4() else | else e4(); | e4() else | else e3(); | e3() else | else e2(); | e2() else | else e1(); | e1() // All brace blocks. | // All brace blocks. if (q1) { | if (q1) if (q2) { | if (q2) if (q3) { | if (q3) if (q4) { | if (q4) a4(); | a4() } | else else { | e4() e4(); | else } | e3() } | else else { | e2() e3(); | else } | e1() } | // Mixed. else { | if (q1) e2(); | if (q2) } | if (q3) } | if (q4) else { | a4() e1(); | else } | e4() // Mixed. | else if (q1) { | e3() if (q2) | else e2() if (q3) { | else if (q4) { | e1() a4(); | } | else | e4(); | } | else { | e3(); | } | else e2(); | } | else { | e1(); | } | } |&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Here is another challenging test for the if/else matching. The B is brace level and the S is singleton level.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;(badelse.c)&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt; void main() { | void main() // All singletons. | // All singletons. for (q1) | for (q1) for (q2) | for (q2) if (q3) | if (q3) while (q4) | while (q4) for (q5) | for (q5) a(); | a() else | else if | if while | while for | for b(); | b() else | else c(); | c() ok1(); | ok1() // With some braces. | // With some braces. for (q1) | for (q1) for (q2) | for (q2) if (q3) { | if (q3) while (q4) | while (q4) for (q5) | for (q5) a(); | a() } | else else { | if if | while while | for for | b() b(); | else else | c() c(); | ok2() } | // With challenging else requirements. ok2(); | if (d) // B1 S0 // With challenging else requirements. | while (w) // B1 S1 if (d) // B1 S0 | if (c) // B1 S2 --------+ while (w) // B1 S1 | if (b) // B2 S0 -----+ | if (c) { // B1 S2 --------+ | for (f) // B2 S1 | | if (b) // B2 S0 -----+ | | if (a) // B2 S2 ---+ | | for (f) // B2 S1 | | | a++ // B2 S3 | | | if (a) // B2 S2 ---+ | | | else // B2 S2 ---+ | | a++; // B2 S3 | | | | a() // B2 S3 | | else // B2 S2 ---+ | | | else // B2 S0 -----+ | a(); // B2 S3 | | | b() // B2 S1 | else // B2 S0 -----+ | | // B2 S1 | b(); // B2 S1 | | else // B1 S2 --------+ } // B2 S1 | | c() // B1 S3 else // B1 S2 --------+ | else // B1 S0 c(); // B1 S3 | d() // B1 s1 else // B1 S0 | return // B1 S0 d(); // B1 s1 | return; // B1 S0 | } |&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;svg width=&quot;4in&quot; height=&quot;4in&quot; viewBox=&quot;0 0 100 77.3&quot;&gt; &lt;path style=&quot;fill:#000000;fill-opacity:1;stroke:none&quot; d=&quot;m 25,-4 -2.87,4.94 2.14,3.71 -3.70,-1.00 -3.91,6.83 12.98,3.47 1.40,2.41 -11.02,-2.94 -7.50,4.18 0.10,8.59 8.06,8.06 h -2.81 l -9.50,-9.48 -3.91,6.77 2.71,2.70 h -4.28 l -2.87,5.00 2.87,4.94 h 4.28 l -2.71,2.76 3.91,6.77 9.50,-9.54 h 2.81 l -8.06,8.06 -0.10,8.65 7.50,4.18 11.02,-2.94 -1.40,2.41 -12.98,3.47 3.91,6.83 3.70,-1.00 -2.14,3.71 2.87,4.94 h 5.74 l 2.14,-3.71 0.99,3.71 h 7.83 l -3.47,-12.95 1.40,-2.47 2.95,11.07 7.40,4.35 7.39,-4.35 2.95,-11.07 1.40,2.47 -3.47,12.95 h 7.83 l 0.99,-3.71 2.14,3.71 h 5.74 l 2.87,-4.94 -2.14,-3.71 3.70,1.00 3.91,-6.83 -12.98,-3.47 -1.40,-2.41 11.02,2.94 7.50,-4.18 -0.10,-8.65 -8.06,-8.06 h 2.81 l 9.50,9.54 3.91,-6.77 -2.71,-2.76 h 4.28 l 2.87,-4.94 -2.87,-5.00 h -4.28 l 2.71,-2.70 -3.91,-6.77 -9.50,9.48 h -2.81 l 8.06,-8.06 0.10,-8.59 -7.50,-4.18 -11.02,2.94 1.40,-2.41 12.98,-3.47 -3.91,-6.83 -3.70,1.00 2.14,-3.71 -2.87,-4.94 h -5.74 l -2.14,3.71 -0.99,-3.71 h -7.83 l 3.47,12.95 -1.40,2.41 -2.95,-11.01 -7.40,-4.35 -7.39,4.35 -2.95,11.01 -1.40,-2.41 3.47,-12.95 h -7.83 l -0.99,3.71 -2.14,-3.71 z m 24.99,6.53 4.62,17.19 c 4.30,0.86 7.84,2.72 10.56,5.65 l 16.64,-4.47 -13.43,13.42 -10.99,0.10 c 0,-6.81 -14.15,-10.00 -14.15,4.84 0.01,15.13 14.15,11.66 14.15,4.94 h 10.96 l 13.46,13.42 -16.65,-4.47 c -2.78,2.92 -6.21,4.78 -10.29,5.59 l -4.89,17.25 -4.65,-17.37 c -4.04,-0.99 -7.48,-2.69 -9.79,-5.65 l -17.38,4.65 13.34,-13.30 c -0.64,-3.76 -0.47,-7.03 -0.01,-10.12 l -13.33,-13.30 17.37,4.65 c 3.03,-3.05 6.26,-5.11 9.80,-5.65 z&quot;/&gt; &lt;/svg&gt;</description></item>
<item><title>SnowC - Challenges - Levels</title><link>http://xed.ch/blog/2026/0728.html</link><guid>http://xed.ch/blog/2026/0728.html</guid><pubDate>Tue, 28 Jul 2026 19:53 EDT</pubDate><description>&lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Back in 2015 when I conceived of the idea for SnowC, I first thought it would be so easy to convert from SnowC to C that I could do it by storing the state of the indentation level in the call stack using recursion. That turned out to be too ambitious but it was close.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;However what was really delusional and naive was thinking that converting &lt;em&gt;from C&lt;/em&gt; might be possible using recursion too. While I got over that quickly, even starting on it fresh this year, I thought, surely this conversion can not be that difficult. Wow, was I profoundly wrong! Over two months later I now know an unhealthy amount about pathological C code and I have been humbled. When it comes to the entire possibility space of C source code, nothing is simple.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Which brings us to indent levels. Easy, right? Well let&amp;#8217;s just say that it&amp;#8217;s easy when it&amp;#8217;s easy. Like this example (C left, SnwoC right).&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt; // Level 0 | // Level 0 int level1(int x) { // Level 0 -&amp;gt; 1 | int level1(int x) // Level 0 -&amp;gt; 1 if (x) { // Level 1 -&amp;gt; 2 | if (x) // Level 1 -&amp;gt; 2 if (x % 2) { // Level 2 -&amp;gt; 3 | if (x % 2) // Level 2 -&amp;gt; 3 x++; | x++ for (int y=x; y&amp;lt;99; y++) { // Level 3 -&amp;gt; 4 | for (int y=x; y&amp;lt;99; y++) // Level 3 -&amp;gt; 4 if (x + y == 99) { // Level 4 -&amp;gt; 5 | if (x + y == 99) // Level 4 -&amp;gt; 5 return y; | return y } // Level 5 -&amp;gt; 4 | // Level 5 -&amp;gt; 4 } // Level 4 -&amp;gt; 3 | // Level 4 -&amp;gt; 3 } // Level 3 -&amp;gt; 2 | // Level 3 -&amp;gt; 2 } //Level 2 -&amp;gt; 1 | //Level 2 -&amp;gt; 1 } //Level 1 -&amp;gt; 0 | //Level 1 -&amp;gt; 0&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;And without the comments, the SnowC is pretty legible.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;int level1(int x) if (x) if (x % 2) x++ for (int y=x; y&amp;lt;99; y++) if (x + y == 99) return y&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Those comments are there to show how the core concept works. You basically need a simple integer variable that stores the indent level. When you find an opening brace (&lt;code&gt;{&lt;/code&gt;), increment the indent level, and when you find a closing brace (&lt;code&gt;}&lt;/code&gt;), decrement the indent level. Easy, right? Right? Ohhhhh no.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;&lt;span class=&quot;image&quot;&gt; &lt;img src=&quot;http://xed.ch/blog/2026/i/0728-7416-hiding_creeper.jpg&quot; alt=&quot;hiding_creeper.jpg&quot; /&gt; &lt;/span&gt;&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;It gets pretty bad. Let&amp;#8217;s start with relatively simple singleton hassles. In &lt;a href=&quot;https://xed.ch/b/2026/0726.html&quot;&gt;a recent post&lt;/a&gt; we just covered how the C creators loved to do level nesting with &lt;em&gt;no braces&lt;/em&gt; at all. I call those cases &lt;strong&gt;singletons&lt;/strong&gt;. Ok, fine, so that is a thing. Sure, whatever. Surely there&amp;#8217;s a solution, I thought.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Usually SnowC can happily not really care one bit what your C code is actually &lt;em&gt;doing&lt;/em&gt;. But because of this issue, I realized there needed to be a complex parsing effort so all the singleton (braceless) control syntax could correctly contribute to the indent levels being adjusted properly. Not easy but doable. So I dug in and got to it.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Single line singletons are easily handled by just ignoring them. They are resolved before they can even make an impact on the code&amp;#8217;s indentation structure.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;&lt;strong&gt;Multi-line singletons&lt;/strong&gt; are a different story, so let&amp;#8217;s start with them.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;An interesting property of SnowC is that a round trip will completely remove multi-line singletons! For example, consider this authentic K&amp;amp;R example (&lt;a href=&quot;https://colorcomputerarchive.com/repo/Documents/Books/The%20C%20Programming%20Language%20(Kernighan%20Ritchie).pdf&quot;&gt;from page 92&lt;/a&gt;).&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;for (i = 1; yearday &amp;gt; daytab[leap][i]; i++) yearday -= daytab[leap][i];&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;In SnowC it becomes.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;for (i = 1; yearday &amp;gt; daytab[leap][i]; i++) yearday -= daytab[leap][i]&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://xed.ch/b/2026/0726.html&quot;&gt;Note how close this actually is to what K&amp;amp;R imagined!&lt;/a&gt; But there is no way to hint that you do not want the more ordinary and predictable C you will get on the return conversion. Here is what converting back to C produces.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;for (i = 1; yearday &amp;gt; daytab[leap][i]; i++) { yearday -= daytab[leap][i]; }&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Note that this C produces the exact same SnowC shown above.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;What if the entire singleton resolves on one line? And importantly what of subsequent additional elements like &lt;code&gt;b()&lt;/code&gt; in this line of C code?&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;if (q) a(); b();&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;In theory this could be done by leaving it mostly as is with the non-trailing semicolons intact.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;if (q) a(); b()&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;That is actually valid SnowC and will convert to the previous C one liner. However, when that C one liner converts to SnowC, it will get broken up.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;if (q) a() b()&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;The reason for this choice can be seen when there are line breaks which can cause trouble. Consider this SnowC.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;if (q) a(); b()&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;If you are a real C programmer, hopefully that did not cause some life threatening allergic reaction. Which of these two possibilities is correct when it converts to C?&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;if (q) { a(); } b();&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Or&amp;#8230;&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;if (q) { a(); b(); }&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Both are reasonable interpretations.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;While the first looks good at a glance, the second follows the simple SnowC rules. And for a few good reasons. What we can be sure about is that the following C code won&amp;#8217;t ever be possible to generate by converting &lt;em&gt;from&lt;/em&gt; SnowC.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;if (q) a(); b();&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Currently converting from C to SnowC, the rule is that singletons get split up if they resolve on the same line as another statement that would not share its level.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;To see why this must be consider the following round trip sequences of conversions. Following the conversions vertically, on the left is &lt;strong&gt;C &amp;#8594; SnowC &amp;#8594; C&lt;/strong&gt; and on the right is &lt;strong&gt;SnowC &amp;#8594; C &amp;#8594; SnowC&lt;/strong&gt;.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;C: SnowC: if (q) if (q) a; b; a; b | | V V SnowC: C: if (q) if (q){ a a; b; b } | | V V C: SnowC: if(q) { if (q) a; a; b } b;&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Note how the SnowC can mostly preserve its form into C and back. But the C structured like this is confusing to begin with, and must get sorted out in SnowC for the braces to actually work when converted back. Because the C style in the upper left is confusing and discouraged (heck, K&amp;amp;R don&amp;#8217;t even do it!) it is better to just cut up singletons packed on a line which will prepare them for how SnowC needs them to be. If you want SnowC as found on the right, use braces in the C like a normal person i.e. like the C code in the middle on the right.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;If you want to see some serious SnowC puzzles check out my test file called &lt;code&gt;spaceless.c&lt;/code&gt; and its corresponding SnowC.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;$ echo &quot;=== $F&quot;;pr -w$(tput cols) -o1 -s&apos;| &apos; -tm $D/$F &amp;lt;(./c2snowc -i4 $D/$F) === spaceless.c void f(int q,int a){ | void f(int q,int a) if(q)a++; | if(q)a++ if(q)a++;b++; | if(q)a++ if(q)a++;else--a; | b++ if(q)if(q)a++;else--a; | if(q)a++ if(q)a++;else/**/if(q)a++;else--a; | else--a } | if(q)if(q)a++ | else--a if(q) | if(q)a++ a();b(); | else/**/if(q)a++ | else--a if(q)a+ | a;b(); | if(q) | a() if(q){ | b() a();b();} | | if(q)a+\ if(q){ | a a();b(); | b() c();} | | if(q) | a();b() | | if(q) | a();b() | c()&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Like many of my test files, it is a reminder that polite indenting is a choice in C; in SnowC it is a promise. It&amp;#8217;s also an unusual example of a conversion to SnowC that results in &lt;em&gt;more&lt;/em&gt; lines.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;But those single singletons are relatively easy. Let&amp;#8217;s take a look at &lt;strong&gt;more serious level hassles&lt;/strong&gt;.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Check out this normal example from K&amp;amp;R p56, where the braces of the &lt;code&gt;do&lt;/code&gt; control keyword control the indent. Also note that the indent is properly controlled for the two &lt;code&gt;if&lt;/code&gt; statements too even though there are no braces. (C left, SnowC right)&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt; /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore itoa: convert n to characters in s content/ current/ i/ ideas/ Xedtheme/ | /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore itoa: convert n to characters in s content/ current/ i/ ideas/ Xedtheme/ void itoa(int n, char s[]) | void itoa(int n, char s[]) { | int i, sign int i, sign; | if ((sign = n) &amp;lt; 0) /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore record sign content/ current/ i/ ideas/ Xedtheme/ if ((sign = n) &amp;lt; 0) /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore record sign content/ current/ i/ ideas/ Xedtheme/ | n = -n /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore make n positive content/ current/ i/ ideas/ Xedtheme/ n = -n; /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore make n positive content/ current/ i/ ideas/ Xedtheme/ | i = 0 i = 0; | do /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore generate digits in reverse order content/ current/ i/ ideas/ Xedtheme/ do { /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore generate digits in reverse order content/ current/ i/ ideas/ Xedtheme/ | s[i++] = n % 10 + &apos;0&apos; /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore get next digit content/ current/ i/ ideas/ Xedtheme/ s[i++] = n % 10 + &apos;0&apos;; /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore get next digit content/ current/ i/ ideas/ Xedtheme/ | while ((n /= 10) &amp;gt; 0) /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore delete it content/ current/ i/ ideas/ Xedtheme/ } while ((n /= 10) &amp;gt; 0); /bin /boot /dev /etc /home /initrd.img /initrd.img.old /intSSD /lib /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old /xcore delete it content/ current/ i/ ideas/ Xedtheme/ | if (sign &amp;lt; 0) if (sign &amp;lt; 0) | s[i++] = &apos;-&apos; s[i++] = &apos;-&apos;; | s[i] = &apos;\0&apos; s[i] = &apos;\0&apos;; | reverse(s) reverse(s); | } |&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;To pull this off requires parsing the code deeply enough to recognize all 6 of the possible control words that can trigger a singleton (&lt;code&gt;if&lt;/code&gt;, &lt;code&gt;while&lt;/code&gt;, &lt;code&gt;for&lt;/code&gt;, &lt;code&gt;switch&lt;/code&gt;, &lt;code&gt;do&lt;/code&gt;, &lt;code&gt;else&lt;/code&gt;) and recognize when they are not going to be using braces. Just identifying those is a big job. When I finally got that working I was pretty relieved. Mostly that it was even possible.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;But it gets worse. The C language allows an unhealthy &lt;em&gt;mix&lt;/em&gt; of brace levels and singleton levels. While experimenting with demented code that nobody would ever use (I hope!) I discovered something very unnerving. I slowly started to realize that simply tracking the brace depth and the singleton level depth was not going to do the job. (Theoretically; in practice it &lt;em&gt;mostly&lt;/em&gt; would.)&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Check out this sample program where I&amp;#8217;ve labeled the conditions to reflect the brace depth (e.g. B1) and also the singleton depth (e.g. S1). (&lt;code&gt;mixed_lvls.c&lt;/code&gt;, C left, SnowC right)&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt; void main (int argc, char **argv) { | void main (int argc, char **argv) if (B1) { | if (B1) while (B1S1) | while (B1S1) for (B1S2) | for (B1S2) if (B2) { | if (B2) if (B2S1) | if (B2S1) if (B2S2) | if (B2S2) B2S2Resolve(); | B2S2Resolve() if (B2S1) | if (B2S1) if (B2S2) | if (B2S2) B2S2Resolve(); | B2S2Resolve() else //B2S2 | else //B2S2 B2S2ElseResolve(); | B2S2ElseResolve() } | else if (B1) } else if (B1) { | while (B1S1) while (B1S1) | for (B1S2) for (B1S2) | if (B1S3) if (B1S3) | B1S3Resolve() B1S3Resolve(); | else // B1 } else // B1 | while (B1S1) while (B1S1) | for (B1S2) for (B1S2) | if (B1S3) if (B1S3) | B1S3Resolve() B1S3Resolve(); | // End main() } // End main() |&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;What&amp;#8217;s important to note here is that although chains of braceless singletons like &lt;code&gt;if (a) if (b) if (c) do_this();&lt;/code&gt; do increase indent depth, when the &lt;code&gt;do_this()&lt;/code&gt; finally runs you can&amp;#8217;t just set the singleton depth to zero and call it a day. That whole thing may be nested in one or more sets of braces that each have their own chain of singletons to unwind!&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;When I realized this, I had to scrap a lot of work and rethink the whole deal. And for what? A crazy obscure problem that &lt;em&gt;in practice&lt;/em&gt; would hardly ever come up. But damn it, I was committed to doing it as well as I could.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;The solution I came up with is that I create a heap array where I can store the singleton depth &lt;em&gt;for each brace level&lt;/em&gt;. This is the only way an accurate unraveling of the most complex structures can be done. The initial allocation allows for ten brace levels deep. Is that the nesting depth limit then? No. If you go beyond ten, it will allocate twenty; if you fill that up, forty more, etc. until you run out of RAM.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;This is my test of the dynamic array provision storing a long chain of singletons in a clause 11 brace levels deep.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt; int main(int c, char **v) { | int main(int c, char **v) if (q) { | if (q) if (q) { | if (q) if (q) { | if (q) if (q) { | if (q) if (q) { | if (q) if (q1) { | if (q1) if (q2){ | if (q2) if (q3) { | if (q3) if (q) { | if (q) if (q) { | if (q) for (qS) a(); | for (qS) a() if (qS) | if (qS) if (qS) | if (qS) if (qS) | if (qS) if (qS) | if (qS) if (qS) | if (qS) if (qS) | if (qS) if (qS) | if (qS) if (qS) | if (qS) if (qS) | if (qS) if (qS) | if (qS) if (qS) | if (qS) if (qS) | if (qS) if (qS) | if (qS) if (qS) | if (qS) a++; | a++ } } } } } } } } } } | // Final line comment immediately after action. } | // Final line comment immediately after action. |&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;If you understand how ugly that is, buckle up, it gets quite a bit worse! I&amp;#8217;ll save that for the next post.&lt;/p&gt;&lt;/div&gt; &lt;svg width=&quot;4in&quot; height=&quot;4in&quot; viewBox=&quot;0 0 100 77.3&quot;&gt; &lt;path style=&quot;fill:#000000;fill-opacity:1;stroke:none&quot; d=&quot;m 25,-4 -2.87,4.94 2.14,3.71 -3.70,-1.00 -3.91,6.83 12.98,3.47 1.40,2.41 -11.02,-2.94 -7.50,4.18 0.10,8.59 8.06,8.06 h -2.81 l -9.50,-9.48 -3.91,6.77 2.71,2.70 h -4.28 l -2.87,5.00 2.87,4.94 h 4.28 l -2.71,2.76 3.91,6.77 9.50,-9.54 h 2.81 l -8.06,8.06 -0.10,8.65 7.50,4.18 11.02,-2.94 -1.40,2.41 -12.98,3.47 3.91,6.83 3.70,-1.00 -2.14,3.71 2.87,4.94 h 5.74 l 2.14,-3.71 0.99,3.71 h 7.83 l -3.47,-12.95 1.40,-2.47 2.95,11.07 7.40,4.35 7.39,-4.35 2.95,-11.07 1.40,2.47 -3.47,12.95 h 7.83 l 0.99,-3.71 2.14,3.71 h 5.74 l 2.87,-4.94 -2.14,-3.71 3.70,1.00 3.91,-6.83 -12.98,-3.47 -1.40,-2.41 11.02,2.94 7.50,-4.18 -0.10,-8.65 -8.06,-8.06 h 2.81 l 9.50,9.54 3.91,-6.77 -2.71,-2.76 h 4.28 l 2.87,-4.94 -2.87,-5.00 h -4.28 l 2.71,-2.70 -3.91,-6.77 -9.50,9.48 h -2.81 l 8.06,-8.06 0.10,-8.59 -7.50,-4.18 -11.02,2.94 1.40,-2.41 12.98,-3.47 -3.91,-6.83 -3.70,1.00 2.14,-3.71 -2.87,-4.94 h -5.74 l -2.14,3.71 -0.99,-3.71 h -7.83 l 3.47,12.95 -1.40,2.41 -2.95,-11.01 -7.40,-4.35 -7.39,4.35 -2.95,11.01 -1.40,-2.41 3.47,-12.95 h -7.83 l -0.99,3.71 -2.14,-3.71 z m 24.99,6.53 4.62,17.19 c 4.30,0.86 7.84,2.72 10.56,5.65 l 16.64,-4.47 -13.43,13.42 -10.99,0.10 c 0,-6.81 -14.15,-10.00 -14.15,4.84 0.01,15.13 14.15,11.66 14.15,4.94 h 10.96 l 13.46,13.42 -16.65,-4.47 c -2.78,2.92 -6.21,4.78 -10.29,5.59 l -4.89,17.25 -4.65,-17.37 c -4.04,-0.99 -7.48,-2.69 -9.79,-5.65 l -17.38,4.65 13.34,-13.30 c -0.64,-3.76 -0.47,-7.03 -0.01,-10.12 l -13.33,-13.30 17.37,4.65 c 3.03,-3.05 6.26,-5.11 9.80,-5.65 z&quot;/&gt; &lt;/svg&gt;</description></item>
<item><title>SnowC - Challenges - Aggregate Types</title><link>http://xed.ch/blog/2026/0727.html</link><guid>http://xed.ch/blog/2026/0727.html</guid><pubDate>Mon, 27 Jul 2026 19:56 EDT</pubDate><description>&lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Today is the first in a series on challenges I ran into while designing and working on &lt;a href=&quot;https://xed.ch/p/snowc&quot;&gt;SnowC&lt;/a&gt;. My hope is that these can offer insight into how the system really works and what the thinking was behind my design decisions. These posts also highlight how challenging this project actually is&amp;#8201;&amp;#8212;&amp;#8201;if you&amp;#8217;re keen to improve on my system&amp;#8201;&amp;#8212;&amp;#8201;and what I did to overcome those challenges.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Instead of starting at the beginning let&amp;#8217;s look at the last major challenge I tackled: &lt;strong&gt;aggregate types&lt;/strong&gt;.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;When I had the realization that &lt;strong&gt;braces are redundant in good code&lt;/strong&gt;, I was naively thinking that the function of braces was to control nesting levels. And that is something they do. &lt;em&gt;Usually&lt;/em&gt;.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;It turns out that&amp;#8201;&amp;#8212;&amp;#8201;in C specifically&amp;#8201;&amp;#8212;&amp;#8201;braces are also used for Other Things. After carefully studying those my personal opinion is that this is a questionable stylistic language design choice. I get it&amp;#8201;&amp;#8212;&amp;#8201;space is tight with a limited number of characters and a lot of functions to perform. But overloading braces with some bonus meanings is none too cool. Fortunately SnowC improves the situation quite a bit!&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Which brings us to a feature of C that I procrastinated dealing with until the last possible moment. While writing &lt;code&gt;c2snowc.c&lt;/code&gt; I actually had no idea how I was going to deal with aggregate types until everything else was finished! These are (possibly) complex definitions and declarations of &lt;a href=&quot;https://www.gnu.org/software/c-intro-and-ref/manual/html_node/Structures.html&quot;&gt;&lt;code&gt;struct&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://www.gnu.org/software/c-intro-and-ref/manual/html_node/Unions.html&quot;&gt;&lt;code&gt;union&lt;/code&gt;&lt;/a&gt;, and &lt;a href=&quot;https://www.gnu.org/software/c-intro-and-ref/manual/html_node/Enumeration-Types.html&quot;&gt;&lt;code&gt;enum&lt;/code&gt;&lt;/a&gt; types that often use curly braces in a quirky way. Programmers generally do not have strong feelings about how those braces correspond to indentation if they have any at all.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Let&amp;#8217;s carefully unravel the solution because it also helps explain effective SnowC usage generally. Remember that SnowC is basically stripped down C that easily reconstitutes back to C if you follow some very &lt;strong&gt;simple rules&lt;/strong&gt;.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Consider an analogy&amp;#8201;&amp;#8212;&amp;#8201;if I ask some kind of robot chef to convert some dehydrated fruit into regular fruit and then I mischievously give it some fresh fruit, I&amp;#8217;m likely to get back well-soaked regular fruit. And maybe that&amp;#8217;s a good trick if what you really wanted was washed fruit. If you know the simple rules you can use them to your advantage.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;SnowC is like that with braces. Generally the point of SnowC is to allow you to not fuss with braces. If you really want them however that&amp;#8217;s fine but the conversion isn&amp;#8217;t going to think too hard about what you really are trying to do. It will just follow the simple rules for converting back to normal C. Making sure the C is correct and makes sense is on you.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Consider this SnowC on the left and it&amp;#8217;s corresponding automatically generated C on the right.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt; void f(int x) | void f(int x) { for (int i=0; i&amp;lt;x; i++) x=-x | for (int i=0; i&amp;lt;x; i++) x=-x; | }&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;It follows the simple rules. Braces are added because of the indent, and a semicolon is tacked on because the conversion sees no reason not to. The &lt;code&gt;for&lt;/code&gt; line isn&amp;#8217;t scrutinized to see if you&amp;#8217;re an intelligent C programmer. Look what happens if you add some clumsy braces to the SnowC.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt; void f(int x) | void f(int x) { for (int i=0; i&amp;lt;x; i++) { x=-x } | for (int i=0; i&amp;lt;x; i++) { x=-x }; | }&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;The same simple rules are correctly applied but now the C is invalid. Getting that part right is up to you!&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;If you understand and anticipate the conversion rules, you &lt;em&gt;can&lt;/em&gt; try adventurous tricks to produce valid C.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt; void f(int x) | void f(int x) { for (int i=0; i&amp;lt;x; i++) { x=-x; } x++ | for (int i=0; i&amp;lt;x; i++) { x=-x; } x++; | }&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;This is now correct C. The exact same simple rules were used&amp;#8201;&amp;#8212;&amp;#8201;the programmer just did a better job of playing the game.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;However note that when you convert &lt;em&gt;this&lt;/em&gt; generated C back to SnowC it will get looked at more carefully and cleaned up. When starting with wild C code, those rules are not so simple! (Now input C on the left and generated SnowC on the right.)&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt; void f(int x) { | void f(int x) for (int i=0; i&amp;lt;x; i++) { x=-x; } x++; | for (int i=0; i&amp;lt;x; i++) } | x=-x | x++&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;If you suspect that a conversion to SnowC and then back to C can help clean up badly structured C code, you&amp;#8217;re right! Even if you never use or look at SnowC code, the conversion programs can highlight and unravel badly structured C code.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Now that we&amp;#8217;ve seen how braces can be dropped into SnowC code and mostly ignored, it&amp;#8217;s easier to imagine how this could be useful for aggregate types. Let&amp;#8217;s look at some examples. Consider the conversion of this complex badly formatted C struct (left) to SnowC (right).&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt; struct Point { | struct Point int x; | int x int y; | int y struct { int z; | struct struct {int w;} nested; | int z } inner; | struct }; | int w | nested | inner | ;&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;The conversion pulls out semicolons and braces that the simple rules of SnowC to C conversion can easily replace. The random styling and indentation is not so random in SnowC. Compare old original C (left above) to the new C (below on the right) after a round trip conversion.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;listingblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt; struct Point | struct Point { int x | int x; int y | int y; struct | struct { int z | int z; struct | struct { int w | int w; nested | } inner | nested; ; | } | inner; | } | ; |&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;If you&amp;#8217;re not a C programmer and have just been following along roughly looking at the structure (bravo BTW!) then this code may not seem remarkable. SnowC conversions are just doing what they&amp;#8217;ve been doing in all code we&amp;#8217;ve seen. Well, with one tiny exception.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;We have not seen a C to SnowC conversion leave an isolated semicolon when it is the last effective character of a line. Normally, the conversion to SnowC drops ending semicolons. This one represents a genuine special case rule that was created for aggregate types. The rule is: if a removable closing brace effectively (ignoring comments and whitespace) immediately precedes a semicolon which is the last effective character, then the semicolon stays.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;If you think about it, a closing brace followed immediately by a semicolon is kind of a weird construction. As far as I can tell, it actually only ever shows up when defining types in situations like this.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;The nice thing about the SnowC to C conversion is that it remains simple and follows the rules. If the last effective character is a semicolon &lt;em&gt;in SnowC&lt;/em&gt; clearly that&amp;#8217;s weird and it must be there for a reason, so it is left alone (and a second semicolon is not added to it).&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Thank goodness we&amp;#8217;ve solved the puzzle of aggregate types! Whew! What a relief.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Wait, what? We still haven&amp;#8217;t? Ugh. It was about here that I (like anyone reading this) started to despair that maybe there were an infinite number of awful edge cases. I worried that maybe the whole concept was intractable. Luckily that wasn&amp;#8217;t true! Let&amp;#8217;s take a look at the last tricky problem involving aggregate types.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;So far we have seen a pretty normal struct definition but there&amp;#8217;s something else that can happen in type related syntax that involves braces and that is actually filling in values. It doesn&amp;#8217;t even have to be fancy aggregate types. Here&amp;#8217;s a simple array getting its values preloaded at definition time.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;int arr[] = {1, 2, 3};&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Gah! Who invited those braces?! And here are some typical aggregate types with similar syntax.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;enum bool { FALSE, TRUE }; struct point p = { y: yvalue, x: xvalue };&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Clearly if those braces are converted to indents, that&amp;#8217;s going to be a mess. This is where being able to leave braces in SnowC becomes critical. Let&amp;#8217;s fast forward and show what &lt;code&gt;c2snowc&lt;/code&gt; actually does with those lines. Here are the SnowC conversions.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;int arr[] = {1, 2, 3} enum bool { FALSE, TRUE } struct point p = { y: yvalue, x: xvalue }&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;That&amp;#8217;s right, it just pulls the trailing semicolons off and says have a nice day! This means of course that SnowC can follow it&amp;#8217;s reliable rules back to C and just add the semicolon back. But how is this possible?&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;What I realized is that this kind of thing only ever happens when the contents of the braces do &lt;em&gt;not&lt;/em&gt; contain semicolons. Contrast with the earlier struct example. I created a function that scans brace pairs and if they contain no semicolons or other braces between the last characters of other C code (if any) and the closing brace, the braces are remapped as normal non-brace miscellaneous C code. I call this kind of brace, &lt;strong&gt;weak braces&lt;/strong&gt;. Once their special meaning as a true brace is taken off the map of what&amp;#8217;s going on, everything behaves very nicely. It&amp;#8217;s like internally pretending to switch those braces out for some other kind of syntax while still in fact using the braces characters.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;Unfortunately that still leaves nasty situations like this.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;struct Point origin = {0, 0, {0, {0}}};&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;I realized that only the innermost &lt;code&gt;{0}&lt;/code&gt; would get converted to weak braces. But! Once that conversion was done, if I ran the conversion again the next pair would get converted. I realized that if I returned how many conversions took place, I could elegantly solve the entire problem with this single line.&lt;/p&gt;&lt;/div&gt; &lt;div class=&quot;literalblock&quot;&gt; &lt;div class=&quot;content&quot;&gt; &lt;pre&gt;&lt;code&gt;while (map_C_weak_braces(...));&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class=&quot;paragraph&quot;&gt;&lt;p&gt;That basically says, keep doing this conversion over and over until there is nothing left to convert. And once that is done, everything works very nicely. This may be the one place where I traded execution time for code elegance and my own sanity.&lt;/p&gt;&lt;/div&gt; &lt;svg width=&quot;4in&quot; height=&quot;4in&quot; viewBox=&quot;0 0 100 77.3&quot;&gt; &lt;path style=&quot;fill:#000000;fill-opacity:1;stroke:none&quot; d=&quot;m 25,-4 -2.87,4.94 2.14,3.71 -3.70,-1.00 -3.91,6.83 12.98,3.47 1.40,2.41 -11.02,-2.94 -7.50,4.18 0.10,8.59 8.06,8.06 h -2.81 l -9.50,-9.48 -3.91,6.77 2.71,2.70 h -4.28 l -2.87,5.00 2.87,4.94 h 4.28 l -2.71,2.76 3.91,6.77 9.50,-9.54 h 2.81 l -8.06,8.06 -0.10,8.65 7.50,4.18 11.02,-2.94 -1.40,2.41 -12.98,3.47 3.91,6.83 3.70,-1.00 -2.14,3.71 2.87,4.94 h 5.74 l 2.14,-3.71 0.99,3.71 h 7.83 l -3.47,-12.95 1.40,-2.47 2.95,11.07 7.40,4.35 7.39,-4.35 2.95,-11.07 1.40,2.47 -3.47,12.95 h 7.83 l 0.99,-3.71 2.14,3.71 h 5.74 l 2.87,-4.94 -2.14,-3.71 3.70,1.00 3.91,-6.83 -12.98,-3.47 -1.40,-2.41 11.02,2.94 7.50,-4.18 -0.10,-8.65 -8.06,-8.06 h 2.81 l 9.50,9.54 3.91,-6.77 -2.71,-2.76 h 4.28 l 2.87,-4.94 -2.87,-5.00 h -4.28 l 2.71,-2.70 -3.91,-6.77 -9.50,9.48 h -2.81 l 8.06,-8.06 0.10,-8.59 -7.50,-4.18 -11.02,2.94 1.40,-2.41 12.98,-3.47 -3.91,-6.83 -3.70,1.00 2.14,-3.71 -2.87,-4.94 h -5.74 l -2.14,3.71 -0.99,-3.71 h -7.83 l 3.47,12.95 -1.40,2.41 -2.95,-11.01 -7.40,-4.35 -7.39,4.35 -2.95,11.01 -1.40,-2.41 3.47,-12.95 h -7.83 l -0.99,3.71 -2.14,-3.71 z m 24.99,6.53 4.62,17.19 c 4.30,0.86 7.84,2.72 10.56,5.65 l 16.64,-4.47 -13.43,13.42 -10.99,0.10 c 0,-6.81 -14.15,-10.00 -14.15,4.84 0.01,15.13 14.15,11.66 14.15,4.94 h 10.96 l 13.46,13.42 -16.65,-4.47 c -2.78,2.92 -6.21,4.78 -10.29,5.59 l -4.89,17.25 -4.65,-17.37 c -4.04,-0.99 -7.48,-2.69 -9.79,-5.65 l -17.38,4.65 13.34,-13.30 c -0.64,-3.76 -0.47,-7.03 -0.01,-10.12 l -13.33,-13.30 17.37,4.65 c 3.03,-3.05 6.26,-5.11 9.80,-5.65 z&quot;/&gt; &lt;/svg&gt;</description></item>
</channel></rss>
