FAKE: a weird programming language


ible

professional vim user
Joined
Mar 24, 2014
Messages
2,589
Location
Seattle, WA
I came up with an esoteric programming language, and an interpreter for it...

At some point, I'd like to write this in C with much more matrix functionality (diagonalization, etc.), but it's easier to play with a new language on the internet, so I made a javascript interpreter for your enjoyment/puzzlement:

https://patchsoul.github.io/FAKE/

Click on the "Show Help" to get a good overview/tutorial of the possible stuffs. (I'll copy it into the spoiler below, though it will be easier to navigate at the site.) I saw MATL a few weeks back, and decided to create a similar language -- each letter/symbol is some function, functions operate on a stack, the operators/functions are postfix; though some functions have instructions (e.g. for branching) which appear on the right.

For example:
Code:
"hello world!"  # prints hello world.
5 4 / # pushes 1.25 to the stack.
0!["First branch executed"]["Second branch executed"] # ! is a conditional branch, takes two instructions on the right, pops stack to figure out which one to execute.
'hello'z"Hello to `y`!"   # creates a function `hello`, which prints "Hello to "+ whatever is on the top of the stack
'you' `hello`
'Q'z0.25  # create a function `Q`, which pushes 0.25 to the stack
Q  # invoke any single-character function without backticks, or with, your choice :)

You can also create functions for things like ö, which is fun :).

It may be easier to navigate this mess on the website, but here it is anyways ...
Code:
# Terminal Commands
   # This terminal has super awesome features, with multi-line code areas possible.
   # Navigate history with up and down arrows.
   # Press enter to execute, and shift-enter to create a newline in the current code area.
   # You can swap this behavior by clicking on the [Enter to execute] button above.

# Numbers and Basic Math: +- */ ^_
   #       # a hash symbol means ignore the remaining input to end-of-line, i.e. make a comment.
   0.5     # push a number to the stack.  a number begins with 0-9, so start with 0. to do fractions.
   -5.3    # push a negative number to the stack, i.e. - is unary minus if 0-9 follows.
   +-*/    # standard mathematical operators, postfix notation, operating on numbers already on the stack.
   ^       # power, takes NOS as base and TOS as exponent.
   _       # natural log, determines ln of TOS.
           #   to take an arbitrary log, base B, of some number N:
           #       N_B_/   # infix: log_B(N) = log(N)/log(B)

   # examples

   0.5 2 - # result is -1.5
   8.5 2 * # result is 17
   5 3 ^   # result is 125
   42_     # result is 3.737...

# Stack Operators: e dyYp ~ n
   # TOS is "top of stack", and NOS is "next on stack."

   e   # identity operator, used for keeping track of stack index when printing the stack.
       #   executes next instruction as is.
       #   CANNOT be used to do scientific notation.
   d   # duplicate: deep copy of TOS.
   y   # copy: shallow copy TOS.  numbers and strings get deep copied.
   Y   # double copy: shallow copy NOS and TOS, to same relative positions.
   p   # pop the TOS, i.e., remove it from the stack.
   ~   # swap the TOS and NOS.
   n   # push number of elements to stack.

   # examples

   5 4Y    # yields 5 4 5 e4
   6n      # if nothing else in the stack, yields 6 e1
   5d*     # yields 25, duplicates 5 and multiplies together.
   5y*     # same as 5d*
   1 2 3~  # yields 1 3 e2

# Statements via Various Parentheses: () [] {}
   # All parentheses [] {} () create a statement based on the functions inside;
   #   A statement acts like a single instruction, but can be composed of multiple instructions.
   #   The statements () {} and () differ in their behavior, though.

   []  # create a normal statement, which acts as one instruction.
   {}  # create a new stack; any internal instructions push elements to this stack.
       #   the new stack appears on the old stack as an array element.
   ()  # this statement lowers the stack index without popping the original TOS.  (NOS becomes TOS.)
       #   instructions inside the () will act on the original NOS as if it was the TOS.
       #   while the stack index is lowered,
       #       any elements which get pushed to the stack get inserted into the stack,
       #       and the original TOS gets moved up further.
       #   the original TOS will reappear at the end of the () statement.
   
   # examples
   {1 2 3} # pushes the array {1 2 3} to the stack.
   [45.5 33.3]  # yields 45.5 e33.3
   1 5d(+) # yields 6 e5

