diff --git a/2015/burton/README.md b/2015/burton/README.md index 4bf4f3f8a..7e3d1e0d8 100644 --- a/2015/burton/README.md +++ b/2015/burton/README.md @@ -46,13 +46,14 @@ grep '[0-9]' prog.c ./prog < test.in -man ./calc.man +man ./calc.1 ``` ## Alternate code An alternate version of this entry, `prog.alt.c`, is provided. -This alternate code is discussed in the "prog.c vs prog.alt.c" section of the author's comments below. +This alternate code is discussed in the "prog.c vs prog.alt.c" section of the +author's comments below. To compile this alternate version: @@ -66,10 +67,11 @@ Use `prog.alt` as you would `prog` above. "Look, Ma, no ~~hands~~ digits!" -The place where some arithmetic operations are done, for example, -division or modulo, are easy to find. Where are addition and subtraction? +The place where some arithmetic operations are done, for example, division or +modulo, are easy to find. Where are addition and subtraction? -We were impressed with the level of detail the author "blogged" the obfuscation process. +We were impressed with the level of detail the author "blogged" the obfuscation +process. ## Author's comments: @@ -80,10 +82,10 @@ See the included man page. ;-) ### Notes -This program is named calc. Onomatopoeia of sorts, in the visual realm. -The formatting was easier: Form Follows Function (although [Hou][1] did this better). -It was tempting to call it iCalc, with i for integer (and it was written on an iDevice), -but sanity prevailed. +This program is named calc. Onomatopoeia of sorts, in the visual realm. The +formatting was easier: Form Follows Function (although [Hou][1] did this +better). It was tempting to call it iCalc, with i for integer (and it was +written on an iDevice), but sanity prevailed. Printed in 8 pt Courier, it makes aesthetically pleasing 8.5 x 11 wall art. @@ -91,12 +93,13 @@ Printed in 8 pt Courier, it makes aesthetically pleasing 8.5 x 11 wall art. ### prog.c vs prog.alt.c -Dominik Muth observed a syntax error on `1+1`, `1-1`, `1|1`, and `1^1` when using an -ARM based computer, although `1*1` and others worked correctly. -This occurs because some platforms by default treat `char` as `unsigned char`. -The original code has this weakness, which can be corrected by adding `-fsigned-char` -to the compile line. -The modified code is agnostic to `char` signed-ness. +Dominik Muth observed a syntax error on `1+1`, `1-1`, `1|1`, and `1^1` when +using an ARM based computer, although `1*1` and others worked correctly. This +occurs because some platforms by default treat `char` as `unsigned char`. The +original code has this weakness, which can be corrected by adding +`-fsigned-char` to the compile line. + +The modified code is agnostic to `char` signedness. ### Why is this entry obfuscated/interesting? @@ -105,21 +108,24 @@ Yes, this is another calculator program. However, unlike any other calculator source code the author is aware of, this one contains no digits. At all. Anywhere. As in - grep '[0-9]' prog.c || echo no digits! +```sh +grep '[0-9]' prog.c || echo no digits! +``` -Nor are the digits simply obfuscated. There are no character constants. -There is only one single, short string, and it does not contain any obfuscated digits. -And yet the output is in decimal and hexadecimal, and it accepts octal, decimal, and hex input. +Nor are the digits simply obfuscated. There are no character constants. There +is only one single, short string, and it does not contain any obfuscated digits. +And yet the output is in decimal and hexadecimal, and it accepts octal, decimal, +and hex input. -Flow control is by `for` and `return`. It is If-less. Switch-less. While-less. Hope-less. -Literally, if not figuratively. ;-) +Flow control is by `for` and `return`. It is if-less, switch-less, +while-less, and hopeless. Literally, if not figuratively. ;-) -The code size is intentionally 2015, as stated by iocccsize -i, -and can be reformatted without changes (as in rule2.c) to exactly 4096 octets. +The code size is intentionally 2015, as stated by iocccsize -i, and can be +reformatted without changes (as in rule2.c) to exactly 4096 octets. -Finally, calc is interesting, if for no other reason than it is practically useful: -Hex/decimal conversion, arithmetic, byte-swapping, bit-shifting, and logical operations with memory, -on the command line, or interactively. +Finally, calc is interesting, if for no other reason than it is practically +useful: Hex/decimal conversion, arithmetic, byte-swapping, bit-shifting, and +logical operations with memory, on the command line, or interactively. ### But the question is why is the entry obfuscated? @@ -130,52 +136,57 @@ The code follows many industry best-practices: - Numbers and digits have often been used for obfuscation, so in a bid for better clarity, this code has no digits at all. -- In the spirit of clarity, most of the variables are declared before use, so they - are readily visible, with single letter names that don't clutter the screen, - in a tautological arrangement. +- In the spirit of clarity, most of the variables are declared before use, so +they are readily visible, with single letter names that don't clutter the +screen, in a tautological arrangement. -- More helpful is the use of `typedef`s instead of `#define`, so the types remain useful - even in the debugger, and source code beautifiers will not undo careful presentation. +- More helpful is the use of `typedef`s instead of `#define`, so the types +remain useful even in the debugger, and source code beautifiers will not undo +careful presentation. - `#defines` are used here in the best spirit of the preprocessor: to shorten - repetitive constructs, not for obscurity. +repetitive constructs, not for obscurity. -- All functions are short (and therefore easy to understand?) and do only one thing, well, .... +- All functions are short (and therefore easy to understand?) and do only one +thing, well, .... -- Curly braces invite endless holy wars over readability. This code only uses them - for function definitions, thereby greatly improving readability, and reducing arguments - about said readability. +- Curly braces invite endless holy wars over readability. This code only uses +them for function definitions, thereby greatly improving readability, and +reducing arguments about said readability. -- One of C's many blessings (until recently) is a modicum of keywords, - of which only 8 are used: - `for`, `return`, `typedef`, `void`, `char`, `short`, `unsigned` and `long`; - 10 if `#include` and `#define` are counted. +- One of C's many blessings (until recently) is a modicum of keywords, of which +only 8 are used: `for`, `return`, `typedef`, `void`, `char`, `short`, `unsigned` +and `long`; 10 if `#include` and `#define` are counted. -- The code helpfully declares "syntax_error!" right up front, filling the reader with confidence. +- The code helpfully declares `"syntax_error!"` right up front, filling the +reader with confidence. -- In spite of its inherent clarity, the code was presented to both *indent*(1) and - *clang-format*(1), after running it through the preprocessor, sustaining its better qualities. +- In spite of its inherent clarity, the code was presented to both *indent*(1) +and *clang-format*(1), after running it through the preprocessor, sustaining its +better qualities. -- That the code is obfuscated is spelled out two different ways in the source; it helps - to reformat the code to see the second spelling. +- That the code is obfuscated is spelled out two different ways in the source; +it helps to reformat the code to see the second spelling. -- The author is overly fond of the code that handles input. - There is no memory allocation, nor pre-allocation of arrays, so where does the - memory come from? The multi-base strtol() code is pretty trick. - No special attempt was made to replace operations with obscure mathematical relations, - as the (lack of) beauty is in the parsing -- ask not where is the modulus operation, - but how does it know when to mod -- okay, but where is add and subtract? - And really, who doesn't enjoy a setjmp() in the argument list? +- The author is overly fond of the code that handles input. There is no memory +allocation, nor pre-allocation of arrays, so where does the memory come from? +The multi-base strtol() code is pretty tricky. No special attempt was made to +replace operations with obscure mathematical relations, as the (lack of) beauty +is in the parsing -- ask not where is the modulus operation, but how does it +know when to mod -- okay, but where is add and subtract? And really, who +doesn't enjoy a setjmp() in the argument list? -- Finally, documentation of the source code structure is helpfully provided in rot13 format. - But isn't it unsportsmanlike to rely upon it? However, even with this helpful guide, - things like reading input lines remain as tricky to read as they are to write. +- Finally, documentation of the source code structure is helpfully provided in +rot13 format. But isn't it unsportsmanlike to rely upon it? However, even with +this helpful guide, things like reading input lines remain as tricky to read as +they are to write. ### Compiler warnings -Calc is C99 compliant, and is ANSI/C90 compliant except for wanting 64-bit `long long`. -Changing the two `long long` typedefs to `long`, and compiling with `gcc -m32` -yields a program that operates on 32-bit values, with wrap-around of shifts mod 32: +Calc is C99 compliant, and is ANSI/C90 compliant except for wanting 64-bit `long +long`. Changing the two `long long` typedefs to `long`, and compiling with `gcc +-m32` yields a program that operates on 32-bit values, with wrap-around of +shifts mod 32: 1<<48 65536 0x00010000 @@ -185,8 +196,10 @@ This is my workaday compiler. gcc is also clean with -ansi, and -std=c99. clang 3.6.0 lives up to its namesake, resounding on non-problems. To stifle these complaints: + -Wno-return-type -Wno-parentheses -Wno-empty-body + When invoked with -ansi -pedantic, there are two warnings, which can be ignored: * `ISO C90 does not support 'long long'` @@ -202,12 +215,14 @@ With -Wall -std=c99, both are quite noisy, and all can be ignored: If the program name begins with an "e", it echoes stdin to stdout. This allows for a convenient test suite: - ln -s prog eprog - PATH=. eprog < test.in | diff - test.out +```sh +ln -s prog eprog +PATH=. eprog < test.in | diff - test.out +``` ### Spoilers -If you do not want to puzzle out how it works, see spoilers.markdown. +If you do not want to puzzle out how it works, see `spoilers.markdown`. ## Copyright: diff --git a/2015/burton/calc.1 b/2015/burton/calc.1 new file mode 100644 index 000000000..5e6ac4b71 --- /dev/null +++ b/2015/burton/calc.1 @@ -0,0 +1,130 @@ +.TH calc 1 "25 March 2023" "calc" "IOCCC 2015" +.SH NAME +calc \- an integer expression calculator that outputs in both hex and decimal +.SH SYNOPSIS +\fBcalc\fP \fI[expr]\fP +.SH DESCRIPTION +.BR calc +presents a simple signed integer expression calculator. +The result is printed on +.BR stdout +as both a decimal value and its hex equivalent. +Results are presented as 32-bit values, displaying as 64-bits only if necessary. +If the program name begins with +.I e +the input is also copied to +.BR stdout . +.BR stdin +is read if there are no arguments. +.PP +Constants may be decimal, octal or hex. +There are 26 registers, initialized to zero, named A-Z; the most recent result is always remembered in the register +.IR . +(dot). +Assignments are silent; the character +.IR $ +will print a table of the non-zero registers. +.PP +All input is case-insensitive. +Semicolons and/or newlines separate statements. +Whitespace is ignored, except newlines cannot occur within expressions. +Comments are introduced with +.I # +and extend through the following newline. +.PP +Operator precedence is as found in Go: +OR and XOR are treated as additive, SHIFTing and AND are multiplicative. +This allows a more natural expression without parentheses, +as in +.BR a&0xff\ |\ b<<8 \c +\&. +.PP +Supported operations in decreasing precedence: +.TP +.B () +forced precedence +.TQ +.B \- + ! ~ ? +unary plus/minus, not, compl, byteswap +.TQ +.B * / % & << >> >>> +mul, div, mod, bitand, shift: left, right, unsigned right +.TQ +.B + \- | ^ +add, sub, bitor, xor +.TQ +.B = +assignment ([a-z] = expr) +.SH EXAMPLES +.PP +Display the decimal value of 0x42: +.sp +.RS +.ft B +$ calc 0x42 +.br +66 0x00000042 +.ft R +.RE +.PP +What is the mask value for 22 bits? +.sp +.RS +.ft B +$ calc '1<<22-1' +.br +4194303 0x003fffff +.ft R +.RE +.PP +What is the integer value of byte-swapped 0x11223344? +.RS +.ft B +$ calc ?0x11223344 +.br +1144201745 0x44332211 +.ft R +.RE +.PP +Some canned expressions: +.RS +.ft B +cat <> 32 # signed shift +.br +-1 >>> 32 # unsigned shift +.br +! +.ft R +.RE +.SH BUGS +An assignment with certain types of syntax error will replace the registers with a partial evaluation: +.sp +.RS +.ft B +calc 'a = 1; a; a = 3 4; a' +.br +1 0x00000001 +.br +syntax error +.br +3 0x00000003 +.ft R +.RE +.PP +Command line expressions longer than 64 characters will give a syntax error. +.SH SEE ALSO +\ diff --git a/2015/burton/road.to.obscurity b/2015/burton/road.to.obscurity index fb2d38359..c824c9a45 100644 --- a/2015/burton/road.to.obscurity +++ b/2015/burton/road.to.obscurity @@ -3,18 +3,19 @@ Development of the obfuscated calculator. I wanted to enter the IOCCC.... -I think I can write something clever. Possibly even obscure. The competition is fierce, -but not a closed door. +I think I can write something clever. Possibly even obscure. The competition +is fierce, but not a closed door. -I needed an interesting program, that had a reasonable chance of begin shrunk within 4096 bytes, -and lent itself to obfuscation. I had been revisiting utilities I've written over -the years. One useful program was calc, a simple recursive descent signed integer -expression parser I developed from first principles as a demonstration of how computers -work for my son. That program did the basic four operations, but as a programmer, -I needed more, so those were added over time. +I needed an interesting program, that had a reasonable chance of being shrunk +within 4096 bytes, and lent itself to obfuscation. I had been revisiting +utilities I've written over the years. One useful program was calc, a simple +recursive descent signed integer expression parser I developed from first +principles as a demonstration of how computers work for my son. That program +did the basic four operations, but as a programmer, I needed more, so those +were added over time. -Calc is a useful, functioning utility. The operators before the obfuscation dance began -(this is called fore-shadowing): +Calc is a useful, functioning utility. The operators before the obfuscation +dance began (this is called fore-shadowing): () forced precedence - ~ ? unary minus, compl, byteswap @@ -22,8 +23,8 @@ Calc is a useful, functioning utility. The operators before the obfuscation dan + - | ^ add, sub, bitor, xor NUM 0-9 -This seemed like a reasonable candidate to experiment with obfuscation; perhaps it might -yield good results. +This seemed like a reasonable candidate to experiment with obfuscation; perhaps +it might yield good results. Principle of operation -------------------------------------------------- @@ -69,7 +70,8 @@ The algorithm was and is rather straightforward: Obfuscation is a process -------------------------------------------------- -Within the above algorithm, there are many details unmentioned, all ripe for obfuscation: +Within the above algorithm, there are many details unmentioned, all ripe for +obfuscation: - input - tokenization @@ -79,153 +81,170 @@ Within the above algorithm, there are many details unmentioned, all ripe for obf - swapping bytes - user interface (command line vs stdin) -Uncertain as to how this would progress, I started obfuscating/condensing everything -except the conversion operations, leaving those to sprintf() and strtol(). The first -version of the obfuscated code used an homage to 1984 anon for I/O, and it featured a -#define for else's and the types, to help minimize the repetition. And of course, most -functions were renamed with one or two letters, and most of the variables became single -letter globals. Tokenization was rather easy with a declared array, just initialized with -unique values for each. +Uncertain as to how this would progress, I started obfuscating/condensing +everything except the conversion operations, leaving those to sprintf() and +strtol(). The first version of the obfuscated code used an homage to 1984 anon +for I/O, and it featured a #define for else's and the types, to help minimize +the repetition. And of course, most functions were renamed with one or two +letters, and most of the variables became single letter globals. Tokenization +was rather easy with a declared array, just initialized with unique values for +each. -This was when the obfuscation took a turn for the better (worse): it was easy to hide the -operations via a simple transformation. The code now dealt with small integer values -instead of direct ASCII representation of the operations. And the strings where collapsed -into a single combined error message and operator tokenization table. This was relatively -obscure. +This was when the obfuscation took a turn for the better (worse): it was easy +to hide the operations via a simple transformation. The code now dealt with +small integer values instead of direct ASCII representation of the operations. +And the strings where collapsed into a single combined error message and +operator tokenization table. This was relatively obscure. -Next, sprintf() was replaced with a custom formatter. This was rather simple, but at least -the formatting was not obvious. Formatting and printing were split early. strtol() was -replaced with custom logic, limited to 3 practical bases: octal, decimal, and hex. The -last result was remembered in a special variable, dot. +Next, sprintf() was replaced with a custom formatter. This was rather simple, +but at least the formatting was not obvious. Formatting and printing were +split early. strtol() was replaced with custom logic, limited to 3 practical +bases: octal, decimal, and hex. The last result was remembered in a special +variable, dot. -By this time, I had a version which I considered obscure, and shared this early version -with a couple of colleagues for comments. I suggested I could improve the obfuscation of -a term() and factor(), and would like to have assignable memories. +By this time, I had a version which I considered obscure, and shared this early +version with a couple of colleagues for comments. I suggested I could improve +the obfuscation of a term() and factor(), and would like to have assignable +memories. -This code was pretty dense, but inspired by previous IOCCC entries, I thought I might get -rid of all the else's, which showed the structure of the parser rather clearly. Investing -in the ternary operator, the code got quite a bit more difficult to follow, used only -for-loops, and I had to start taking notes to follow it. This was a good sign! ;-) +This code was pretty dense, but inspired by previous IOCCC entries, I thought I +might get rid of all the else's, which showed the structure of the parser +rather clearly. Investing in the ternary operator, the code got quite a bit +more difficult to follow, used only for-loops, and I had to start taking notes +to follow it. This was a good sign! ;-) -I started testing the code. I wrote a test suite to ensure the changes I made did not -corrupt the processing (oh so easy to do with this obfuscated mess). Why does -1>>32 -yield -1? Oh, yeah: signed arithmetic. Enter Java's unsigned shift: >>>. Better. But -with more features, more to get wrong, and harder to know which expression is causing the -error. The easy solution: if the name begins with 'e', echo the input. Now I can see the -failing expression. Wait, what is this test expression supposed to be testing? +I started testing the code. I wrote a test suite to ensure the changes I made +did not corrupt the processing (oh so easy to do with this obfuscated mess). +Why does -1>>32 yield -1? Oh, yeah: signed arithmetic. Enter Java's unsigned +shift: >>>. Better. But with more features, more to get wrong, and harder to +know which expression is causing the error. The easy solution: if the name +begins with 'e', echo the input. Now I can see the failing expression. Wait, +what is this test expression supposed to be testing? Wouldn't it be nice to have comments? -Adding new functionality to an obscure program is ... going about it backwards. Get the -functionality correct and complete, THEN obscure. Sounds obvious, but that did not -happen. I got comments working, then desired to have multiple statements on a single -line (read: semicolon expression terminators). This involved quite a bit of rework of -the input code, but now allowed very convenient user interaction. +Adding new functionality to an obscure program is ... going about it backwards. +Get the functionality correct and complete, THEN obscure. Sounds obvious, but +that did not happen. I got comments working, then desired to have multiple +statements on a single line (read: semicolon expression terminators). This +involved quite a bit of rework of the input code, but now allowed very +convenient user interaction. Better Obscurity is a laborious process -------------------------------------------------- -Satisfied with the functionality, but unsatisfied with the obscurity, I pondered if I -could get rid of all the constants? This had not been done before (to my knowledge), and -I was able to work out the how-tos and the compromises. Now I can no longer declare my -arrays, I have to allocate them. Obscurity now increased! +Satisfied with the functionality, but unsatisfied with the obscurity, I +pondered if I could get rid of all the constants? This had not been done +before (to my knowledge), and I was able to work out the how-tos and the +compromises. Now I can no longer declare my arrays, I have to allocate them. +Obscurity now increased! But wouldn't it be nice to have assignable memories? -Around this time, the formatted code no longer fit with the 2053 limit (due to a lack of -comprehension of the significance of the space-after-brace-and-semi rule, and a layout -program that did not know how to do this). Frustrated, I wrote another program to -reformat this code into rule2.c, which was a single C token per line. This solved the -formatting problem, but intriguingly introduced another. Now I was faced with the 4096 -rule as well as the 2053 rule. But this was a challenge! +Around this time, the formatted code no longer fit with the 2053 limit (due to +a lack of comprehension of the significance of the space-after-brace-and-semi +rule, and a layout program that did not know how to do this). Frustrated, I +wrote another program to reformat this code into rule2.c, which was a single C +token per line. This solved the formatting problem, but intriguingly +introduced another. Now I was faced with the 4096 rule as well as the 2053 +rule. But this was a challenge! -I wrote a de-obfuscator, and worked on both the unobfuscated version and the master code. -I worked on the template formatter to make this as automated as possible, and worked to -achieve both a perfectly presented picture AND the single-line exactly-at-4096 rule2.c. +I wrote a de-obfuscator, and worked on both the unobfuscated version and the +master code. I worked on the template formatter to make this as automated as +possible, and worked to achieve both a perfectly presented picture AND the +single-line exactly-at-4096 rule2.c. -Finally, "perfection", with assignable memories! Where perfection is defined as a nicely -formatted picture at less than 2053, and exactly 4096 token-per-line rule2.c. +Finally, "perfection", with assignable memories! Where perfection is defined +as a nicely formatted picture at less than 2053, and exactly 4096 +token-per-line rule2.c. Testing Testing Testing -------------------------------------------------- -Cross platform, it works on Mac OSX, fails on Linux. It works if #include , but -it dumps core without it (the only change!)?. 64bits without proper declaration of ints -in syscalls yields unpredictable results. 32bit compilation showed problems with byte- -swapping in using addition versus bitwise-or. In 32bit land, x<<32 == x, so x<<32 + x>>32 -does not yield the desired result, but x<<32 | x>>32 does (with appropriate care and -feeding). And the different compilers on different hosts complained loudly and +Cross platform, it works on Mac OSX, fails on Linux. It works if #include +, but it dumps core without it (the only change!)?. 64bits without +proper declaration of ints in syscalls yields unpredictable results. 32bit +compilation showed problems with byte- swapping in using addition versus +bitwise-or. In 32bit land, x<<32 == x, so x<<32 + x>>32 does not yield the +desired result, but x<<32 | x>>32 does (with appropriate care and feeding). +And the different compilers on different hosts complained loudly and differently, although all produced correct bits in spite of the noise. -Further, the program did not do UNIX pipelines well, due to the unbuffered homage code -(byte-at-a-time I/O), of which write() was also the source of the portability problem. -Replace write() with getchar()/putchar(), and it piped well but lost some obscurity and -no longer paid (direct) homage to history. +Further, the program did not do UNIX pipelines well, due to the unbuffered +homage code (byte-at-a-time I/O), of which write() was also the source of the +portability problem. Replace write() with getchar()/putchar(), and it piped +well but lost some obscurity and no longer paid (direct) homage to history. -Another problem: only one expression at a time was allowed on the command line, no semis, -no comments, and it turns out $2, $3, ... were not handled correctly. Given the new I/O -code, it was now possible to copy the command line arguments into a buffer, and write a -small routine to retrieve bytes from this buffer like getchar(). Voila! Better obfuscation -with much better functionality! +Another problem: only one expression at a time was allowed on the command line, +no semis, no comments, and it turns out $2, $3, ... were not handled correctly. +Given the new I/O code, it was now possible to copy the command line arguments +into a buffer, and write a small routine to retrieve bytes from this buffer +like getchar(). Voila! Better obfuscation with much better functionality! But wouldn't it be nice to show what the current variables hold? -Bytes are now hard to find to keep exactly at 4096, and adding functionality to obscured -code is not easy. Restructuring the logic to gain the bytes necessary, code to display -the variables was added. +Bytes are now hard to find to keep exactly at 4096, and adding functionality to +obscured code is not easy. Restructuring the logic to gain the bytes +necessary, code to display the variables was added. -Test. Test. Test. Oh, noes! More bugs! Typing "1 + 2 5" yields 3, but no syntax error. -"g = 1" yields a syntax error, and "0xg" does not! ")" causes a seg fault! Why didn't I -test these before I started obfuscating?! By this point, everything I did to the code -perturbed the 4096 bytes, all variables were already a single character, and it was really -difficult to follow the logic in a debugger (single-stepping an expression did the entire -line of code, even if it involved multiple statements -- and it did, everywhere, even in -the unobfuscated version; think: a complex chain of ternary expressions is a single -statement). This involved careful examination of all the expressions within the logic, -trying to find a different way of getting the same result, but typically within fewer -bytes than before to allow for the added logic to fix the bugs. +Test. Test. Test. Oh, noes! More bugs! Typing "1 + 2 5" yields 3, but no +syntax error. "g = 1" yields a syntax error, and "0xg" does not! ")" causes a +segfault! Why didn't I test these before I started obfuscating?! By this +point, everything I did to the code perturbed the 4096 bytes, all variables +were already a single character, and it was really difficult to follow the +logic in a debugger (single-stepping an expression did the entire line of code, +even if it involved multiple statements -- and it did, everywhere, even in the +unobfuscated version; think: a complex chain of ternary expressions is a single +statement). This involved careful examination of all the expressions within +the logic, trying to find a different way of getting the same result, but +typically within fewer bytes than before to allow for the added logic to fix +the bugs. The bugs were fixed, the code re-formatted, and rule2.c was again 4096 bytes. But wouldn't it be nice for unary plus to work? -Add unary plus, re-balance. Little things now take a fair bit of work. Adding anything -requires taking away in others, but since all the logic is functional, this means -rewriting the logic to be more concise. After several rounds of this, bytes are scarce to -come by. +Add unary plus, re-balance. Little things now take a fair bit of work. Adding +anything requires taking away in others, but since all the logic is functional, +this means rewriting the logic to be more concise. After several rounds of +this, bytes are scarce to come by. But wouldn't it be nice for assignments to be silent? -Made them silent (easier said than done!). Why does "1 + 2 5" print a syntax error, but -"a = 1 + 2 5" does not? Rewrite(!) the assignment logic -- and this requires more bytes! +Made them silent (easier said than done!). Why does "1 + 2 5" print a syntax +error, but "a = 1 + 2 5" does not? Rewrite(!) the assignment logic -- and this +requires more bytes! Better Obscurity Through Simplification -------------------------------------------------- -Except there were not enough bytes to do this, unless #include is removed. But -then sbrk() must be declared, eating into the savings. If only there were 512 bytes of -memory already allocated that could be portably touched for free.... +Except there were not enough bytes to do this, unless #include is +removed. But then sbrk() must be declared, eating into the savings. If only +there were 512 bytes of memory already allocated that could be portably touched +for free.... Light bulb! Memory allocation was excised, and the code became more obscure. -It was working again! It formats beautifully, it compiled cleanly (with apropos --Wno-guards), it remains obscure after cpp and clang-format, it measures within the limits -and exactly at 4096 (this latter was really getting challenging to maintain). No more bugs -known, it is feature complete, documentation written. +It was working again! It formats beautifully, it compiled cleanly (with +apropos -Wno-guards), it remains obscure after cpp and clang-format, it +measures within the limits and exactly at 4096 (this latter was really getting +challenging to maintain). No more bugs known, it is feature complete, +documentation written. But wouldn't it be nice to have a logical not? -Sigh. This was the last operation added. Another round of code churn, expressions are -difficult to rewrite because they are both obscure and relatively tight. But changing -around the stable initialization code -- by now the largest piece of logic -- yielded the -necessary bytes, and improved obscurity by double duty encoding of the operations -initialization. +Sigh. This was the last operation added. Another round of code churn, +expressions are difficult to rewrite because they are both obscure and +relatively tight. But changing around the stable initialization code -- by now +the largest piece of logic -- yielded the necessary bytes, and improved +obscurity by double duty encoding of the operations initialization. -The utility is now truly functional, and feature complete, and obscure. Without src.doc -(basic documentation of the code structure), and some commented expanded logic stashed away, -and the familiarity I have with the code having written and revised it repeatedly, I would -not even want to attempt to decipher the logic. Many times in the debugging, I was thwarted -by my own obfuscations, and went down blind paths in trying to fix a bug or add functionality. -Now I think I have a good entry. +The utility is now truly functional, and feature complete, and obscure. +Without src.doc (basic documentation of the code structure), and some commented +expanded logic stashed away, and the familiarity I have with the code having +written and revised it repeatedly, I would not even want to attempt to decipher +the logic. Many times in the debugging, I was thwarted by my own obfuscations, +and went down blind paths in trying to fix a bug or add functionality. Now I +think I have a good entry. But wouldn't it be nice to import pre-set definitions from a file?