]> gitweb.factorcode.org Git - factor.git/blob - basis/help/tutorial/tutorial.factor
Fix permission bits
[factor.git] / basis / help / tutorial / tutorial.factor
1 USING: help.markup help.syntax ui.commands ui.operations
2 ui.tools.search ui.tools.workspace editors vocabs.loader
3 kernel sequences prettyprint tools.test tools.vocabs strings
4 unicode.categories unicode.case ;
5 IN: help.tutorial
6
7 ARTICLE: "first-program-start" "Creating a vocabulary for your first program"
8 "Factor source code is organized into " { $link "vocabularies" } ". Before we can write our first program, we must create a vocabulary for it."
9 $nl
10 "Start by asking Factor for the path to your ``work'' directory, where you will place your own code:"
11 { $code "\"work\" resource-path ." }
12 "Open the work directory in your file manager, and create a subdirectory named " { $snippet "palindrome" } ". Inside this directory, create a file named " { $snippet "palindrome.factor" } " using your favorite text editor. Leave the file empty for now."
13 $nl
14 "Inside the Factor listener, type"
15 { $code "USE: palindrome" }
16 "The source file should now load. Since it is empty, it does nothing. If you get an error message, make sure you created the directory and the file in the right place and gave them the right names."
17 $nl
18 "Now, we will start filling out this source file. Go back to your editor, and type:"
19 { $code
20     "! Copyright (C) 2008 <your name here>"
21     "! See http://factorcode.org/license.txt for BSD license."
22 }
23 "This is the standard header for Factor source files; it consists of two " { $link "syntax-comments" } "."
24 $nl
25 "Now, we tell Factor that all definitions in this source file should go into the " { $snippet "palindrome" } " vocabulary using the " { $link POSTPONE: IN: } " word:"
26 { $code "IN: palindrome" }
27 "You are now ready to go on to the next section: " { $link "first-program-logic" } "." ;
28
29 ARTICLE: "first-program-logic" "Writing some logic in your first program"
30 "Your " { $snippet "palindrome.factor" } " file should look like the following after the previous section:"
31 { $code
32     "! Copyright (C) 2008 <your name here>"
33     "! See http://factorcode.org/license.txt for BSD license."
34     "IN: palindrome"
35 }
36 "We will now write our first word using " { $link POSTPONE: : } ". This word will test if a string is a palindrome; it will take a string as input, and give back a  boolean as output. We will call this word " { $snippet "palindrome?" } ", following a naming convention that words returning booleans have names ending with " { $snippet "?" } "."
37 $nl
38 "Recall that a string is a palindrome if it is spelled the same forwards or backwards; that is, if the string is equal to its reverse. We can express this in Factor as follows:"
39 { $code ": palindrome? ( string -- ? ) dup reverse = ;" }
40 "Place this definition at the end of your source file."
41 $nl
42 "Now we have changed the source file, we must reload it into Factor so that we can test the new definition. To do this, simply go to the Factor workspace and press " { $command workspace "workflow" refresh-all } ". This will find any previously-loaded source files which have changed on disk, and reload them."
43 $nl
44 "When you do this, you will get an error about the " { $link dup } " word not being found. This is because this word is part of the " { $vocab-link "kernel" } " vocabulary, but this vocabulary is not part of the source file's " { $link "vocabulary-search" } ". You must explicitly list dependencies in source files. This allows Factor to automatically load required vocabularies and makes larger programs easier to maintain."
45 $nl
46 "To add the word to the search path, first convince yourself that this word is in the " { $vocab-link "kernel" } " vocabulary by entering the following in the listener:"
47 { $code "\\ dup see" }
48 "This shows the definition of " { $link dup } ", along with an " { $link POSTPONE: IN: } " form."
49 $nl
50 "Now, add the following at the start of the source file:"
51 { $code "USING: kernel ;" }
52 "Next, find out what vocabulary " { $link reverse } " lives in:"
53 { $code "\\ reverse see" }
54 "It lives in the " { $vocab-link "sequences" } " vocabulary, so we add that to the search path:"
55 { $code "USING: kernel sequences ;" }
56 "Finally, check what vocabulary " { $link = } " lives in:"
57 { $code "\\ = see" }
58 "It's in the " { $vocab-link "kernel" } " vocabulary, which we've already added to the search path."
59
60 "Now press " { $command workspace "workflow" refresh-all } " again, and the source file should reload without any errors. You can now go on and learn about " { $link "first-program-test" } "." ;
61
62 ARTICLE: "first-program-test" "Testing your first program"
63 "Your " { $snippet "palindrome.factor" } " file should look like the following after the previous section:"
64 { $code
65     "! Copyright (C) 2008 <your name here>"
66     "! See http://factorcode.org/license.txt for BSD license."
67     "IN: palindrome"
68     "USING: kernel sequences ;"
69     ""
70     ": palindrome? ( str -- ? ) dup reverse = ;"
71 }
72 "We will now test our new word in the listener. First, push a string on the stack:"
73 { $code "\"hello\"" }
74 "Note that the stack display at the top of the workspace now shows this string. Having supplied the input, we call our word:"
75 { $code "palindrome?" }
76 "The stack display should now have a boolean false - " { $link f } " - which is the word's output. Since ``hello'' is not a palindrome, this is what we expect. We can get rid of this boolean by calling " { $link drop } ". The stack should be empty after this is done."
77 $nl
78 "Now, let's try it with a palindrome; we will push the string and call the word in the same line of code:"
79 { $code "\"racecar\" palindrome?" }
80 "The stack should now contain a boolean true - " { $link t } ". We can print it and drop it using the " { $link . } " word:"
81 { $code "." }
82 "What we just did is called " { $emphasis "interactive testing" } ". A more advanced technique which comes into play with larger programs is " { $link "tools.test" } "."
83 $nl
84 "Create a file named " { $snippet "palindrome-tests.factor" } " in the same directory as " { $snippet "palindrome.factor" } ". Now, we can run unit tests from the listener:"
85 { $code "\"palindrome\" test" }
86 "We will add some unit tests corresponding to the interactive tests we did above. Unit tests are defined with the " { $link unit-test } " word, which takes a sequence of expected outputs, and a piece of code. It runs the code, and asserts that it outputs the expected values."
87 $nl
88 "Add the following three lines to " { $snippet "palindrome-tests.factor" } ":"
89 { $code
90     "USING: palindrome tools.test ;"
91     "[ f ] [ \"hello\" palindrome? ] unit-test"
92     "[ t ] [ \"racecar\" palindrome? ] unit-test"
93 }
94 "Now, you can run unit tests:"
95 { $code "\"palindrome\" test" }
96 "It should report that all tests have passed. Now you can read about " { $link "first-program-extend" } "." ;
97
98 ARTICLE: "first-program-extend" "Extending your first program"
99 "Our palindrome program works well, however we'd like to extend it to ignore spaces and non-alphabetical characters in the input."
100 $nl
101 "For example, we'd like it to identify the following as a palindrome:"
102 { $code "\"A man, a plan, a canal: Panama.\"" }
103 "However, right now, the simplistic algorithm we use says this is not a palindrome:"
104 { $example "\"A man, a plan, a canal: Panama.\" palindrome?" "f" }
105 "We would like it to output " { $link t } " there. We can encode this requirement with a unit test that we add to " { $snippet "palindrome-tests.factor" } ":"
106 { $code "[ t ] [ \"A man, a plan, a canal: Panama.\" palindrome? ] unit-test" }
107 "If you now run unit tests, you will see a unit test failure:"
108 { $code "\"palindrome\" test" }
109 "The next step is to, of course, fix our code so that the unit test can pass."
110 $nl
111 "We begin by writing a word called " { $snippet "normalize" } " which removes blanks and non-alphabetical characters from a string, and then converts the string to lower case. We call this word " { $snippet "normalize" } ". To figure out how to write this word, we begin with some interactive experimentation in the listener."
112 $nl
113 "Start by pushing a character on the stack; notice that characters are really just integers:"
114 { $code "CHAR: a" }
115 "Now, use the " { $link Letter? } " word to test if it is an alphabetical character, upper or lower case:"
116 { $example "Letter? ." "t" }
117 "This gives the expected result."
118 $nl
119 "Now try with a non-alphabetical character:"
120 { $code "CHAR: #" }
121 { $example "Letter? ." "f" }
122 "What we want to do is given a string, remove all characters which do not match the " { $link Letter? } " predicate. Let's push a string on the stack:"
123 { $code "\"A man, a plan, a canal: Panama.\"" }
124 "Now, place a quotation containing " { $link Letter? } " on the stack; quoting code places it on the stack instead of executing it immediately:"
125 { $code "[ Letter? ]" }
126 "Finally, pass the string and the quotation to the " { $link filter } " word:"
127 { $code "filter" }
128 "Now the stack should contain the following string:"
129 { "\"AmanaplanacanalPanama\"" }
130 "This is almost what we want; we just need to convert the string to lower case now. This can be done by calling " { $link >lower } "; the " { $snippet ">" } " prefix is a naming convention for conversion operations, and should be read as ``to'':"
131 { $code ">lower" }
132 "Finally, let's print the top of the stack and discard it:"
133 { $code "." }
134 "This will output " { $snippet "amanaplanacanalpanama" } ". This string is in the form that we want, and we evaluated the following code to get it into this form:"
135 { $code "[ Letter? ] filter >lower" }
136 "This code starts with a string on the stack, removes non-alphabetical characters, and converts the result to lower case, leaving a new string on the stack. We put this code in a new word, and add the new word to " { $snippet "palindrome.factor" } ":"
137 { $code ": normalize ( str -- newstr ) [ Letter? ] filter >lower ;" }
138 "You will need to add " { $vocab-link "unicode.case" } " and " { $vocab-link "unicode.categories" } " to the vocabulary search path, so that " { $link Letter? } " can be used in the source file."
139 $nl
140 "We modify " { $snippet "palindrome?" } " to first apply " { $snippet "normalize" } " to its input:"
141 { $code ": palindrome? ( str -- ? ) normalize dup reverse = ;" }
142 "Now if you press " { $command workspace "workflow" refresh-all } ", the source file should reload without any errors. You can run unit tests again, and this time, they will all pass:"
143 { $code "\"palindrome\" test" } ;
144
145 ARTICLE: "first-program" "Your first program"
146 "In this tutorial, we will write a simple Factor program which prompts the user to enter a word, and tests if it is a palindrome (that is, the word is spelled the same backwards and forwards)."
147 $nl
148 "In this tutorial, you will learn about basic Factor development tools, as well as application deployment."
149 { $subsection "first-program-start" }
150 { $subsection "first-program-logic" }
151 { $subsection "first-program-test" }
152 { $subsection "first-program-extend" } ;
153
154 ABOUT: "first-program"