# Strings, Print Strings, and Execution Strings: '' "" ``
   ''  # push a string to the stack.
   ""  # print string to output.
   ``  # execute the enclosed instruction.
       # this is useful for multi-character instructions, e.g. `function`.  (see "Function definitions".)
       # `` can also be nested inside a '' or "" string, which executes the instruction inside
       #   and then pops the TOS to write to that point in the string.  YAY for arbitrary code execution!

   # examples
   
   "hello world!"      # this is the hello world program.
   'this is a string'  # pushed to stack.
   3'what is the TOS?  it is `y`.'
                       # `y` executes and duplicates what is on the stack (3), which gets popped and written into the string.

# Context-dependent Printing: ,;
   # The comma and semi-colon are useful for printing the TOS to output,
   # though in some circumstances (e.g. check out Matrix Definition) they will do different things.

   ,   # print TOS and pop it, but don't use a newline after printing.  (use a space.)
   ;   # print TOS and pop it, appending a newline to the output.

   # examples

   'hello!';   # prints 'hello!' to output.  could also be done with "hello!" (see Print Strings).
   1,2,3,4,5;  # prints "1 2 3 4 5" to output (newline after 5).

# Comparisons, Logic, and Conditional Branching: <=> ?&| !
   <   # push NOS < TOS (result is 0 or 1), after popping both.
   >   # push NOS > TOS (result is 0 or 1), after popping both.
   =   # check if NOS is equal to TOS (result is 0 or 1), after popping both.

   ?   # push logical value of TOS (either 1 or 0), based on value of TOS.
   &   # push logical value (0 or 1) of NOS logical-AND TOS, after popping both.
   |   # push logical value (0 or 1) of NOS logical-OR TOS, after popping both.

   !   # NOT conditional: branch based on logical value of TOS, and pop it.
       #   execute first subsequent instruction if TOS was logically zero,
       #   otherwise execute the second instruction.

   # examples

   5 3 <   # result is e0
   4 4 =   # result is e1
   5 5 >   # result is e0
   6 0 >   # result is e1

   123?    # stack is 123 e1
   0?      # stack is 0 e0
   3 4&    # stack is e1
   0 3&    # result is e0
   3 0|    # result is e1
   0 0|    # result is e0

   0!["TOS was zero/null"100]["TOS was non-null"1000]
           # end stack result is e100
   3 4< !["got not branch (>=)"]["got yes branch (<)"]

# Loop Operators: j k l
   # loops use the letters jkl, which are right in a row, and thus easy to remember!

   l   # loop statement; will loop over the next instruction if the jump operator is hit.
   j   # jump back to start of loop, or "continue", though this is required for a loop to repeat.
   k   # kill or quit loop, or "breaK", or kick out of loop prematurely.

   # examples

   10l[?!k"hello `y`!"1-j] # counts down from 10 to 1, leaves 0 on the stack.

# Function definitions: abc zZ
   # Functions are defined with up to three subsequent instructions.

   abc # pop the TOS, use a string to define a function name.  abc are placeholders for instructions.
       #   the next instruction is interpreted as the function definition, which can use the placeholder instructions.
   z   # define a zero-instruction function, popping the TOS for a function name.
   Z   # define a variable zero-instruction function, popping TOS for a function name.
       #   i.e., the function can be changed later to something else.
   
   # examples
   
   'w'abl[a!kbj]   # create a while loop with two instructions (`ab`);
                   #   the first is executed to check whether to continue the loop, and if so, the second is executed
   1 2 3 4 5wn["hi `y`"p]
       # the first instruction is `n` (number of elements on stack), the second prints a string and pops the TOS

   'H'z0.5     # create a constant variable function (pushes 0.5 to stack)
   3H*         # yields e1.5
   'q'z[0.5^]  # create a square root function
   9q          # yields e3
   'inv'z[-1^] # create inverse function
   9`inv`      # yields e0.1111...
   'Q'Z0.25    # create variable zero-instruction function
   Q           # yields e0.25
   'Q'Z[4/]    # redefine Q to divide; requires something on TOS
   5Q          # yields e1.25

   # See also "Dictionary definitions" for some advanced function definition uses.

