diff --git a/2015/burton/README.md b/2015/burton/README.md index 5dba3f97f..0c83957c9 100644 --- a/2015/burton/README.md +++ b/2015/burton/README.md @@ -9,7 +9,7 @@ make ``` -### To run: +## To run: ```sh ./prog "expression" @@ -23,7 +23,7 @@ or: Type in expressions on standard input. -### Try: +## Try: ```sh ./prog '?0x3343<<2' @@ -49,7 +49,7 @@ grep '[0-9]' prog.c man ./calc.1 ``` -## Alternate code +## 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 @@ -210,7 +210,7 @@ With -Wall -std=c99, both are quite noisy, and all can be ignored: * `value computed is not used [-Wunused-value]` * `operation on 'j' may be undefined [-Wsequence-point]` -### Test Suite +### Test Suite: If the program name begins with an "e", it echoes stdin to stdout. This allows for a convenient test suite: @@ -220,9 +220,9 @@ 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.md](spoilers.md). ## Copyright and CC BY-SA 4.0 License: diff --git a/2015/burton/prog.c b/2015/burton/prog.c index aeae34935..056dfe777 100644 --- a/2015/burton/prog.c +++ b/2015/burton/prog.c @@ -14,8 +14,8 @@ I H char O; I H short Y; I H long long Z; - O S[]="syntax_error!" - "M@K~|JOEF\\^~_NHI]"; L*N,*K,* + O S[]="syntax_error=" + "M@K~|JOEF\\^~_NHIA"; L*N,*K,* B,*E,*T,*A,*x,D; Q(*k)(),v; V z(P),j,_,* o,b,f,u,s,c,a,t,e,d; J l; Q _k(P){ R*K?*K++: ~-d; } V r(L a){ R a&&putchar(a); } L n(){ R*T=j= @@ -90,21 +90,21 @@ I H long long Z; ,L**K){ V*A; q(K); W(w())** K-c*c-b||p(B),_i(!setjmp(l)?T=B ,M(((A=(*o=m()==c-~s&&(j=a|T[-b])&& - s-m()==~s)?&o[j-a*u]:(T=B,&_)),*A=F()) + c-m()==-c)?&o[j-a*u]:(T=B,&_)),*A=F()) ),B:(L*)S); } V z(P){ V - s; W(m() &&v<=f*c + s; W(m() &&v< f*c )R v> b?s :G (): _:! (v-= t)? --T,_f - (): v-f?o[ + (): v==b?o[ (T[ -b]|a)- a*u]: !z(); G(); } diff --git a/2015/burton/spoilers.md b/2015/burton/spoilers.md new file mode 100644 index 000000000..ad1d87d2a --- /dev/null +++ b/2015/burton/spoilers.md @@ -0,0 +1,34 @@ +### Spoilers + + +Calc is a recursive-descent parser. It allocates from the stack deep enough +that all expressions are exhausted before the recursion stack depth reaches the +allocations. The memory map and source is documented in src.doc.rot13. Tokens +are encoded using values 0-20, where 0 is an invalid token, 1 is minus, 2 is +space, 3 is plus, etc. The necessary constants are derived at the start of the program, +using the guarantee that globals are initialized to zero; so a correct null pointer +is obtained from an uninitialized global pointer variable, and the integers from +uninitialized global int variables. If x is a global int, then !x is 1, and the +derivation proceeds from there, using some identities that are confusingly similar +and interestingly combined (increasing obfuscation): + + obc clr + --- --- + -~x x+1 + ~-x x-1 + y-~x y+x+1 + +The derived constants are a compromise between simple expressions, maximum utility, +and minimum number of variables used. + +Command line arguments are copied into the upper half of the input buffer, +separated by a space. This allows the code to have a single input parser by using +a pointer-to-func to either getchar() if live, or a getchar() simulator. + +A small amount of obfuscation comes from void functions declared int, but missing return. +This is necessary to allow these functions in ternary ?: expressions and paired with && and ||; +but the requisite returns would add symbol count at no benefit to obscurity or functionality. +Function decomposition was mostly chosen to avoid curly braces: +if a for-loop required multiple statements, the body became a new function. +Last, but not least: sharing state via global variables is an ancient and honored +form of obfuscation, and remarkably effective.