diff --git a/doc/add.doc.txt b/doc/add.doc.txt new file mode 100755 index 0000000..eb80a13 --- /dev/null +++ b/doc/add.doc.txt @@ -0,0 +1,10 @@ +There are two versions of this program: +asciiadd.w: reads two characters, adds their ASCII values, and prints them out +add.w: like above, but treats 0 as origin, so 1+2 is 3 rather than c + +Note that add.w uses the 0 in the top-left corner (which is operationally +equivalent to the star used elsewhere) for the origin. This character can be +changed to an arbitrary one, but 0 has been chosen because it gives the most +intuitive effect. + +See cat.doc for general Wierd notes. diff --git a/doc/cat.doc.txt b/doc/cat.doc.txt new file mode 100755 index 0000000..52a86be --- /dev/null +++ b/doc/cat.doc.txt @@ -0,0 +1,85 @@ +There are several versions of this program: +cat1.w: reads one character and write it out, no check for EOF +cat2.w: pumps input to output, no check for EOF (makes use of the fact that + "conditionals" without any stack just continue going, although it + would be more logical to terminate the program with an error message) +cat3.w: reads one character, and if it isn't EOF, dump it out again +cat4.w: pumps input to output until EOF is reached + +Versions 3 and 4 implicitly assume that EOF is -1. Due to how hard it is to +change a Wierd program once it has been wired, I, with this very sentence, +define that Wierd programs will see EOF as -1, even on computers that normally +use a different method. + +Versions 3 and 4 make use of the getput operator, which selects which +operatation to perform opposite from the specifications. Since neither I nor +anybody else (like the writer of hello.w) wishes to change our programs, I +recommend leaving the interpreter alone and changing the specifications. Also, +the angles work the opposite way (the first example in the specifications is +treated by the interpreter as 315 degrees). Again, I recommend changing the +specifications. + +The only true use of conditionals is in the EOF test candle (or if you prefer, +dynamite) of versions 3 and 4, although fake ones appear in versions 2 and 4 +with the top-of-stack known to be zero just to turn the execution around. + +It is also interesting to note that the loop to continue reading after the +first character seems like a big change but takes barely any space (although it +does cheat by temporarily creating a new thread which soon dies out), while +testing for EOF seems like a small change (after all, only one out of 256 input +characters will be affected) but takes a very long wire, primarily because the +input character is needed twice - once to test for EOF, once to print it out - +and since there is no stack-duplication operator (not that there is any room +for it, though gap-sparking could be made to have a side effect) and so I must +simulate it by putting the character in (1,1) (those coordinates have been +chosen because they are the easiest to push on the stack), then getting it +twice. + +In cat4.w, the doomed thread (the one which appears when, quite literally, +looping, and soon vanishes again) might stumble across this character, but +Wierd has been designed in such a way that this can, at the worst, kill the +thread off one cycle earlier. This proves that it is more robust than normal +languages. In fact, the ONLY error condition possible in Wierd is if the +lop-left character is a space (although, as stated above, errors SHOULD also +appear for insufficient stack), and therefore Wierd is useful to teach +beginning programmers without getting them frustated about pages full of error +messages. Now if you're talking about bugs that aren't caught by the language +but just have the wrong effect, THOSE are easy. + +But although cat4.w is large, it's not a bad as hello.w. One starts +understanding why many programs use external data files. They not only +increases modifiability (for example, the program does not have to be changed +if you choose to use a different languange for the output), but also +significantly decrease program size. + +This gap jumping is annoying. I never yet ran into any need for it (if there +is nothing around that the program can go to instead, why not just fill the gap +with stars?), but sometimes the interpreter tries to gap jump when I want it to +kill the thread (which forces me to make the program LARGER to add space so no +gap jump occurs, which is the opposite of what it was meant to do), and worse, +I cannot see how it chooses when and where to jump. I recommend that the whole +misfeature be removed. By the way, according the the manual, "programs beyond +a certain complexity might simply be impossible to write, and would at least be +extremely impractical". If they are impossible, gap jumping will not help (see +above question). If practicality is an issue, I should be programming C, not +Wierd. + +A more severe problem is that check-for-zero is the only available conditional. +This means that if a program wants to check, for exameple, if the top of the +stack is less than zero, then all 32768 options must be scanned. Changing this +would imply having to change existing programs, but the problem is severe +enough that this is a necessary evil. The earlier, the better (before even +more programs use the outdated version). + +Although I do not do so myself, it is possible to include in-program comments +by including text in the program, either as the wire (with underscores +replacing spaces) or at a nonreachable distance from the program. + +Finally, Wierd programs (or rather wires) are a good example of what sould NOT +be made with a text editor or anything remotely similar to it. In fact, it +requires a special "wire editor" which can, as one of its basic operations, +stretch wires, taking any knotted wires along, and as another, cutting off +wires for later use and then place them again in a possibly rotated form. It +is also useful if it would draw the angles next to the turns. Sometime in the +future, when I have a lot of free time and feel excessively insane, I might try +it. Don't hold your breath, though. diff --git a/doc/count.doc.txt b/doc/count.doc.txt new file mode 100755 index 0000000..b957d3c --- /dev/null +++ b/doc/count.doc.txt @@ -0,0 +1,37 @@ +This program prints sequential ASCII characters, starting with the character in +(1,1) and ending with the character in (2,2). Currently, it starts with 0 and +ends with 9, so it counts through all digits. A not-so-well-formatted table of +all printable ASCII characters may be obtained by starting with ! and ending +with ~. The former ascii.w (not by me) also tries to print non-printable +characters, and doesn't automatically stop (beep beep beep beep beep beep beep +beep beep beep beep beep beep ad nauseum), so my version is fully superior. +Space may not be used as endpoint (because the endpoints must be parsed as code +once), but it may appear as intermediate value. + +The basic operation is: +0. Get the current character and place it on the stack. +1. Write the top-of-stack (the current character) to the terminal. +2. Get the current character, which was lost from the stack in step 1, again. +3. Get the termination character and place it on the stack. +4. Subtract the two values on the stack. +5. If the result of step 4 was zero (the last character has just been printed), + quit the program. Otherwise, continue to step 6. +6. The stack is, once again, empty, so push the current character. +7. Increment the top of stack by dancing "left, left, right, left, right, + right", where all turns are 45 degrees. +8. Put the top of stack in the current character position. +9. With the stack empty again, go back to step 0 and repeat the whole process. + +The wire has many crossings, although all but one are harmless "bridges" which +do not change direction and just shrink the wire a bit by avoiding padding. + +To do: write a newline when done (this is not as easy in Wierd as it sounds, +because a newline cannot be entered as starting element for a cell), ask the +user to enter the start and end characters (inputting and remembering is not +that hard, but the nature of Wierd is such that adding anything at the +beginning requires a lot of work to get the rest right, see notes in cat.doc +about a Wierd editor). + +See cat.doc for general language notes. In contrast to what is said there, +this wire does have comments. Not many, but the important actions are probably +somewhere near where the comments say they are. diff --git a/doc/half.doc.txt b/doc/half.doc.txt new file mode 100755 index 0000000..550d217 --- /dev/null +++ b/doc/half.doc.txt @@ -0,0 +1,12 @@ +This is not a full program (although it can be run as such), but rather a +reusable subroutine. Therefore, input is taken directly from memory, and output +is stored back in memory and on the stack. There is no user interaction. + +Input: Numerator in (1,1) + Denominator is hard-coded 2 +Output: Quotient in (1,1) + Remainder pushed on the stack + Nothing else cluttered + +The algorithm takes O(numerator) time. There might exist a faster algorithm but +I don't know it. diff --git a/doc/hellow.txt b/doc/hellow.txt new file mode 100755 index 0000000..5e7fbe9 --- /dev/null +++ b/doc/hellow.txt @@ -0,0 +1,37 @@ +Just to illustrate exactly how sick I am, this took about three hours, +most of which was fidgeting with the editor to make the geometry work. + +OK, I cheated a bit by: + -- putting the text in the program + -- using the fact that functions with an empty stack are + essentially nops +And I didn't use anything nifty--no multithreading, genuine conditionals, +or anything of the like. Just brute force. I did, however, when I was +running out of space, "put" the coordinates in memory so I could just +"get" them later. + +Anyway, I found at least one not-quite-as-advertised "feature" in the +interpreter. The main one is that a zero on top of the stack does a put +of put and get, instead of the expected get, and a non-zero does get +instead of put. Since I was doing more getting than putting in the +program, I was actually kind of happy, but that means the spec and my +comments are wrong (unless you *really* want to declare the least-trivial +piece of Wierd code written to date *and* the only currently implemented +interpreter non-standard...). The others are really obscure things that I +can't figure out how to fix (the gap-jumping doesn't seem to quite work as +I understand it). + +Well, I could probably strip out another couple of bytes here and there, +but I just don't have the stomach for it today... + +Oh, Ben: If you're going to try a 99-Bottles program, I'd suggest +expanding the playing field, since this program clocked in at just under +128x128... + +Now I'm going to bang my head against a softer object for a while so I +don't notice the pain... + + --John + +P.S. Anybody besides me see the portrait of George Washington in the +program? No? I must be hallucinating, then...Oh, well... diff --git a/doc/loop.doc.txt b/doc/loop.doc.txt new file mode 100755 index 0000000..3b7d8b2 --- /dev/null +++ b/doc/loop.doc.txt @@ -0,0 +1,13 @@ +loop1.w is an infinite loop, which I have tried to write with minimal size (with +the restraints that any garbage put on the stack must be taken off again, but +that no popping intructions may be performed when there is still data on the +stack). It seems at first that the first row and column can be ommited, but +this doesn't work, and I suspect broken gap jumping to be the cause. + +loop2.w is also an infinite loop, but with the extra restraint that no threads +may be started. Notice how the horizontal line at the bottom can be entered by +two parallel paths. The leftmost path is used the first time, afterwards the +rightmost path is used. Both have the same angle, therefore the same effect. + +For more information on this broken gap jumping, and the language in general, +see cat.doc. diff --git a/doc/output.doc.txt b/doc/output.doc.txt new file mode 100755 index 0000000..b63a6ed --- /dev/null +++ b/doc/output.doc.txt @@ -0,0 +1,7 @@ +This program takes the number at (1,1) (input as the ASCII code of a character) +and prints it in binary (I would do decimal but that would require a divide by +ten subroutine), using the halving subroutine in half.w. Since it takes the +number direct;y from memory, this proram is not quite suitable for the end-user, +but rather it can be used as a subroutine in a yet larger program. + +The algorithm takes O(n) time, most of which is spent in the halving subroutine. diff --git a/doc/quine.doc.txt b/doc/quine.doc.txt new file mode 100755 index 0000000..8f70d68 --- /dev/null +++ b/doc/quine.doc.txt @@ -0,0 +1,56 @@ +Note: coordinates (x,y) here mean that y is the higher-on-stack (the row) and x +is below it (the column). The "stack" position is, of course, for purposes of +getput. + +(1,1) holds the end-of-line character. +(1,2) holds the current x coordinate. +(2,1) holds the current y coordinate. +(2,2) holds the beginnning-of-last-line-of-file character. + +This program is literally spaghetti code. It basically works by getting itself +with 135 degree turns, but it uses a few kludges to work around the data in the +top-left. Warning: trying to read the code may cause significant loss of +sanity. Oh wait a moment, you didn't have any in the first place. Ok, go +ahead, read the code, but don't blame me. + +Algorithm: +1. Get (1,1) and output (the first line must consist of just one character). +2. Push '\n'=10 (with "left, left, left, right, left, right, left, right, + left, right, left, right, left, right, left, right, left, right, left, + right, left, right, right") and output. +3. Put 3 in (2,1). +4. Put 1 in (1,2). +5. Get (1,3), which was equal to (2,1) at startup, and output. +6. Get (2,2) and output (the second line may have only have two characters). +7. Push '\n'=10 again (I didn't save it in step 2) and output. +8. Get (1,2). +9. Get (2,1). +10. Get the character specified by the two coordinates at the top of the stack. +11. Output it. +12. Get ((1,2),(2,1)) again. +13. Get (1,1) and compare. +14. If equal, go to step 19, otherwise continue with step 15. +15. Get (1,2). +16. Increment. +17. Put it back. +18. Return to step 8 and repeat the whole process. +19. This is a no-op step to remind you that you can only get here from the + conditional in step 14, not from step 18. +20. Put 1 in (1,2). +21. Push '\n'=10 (yes, I should save it somewhere, but place is limited, + particularly in this quine where special locations need special treatment) + and output. +22. Get (1,(2,1)). Note that this is the first character of the just-finished + line. +23. Get (2,2) and compare. +24. If equal, quit the program. If unequal, continue to step 25. +25. Get (2,1). +26. Increment. +27. Put it back. +28. Return to step 8. +And that's making it sound simpler than it really is. + +As it happens, the Wierd logo is 42 characters wide. Also, the asterisk, which +is the traditional wire character (although it is not treated specially by the +computer), has ASCII code 42. Is Wierd the answer to life, the universe, and +everything (or to anything, for that matter)? diff --git a/doc/random.doc.txt b/doc/random.doc.txt new file mode 100755 index 0000000..d5daf13 --- /dev/null +++ b/doc/random.doc.txt @@ -0,0 +1,39 @@ +Special data: +(1,1): The number so far (starts at 0). +(2,2): The negative of the bit to be set (starts at -1). + +(2,2) is doubled each time, and tested for zero. This is an overflow trick that +only works on binary computers, and if Wierd is ever ported to ternary +computers, this program will run indefinitely or at least much longer than it +should. + +In detail: +1. Put 0 in (1,1). +2. Put -1 in (2,2). +3. Get (2,2). +4. Push zero (dance "left left right") and subtract. This is necessary because + a condition can only follow a 45 or 315 turn (there needs to be space for + turning back). Following a 45 turn is quite useless though... +5. Test. If zero, go to step 17. Otherwise, continue to step 6. +6. Use a 45/315 split to randomly chose whether or not the bit is to be + included in the number. Depending on the result, either continue to step 7 + or jump to step 11. +7. Get (1,1). +8. Get (2,2). +9. Subtract (see why (2,2) is negated now?). +10. Put the result back in (1,1). +11. Get (2,2). +12. Push a zero through the "left left right" dance. +13. Get (2,2) again. +14. Subtract twice. +15. Put the result in (2,2). +16. Go back to step 3. +17. Get (3,3) and put in (2,2) (for output subroutine). +18. Output as a binary number, using output.w. +19. Done! + +Since the output routine is O(n), you should modify the interpreter to use a +smaller data type (short or even char) if you want the program to terminate this +year. Of course, since outputted number is random, you just might be lucky and +get a very small number which can be printed in less than a second. The random +number generation itself is quite fast. diff --git a/doc/wierdspec.txt b/doc/wierdspec.txt new file mode 100755 index 0000000..6d928a5 --- /dev/null +++ b/doc/wierdspec.txt @@ -0,0 +1,138 @@ +Specification for the Wierd Programming Language--A MicroFunge + +First, a Riddle: +Q: What do you get when you put three marginally-sane programmers on a + mailing list with the Befunge and BrainF*** programming languages? +A: You get BeF***, and then they get Wierd. +No, it's not really funny unless you already know what we're talking +about. So let's get you up to speed. + +History: +The now-defunct Befunge Mailing List once questioned if Befunge was too +"top-heavy" a language--that is, if its instruction set was needlessly +large. This, as one might expect (especially one reading this +specification), quickly degenerated into an argument of "my language is +smaller than yours." Ben Olmstead managed to trim down Befunge to a +lean seven instructions, dubbing the creation "BeF***". +Not to be outdone, it was quickly suggested by John Colagioia that +instructions, per se, be eliminated in favor of the "visual programming" +aspect of Befunge: The angles made by sequences of instructions. Such +a language, of course, would have a single "instruction" from the +syntactic standpoint, although it surely cheats by horribly abusing +the concept of Euclidean geometry. +Chris Pressey then jumped on it, created the angle-to-instruction +mapping, and christened the entire mess "Wierd"--a cross between the +words "weird" (which the language seemed to be) and "wired" (which +would describe the appearance of programs written in the language). + +The Basics: +Wierd, like Befunge, is a two-dimensional language with a rectalinear +grid of instructions which manipulate a system stack area. Where +Befunge has discrete, textual instructions, however, Wierd has a more +implicit instruction set, each instruction denoted, not by a symbol, but +by a configuration of symbols relative to the current direction of the +instruction pointer: the angle of deviation from "straight ahead" from +the IP's perpsective. That is, reading from left to right, the +following program contains a single instruction: + ****** + * + * + * +and the instruction is "read" as a 45-degree angle to the right. + +The Details: +A Wierd source file can be any file, as the language only distinguishes +between two symbols: Whitespace and everything else. A chain of non- +whitespace symbols makes a Wierd program. +Execution begins with the first character of the file (typically +considered to be the "upper left" of the program), and the initial +direction of the Instruction Pointer is down and to the right. This was +chosen to allow the programmer to get away from the corner as +efficiently as possible, which reduces the risk of the program becoming +trapped. +Newlines are defined in a system-dependant manner, though it can be +safely assumed on most systems that a carriage return and/or line feed +character delineates the end of each line and begins the next. +Execution then passes from character to character, in the specified +direction until a turn needs to be made. + +Inertia: +Like water, the Wierd IP takes the path of least resistance when +confronted with a choice. If the Wierd program branches, the IP will +always choose the path which is closest to the direction it is already +travelling. Therefore, the following code (assuming the IP starts at +the left, and travels to the right, like the above): + *********** + * + * + * +will stay on the top line, and never travel down the branch shown. +Should the IP be faced with two equally-likely choices, the branch +chosen by the interpreter is undefined, except for one condition which +will be treated later. + +(Though, as an implementation note, it should be pointed out that the + only existing interpreter at this time chooses the left-hand path. + Note that Wierd was not intended to be left-handed, specifically; this + is just how the coin showed during the design phase--literally). + +Gap-Jumping: +While the above is suitable for a fully-featured programming language, +it was quickly realized that programmers could easily (possibly +certainly) "paint themselves into a corner," where programs beyond a +certain complexity might simply be impossible to write, and would at +least be extremely impractical. +Therefore, in keeping with the pseudo-electronic motif, the Instruction +Pointer was given the ability to launch itself over a limited distance +of unused ("empty") programspace to attach to a new program segment. +This jumping, to keep it only used in emergency situations, is only +usable over a maximum distance of two cells in the program grid. +Gap-Jumping follows the same rules as typical movement, regarding +Inertia. That is, the IP will choose paths to jump into by how much of +a change of direction is required to reach it. +Note that jumping a gap into an isolated instruction, like in the +following code segment: + ***** * +is considered an error. + +The Instructions: +The Wierd instruction set is based on that of Befunge, though reduced to +a nearly-absurd minimalism, and with a certain amount of "stack abuse," +in addition to the abuses already heaped upon the program's geometry. +While the People for the Ethical Treatment of Data Structures threatened +to protest, they quickly realized that their acronym was not easily +pronounced, and retreated to debate the merits of a new name before +further business could be discussed. +The instructions, listed by angle, are: + 0 degrees NO: No operation, continue as normal. + 45 degrees P1: Push a data value of 1 onto the stack. + 90 degrees IF: Pop the stack. If the value is zero, continue + executing as normal. If the value is nonzero, + however, reverse direction. +135 degrees GP: Pop the stack. If the value is zero, pops the next + two items from the stack, retrieves (gets) the + value stored at the coordinates specified by these + values (x, then y), and push it onto the stack. If + the first value was nonzero, however, takes the + value stored below the coordinates on the stack, + and stores (puts) it at the coordinates. +180 degrees QU: Jump the gap, if possible. Otherwise, terminate. +225 degrees IO: Pop the stack. If the value is zero, read a + character from input, pushing it onto the stack. + If the value was nonzero, pop the stack, and print + the value to output as a character. +270 degrees IF: See 90 degrees. Included for flexibility. +315 degrees SB: Subtract the top of the stack from the value + beneath it, popping both values, and pushing the + result. +Note that pushing a 1 is the only form of direct data specification +available to the programmer, and subtraction is the only available +arithmetic operation. + +Concurrency: +The exception mentioned above to the "always choose a single path" rule +is when the two likeliest paths are at angles of both 90 and 270 +degrees--both forms of conditional. In such a case, rather than +choosing one or the other, both paths are chosen, and the IP is cloned +(including stack) to cover the other path. + diff --git a/eg/add.w b/eg/add.w new file mode 100755 index 0000000..18bcfed --- /dev/null +++ b/eg/add.w @@ -0,0 +1,27 @@ +0 ****** + * * * + * * * + * * * + ************* * + * + ****** * + * * * + * * * + * * * + ***** * * + * * * + * * * + * * * + * * * + * * * + * * * + * * * + **** * * + * * * + * * * + * * **** + * * * + * * + * * + ********* + diff --git a/eg/ascii.w b/eg/ascii.w new file mode 100755 index 0000000..8669ddd --- /dev/null +++ b/eg/ascii.w @@ -0,0 +1,24 @@ +* ***************** + * * * + * * ** * + * * * * * + * * * * * + * * * * * + * * * * * + * * * * * + * * *************** + * * * + * * * + * * * + * * * + * ********** * + * * * * *********** * + * * * * * * * * + * * ** * * * ****** * ** + * * * * ** * * * * ** + * *** ** * ** * * * + * * * * * * * + * * * * * * + * **** ***** * * + ** * * + **** diff --git a/eg/asciiadd.w b/eg/asciiadd.w new file mode 100755 index 0000000..ff68bed --- /dev/null +++ b/eg/asciiadd.w @@ -0,0 +1,27 @@ +* ****** + * * * + * * * + * * * + ************* * + * + * + ************** + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * **** + * * + * * + * * + ********* + diff --git a/eg/cat1.w b/eg/cat1.w new file mode 100755 index 0000000..237a5ea --- /dev/null +++ b/eg/cat1.w @@ -0,0 +1,9 @@ +* ****** + * * * + *** * + * + * + * * + ** + * + diff --git a/eg/cat2.w b/eg/cat2.w new file mode 100755 index 0000000..588eab7 --- /dev/null +++ b/eg/cat2.w @@ -0,0 +1,13 @@ +* ****** + * * * + * * * + * * * * +* *** * + ** * + * * + * * + * * + * * + * *** + ** * + diff --git a/eg/cat3.w b/eg/cat3.w new file mode 100755 index 0000000..50aecb0 --- /dev/null +++ b/eg/cat3.w @@ -0,0 +1,59 @@ +* ****** * + * * * * * + * * * * * + ********** * * * + * ** * + * * * + * * * + * * * + * * * + * * * + * * * + * * * + ******************************************** + * * * * + * * * * + * * * * + * * * * + * * * * + * * * * + * * * * + * * * * + * * * * + **************** * * * + * * * * * * + * * * * * * + * * * * * ***** + * * **** * * * + * * * * * * + * * * * * + * **** * * * + * * * * + * * * * + * * ************************ + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + ** + * + diff --git a/eg/cat4.w b/eg/cat4.w new file mode 100755 index 0000000..87267a2 --- /dev/null +++ b/eg/cat4.w @@ -0,0 +1,70 @@ +* + * + * + * + * + * + * + * + * + * + * + * ****** * + * * * * * * + * * * * * * + * ********** * * * + * * ** * + * * * * + * * * * + * * * * + * * * * + * * * * + * * * * + * * * * + * ****************************************** + * * * * * * + * * * * * * + * * * * * * + * * * * * * + * * * * * * + * ** * * * * + * * * * * * + * * * * * * + * * * * * + **************** * * * + * * * * * * + * * * * * * + * * * * * ***** + * * **** * * * + * * * * * * + * * * * * + * **** * * * + * * * * + * * * * + * * ************************ + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + ** + * + diff --git a/eg/count.w b/eg/count.w new file mode 100755 index 0000000..203f35a --- /dev/null +++ b/eg/count.w @@ -0,0 +1,73 @@ +0 + 9 + * + * + * + * PUT + * + * * + * ** + * * * + * * * + * * * + * * * + * ********************************** + * * * + * *** * + * * * + * * * + * * * + * * * + * ************** * + * * * * * * + * * * * * * + * * * * * + * * * * * *** * + * * * * GET ********** *** * * + * * * END * * *** * * + * * * * ** *** * + * * * * * *** * + * * *** * * * * + * * * * * * * + * * * ADD ONE * * * * + * * * * DONE? * * * + * * * * * * * + * * * * * * * + * * * *************** * * + * * * * * GET * * + * * * * ** * * + * * * * * * * * + * ********* * * * * * +* * * * * * + * GET AGAIN * * * * * + *** * * * * + * * * * * * + * * *** * * + * * * * + * *** * * * + * * ** * * + * * * * * + * * * * + * * WRITE * * + * * * * + * * * * + * * * * + * * * * + * * * * + * * * * + * * * * + * *** * * * + * * * * * * + *** * ** * + * * * + * * + * GET AGAIN * + * * + * ************************** + * * + * * + * * + * * + * * + * + diff --git a/eg/half.w b/eg/half.w new file mode 100755 index 0000000..2b8e6d1 --- /dev/null +++ b/eg/half.w @@ -0,0 +1,56 @@ ++ * + * ** + * * * + * * * ** + * **************************** * + * * * * * + * * * * * + * * * * * + * **RESTART*** * + * * * * + * **LOOP** * + ** * * * + * ** * * + ** * ** ** * + * ***** * * * * * + * * * * * * * + * * * *********** **** * + ********** * * * * + * * * * * ** + * * ******* * * * + ** * * * * *********** * + ** * * ** * * * * * + *** ** * * * * * * * + * * * *********** * * * * + * **DEHSINIF** * * ** * + * * * F * * + * * * I * ********* + * ** * N ** + * ** I + * * S + * H + * **E***** + *** * D * + * * * * + ********* * * * + * * * ** ** + * * * * + * * * * + * * * * + * * * + * * + * * ** + ******************* * + ** + * + * * + * * * + * * * + * * D + ** O + N + E + ! + * + * + diff --git a/eg/hello.w b/eg/hello.w new file mode 100755 index 0000000..cc4bb9f --- /dev/null +++ b/eg/hello.w @@ -0,0 +1,120 @@ +H ************************ ****************** ********** + e * * * * * * * + l * *** * ** * * * * * +! o * *** * * * * * * * * * + , * * * ** * * * * * * * * + W * * * * * * * * * * * ** * + r * * * * * * ************ * * * * * * * + d * * * * * * * * ****** * * * * * + * * * * * ** ** * * * * * * * + * ** * * * * * * * ** * * + * * * * * * ** ** * * * * * + * * * * * ** ** * ** * * * * + ******************** * * * ** * ** **** *** * + * ** * * ** * ** * + * ** * * * ** * ** * + * ** * ** * ** ** * ** * + ***** * ** * * ** ** ** * + ** * * ** * + * * * + ************************************************************* * ***************************** + * * * + * *** * * +* * * * * * +* ** * * * * + * * * * ** ****** * ********************* + * * * * * * * * * + * * * * * * * * * + * * * * ** * * * * + * * * * ** * * * * + * * * ***** ** * * * * + * * ** * ** ** * * * * + * * ** * ** ** ***** * * * + * * ** * * ** * * * + * * ** ** * * ** * ** * + * * * ** ***** ** ** * ** * + * * * ** ** * * * + * * ** ** * * * + ** * ** * ** * + * * * * * + * * ************************************************************* * * * + ** * * * * * * + ** * * * * * * * + * * * ************************* ** * * * * + * * * * * * * * * * * + *** * * * * * * * * * * ************** * + * * * * ** * * * * * ** ** * * + * * * * * * *** * * * * ** ** ******* * + ** * * * ** * * * * ** * * + * * * * * * * * ** * * + * * * * * * * ** * * + * * * * * * * ** * * + * * * * * * * * ** * * + * * * * * ** * * ** * * + * * * * * * * * * * * * + * * * * * * * * * * * * + * * * * ** * * * * * * + * * * * * * * * * * * + * * * * * * * ** * + * * * * * * * * * + * * * * * * * * + * * * ***** * * * + * * * * * * + * * * * * * + * ** * * * + * * * * * + * * * * + ***************************** * * + * * +*********************************************************************************************** * +* * +* * +* ** * +* ** ** * +* ** * * +* ** * * +* ** * * +* ** * * +* ** * ****** * +* **** ** * * * ****************** +* * * ** ** ** * * +* * * ** ** * ** * +* * * * * * +* * * * * * +* ***** * * * * +* * * * * * +* * ** * * +* * * * * +* * * * +******* * * + ** ** * + ** ** * * + ** ** * ** +************************ ** ** * * * +* ** * * * +* * * * * +* ** * * * +* * * * * * +* * * * * * +* * * ** * +* * ** * * +* * ** * +* * ** * + ** ** * + * ** ****************************** + ** ** ** + ** ** ** + ** ** ** + ** * ** + * ** + * ** + * ** + * ** ** ** + *************** ** ** + ** + + * ** ** ** + + *************** ** ** + + diff --git a/eg/loop1.w b/eg/loop1.w new file mode 100755 index 0000000..4d7eba3 --- /dev/null +++ b/eg/loop1.w @@ -0,0 +1,16 @@ +* + * + * + * + * * ** + * ** * + ** ** + * * + * * + * * + * * + * ** + * * + * ** * + ** * + diff --git a/eg/loop2.w b/eg/loop2.w new file mode 100755 index 0000000..ce9f84c --- /dev/null +++ b/eg/loop2.w @@ -0,0 +1,21 @@ +* * + * * * + * * *** + * * * + * * * + * * * + * * * + * * * + * *** * + * * * + * * * + * * * + * * * + * * * + * * * + * * * + * * * + * * *** + * * * + ******* + diff --git a/eg/output.w b/eg/output.w new file mode 100755 index 0000000..725487c --- /dev/null +++ b/eg/output.w @@ -0,0 +1,94 @@ +& * + 0 * * + * * * + * ** * + ** * + * **************************** + * *********** * + * * * + ** * * + * ** * + *H * * * + * A * * ** * + * L **************************** * * + * F * * * * * + N * * * * * * + E * * * * * * + X * **RESTART*** * * + T * * * * * + * * * **LOOP** * * + * * ***** * * * * + * * * ** * * * + * ** ** * ** ** * * + * * * ***** * * * * * * + * * * * * * * * * * + ** * * * * *********** **** * * + * ********** * * * * * + * * * * * ** * + * * ******* * * * * + ** * * * * *********** * * + ** * * ** * * * * * * + *** ** * * * * * * * * + * * * *********** * * * * * + * **DEHSINIF** * * ** * * + * * * F * * * + * * * I * ********* * + * ** * N ** * + * ** I * + * * S * + * H * + * **E***** * + *** * D * * + * * * * * + ********* * * * * + * * * ** ** * + * * * * * + * * * * * + * * * * * + * * * * + * * * + * * ** * * + ******************* * ** * + ** * * * + * ** * * * * + * ** * * * * ** * + * ** * * * * ** * + * * * * * * * * + *** * * * ** * * * + * * ** * * * * + * **** H * * * + * * * * A* * * + * * **OREZ** L * * + ****PRINT* * * * * *V * * + * * * * * * E * * + * * ** * ** * D ** * + * * ** * ** * ** * + ************* * * * * * + * * * * * * * * * + * * * * D * **IICSA** * + ** * * O * * * * + * ***** N * * * * + E * ** * + * * * * + ( * * + P ** * + R * * + I * * + N * * + T * * + * * * + N * * + E ***************** + W + L ** + I ** **** + N ** * + E ** * + ) ** * + * ** * + * ** + * ** + * ** + * ** + ** + diff --git a/eg/polyglot.i b/eg/polyglot.i new file mode 100755 index 0000000..1959e66 --- /dev/null +++ b/eg/polyglot.i @@ -0,0 +1,50 @@ +DONOTWierd * +DO,1<- # 0 09 +DO,1SUB#1 <- # 01 10 +PLEASEDO,1SUB # 2 <- #0 3 2 +DO,1SUB # 3 <- #0 7 2 + DO ,1SUB#004<-#0 0 0 0 000000136 +DO,1SUB#5 <- # 0 0 0 8 8 +PLEASEDO,1SUB # 06 <- # 0 1 3 6 + DO , 1SUB#7 <- # 000 0 6 4 +DO ,01 SUB #8 <- # 0 0 0 0 0 80 +DO ,1 SUB # 00 0 0 0 0 9 <-#2 26 + PLEASE READ OUT ,0 0 0 0 0 000000000000000 0 0 01 +PLEASEGIVEUPDONOTBOTHERME * * * P * * * * * ** +DONOT * * * * * O* * * * * * ** +DONOT ** * * * O ** * * ** * * ** +DONOT ** * * *L * * ** * * * ** +PLEASEDONOT * * * * * ** * ** * * ** +DONOT * * * * N* * * * * * * ** +DONOT * * * * I ** ** * * * ** +DONOT * * * * A * * * * * * +PLEASEDO NOT * * * M ** ** * * * * +DONOT * * * * * * * * * ** ** * +DONOT * * * * * * ** * ** * * ***************** +DONOT * * * * * * * ********* * ** * +PLEASEDONOT **** * * ** ** * +DONOT * ** * * * * * +DONOT ** * * * * * * +DONOT * ** * * * +PLEASEDONOT * * * * * +DONOT *********** * * * +DONOT * * * * * * +DONOT * * * * * +PLEASEDONOT * * * * * * +DONOT * * * * * +DONUT * * * * * * +DONOT * * * * * * +PLEASEDONOY * * * * * * +DONOT * * * * * * +DONOT * * * * * * +DONOT * * * * * W +PLEASEDONOT * * * * * R +DONOT * ** * * I +DONOT * * * *T +DONOT * * E +DONOT * * +OKAY,DOIFYOUPLEASE! +ICHANGEDMYMINDDON'T +DONOTDOTHAT ** +PLEASESTOP * + diff --git a/eg/quine.w b/eg/quine.w new file mode 100755 index 0000000..c80beb0 --- /dev/null +++ b/eg/quine.w @@ -0,0 +1,112 @@ +! + @ + * ! + * *! + * * ! + * * ! + * * * ! + * * * ! + * *** * ! + ** * * ! + * ** ** * **! + * * ** **** * * ! + * ** * * ** ! + * ** * * * * ! +* ** * * * * *** * ! +* ** * * * * * * * * ! + * ** * * * * * * * * * ! + * ** * * * ** * * * * * *! + ** * * * *** * * * * * * * ! + * * * * * * * * * * ! + *** * ** * * * * * * ! + * * * * * * * * ! + * * * * * * * * * ! + * * * * * ** *** * * * ! + * * * * * * ** * * * * ! + * * * * * * **** * * * ! + * ** * * * * * * **************************! + * * * * * * * * * * ! + * * * * * * * * * * ! + * * * * * * * * * * ! + * * * * * * * * * ********************** ! + * * * * * * * * * * * ! + * * ***** *** * * * * * * * ** ! + * * * ** * * * * * * * ! + * * * * * * * * * * **** * * * ! + * * * * * * * * * * * * * ** * ! + * * * * ** * * * * * * ********* * * ! + * * * * ** * * * ** * * * * ** ! + * * * ** ** * * * * * * * * * ! + * ** * * * * * * * * * * * ! + * * * * * * * ** * * * ! + ********************************************************* * * * * ** * * * * ! + * * * * * * * * * * ! + __ ___________________ _____ * * * * * * * * * * ! + \ \ / __ __ ___ _ \ | __ \ * * * *RESTART* ** * * * ! + \ \ /\ / / | | | |_ | |_) \| | \ \ * * * * * * ** * * ! + \ \/ \/ / | | | _| | _ /| | ) ) * * * **RESTART* ** * * * * ! + \ /\ / __| |__| |___| | \ \| |__/ / * * * * * * * * * * ** ! + \/ \/ |_______________| \_______/ * **************** ****MAIN*LOOP**** * * * * * ! + ____ _ _____________ ____ ** * * * ** * ** * ! + / __ \ | | | __ __ \ | __| ** * * * ** ** * * ! + / / \ \ | | | | | | | |\ \ | |_ * * * ** ** * ! + ( ( ) )| | | | | | | | \ \ | _| * * * * * * * ! + \ \__/ \| |_| |__| |__| | \ \| |__ ** * * ****YAW*SIHT****** * ! + \____/\_________________| \______| * * * * * * * * ! + ********WON*1=X**** ** * ***** ** * ! + ____ ____ ___ ___ ___ * * * * * * * ! + / __ \ / _ \ / _ \ / _ \ / | * * ** * * * * * ! + / / \ \ (_/ ) / / / \ \ / / \ \ (_/| | * * E * * * * * ! +( ( ) / / ( ( ) )( ( ) ) | | * *O * ** * * * * ! + \ \__/ / / /__ \ \_/ / \ \_/ / __| |__ * L * * * * * * ! + \____/ /_____| \___/ \___/ |_______| * ** * * * * * * ! + __ ____ ___ ___ ____ * * * * * * *********** ! + / \ / _ \ / _ \ / _ \ / _ \ * * * * * * * ! +( () ) _ (_/ ) / / / \ \ / / \ \ (_/ ) / * * * * * * * ! + ) _ \/ ) / / ( ( ) )( ( ) ) / / * * ** **** * ** ***** ! +( (_) ( / /__ \ \_/ / \ \_/ / / /__ * * * * ** ** ! + \___/\_) /_____| \___/ \___/ /_____| * * * ** ! + __ ____________ ____ * * * ! + | \ / __ __ | / __ \ * * * ! + | \ / | | | | | / / \ \ * *** ! + | |\ \/ /| | | | | | ( ( ) ) * ! + | | \ / | |__| |__| |_____) \__/ / * ! + |_| \/ |_______________________/ * ! + __ ___ _ * ! + \ \ /\ | \ | | * ! + \ \ / \ | |\ \ | | * ! + \ \/ /\ \| | \ \ | | * ! + \ ____ \ | \ \| | * ! + \/ \__| \___| * ! + _ _ ___ _____ _______ *** ! +| | | | /\ | \ | __ \ | ___ | * * ! +| |__| | / \ | |\ \ | | \ \| |_ | | * ! +| __ | / /\ \| | \ \ | | ) ) _| | | * * ! +| | | |/ ____ \ | \ \| |__/ /| |___| |___ * * * * ! +|_| |___/ \__| \_______/ |___________| * D * ** ! + * O * * * ! + SOURCE AND OUTPUT BOTH LICENCED BY THE GPL * * N ******* * * ! + (DOESN'T FIT WITH ASCII ART) * ** E * * * * ! + * * * ******* * * ! + ** * * * * * ! + * * * N * * ! + *** * O * * ! + * * ** * T * ** ! + * ** ** * * ! + *** * Y * ! + * E * ! + * T * ! + * ** ! + * ! + * ! + * ! + * ! + * *! + ** * *! + ** ** ! + * ! + * ****************************! +@ *! + + + diff --git a/eg/random.w b/eg/random.w new file mode 100755 index 0000000..fc2ff6b --- /dev/null +++ b/eg/random.w @@ -0,0 +1,157 @@ +* ** * + * * ** * ** + 0 ** **** ** * * * + * * ** * * ** ** * * + * * * ** * ** * * + * * * * * * ** * * + * * ** * * * * * * + **** * * * * * * + * * * * * * * + * * * * * * * + * * * * * * * + ** * * * * * + * * * * * * + ** * * * ****** + * * * * * * * + * * * * * * * * * + * * ** * * * * * * + * * * * * * ****** * + * * * * ** * + * ** * * N ******** * + * ** * * ******E******* * * * + M ** * S * ** * ** * * + A * O ** * * * * * + I * H * * * * + N * C * * * * + * * * * * * * * + L * R ** ** * * * + O * E * * * ** * + O * B * * * ** * * + P * M * ** * * *** * + * * U * ** * * * * + ** N * * * * * * + * * * * * * * * + * * ** * * * * * * + ** **** * * * * * * * + ** * * * * * * * * + * **** * * * * * * * + * ** * **** * * ** * * + ** * * * * * * + ***************** * * + * * * ** * + * * * * * + * * * * * + * * * * + * * ** * * + * * **************** * + * ** * + * * * + ****** * * + * * * * * + * ** * * + * *** * + * * * + * ** * * + * **** * * * + * * * * * + * * * * * + ** * * * * + ** * *** * + * * ** * + * * * + ** * + * * + * * + * * * + * * * + * ** * + ** * + * **************************** + * *********** * + * * * + ** * * + * ** * + *H * * * + * A * * ** * + * L **************************** * * + * F * * * * * + N * * * * * * + E * * * * * * + X * **RESTART*** * * + T * * * * * + * * * **LOOP** * * + * * ***** * * * * + * * * ** * * * + * ** ** * ** ** * * + * * * ***** * * * * * * + * * * * * * * * * * + ** * * * * *********** **** * * + * ********** * * * * * + * * * * * ** * + * * ******* * * * * + ** * * * * *********** * * + ** * * ** * * * * * * + *** ** * * * * * * * * + * * * *********** * * * * * + * **DEHSINIF** * * ** * * + * * * F * * * + * * * I * ********* * + * ** * N ** * + * ** I * + * * S * + * H * + * **E***** * + *** * D * * + * * * * * + ********* * * * * + * * * ** ** * + * * * * * + * * * * * + * * * * * + * * * * + * * * + * * ** * * + ******************* * ** * + ** * * * + * ** * * * * + * ** * * * * ** * + * ** * * * * ** * + * * * * * * * * + *** * * * ** * * * + * * ** * * * * + * **** H * * * + * * * * A* * * + * * **OREZ** L * * + ****PRINT* * * * * *V * * + * * * * * * E * * + * * ** * ** * D ** * + * * ** * ** * ** * + ************* * * * * * + * * * * * * * * * + * * * * D * **IICSA** * + ** * * O * * * * + * ***** N * * * * + E * ** * + * * * * + ( * * + P ** * + R * * + I * * + N * * + T * * + * * * + N * * + E ***************** + W + L ** + I ** **** + N ** * + E ** * + ) ** * + * ** * + * ** + * ** + * ** + * ** + ** + diff --git a/src/wierd-milo.c b/src/wierd-milo.c new file mode 100755 index 0000000..5a2a15b --- /dev/null +++ b/src/wierd-milo.c @@ -0,0 +1,343 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <getopt.h> + +typedef struct stack +{ + int value; + struct stack *below; +} stack; + +typedef struct thread +{ + int id; + int x, y, dx, dy; + stack *stack_head; + struct thread *next, *prev; +} thread; + +int size = 256; +int debug = 0; +int **world; +thread *current_thread; +int threads = 0; + +void push(int value) +{ + stack *new; + + new = malloc(sizeof(*new)); + new->value = value; + new->below = current_thread->stack_head; + current_thread->stack_head = new; +} + +int pop(void) +{ + stack *old; + int value; + + old = current_thread->stack_head; + if(old == NULL) + { + fprintf(stderr, "Insufficient stack!\n"); + exit(1); + } + value = old->value; + current_thread->stack_head = old->below; + free(old); + return value; +} + +void copy_stack(thread *dst, thread *src) +{ + stack *dst_loc, *prev_dst_loc = NULL, *src_loc; + + if(src->stack_head == NULL) + { + dst->stack_head = NULL; + return; + } + for(src_loc = src->stack_head; src_loc != NULL; src_loc = src_loc->below) + { + dst_loc = malloc(sizeof(*dst_loc)); + dst_loc->value = src_loc->value; + dst_loc->below = NULL; + if(prev_dst_loc == NULL) dst->stack_head = dst_loc; else prev_dst_loc->below = dst_loc; + prev_dst_loc = dst_loc; + } +} + +void new_thread(void) +{ + thread *new; + + new = malloc(sizeof(*new)); + new->id = threads++; + new->x = current_thread->x; + new->y = current_thread->y; + new->dx = -current_thread->dx; + new->dy = -current_thread->dy; + copy_stack(new, current_thread); + new->next = current_thread->next; + new->prev = current_thread; + current_thread->next->prev = new; + current_thread->next = new; +} + +void kill_thread(void) +{ + thread *old; + + old = current_thread; + while(old->stack_head != NULL) pop(); + if(old->next == old) exit(0); + old->prev->next = old->next; + old->next->prev = old->prev; + current_thread = old->next; + free(old); +} + +void turn_dxdy(int *dx, int *dy, int direction) +{ + int old_dx, old_dy; + + direction %= 8; + if(direction < 0) direction += 8; + while(direction-- > 0) + { + old_dx = *dx; + old_dy = *dy; + *dx = old_dy + old_dx; + *dy = old_dy - old_dx; + if(abs(*dx) == 2) *dx /= 2; + if(abs(*dy) == 2) *dy /= 2; + } +} + +int can_turn(int direction) +{ + int dx = current_thread->dx, dy = current_thread->dy; + + turn_dxdy(&dx, &dy, direction); + return world[current_thread->x + dx][current_thread->y + dy] != ' '; +} + +void turn(int direction) +{ + turn_dxdy(¤t_thread->dx, ¤t_thread->dy, direction); +} + +void print_stack(void) +{ + char str1[256], str2[256] = ""; + stack *printing; + + fprintf(stderr, "The new stack is"); + if(current_thread->stack_head == NULL) + { + fprintf(stderr, " empty.\n"); + return; + } + for(printing = current_thread->stack_head; printing != NULL; printing = printing->below) + { + sprintf(str1, " [%d]%s", printing->value, str2); + strcpy(str2, str1); + } + fputs(str2, stderr); + fputc('\n', stderr); +} + +void help(void) +{ + fprintf(stderr, "Valid options:\n"); + fprintf(stderr, "-help: Show this screen.\n"); + fprintf(stderr, "-size <number>: Set the memory size.\n"); + fprintf(stderr, "-debug: Print debugging information.\n"); + fprintf(stderr, "There should also be a non-option argument naming the program to be run.\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + struct option options[] = {{"help", 0, NULL, '?'}, + {"size", 1, NULL, 's'}, + {"debug", 0, NULL, 'd'}}; + int done = 0; + char *ptr; + int x, y; + char *in_name = NULL; + FILE *in_file; + + opterr = 0; + while(!done) + { + switch(getopt_long_only(argc, argv, "-?", options, NULL)) + { + case 's': if(*optarg == '\0') help(); + size = strtol(optarg, &ptr, 10); + if(*ptr != '\0') help(); + break; + case 'd': if(debug) help(); + debug = 1; + break; + case '?': help(); + case 1: if(in_name != NULL) help(); + in_name = strdup(optarg); + break; + case EOF: done = 1; + break; + } + } + if(in_name == NULL) help(); + world = malloc(sizeof(*world) * size); + for(x = 0; x < size; x++) + { + world[x] = malloc(sizeof(**world) * size); + for(y = 0; y < size; y++) world[x][y] = ' '; + } + in_file = fopen(in_name, "r"); + if(in_file == NULL) + { + perror(in_name); + return 1; + } + y = 1; + for(;;) + { + char ch; + + x = 1; + for(;;) + { + ch = fgetc(in_file); + if(ch == '\n' || ch == EOF) break; + world[x++][y] = ch; + } + if(ch == EOF) break; + y++; + } + fclose(in_file); + if(world[1][1] == ' ') + { + fprintf(stderr, "Starting point for program is empty.\n"); + return 1; + } + current_thread = malloc(sizeof(*current_thread)); + current_thread->id = threads++; + current_thread->x = current_thread->y = 1; + current_thread->dx = current_thread->dy = 1; + current_thread->stack_head = NULL; + current_thread->next = current_thread->prev = current_thread; + srandom(time(NULL)); + for(;;) + { + int closest_right = 4, closest_left = 4; + int which, x, y; + + if(debug) fprintf(stderr, "Thread %3d at (%3d,%3d) ", current_thread->id, current_thread->x, current_thread->y); + if(can_turn(1)) closest_left = 1; else if(can_turn(2)) closest_left = 2; else if(can_turn(3)) closest_left = 3; + if(can_turn(-1)) closest_right = 1; else if(can_turn(-2)) closest_right = 2; else if(can_turn(-3)) closest_right = 3; + if(can_turn(0)) + { + if(debug) fprintf(stderr, "is happily marching along (0 degrees).\n"); + } + else if(closest_left == closest_right) + { + switch(closest_left) + { + case 1: if(debug) fprintf(stderr, "faces a difficult choice (45 or 315 degrees).\n"); + if(random() & 1024) turn(1); else turn(-1); + break; + case 2: if(debug) fprintf(stderr, "spawns a new thread (90 and 270 degrees).\n"); + turn(2); + new_thread(); + current_thread = current_thread->next; + continue; + case 3: if(debug) fprintf(stderr, "is confused (135 and 225 degrees).\n"); + fprintf(stderr, "The programmer has not yet decided what a 135/225 T-junction should do!"); + return 1; + case 4: if(debug) fprintf(stderr, "bumps into a dead end (180 degrees).\n"); + kill_thread(); + continue; + } + } + else if(closest_left < closest_right) + { + switch(closest_left) + { + case 1: if(debug) fprintf(stderr, "places a one on his stack (45 degrees).\n"); + push(1); + break; + case 2: which = pop(); + if(which) + { + if(debug) fprintf(stderr, "is afraid from the turn (90 degrees).\n"); + turn(2); + break; + } + if(debug) fprintf(stderr, "happily marches through the turn (90 degrees).\n"); + break; + case 3: which = pop(); + y = pop(); + x = pop(); + if(which) + { + if(debug) fprintf(stderr, "gets a number for his stack (135 degrees).\n"); + push(world[x][y]); + } + else + { + if(debug) fprintf(stderr, "puts a number in his stack down (135 degrees).\n"); + world[x][y] = pop(); + } + break; + } + if(debug) print_stack(); + turn(closest_left); + } + else + { + switch(closest_right) + { + case 1: if(debug) fprintf(stderr, "sits down to work out a subtraction (315 degrees).\n"); + y = pop(); + x = pop(); + push(x - y); + break; + case 2: which = pop(); + if(which) + { + if(debug) fprintf(stderr, "is afraid from the turn (270 degrees).\n"); + turn(-2); + break; + } + if(debug) fprintf(stderr, "happily marches through the turn (270 degrees).\n"); + break; + case 3: which = pop(); + if(which) + { + which = pop(); + if(debug) fprintf(stderr, "wishes you to know that \"%c\" (225 degrees).\n", which); + if(!debug || !isatty(1)) putchar(which); + } + else + { + which = getchar(); + if(debug) fprintf(stderr, "discovers that \"%c\" (225 degrees).\n", which); + push(which); + } + break; + } + if(debug) print_stack(); + turn(-closest_right); + } + current_thread->x += current_thread->dx; + current_thread->y += current_thread->dy; + current_thread = current_thread->next; + } +} + diff --git a/src/wierd.c b/src/wierd.c new file mode 100755 index 0000000..3effe03 --- /dev/null +++ b/src/wierd.c @@ -0,0 +1,484 @@ +/* + * Wierd Interpreter: + * + * Wierd Language developed by Chris Pressey, August 1997 + * Interpreter written 05 August 1997 by John Colagioia + * + * Deviations from the "Official" Wierd: + * Direction: The IP will always try to continue in the same + * direction (or as close as possible). When two + * equally-likely possibilities exist, it will choose + * the "leftmost" branch. + * Conditionals: When the IP connects to a 90-degree angle, + * it pops the stack. If the result is zero, it + * continues as if nothing interesting happens. If + * nonzero, it reverses the IP direction. + * Gap-Sparking: If the IP has just "sparked into" a + * location which is isolated, the program is + * considered erroneous, as the IP direction is + * undefined at that time. + * + * Revision History: + * R#: Date: Inits: Reason: + * 0 05aug97 jnc Origination + * 1 06aug97 jnc Various Bugfixes, Revised Conditionals + * 2 07aug97 jnc Added Multithreading, Revised Conditionals + * 3 08aug97 jnc Added "Gap-Sparking" + * 4 08aug97 jnc General Cleanup and Bugfixes + * + * To Do: + * -- Heavier testing (requires writing Wierd code...). + * -- Add a tty or graphical display routine. + * + */ + +/*** Preprocessor Directives ***/ + /* Included header files */ +#include <stdio.h> +#include <stdlib.h> +#include <malloc.h> + /* Constants and Macros */ +#define SIZE 128 + +/*** Data Structures ***/ +struct iplist /* An instruction pointer */ + { + int ipid; /* A unique identifier */ + int x; /* The current ordinate value */ + int y; /* The current abscissa value */ + int dx; /* The next change in x to be applied */ + int dy; /* The next change in y to be applied */ + int stack[SIZE]; /* A stack exclusive to this IP */ + int s_top; /* A pointer to the stack top */ + struct iplist *next; /* The next IP available */ + }; + +/*** Function Prototypes (only for the function table below) ***/ +int nop (struct iplist **); +int push1 (struct iplist **); +int subtract (struct iplist **); +int condition (struct iplist **); +int getput (struct iplist **); +int inputoutput (struct iplist **); +int terminus (struct iplist **); + +/*** more prototypes, because my compiler likes them - cap */ +int turn45 (int * xdir, int * ydir); +int getnextturn (int x, int y, int * dx, int * dy); + +/*** Global Variables ***/ +char workspace[SIZE][SIZE]; /* The "Wierd memory space" */ +int tmpx, tmpy, /* temporary deltas */ + ipid = 0, ip_killed; /* IP identifier, kill flag */ +struct iplist *list; /* The list of IPs */ +int (*function[])(struct iplist **) = + { /* Function table */ + nop, /* 0 degrees */ + push1, /* 45 degrees */ + condition, /* 90 degrees */ + getput, /* 135 degrees */ + terminus, /* 180 degrees */ + inputoutput, /* 225 degrees */ + condition, /* 270 degrees */ + subtract /* 315 degrees */ + }; + +/*** Source code for Interpreter Functions ***/ + +/* Function main(): + * Initializes data structures and environment, and iterates through + * each IP until termination. Also handles most common errors. + */ +int main (int argc, char *argv[]) +{ + int i, x, y; /* Temporary variables */ + FILE *infile; /* Input file */ + + if (argc < 2) /* Not enough arguments--don't know what to do */ + { + fprintf (stderr, "Error:\n\t%s <Wierd Filename>\n", argv[0]); + exit (1); + } + infile = fopen (argv[1], "r"); + if (infile == NULL) /* Bad input filename given */ + { + fprintf (stderr, "Error: Invalid Wierd filename: \"%s\".\n", + argv[1]); + exit (2); + } + + for (x=0;x<SIZE;++x) /* Clear the memoryspace */ + for (y=0;y<SIZE;++y) + workspace[x][y] = '\000'; + + y = 1; /* Read the file into the memoryspace */ + while (!feof (infile)) + { + fscanf (infile, "%[^\n]", &workspace[y][1]); + fgetc (infile); + #ifdef DEBUG + printf ("%s\n", &workspace[y][1]); + #endif + ++y; + } + fclose (infile); /* Clean up file handles */ + for (x=0;x<SIZE;++x) /* Convert nulls to spaces */ + for (y=0;y<SIZE;++y) + if (workspace[x][y] == '\000') + workspace[x][y] = ' '; + + /* Initialize the first IP */ + list = (struct iplist *) malloc (sizeof (struct iplist)); + list->x = list->y = list->dx = list->dy = 1; + list->s_top = 0; + list->next = list; + list->ipid = ++ipid; + + /* Loop as long as the location is valid */ + while (workspace[list->x][list->y] != ' ') + { + ip_killed = 0; /* Initialize the kill-flag */ + #ifdef DEBUG + printf ("<%d>\t", list->ipid); + printf ("(%d,%d)\t", list->y, list->x); + for (i=0;i<list->s_top;++i) + printf ("[%d] ", list->stack[i]); + #endif + tmpx = list->dx; /* Save the current direction */ + tmpy = list->dy; /* and change it */ + i = getnextturn (list->x, list->y, &tmpx, &tmpy); + #ifdef DEBUG + printf ("\t\t%d\n", i); + #endif + if (i % 45) /* Invalid direction! */ + printf ("\007ERROR!!\007\n"); + else { /* Call the appropriate function */ + i = function[i/45](&list); + if (i == -1) /* Out of IPs */ + { + free (list); + return 0; + } + } + if (!ip_killed) /* Only update if it's the same IP */ + { + list->x += list->dx = tmpx; /* Update location */ + list->y += list->dy = tmpy; + list = list->next; /* Next IP */ + } + } +#ifdef DEBUG + printf ("Program ungracefully terminated!\n"); +#endif + return 0; +} + +/* Function checkdir(): + * Simple function to determine validity of location a given delta + * from the given location. + */ +int checkdir (int x, int y, int dx, int dy) +{ + return (workspace[x+dx][y+dy] != ' '); /* Only space is invalid */ +} + +/* Function rotate(): + * Turns an IP the specified number of degrees (though only in 45- + * degree increments). + */ +int rotate (int * xdir, int * ydir, int turn) +{ + int i; + + for (i=0;i<turn;i+=45) /* We only work in 45-degree increments */ + turn45 (xdir, ydir); + return 0; +} + +/* Function turn45(): + * Rotates a dx,dy heading 45 degrees clockwise. Very unclean + * implementation, but it works for now. Once we expand beyond two + * dimensions, though, this will have to be heavily rewritten. + */ +int turn45 (int * xdir, int * ydir) +{ + if (*ydir == 1 && *xdir == -1 || *ydir == -1 && *xdir == 1) + *ydir = 0; + else if (*xdir == 1 && *ydir == 1 || *xdir == -1 && *ydir == -1) + *xdir = 0; + else if (*ydir) + *xdir -= *ydir; + else *ydir += *xdir; + return 0; +} + +/* Function getnextturn(): + * Checks the 7 of the 8 valid directions from the given point, + * choosing those 7 based on the IP delta. Returns the "leftmost" + * direction closest to "straight" that is available from the given + * location. + */ +int getnextturn (int x, int y, int * dx, int * dy) +{ + if (checkdir (x, y, *dx, *dy)) + return 0; + rotate (dx, dy, 45); + if (checkdir (x, y, *dx, *dy)) + return 45; + rotate (dx, dy, 270); + if (checkdir (x, y, *dx, *dy)) + return 315; + rotate (dx, dy, 135); + if (checkdir (x, y, *dx, *dy)) + return 90; + rotate (dx, dy, 180); + if (checkdir (x, y, *dx, *dy)) + return 270; + rotate (dx, dy, 225); + if (checkdir (x, y, *dx, *dy)) + return 135; + rotate (dx, dy, 90); + if (checkdir (x, y, *dx, *dy)) + return 225; + return 180; +} + +/* Function add_thread(): + * Inserts a node on the circular linked list of IPs, and initializes + * the node as a new IP. + */ +int add_thread (struct iplist * ip, int dx, int dy) +{ + struct iplist *temp; + int i; + + /* Create new node on list */ + temp = (struct iplist *) malloc (sizeof (struct iplist)); + temp->ipid = ++ipid; /* Give an identifier */ + temp->next = ip->next; /* Insert into list */ + temp->x = ip->x - dx; /* Initialize based on current IP */ + temp->y = ip->y - dy; + temp->dx = - dx; + temp->dy = - dy; + temp->s_top = ip->s_top; + for (i=0;i<ip->s_top;++i) + temp->stack[i] = ip->stack[i]; + ip->next = temp; +#ifdef DEBUG + printf ("Thread #%d created.\n", temp->ipid); +#endif + return 0; +} + +/* Function kill_thread(): + * Removes the current node from the circular linked list of IPs. + */ +int kill_thread (struct iplist ** ip) +{ + struct iplist *temp; +#ifdef DEBUG + int id; + + id = (*ip)->ipid; +#endif + temp = *ip; + while (temp->next != *ip) /* Get IP before this one */ + temp = temp->next; + temp->next = (*ip)->next; /* Point around the current IP */ + free (*ip); /* Deallocate the IP */ + *ip = temp; /* Return the previous one */ +#ifdef DEBUG + printf ("Thread #%d destroyed, passing to #%d.\n", id, (*ip)->ipid); +#endif + return 0; +} + +/* Function find_far(): + * Called when the IP has no obvious place to go. Checks the region + * around (7x7 locations) the current IP location for a "closest + * point" based on the current location as well as the current IP + * direction. + */ +int find_far (int x, int y, int dx, int dy, int **xcoors, int **ycoors) +{ + int i, j, pts, xs[40], ys[40], ti, tj, + a[] = {2, 3, -2, -3, 0, 1, -1}; /* Order of precedence */ + + pts = 0; + for (i=0;i<7;++i) for (j=0;j<7;++j) + /* Scan along the array in two dimensions */ + if (i < 4 || j < 4) /* If at least one index looks at {2,3} */ + { + ti = dx>0?a[i]:-a[i]; /* Invert if a delta is */ + tj = dy>0?a[j]:-a[j]; /* negative */ + if (x>a[i] && y>a[j] && + workspace[x+ti][y+tj] != ' ') + { /* If the location exists and isn't */ + xs[pts] = x + ti; /* a space */ + ys[pts] = y + tj; /* Add to list */ + if (xs[pts] > 0 && ys[pts] > 0) + ++ pts; + } + } + *xcoors = (int *) malloc (sizeof (int) * pts); + *ycoors = (int *) malloc (sizeof (int) * pts); + for (i=0;i<pts;++i) /* Copy the list of candidates to where the */ + { /* calling function suggests */ + (*xcoors)[i] = xs[i]; + (*ycoors)[i] = ys[i]; + } + return (pts); /* Return the number of points found */ +} + +/*** Source Code for internal Interpreter Operations (called from the + function table only ***/ + +/* Function nop(): + * No-operation. + */ +int nop (struct iplist ** list) +{ + return 0; /* Self-explanatory */ +} + +/* Function push1(): + * Pushes a constant value 1 onto the IP's stack. + */ +int push1 (struct iplist ** ip) +{ + struct iplist *list = *ip; + + list->stack[list->s_top++] = 1; /* Self-explanatory */ + return 0; +} + +/* Function condition(): + * If the top of the IP's stack is zero, functions as nop(). + * Otherwise, it reverses the direction of the IP. + */ +int condition (struct iplist ** ip) +{ + int i; + struct iplist *list = *ip; + + /* Two coincident 90-degree turns spawns a new IP */ + if (workspace[list->x-tmpx][list->y-tmpy] != ' ') + add_thread (list, tmpx, tmpy); + /* Otherwise, check top of the stack determines whether */ + /* we continue or change direction */ + else if (list->s_top && list->stack[--list->s_top]) + { + list->x += tmpx; + list->y += tmpy; + i = tmpx; /* Reflect the IP */ + tmpx = -tmpy; + tmpy = -i; + i = getnextturn (list->x, list->y, &tmpx, &tmpy); + #ifdef DEBUG + printf ("<%d>\t", list->ipid); + printf ("(%d,%d)\t", list->y, list->x); + for (i=0;i<list->s_top;++i) + printf ("[%d] ", list->stack[i]); + printf ("\t\t%d\n", i); + #endif + } + *ip = list; /* Fix the IP list in case the current thread changed */ + return 0; +} + +/* Function getput(): + * If the top of the stack is zero, retrieves the byte stored at the + * coordinates specified on the stack. Otherwise, takes the element + * on the stack beneath the coordinates and stores the value at those + * coordinates. + */ +int getput (struct iplist ** ip) +{ + int xcoor, ycoor; + struct iplist *list = *ip; + + if (list->s_top < 3) /* Not enough stack elements to do anything */ + return 1; + if (list->stack[--list->s_top]) /* A non-zero is a "put" */ + { + xcoor = list->stack[--list->s_top]; + ycoor = list->stack[--list->s_top]; + list->stack[list->s_top++] = workspace[xcoor][ycoor]; + } + else { /* Otherwise it's a "get" */ + xcoor = list->stack[--list->s_top]; + ycoor = list->stack[--list->s_top]; + workspace[xcoor][ycoor] = list->stack[--list->s_top]; + } + return 0; +} + +/* Function inputoutput(): + * If the top of the stack is zero, gets a character from input. + * Otherwise, it prints the next character on the stack. + */ +int inputoutput (struct iplist ** ip) +{ + struct iplist *list = *ip; + + if (list->s_top < 1) /* Nothing useful on the stack */ + return 1; + if (list->stack[--list->s_top]) /* If it's nonzero, print the next */ + putchar (list->stack[--list->s_top]); + /* Otherwise request a character */ + else list->stack[list->s_top++] = getchar (); + return 0; +} + +/* Function subtract(): + * Subtracts the top two stack elements. + */ +int subtract (struct iplist ** ip) +{ + struct iplist *list = *ip; + + if (list->s_top < 2) /* Can't subtract without two elements */ + return 1; + list->stack[list->s_top - 2] -= list->stack[list->s_top - 1]; + --list->s_top; /* Otherwise, subtract top from under and pop */ + return 0; +} + +/* Function terminus(): + * Based on current environmental conditions, either sparks the IP + * across the gap, or kills the IP. + */ +int terminus (struct iplist ** ip) +{ + int i, j, k, *xlist, *ylist; + struct iplist *list = *ip; + + /* Check for a line to spark to */ + j = find_far (list->x, list->y, list->dx, list->dy, &xlist, &ylist); +#ifdef DEBUG + for (k=0;k<j;++k) + printf ("\t\t{%d,%d}\n", ylist[k], xlist[k]); +#endif + if (j < 3) /* Less than three means it's probably our */ + { /* incoming path */ + if (list->next == list) + return -1; /* so kill the thread */ + kill_thread (&list); + ip_killed = 1; + } + else { /* Otherwise, we have valid locations */ + list->x = xlist[0]; /* and they *should* be ordered */ + list->y = ylist[0]; /* by preference */ + i = getnextturn (list->x, list->y, &tmpx, &tmpy); + #ifdef DEBUG + printf ("==> %d choices available.\n", j); + printf ("<%d>\t", list->ipid); + printf ("(%d,%d)\t", list->y, list->x); + for (i=0;i<list->s_top;++i) + printf ("[%d] ", list->stack[i]); + printf ("\t\t%d\n", i); + #endif + } + *ip = list; /* Fix the list pointer in case it was changed */ + return 0; +}