Fatmouse
Fatmouse Overview
Fatmouse is a language where the only process is variables being consumed by Fatmouse. Variables in Fatmouse have the following properties:
- Variables, when consumed by Fatmouse, can only take one value: Fatmouse.
- Once consumed, they can never be un-consumed.
- Because Fatmouse needs much sustenance, unbounded multidimensional arrays of potential variables can be used.
- Unconsumed variable are of no interest. Hence, you can only check if a variable has been consumed and is part of Fatmouse. You cannot check if it is unconsumed.
- It does not matter in which order variables are consumed. Hence, there is no flow control.
- To make it efficient to feed a lot of variables to Fatmouse, iterators are allowed within a statement.
Fatmouse Statements
Every Fatmouse statement takes one line and takes the following form:
<Name of variable to be consumed> {[Conditions]}
Once all the conditions are filled, the variable is added to Fatmouse (and thus consumed). There are only two types of conditions allowed: testing if a variable has already been eaten, and numerical comparation involving iterators. If there are no conditions, the variable is immediately consumed.
Examples of Fatmouse statements:
World Hello Hello
Since "Hello" has no conditions, it is immediately consumed. Once that happens, "World" gets consumed too, since all the conditions necessary to its consumption (in this case, "Hello" having been consumed) have been filled. Since the only possible statements in Fatmouse are conditional consumption, it is not necessary to specify anything else. Also note that the order of statements has no effect in Fatmouse.
Shave Haircut Two_bits Shave Haircut
Here, "Shave" and "Haircut" will be consumed. Since both its conditions have been filled (the consumption of "Shave" and "Haircut"), "Two_bits" will be consumed too.
Iterators and Arrays
Arrays in Fatmouse are notated with a dot "." after the variable name. To make it possible to consume whole arrays, iterators can be used. Here's an example:
ages.i i>=7 i<=77
Here, variables ages.7, ages.8, ages.9, ages.10, etc... up to ages.77 will be consumed. All possible integer values will be tried until a consumable variable can be found.
loop.i+1 loop.i loop.0
Here, loop.0 will be consumed first. Then, since loop.0 has been consumed, if you give i the value 0, you can form "loop.0+1 loop.0", so loop.1 will be consumed too. This will in turn allow the consumption of loop.2 (since with i=1, you can get "loop.1+1 loop.1"). All variables with indexes >= 0 will be consumed. Since integers are unbounded, an infinity of variables will be consumed (which will produce an infinite loop unfortunately).
slope.0.0 slope.i+1.j+1 slope.i.j
Here's an example of multidimensional array. Since slope.0.0 gets consumed, this allows the consumption of slope.1.1, and in then slope.2.2, and so on...
Elements Usable in Statements
- Variables: they take the form "name" for single values, "name.x" for one dimensional arrays, "name.x.y" for two dimensions, "name.x.y.z" for three dimensions, and so on. The array indexes must be either integer constants, iterators, or math expressions. The first element of a statement is always a variable, and the other elements must be either a variable (to test if it has been eaten yet) or a comparator. Note that it's not possible to test if a variable has not been eaten yet (so !name is NOT valid), only if it has been eaten. These follow the C naming convention (valid chars are "a-Z A-Z 0-9 _", first character must be a letter or '_', case sensitive, etc).
- Iterators: these also follow C naming conventions. Any name that doesn't appear somewhere as a consumed variable is automatically considered to be an iterator. Iterators local to a given statement - for instance, if the name "i" appears in two different statements, they are totally independent. In theory, each statement has to be checked with an infinite number of values for each iterator, but in practice, the interpreter can be smart about this: if one of the conditions is varname.x for instance, it can simply check out all the consumed variables with the name varname, and use those values for x, and toss out all the other possible values of x.
- Integer constants: 0, 1, 2, -1, and so on... It's also possible to use ASCII chars, such as 'A' (same as 65), '0' (same as 48), '!' (same as 33), etc...
- Math operators: only 4 are defined: "+ - * /". They can be used within variable array indexes, and take either iterator or integer constants as operands. "* /" have operator precedence over "+ -". Parenthesis can be used.
- Comparators: there are 6 comparators: "= != < <= > >=". They serve as conditions and can compare iterators, integer constants, or results of math operators.
- Special variables: input.x.y, output.x.y: these arrays behave mostly like other variable arrays, but are used for i/o. When the user inputs a character, input.x.y becomes consumed, where x is the number of characters inputted since the beginning of the program and y is the value of that character. For instance, if the user inputs "Hey", then input.0.'H', input.1.'e' and input.2.'y' become defined. Likewise, output behaves the same way but in reverse: if values output.0.'Y', output.1.'o', output.2.'!' become consumed, the text "Yop!" will be printed onscreen.
Example Program
This reads in a Brainfuck program until the character "@", then starts executing it.
icnt.0 pr.x.v icnt.x input.x.v icnt.x+1 icnt.x input.x.v v!='@' inp.0.x+1 icnt.x input.x.'@' ip.0.0 icnt.x input.x.'@' dp.0.0 dat.0.n.0 outp.0.0 dat.i+1.x.y ip.i.j pr.j.v v!='+' v!='-' v!=',' dat.i.x.y dat.i+1.w.x ip.i.j pr.j.'+' dp.i.v w!=v dat.i.w.x dat.i+1.v.x+1 ip.i.j pr.j.'+' dp.i.v dat.i.v.x dat.i+1.w.x ip.i.j pr.j.'-' dp.i.v w!=v dat.i.w.x dat.i+1.v.x-1 ip.i.j pr.j.'-' dp.i.v dat.i.v.x dat.i+1.w.x ip.i.j pr.j.',' dp.i.v w!=v dat.i.w.x dat.i+1.v.x ip.i.j pr.j.',' dp.i.v inp.i.w input.w.x dat.i.v.y inp.i+1.x ip.i.j pr.j.v v!=',' inp.i.x inp.i+1.x+1 ip.i.j pr.j.',' inp.i.x output.w.x ip.i.j pr.j.'.' dp.i.v outp.i.w dat.i.v.x outp.i+1.x ip.i.j pr.j.v v!='.' outp.i.x outp.i+1.x+1 ip.i.j pr.j.'.' outp.i.x dp.i+1.v ip.i.j pr.j.v v!='<' v!='>' dp.i.v dp.i+1.v-1 ip.i.j pr.j.'<' dp.i.v dp.i+1.v+1 ip.i.j pr.j.'>' dp.i.v ip.i+1.j+1 ip.i.j pr.j.v v!='[' v!=']' v!='@' ip.i+1.j+1 ip.i.j pr.j.'[' dp.i.v dat.i.v.w w!=0 ip.i+1.j+1 ip.i.j pr.j.']' dp.i.v dat.i.v.0 ipl.i.j+1.1 ip.i.j pr.j.'[' dp.i.v dat.i.v.0 ipz.i.j-1.1 ip.i.j pr.j.']' dp.i.v dat.i.v.w w!=0 ipl.i.j+1.k+1 ipl.i.j.k pr.j.'[' ipl.i.j+1.k-1 ipl.i.j.k pr.j.']' k!=1 ipl.i.j+1.k ipl.i.j.k pr.j.m m!='[' m!=']' ip.i+1.j+1 ipl.i.j.k pr.j.']' k=1 ipz.i.j-1.k+1 ipz.i.j.k pr.j.']' ipz.i.j-1.k-1 ipz.i.j.k pr.j.'[' k!=1 ipz.i.j-1.k ipz.i.j.k pr.j.m m!='[' m!=']' ip.i+1.j+1 ipz.i.j.k pr.j.'[' k=1
Statement Evaluation and Garbage Collecting
- Some expressions involving misuse of iterators might be impossible to execute - for instance trying to force the interpreter to solve a complex polynomial ("val.x 0=x*x+x-12" for instance can be rejected). Fatmouse is not Matlab. Iterators are guaranteed to work in the following cases: either the iterator appears in one of the conditional variables as an index (for instance in "vara.x.y varb.x.y", both x and y appear in "varb.x.y"), either it appears as the result of an obvious math expression (such as i=5+6 or 5+6=i), or it appears as an index only in the variable to be consumed, with or without conditions to restrict its range. This lets you consume a whole row/column/plane of an array - "dat.x" for instance consumes all possible values of dat, such as dat.0, dat.1, dat.2 etc up to infinity and down to -infinity... ALL of them at the same time. Compilers/interpreters are welcome to support other possibilities (such as simple solutions like "dat.x x+5=8") but support is not guaranteed.
- Some definitions obviously lead to infinite loops (such as "val.x+1 val.x" if started with "val.0") - the interpreter might reinterpret them as ranges or use some kind of lazy evaluation to avoid the infinite loop but no guarantees here!
- Obviously, since the mass of variables consumed by Fatmouse can only grow, some special garbage collection scheme must be used (to figure out when a consumed variable has become irrelevant to new definitions and can thus be tossed). This is left as an exercise to the reader.