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