ComeFrom Language is an estoric programming language. It explores what is possible given only the most unwieldy of flow control operators, 'comefrom' and 'comefromif'. While the idea is certainly not unique, nor at 40 years of age new, it has previously lacked some accessibility. Any language implementing it, such as INTERCAL, has way too much cruft to let you focus on the problem of comefrom. CFL therefore features a minimal stack-based language which is easy to learn and read, and a nice web-based IDE to program in.
Every command must have a line number, then a value or a command. CFL is a stack-based language, and values and commands all use the same first-in, last-out stack.
Values are pushed to the stack, from which commands will take them and process them. To put a string on the stack, preface it with a $. To put a number on the stack, preface it with a #. Example: The program 10 #3.14
puts the number 3.14 on the stack. Note: It is only customary for the lines to appear in order in the source code, not required.
There are two types of commands. Normal commands, when run, take their arguments off the top of the stack and put their return values on the stack when they're done. Infix commands, however, are put on the operator stack while they wait for more arguments. For example, the divider operator, /, is an infix command. Running 10 #2, 20 /, 30 #4
would leave the value of 0.5 on the stack. 10 $hello, 20 +, 30 $ there
would leave "hello there" on the stack.
Comefrom and comefromif are special cases, and are written as 10 comefrom 20
.
Comments are written as 10 !this is a comment
.
If two or more instructions share a line number, one is chosen at random each time that line is executed. 10 comefrom 20, 20 #0, 20 #1
will push 0s and 1s to the stack.
A statement is terminated by a comma or a newline, unless preceded by a comma. In that case, the preceding comma will be removed from the program. Most of the examples here use commas for compactness.
Math Operators | ||
---|---|---|
+, -, *, /, % | Infix operators. Add, subtract, multiply, divide, or take the modulo of the next number to be pushed to the stack with the number currently on the stack. Only addition works with strings, concatenating them. | 10 2, 20 /, 30 4 → [0.5] |
<, =, > | Infix operators. Compare the number currently on the stack with the next to be put on the stack, returning 1 if the assertion was true and 0 if not. | 10 2, 20 <, 30 4 → [1] |
^ | Infix operator. Raise the number on the stack to the power of the next number to be put on the stack. | 10 2, 20 ^, 30 4 → [16] |
calljs | Call a javascript function. For example, take the stack ['Enter your password:', 1, 'prompt<window']. When calljs is executed it will prompt the user to enter their password and put the result on the stack. The newest value on the stack is the name of the function to call. eg; 'prompt<window' calls Javascript's window.prompt function. The number indicates the arity of the function, and then pops that many variables off the stack to feed to the function. A value which is not a number, boolean, or string is interpreted as nil. See also: readjs | → [$my_password] |
comefrom | Come from a line number to this line. | 10 $cfl , 20 comefrom 40, 30 dup, 40 print prints "cfl" repeatedly |
comefromif | Comes from a line number to this line, IF the top value on the stack is truthy. The value is not removed from stack. Truthy values are anything other than #0, $, nul, or the empty stack. | 10 #5, 20 comefromif 40, 30 -, 40 #1 → [0] |
depth | Puts the size of the stack prior to the instruction to the stack. | 10 #100, 20 #101, 30 depth → [#100, #101, #2] |
drop | Removes the first value on the stack. | 10 #5, 20 drop → [] |
dup | Takes the first value on the stack and adds it to the stack twice, duplicating it. | 10 $test1, 20 $test2, 30 drop → [$test1] |
log | Pop a value to stack, and print it to the javascript console. See also: print, println | 10 #2, 20 log → [] |
nop | Take no action. Very easy to implement! | 10 nop → [] |
not | Logically negate the first value on the stack. (In CFL, truthy values are anything other than #0, $, or nul.) | 10 "hello", 20 not → [0] |
num | Cast the top value on the stack to a number. See also: str | 10 $512, 20 num → [#512] |
Print and remove the top value of the stack. See also: log, println | 10 $hi, 20 print → [] |
|
println | Print and remove the top value of the stack. Advance the cursor to a new line. See also: log, print | 10 $hi, 20 println → [] |
reach | Reach back in the stack, read a value, and push the read value to the stack. Does not remove the read value. 10 #5, 20 #1, 30 reach is equivalent to 10 #5, 20 dup . |
10 $A, 20 $C, 30 $D, 40 #2, 50 reach → [$A, $C, $D, $C] |
readjs | Read a javascript value as given by the js path string on top of the stack. For example, to read Math.PI, we'd push $PI<Math to the stack and then invoke readjs. See also: calljs | 10 $PI<Math, 20 readjs → [#3.141592653589793] |
str | Cast the top value on the stack to a string. See also: num | 10 #512, 20 str → [$512] |
swap | Reverse the position of the top two values on the stack. | 10 #1, 20 #2, 30 swap → [#2, #1] |
10 #1
20 /
30 #2
40 print
Skipping a Command
10 $a
20 $b
30 $c
40 comefrom 20
50 log
Adding Three Numbers
10 #5
20 #3
30 +
40 +
50 #4
Counting Up to Ten
10 #0
20 comefrom 120
30 +
40 #1
50 dup
55 dup
60 <
70 #10
80 drop
100 comefromif 75
120 drop
130 comefrom 90
145 drop
99 Bottles of Beer
0 !99 Bottles v1.0.0
10 #99
19 nul
20 comefromif 205
21 drop
30 dup
40 str
45 dup
50 +
60 $ bottles of beer on the wall,,
70 println
80 +
90 $ bottles of beer.\nTake one down,, pass it around,,
100 println
120 -
130 #1
140 dup
145 swap
150 str
155 +
160 $ bottles of beer on the wall.\n
170 println
180 dup
190 >
200 #2
215 $1 bottle of beer on the wall,,\n1 bottle of beer.
214 $Take one down,, pass it around,,\nno more bottles of beer on the wall.\n
213 $No more bottles of beer on the wall,,\nno more bottles of beer.
212 $Go to the store,, buy some more,,
211 $99 bottles of beer on the wall.\n
210 $... oh fine I'll stop now.
218 !loop until we've printed the entire stack we just filled
219 comefromif 220
220 println
How about a return statement, gotoing the last line we camefrom? That would allow truly invisible flow.