diff --git a/CHANGES.html b/CHANGES.html index e791a6a0b0..54b562662a 100644 --- a/CHANGES.html +++ b/CHANGES.html @@ -65,6 +65,7 @@ make-sbuf ==> SBUF" " make
  • New sleep ( ms -- ) word pauses current thread for a number of milliseconds.
  • New with-datastack ( stack word -- stack ) combinator.
  • New cond ( conditions -- ) combinator. It behaves like a set of nested iftes, and compiles if each branch has the same stack effect. See its documentation comment for details.
  • +
  • Formally documented method combination (G: syntax) in handbook.
  • Erlang/Termite-style concurrency library in contrib/concurrency (Chris Double).
  • Completely redid infix algebra in conrib/algebra/. Now, vector operations are possible and the syntax doesn't use so many spaces. New way to write the quadratic formula: diff --git a/TODO.FACTOR.txt b/TODO.FACTOR.txt index 5aa9266a9e..a1833a2579 100644 --- a/TODO.FACTOR.txt +++ b/TODO.FACTOR.txt @@ -2,13 +2,14 @@ - out of memory error when printing global namespace - removing unneeded #label - pprint trailing space regression +- finish scrollbars +- fix up the min thumb size hack + ui: -- fix up the min thumb size hack +- long lines of text fail in draw-surface - only redraw dirty gadgets - faster mouse tracking - - off-by-one error in pick-up? - closing ui does not stop timers - adding/removing timers automatically for animated gadgets @@ -66,8 +67,8 @@ - the invalid recursion form case needs to be fixed, for inlines too - #jump-f #jump-f-label - re-introduce #target-label => #target optimization -- recursion is iffy; no base case needs to throw an error, and if the - stack at the recursive call doesn't match up, throw an error +- recursion is iffy; if the stack at the recursive call doesn't match + up, throw an error + kernel: diff --git a/doc/handbook.tex b/doc/handbook.tex index d9d5f97cf7..2e3a3486a1 100644 --- a/doc/handbook.tex +++ b/doc/handbook.tex @@ -2543,21 +2543,6 @@ Outputs a new sequence with the reverse element order. } Tests if \texttt{s1} starts or ends with \texttt{s1}. If \texttt{s1} is longer than \texttt{s2}, outputs \texttt{f}. -\wordtable{ -\vocabulary{sequences} -\ordinaryword{cut}{cut ( seq n -- s1 s2 )} -} -Outputs a pair of sequences that equal the original sequence when appended. The first sequence has length $n$, the second has length $l-n$ where $l$ is the length of the input. -\begin{alltt} - "Hello world" 5 cut .s -\textbf{" world" -"Hello"} -\end{alltt} -This word has a simple definition: -\begin{verbatim} -: cut ( n seq -- seq seq ) - [ head ] 2keep tail ; -\end{verbatim} \wordtable{ \vocabulary{sequences} \ordinaryword{?head}{?head~( s1 s2 -- seq ?~)} diff --git a/library/alien/compiler.factor b/library/alien/compiler.factor index 065bfb208b..9a03310161 100644 --- a/library/alien/compiler.factor +++ b/library/alien/compiler.factor @@ -167,5 +167,5 @@ M: compound (uncrossref) drop ] [ dup { "infer-effect" "base-case" "no-effect" } - reset-props decompile + reset-props update-xt ] ifte ; diff --git a/library/bootstrap/boot-stage1.factor b/library/bootstrap/boot-stage1.factor index c11a3ed8f2..74ff9842ad 100644 --- a/library/bootstrap/boot-stage1.factor +++ b/library/bootstrap/boot-stage1.factor @@ -54,6 +54,7 @@ sequences io vectors words ; "/library/collections/queues.factor" "/library/math/matrices.factor" + "/library/math/parse-numbers.factor" "/library/words.factor" "/library/vocabularies.factor" @@ -71,7 +72,6 @@ sequences io vectors words ; "/library/io/directories.factor" "/library/io/binary.factor" - "/library/syntax/parse-numbers.factor" "/library/syntax/parse-words.factor" "/library/syntax/parse-errors.factor" "/library/syntax/parser.factor" @@ -95,20 +95,17 @@ sequences io vectors words ; "/library/io/logging.factor" - "/library/tools/gensym.factor" "/library/tools/interpreter.factor" "/library/tools/debugger.factor" "/library/tools/memory.factor" "/library/tools/listener.factor" - "/library/tools/word-tools.factor" "/library/tools/walker.factor" "/library/tools/jedit.factor" - - "/library/test/test.factor" - "/library/tools/annotations.factor" "/library/tools/inspector.factor" + "/library/test/test.factor" + "/library/syntax/see.factor" "/library/threads.factor" diff --git a/library/bootstrap/primitives.factor b/library/bootstrap/primitives.factor index 78d4c52e27..0e02885e54 100644 --- a/library/bootstrap/primitives.factor +++ b/library/bootstrap/primitives.factor @@ -38,8 +38,8 @@ vocabularies get [ "syntax" set [ reveal ] each ] bind { ">bignum" "math" } { ">float" "math" } { "(fraction>)" "math-internals" } - { "str>float" "parser" } - { "(unparse-float)" "parser" } + { "string>float" "math-internals" } + { "float>string" "math-internals" } { "float>bits" "math" } { "double>bits" "math" } { "bits>float" "math" } diff --git a/library/collections/namespaces.factor b/library/collections/namespaces.factor index ff7dc68095..78a875244a 100644 --- a/library/collections/namespaces.factor +++ b/library/collections/namespaces.factor @@ -106,6 +106,10 @@ SYMBOL: building #! Append to the sequence being built with make-seq. building get swap nappend ; +: # ( n -- ) + #! Only useful with "" make. + number>string % ; + ! Building hashtables, and computing a transitive closure. SYMBOL: hash-buffer diff --git a/library/compiler/compiler.factor b/library/compiler/compiler.factor index e5f6fc447e..989a5e6788 100644 --- a/library/compiler/compiler.factor +++ b/library/compiler/compiler.factor @@ -48,15 +48,8 @@ M: compound (compile) ( word -- ) : compile-all ( -- ) [ try-compile ] each-word ; -: decompile ( word -- ) - dup compiled? [ - "Decompiling " write dup . update-xt - ] [ - drop - ] ifte ; - : recompile ( word -- ) - dup decompile compile ; + dup update-xt compile ; : compile-1 ( quot -- word ) #! Compute a quotation into an uninterned word, for testing diff --git a/library/generic/generic.factor b/library/generic/generic.factor index cc1dd2d667..39bc1ec7bf 100644 --- a/library/generic/generic.factor +++ b/library/generic/generic.factor @@ -140,3 +140,12 @@ M: generic definer drop \ G: ; : define-class ( class metaclass -- ) dupd "metaclass" set-word-prop dup types number-sort typemap get set-hash ; + +: implementors ( class -- list ) + #! Find a list of generics that implement a method + #! specializing on this class. + [ "methods" word-prop ?hash ] word-subset-with ; + +: classes ( -- list ) + #! Output a list of all defined classes. + [ metaclass ] word-subset ; diff --git a/library/help/tutorial.factor b/library/help/tutorial.factor index f69946bab2..4f68495533 100644 --- a/library/help/tutorial.factor +++ b/library/help/tutorial.factor @@ -30,7 +30,7 @@ M: general-list tutorial-line : ( list -- gadget ) [ tutorial-line ] map - 1 [ add-gadgets ] keep + dup 1 over set-pack-fill [ add-gadgets ] keep empty-border ; : tutorial-pages diff --git a/library/httpd/file-responder.factor b/library/httpd/file-responder.factor index 04f3fc08cc..e909a28185 100644 --- a/library/httpd/file-responder.factor +++ b/library/httpd/file-responder.factor @@ -1,7 +1,7 @@ ! Copyright (C) 2004,2005 Slava Pestov. ! See http://factor.sf.net/license.txt for BSD license. IN: file-responder -USING: html httpd kernel lists namespaces parser sequences +USING: html httpd kernel lists math namespaces parser sequences io strings ; : serving-path ( filename -- filename ) diff --git a/library/httpd/html.factor b/library/httpd/html.factor index 93b630dc66..8afe197d43 100644 --- a/library/httpd/html.factor +++ b/library/httpd/html.factor @@ -1,7 +1,7 @@ ! Copyright (C) 2004, 2005 Slava Pestov. ! See http://factor.sf.net/license.txt for BSD license. IN: html -USING: generic http io kernel lists namespaces parser +USING: generic http io kernel lists math namespaces parser presentation sequences strings styles words ; : html-entities ( -- alist ) @@ -35,7 +35,7 @@ presentation sequences strings styles words ; [ "text-decoration: underline; " % ] when ; : size-css, ( size -- ) - "font-size: " % number>string % "; " % ; + "font-size: " % # "; " % ; : font-css, ( font -- ) "font-family: " % % "; " % ; diff --git a/library/httpd/http-client.factor b/library/httpd/http-client.factor index acdcc317d1..de92da80ce 100644 --- a/library/httpd/http-client.factor +++ b/library/httpd/http-client.factor @@ -1,7 +1,7 @@ ! Copyright (C) 2005 Slava Pestov. ! See http://factor.sf.net/license.txt for BSD license. IN: http-client -USING: errors http kernel lists namespaces parser sequences +USING: errors http kernel lists math namespaces parser sequences io strings ; : parse-host ( url -- host port ) diff --git a/library/httpd/httpd.factor b/library/httpd/httpd.factor index 1a9ceb1c86..1e3a35f404 100644 --- a/library/httpd/httpd.factor +++ b/library/httpd/httpd.factor @@ -38,7 +38,7 @@ sequences ; [ (handle-request) serve-responder ] with-scope ; : parse-request ( request -- ) - dup log + dup log-message " " split1 dup [ " HTTP" split1 drop url>path secure-path dup [ swap handle-request diff --git a/library/httpd/responder.factor b/library/httpd/responder.factor index 6c8e01e365..3873b1e26e 100644 --- a/library/httpd/responder.factor +++ b/library/httpd/responder.factor @@ -1,8 +1,8 @@ ! Copyright (C) 2004, 2005 Slava Pestov. ! See http://factor.sf.net/license.txt for BSD license. IN: httpd -USING: hashtables http kernel lists namespaces parser sequences -io strings ; +USING: hashtables http kernel lists math namespaces parser +sequences io strings ; ! Variables SYMBOL: vhosts @@ -66,7 +66,7 @@ SYMBOL: responders : log-user-agent ( alist -- ) "User-Agent" swap assoc* [ - unswons [ % ": " % % ] "" make log + unswons [ % ": " % % ] "" make log-message ] when* ; : prepare-url ( url -- url ) @@ -138,7 +138,7 @@ SYMBOL: responders "default" responder call-responder ; : log-responder ( path -- ) - "Calling responder " swap append log ; + "Calling responder " swap append log-message ; : trim-/ ( url -- url ) #! Trim a leading /, if there is one. diff --git a/library/inference/known-words.factor b/library/inference/known-words.factor index 3fbeb92935..3d1c5d8a78 100644 --- a/library/inference/known-words.factor +++ b/library/inference/known-words.factor @@ -113,13 +113,13 @@ memory parser sequences strings vectors words prettyprint ; \ (fraction>) t "flushable" set-word-prop \ (fraction>) t "foldable" set-word-prop -\ str>float [ [ string ] [ float ] ] "infer-effect" set-word-prop -\ str>float t "flushable" set-word-prop -\ str>float t "foldable" set-word-prop +\ string>float [ [ string ] [ float ] ] "infer-effect" set-word-prop +\ string>float t "flushable" set-word-prop +\ string>float t "foldable" set-word-prop -\ (unparse-float) [ [ float ] [ string ] ] "infer-effect" set-word-prop -\ (unparse-float) t "flushable" set-word-prop -\ (unparse-float) t "foldable" set-word-prop +\ float>string [ [ float ] [ string ] ] "infer-effect" set-word-prop +\ float>string t "flushable" set-word-prop +\ float>string t "foldable" set-word-prop \ float>bits [ [ real ] [ integer ] ] "infer-effect" set-word-prop \ float>bits t "flushable" set-word-prop diff --git a/library/io/logging.factor b/library/io/logging.factor index 62abfa755d..efe44653d9 100644 --- a/library/io/logging.factor +++ b/library/io/logging.factor @@ -1,28 +1,25 @@ ! Copyright (C) 2003, 2005 Slava Pestov. ! See http://factor.sf.net/license.txt for BSD license. IN: io -USING: io kernel namespaces parser sequences strings ; +USING: io kernel math namespaces parser sequences strings ; ! A simple logging framework. SYMBOL: log-stream -: log ( msg -- ) +: log-message ( msg -- ) #! Log a message to the log stream, either stdio or a file. - log-stream get [ - [ stream-print ] keep stream-flush - ] [ - print flush - ] ifte* ; + log-stream get [ stdio get ] unless* + [ stream-print ] keep stream-flush ; -: log-error ( error -- ) "Error: " swap append log ; +: log-error ( error -- ) "Error: " swap append log-message ; : log-client ( client-stream -- ) [ "Accepted connection from " % dup client-stream-host % CHAR: : , - client-stream-port number>string % - ] "" make log ; + client-stream-port # + ] "" make log-message ; : with-log-file ( file quot -- ) #! Calls to log inside quot will output to a file. diff --git a/library/math/math.factor b/library/math/math.factor index 6ea8dec95e..79f7ac0d16 100644 --- a/library/math/math.factor +++ b/library/math/math.factor @@ -86,3 +86,6 @@ GENERIC: abs ( z -- |z| ) ] [ dup 1 = [ drop 0 ] [ 2 /i log2 1 + ] ifte ] ifte ; foldable + +GENERIC: string>number ( str -- num ) foldable +GENERIC: number>string ( str -- num ) foldable diff --git a/library/syntax/parse-numbers.factor b/library/math/parse-numbers.factor similarity index 72% rename from library/syntax/parse-numbers.factor rename to library/math/parse-numbers.factor index 91d19cf68b..a955370a95 100644 --- a/library/syntax/parse-numbers.factor +++ b/library/math/parse-numbers.factor @@ -1,7 +1,8 @@ ! Copyright (C) 2004, 2005 Slava Pestov. ! See http://factor.sf.net/license.txt for BSD license. -IN: parser -USING: errors generic kernel math namespaces sequences strings ; +IN: math +USING: errors generic kernel math-internals namespaces sequences +strings ; ! Number parsing @@ -26,9 +27,7 @@ M: object digit> not-a-number ; : base> ( str base -- num ) #! Convert a string to an integer. Throw an error if #! conversion fails. - swap "-" ?head [ (base>) neg ] [ (base>) ] ifte ; - -GENERIC: string>number ( str -- num ) + swap "-" ?head >r (base>) r> [ neg ] when ; M: string string>number 10 base> ; @@ -37,24 +36,18 @@ M: potential-ratio string>number ( str -- num ) "/" split1 >r 10 base> r> 10 base> / ; PREDICATE: string potential-float CHAR: . swap member? ; -M: potential-float string>number ( str -- num ) - str>float ; +M: potential-float string>number ( str -- num ) string>float ; : bin> 2 base> ; : oct> 8 base> ; : hex> 16 base> ; -GENERIC: number>string ( str -- num ) - : >digit ( n -- ch ) dup 10 < [ CHAR: 0 + ] [ 10 - CHAR: a + ] ifte ; : integer, ( num radix -- ) - dup >r /mod >digit , dup 0 > [ - r> integer, - ] [ - r> 2drop - ] ifte ; + dup >r /mod >digit , dup 0 > + [ r> integer, ] [ r> 2drop ] ifte ; : >base ( num radix -- string ) #! Convert a number to a string in a certain base. @@ -73,17 +66,9 @@ GENERIC: number>string ( str -- num ) M: integer number>string ( obj -- str ) 10 >base ; M: ratio number>string ( num -- str ) - [ - dup - numerator number>string % - CHAR: / , - denominator number>string % - ] "" make ; - -: fix-float ( str -- str ) - #! This is terrible. Will go away when we do our own float - #! output. - CHAR: . over member? [ ".0" append ] unless ; + [ dup numerator # CHAR: / , denominator # ] "" make ; M: float number>string ( float -- str ) - (unparse-float) fix-float ; + #! This is terrible. Will go away when we do our own float + #! output. + float>string CHAR: . over member? [ ".0" append ] unless ; diff --git a/library/sdl/sdl-video.factor b/library/sdl/sdl-video.factor index ec10633516..0c71978499 100644 --- a/library/sdl/sdl-video.factor +++ b/library/sdl/sdl-video.factor @@ -69,8 +69,8 @@ BEGIN-STRUCT: surface FIELD: void* hwdata FIELD: short clip-x FIELD: short clip-y - FIELD: ushort clip-w - FIELD: ushort clip-h + FIELD: ushort clip-w + FIELD: ushort clip-h FIELD: uint unused1 FIELD: uint locked FIELD: int map diff --git a/library/syntax/parse-stream.factor b/library/syntax/parse-stream.factor index 2981e1e563..ee45a7299c 100644 --- a/library/syntax/parse-stream.factor +++ b/library/syntax/parse-stream.factor @@ -1,7 +1,7 @@ ! Copyright (C) 2004, 2005 Slava Pestov. ! See http://factor.sf.net/license.txt for BSD license. IN: parser -USING: kernel lists namespaces sequences io ; +USING: kernel lists namespaces sequences io words ; : file-vocabs ( -- ) "scratchpad" "in" set @@ -38,3 +38,12 @@ USING: kernel lists namespaces sequences io ; : run-resource ( file -- ) parse-resource call ; + +: word-file ( word -- file ) + "file" word-prop dup [ + "resource:/" ?head [ resource-path swap path+ ] when + ] when ; + +: reload ( word -- ) + #! Reload the source file the word originated from. + word-file run-file ; diff --git a/library/syntax/prettyprint.factor b/library/syntax/prettyprint.factor index 41fa8d6d87..9e766b20b6 100644 --- a/library/syntax/prettyprint.factor +++ b/library/syntax/prettyprint.factor @@ -11,7 +11,6 @@ SYMBOL: last-newline SYMBOL: recursion-check SYMBOL: line-count SYMBOL: end-printing -SYMBOL: newline-ok? ! Configuration SYMBOL: tab-size @@ -30,7 +29,6 @@ global [ 0 last-newline set 0 line-count set string-limit off - newline-ok? off ] bind TUPLE: pprinter stack ; @@ -48,9 +46,6 @@ C: section ( length -- section ) : section-fits? ( section -- ? ) section-end last-newline get - indent get + margin get <= ; -: insert-newline? ( section -- ? ) - section-fits? not newline-ok? and ; - : line-limit? ( -- ? ) line-limit get dup [ line-count get <= ] when ; @@ -58,10 +53,14 @@ C: section ( length -- section ) : fresh-line ( n -- ) #! n is current column position. - last-newline set - line-count inc - line-limit? [ "..." write end-printing get call ] when - "\n" write do-indent ; + dup last-newline get = [ + drop + ] [ + last-newline set + line-count inc + line-limit? [ "..." write end-printing get call ] when + "\n" write do-indent + ] ifte ; TUPLE: text string style ; @@ -71,7 +70,7 @@ C: text ( string style -- section ) [ set-text-string ] keep ; M: text pprint-section* - dup text-string swap text-style format " " write ; + dup text-string swap text-style format " " write ; TUPLE: block sections ; @@ -107,8 +106,8 @@ C: block ( -- block ) [ section-end fresh-line ] [ drop ] ifte ; : pprint-section ( section -- ) - dup insert-newline? newline-ok? on - [ inset-section ] [ pprint-section* ] ifte ; + dup section-fits? + [ pprint-section* ] [ inset-section ] ifte ; TUPLE: newline ; @@ -116,7 +115,7 @@ C: newline ( -- section ) 0
    over set-delegate ; M: newline pprint-section* ( newline -- ) - section-start fresh-line newline-ok? off ; + section-start fresh-line ; M: block pprint-section* ( block -- ) block-sections [ pprint-section ] each ; diff --git a/library/syntax/see.factor b/library/syntax/see.factor index 1b4e14dc04..fc1dbb645b 100644 --- a/library/syntax/see.factor +++ b/library/syntax/see.factor @@ -118,3 +118,14 @@ M: word class. drop ; : see ( word -- ) [ dup in. dup (see) dup class. methods. ] with-pprint ; + +: (apropos) ( substring -- seq ) + vocabs [ + words [ word-name subseq? ] subset-with + ] map-with concat ; + +: apropos ( substring -- ) + #! List all words that contain a string. + (apropos) [ + "IN: " write dup word-vocabulary write " " write . + ] each ; diff --git a/library/test/test.factor b/library/test/test.factor index 9d6c571bbb..4f732c67aa 100644 --- a/library/test/test.factor +++ b/library/test/test.factor @@ -21,10 +21,7 @@ M: assert error. #! Evaluates the given code and prints the time taken to #! execute it. millis >r gc-time >r call gc-time r> - millis r> - - [ - number>string % " ms run / " % - number>string % " ms GC time" % - ] "" make print ; + [ # " ms run / " % # " ms GC time" % ] "" make print ; : unit-test ( output input -- ) [ diff --git a/library/tools/gensym.factor b/library/tools/gensym.factor deleted file mode 100644 index ce7cdb86f8..0000000000 --- a/library/tools/gensym.factor +++ /dev/null @@ -1,14 +0,0 @@ -! Copyright (C) 2004, 2005 Slava Pestov. -! See http://factor.sf.net/license.txt for BSD license. -IN: words -USING: hashtables kernel math namespaces parser sequences -strings ; - -: gensym ( -- word ) - #! Return a word that is distinct from every other word, and - #! is not contained in any vocabulary. - "G:" - global [ \ gensym dup inc get ] bind - number>string append f ; - -0 \ gensym global set-hash diff --git a/library/tools/jedit.factor b/library/tools/jedit.factor index 033d95b297..1123aa5fb5 100644 --- a/library/tools/jedit.factor +++ b/library/tools/jedit.factor @@ -1,7 +1,7 @@ ! Copyright (C) 2004, 2005 Slava Pestov. ! See http://factor.sf.net/license.txt for BSD license. IN: jedit -USING: errors io kernel lists namespaces parser prettyprint +USING: errors io kernel lists math namespaces parser prettyprint sequences strings unparser vectors words ; ! Some words to send requests to a running jEdit instance to diff --git a/library/tools/telnetd.factor b/library/tools/telnetd.factor index a56fe13a64..f61ca9f547 100644 --- a/library/tools/telnetd.factor +++ b/library/tools/telnetd.factor @@ -1,7 +1,7 @@ ! Copyright (C) 2003, 2005 Slava Pestov. ! See http://factor.sf.net/license.txt for BSD license. IN: telnetd -USING: errors listener kernel namespaces io threads parser ; +USING: errors listener kernel math namespaces io threads parser ; : telnet-client ( socket -- ) dup [ log-client print-banner listener ] with-stream ; diff --git a/library/tools/word-tools.factor b/library/tools/word-tools.factor deleted file mode 100644 index af10d0d6bd..0000000000 --- a/library/tools/word-tools.factor +++ /dev/null @@ -1,43 +0,0 @@ -! Copyright (C) 2003, 2005 Slava Pestov. -! See http://factor.sf.net/license.txt for BSD license. -IN: words -USING: generic inspector lists kernel namespaces -prettyprint io strings sequences math hashtables parser ; - -: vocab-apropos ( substring vocab -- list ) - #! Push a list of all words in a vocabulary whose names - #! contain a string. - words [ word-name subseq? ] subset-with ; - -: vocab-apropos. ( substring vocab -- ) - #! List all words in a vocabulary that contain a string. - tuck vocab-apropos dup [ - "IN: " write swap print sequence. - ] [ - 2drop - ] ifte ; - -: apropos. ( substring -- ) - #! List all words that contain a string. - vocabs [ vocab-apropos. ] each-with ; - -: word-file ( word -- file ) - "file" word-prop dup [ - "resource:/" ?head [ - resource-path swap path+ - ] when - ] when ; - -: reload ( word -- ) - #! Reload the source file the word originated from. - word-file run-file ; - -: implementors ( class -- list ) - #! Find a list of generics that implement a method - #! specializing on this class. - [ - "methods" word-prop [ dupd hash ] [ f ] ifte* - ] word-subset nip ; - -: classes ( -- list ) - [ metaclass ] word-subset ; diff --git a/library/ui/books.factor b/library/ui/books.factor index 877f7315fb..eddf33b62b 100644 --- a/library/ui/books.factor +++ b/library/ui/books.factor @@ -1,7 +1,8 @@ ! Copyright (C) 2005 Slava Pestov. ! See http://factor.sf.net/license.txt for BSD license. -IN: gadgets -USING: generic kernel lists math matrices sequences ; +IN: gadgets-books +USING: gadgets gadgets-buttons gadgets-labels gadgets-layouts +generic kernel lists math matrices sequences ; TUPLE: book page ; @@ -46,7 +47,7 @@ TUPLE: book-browser book ; { ">" [ find-book next-page ] } { ">|" [ find-book last-page ] } ] [ 2unseq >r