checkin of Factor Developer's Handbook
parent
09123b279a
commit
dfd3901a39
2859
doc/devel-guide.tex
2859
doc/devel-guide.tex
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,78 +0,0 @@
|
|||
FACTOR CODING CONVENTIONS.
|
||||
|
||||
=== Naming words
|
||||
|
||||
foo. - perform "foo", but instead of pushing the result on the
|
||||
stack, print it in a human-readable form suitable for
|
||||
interactive use.
|
||||
|
||||
Eg: words. vocabs.
|
||||
|
||||
.X - four words to print the contents of the stacks:
|
||||
.s - data stack
|
||||
.r - call stack
|
||||
.n - name stack
|
||||
.c - catch stack
|
||||
|
||||
foo* - a variation of "foo" that takes more parameters.
|
||||
|
||||
Eg: index-of* parse* random-element*
|
||||
|
||||
- a lower-level word used in the implementation of "foo".
|
||||
|
||||
Eg: compile* prettyprint*
|
||||
|
||||
- a word that is a variation on "foo", but is more specialized
|
||||
and less frequently used.
|
||||
|
||||
Eg: last* get*
|
||||
|
||||
(foo) - a word that is only useful in the implementation of "foo".
|
||||
|
||||
Eg: (vector=) (split)
|
||||
|
||||
>to - convert object to type "to".
|
||||
|
||||
Eg: >str >lower >upper >fixnum >realnum
|
||||
|
||||
- move top of data stack "to" stack.
|
||||
|
||||
Eg: >r >n >c
|
||||
|
||||
from> - convert object from type "from".
|
||||
|
||||
Eg: dec> oct> hex>
|
||||
|
||||
- move top of "from" stack to data stack.
|
||||
|
||||
Eg: r> n> c>
|
||||
|
||||
one>two - convert object of type "one" to "two".
|
||||
|
||||
Eg: stream>str stack>list worddef>list
|
||||
|
||||
- transfer values between stacks.
|
||||
|
||||
Eg: >r r> 2>r 2r> >n
|
||||
|
||||
<type> - create an object of "type".
|
||||
|
||||
Eg: <namespace> <sbuf> <stream>
|
||||
|
||||
foo@ - get the value of a variable at the top of the stack;
|
||||
operate on the value with "foo"; store the value back in the
|
||||
variable.
|
||||
|
||||
Eg: +@ *@ -@ /@ cons@ append@
|
||||
|
||||
foo-iter - a tail-recursive word used in the implementatin of "foo".
|
||||
|
||||
Eg: nreverse-iter partition-iter
|
||||
|
||||
nfoo - on lists, a destructive (non-consing) version of "foo".
|
||||
|
||||
Eg: nappend nreverse
|
||||
|
||||
2foo - like foo but with two operands taken from stack.
|
||||
|
||||
Eg: 2drop 2dup 2each
|
704
doc/tools.tex
704
doc/tools.tex
|
@ -1,704 +0,0 @@
|
|||
\documentclass{article}
|
||||
\usepackage{times}
|
||||
\usepackage{tabularx}
|
||||
\usepackage{alltt}
|
||||
|
||||
\newcommand{\ttbs}{\symbol{92}}
|
||||
\newcommand{\tto}{\symbol{123}}
|
||||
\newcommand{\ttc}{\symbol{125}}
|
||||
\begin{document}
|
||||
|
||||
\title{The Factor Development Environment}
|
||||
\author{Slava Pestov}
|
||||
|
||||
\maketitle
|
||||
|
||||
\tableofcontents
|
||||
|
||||
\section{Introduction}
|
||||
|
||||
This article covers the interactive development environment for the Factor programming language, whose homepage is located at \texttt{http://factor.sf.net}.
|
||||
|
||||
Factor supports interactive development in a live environment. Instead of working with
|
||||
static executable files and restarting your application after each change, you can
|
||||
incrementally make changes to your application and test them immediately. If you
|
||||
notice an undesirable behavior, Factor's powerful reflection features will aid in
|
||||
pinpointing the error.
|
||||
|
||||
If you are used to a statically typed language, you might find Factor's tendency to only fail at runtime hard to work with at first. However, the interactive development tools outlined in this document allow a much quicker turn-around time for testing changes. Also, write unit tests -- unit testing is a great way to ensure that old bugs do not re-appear once they've been fixed.
|
||||
|
||||
\section{System organization}
|
||||
|
||||
\subsection{The listener}
|
||||
|
||||
Factor is an \emph{image-based environment}. When you compiled Factor, you also generated a file named \texttt{factor.image}. I will have more to say about images later, but for now it suffices to understand that to start Factor, you must pass the image file name on the command line:
|
||||
|
||||
\begin{alltt}
|
||||
./f factor.image
|
||||
\textbf{Loading factor.image... relocating... done
|
||||
Factor 0.73 :: http://factor.sourceforge.net :: unix/x86
|
||||
(C) 2003, 2005 Slava Pestov, Chris Double,
|
||||
Mackenzie Straight
|
||||
ok}
|
||||
\end{alltt}
|
||||
|
||||
|
||||
An \texttt{\textbf{ok}} prompt is printed after the initial banner, indicating the listener is ready to execute Factor phrases. The listener is a piece of Factor code, like any other; however, it helps to think of it as the primary interface to the Factor system. The listener reads Factor code and executes it. You can try the classical first program:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} "Hello, world." print
|
||||
\textbf{Hello, world.}
|
||||
\end{alltt}
|
||||
|
||||
|
||||
Multi-line phrases are supported; if there are unclosed brackets, the listener outputs \texttt{...} instead of the \texttt{ok} prompt, and the entire phrase is executed once all brackets are closed:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} [ 1 2 3 ] [
|
||||
\textbf{...} .
|
||||
\textbf{...} ] each
|
||||
\textbf{1
|
||||
2
|
||||
3}
|
||||
\end{alltt}
|
||||
|
||||
The listener knows when to print a continuation prompt by looking at the height of the
|
||||
stack. Parsing words such as \texttt{[} and \texttt{:} leave elements on the parser
|
||||
stack; these elements are popped by \texttt{]} and \texttt{;}.
|
||||
|
||||
\subsection{Source files}
|
||||
|
||||
While it is possible to do all development in the listener and save your work in images, it is far more convenient to work with source files, at least until an in-image structure editor is developed.
|
||||
|
||||
By convention, Factor source files are saved with the \texttt{.factor} filename extension. They can be loaded into the image as follows:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} "examples/numbers-game.factor" run-file
|
||||
\end{alltt}
|
||||
|
||||
In Factor, loading a source file replaces any existing definitions\footnote{But see \ref{compiler} for this is not true of compiled code.}. Each word definition remembers what source file it was loaded from (if any). To reload the source file associated with a definition, use the \texttt{reload} word:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} \ttbs draw reload
|
||||
\end{alltt}
|
||||
|
||||
Word definitions also retain the line number where they are located in their original source file. This allows you to open a word definition in jEdit\footnote{\texttt{http://www.jedit.org}} for editing using the
|
||||
\texttt{jedit} word:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} \ttbs compile jedit
|
||||
\end{alltt}
|
||||
|
||||
This word requires that a jEdit instance is already running.
|
||||
|
||||
For the \texttt{jedit} word to work with words in the Factor library, you must set the \texttt{"resource-path"} variable to the location of the Factor source tree. One way to do this is to add a phrase like the following to your \texttt{.factor-rc}:
|
||||
|
||||
\begin{verbatim}
|
||||
"/home/slava/Factor/" "resource-path" set
|
||||
\end{verbatim}
|
||||
|
||||
On startup, Factor reads the \texttt{.factor-rc} file from your home directory. You can put
|
||||
any quick definitions you want available at the listener there. To avoid loading this
|
||||
file, pass the \texttt{-no-user-init} command line switch. Another way to have a set of definitions available at all times is to save a custom image, as described in the next section.
|
||||
|
||||
\subsection{Images}
|
||||
|
||||
The \texttt{factor.image} file is basically a dump of all objects in the heap. A new image can be saved as follows:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} "work.image" save-image
|
||||
\textbf{Saving work.image...}
|
||||
\end{alltt}
|
||||
|
||||
When you save an image before exiting Factor, then start Factor again, everything will be almost as you left it. Try the following:
|
||||
|
||||
\begin{alltt}
|
||||
./f factor.image
|
||||
\textbf{ok} "Learn Factor" "reminder" set
|
||||
\textbf{ok} "factor.image" save-image bye
|
||||
\textbf{Saving factor.image...}
|
||||
\end{alltt}
|
||||
|
||||
Factor will save the image and exit. Now start it again and see that the reminder is still there:
|
||||
|
||||
\begin{alltt}
|
||||
./f factor.image
|
||||
\textbf{ok} "reminder" get .
|
||||
\textbf{"Learn Factor"}
|
||||
\end{alltt}
|
||||
|
||||
This is what is meant by the image being an \emph{infinite session}. When you shut down and restart Factor, what happends is much closer to a Laptop's ``suspend'' mode, than a desktop computer being fully shut down.
|
||||
|
||||
\subsection{Looking at objects}
|
||||
|
||||
Probably the most important debugging tool of them all is the \texttt{.} word. It prints the object at the top of the stack in a form that can be parsed by the Factor parser. A related word is \texttt{prettyprint}. It is identical to \texttt{.} except the output is more verbose; lists, vectors and hashtables are broken up into multiple lines and indented.
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} [ [ \tto 1 \ttc \tto 2 \ttc ] dup car swap cdr ] .
|
||||
[ [ \tto 1 \ttc \tto 2 \ttc ] dup car swap cdr ]
|
||||
\end{alltt}
|
||||
|
||||
Most objects print in a parsable form, but not all. One exceptions to this rule is objects with external state, such as I/O ports or aliens (pointers to native structures). Also, objects with circular or very deeply nested structure will not print in a fully parsable form, since the prettyprinter has a limit on maximum nesting. Here is an example -- a vector is created, that holds a list whose first element is the vector itself:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} \tto \ttc [ unit 0 ] keep [ set-vector-nth ] keep .
|
||||
\tto [ ... ] \ttc
|
||||
\end{alltt}
|
||||
|
||||
The prettyprinted form of a vector or list with many elements is not always readable. The \texttt{[.]} and \texttt{\tto.\ttc} words output a list or a vector, respectively, with each element on its own line. In fact, the stack printing words are defined in terms of \texttt{[.]} and \texttt{\tto.\ttc}:
|
||||
|
||||
\begin{verbatim}
|
||||
: .s datastack {.} ;
|
||||
: .r callstack {.} ;
|
||||
: .n namestack [.] ;
|
||||
: .c catchstack [.] ;
|
||||
\end{verbatim}
|
||||
|
||||
Before we move on, one final set of output words comes is used to output integers in
|
||||
different numeric bases. The \texttt{.b} word prints an integer in binary, \texttt{.o} in octal, and \texttt{.h} in hexadecimal.
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} 31337 .b
|
||||
\textbf{111101001101001}
|
||||
\textbf{ok} 31337 .o
|
||||
\textbf{75151}
|
||||
\textbf{ok} 31337 .h
|
||||
\textbf{7a69}
|
||||
\end{alltt}
|
||||
|
||||
\section{Word tools}
|
||||
|
||||
\subsection{Exploring vocabularies}
|
||||
|
||||
Factor organizes code in a two-tier structure of vocabularies and words. A word is the smallest unit of code; it corresponds to a function or method in other languages. Vocabularies group related words together for easy browsing and tracking of source dependencies.
|
||||
|
||||
Entering \texttt{vocabs .}~in the listener produces a list of all existing vocabularies:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} vocabs .
|
||||
\textbf{[ "alien" "ansi" "assembler" "browser-responder"
|
||||
"command-line" "compiler" "cont-responder" "errors"
|
||||
"file-responder" "files" "gadgets" "generic"
|
||||
"hashtables" "html" "httpd" "httpd-responder" "image"
|
||||
"inference" "interpreter" "io-internals" "jedit"
|
||||
"kernel" "kernel-internals" "line-editor" "listener"
|
||||
"lists" "logging" "math" "math-internals" "memory"
|
||||
"namespaces" "parser" "prettyprint" "profiler"
|
||||
"quit-responder" "random" "resource-responder"
|
||||
"scratchpad" "sdl" "shells" "stdio" "streams"
|
||||
"strings" "syntax" "telnetd" "test" "test-responder"
|
||||
"threads" "unparser" "url-encoding" "vectors" "words" ]}
|
||||
\end{alltt}
|
||||
|
||||
As you can see, there are a lot of vocabularies! Now, you can use \texttt{words .}~to list the words inside a given vocabulary:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} "namespaces" words .
|
||||
\textbf{[ (get) , <namespace> >n append, bind change cons@
|
||||
dec extend get global inc init-namespaces list-buffer
|
||||
literal, make-list make-rlist make-rstring make-string
|
||||
make-vector n> namespace namestack nest off on put set
|
||||
set-global set-namestack unique, unique@ with-scope ]}
|
||||
\end{alltt}
|
||||
|
||||
You can look at the definition of any word, including library words, using \texttt{see}. Keep in mind you might have to \texttt{USE:} the vocabulary first.
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} USE: httpd
|
||||
\textbf{ok} \ttbs httpd-connection see
|
||||
\textbf{IN: httpd : httpd-connection ( socket -- )
|
||||
"http-server" get accept [
|
||||
httpd-client
|
||||
] in-thread drop ;}
|
||||
\end{alltt}
|
||||
|
||||
The \texttt{see} word shows a reconstruction of the source code, not the original source code. So in particular, formatting and some comments are lost.
|
||||
|
||||
\subsection{Cross-referencing words}
|
||||
|
||||
The \texttt{apropos.} word is handy when searching for related words. It lists all words
|
||||
whose names contain a given string. The \texttt{apropos.} word is also useful when you know the exact name of a word, but are unsure what vocabulary it is in. For example, if you're looking for ways to iterate over various collections, you can do an apropos search for \texttt{map}:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} "map" apropos.
|
||||
\textbf{IN: inference
|
||||
type-value-map
|
||||
IN: lists
|
||||
map
|
||||
map-with
|
||||
IN: sdl
|
||||
set-surface-map
|
||||
surface-map
|
||||
IN: strings
|
||||
string-map
|
||||
IN: vectors
|
||||
vector-map}
|
||||
\end{alltt}
|
||||
|
||||
From the above output, you can see that \texttt{map} is for lists, \texttt{string-map} is for strings, and \texttt{vector-map} is for vectors.
|
||||
|
||||
The \texttt{usage} word finds all words that refer to a given word and pushes a list on the stack. This word is helpful in two situations; the first is for learning -- a good way to learn a word is to see it used in context. The second is during refactoring -- if you change a word's stack effect, you must also update all words that call it. Usually you print the
|
||||
return value of \texttt{usage} using \texttt{.}:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} \ttbs string-map usage .
|
||||
\textbf{schars>entities
|
||||
filter-null
|
||||
url-encode}
|
||||
\end{alltt}
|
||||
|
||||
Another useful word is \texttt{usages}. Unlike \texttt{usage}, it finds all usages, even
|
||||
indirect ones -- so if a word refers to another word that refers to the given word,
|
||||
both words will be in the output list.
|
||||
|
||||
\subsection{Exploring classes}
|
||||
|
||||
Factor supports object-oriented programming via generic words. Generic words are called
|
||||
like ordinary words, however they can have multiple definitions, one per class, and
|
||||
these definitions do not have to appear in the same source file. Such a definition is
|
||||
termed a \emph{method}, and the method is said to \emph{specialize} on a certain
|
||||
class. A class in the most
|
||||
general sense is just a set of objects. You can output a list of classes in the system
|
||||
with \texttt{classes .}:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} classes.
|
||||
\textbf{[ alien alien-error byte-array displaced-alien
|
||||
dll ansi-stream disp-only displaced indirect operand
|
||||
register absolute absolute-16/16 relative relative-bitfld
|
||||
item kernel-error no-method border checkbox dialog editor
|
||||
ellipse etched-rect frame gadget hand hollow-ellipse
|
||||
hollow-rect label line menu pane pile plain-ellipse
|
||||
plain-rect rectangle roll-rect scroller shelf slider
|
||||
stack tile viewport world 2generic arrayed builtin
|
||||
complement generic null object predicate tuple
|
||||
tuple-class union hashtable html-stream class-tie
|
||||
computed inference-error inference-warning literal
|
||||
literal-tie value buffer port jedit-stream boolean
|
||||
general-t array cons general-list list bignum complex
|
||||
fixnum float integer number ratio rational real
|
||||
parse-error potential-float potential-ratio
|
||||
button-down-event button-up-event joy-axis-event
|
||||
joy-ball-event joy-button-down-event joy-button-up-event
|
||||
joy-hat-event key-down-event key-up-event motion-event
|
||||
quit-event resize-event user-event sequence stdio-stream
|
||||
client-stream fd-stream null-stream server string-output
|
||||
wrapper-stream LETTER blank digit letter printable sbuf
|
||||
string text POSTPONE: f POSTPONE: t vector compound
|
||||
primitive symbol undefined word ]}
|
||||
\end{alltt}
|
||||
|
||||
If you \texttt{see} a generic word, all methods defined on the generic word are shown.
|
||||
Alternatively, you can use \texttt{methods.} to print all methods specializing on a
|
||||
given class:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} \ttbs list methods.
|
||||
\textbf{PREDICATE: general-list list
|
||||
dup [
|
||||
last* cdr
|
||||
] when not ;
|
||||
IN: gadgets
|
||||
M: list custom-sheet
|
||||
[
|
||||
length count
|
||||
] keep zip alist>sheet "Elements:" <titled> ;
|
||||
IN: prettyprint
|
||||
M: list prettyprint*
|
||||
[
|
||||
[
|
||||
POSTPONE: [
|
||||
] car swap [
|
||||
POSTPONE: ]
|
||||
] car prettyprint-sequence
|
||||
] check-recursion ;}
|
||||
\end{alltt}
|
||||
|
||||
\subsection{Browsing via the HTTP server}
|
||||
|
||||
|
||||
A more sophisticated way to browse the library is using the integrated HTTP server. You can start the HTTP server using the following pair of commands:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} USE: httpd
|
||||
\textbf{ok} 8888 httpd
|
||||
\end{alltt}
|
||||
|
||||
Then, point your browser to the following URL, and start browsing:
|
||||
|
||||
\begin{quote}
|
||||
\texttt{http://localhost:8888/responder/inspect/vocabularies}
|
||||
\end{quote}
|
||||
|
||||
To stop the HTTP server, point your browser to
|
||||
|
||||
\begin{quote}
|
||||
\texttt{http://localhost:8888/responder/quit}.
|
||||
\end{quote}
|
||||
|
||||
You can even start the HTTP in a separate thread, and look at code in your web browser while continuing to play in the listener:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} USE: httpd
|
||||
\textbf{ok} USE: threads
|
||||
\textbf{ok} [ 8888 httpd ] in-thread
|
||||
\end{alltt}
|
||||
|
||||
\section{Dealing with runtime errors}
|
||||
|
||||
\subsection{Looking at stacks}
|
||||
|
||||
To see the contents of the data stack, use the \texttt{.s} word. Similarly, the other stacks can be shown with \texttt{.r} (return stack), \texttt{.n} (name stack), and \texttt{.c} (catch stack). Each stack is printed with each element on its own line; the top of the stack is the first element printed.
|
||||
|
||||
\subsection{The debugger}
|
||||
|
||||
If the execution of a phrase in the listener causes an error to be thrown, the error
|
||||
is printed and the stacks at the time of the error are saved. If you're spent any
|
||||
time with Factor at all, you are probably familiar with this type of message:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} [ 1 2 3 ] 4 append reverse
|
||||
\textbf{The generic word car does not have a suitable method for 4
|
||||
:s :r :n :c show stacks at time of error.
|
||||
:get ( var -- value ) inspects the error namestack.}
|
||||
\end{alltt}
|
||||
|
||||
The words \texttt{:s}, \texttt{:r}, \texttt{:n} and \texttt{:s} behave like their counterparts that are prefixed with \texttt{.}, except they show the stacks as they were when the error was thrown.
|
||||
|
||||
The return stack warrants some special attention. To successfully develop Factor, you will need to learn to understand how it works. Lets look at the first few lines of the return stack at the time of the above error:
|
||||
|
||||
\begin{verbatim}
|
||||
[ swap cdr ]
|
||||
uncons
|
||||
[ r> tuck 2slip ]
|
||||
(each)
|
||||
[ swons ]
|
||||
[ each ]
|
||||
each
|
||||
\end{verbatim}
|
||||
|
||||
You can see the sequence of calls leading up to the error was \texttt{each} calling \texttt{(each)} calling \texttt{uncons}. The error tells us that the \texttt{car} word is the one that failed. Now, you can stare at the stack dump, at notice that if the call to \texttt{car} was successful and execution returned to \texttt{(each)}, the quotation \texttt{[ r> tuck 2slip ]} would resume executing. The first word there, \texttt{r>}, would take the quotation \texttt{[ swons ]} and put it back on the data stack. After \texttt{(each)} returned, it would then continue executing the quotation \texttt{[ each ]}. So what is going on here is a recursive loop, \texttt{[ swons ] each}. If you look at the definition of \texttt{reverse}, you will see that this is exactly what is being done:
|
||||
|
||||
\begin{verbatim}
|
||||
: reverse ( list -- list ) [ ] swap [ swons ] each ;
|
||||
\end{verbatim}
|
||||
|
||||
So a list is being reversed, but at some stage, the \texttt{car} is taken of something that is not a number. Now, you can look at the data stack with \texttt{:s}:
|
||||
|
||||
\begin{verbatim}
|
||||
<< no-method [ ] 4 car >>
|
||||
car
|
||||
4
|
||||
4
|
||||
[ 3 2 1 ]
|
||||
\end{verbatim}
|
||||
|
||||
So now, the mystery has been solved: as \texttt{reverse} iterates down the input value, it hits a cons cells whose \texttt{cdr} is not a list. Indeed, if you look at the value we are passing to \texttt{reverse}, you will see why:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} [ 1 2 3 ] 4 append .
|
||||
[[ 1 [[ 2 [[ 3 4 ]] ]] ]]
|
||||
\end{alltt}
|
||||
|
||||
In the future, the debugger will be linked with the walker, documented below. Right now, the walker is a separate tool. Another caveat is that in compiled code, the return stack is not reconstructed if there is an error. Until this is fixed, you should only compile code once it is debugged. For more potential compiler pitfalls, see \ref{compiler}.
|
||||
|
||||
\subsection{The walker}
|
||||
|
||||
The walker lets you step through the execution of a qotation. When a colon definition is reached, you can either keep walking inside the definition, or execute it in one step. The stacks can be inspected at each stage.
|
||||
|
||||
There are two ways to use the walker. First of all, you can call the \texttt{walk} word explicitly, giving it a quotation:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} [ [ 10 [ dup , ] repeat ] make-list ] walk
|
||||
\textbf{\&s \&r \&n \&c show stepper stacks.
|
||||
\&get ( var -- value ) inspects the stepper namestack.
|
||||
step -- single step over
|
||||
into -- single step into
|
||||
continue -- continue execution
|
||||
bye -- exit single-stepper
|
||||
[ [ 10 [ dup , ] repeat ] make-list ]
|
||||
walk}
|
||||
\end{alltt}
|
||||
|
||||
As you can see, the walker prints a brief help message, then the currently executing quotation. It changes the listener prompt from \texttt{ok} to \texttt{walk}, to remind you that there is a suspended continuation.
|
||||
|
||||
The first element of the quotation shown is the next object to be evaluated. If it is a literal, both \texttt{step} and \texttt{into} have the effect of pushing it on the walker data stack. If it is a colon definition, then \texttt{into} will recurse the walker into the colon definition; otherwise, the word executes in one step.
|
||||
|
||||
The \texttt{\&r} word shows the walker return stack, which is laid out just like the primary interpreter's return stack. In fact, a good way to understand how Factor's return stack works is to play with the walker.
|
||||
|
||||
Note that the walker does not automatically stop when the quotation originally given finishes executing; it just keeps on walking up the return stack, and even lets you step through the listener's code. You can invoke \texttt{continue} or \texttt{exit} to terminate the walker.
|
||||
|
||||
While the walker can be invoked explicitly using the \texttt{walk} word, sometimes it is more convenient to \emph{annotate} a word such that the walker is invoked automatically when the word is called. This can be done using the \texttt{break} word:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} \ttbs layout* break
|
||||
\end{alltt}
|
||||
|
||||
Now, when some piece of code calls \texttt{layout*}, the walker will open, and you will be able to step through execution and see exactly what's going on. An important point to keep in mind is that when the walker is invoked in this manner, \texttt{exit} will not have the desired effect; execution will continue, but the data stack will be inconsistent, and an error will most likely be raised a short time later. Always use \texttt{continue} to resume execution after a break.
|
||||
|
||||
The walker is very handy, but sometimes you just want to see if a word is being called at all and when, and you don't care to single-step it. In that case, you can use the \texttt{watch} word:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} \ttbs draw-shape break
|
||||
\end{alltt}
|
||||
|
||||
Now when \texttt{draw-shape} is called, a message will be printed to that effect.
|
||||
|
||||
You can undo the effect of \texttt{break} or \texttt{watch} by reloading the original source file containing the word definition in question:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} \ttbs layout* reload
|
||||
\textbf{ok} \ttbs draw-shape reload
|
||||
\end{alltt}
|
||||
|
||||
\subsection{Dealing with hangs}
|
||||
|
||||
If you accidentally start an infinite loop, you can send the Factor runtime a \texttt{QUIT} signal. On Unix, this is done by pressing \texttt{Control-\ttbs} in the controlling terminal. This will cause the runtime to dump the data and return stacks in a semi-readable form. Note that this will help you find the root cause of the hang, but it will not let you interrupt the infinite loop.
|
||||
|
||||
|
||||
\section{Defensive coding}
|
||||
|
||||
\subsection{Unit testing}
|
||||
|
||||
Unit tests are very easy to write. They are usually placed in source files. A unit test can be executed with the \texttt{unit-test} word in the \texttt{test} vocabulary. This word takes a list and a quotation; the quotation is executed, and the resulting data stack is compared against the list. If they do not equal, the unit test has failed. Here is an example of a unit test:
|
||||
|
||||
\begin{verbatim}
|
||||
[ "Hello, crazy world" ] [
|
||||
"editor" get [ 0 caret set ] bind
|
||||
", crazy" 5 "editor" get [ line-insert ] bind
|
||||
"editor" get [ line-text get ] bind
|
||||
] unit-test
|
||||
\end{verbatim}
|
||||
|
||||
To have a unit test assert that a piece of code does not execute successfully, but rather throws an exception, use the \texttt{unit-test-fails} word. It takes only one quotation; if the quotation does \emph{not} throw an exception, the unit test has failed.
|
||||
|
||||
\begin{verbatim}
|
||||
[ -3 { } vector-nth ] unit-test-fails
|
||||
\end{verbatim}
|
||||
|
||||
Unit testing is a good habit to get into. Sometimes, writing tests first, before any code, can speed the development process too; by running your unit test script, you can gauge progress.
|
||||
|
||||
\subsection{Stack effect inference}
|
||||
|
||||
While most programming errors in Factor are only caught at runtime, the stack effect checker can be useful for checking correctness of code before it is run. It can also help narrow down problems with stack shuffling. The stack checker is used by passing a quotation to the \texttt{infer} word. It uses a sophisticated algorithm to infer stack effects of recursive words, combinators, and other tricky constructions, however, it cannot infer the stack effect of all words. In particular, anything using continuations, such as \texttt{catch} and I/O, will stump the stack checker. Despite this fault, it is still a useful tool.
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} [ pile-fill * >fixnum over pref-size dup y
|
||||
\texttt{...} [ + ] change ] infer .
|
||||
\textbf{[ [ tuple number tuple ] [ tuple fixnum object number ] ]}
|
||||
\end{alltt}
|
||||
|
||||
The stack checker will report an error it it cannot infer the stack effect of a quotation. The ``recursive state'' dump is similar to a return stack, but it is not a real return stack, since only a code walk is taking place, not full evaluation. Understanding recursive state dumps is an art, much like understanding return stacks.
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} [ 100 [ f f cons ] repeat ] infer .
|
||||
\textbf{! Inference error: Unbalanced branches
|
||||
! Recursive state:
|
||||
! [ (repeat) G:54044 pick pick >= [ 3drop ]
|
||||
[ [ swap >r call 1 + r> ] keep (repeat) ] ifte ]
|
||||
! [ repeat G:54042 0 -rot (repeat) ]
|
||||
:s :r :n :c show stacks at time of error.
|
||||
:get ( var -- value ) inspects the error namestack.}
|
||||
\end{alltt}
|
||||
|
||||
One reason stack inference might fail is if the quotation contains unbalanced branches, as above. For the inference to work, both branches of a conditional must exit with the same stack height.
|
||||
|
||||
Another situation when it fails is if your code calls quotations that are not statically known. This can happen if the word in question uses continuations, or if it pulls a quotation from a variable and calls it. This can also happen if you wrote your own combinator, but forgot to mark it as \texttt{inline}. For example, the following will fail:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} : dip swap >r call r> ;
|
||||
\textbf{ok} [ [ + ] dip * ] infer .
|
||||
! Inference error: A literal value was expected where a
|
||||
computed value was found: \#<computed @ 679711507>
|
||||
...
|
||||
\end{alltt}
|
||||
|
||||
However, defining \texttt{dip} to be inlined will work:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} : dip swap >r call r> ; inline
|
||||
\textbf{ok} [ [ + ] dip * ] infer .
|
||||
\textbf{[ [ number number number ] [ number ] ]}
|
||||
\end{alltt}
|
||||
|
||||
You can combine unit testing with stack effect inference by writing unit tests that check stack effects of words. In fact, this can be automated with the \texttt{infer>test.} word; it takes a quotation on the stack, and prints a code snippet that tests the stack effect of the quotation:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} [ draw-shape ] infer>test.
|
||||
\textbf{[ [ [ object ] [ ] ] ]
|
||||
[ [ draw-shape ] infer ]
|
||||
unit-test}
|
||||
\end{alltt}
|
||||
|
||||
You can then copy and paste this snippet into a test script, and run the test script after
|
||||
making changes to the word to ensure its stack effect signature has not changed.
|
||||
|
||||
\section{Optimization}
|
||||
|
||||
While both the Factor interpreter and compiler are relatively slow at this stage, there
|
||||
are still ways you can make your Factor code go faster. The key is to find bottlenecks,
|
||||
and optimize them.
|
||||
|
||||
\subsection{Timing code}
|
||||
|
||||
The \texttt{time} word reports the time taken to execute a quotation, in milliseconds. The portion of time spent in garbage collection is also shown:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} [ 1000000 [ f f cons drop ] repeat ] time
|
||||
\textbf{515 milliseconds run time
|
||||
11 milliseconds GC time}
|
||||
\end{alltt}
|
||||
|
||||
\subsection{Exploring memory usage}
|
||||
|
||||
Factor supports heap introspection. You can find all objects in the heap that match a certain predicate using the \texttt{instances} word. For example, if you suspect a resource leak, you can find all I/O ports as follows:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} USE: io-internals
|
||||
\textbf{ok} [ port? ] instances .
|
||||
\textbf{[ \#<port @ 805466443> \#<port @ 805466499> ]}
|
||||
\end{alltt}
|
||||
|
||||
The \texttt{references} word finds all objects that refer to a given object:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} [ float? ] instances car references .
|
||||
\textbf{[ \#<array @ 805542171> [ -1.0 0.0 / ] ]}
|
||||
\end{alltt}
|
||||
|
||||
You can print a memory usage summary with \texttt{room.}:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} room.
|
||||
\textbf{Data space: 16384 KB total 2530 KB used 13853 KB free
|
||||
Code space: 16384 KB total 490 KB used 15893 KB free}
|
||||
\end{alltt}
|
||||
|
||||
And finally, a detailed memory allocation breakdown by type with \texttt{heap-stats.}:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} heap-stats.
|
||||
\textbf{bignum: 312 bytes, 17 instances
|
||||
cons: 850376 bytes, 106297 instances
|
||||
float: 112 bytes, 7 instances
|
||||
t: 8 bytes, 1 instances
|
||||
array: 202064 bytes, 3756 instances
|
||||
hashtable: 54912 bytes, 3432 instances
|
||||
vector: 5184 bytes, 324 instances
|
||||
string: 391024 bytes, 7056 instances
|
||||
sbuf: 64 bytes, 4 instances
|
||||
port: 112 bytes, 2 instances
|
||||
word: 96960 bytes, 3030 instances
|
||||
tuple: 688 bytes, 22 instances}
|
||||
\end{alltt}
|
||||
|
||||
\subsection{The profiler}
|
||||
|
||||
Factor provides a statistical sampling profiler for narrowing down memory and processor bottlenecks.
|
||||
The profiler is only supported on Unix platforms. On FreeBSD 4.x, the Factor runtime must
|
||||
be compiled without the \texttt{-pthread} switch, since FreeBS 4.x userspace threading makes
|
||||
use of a signal that conflicts with the signal used for profiling.
|
||||
|
||||
The \texttt{allot-profile} word executes a quotation with the memory profiler enabled, then prints a list of all words that allocated memory, along with the bytes allocated. Note that during particularly long executions, or executions where a lot of memory is allocated, these counters may overrun.
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} [ "boot.image.le32" make-image ] allot-profile
|
||||
\emph{... many lines omitted ...}
|
||||
\textbf{[[ write-little-endian-32 673952 ]]
|
||||
[[ wait-to-read-line 788640 ]]
|
||||
[[ blocking-read-line 821264 ]]
|
||||
[[ vocabularies 822624 ]]
|
||||
[[ parse-resource 823376 ]]
|
||||
[[ next-line 1116440 ]]
|
||||
[[ vector-map 1326504 ]]
|
||||
[[ fixup-words 1326520 ]]
|
||||
[[ vector-each 1768640 ]]
|
||||
[[ (parse) 2434208 ]]
|
||||
[[ classes 2517920 ]]
|
||||
[[ when* 2939088 ]]
|
||||
[[ while 3614408 ]]
|
||||
[[ (parse-stream) 3634552 ]]
|
||||
[[ make-list 3862000 ]]
|
||||
[[ object 4143784 ]]
|
||||
[[ each 4712080 ]]
|
||||
[[ run-resource 5036904 ]]
|
||||
[[ (callcc) 5183400 ]]
|
||||
[[ catch 5188976 ]]
|
||||
[[ 2slip 8631736 ]]
|
||||
[[ end 202896600 ]]
|
||||
[[ make-image 208611888 ]]
|
||||
[[ with-scope 437823992 ]]}
|
||||
\end{alltt}
|
||||
|
||||
The \texttt{call-profile} word executes a quotation with the CPU profiler enabled, then prints a list of all words that were found on the return stack, along with the number of times they were seen there. This gives a rough idea of what words are taking up the majority of execution time.
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} [ "boot.image.le32" make-image ] call-profile
|
||||
\emph{... many lines omitted ...}
|
||||
\textbf{[[ stream-write 7 ]]
|
||||
[[ wait-to-write 7 ]]
|
||||
[[ vector-map 11 ]]
|
||||
[[ fixup-words 11 ]]
|
||||
[[ when* 12 ]]
|
||||
[[ write 16 ]]
|
||||
[[ write-word 17 ]]
|
||||
[[ parse-loop 22 ]]
|
||||
[[ make-list 24 ]]
|
||||
[[ (parse) 29 ]]
|
||||
[[ blocking-write 32 ]]
|
||||
[[ while 35 ]]
|
||||
[[ (parse-stream) 36 ]]
|
||||
[[ dispatch 47 ]]
|
||||
[[ run-resource 50 ]]
|
||||
[[ write-little-endian-32 76 ]]
|
||||
[[ (callcc) 173 ]]
|
||||
[[ catch 174 ]]
|
||||
[[ each 175 ]]
|
||||
[[ 2slip 199 ]]
|
||||
[[ end 747 ]]
|
||||
[[ make-image 785 ]]
|
||||
[[ with-scope 1848 ]]}
|
||||
\end{alltt}
|
||||
|
||||
Normally, the memory and CPU profilers run every millisecond, and increment counters for all words on the return stack. The \texttt{only-top} variable can be switched on, in which case only the counter for the word at the top of the return stack is incremented. This gives a more localized picture of CPU and memory usage.
|
||||
|
||||
\subsection{\label{compiler}The compiler}
|
||||
|
||||
The compiler can provide a substantial speed boost for words whose stack effect can be inferred. Words without a known stack effect cannot be compiled, and must be run in the interpreter. The compiler generates native code, and so far, x86 and PowerPC backends have been developed.
|
||||
|
||||
To compile a single word, call \texttt{compile}:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} \ttbs pref-size compile
|
||||
\textbf{Compiling pref-size}
|
||||
\end{alltt}
|
||||
|
||||
During bootstrap, all words in the library with a known stack effect are compiled. You can
|
||||
circumvent this, for whatever reason, by passing the \texttt{-no-compile} switch during
|
||||
bootstrap:
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{bash\$} ./f boot.image.le32 -no-compile
|
||||
\end{alltt}
|
||||
|
||||
The compiler has two limitations you must be aware of. First, if an exception is thrown in compiled code, the return stack will be incomplete, since compiled words do not push themselves there. Second, compiled code cannot be profiled. These limitations will be resolved in a future release.
|
||||
|
||||
The compiler consists of multiple stages -- first, a dataflow graph is inferred, then various optimizations are done on this graph, then it is transformed into a linear representation, further optimizations are done, and finally, machine code is generated from the linear representation. To perform everything except for the machine code generation, use the \texttt{precompile} word. This will dump the optimized linear IR instead of generating code, which can be useful sometimes.
|
||||
|
||||
\begin{alltt}
|
||||
\textbf{ok} \ttbs append precompile
|
||||
\textbf{[ \#prologue ]
|
||||
[ over ]
|
||||
[[ \#jump-t-label G:54091 ]]
|
||||
[ swap ]
|
||||
[ drop ]
|
||||
[ \#return ]
|
||||
[[ \#label G:54091 ]]
|
||||
[ >r ]
|
||||
[[ \#call uncons ]]
|
||||
[ r> ]
|
||||
[[ \#call append ]]
|
||||
[[ \#jump cons ]]}
|
||||
\end{alltt}
|
||||
|
||||
\end{document}
|
|
@ -52,7 +52,7 @@ M: cons (tree-each) [ car (tree-each) ] 2keep cdr (tree-each) ;
|
|||
|
||||
M: f (tree-each) swap call ;
|
||||
|
||||
M: sequence (tree-each) [ swap call ] seq-each-with ;
|
||||
M: sequence (tree-each) [ (tree-each) ] seq-each-with ;
|
||||
|
||||
: tree-each swap (tree-each) ; inline
|
||||
|
||||
|
|
|
@ -44,6 +44,9 @@ namespaces parser strings words vectors math math-internals ;
|
|||
: methods ( generic -- alist )
|
||||
"methods" word-prop hash>alist [ 2car class< ] sort ;
|
||||
|
||||
: order ( generic -- list )
|
||||
"methods" word-prop hash-keys [ class< ] sort ;
|
||||
|
||||
: add-method ( generic vtable definition class -- )
|
||||
#! Add the method entry to the vtable. Unlike define-method,
|
||||
#! this is called at vtable build time, and in the sorted
|
||||
|
|
Loading…
Reference in New Issue