2 files changed
+121
-104
No files matched your search
@@ -25,7 +25,10 @@ make
|
|||||||
./dlowe 16 32 64 128 256
|
./dlowe 16 32 64 128 256
|
||||||
./dlowe 16 32 64 128 256 512
|
./dlowe 16 32 64 128 256 512
|
||||||
|
|
||||||
|
./dlowe 0
|
||||||
|
|
||||||
echo "sparkline of file sizes: $(wc -c * | awk '{print $1}' | xargs ./dlowe)" # or ./slen.sh
|
echo "sparkline of file sizes: $(wc -c * | awk '{print $1}' | xargs ./dlowe)" # or ./slen.sh
|
||||||
|
```
|
||||||
|
|
||||||
./dlowe 0
|
./dlowe 0
|
||||||
|
|
||||||
|
|||||||
+118
-104
@@ -30,19 +30,22 @@ Be patient with the last one.
|
|||||||
|
|
||||||
## Judges' remarks:
|
## Judges' remarks:
|
||||||
|
|
||||||
We liked this entry because it can serve as a standalone program as well as an include file; and, as usual,
|
We liked this entry because it can serve as a standalone program as well as an
|
||||||
because its input data is at least as obfuscated as the program itself.
|
include file; and, as usual, because its input data is at least as obfuscated as
|
||||||
|
the program itself.
|
||||||
|
|
||||||
This entry can be considered an abuse of the GCC's optimizer; it takes GCC about 4x longer to compile it
|
This entry can be considered an abuse of the GCC's optimizer; it takes GCC about
|
||||||
with -O3 than without, compared to clang's 2x.
|
4x longer to compile it with -O3 than without, compared to clang's 2x.
|
||||||
|
|
||||||
## Author's remarks:
|
## Author's remarks:
|
||||||
|
|
||||||
### Remarks
|
### Remarks
|
||||||
|
|
||||||
$ less prog.c
|
```sh
|
||||||
$ gcc -o prog prog.c
|
less endoh1.c
|
||||||
$ ./prog
|
gcc -o endoh1 endoh1.c
|
||||||
|
./endoh1
|
||||||
|
```
|
||||||
|
|
||||||
... Isn't there any more to it?
|
... Isn't there any more to it?
|
||||||
|
|
||||||
@@ -51,30 +54,33 @@ Yes, of course.
|
|||||||
This is a tool *for C programmers* to play the [SKI combinator calculus][1],
|
This is a tool *for C programmers* to play the [SKI combinator calculus][1],
|
||||||
especially, [Lazy K][2].
|
especially, [Lazy K][2].
|
||||||
|
|
||||||
This program will shine when it is used as a library.
|
This program will shine when it is used as a library. For example, `hello.lazy`
|
||||||
For example, `hello.lazy` is a "Hello world" program written in Lazy K.
|
is a "Hello world" program written in Lazy K.
|
||||||
|
|
||||||
$ lazy hello.lazy
|
```sh
|
||||||
Hello, world!
|
$ lazy hello.lazy
|
||||||
|
Hello, world!
|
||||||
|
```
|
||||||
|
|
||||||
where `lazy` is [a reference implementation of Lazy K][3].
|
where `lazy` is [a reference implementation of Lazy K][3].
|
||||||
At the same time, `hello.lazy` is *a valid C program* that uses `prog.c` as a library.
|
At the same time, `hello.lazy` is *a valid C program* that uses `endoh1.c` as a library.
|
||||||
|
|
||||||
$ gcc -o hello -xc hello.lazy
|
```sh
|
||||||
$ ./hello
|
$ gcc -o hello -xc hello.lazy
|
||||||
Hello, world!
|
$ ./hello
|
||||||
|
Hello, world!
|
||||||
|
```
|
||||||
|
|
||||||
In other words, this program is a kind of [polyglot][4].
|
In other words, this program is a kind of [polyglot][4].
|
||||||
|
|
||||||
The usage is simple:
|
The usage is simple: you just have to wrap Lazy K program with `#include
|
||||||
you just have to wrap Lazy K program with `#include "prog.c"`.
|
"endoh1.c"`. Note that `#` is a comment in Lazy K.
|
||||||
Note that `#` is a comment in Lazy K.
|
|
||||||
|
|
||||||
[1]: http://en.wikipedia.org/wiki/SKI_combinator_calculus
|
[1]: http://en.wikipedia.org/wiki/SKI_combinator_calculus
|
||||||
[2]: redacted
|
[2]: https://tromp.github.io/cl/lazy-k.html
|
||||||
[3]: redacted
|
[3]: https://github.com/irori/lazyk
|
||||||
[4]: http://en.wikipedia.org/wiki/Polyglot_%28computing%29
|
[4]: http://en.wikipedia.org/wiki/Polyglot_%28computing%29
|
||||||
|
|
||||||
### Obfuscation
|
### Obfuscation
|
||||||
|
|
||||||
... is inherent in SKI combinator calculus :-)
|
... is inherent in SKI combinator calculus :-)
|
||||||
@@ -87,38 +93,40 @@ and to satisfy IOCCC's size rule.
|
|||||||
* Short coding
|
* Short coding
|
||||||
|
|
||||||
These led to the good obfuscation.
|
These led to the good obfuscation.
|
||||||
See Spoiler section in detail, if you need.
|
|
||||||
|
See [Spoiler section](#spoiler) in detail, if you need.
|
||||||
|
|
||||||
### Limitation
|
### Limitation
|
||||||
|
|
||||||
This program supports only "Combinator-calculus style notation" of Lazy K.
|
This program supports only "Combinator-calculus style notation" of Lazy K.
|
||||||
"Unlambda style" and "Iota and Jot" style are not supported.
|
"Unlambda style" and "Iota and Jot" style are not supported.
|
||||||
Also, it requires a space between identifiers.
|
|
||||||
In short, use `(S K)` instead of `(SK)`, "\`sk", `**i*i*i*ii*i*i*ii`,
|
|
||||||
or `11111100011100`.
|
|
||||||
|
|
||||||
Huge memory may be required to compile the program
|
Also, it requires a space between identifiers. In short, use `(S K)` instead of
|
||||||
(about 300 MB on my machine).
|
`(SK)`, "\`sk", `**i*i*i*ii*i*i*ii`, or `11111100011100`.
|
||||||
|
|
||||||
In addition, there are some limitations (and workarounds)
|
Huge memory may be required to compile the program (about 300 MB on my machine).
|
||||||
mentioned in Spoiler section.
|
|
||||||
But I think it wouldn't matter only when you run the attached programs.
|
In addition, there are some limitations (and workarounds) mentioned in [Spoiler
|
||||||
|
section](#spoiler).
|
||||||
|
|
||||||
|
But I think it would only matter when you run the attached programs.
|
||||||
|
|
||||||
### Portability
|
### Portability
|
||||||
|
|
||||||
I confirmed that the program successfully worked
|
I confirmed that the program successfully worked with the following compilers:
|
||||||
with the following compilers.
|
|
||||||
|
|
||||||
* gcc 4.6.3 [Linux, Mac OS X]
|
* gcc 4.6.3 [Linux, Mac OS X]
|
||||||
* clang 3.0 [Linux, Mac OS X]
|
* clang 3.0 [Linux, Mac OS X]
|
||||||
* tcc 0.9.26 [Linux]
|
* tcc 0.9.26 [Linux]
|
||||||
* N.B. tcc 0.9.25 seems to have a bug and fails to run it.
|
- N.B. tcc 0.9.25 seems to have a bug and fails to run it.
|
||||||
|
|
||||||
Recent compilers with `-Wall -W -Wextra -pedantic` say nothing.
|
Recent compilers with `-Wall -W -Wextra -pedantic` say nothing.
|
||||||
|
|
||||||
gcc -Wall -W -Wextra -pedantic prog.c
|
```sh
|
||||||
clang -Wall -W -Wextra -pedantic prog.c
|
gcc -Wall -W -Wextra -pedantic prog.c
|
||||||
tcc -Wall -W -Wextra -pedantic prog.c
|
clang -Wall -W -Wextra -pedantic prog.c
|
||||||
|
tcc -Wall -W -Wextra -pedantic prog.c
|
||||||
|
```
|
||||||
|
|
||||||
I think it will work on almost all platforms. I confirmed:
|
I think it will work on almost all platforms. I confirmed:
|
||||||
|
|
||||||
@@ -126,83 +134,89 @@ I think it will work on almost all platforms. I confirmed:
|
|||||||
* Mac OS X
|
* Mac OS X
|
||||||
* Raspberry PI
|
* Raspberry PI
|
||||||
|
|
||||||
To check whether the program is specified on the command line
|
To check whether the program is specified on the command line or included from
|
||||||
or included from another source file,
|
another source file, the program uses a predefined macro `__INCLUDE_LEVEL__`.
|
||||||
the program uses a predefined macro `__INCLUDE_LEVEL__`.
|
It is a gcc extension, and also supported by clang. If your compiler does not
|
||||||
It is a gcc extention, and also supported by clang.
|
support it, you cannot use the program as a library. But you can compile and
|
||||||
If your compiler does not support it,
|
run it as a standalone program, at least. In fact, tcc does not support the
|
||||||
you cannot use the program as a library.
|
macro, but does work.
|
||||||
But you can compile and run it as a standalone program, at least.
|
|
||||||
In fact, tcc does not support the macro, but does work.
|
|
||||||
|
|
||||||
### Spoiler (rot13)
|
|
||||||
|
|
||||||
Gur jubyr cebtenz vf vagrecergrq ol znpeb rkcnafvba.
|
|
||||||
Sbe rknzcyr, `F (X V)` vf genafyngrq gb n abezny P pbqr, `(f)((x)(v))`,
|
|
||||||
be fvzcyl `f(x(v))`.
|
|
||||||
Guvf rkcerffvba ergheaf na nofgenpg flagnk gerr,
|
|
||||||
naq `cebt.p` rinyhngrf vg.
|
|
||||||
Guvf cebtenz hfrf irel fvzcyr "grez erjevgvat" nccebnpu
|
|
||||||
sbe rinyhngvat FXV pbzovangbe pnyphyhf.
|
|
||||||
Gur erjevgvat ehyrf ner fubja va gur funcr bs gur pbqr.
|
|
||||||
|
|
||||||
#### Nohfr bs shapgvba cbvagref
|
|
||||||
|
|
||||||
Pbafvqre n frdhrapr bs shapgvba nccyvpngvbaf va P:
|
|
||||||
|
|
||||||
f(k)(k)(k)(k)...
|
|
||||||
|
|
||||||
Jung glcr fubhyq `f` unir?
|
|
||||||
Hasbeghangryl, P qbrf *abg* cebivqr n "erphefvir glcr",
|
|
||||||
fhpu nf `glcrqrs s (*s)();`.
|
|
||||||
Fb, V hfrq n urniyl arfgrq shapgvba cbvagre glcr:
|
|
||||||
|
|
||||||
glcrqrs ibvq *(*(*(*...(*(s))()...)())())
|
|
||||||
|
|
||||||
Guvf yvzvgf ubj znal nethzragf bar shapgvba pna or pbafrphgviryl nccyvrq gb.
|
|
||||||
Ohg lbh pna vapernfr gur ahzore ol gjrnxvat gur qrsvavgvba bs znpeb `c`.
|
|
||||||
|
|
||||||
|
|
||||||
#### Pbqr qhcyvpngvba ol znpebf
|
### Spoiler
|
||||||
|
|
||||||
Arkg, jr arrq gb rapbqr "pybfherf".
|
The whole program is interpreted by macro expansion.
|
||||||
N pybfher vf n shapgvba gbtrgure jvgu na raivebazrag
|
For example, `S (K I)` is translated to a normal C code, `(s)((k)(i))`, or
|
||||||
juvpu vf n ersrerapr gb aba-ybpny inevnoyr.
|
simply `s(k(i))`.
|
||||||
|
|
||||||
Va guvf pnfr, jr arrq fbzrguvat gung:
|
This expression returns an abstract syntax tree, and [endoh1.c](endoh1.c)
|
||||||
|
evaluates it.
|
||||||
|
|
||||||
* vf pnyynoyr vgfrys,
|
This program uses a very simple "term rewriting" approach for evaluating SKI
|
||||||
* vagreanyyl cbffrffrf n ersrerapr gb nabgure pybfher nf n aba-ybpny inevnoyr, naq
|
combinator calculus. The rewriting rules are shown in the shape of the code.
|
||||||
* nccyvrf gur vagreany pybfher gb na nethzrag jura vg vf pnyyrq.
|
|
||||||
|
|
||||||
Ubjrire, ab glcr va P vf pnyynoyr naq unf n ersrerapr ng gur fnzr gvzr.
|
#### Abuse of function pointers
|
||||||
(Vg vf srnfvoyr ol qlanzvp pbqr trarengvba, ohg vg vf sne sebz cbegnoyr.)
|
|
||||||
|
|
||||||
Fb, V nqqerffrq guvf vffhr ol trarengvat znal shapgvba qrsvavgvbaf
|
Consider a sequence of function applications in C:
|
||||||
fgngvpnyyl ol (no)hfvat znpebf:
|
|
||||||
|
|
||||||
ibvq *k1; ibvq s1(ibvq *l) { erghea nccyl(k1, l); }
|
```c
|
||||||
ibvq *k2; ibvq s2(ibvq *l) { erghea nccyl(k2, l); }
|
s(x)(x)(x)(x)...
|
||||||
ibvq *k3; ibvq s3(ibvq *l) { erghea nccyl(k3, l); }
|
```
|
||||||
...
|
|
||||||
ibvq *(u[]) = { s1, s2, s3, ... }
|
|
||||||
|
|
||||||
naq ol nyybgvat rnpu bs gurz jura n pybfher vf arrqrq.
|
What type should `s` have? Unfortunately, C does *not* provide a "recursive
|
||||||
|
type", such as `typedef f (*f)();`.
|
||||||
|
|
||||||
Guvf yrnqf gb nabgure yvzvgngvba:
|
So, I used a heavily nested function pointer type:
|
||||||
gur ahzore bs cer-qrsvarq pybfherf yvzvgf
|
|
||||||
ubj znal vaqragvsvref (`F` `X` `V`) bar cebtenz pna hfr.
|
```c
|
||||||
Ohg lbh pna vapernfr guvf ahzore
|
typedef void *(*(*(*...(*(f))()...)())())
|
||||||
ol gjrnxvat gur qrsvavgvba bs znpeb `N` `O` `P` naq `Q`.
|
```
|
||||||
(Abgr gung pybfherf ner nyybggrq bayl jura cnefvat;
|
|
||||||
nsgre gur rinyhngvba fgnegf, "bhg bs pybfher" pnaabg bpphe.)
|
This limits how many arguments one function can be consecutively applied to.
|
||||||
|
But you can increase the number by tweaking the definition of macro `p`.
|
||||||
|
|
||||||
|
|
||||||
|
#### Code duplication by macros
|
||||||
|
|
||||||
|
Next, we need to encode "closures". A closure is a function together with an
|
||||||
|
environment which is a reference to non-local variable.
|
||||||
|
|
||||||
|
In this case, we need something that:
|
||||||
|
|
||||||
|
* is callable itself,
|
||||||
|
* internally possesses a reference to another closure as a non-local variable,
|
||||||
|
and
|
||||||
|
* applies the internal closure to an argument when it is called.
|
||||||
|
|
||||||
|
However, no type in C is callable and has a reference at the same time.
|
||||||
|
(It is feasible by dynamic code generation, but it is far from portable.)
|
||||||
|
|
||||||
|
So, I addressed this issue by generating many function definitions
|
||||||
|
statically by (ab)using macros:
|
||||||
|
|
||||||
|
```c
|
||||||
|
void *x1; void f1(void *y) { return apply(x1, y); }
|
||||||
|
void *x2; void f2(void *y) { return apply(x2, y); }
|
||||||
|
void *x3; void f3(void *y) { return apply(x3, y); }
|
||||||
|
...
|
||||||
|
void *(h[]) = { f1, f2, f3, ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
and by allotting each of them when a closure is needed.
|
||||||
|
|
||||||
|
This leads to another limitation: the number of pre-defined closures limits how
|
||||||
|
many identifiers (`S` `K` `I`) one program can use.
|
||||||
|
|
||||||
|
But you can increase this number by tweaking the definition of macro `A` `B` `C`
|
||||||
|
and `D`. (Note that closures are allotted only when parsing; after the
|
||||||
|
evaluation starts, "out of closure" cannot occur.)
|
||||||
|
|
||||||
|
#### Short cording
|
||||||
|
|
||||||
|
This margin is too narrow to contain a detailed explanation. Instead, I just
|
||||||
|
ask you one question. Can you tell what `v
|
||||||
|
s[]={0,0,s+6,s+2,s+4,s,s+3,s+5,s+1};` is? I found this by using SMT solver.
|
||||||
|
|
||||||
#### Fubeg pbeqvat
|
|
||||||
|
|
||||||
Guvf znetva vf gbb aneebj gb pbagnva n qrgnvyrq rkcynangvba.
|
|
||||||
Vafgrnq, V whfg nfx lbh bar dhrfgvba.
|
|
||||||
Pna lbh gryy jung `i f[]={0,0,f+6,f+2,f+4,f,f+3,f+5,f+1};` vf?
|
|
||||||
V sbhaq guvf ol hfvat FZG fbyire.
|
|
||||||
|
|
||||||
## Copyright and CC BY-SA 4.0 License:
|
## Copyright and CC BY-SA 4.0 License:
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user