Merge pull request #369 from xexyl/1991-brnstnd
Updates and fixes in 1991/brnstnd
This commit is contained in:
2 files changed
+128
-129
No files matched your search
+88
-85
@@ -1,11 +1,13 @@
|
||||
# Best Of Show:
|
||||
|
||||
Daniel J. Bernstein
|
||||
New York University
|
||||
Daniel J. Bernstein
|
||||
New York University
|
||||
|
||||
## To build:
|
||||
|
||||
make all
|
||||
```sh
|
||||
make all
|
||||
```
|
||||
|
||||
[Cody Boone Ferguson](/winners.html#Cody_Boone_Ferguson) fixed this for modern
|
||||
systems. There were two invalid operands to binary expression (`char *` and
|
||||
@@ -18,20 +20,22 @@ were resolved with the comma operator. Thank you Cody for your assistance!
|
||||
|
||||
## Try:
|
||||
|
||||
./brnstnd < sorta.i2+2
|
||||
./brnstnd < sorta.iwhosort
|
||||
```sh
|
||||
./brnstnd < sorta.i2+2
|
||||
./brnstnd < sorta.iwhosort
|
||||
```
|
||||
|
||||
See also the other `sorta.*` files.
|
||||
|
||||
|
||||
## Judges' comments
|
||||
## Judges' comments:
|
||||
|
||||
For info on more examples, read the sorta.README file.
|
||||
For info on more examples, read the [sorta.README.md](sorta.README.md) file.
|
||||
|
||||
The author wished to win the "most useful program" award and
|
||||
documented this in the source code. The judges were unmoved
|
||||
by this blatant attempt to influence the contest and rejected
|
||||
this idea... so we gave it the Best of Show award instead!!
|
||||
The author wished to win the "most useful program" award and documented this in
|
||||
the source code. The judges were unmoved by this blatant attempt to influence
|
||||
the contest and rejected this idea... so we gave it the `Best of Show`
|
||||
instead!!
|
||||
|
||||
NOTE: One should remove the final trailing newline to obtain the
|
||||
original source file. This step is not needed to compile
|
||||
@@ -40,7 +44,7 @@ this entry.
|
||||
To have a chance to compile under a modern CPP, we had to
|
||||
replace `#D` with `#define`.
|
||||
|
||||
## Author's comments
|
||||
## Author's comments:
|
||||
|
||||
This is an interpreter for the programming language SORTA, a systems
|
||||
and numerical programming language with features sorta from C, sorta
|
||||
@@ -59,104 +63,103 @@ instance, not to mention infinite-depth tail recursion.
|
||||
|
||||
SORTA ignores its arguments (though it makes them available to the
|
||||
script); it takes all commands, character by character, from its
|
||||
standard input. Unrecognized commands are repeated with a ?.
|
||||
standard input. Unrecognized commands are repeated with a '?'.
|
||||
|
||||
SORTA maintains an ``i stack'' for integers and an ``s stack'' for
|
||||
strings. It also keeps track of ``programs,'' one for each character.
|
||||
Operations are silently ignored if the relevant stacks are too low,
|
||||
except as noted for s, S, and !. If the i stack or s stack (or the stack
|
||||
of buffered macro commands) grows too high, SORTA will exit silently
|
||||
with exit code 2. (Compile parameter -Do=250 controls what ``too high''
|
||||
means; you should not make o larger than 250.) I don't think it's
|
||||
possible to crash the interpreter.
|
||||
SORTA maintains an `i stack` for integers and an `s stack` for strings. It also
|
||||
keeps track of `programs`, one for each character. Operations are silently
|
||||
ignored if the relevant stacks are too low, except as noted for `s`, `S`, and
|
||||
`!`. If the `i stack` or `s stack` (or the stack of buffered macro commands)
|
||||
grows too high, SORTA will exit silently with exit code 2. (Compile parameter
|
||||
`-Do=250` controls what 'too high' means; you should not make `o` larger than
|
||||
250.) I don't think it's possible to crash the interpreter.
|
||||
|
||||
Here, then, is the SORTA programming language. All non-digits delimit
|
||||
numeric constants. Spaces and newlines are ignored except as numeric
|
||||
delimiters.
|
||||
|
||||
Basic operations:
|
||||
### Basic operations:
|
||||
|
||||
q: quit
|
||||
number: push that number on top of the i stack
|
||||
"string": push that string on top of the s stack, no length limit
|
||||
[string]: push that string on top of the s stack, no length limit
|
||||
#: make top of i stack non-destructively in ASCII, push result onto s stack
|
||||
`: print top of s stack non-destructively
|
||||
$: print top of s stack non-destructively without newline
|
||||
d: drop (pop) top of i stack
|
||||
D: dup (duplicate) top of i stack
|
||||
': dup top of s stack
|
||||
s: pop top of i stack. If it is n, swap (n+1)th-top of i stack with top.
|
||||
1s, for example, swaps the top two elements; 2s swaps the top with
|
||||
the third down; etc. This always pops the top of the i stack, even
|
||||
upon failure.
|
||||
S: pop top of i stack, then act just like s but upon s stack
|
||||
l: pop top of s stack, push its length onto i stack
|
||||
a: push argc onto i stack
|
||||
A: pop top of i stack, push argv[i] on top of s stack
|
||||
T: concatenate top two elements of s stack
|
||||
_: negate top of i stack
|
||||
+: pop top two elements of i stack, add, push back sum
|
||||
*: pop top two elements of i stack, multiply, push back product
|
||||
/: pop top two elements of i stack, divide, push back quotient
|
||||
>: pop top two elements of i stack, compare, push back 1 or 0 as in C
|
||||
&: pop top two elements of i stack, nand, push back bitwise result
|
||||
Note that nand is sufficient to construct all bitwise operations,
|
||||
as demonstrated by icalc below.
|
||||
- `q`: quit
|
||||
- `number`: push that number on top of the i stack
|
||||
- `"string"`: push that string on top of the s stack, no length limit
|
||||
- `[string]`: push that string on top of the s stack, no length limit
|
||||
- `#`: make top of i stack non-destructively in ASCII, push result onto s stack
|
||||
- ` ` `: print top of s stack non-destructively
|
||||
- `$`: print top of s stack non-destructively without newline
|
||||
- `d`: drop (pop) top of i stack
|
||||
- `D`: dup (duplicate) top of i stack
|
||||
- `'`: dup top of s stack
|
||||
- `s`: pop top of i stack. If it is `n`, swap `(n+1)`th-top of `i` stack with top.
|
||||
`1s`, for example, swaps the top two elements; `2s` swaps the top with the
|
||||
third down; etc. This always pops the top of the `i stack`, even upon
|
||||
failure.
|
||||
- `S`: pop top of `i stack`, then act just like `s` but upon `s stack`
|
||||
- `l`: pop top of `s stack`, push its length onto `i stack`
|
||||
- `a`: push argc onto i stack
|
||||
- `A`: pop top of `i stack`, push `argv[i]` on top of `s stack`
|
||||
- `T`: concatenate top two elements of `s stack`
|
||||
- ` _ `: negate top of i stack
|
||||
- `+`: pop top two elements of i stack, add, push back sum
|
||||
- ` * `: pop top two elements of i stack, multiply, push back product
|
||||
- `/`: pop top two elements of i stack, divide, push back quotient
|
||||
- `>`: pop top two elements of i stack, compare, push back 1 or 0 as in C
|
||||
- `&`: pop top two elements of i stack, nand, push back bitwise result
|
||||
Note that nand is sufficient to construct all bitwise operations, as
|
||||
demonstrated by icalc below.
|
||||
|
||||
System operations:
|
||||
### System operations:
|
||||
|
||||
o: pop top of s stack and top two of i stack; open(s1,i2,i1); leave
|
||||
result on i stack
|
||||
O: pop top of i stack, close() it
|
||||
u: pop top of i stack, dup() it, push result back on
|
||||
F: fork(), push result on i stack. This is not always safe while SORTA
|
||||
is reading keyboard input or a script, as the forked programs share
|
||||
file descriptors. It is always safe inside a program (see below).
|
||||
P: pipe(), push two ends <p0> <p1> onto i stack. In case of trouble,
|
||||
push <?> <-1> onto i stack.
|
||||
w: wait(), push successful result (or -1 if no children) on i stack
|
||||
!: execvp() top of the s stack. Arguments are the next things down on
|
||||
the s stack, in reverse order from popping; the first character of
|
||||
each string is lopped off. There must be an empty string somewhere
|
||||
in the s stack to terminate the argument list. If the execve()
|
||||
fails, any operation involving current members of the s stack has
|
||||
undefined effects, and -1 is pushed onto the i stack.
|
||||
- `o`: pop top of `s stack` and top two of `i stack`; `open(s1,i2,i1);` leave
|
||||
result on `i stack`
|
||||
- `O`: pop top of `i stack`, `close()` it
|
||||
- `u`: pop top of `i stack`, `dup()` it, push result back on
|
||||
- `F`: `fork()`, push result on `i stack`. This is not always safe while SORTA is
|
||||
reading keyboard input or a script, as the forked programs share file
|
||||
descriptors. It is always safe inside a program (see below).
|
||||
- `P`: `pipe()`, push two ends `<p0> <p1>` onto `i stack`. In case of trouble,
|
||||
`push <?> <-1>` onto `i stack`.
|
||||
- `w`: `wait()`, push successful result (or -1 if no children) on `i stack`
|
||||
- `!`: `execvp()` top of the `s stack`. Arguments are the next things down on
|
||||
the s stack, in reverse order from popping; the first character of
|
||||
each string is lopped off. There must be an empty string somewhere
|
||||
in the s stack to terminate the argument list. If the execve()
|
||||
fails, any operation involving current members of the s stack has
|
||||
undefined effects, and -1 is pushed onto the i stack.
|
||||
|
||||
High-level language operations:
|
||||
### High-level language operations:
|
||||
|
||||
:x: copy the top of the s stack into a program labeled by character x
|
||||
=x: pop the top of the i stack. If it was nonzero, execute the program
|
||||
labeled by character x. Note that a digit at the end of a program
|
||||
will merge with any digits after =x; in that case you usually want
|
||||
to add a space at the end of the program.
|
||||
- :x: copy the top of the s stack into a program labeled by character x
|
||||
- =x: pop the top of the i stack. If it was nonzero, execute the program
|
||||
labeled by character x. Note that a digit at the end of a program
|
||||
will merge with any digits after =x; in that case you usually want
|
||||
to add a space at the end of the program.
|
||||
|
||||
Common idioms: To drop the top of the s stack, ld. To do a <, 1s>. To
|
||||
unconditionally execute the program labeled by character x, 1=x (or any
|
||||
nonzero number followed by =x). To print the top of the i stack
|
||||
non-destructively, #` (or #$ if you don't want the newline). To
|
||||
subtract, _+. To do mod or and or any of the other missing operations,
|
||||
non-destructively, `# ` ` (or `#$` if you don't want the newline). To
|
||||
subtract, `_+`. To do mod or and or any of the other missing operations,
|
||||
combine the available operations as illustrated below. To introduce a
|
||||
comment, [ this is a comment string which is promptly eliminated ]ld.
|
||||
comment, `[ this is a comment string which is promptly eliminated ]ld`.
|
||||
|
||||
SORTA's requirements: it wants fork(), execvp(), open(),
|
||||
close(), dup(), pipe(), and wait(), so it obviously won't even compile
|
||||
SORTA's requirements: it wants `fork()`, `execvp()`, `open()`,
|
||||
`close()`, `dup()`, `pipe()`, and `wait()`, so it obviously won't even compile
|
||||
on a non-UNIX machine. It also assumes that you have a size-256
|
||||
character set and that the characters between '0' and '9' are exactly
|
||||
character set and that the characters between `'0'` and `'9'` are exactly
|
||||
the digits in order. It does *not* depend on ASCII, despite the code
|
||||
appearance. Also note that SORTA does not attempt to declare malloc(),
|
||||
appearance. Also note that SORTA does not attempt to declare `malloc()`,
|
||||
so you will get some warnings about illegal pointer combinations. Also
|
||||
note that there are unreached statements in SORTA.
|
||||
note that there are unreachable statements in SORTA.
|
||||
|
||||
While the program source of course depends highly on #defines for
|
||||
While the program source of course depends highly on `#define`s for
|
||||
obfuscation, I like to think that this code has those little touches,
|
||||
those professionally sharpened edges that mark true software
|
||||
engineering. Observe, for instance, how i and s are the string and
|
||||
engineering. Observe, for instance, how `i` and `s` are the string and
|
||||
integer stacks respectively. It's these little things that make you
|
||||
feel at home in an otherwise utterly useless piece of code. They're
|
||||
what make an obfuscation work.
|
||||
|
||||
The character pool (``I'' after cpp) makes it rather painful to
|
||||
The character pool (`I` after cpp) makes it rather painful to
|
||||
see the effect of commands at a glance. I can just imagine people
|
||||
spending hours bouncing between the pool and the rest of the code,
|
||||
or accidentally changing the pool without realizing its importance.
|
||||
@@ -173,8 +176,8 @@ everywhere will make people think of ASCII, even though the code is
|
||||
not ASCII-dependent. What's your first thought when you see
|
||||
several arrays[256]? This is pretty standard obfuscation otherwise.
|
||||
I like the way that unbalanced macro braces can throw off the
|
||||
reader in ``if(c>0){y+1]=z[c];'', and I had fun covering the
|
||||
alphabet with #defines, but that's nothing special.
|
||||
reader in `if(c>0){y+1]=z[c];`, and I had fun covering the
|
||||
alphabet with `#define`s, but that's nothing special.
|
||||
|
||||
Possible future extensions to SORTA include string extraction and
|
||||
matching, reading from files into strings, and encrypting the string
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
The 'sorta' scripts described below can sorta be found in this directory.
|
||||
The `sorta` scripts described below can sorta be found in this directory.
|
||||
|
||||
# [sorta.i2+2](sorta.i2+2)
|
||||
|
||||
sorta.i2+2
|
||||
----------
|
||||
|
||||
2 2+#` [ about as basic as you can get ]ld
|
||||
|
||||
|
||||
This script takes 2, adds 2 to it, and prints the result (with a
|
||||
newline). I get 5 on the hand-held Sinclair under ZYNIX. Let me know if
|
||||
your machine produces a different result so that I can send you patches.
|
||||
|
||||
|
||||
sorta.iarg0
|
||||
-----------
|
||||
# [sorta.iarg0](sorta.iarg0)
|
||||
|
||||
0A` [ take arg 0, print it ]
|
||||
|
||||
This is self-explanatory.
|
||||
|
||||
|
||||
sorta.icalc
|
||||
-----------
|
||||
# [sorta.icalc](sorta.icalc)
|
||||
|
||||
|
||||
|
||||
[D2sD3s]:Dld [ input m n, output m n m n ]ld
|
||||
@@ -56,18 +56,18 @@ sorta.icalc
|
||||
1=D 1s# " | "T 1s#T " is "T D&1sD&& #T$ld " (" 1=2 ")"T `ld
|
||||
1=D 1s# " ^ "T 1s#T " is "T 1=D&2sD&1sD&&&D& #T$ld " (" 1=2 ")"T `ld
|
||||
|
||||
icalc shows off SORTA's understanding of integer arithmetic. You can run
|
||||
`icalc` shows off SORTA's understanding of integer arithmetic. You can run
|
||||
it with up to two arguments; the arguments have default values of 27182
|
||||
and 3145. Notice that sorta doesn't have a language primitive for
|
||||
converting a string to a number, yet icalc is performing arithmetic on
|
||||
its arguments. Can you see how? (Hint: Try sorta "2 2+" 3 < sorta.icalc.
|
||||
Look at program A.) icalc also provides a useful binary output routine,
|
||||
and if you can't remember offhand that D2sD3s&2sD&1sD&&&D& means xor,
|
||||
converting a string to a number, yet `icalc` is performing arithmetic on
|
||||
its arguments. Can you see how? (Hint: Try `./brnstnd "2 2+" 3 < sorta.icalc`.
|
||||
Look at program A.) `icalc` also provides a useful binary output routine,
|
||||
and if you can't remember offhand that `D2sD3s&2sD&1sD&&&D&` means xor,
|
||||
you can just look it up here.
|
||||
|
||||
|
||||
sorta.idup
|
||||
-----------
|
||||
# [sorta.idup](sorta.idup)
|
||||
|
||||
|
||||
[ld#1_+1=y]:x
|
||||
[uD1+=x]:y
|
||||
@@ -77,8 +77,7 @@ This script prints out the maximum file descriptor available from
|
||||
repeated dup()s---usually NOFILE - 1. Can you see how it works?
|
||||
|
||||
|
||||
sorta.iecho
|
||||
-----------
|
||||
# [sorta.iecho](sorta.iecho)
|
||||
|
||||
[" "$ld]:sld [ print a space ]ld
|
||||
[1s1+DA$ld1s1_+D=sD=x]:xld [ given n (argc-n), print args ]ld
|
||||
@@ -89,8 +88,8 @@ interpretation. (Note that, unlike some shells' echo, /bin/echo prints a
|
||||
newline even with no arguments.) Try sorta foo bar < iecho.
|
||||
|
||||
|
||||
sorta.ifact1
|
||||
------------
|
||||
# [sorta.ifact1](sorta.ifact1)
|
||||
|
||||
|
||||
1#`ld2*#`ld3*#`ld4*#`ld5*#`ld6*#`ld7*#`ld8*#`ld9*#`ld10*#`ld
|
||||
|
||||
@@ -98,32 +97,31 @@ This is a very straightforward factorial program: Take 1, print it,
|
||||
multiply by 2, print it, multiply by 3, print it, etc.
|
||||
|
||||
|
||||
sorta.ifact2
|
||||
------------
|
||||
# [sorta.ifact2](sorta.ifact2)
|
||||
|
||||
|
||||
[1+D2s*#`ld1s]:f
|
||||
1 0
|
||||
1=f 1=f 1=f 1=f 1=f 1=f 1=f 1=f 1=f 1=f
|
||||
|
||||
ifact1 does a lot of repeated work. ifact2 takes advantage of this by
|
||||
keeping the current n! n on the stack. Each call to routine f (1=f)
|
||||
`ifact1` does a lot of repeated work. `ifact2` takes advantage of this by
|
||||
keeping the current `n!` n on the stack. Each call to routine f (1=f)
|
||||
increments n by 1 and prints out the factorial.
|
||||
|
||||
|
||||
sorta.ifact3
|
||||
------------
|
||||
# [sorta.ifact3](sorta.ifact3)
|
||||
|
||||
|
||||
[1+D2s*#`ld1s]:f [ given (n-1)! (n-1), print n!, put n! n ]
|
||||
[2s1=f2s1_+D0>=x]:x [ given n n! x, print (n+1)! through (n+x)! ]
|
||||
0 1 10 1=x
|
||||
|
||||
Finally, ifact3 removes all the redundancy from ifact2, and exhibits
|
||||
Finally, `ifact3` removes all the redundancy from `ifact2`, and exhibits
|
||||
proper documentation of stack routines. You can replace the 10 by
|
||||
anything in this version.
|
||||
|
||||
|
||||
sorta.iio
|
||||
---------
|
||||
# [sorta.iio](sorta.iio)
|
||||
|
||||
[ a simple example of interpreting code on the fly ]ld
|
||||
|
||||
@@ -132,8 +130,8 @@ sorta.iio
|
||||
Notice that "2 2+" is a literal string inside the program.
|
||||
|
||||
|
||||
sorta.irot13
|
||||
------------
|
||||
# [sorta.irot13](sorta.irot13)
|
||||
|
||||
|
||||
"[] [.tr] "
|
||||
["]".[a-m][n-z][A-M][N-Z]"[" ]TTT ["]".[n-z][a-m][N-Z][A-M]"[" ]TTT
|
||||
@@ -145,17 +143,17 @@ sorta.irot13
|
||||
0 'o D0 1s>=hld
|
||||
0Ou1=x [ in a more complex spawner, this would be done under a fork ]ld
|
||||
|
||||
Despite its name, irot13 is meant to show how to exec a program with
|
||||
redirection from within sorta. You run it as sorta blah < irot13; it
|
||||
opens blah and runs a (BSD-style) tr command to rotate all letters by 13
|
||||
spots. 1A 0 0 o opens argv[1]; the ' keeps the filename around for
|
||||
subroutine h to print a nice error message if necessary. 0Ou closes
|
||||
stdout and replaces it with the new descriptor. Finally, subroutine x
|
||||
executes the tr.
|
||||
Despite its name, `irot13` is meant to show how to exec a program with
|
||||
redirection from within sorta. You run it as `./brnstnd blah < irot13`; it
|
||||
opens `blah` and runs a (BSD-style) `tr` command to rotate all letters by 13
|
||||
spots. `1A 0 0 o` opens `argv[1]`; the ` ' ` keeps the filename around for
|
||||
subroutine `h` to print a nice error message if necessary. `0Ou` closes
|
||||
stdout and replaces it with the new descriptor. Finally, subroutine `x`
|
||||
executes the `tr`.
|
||||
|
||||
|
||||
sorta.isleep
|
||||
------------
|
||||
# [sorta.isleep](sorta.isleep)
|
||||
|
||||
|
||||
["" "+sleep" "+5" "sleep" !q]:S [F0>1_+=S]:s
|
||||
"forking sleep 5..."`ld
|
||||
@@ -167,22 +165,20 @@ sorta.isleep
|
||||
This script illustrates the classic fork-exec-wait.
|
||||
|
||||
|
||||
sorta.itailrec
|
||||
--------------
|
||||
# [sorta.itailrec](sorta.itailrec)
|
||||
|
||||
[3*#" "T$ldD=f]:f
|
||||
1=f
|
||||
|
||||
The sole purpose of this script is to prove that sorta can do infinite
|
||||
tail recursion (in this case, in program f). Do sorta < itailrec | more
|
||||
tail recursion (in this case, in program `f`). Do `./brnstnd < itailrec | more`
|
||||
and watch the powers of 3 (mod your computer's word size) flow by.
|
||||
|
||||
|
||||
sorta.iwhosort
|
||||
--------------
|
||||
# [sorta.iwhosort](sorta.iwhosort)
|
||||
|
||||
[0Ou "" "+-u" "sort" !q]:S [F0>1_+=S]:s
|
||||
[1Ou "" "+-u" "who" !q]:W [F0>1_+=W]:w
|
||||
P1=wO1=sOww
|
||||
|
||||
This final example is a simple pipeline, who | sort.
|
||||
This final example is a simple pipeline, `who | sort`.
|
||||
Reference in new issue
Block a user