# Dictionary definitions: aa bb cc
   # Dictionaries are simply functions with multiple instructions.
   # They have their own context for functions.

   # examples

   'A'aa                       # create a dictionary A, which is an identity operation.
   A['Z'z["hello arizona!"]]   # create a subdictionary Z, defined only inside the A context.
   AZ                          # call Z from within an A context, prints "hello arizona!"
   'B'Aaa                      # create subdictionary B
   'C'ABz["123"]               # create sub-sub-function C
   ABC                         # prints 123
   AB['E'z"four score and seven years ago"]
   ABE                         # prints iconic line.

   # It can get way more complicated than this, but these are the fundamentals.

# Matrix Definitions: m ,;
   # You can build matrices using the `m` command.
   # `m` is a loop specification, so you can use the functions `j` and `k` to write the matrix.
   
   m   # take two arguments from the stack to get matrix dimensions (NOS for rows, TOS for columns), popping them,
       #   and execute next instruction to build the matrix.
   ,   # in the instruction after `m`, this pushes the TOS to the matrix.
   ;   # similarly, this pushes the TOS to the matrix, and fills the remainder of the row with zeroes.
   
   # examples

   2 2 m[1,0,0,1]  # build 2x2 identity matrix.
       # note that if the final instruction is to push a number to the stack,
       # it will be pushed to the matrix.  (i.e., the final 1 did not need a subsequent comma.)
   2dm[1;0,1]      # equivalent; d duplicates the 2, so the matrix is 2x2.
   1 5m[1,2,3,4,5] # create a row vector
   0 3dm[1+d,j]    # creates a matrix using a loop; loop terminates once last element is written.

# Array/Stack Definitions: {} SP Sp Se
   # You can build a new stack using squirrely braces: {'instructions go here'}
   # You can think of the new stack as a resizable array, with dynamically-typed elements inside.
   # The new stack will appear on your usual stack, and you can manipulate it with the following functions.

   S   # single-instruction dictionary for stack operations.
   Sp  # pop from the stack on the TOS, push it to the main stack.
   SP  # push the element on the TOS to the stack at the NOS.
   Se  # execute the next instruction using the stack on the TOS as the stack to manipulate.
       #   as a technical note, the \ context for Se is the root context,
       #   so that p inside the statement works to pop an element from the stack, rather than executing Sp
   
   # example (run commands sequentially)

   {1 2 3 'hello'}     # pushes this new stack to the main stack.
   Sp  # result is {1 2 e3} e'hello'   # pops 'hello' from new stack.
   ' and goodbye!'+SP  # pushes 'hello and goodbye' onto the new stack.
   Se["My message for you is `y`" # execute these instructions within/for the new stack.
       l(n!k;j)   # loop through the elements below the string (using the parentheses ()), printing them
   ]   # return control the main stack

It's a bit of practice for some other programming languages I hope to write out someday...

There are probably bugs, and I haven't written in all the features from the README, and I'm not sure I will (at least for the javascript version). I'll probably at least put in random numbers... Feedback and feature requests are welcome, though they won't necessarily be heeded :D

Thought about putting this in the "Development Section", but it's not really a game or something specific to handheld devices...
 
I was reminded of fiddling with my HP 28 calculator... (ah, no surprise, Forth uses UPN).
 
oh yes, and i was earlier inspired by forth. though i didn't learn enough about it to tell you all the differences... (at one point i was writing my own forth compiler, though obviously i didn't get too far...)
 
Nice project. Nevertheless the JS page does not really parse things for me (not even the "hello world"). Ah... after a simple reload it now works again :)

unable to pop with command 'p' (nor combinations 'p 0 +' or '0 p'). How do I do this?
 
Last edited:
well, i should make some contests or "homework" for people.
  1. function to calculate the n'th element of the fibonacci sequence? (using the formula only gets you partial credit ;))
  2. function to test for prime number? (i added % to help with that.)
  3. dare i ask for a sorting algorithm? (given an arbitrary array, e.g. {1 5 2 3}, on the stack, sort it. maybe too evil...)
some extra commands that might be helpful -- just so you know i've done my own homework:
  • R (random number dictionary, Rs, Ru, Rr, and RR for signed integer, unsigned integer, positive real [0, 1) and real [-1,1)) and
  • x (for loop, repeats internal instructions as many times as the number popped from the stack). inside an `x` instruction, you can use `o` to determine how many iterations you have left.
here's an example of the for loop, and a very popular song:
Code:
99x"`o` bottles of beer on the wall, `o` bottles of beer; take one down, pass it around, `e`[o1-] bottles of beer on the wall!"

you may need a hard reload (shift+ctrl+R) to get some of the new features.
 
Back
Top