Factor is a modern stack-based language, based on Forth, created by Slava Pestov.
Code in this file can be typed into Factor, but not directly imported because the vocabulary and import header would make the beginning thoroughly confusing.
1! This is a comment
2
3! Like Forth, all programming is done by manipulating the stack.
4! Stating a literal value pushes it onto the stack.
55 2 3 56 76 23 65 ! No output, but stack is printed out in interactive mode
6
7! Those numbers get added to the stack, from left to right.
8! .s prints out the stack non-destructively.
9.s ! 5 2 3 56 76 23 65
10
11! Arithmetic works by manipulating data on the stack.
125 4 + ! No output
13
14! `.` pops the top result from the stack and prints it.
15. ! 9
16
17! More examples of arithmetic:
186 7 * . ! 42
191360 23 - . ! 1337
2012 12 / . ! 1
2113 2 mod . ! 1
22
2399 neg . ! -99
24-99 abs . ! 99
2552 23 max . ! 52
2652 23 min . ! 23
27
28! A number of words are provided to manipulate the stack, collectively known as shuffle words.
29
303 dup - ! duplicate the top item (1st now equals 2nd): 3 - 3
312 5 swap / ! swap the top with the second element: 5 / 2
324 0 drop 2 / ! remove the top item (don't print to screen): 4 / 2
331 2 3 nip .s ! remove the second item (similar to drop): 1 3
341 2 clear .s ! wipe out the entire stack
351 2 3 4 over .s ! duplicate the second item to the top: 1 2 3 4 3
361 2 3 4 2 pick .s ! duplicate the third item to the top: 1 2 3 4 2 3
37
38! Creating Words
39! The `:` word sets Factor into compile mode until it sees the `;` word.
40: square ( n -- n ) dup * ; ! No output
415 square . ! 25
42
43! We can view what a word does too.
44! \ suppresses evaluation of a word and pushes its identifier on the stack instead.
45\ square see ! : square ( n -- n ) dup * ;
46
47! After the name of the word to create, the declaration between brackets gives the stack effect.
48! We can use whatever names we like inside the declaration:
49: weirdsquare ( camel -- llama ) dup * ;
50
51! Provided their count matches the word's stack effect:
52: doubledup ( a -- b ) dup dup ; ! Error: Stack effect declaration is wrong
53: doubledup ( a -- a a a ) dup dup ; ! Ok
54: weirddoubledup ( i -- am a fish ) dup dup ; ! Also Ok
55
56! Where Factor differs from Forth is in the use of quotations.
57! A quotation is a block of code that is pushed on the stack as a value.
58! [ starts quotation mode; ] ends it.
59[ 2 + ] ! Quotation that adds 2 is left on the stack
604 swap call . ! 6
61
62! And thus, higher order words. TONS of higher order words.
632 3 [ 2 + ] dip .s ! Pop top stack value, run quotation, push it back: 4 3
643 4 [ + ] keep .s ! Copy top stack value, run quotation, push the copy: 7 4
651 [ 2 + ] [ 3 + ] bi .s ! Run each quotation on the top value, push both results: 3 4
664 3 1 [ + ] [ + ] bi .s ! Quotations in a bi can pull values from deeper on the stack: 4 5 ( 1+3 1+4 )
671 2 [ 2 + ] bi@ .s ! Run the quotation on first and second values
682 [ + ] curry ! Inject the given value at the start of the quotation: [ 2 + ] is left on the stack
69
70! Conditionals
71! Any value is true except the built-in value f.
72! A built-in value t does exist, but its use isn't essential.
73! Conditionals are higher order words as with the combinators above.
74
755 [ "Five is true" . ] when ! Five is true
760 [ "Zero is true" . ] when ! Zero is true
77f [ "F is true" . ] when ! No output
78f [ "F is false" . ] unless ! F is false
792 [ "Two is true" . ] [ "Two is false" . ] if ! Two is true
80
81! By default the conditionals consume the value under test, but starred variants
82! leave it alone if it's true:
83
845 [ . ] when* ! 5
85f [ . ] when* ! No output, empty stack, f is consumed because it's false
86
87
88! Loops
89! You've guessed it.. these are higher order words too.
90
915 [ . ] each-integer ! 0 1 2 3 4
924 3 2 1 0 5 [ + . ] each-integer ! 0 2 4 6 8
935 [ "Hello" . ] times ! Hello Hello Hello Hello Hello
94
95! Here's a list:
96{ 2 4 6 8 } ! Goes on the stack as one item
97
98! Loop through the list:
99{ 2 4 6 8 } [ 1 + . ] each ! Prints 3 5 7 9
100{ 2 4 6 8 } [ 1 + ] map ! Leaves { 3 5 7 9 } on stack
101
102! Loop reducing or building lists:
103{ 1 2 3 4 5 } [ 2 mod 0 = ] filter ! Keeps only list members for which quotation yields true: { 2 4 }
104{ 2 4 6 8 } 0 [ + ] reduce . ! Like "fold" in functional languages: prints 20 (0+2+4+6+8)
105{ 2 4 6 8 } 0 [ + ] accumulate . . ! Like reduce but keeps the intermediate values in a list: prints { 0 2 6 12 } then 20
1061 5 [ 2 * dup ] replicate . ! Loops the quotation 5 times and collects the results in a list: { 2 4 8 16 32 }
1071 [ dup 100 < ] [ 2 * dup ] produce ! Loops the second quotation until the first returns false and collects the results: { 2 4 8 16 32 64 128 }
108
109! If all else fails, a general purpose while loop:
1101 [ dup 10 < ] [ "Hello" . 1 + ] while ! Prints "Hello" 10 times
111 ! Yes, it's hard to read
112 ! That's what all those variant loops are for
113
114! Variables
115! Usually Factor programs are expected to keep all data on the stack.
116! Using named variables makes refactoring harder (and it's called Factor for a reason)
117! Global variables, if you must:
118
119SYMBOL: name ! Creates name as an identifying word
120"Bob" name set-global ! No output
121name get-global . ! "Bob"
122
123! Named local variables are considered an extension but are available
124! In a quotation..
125[| m n ! Quotation captures top two stack values into m and n
126 | m n + ] ! Read them
127
128! Or in a word..
129:: lword ( -- ) ! Note double colon to invoke lexical variable extension
130 2 :> c ! Declares immutable variable c to hold 2
131 c . ; ! Print it out
132
133! In a word declared this way, the input side of the stack declaration
134! becomes meaningful and gives the variable names stack values are captured into
135:: double ( a -- result ) a 2 * ;
136
137! Variables are declared mutable by ending their name with a shriek
138:: mword2 ( a! -- x y ) ! Capture top of stack in mutable variable a
139 a ! Push a
140 a 2 * a! ! Multiply a by 2 and store result back in a
141 a ; ! Push new value of a
1425 mword2 ! Stack: 5 10
143
144! Lists and Sequences
145! We saw above how to push a list onto the stack
146
1470 { 1 2 3 4 } nth ! Access a particular member of a list: 1
14810 { 1 2 3 4 } nth ! Error: sequence index out of bounds
1491 { 1 2 3 4 } ?nth ! Same as nth if index is in bounds: 2
15010 { 1 2 3 4 } ?nth ! No error if out of bounds: f
151
152{ "at" "the" "beginning" } "Append" prefix ! { "Append" "at" "the" "beginning" }
153{ "Append" "at" "the" } "end" suffix ! { "Append" "at" "the" "end" }
154"in" 1 { "Insert" "the" "middle" } insert-nth ! { "Insert" "in" "the" "middle" }
155"Concat" "enate" append ! "Concatenate" - strings are sequences too
156"Concatenate" "Reverse " prepend ! "Reverse Concatenate"
157{ "Concatenate " "seq " "of " "seqs" } concat ! "Concatenate seq of seqs"
158{ "Connect" "subseqs" "with" "separators" } " " join ! "Connect subseqs with separators"
159
160! And if you want to get meta, quotations are sequences and can be dismantled..
1610 [ 2 + ] nth ! 2
1621 [ 2 + ] nth ! +
163[ 2 + ] \ - suffix ! Quotation [ 2 + - ]