]> gitweb.factorcode.org Git - factor.git/commitdiff
Merge branch 'emacs-mode' into lisp
authorJames Cash <james.nvc@gmail.com>
Tue, 3 Jun 2008 08:33:53 +0000 (04:33 -0400)
committerJames Cash <james.nvc@gmail.com>
Tue, 3 Jun 2008 08:33:53 +0000 (04:33 -0400)
319 files changed:
core/alien/compiler/compiler.factor
core/alien/structs/structs-docs.factor
core/assocs/assocs-tests.factor
core/assocs/assocs.factor
core/classes/classes-tests.factor
core/classes/tuple/tuple-tests.factor
core/classes/tuple/tuple.factor
core/combinators/combinators-docs.factor
core/combinators/combinators.factor
core/compiler/units/units.factor
core/generator/fixup/fixup-docs.factor
core/generator/fixup/fixup.factor
core/generic/generic.factor
core/generic/standard/engines/predicate/predicate.factor
core/inference/class/class.factor
core/io/binary/binary-docs.factor
core/io/binary/binary.factor
core/kernel/kernel-docs.factor
core/parser/parser-tests.factor
core/parser/parser.factor
core/prettyprint/prettyprint-tests.factor
core/sequences/sequences-docs.factor
core/sequences/sequences-tests.factor
core/sequences/sequences.factor
core/sets/sets-docs.factor
core/sets/sets-tests.factor
core/sets/sets.factor
core/splitting/splitting-docs.factor
core/splitting/splitting.factor
core/syntax/syntax.factor
core/words/words.factor
extra/assocs/lib/lib.factor
extra/bunny/model/model.factor
extra/cairo/gadgets/gadgets.factor
extra/cairo/samples/samples.factor
extra/calendar/format/format-tests.factor
extra/calendar/format/format.factor
extra/combinators/lib/lib.factor
extra/db/db.factor
extra/db/errors/errors.factor [new file with mode: 0644]
extra/db/pools/pools.factor
extra/db/postgresql/postgresql.factor
extra/db/queries/queries.factor
extra/db/sql/sql-tests.factor
extra/db/sqlite/lib/lib.factor
extra/db/sqlite/sqlite.factor
extra/db/tuples/tuples-tests.factor
extra/db/tuples/tuples.factor
extra/db/types/types.factor
extra/dns/cache/cache.factor
extra/dns/dns.factor
extra/dns/forwarding/forwarding.factor [new file with mode: 0644]
extra/dns/misc/misc.factor [new file with mode: 0644]
extra/dns/recursive/recursive.factor [new file with mode: 0644]
extra/dns/resolver/resolver.factor
extra/dns/stub/stub.factor [new file with mode: 0644]
extra/farkup/farkup-tests.factor
extra/farkup/farkup.factor
extra/fry/fry-tests.factor
extra/fry/fry.factor
extra/globs/globs.factor
extra/html/authors.txt [deleted file]
extra/html/components/components-tests.factor [new file with mode: 0644]
extra/html/components/components.factor [new file with mode: 0644]
extra/html/elements/elements-tests.factor
extra/html/elements/elements.factor
extra/html/html-tests.factor [deleted file]
extra/html/html.factor [deleted file]
extra/html/streams/authors.txt [new file with mode: 0644]
extra/html/streams/streams-tests.factor [new file with mode: 0644]
extra/html/streams/streams.factor [new file with mode: 0755]
extra/html/streams/summary.txt [new file with mode: 0644]
extra/html/streams/tags.txt [new file with mode: 0644]
extra/html/stylesheet.css [deleted file]
extra/html/summary.txt [deleted file]
extra/html/tags.txt [deleted file]
extra/html/templates/chloe/chloe-tests.factor [new file with mode: 0644]
extra/html/templates/chloe/chloe.factor [new file with mode: 0644]
extra/html/templates/chloe/test/test1.xml [new file with mode: 0644]
extra/html/templates/chloe/test/test10.xml [new file with mode: 0644]
extra/html/templates/chloe/test/test11.xml [new file with mode: 0644]
extra/html/templates/chloe/test/test2.xml [new file with mode: 0644]
extra/html/templates/chloe/test/test3-aux.xml [new file with mode: 0644]
extra/html/templates/chloe/test/test3.xml [new file with mode: 0644]
extra/html/templates/chloe/test/test4.xml [new file with mode: 0644]
extra/html/templates/chloe/test/test5.xml [new file with mode: 0644]
extra/html/templates/chloe/test/test6.xml [new file with mode: 0644]
extra/html/templates/chloe/test/test7.xml [new file with mode: 0644]
extra/html/templates/chloe/test/test8.xml [new file with mode: 0644]
extra/html/templates/chloe/test/test9.xml [new file with mode: 0644]
extra/html/templates/fhtml/authors.txt [new file with mode: 0644]
extra/html/templates/fhtml/fhtml-tests.factor [new file with mode: 0755]
extra/html/templates/fhtml/fhtml.factor [new file with mode: 0755]
extra/html/templates/fhtml/test/bug.fhtml [new file with mode: 0644]
extra/html/templates/fhtml/test/bug.html [new file with mode: 0644]
extra/html/templates/fhtml/test/example.fhtml [new file with mode: 0644]
extra/html/templates/fhtml/test/example.html [new file with mode: 0644]
extra/html/templates/fhtml/test/stack.fhtml [new file with mode: 0644]
extra/html/templates/fhtml/test/stack.html [new file with mode: 0644]
extra/html/templates/templates.factor [new file with mode: 0644]
extra/http/client/client.factor
extra/http/http-tests.factor
extra/http/http.factor
extra/http/server/actions/actions-tests.factor
extra/http/server/actions/actions.factor
extra/http/server/auth/admin/admin.factor [deleted file]
extra/http/server/auth/admin/admin.xml [deleted file]
extra/http/server/auth/admin/edit-user.xml [deleted file]
extra/http/server/auth/admin/new-user.xml [deleted file]
extra/http/server/auth/admin/user-list.xml [deleted file]
extra/http/server/auth/admin/user-summary.xml [deleted file]
extra/http/server/auth/auth.factor
extra/http/server/auth/login/edit-profile.xml
extra/http/server/auth/login/login.factor
extra/http/server/auth/login/login.xml
extra/http/server/auth/login/recover-1.xml
extra/http/server/auth/login/recover-3.xml
extra/http/server/auth/login/register.xml
extra/http/server/auth/providers/assoc/assoc.factor
extra/http/server/boilerplate/boilerplate.factor
extra/http/server/callbacks/callbacks.factor
extra/http/server/components/code/code.factor [deleted file]
extra/http/server/components/components-tests.factor [deleted file]
extra/http/server/components/components.factor [deleted file]
extra/http/server/components/farkup/farkup.factor [deleted file]
extra/http/server/components/inspector/inspector.factor [deleted file]
extra/http/server/components/test/form.fhtml [deleted file]
extra/http/server/crud/crud.factor [deleted file]
extra/http/server/db/db.factor
extra/http/server/forms/forms.factor [deleted file]
extra/http/server/server-tests.factor
extra/http/server/server.factor
extra/http/server/sessions/sessions-tests.factor
extra/http/server/static/static.factor
extra/http/server/templating/chloe/chloe-tests.factor [deleted file]
extra/http/server/templating/chloe/chloe.factor [deleted file]
extra/http/server/templating/chloe/test/test1.xml [deleted file]
extra/http/server/templating/chloe/test/test2.xml [deleted file]
extra/http/server/templating/chloe/test/test3-aux.xml [deleted file]
extra/http/server/templating/chloe/test/test3.xml [deleted file]
extra/http/server/templating/chloe/test/test4.xml [deleted file]
extra/http/server/templating/chloe/test/test5.xml [deleted file]
extra/http/server/templating/chloe/test/test6.xml [deleted file]
extra/http/server/templating/chloe/test/test7.xml [deleted file]
extra/http/server/templating/fhtml/authors.txt [deleted file]
extra/http/server/templating/fhtml/fhtml-tests.factor [deleted file]
extra/http/server/templating/fhtml/fhtml.factor [deleted file]
extra/http/server/templating/fhtml/test/bug.fhtml [deleted file]
extra/http/server/templating/fhtml/test/bug.html [deleted file]
extra/http/server/templating/fhtml/test/example.fhtml [deleted file]
extra/http/server/templating/fhtml/test/example.html [deleted file]
extra/http/server/templating/fhtml/test/stack.fhtml [deleted file]
extra/http/server/templating/fhtml/test/stack.html [deleted file]
extra/http/server/templating/templating.factor [deleted file]
extra/http/server/validators/validators-tests.factor [deleted file]
extra/http/server/validators/validators.factor [deleted file]
extra/io/windows/nt/backend/backend.factor
extra/irc/authors.txt [deleted file]
extra/irc/client/authors.txt [new file with mode: 0644]
extra/irc/client/client.factor [new file with mode: 0644]
extra/irc/client/summary.txt [new file with mode: 0644]
extra/irc/irc.factor [deleted file]
extra/irc/summary.txt [deleted file]
extra/json/reader/reader-tests.factor [new file with mode: 0644]
extra/json/reader/reader.factor
extra/lazy-lists/authors.txt [deleted file]
extra/lazy-lists/examples/authors.txt [deleted file]
extra/lazy-lists/examples/examples-tests.factor [deleted file]
extra/lazy-lists/examples/examples.factor [deleted file]
extra/lazy-lists/lazy-lists-docs.factor [deleted file]
extra/lazy-lists/lazy-lists-tests.factor [deleted file]
extra/lazy-lists/lazy-lists.factor [deleted file]
extra/lazy-lists/old-doc.html [deleted file]
extra/lazy-lists/summary.txt [deleted file]
extra/lazy-lists/tags.txt [deleted file]
extra/lcs/diff2html/diff2html.factor [new file with mode: 0644]
extra/lisp/lisp-tests.factor
extra/lisp/lisp.factor
extra/lisp/parser/parser-tests.factor
extra/lisp/parser/parser.factor
extra/lists/authors.txt [new file with mode: 0644]
extra/lists/lazy/authors.txt [new file with mode: 0644]
extra/lists/lazy/examples/authors.txt [new file with mode: 0755]
extra/lists/lazy/examples/examples-tests.factor [new file with mode: 0644]
extra/lists/lazy/examples/examples.factor [new file with mode: 0644]
extra/lists/lazy/lazy-docs.factor [new file with mode: 0644]
extra/lists/lazy/lazy-tests.factor [new file with mode: 0644]
extra/lists/lazy/lazy.factor [new file with mode: 0644]
extra/lists/lazy/old-doc.html [new file with mode: 0644]
extra/lists/lazy/summary.txt [new file with mode: 0644]
extra/lists/lazy/tags.txt [new file with mode: 0644]
extra/lists/lists-docs.factor [new file with mode: 0644]
extra/lists/lists-tests.factor [new file with mode: 0644]
extra/lists/lists.factor [new file with mode: 0644]
extra/lists/summary.txt [new file with mode: 0644]
extra/lists/tags.txt [new file with mode: 0644]
extra/locals/locals-tests.factor
extra/locals/locals.factor
extra/logging/logging-tests.factor [new file with mode: 0644]
extra/logging/logging.factor
extra/macros/macros-tests.factor
extra/macros/macros.factor
extra/math/erato/erato-tests.factor
extra/math/erato/erato.factor
extra/math/functions/functions-tests.factor
extra/math/functions/functions.factor
extra/math/libm/libm.factor
extra/math/primes/factors/factors.factor
extra/math/primes/primes-tests.factor
extra/math/primes/primes.factor
extra/memoize/memoize-tests.factor
extra/memoize/memoize.factor
extra/monads/monads-tests.factor
extra/monads/monads.factor
extra/morse/morse.factor
extra/multi-methods/multi-methods.factor
extra/namespaces/lib/lib.factor
extra/newfx/newfx.factor
extra/opengl/gadgets/gadgets.factor [new file with mode: 0644]
extra/opengl/opengl.factor
extra/openssl/openssl.factor
extra/pango/cairo/cairo.factor [new file with mode: 0644]
extra/pango/cairo/gadgets/gadgets.factor [new file with mode: 0644]
extra/pango/fonts/fonts.factor [new file with mode: 0644]
extra/pango/pango.factor [new file with mode: 0644]
extra/parser-combinators/parser-combinators-docs.factor
extra/parser-combinators/parser-combinators-tests.factor
extra/parser-combinators/parser-combinators.factor
extra/parser-combinators/simple/simple-docs.factor
extra/parser-combinators/simple/simple.factor
extra/project-euler/007/007.factor
extra/project-euler/134/134.factor
extra/qualified/qualified-docs.factor
extra/regexp/regexp.factor
extra/rss/rss.factor
extra/tangle/html/html-tests.factor
extra/tangle/html/html.factor
extra/tangle/tangle.factor
extra/tetris/game/game.factor
extra/tetris/piece/piece.factor
extra/tools/deploy/deploy-tests.factor
extra/tools/deploy/shaker/shaker.factor
extra/tools/walker/walker.factor
extra/trees/splay/splay-tests.factor
extra/ui/freetype/freetype-docs.factor
extra/ui/freetype/freetype.factor
extra/ui/tools/interactor/interactor.factor
extra/unicode/collation/collation-tests.factor
extra/urls/authors.txt [new file with mode: 0644]
extra/urls/summary.txt [new file with mode: 0644]
extra/urls/tags.txt [new file with mode: 0644]
extra/urls/urls-tests.factor [new file with mode: 0644]
extra/urls/urls.factor [new file with mode: 0644]
extra/validators/validators-tests.factor [new file with mode: 0644]
extra/validators/validators.factor [new file with mode: 0644]
extra/webapps/counter/counter.factor
extra/webapps/counter/counter.fhtml [deleted file]
extra/webapps/counter/counter.xml [new file with mode: 0644]
extra/webapps/factor-website/factor-website.factor
extra/webapps/factor-website/page.css
extra/webapps/pastebin/annotation.xml [deleted file]
extra/webapps/pastebin/new-annotation.xml [deleted file]
extra/webapps/pastebin/new-paste.xml
extra/webapps/pastebin/paste-list.xml [deleted file]
extra/webapps/pastebin/paste-summary.xml [deleted file]
extra/webapps/pastebin/paste.xml
extra/webapps/pastebin/pastebin-common.xml [new file with mode: 0644]
extra/webapps/pastebin/pastebin.factor
extra/webapps/pastebin/pastebin.xml
extra/webapps/planet/admin.xml
extra/webapps/planet/blog-admin-link.xml [deleted file]
extra/webapps/planet/edit-blog.xml
extra/webapps/planet/mini-planet.xml [new file with mode: 0644]
extra/webapps/planet/new-blog.xml [new file with mode: 0644]
extra/webapps/planet/planet-common.xml [new file with mode: 0644]
extra/webapps/planet/planet.factor
extra/webapps/planet/planet.xml
extra/webapps/planet/postings-summary.xml [deleted file]
extra/webapps/planet/postings.xml [deleted file]
extra/webapps/todo/edit-todo.xml
extra/webapps/todo/todo-list.xml
extra/webapps/todo/todo-summary.xml [deleted file]
extra/webapps/todo/todo.factor
extra/webapps/todo/todo.xml
extra/webapps/todo/view-todo.xml
extra/webapps/user-admin/edit-user.xml [new file with mode: 0644]
extra/webapps/user-admin/new-user.xml [new file with mode: 0644]
extra/webapps/user-admin/user-admin.factor [new file with mode: 0644]
extra/webapps/user-admin/user-admin.xml [new file with mode: 0644]
extra/webapps/user-admin/user-list.xml [new file with mode: 0644]
extra/webapps/wiki/articles.xml [new file with mode: 0644]
extra/webapps/wiki/changes.xml [new file with mode: 0644]
extra/webapps/wiki/diff.xml [new file with mode: 0644]
extra/webapps/wiki/edit.xml [new file with mode: 0644]
extra/webapps/wiki/revisions.xml [new file with mode: 0644]
extra/webapps/wiki/user-edits.xml [new file with mode: 0644]
extra/webapps/wiki/view.xml [new file with mode: 0644]
extra/webapps/wiki/wiki-common.xml [new file with mode: 0644]
extra/webapps/wiki/wiki.css [new file with mode: 0644]
extra/webapps/wiki/wiki.factor [new file with mode: 0644]
extra/xmode/catalog/catalog.factor
extra/xmode/code2html/code2html.factor
extra/xmode/code2html/responder/responder.factor
vm/callstack.c
vm/code_gc.c
vm/code_gc.h
vm/code_heap.c
vm/code_heap.h
vm/data_gc.c
vm/debug.c
vm/image.c
vm/layouts.h
vm/os-unix.c
vm/os-windows-nt.c
vm/os-windows.c
vm/profiler.c
vm/quotations.c
vm/types.c
vm/types.h

index 08b52367b033f100f071165bdc84a522ab10065c..67665b4d7ebc47f474fa923cc1c4d27da24ecc14 100755 (executable)
@@ -7,7 +7,7 @@ math.parser classes alien.arrays alien.c-types alien.strings
 alien.structs alien.syntax cpu.architecture alien inspector
 quotations assocs kernel.private threads continuations.private
 libc combinators compiler.errors continuations layouts accessors
-;
+init ;
 IN: alien.compiler
 
 TUPLE: #alien-node < node return parameters abi ;
@@ -336,7 +336,7 @@ M: #alien-indirect generate-node
 ! this hashtable, they will all be blown away by code GC, beware
 SYMBOL: callbacks
 
-callbacks global [ H{ } assoc-like ] change-at
+[ H{ } clone callbacks set-global ] "alien.compiler" add-init-hook
 
 : register-callback ( word -- ) dup callbacks get set-at ;
 
@@ -344,7 +344,7 @@ M: alien-callback-error summary
     drop "Words calling ``alien-callback'' must be compiled with the optimizing compiler." ;
 
 : callback-bottom ( node -- )
-    xt>> [ word-xt drop <alien> ] curry
+    xt>> [ [ register-callback ] [ word-xt drop <alien> ] bi ] curry
     recursive-state get infer-quot ;
 
 \ alien-callback [
@@ -354,7 +354,7 @@ M: alien-callback-error summary
     pop-literal nip >>abi
     pop-parameters >>parameters
     pop-literal nip >>return
-    gensym dup register-callback >>xt
+    gensym >>xt
     callback-bottom
 ] "infer" set-word-prop
 
index e7e576293fe3df422923737a74c2753f22d9649b..baf0b40707ff34fd7b30d937262a54d21cf2d85c 100755 (executable)
@@ -91,6 +91,6 @@ $nl
 ARTICLE: "c-unions" "C unions"
 "A " { $snippet "union" } " in C defines a type large enough to hold its largest member. This is usually used to allocate a block of memory which can hold one of several types of values."
 { $subsection POSTPONE: C-UNION: }
-"C structure objects can be allocated by calling " { $link <c-object> } " or " { $link malloc-object } "."
+"C union objects can be allocated by calling " { $link <c-object> } " or " { $link malloc-object } "."
 $nl
 "Arrays of C unions can be created by calling " { $link <c-array> } " or " { $link malloc-array } ". Elements can be read and written using words named " { $snippet { $emphasis "type" } "-nth" } " and " { $snippet "set-" { $emphasis "type" } "-nth" } "; these words are automatically generated by " { $link POSTPONE: C-UNION: } "." ;
index 30f2ec23c4810b075631139907a2fb5f5b9c400f..43a1bac82d8522bf569d55bc89c326453ffbad4e 100755 (executable)
@@ -104,3 +104,17 @@ unit-test
         2drop
     ] { } make
 ] unit-test
+
+[
+    H{
+        { "bangers" "mash" }
+        { "fries" "onion rings" }
+    }
+] [
+    { "bangers" "fries" } H{
+        { "fish" "chips" }
+        { "bangers" "mash" }
+        { "fries" "onion rings" }
+        { "nachos" "cheese" }
+    } extract-keys
+] unit-test
index 92db38573ad28b747400e9fe1b73c576ba43772c..6b0798f2e307fd107b7bab75ea208a9156a718d9 100755 (executable)
@@ -150,6 +150,9 @@ M: assoc assoc-clone-like ( assoc exemplar -- newassoc )
 : map>assoc ( seq quot exemplar -- assoc )
     >r [ 2array ] compose { } map-as r> assoc-like ; inline
 
+: extract-keys ( seq assoc -- subassoc )
+    [ [ dupd at ] curry ] keep map>assoc ;
+
 M: assoc >alist [ 2array ] { } assoc>map ;
 
 : value-at ( value assoc -- key/f )
index bb9fbd0167a03e4a72a6b5e2201d843f0b55b164..eb55b5fccdba8129c02e3d0678c1cd59bb777b85 100755 (executable)
@@ -160,3 +160,12 @@ TUPLE: flat-mx-2-1 ; INSTANCE: flat-mx-2-1 flat-mx-2
 [ t ] [ 3 number instance? ] unit-test
 [ f ] [ 3 null instance? ] unit-test
 [ t ] [ "hi" \ hi-tag instance? ] unit-test
+
+! Regression
+GENERIC: method-forget-test
+TUPLE: method-forget-class ;
+M: method-forget-class method-forget-test ;
+
+[ f ] [ \ method-forget-test "methods" word-prop assoc-empty? ] unit-test
+[ ] [ [ \ method-forget-class forget ] with-compilation-unit ] unit-test
+[ t ] [ \ method-forget-test "methods" word-prop assoc-empty? ] unit-test
index 0cf7ea351096ef8fc4f074edc0efdf072537cb23..ab6c139f7b00832a9f72ce1ffa11a23a4fa58e94 100755 (executable)
@@ -4,7 +4,7 @@ namespaces quotations sequences.private classes continuations
 generic.standard effects classes.tuple classes.tuple.private
 arrays vectors strings compiler.units accessors classes.algebra
 calendar prettyprint io.streams.string splitting inspector
-columns math.order ;
+columns math.order classes.private ;
 IN: classes.tuple.tests
 
 TUPLE: rect x y w h ;
@@ -543,6 +543,7 @@ TUPLE: another-forget-accessors-test ;
 ! Missing error check
 [ "IN: classes.tuple.tests USE: words TUPLE: wrong-superclass < word ;" eval ] must-fail
 
+! Class forget messyness
 TUPLE: subclass-forget-test ;
 
 TUPLE: subclass-forget-test-1 < subclass-forget-test ;
@@ -551,6 +552,14 @@ TUPLE: subclass-forget-test-3 < subclass-forget-test-2 ;
 
 [ ] [ "IN: classes.tuple.tests FORGET: subclass-forget-test" eval ] unit-test
 
+[ H{ { subclass-forget-test-2 subclass-forget-test-2 } } ]
+[ subclass-forget-test-2 class-usages ]
+unit-test
+
+[ H{ { subclass-forget-test-3 subclass-forget-test-3 } } ]
+[ subclass-forget-test-3 class-usages ]
+unit-test
+
 [ f ] [ subclass-forget-test-1 tuple-class? ] unit-test
 [ f ] [ subclass-forget-test-2 tuple-class? ] unit-test
 [ subclass-forget-test-3 new ] must-fail
index f4054c8468ab0f427d9c26b53b3ca6b94db63754..4e6ce0d2bb9922e2980c3d9234ea4aa3237f266e 100755 (executable)
@@ -226,12 +226,6 @@ M: tuple-class reset-class
         } reset-props
     ] bi ;
 
-: reset-tuple-class ( class -- )
-    [ [ reset-class ] [ update-map- ] bi ] each-subclass ;
-
-M: tuple-class forget*
-    [ reset-tuple-class ] [ call-next-method ] bi ;
-
 M: tuple-class rank-class drop 0 ;
 
 M: tuple clone
index 61752ac7d669208d6e3a76e34634fe78a20b9aa2..c65c01d2abf23abad24f3059863bff309b2c2833 100755 (executable)
@@ -10,8 +10,10 @@ ARTICLE: "combinators-quot" "Quotation construction utilities"
 { $subsection alist>quot } ;
 
 ARTICLE: "combinators" "Additional combinators"
-"The " { $vocab-link "combinators" } " vocabulary provides generalizations of certain combinators from the " { $vocab-link "kernel" } " vocabulary."
+"The " { $vocab-link "combinators" } " vocabulary provides a few useful combinators."
 $nl
+"A looping combinator:"
+{ $subsection while }
 "Generalization of " { $link bi } " and " { $link tri } ":"
 { $subsection cleave }
 "Generalization of " { $link bi* } " and " { $link tri* } ":"
index d33edfab3048c5dc7cd8825aff9b846d95d89181..f6873429fe533769cbec0d903aee9efe8b603b0f 100755 (executable)
@@ -95,10 +95,10 @@ M: hashtable hashcode*
 
 : (distribute-buckets) ( buckets pair keys -- )
     dup t eq? [
-        drop [ swap push-new ] curry each
+        drop [ swap adjoin ] curry each
     ] [
         [
-            >r 2dup r> hashcode pick length rem rot nth push-new
+            >r 2dup r> hashcode pick length rem rot nth adjoin
         ] each 2drop
     ] if ;
 
index a31cd8de165e296c6da1284c0bb94521e1945577..c2e84429cf5ed873de0bd4f5b4d40d461d88c694 100755 (executable)
@@ -1,7 +1,7 @@
 ! Copyright (C) 2008 Slava Pestov.
 ! See http://factorcode.org/license.txt for BSD license.
 USING: kernel continuations assocs namespaces sequences words
-vocabs definitions hashtables init ;
+vocabs definitions hashtables init sets ;
 IN: compiler.units
 
 SYMBOL: old-definitions
@@ -14,7 +14,7 @@ TUPLE: redefine-error def ;
     { { "Continue" t } } throw-restarts drop ;
 
 : add-once ( key assoc -- )
-    2dup key? [ over redefine-error ] when dupd set-at ;
+    2dup key? [ over redefine-error ] when conjoin ;
 
 : (remember-definition) ( definition loc assoc -- )
     >r over set-where r> add-once ;
@@ -83,7 +83,14 @@ SYMBOL: update-tuples-hook
     call-recompile-hook
     call-update-tuples-hook
     dup [ drop compiled-crossref? ] assoc-contains? modify-code-heap
-    updated-definitions notify-definition-observers ;
+     ;
+
+: with-nested-compilation-unit ( quot -- )
+    [
+        H{ } clone changed-definitions set
+        H{ } clone outdated-tuples set
+        [ finish-compilation-unit ] [ ] cleanup
+    ] with-scope ; inline
 
 : with-compilation-unit ( quot -- )
     [
@@ -92,8 +99,11 @@ SYMBOL: update-tuples-hook
         H{ } clone outdated-tuples set
         <definitions> new-definitions set
         <definitions> old-definitions set
-        [ finish-compilation-unit ]
-        [ ] cleanup
+        [
+            finish-compilation-unit
+            updated-definitions
+            notify-definition-observers
+        ] [ ] cleanup
     ] with-scope ; inline
 
 : compile-call ( quot -- )
index f5d530dccbbba9632c13c899f8fed24c3eafe57c..64d733ef8c43c4f622dd33e25415914b614aac08 100644 (file)
@@ -1,14 +1,11 @@
-USING: help.syntax help.markup generator.fixup math kernel
+USING: help.syntax help.markup math kernel
 words strings alien ;
+IN: generator.fixup
 
 HELP: frame-required
 { $values { "n" "a non-negative integer" } }
 { $description "Notify the code generator that the currently compiling code block needs a stack frame with room for at least " { $snippet "n" } " parameters." } ;
 
-HELP: (rel-fixup)
-{ $values { "arg" integer } { "class" "a relocation class" } { "type" "a relocation type" } { "offset" integer } { "pair" "a pair of integers" } }
-{ $description "Creates a relocation instruction for the VM's runtime compiled code linker." } ;
-
 HELP: add-literal
 { $values { "obj" object } { "n" integer } }
 { $description "Adds a literal to the " { $link literal-table } ", if it is not already there, and outputs the index of the literal in the table. This literal can then be used as an argument for a " { $link rt-literal } " relocation with " { $link rel-fixup } "." } ;
index 06895cd8ac6196aa4927d80069ae38ceb51b9d97..a0961984ede64e2db8e898367122ab89972bccb2 100755 (executable)
@@ -1,9 +1,10 @@
 ! Copyright (C) 2007, 2008 Slava Pestov.
 ! See http://factorcode.org/license.txt for BSD license.
-USING: arrays generic assocs hashtables
+USING: arrays byte-arrays generic assocs hashtables io.binary
 kernel kernel.private math namespaces sequences words
-quotations strings alien.strings layouts system combinators
-math.bitfields words.private cpu.architecture math.order ;
+quotations strings alien.accessors alien.strings layouts system
+combinators math.bitfields words.private cpu.architecture
+math.order accessors growable ;
 IN: generator.fixup
 
 : no-stack-frame -1 ; inline
@@ -77,38 +78,35 @@ TUPLE: label-fixup label class ;
 : label-fixup ( label class -- ) \ label-fixup boa , ;
 
 M: label-fixup fixup*
-    dup label-fixup-class rc-absolute?
+    dup class>> rc-absolute?
     [ "Absolute labels not supported" throw ] when
-    dup label-fixup-label swap label-fixup-class
-    compiled-offset 4 - rot 3array label-table get push ;
+    dup label>> swap class>> compiled-offset 4 - rot
+    3array label-table get push ;
 
 TUPLE: rel-fixup arg class type ;
 
 : rel-fixup ( arg class type -- ) \ rel-fixup boa , ;
 
-: (rel-fixup) ( arg class type offset -- pair )
-    pick rc-absolute-cell = cell 4 ? -
-    >r { 0 8 16 } bitfield r>
-    2array ;
+: push-4 ( value vector -- )
+    [ length ] [ B{ 0 0 0 0 } swap push-all ] [ underlying ] tri
+    swap set-alien-unsigned-4 ;
 
 M: rel-fixup fixup*
-    dup rel-fixup-arg
-    over rel-fixup-class
-    rot rel-fixup-type
-    compiled-offset (rel-fixup)
-    relocation-table get push-all ;
+    [ [ arg>> ] [ class>> ] [ type>> ] tri { 0 8 16 } bitfield ]
+    [ class>> rc-absolute-cell = cell 4 ? compiled-offset swap - ] bi
+    [ relocation-table get push-4 ] bi@ ;
 
 M: frame-required fixup* drop ;
 
 M: integer fixup* , ;
 
-: push-new* ( obj table -- n )
+: adjoin* ( obj table -- n )
     2dup swap [ eq? ] curry find drop
     [ 2nip ] [ dup length >r push r> ] if* ;
 
 SYMBOL: literal-table
 
-: add-literal ( obj -- n ) literal-table get push-new* ;
+: add-literal ( obj -- n ) literal-table get adjoin* ;
 
 : add-dlsym-literals ( symbol dll -- )
     >r string>symbol r> 2array literal-table get push-all ;
@@ -134,7 +132,7 @@ SYMBOL: literal-table
     0 swap rt-here rel-fixup ;
 
 : init-fixup ( -- )
-    V{ } clone relocation-table set
+    BV{ } clone relocation-table set
     V{ } clone label-table set ;
 
 : resolve-labels ( labels -- labels' )
@@ -150,6 +148,6 @@ SYMBOL: literal-table
         dup stack-frame-size swap [ fixup* ] each drop
 
         literal-table get >array
-        relocation-table get >array
+        relocation-table get >byte-array
         label-table get resolve-labels
     ] { } make ;
index e446689303ab5f571f0e1ac08c4d8143ac1fedcb..b9a556e316298e127868bb4be6ba01155275ea08 100755 (executable)
@@ -147,12 +147,16 @@ M: method-body forget*
     [ implementors ] [ [ swap 2array ] curry ] bi map forget-all ;
 
 M: class forget* ( class -- )
-    {
-        [ forget-methods ]
-        [ update-map- ]
-        [ reset-class ]
-        [ call-next-method ]
-    } cleave ;
+    [
+        class-usages [
+            drop
+            [ forget-methods ]
+            [ update-map- ]
+            [ reset-class ]
+            tri
+        ] assoc-each
+    ]
+    [ call-next-method ] bi ;
 
 M: assoc update-methods ( assoc -- )
     implementors* [ make-generic ] each ;
index b1bfc659df7aeec4e0e70dae58c963cf381a3348..9c810592a074d995f0aa58590c9cfd5132cc0fba 100644 (file)
@@ -22,8 +22,7 @@ C: <predicate-dispatch-engine> predicate-dispatch-engine
     } cond ;
 
 : sort-methods ( assoc -- assoc' )
-    [ keys sort-classes ]
-    [ [ dupd at ] curry ] bi { } map>assoc ;
+    >alist [ keys sort-classes ] keep extract-keys ;
 
 M: predicate-dispatch-engine engine>quot
     methods>> clone
index 933710aaca396424ab56c9c899e13a7263756205..dc632425fe4335e7e78ffc4af54d7579ef5ce13d 100755 (executable)
@@ -152,16 +152,16 @@ M: pair apply-constraint
 M: pair constraint-satisfied?
     first constraint-satisfied? ;
 
-: extract-keys ( seq assoc -- newassoc )
-    [ dupd at ] curry H{ } map>assoc [ nip ] assoc-filter f assoc-like ;
+: valid-keys ( seq assoc -- newassoc )
+    extract-keys [ nip ] assoc-filter f assoc-like ;
 
 : annotate-node ( node -- )
     #! Annotate the node with the currently-inferred set of
     #! value classes.
     dup node-values {
-        [ value-intervals get extract-keys >>intervals ]
-        [ value-classes   get extract-keys >>classes   ]
-        [ value-literals  get extract-keys >>literals  ]
+        [ value-intervals get valid-keys >>intervals ]
+        [ value-classes   get valid-keys >>classes   ]
+        [ value-literals  get valid-keys >>literals  ]
         [ 2drop ]
     } cleave ;
 
@@ -330,7 +330,7 @@ M: #return infer-classes-around
             [ ] [ node-input-classes ] [ in-d>> [ value-class* ] map ] tri
             classes= not [
                 fixed-point? off
-                [ in-d>> value-classes get extract-keys ] keep
+                [ in-d>> value-classes get valid-keys ] keep
                 set-node-classes
             ] [ drop ] if
         ] [ call-next-method ] if
index 507571c04451863f823f6468d2694555181c7cf0..ab82abe146e289f585ab745747b0b6acc3fcd901 100644 (file)
@@ -1,8 +1,8 @@
-USING: help.markup help.syntax io math ;
+USING: help.markup help.syntax io math byte-arrays ;
 IN: io.binary
 
 ARTICLE: "stream-binary" "Working with binary data"
-"The core stream words read and write strings. Packed binary integers can be read and written by converting to and from sequences of bytes. Floating point numbers can be read and written by converting them into a their bitwise integer representation (" { $link "floats" } ")."
+"Stream words on binary streams only read and write byte arrays. Packed binary integers can be read and written by converting to and from sequences of bytes. Floating point numbers can be read and written by converting them into a their bitwise integer representation (" { $link "floats" } ")."
 $nl
 "There are two ways to order the bytes making up an integer; " { $emphasis "little endian" } " byte order outputs the least significant byte first, and the most significant byte last, whereas " { $emphasis "big endian" } " is the other way around."
 $nl
@@ -42,11 +42,11 @@ HELP: nth-byte
 { $description "Outputs the " { $snippet "n" } "th least significant byte of the sign-extended 2's complement representation of " { $snippet "x" } "." } ;
 
 HELP: >le
-{ $values { "x" integer } { "n" "a non-negative integer" } { "str" "a string" } }
+{ $values { "x" integer } { "n" "a non-negative integer" } { "byte-array" byte-array } }
 { $description "Converts an integer " { $snippet "x" } " into a string of " { $snippet "n" } " bytes in little endian order. Truncation will occur if the integer is not in the range " { $snippet "[-2^(8n),2^(8n))" } "." } ;
 
 HELP: >be
-{ $values { "x" integer } { "n" "a non-negative integer" } { "str" "a string" } }
+{ $values { "x" integer } { "n" "a non-negative integer" } { "byte-array" byte-array } }
 { $description "Converts an integer " { $snippet "x" } " into a string of " { $snippet "n" } " bytes in big endian order. Truncation will occur if the integer is not in the range " { $snippet "[-2^(8n),2^(8n))" } "." } ;
 
 HELP: mask-byte
index f2ede93fd5c0c8b104a08f00b25c40c8b0a2980c..f3d236433f1e0b72426242b3a70c427c2fdf9df1 100755 (executable)
@@ -10,8 +10,8 @@ IN: io.binary
 
 : nth-byte ( x n -- b ) -8 * shift mask-byte ; inline
 
-: >le ( x n -- str ) [ nth-byte ] with B{ } map-as ;
-: >be ( x n -- str ) >le dup reverse-here ;
+: >le ( x n -- byte-array ) [ nth-byte ] with B{ } map-as ;
+: >be ( x n -- byte-array ) >le dup reverse-here ;
 
 : d>w/w ( d -- w1 w2 )
     dup HEX: ffffffff bitand
index 96c582a3e5fa418c4e0663e0165313d078d0a174..c39010f228f98d1578f781428a987a2dcc4aac4a 100755 (executable)
@@ -193,10 +193,7 @@ ARTICLE: "implementing-combinators" "Implementing combinators"
     ": keep ( x quot -- x )"
     "    over >r call r> ; inline"
 }
-"Word inlining is documented in " { $link "declarations" } "."
-$nl
-"A looping combinator:"
-{ $subsection while } ;
+"Word inlining is documented in " { $link "declarations" } "." ;
 
 ARTICLE: "booleans" "Booleans"
 "In Factor, any object that is not " { $link f } " has a true value, and " { $link f } " has a false value. The " { $link t } " object is the canonical true value."
index 3df9dc9cb2400db94276a487e37d670148692489..df6c9dadc5f072b6e3bcd81cd24a239b91b09683 100755 (executable)
@@ -460,3 +460,30 @@ must-fail-with
     "change-combination" "parser.tests" lookup
     "methods" word-prop assoc-size
 ] unit-test
+
+[ ] [
+    2 [
+        "IN: parser.tests DEFER: twice-fails FORGET: twice-fails MIXIN: twice-fails"
+        <string-reader> "twice-fails-test" parse-stream drop
+    ] times
+] unit-test
+
+[ [ ] ] [
+    "IN: parser.tests : staging-problem-test-1 1 ; : staging-problem-test-2 staging-problem-test-1 ;"
+    <string-reader> "staging-problem-test" parse-stream
+] unit-test
+
+[ t ] [ "staging-problem-test-1" "parser.tests" lookup >boolean ] unit-test
+
+[ t ] [ "staging-problem-test-2" "parser.tests" lookup >boolean ] unit-test
+
+[ [ ] ] [
+    "IN: parser.tests << : staging-problem-test-1 1 ; >> : staging-problem-test-2 staging-problem-test-1 ;"
+    <string-reader> "staging-problem-test" parse-stream
+] unit-test
+
+[ t ] [ "staging-problem-test-1" "parser.tests" lookup >boolean ] unit-test
+
+[ t ] [ "staging-problem-test-2" "parser.tests" lookup >boolean ] unit-test
+
+[ "DEFER: blah" eval ] [ error>> no-current-vocab? ] must-fail-with
index f08ba8fbc2972bd552dc764fc31e4c35149c366b..46e93753b547905769f8d124f1461f275f7709c8 100755 (executable)
@@ -236,7 +236,7 @@ PREDICATE: unexpected-eof < unexpected
 ERROR: no-current-vocab ;
 
 M: no-current-vocab summary ( obj -- )
-    drop "Current vocabulary is f, use IN:" ;
+    drop "Not in a vocabulary; IN: form required" ;
 
 : current-vocab ( -- str )
     in get [ no-current-vocab ] unless* ;
@@ -357,10 +357,9 @@ M: staging-violation summary
     "A parsing word cannot be used in the same file it is defined in." ;
 
 : execute-parsing ( word -- )
-    new-definitions get [
-        dupd first key? [ staging-violation ] when
-    ] when*
-    execute ;
+    [ changed-definitions get key? [ staging-violation ] when ]
+    [ execute ]
+    bi ;
 
 : parse-step ( accum end -- accum ? )
     scan-word {
index ed6b2f3c3c594966ed83df5b4e3a1afe12c146c0..f5ec263f117d0d969c7d2dc12d10d1cc2f34e79d 100755 (executable)
@@ -342,3 +342,5 @@ INTERSECTION: intersection-see-test sequence number ;
 
 [ ] [ \ compose see ] unit-test
 [ ] [ \ curry see ] unit-test
+
+[ "POSTPONE: [" ] [ \ [ unparse ] unit-test
index 351ba8969205ce9f66f167579555e0c34441406d..2c1a3b8ab90acf5f5a75f86665676a0d64beb8af 100755 (executable)
@@ -191,7 +191,6 @@ $nl
 "Other destructive words:"
 { $subsection move }
 { $subsection exchange }
-{ $subsection push-new }
 { $subsection copy }
 { $subsection replace-slice }
 { $see-also set-nth push pop "sequences-stacks" } ;
@@ -624,22 +623,7 @@ HELP: replace-slice
 { $errors "Throws an error if " { $snippet "new" } " contains elements whose types are not permissible in " { $snippet "seq" } "." }
 { $side-effects "seq" } ;
 
-HELP: push-new
-{ $values { "elt" object } { "seq" "a resizable mutable sequence" } }
-{ $description "Removes all elements equal to " { $snippet "elt" } ", and adds " { $snippet "elt" } " at the end of the sequence." }
-{ $examples
-    { $example
-        "USING: namespaces prettyprint sequences ;"
-        "V{ \"beans\" \"salsa\" \"cheese\" } \"v\" set"
-        "\"nachos\" \"v\" get push-new"
-        "\"salsa\" \"v\" get push-new"
-        "\"v\" get ."
-        "V{ \"beans\" \"cheese\" \"nachos\" \"salsa\" }"
-    }
-}
-{ $side-effects "seq" } ;
-
-{ push push-new prefix suffix } related-words
+{ push prefix suffix } related-words
 
 HELP: suffix
 { $values { "seq" sequence } { "elt" object } { "newseq" sequence } }
index 2479c125a26fbda76fdc1413702a140ac69049e2..81384a40c452536067dabfab934d4da6386f3d7a 100755 (executable)
@@ -215,12 +215,6 @@ unit-test
     3 V{ 1 2 3 4 5 6 } clone [ delete-nth ] keep
 ] unit-test
 
-[ V{ 1 2 3 } ]
-[ 3 V{ 1 2 } clone [ push-new ] keep ] unit-test
-
-[ V{ 1 2 3 } ]
-[ 3 V{ 1 3 2 } clone [ push-new ] keep ] unit-test
-
 ! erg's random tester found this one
 [ SBUF" 12341234" ] [
     9 <sbuf> dup "1234" swap push-all dup dup swap push-all
index cbddfa7d28dd2ce52208d17bf7a45d9cb3e13314..4854ff8001ed88b18cc1006ea77500f9f833cd72 100755 (executable)
@@ -499,8 +499,6 @@ M: slice equal? over slice? [ sequence= ] [ 2drop f ] if ;
 
 : delete ( elt seq -- ) 0 0 rot (delete) nip set-length drop ;
 
-: push-new ( elt seq -- ) [ delete ] 2keep push ;
-
 : prefix ( seq elt -- newseq )
     over >r over length 1+ r> [
         [ 0 swap set-nth-unsafe ] keep
@@ -680,7 +678,7 @@ PRIVATE>
 : unclip ( seq -- rest first )
     [ rest ] [ first ] bi ;
 
-: unclip-last ( seq -- butfirst last )
+: unclip-last ( seq -- butlast last )
     [ but-last ] [ peek ] bi ;
 
 : unclip-slice ( seq -- rest first )
index f4e2557a718318e76f923d828023c8acedbfc4c1..205d4d34bfed39ec66eb5ed11773844a03f68082 100644 (file)
@@ -16,10 +16,28 @@ $nl
 { $subsection set= }
 "A word used to implement the above:"
 { $subsection unique }
+"Adding elements to sets:"
+{ $subsection adjoin }
+{ $subsection conjoin }
 { $see-also member? memq? contains? all? "assocs-sets" } ;
 
 ABOUT: "sets"
 
+HELP: adjoin
+{ $values { "elt" object } { "seq" "a resizable mutable sequence" } }
+{ $description "Removes all elements equal to " { $snippet "elt" } ", and adds " { $snippet "elt" } " at the end of the sequence." }
+{ $examples
+    { $example
+        "USING: namespaces prettyprint sets ;"
+        "V{ \"beans\" \"salsa\" \"cheese\" } \"v\" set"
+        "\"nachos\" \"v\" get adjoin"
+        "\"salsa\" \"v\" get adjoin"
+        "\"v\" get ."
+        "V{ \"beans\" \"cheese\" \"nachos\" \"salsa\" }"
+    }
+}
+{ $side-effects "seq" } ;
+
 HELP: unique
 { $values { "seq" "a sequence" } { "assoc" "an assoc" } }
 { $description "Outputs a new assoc where the keys and values are equal." }
index 86ee100da5a91cca999e3f07ae75eeb0fdaa47f0..b6e6443afadae10dac08777f5f1263af2df9c82f 100644 (file)
@@ -15,3 +15,9 @@ IN: sets.tests
 
 [ V{ } ] [ { } { } union ] unit-test
 [ V{ 1 2 3 4 } ] [ { 1 2 3 } { 2 3 4 } union ] unit-test
+
+[ V{ 1 2 3 } ]
+[ 3 V{ 1 2 } clone [ adjoin ] keep ] unit-test
+
+[ V{ 1 2 3 } ]
+[ 3 V{ 1 3 2 } clone [ adjoin ] keep ] unit-test
index b0d26e0f301cd65b456762d8894ef9af67f1bc17..5fbec9a7c88d21d5b3af00f8b5fc0395fe6ab2c3 100644 (file)
@@ -3,10 +3,14 @@
 USING: assocs hashtables kernel sequences vectors ;
 IN: sets
 
+: adjoin ( elt seq -- ) [ delete ] [ push ] 2bi ;
+
+: conjoin ( elt assoc -- ) dupd set-at ;
+
 : (prune) ( elt hash vec -- )
-    3dup drop key?
-    [ [ drop dupd set-at ] [ nip push ] [ ] 3tri ] unless
-    3drop ; inline
+    3dup drop key? [ 3drop ] [
+        [ drop conjoin ] [ nip push ] 3bi
+    ] if ; inline
 
 : prune ( seq -- newseq )
     [ ] [ length <hashtable> ] [ length <vector> ] tri
@@ -16,7 +20,7 @@ IN: sets
     [ dup ] H{ } map>assoc ;
 
 : (all-unique?) ( elt hash -- ? )
-    2dup key? [ 2drop f ] [ dupd set-at t ] if ;
+    2dup key? [ 2drop f ] [ conjoin t ] if ;
 
 : all-unique? ( seq -- ? )
     dup length <hashtable> [ (all-unique?) ] curry all? ;
index 5000dbf5fdb3f9b01c7bc5279426e8472fd9d1dc..1beafc710adf79110daf9f4d4ea4600d511eec27 100644 (file)
@@ -1,6 +1,25 @@
 USING: help.markup help.syntax sequences strings ;
 IN: splitting
 
+ARTICLE: "groups-clumps" "Groups and clumps"
+"A virtual sequence for splitting a sequence into disjoint, fixed-length subsequences:"
+{ $subsection groups }
+{ $subsection <groups> }
+{ $subsection <sliced-groups> }
+"A virtual sequence for splitting a sequence into overlapping, fixed-length subsequences:"
+{ $subsection clumps }
+{ $subsection <clumps> }
+{ $subsection <sliced-clumps> }
+"The difference can be summarized as the following:"
+{ $list
+    { "With groups, the subsequences form the original sequence when concatenated:"
+        { $unchecked-example "dup n groups concat sequence= ." "t" }
+    }
+    { "With clumps, collecting the first element of each subsequence but the last one, together with the last subseqence, yields the original sequence:"
+        { $unchecked-example "dup n clumps unclip-last >r [ first ] map r> append sequence= ." "t" }
+    }
+} ;
+
 ARTICLE: "sequences-split" "Splitting sequences"
 "Splitting sequences at occurrences of subsequences:"
 { $subsection ?head }
@@ -9,14 +28,9 @@ ARTICLE: "sequences-split" "Splitting sequences"
 { $subsection ?tail-slice }
 { $subsection split1 }
 { $subsection split }
-"Grouping elements:"
-{ $subsection group }
-"A virtual sequence for grouping elements:"
-{ $subsection groups }
-{ $subsection <groups> }
-{ $subsection <sliced-groups> }
 "Splitting a string into lines:"
-{ $subsection string-lines } ;
+{ $subsection string-lines }
+{ $subsection "groups-clumps" } ;
 
 ABOUT: "sequences-split"
 
@@ -36,19 +50,22 @@ HELP: split
 { $examples { $example "USING: prettyprint splitting ;" "\"hello world-how are you?\" \" -\" split ." "{ \"hello\" \"world\" \"how\" \"are\" \"you?\" }" } } ;
 
 HELP: groups
-{ $class-description "Instances are virtual sequences whose elements are fixed-length subsequences or slices of an underlying sequence. Groups are mutable and resizable if the underlying sequence is mutable and resizable, respectively."
+{ $class-description "Instances are virtual sequences whose elements are disjoint fixed-length subsequences of an underlying sequence. Groups are mutable and resizable if the underlying sequence is mutable and resizable, respectively."
 $nl
 "New groups are created by calling " { $link <groups> } " and " { $link <sliced-groups> } "." }
 { $see-also group } ;
 
 HELP: group
 { $values { "seq" "a sequence" } { "n" "a non-negative integer" } { "array" "a sequence of sequences" } }
-{ $description "Splits the sequence into groups of " { $snippet "n" } " elements and collects the groups into a new array." }
-{ $notes "If the sequence length is not a multiple of " { $snippet "n" } ", the final subsequence in the list will be shorter than " { $snippet "n" } " elements." } ;
+{ $description "Splits the sequence into disjoint groups of " { $snippet "n" } " elements and collects the groups into a new array." }
+{ $notes "If the sequence length is not a multiple of " { $snippet "n" } ", the final subsequence in the list will be shorter than " { $snippet "n" } " elements." }
+{ $examples
+    { $example "USING: splitting prettyprint ;" "{ 3 1 3 3 7 } 2 group ." "{ { 3 1 } { 3 3 } { 7 } }" }
+} ;
 
 HELP: <groups>
 { $values { "seq" "a sequence" } { "n" "a non-negative integer" } { "groups" groups } }
-{ $description "Outputs a virtual sequence whose elements are subsequences consisting of groups of " { $snippet "n" } " elements from the underlying sequence." }
+{ $description "Outputs a virtual sequence whose elements are disjoint subsequences of " { $snippet "n" } " elements from the underlying sequence." }
 { $examples
     { $example
         "USING: arrays kernel prettyprint sequences splitting ;"
@@ -58,7 +75,7 @@ HELP: <groups>
 
 HELP: <sliced-groups>
 { $values { "seq" "a sequence" } { "n" "a non-negative integer" } { "groups" groups } }
-{ $description "Outputs a virtual sequence whose elements are slices consisting of groups of " { $snippet "n" } " elements from the underlying sequence." }
+{ $description "Outputs a virtual sequence whose elements are overlapping subsequences of " { $snippet "n" } " elements from the underlying sequence." }
 { $examples
     { $example
         "USING: arrays kernel prettyprint sequences splitting ;"
@@ -68,7 +85,46 @@ HELP: <sliced-groups>
     }
 } ;
 
-{ group <groups> <sliced-groups> } related-words
+HELP: clumps
+{ $class-description "Instances are virtual sequences whose elements are overlapping fixed-length subsequences o an underlying sequence. Clumps are mutable and resizable if the underlying sequence is mutable and resizable, respectively."
+$nl
+"New clumps are created by calling " { $link <clumps> } " and " { $link <sliced-clumps> } "." } ;
+
+HELP: clump
+{ $values { "seq" "a sequence" } { "n" "a non-negative integer" } { "array" "a sequence of sequences" } }
+{ $description "Splits the sequence into overlapping clumps of " { $snippet "n" } " elements and collects the clumps into a new array." }
+{ $errors "Throws an error if " { $snippet "n" } " is smaller than the length of the sequence." }
+{ $examples
+    { $example "USING: splitting prettyprint ;" "{ 3 1 3 3 7 } 2 clump ." "{ { 3 1 } { 1 3 } { 3 3 } { 3 7 } }" }
+} ;
+
+HELP: <clumps>
+{ $values { "seq" "a sequence" } { "n" "a non-negative integer" } { "clumps" clumps } }
+{ $description "Outputs a virtual sequence whose elements are overlapping subsequences of " { $snippet "n" } " elements from the underlying sequence." }
+{ $examples
+    "Running averages:"
+    { $example
+        "USING: splitting sequences math prettyprint kernel ;"
+        "IN: scratchpad"
+        ": share-price"
+        "    { 13/50 51/100 13/50 1/10 4/5 17/20 33/50 3/25 19/100 3/100 } ;"
+        ""
+        "share-price 4 <clumps> [ [ sum ] [ length ] bi / ] map ."
+        "{ 113/400 167/400 201/400 241/400 243/400 91/200 1/4 }"
+    }
+} ;
+
+HELP: <sliced-clumps>
+{ $values { "seq" "a sequence" } { "n" "a non-negative integer" } { "clumps" clumps } }
+{ $description "Outputs a virtual sequence whose elements are overlapping slices of " { $snippet "n" } " elements from the underlying sequence." } ;
+
+{ clumps groups } related-words
+
+{ clump group } related-words
+
+{ <clumps> <groups> } related-words
+
+{ <sliced-clumps> <sliced-groups> } related-words
 
 HELP: ?head
 { $values { "seq" "a sequence" } { "begin" "a sequence" } { "newseq" "a new sequence" } { "?" "a boolean" } }
index 9f6ae75d321dc5b3463f3481752a0e9bb208ddbd..62e7ef3782564a12cba0e3ca6b084bcd0a5d4c63 100755 (executable)
@@ -44,7 +44,7 @@ M: sliced-groups nth group@ <slice> ;
 
 TUPLE: clumps < abstract-groups ;
 
-: <clumps> ( seq n -- groups )
+: <clumps> ( seq n -- clumps )
     clumps construct-groups ; inline
 
 M: clumps length
@@ -58,7 +58,7 @@ M: clumps group@
 
 TUPLE: sliced-clumps < groups ;
 
-: <sliced-clumps> ( seq n -- groups )
+: <sliced-clumps> ( seq n -- clumps )
     sliced-clumps construct-groups ; inline
 
 M: sliced-clumps nth group@ <slice> ;
index 2410185b18723e315b0bd3034c84a0d6e8e75720..27c8609a99bd105dcd3ab81699c0efca3dac2691 100755 (executable)
@@ -100,8 +100,8 @@ IN: bootstrap.syntax
     ] define-syntax
 
     "DEFER:" [
-        scan in get create
-        dup old-definitions get first delete-at
+        scan current-vocab create
+        dup old-definitions get [ delete-at ] with each
         set-word
     ] define-syntax
 
@@ -189,8 +189,9 @@ IN: bootstrap.syntax
     "MAIN:" [ scan-word in get vocab set-vocab-main ] define-syntax
 
     "<<" [
-        [ \ >> parse-until >quotation ] with-compilation-unit
-        call
+        [
+            \ >> parse-until >quotation
+        ] with-nested-compilation-unit call
     ] define-syntax
 
     "call-next-method" [
index 5812516912bacda0220eb650011cc02c35d2420a..5549f980106b91df23ee9918b93aac3bce5ee4f5 100755 (executable)
@@ -175,7 +175,9 @@ PRIVATE>
 : define-symbol ( word -- )
     dup [ ] curry define-inline ;
 
-: reset-word ( word -- )
+GENERIC: reset-word ( word -- )
+
+M: word reset-word
     {
         "unannotated-def"
         "parsing" "inline" "foldable" "flushable"
index 7c274edb2e548c5d0b37cf9b0077e033fe5aae23..c3e487a9fce6c598e9680b36557a66eca482ea41 100755 (executable)
@@ -1,5 +1,5 @@
 USING: arrays assocs kernel vectors sequences namespaces
-random math.parser ;
+random math.parser math fry ;
 IN: assocs.lib
 
 : ref-at ( table key -- value ) swap at ;
@@ -40,3 +40,8 @@ IN: assocs.lib
 
 : set-at-unique ( value assoc -- key )
     dup generate-key [ swap set-at ] keep ;
+
+: histogram ( assoc quot -- assoc' )
+    H{ } clone [
+        swap [ change-at ] 2curry assoc-each
+    ] keep ;
index 2dac9eb68817bbde5591b712c44d216e6f53915c..8fef44a76a9a82e0cb8f4f38bd316a496b7fac68 100755 (executable)
@@ -35,10 +35,8 @@ IN: bunny.model
     [ normalize ] map ;
 
 : read-model ( stream -- model )
-    "Reading model" print flush [
-        ascii [ parse-model ] with-file-reader
-        [ normals ] 2keep 3array
-    ] time ;
+    ascii [ parse-model ] with-file-reader
+    [ normals ] 2keep 3array ;
 
 : model-path "bun_zipper.ply" temp-file ;
 
index e0daefd63c80d1060837f0fe82dc62e4a1cbf367..f5f4d3e9651bdad04d08103e4f0857fa1dc85527 100644 (file)
@@ -1,73 +1,39 @@
 ! Copyright (C) 2008 Matthew Willis.
 ! See http://factorcode.org/license.txt for BSD license.
-USING: cairo cairo.ffi ui.render kernel opengl.gl opengl
-math byte-arrays ui.gadgets accessors arrays 
-namespaces io.backend ;
+USING: sequences math opengl.gadgets kernel
+byte-arrays cairo.ffi cairo io.backend
+opengl.gl arrays ;
 
 IN: cairo.gadgets
 
-! We need two kinds of gadgets:
-! one performs the cairo ops once and caches the bytes, the other
-! performs cairo ops every refresh
-
-TUPLE: cairo-gadget width height quot cache? bytes ;
-PREDICATE: cached-cairo < cairo-gadget cache?>> ;
-: <cairo-gadget> ( width height quot -- cairo-gadget )
-    cairo-gadget construct-gadget 
-    swap >>quot
-    swap >>height
-    swap >>width ;
-
-: <cached-cairo> ( width height quot -- cairo-gadget )
-    <cairo-gadget> t >>cache? ;
-
 : width>stride ( width -- stride ) 4 * ;
     
-: copy-cairo ( width height quot -- byte-array )
-    >r over width>stride
+: copy-cairo ( dim quot -- byte-array )
+    >r first2 over width>stride
     [ * nip <byte-array> dup CAIRO_FORMAT_ARGB32 ]
     [ cairo_image_surface_create_for_data ] 3bi
     r> with-cairo-from-surface ;
 
-: (cairo>bytes) ( gadget -- byte-array )
-    [ width>> ] [ height>> ] [ quot>> ] tri copy-cairo ;
-
-GENERIC: cairo>bytes
-M: cairo-gadget cairo>bytes ( gadget -- byte-array )
-    (cairo>bytes) ;
-
-M: cached-cairo cairo>bytes ( gadget -- byte-array )
-    dup bytes>> [ ] [
-        dup (cairo>bytes) [ >>bytes drop ] keep
-    ] ?if ;
+: <cairo-gadget> ( dim quot -- )
+    over 2^-bounds swap copy-cairo
+    GL_BGRA rot <texture-gadget> ;
 
-: cairo>png ( gadget path -- )
-    >r [ cairo>bytes CAIRO_FORMAT_ARGB32 ] [ width>> ]
-    [ height>> ] tri over width>stride
-    cairo_image_surface_create_for_data
-    r> [ cairo_surface_write_to_png check-cairo ] curry with-surface ;
-
-M: cairo-gadget draw-gadget* ( gadget -- )
-    origin get [
-        0 0 glRasterPos2i
-        1.0 -1.0 glPixelZoom
-        [ width>> ] [ height>> GL_BGRA GL_UNSIGNED_BYTE ]
-        [ cairo>bytes ] tri glDrawPixels
-    ] with-translation ;
-    
-M: cairo-gadget pref-dim* ( gadget -- rect )
-    [ width>> ] [ height>> ] bi 2array ;
+! maybe also texture>png
+! : cairo>png ( gadget path -- )
+!    >r [ cairo>bytes CAIRO_FORMAT_ARGB32 ] [ width>> ]
+!    [ height>> ] tri over width>stride
+!    cairo_image_surface_create_for_data
+!    r> [ cairo_surface_write_to_png check-cairo ] curry with-surface ;
 
 : copy-surface ( surface -- )
     cr swap 0 0 cairo_set_source_surface
     cr cairo_paint ;
 
-: <bytes-gadget> ( width height bytes -- cairo-gadget )
-    >r [ ] <cached-cairo> r> >>bytes ;
-
 : <png-gadget> ( path -- gadget )
     normalize-path cairo_image_surface_create_from_png
     [ cairo_image_surface_get_width ]
-    [ cairo_image_surface_get_height 2dup ]
+    [ cairo_image_surface_get_height 2array dup 2^-bounds ]
     [ [ copy-surface ] curry copy-cairo ] tri
-    <bytes-gadget> ;
\ No newline at end of file
+    GL_BGRA rot <texture-gadget> ;
+
+
index 402c3881f4399248ce3432e47995103015e4a395..0e83381349c76cd4cebba7812da851adecc37f24 100644 (file)
@@ -116,11 +116,11 @@ IN: cairo.samples
     cr cairo_fill ;
 
 : utf8 ( -- )
-    cr "Serif" CAIRO_FONT_SLANT_NORMAL CAIRO_FONT_WEIGHT_NORMAL
+    cr "Sans" CAIRO_FONT_SLANT_NORMAL CAIRO_FONT_WEIGHT_NORMAL
     cairo_select_font_face
     cr 50 cairo_set_font_size
     "cairo_text_extents_t" malloc-object
-    cr B{ 230 151 165 230 156 172 232 170 158 } pick cairo_text_extents
+    cr "日本語" pick cairo_text_extents
     cr over
     [ cairo_text_extents_t-width 2 / ]
     [ cairo_text_extents_t-x_bearing ] bi +
@@ -129,7 +129,7 @@ IN: cairo.samples
     [ cairo_text_extents_t-y_bearing ] bi +
     128 swap - cairo_move_to
     free
-    cr B{ 230 151 165 230 156 172 232 170 158 } cairo_show_text
+    cr "日本語" cairo_show_text
     
     cr 1 0.2 0.2 0.6 cairo_set_source_rgba
     cr 6 cairo_set_line_width
@@ -142,6 +142,6 @@ IN: cairo.samples
  USING: quotations cairo.gadgets ui.gadgets.panes sequences ;
  : samples ( -- )
     { arc clip clip-image dash gradient text utf8 }
-    [ 256 256 rot 1quotation <cached-cairo> gadget. ] each ;
+    [ { 256 256 } swap 1quotation <cairo-gadget> gadget. ] each ;
  
- MAIN: samples
\ No newline at end of file
+ MAIN: samples
index f4e1669178e7c501dbcbf5040177ff90efd61af5..3efe33e26569063ebc3d867b8654647b6481daf3 100755 (executable)
@@ -50,3 +50,15 @@ IN: calendar.format.tests
     "Sun May 04 07:00:00 2008 GMT" cookie-string>timestamp\r
     timestamp>string\r
 ] unit-test\r
+\r
+[\r
+    T{ timestamp f\r
+        2008\r
+        5\r
+        26\r
+        0\r
+        37\r
+        42.12345\r
+        T{ duration f 0 0 0 -5 0 0 }\r
+    }\r
+] [ "2008-05-26T00:37:42.12345-05:00" rfc3339>timestamp ] unit-test\r
index 91a034f8bdcddf49f314a3157f4225bc7078bad3..ff1811e9d595aacc58b4ff4e9149b4c6b8323f81 100755 (executable)
@@ -1,4 +1,4 @@
-USING: math math.order math.parser kernel sequences io\r
+USING: math math.order math.parser math.functions kernel sequences io\r
 accessors arrays io.streams.string splitting\r
 combinators accessors debugger\r
 calendar calendar.format.macros ;\r
@@ -151,11 +151,15 @@ M: timestamp year. ( timestamp -- )
 : read-hms ( -- h m s )\r
     read-00 ":" expect read-00 ":" expect read-00 ;\r
 \r
+: read-rfc3339-seconds ( s -- s' ch )\r
+    "+-Z" read-until >r\r
+    [ string>number ] [ length 10 swap ^ ] bi / + r> ;\r
+\r
 : (rfc3339>timestamp) ( -- timestamp )\r
     read-ymd\r
     "Tt" expect\r
     read-hms\r
-    read1 { { CHAR: . [ read-000 1000 / + read1 ] } [ ] } case\r
+    read1 { { CHAR: . [ read-rfc3339-seconds ] } [ ] } case\r
     read-rfc3339-gmt-offset\r
     <timestamp> ;\r
 \r
index 4c4a98893531681cb780587138a7ce5d88a5c83a..3976b36cb9ccac61446ec48ccfb69e56432771d6 100755 (executable)
@@ -77,8 +77,21 @@ MACRO: <--&& ( quots -- )
     [ [ 2dup ] prepend [ not ] append [ f ] ] t short-circuit
     [ 2nip ] append ;
 
+! or
+
 MACRO: || ( quots -- ? ) [ [ t ] ] f short-circuit ;
 
+MACRO: 0|| ( quots -- ? ) [ [ t ] ] f short-circuit ;
+
+MACRO: 1|| ( quots -- ? )
+  [ [ dup ] prepend [ t ] ] f short-circuit [ nip ] append ;
+
+MACRO: 2|| ( quots -- ? )
+  [ [ 2dup ] prepend [ t ] ] f short-circuit [ 2nip ] append ;
+
+MACRO: 3|| ( quots -- ? )
+  [ [ 3dup ] prepend [ t ] ] f short-circuit [ 3nip ] append ;
+
 ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 ! ifte
 ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
index 9514f62cf0eed2e32a13358bdefb61fbd57c58cb..8d1feca6c73c3efd41fa900452f6e9df4c061000 100755 (executable)
@@ -39,31 +39,13 @@ TUPLE: statement handle sql in-params out-params bind-params bound? type ;
 TUPLE: simple-statement < statement ;
 TUPLE: prepared-statement < statement ;
 
-SINGLETON: throwable
-SINGLETON: nonthrowable
-
-: make-throwable ( obj -- obj' )
-    dup sequence? [
-        [ make-throwable ] map
-    ] [
-        throwable >>type
-    ] if ;
-
-: make-nonthrowable ( obj -- obj' )
-    dup sequence? [
-        [ make-nonthrowable ] map
-    ] [
-        nonthrowable >>type
-    ] if ;
-
 TUPLE: result-set sql in-params out-params handle n max ;
 
 : construct-statement ( sql in out class -- statement )
     new
         swap >>out-params
         swap >>in-params
-        swap >>sql
-        throwable >>type ;
+        swap >>sql ;
 
 HOOK: <simple-statement> db ( str in out -- statement )
 HOOK: <prepared-statement> db ( str in out -- statement )
@@ -81,12 +63,9 @@ GENERIC: more-rows? ( result-set -- ? )
 
 GENERIC: execute-statement* ( statement type -- )
 
-M: throwable execute-statement* ( statement type -- )
+M: object execute-statement* ( statement type -- )
     drop query-results dispose ;
 
-M: nonthrowable execute-statement* ( statement type -- )
-    drop [ query-results dispose ] [ 2drop ] recover ;
-
 : execute-statement ( statement -- )
     dup sequence? [
         [ execute-statement ] each
@@ -127,7 +106,7 @@ M: nonthrowable execute-statement* ( statement type -- )
 : query-map ( statement quot -- seq )
     accumulator >r query-each r> { } like ; inline
 
-: with-db ( db seq quot -- )
+: with-db ( seq class quot -- )
     >r make-db db-open db r>
     [ db get swap [ drop ] prepose with-disposal ] curry with-variable ;
     inline
diff --git a/extra/db/errors/errors.factor b/extra/db/errors/errors.factor
new file mode 100644 (file)
index 0000000..1e0d1e7
--- /dev/null
@@ -0,0 +1,11 @@
+! Copyright (C) 2008 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: kernel ;
+IN: db.errors
+
+ERROR: db-error ;
+ERROR: sql-error ;
+
+
+ERROR: table-exists ;
+ERROR: bad-schema ;
index 4d201c2edf6d4dd2808d9f5e0bb42ef16c1a01bb..63153c451ea0bed6fd5b9eb9dc2437816b75f719 100644 (file)
@@ -6,16 +6,16 @@ IN: db.pools
 
 TUPLE: db-pool < pool db params ;
 
-: <db-pool> ( db params -- pool )
+: <db-pool> ( params db -- pool )
     db-pool <pool>
-        swap >>params
-        swap >>db ;
+        swap >>db
+        swap >>params ;
 
 : with-db-pool ( db params quot -- )
     >r <db-pool> r> with-pool ; inline
 
 M: db-pool make-connection ( pool -- )
-    [ db>> ] [ params>> ] bi make-db db-open ;
+    [ params>> ] [ db>> ] bi make-db db-open ;
 
 : with-pooled-db ( pool quot -- )
     [ db swap with-variable ] curry with-pooled-connection ; inline
index 3e81b264d69e93f83c3361f14208db91f5853d58..f55897db88ad65d80a2ebd3a9064378c9d172074 100755 (executable)
@@ -175,7 +175,7 @@ M: postgresql-db create-sql-statement ( class -- seq )
 
 : drop-table-sql ( table -- statement )
     [
-        "drop table " 0% 0% ";" 0% drop
+        "drop table " 0% 0% drop
     ] query-make ;
 
 M: postgresql-db drop-sql-statement ( class -- seq )
index 6dab4f80b8119183edfda59c7a581bfb792039db..59ee60aa1fd68b8dfbde5e2a725b7be51a062a0d 100644 (file)
@@ -1,21 +1,19 @@
 ! Copyright (C) 2008 Doug Coleman.
 ! See http://factorcode.org/license.txt for BSD license.
 USING: accessors kernel math namespaces sequences random
-strings
-math.bitfields.lib namespaces.lib db db.tuples db.types
-math.intervals ;
+strings math.parser math.intervals combinators
+math.bitfields.lib namespaces.lib db db.tuples db.types ;
 IN: db.queries
 
 GENERIC: where ( specs obj -- )
 
 : maybe-make-retryable ( statement -- statement )
-    dup in-params>> [ generator-bind? ] contains? [
-        make-retryable
-    ] when ;
+    dup in-params>> [ generator-bind? ] contains?
+    [ make-retryable ] when ;
 
 : query-make ( class quot -- )
     >r sql-props r>
-    [ 0 sql-counter rot with-variable ";" 0% ] { "" { } { } } nmake
+    [ 0 sql-counter rot with-variable ] { "" { } { } } nmake
     <simple-statement> maybe-make-retryable ; inline
 
 M: db begin-transaction ( -- ) "BEGIN" sql-command ;
@@ -127,3 +125,36 @@ M: db <select-by-slots-statement> ( tuple class -- statement )
         " from " 0% 0%
         where-clause
     ] query-make ;
+
+: do-group ( tuple groups -- )
+    [
+        ", " join " group by " prepend append
+    ] curry change-sql drop ;
+
+: do-order ( tuple order -- )
+    [
+        ", " join " order by " prepend append
+    ] curry change-sql drop ;
+
+: do-offset ( tuple n -- )
+    [
+        number>string " offset " prepend append
+    ] curry change-sql drop ;
+
+: do-limit ( tuple n -- )
+    [
+        number>string " limit " prepend append
+    ] curry change-sql drop ;
+
+: make-advanced-statement ( tuple advanced -- tuple' )
+    dupd
+    {
+        [ group>> [ do-group ] [ drop ] if* ]
+        [ order>> [ do-order ] [ drop ] if* ]
+        [ limit>> [ do-limit ] [ drop ] if* ]
+        [ offset>> [ do-offset ] [ drop ] if* ]
+    } 2cleave ;
+
+M: db <advanced-select-statement> ( tuple class group order limit offset -- tuple )
+    advanced-statement boa
+    [ <select-by-slots-statement> ] dip make-advanced-statement ;
index cab7b83ced9c6981a37e213e9630901459db413e..0b57c2d8faa3556f6a740ee8d3be1673a3f4ca1b 100644 (file)
@@ -4,9 +4,11 @@ IN: db.sql.tests
 ! TUPLE: person name age ;
 : insert-1
     { insert
-        { table "person" }
-        { columns "name" "age" }
-        { values "erg" 26 }
+        {
+            { table "person" }
+            { columns "name" "age" }
+            { values "erg" 26 }
+        }
     } ;
 
 : update-1
index e92c4bbd8a415506fd6477bcf16a6063fd669956..b652e8fed708b3d929fe7691c490d61620c36481 100755 (executable)
@@ -4,24 +4,25 @@ USING: alien.c-types arrays assocs kernel math math.parser
 namespaces sequences db.sqlite.ffi db combinators
 continuations db.types calendar.format serialize
 io.streams.byte-array byte-arrays io.encodings.binary
-io.backend ;
+io.backend db.errors ;
 IN: db.sqlite.lib
 
-: sqlite-error ( n -- * )
-    sqlite-error-messages nth throw ;
+ERROR: sqlite-error < db-error n string ;
+ERROR: sqlite-sql-error < sql-error n string ;
 
-: sqlite-statement-error-string ( -- str )
-    db get db-handle sqlite3_errmsg ;
+: throw-sqlite-error ( n -- * )
+    dup sqlite-error-messages nth sqlite-error ;
 
 : sqlite-statement-error ( -- * )
-    sqlite-statement-error-string throw ;
+    SQLITE_ERROR
+    db get db-handle sqlite3_errmsg sqlite-sql-error ;
 
 : sqlite-check-result ( n -- )
     {
-        { [ dup SQLITE_OK = ] [ drop ] }
-        { [ dup SQLITE_ERROR = ] [ sqlite-statement-error ] }
-        [ sqlite-error ]
-    } cond ;
+        { SQLITE_OK [ ] }
+        { SQLITE_ERROR [ sqlite-statement-error ] }
+        [ throw-sqlite-error ]
+    } case ;
 
 : sqlite-open ( path -- db )
     normalize-path
@@ -158,12 +159,11 @@ IN: db.sqlite.lib
     dup sqlite-#columns [ sqlite-column ] with map ;
 
 : sqlite-step-has-more-rows? ( prepared -- bool )
-    dup SQLITE_ROW =  [
-        drop t
-    ] [
-        dup SQLITE_DONE =
-        [ drop ] [ sqlite-check-result ] if f
-    ] if ;
+    {
+        { SQLITE_ROW [ t ] }
+        { SQLITE_DONE [ f ] }
+        [ sqlite-check-result f ]
+    } case ;
 
 : sqlite-next ( prepared -- ? )
     sqlite3_step sqlite-step-has-more-rows? ;
index c10775f1c94bdce036485add02697542d76a1620..cc4e4d116ad03dbcd2831725c0f414ac19c9e4e6 100755 (executable)
@@ -16,7 +16,7 @@ M: sqlite-db make-db* ( path db -- db )
     swap >>path ;
 
 M: sqlite-db db-open ( db -- db )
-    [ path>> sqlite-open ] [ swap >>handle ] bi ;
+    dup path>> sqlite-open >>handle ;
 
 M: sqlite-db db-close ( handle -- ) sqlite-close ;
 M: sqlite-db dispose ( db -- ) dispose-db ;
@@ -197,4 +197,3 @@ M: sqlite-db compound ( str seq -- str' )
         { "default" [ first number>string join-space ] }
         [ 2drop ] 
     } case ;
-
index 4da82d92d6a1167b75c3cce2a51e35af2480aa5c..f9a597e814a2924344f9ed30d34fbf8bd2f22158 100755 (executable)
@@ -3,7 +3,8 @@
 USING: io.files kernel tools.test db db.tuples classes
 db.types continuations namespaces math math.ranges
 prettyprint calendar sequences db.sqlite math.intervals
-db.postgresql accessors random math.bitfields.lib ;
+db.postgresql accessors random math.bitfields.lib
+math.ranges strings sequences.lib ;
 IN: db.tuples.tests
 
 TUPLE: person the-id the-name the-number the-real
@@ -198,8 +199,8 @@ TUPLE: annotation n paste-id summary author mode contents ;
 : test-sqlite ( quot -- )
     >r "tuples-test.db" temp-file sqlite-db r> with-db ;
 
-: test-postgresql ( -- )
->r { "localhost" "postgres" "foob" "factor-test" } postgresql-db r> with-db ;
+: test-postgresql ( quot -- )
+    >r { "localhost" "postgres" "foob" "factor-test" } postgresql-db r> with-db ;
 
 : test-repeated-insert
     [ ] [ person ensure-table ] unit-test
@@ -224,6 +225,12 @@ TUPLE: serialize-me id data ;
 
 TUPLE: exam id name score ; 
 
+: random-exam ( -- exam )
+        f
+        6 [ CHAR: a CHAR: b [a,b] random ] replicate >string
+        100 random
+    exam boa ;
+
 : test-intervals ( -- )
     exam "EXAM"
     {
@@ -414,6 +421,43 @@ TUPLE: does-not-persist ;
     [ class \ not-persistent = ] must-fail-with
 ] test-postgresql
 
+
+TUPLE: suparclass id a ;
+
+suparclass f {
+    { "id" "ID" +db-assigned-id+ }
+    { "a" "A" INTEGER }
+} define-persistent
+
+TUPLE: subbclass < suparclass b ;
+
+subbclass "SUBCLASS" {
+    { "b" "B" TEXT }
+} define-persistent
+
+TUPLE: fubbclass < subbclass ;
+
+fubbclass "FUBCLASS" { } define-persistent
+
+: test-db-inheritance ( -- )
+    [ ] [ subbclass ensure-table ] unit-test
+    [ ] [ fubbclass ensure-table ] unit-test
+    
+    [ ] [
+        subbclass new 5 >>a "hi" >>b dup insert-tuple id>> "id" set
+    ] unit-test
+    
+    [ t "hi" 5 ] [
+        subbclass new "id" get >>id select-tuple
+        [ subbclass? ] [ b>> ] [ a>> ] tri
+    ] unit-test
+    
+    [ ] [ fubbclass new 0 >>a "hi" >>b insert-tuple ] unit-test
+    
+    [ t ] [ fubbclass new select-tuples [ fubbclass? ] all? ] unit-test ;
+
+[ test-db-inheritance ] test-sqlite
+
 ! Don't comment these out. These words must infer
 \ bind-tuple must-infer
 \ insert-tuple must-infer
index c940d121bb7503ac9d0e5071b9c1e6ec355fcb01..bac141d6d28e634b49c31fb0febc739ed152c143 100755 (executable)
@@ -13,13 +13,13 @@ IN: db.tuples
     "db-columns" set-word-prop
     "db-relations" set-word-prop ;
 
-ERROR: not-persistent ;
+ERROR: not-persistent class ;
 
 : db-table ( class -- obj )
-    "db-table" word-prop [ not-persistent ] unless* ;
+    dup "db-table" word-prop [ ] [ not-persistent ] ?if ;
 
 : db-columns ( class -- obj )
-    "db-columns" word-prop ;
+    superclasses [ "db-columns" word-prop ] map concat ;
 
 : db-relations ( class -- obj )
     "db-relations" word-prop ;
@@ -42,6 +42,8 @@ HOOK: <insert-user-assigned-statement> db ( class -- obj )
 HOOK: <update-tuple-statement> db ( class -- obj )
 HOOK: <delete-tuples-statement> db ( tuple class -- obj )
 HOOK: <select-by-slots-statement> db ( tuple class -- tuple )
+TUPLE: advanced-statement group order offset limit ;
+HOOK: <advanced-select-statement> db ( tuple class group order offset limit -- tuple )
 
 HOOK: insert-tuple* db ( tuple statement -- )
 
@@ -74,16 +76,16 @@ M: retryable execute-statement* ( statement type -- )
         [ regenerate-params bind-statement* f ] cleanup
     ] curry 10 retry drop ;
 
-: resulting-tuple ( row out-params -- tuple )
-    dup first class>> new [
+: resulting-tuple ( class row out-params -- tuple )
+    rot class new [
         [
             >r slot-name>> r> set-slot-named
         ] curry 2each
     ] keep ;
 
-: query-tuples ( statement -- seq )
+: query-tuples ( exemplar-tuple statement -- seq )
     [ out-params>> ] keep query-results [
-        [ sql-row-typed swap resulting-tuple ] with query-map
+        [ sql-row-typed swap resulting-tuple ] with with query-map
     ] with-disposal ;
  
 : query-modify-tuple ( tuple statement -- )
@@ -110,8 +112,8 @@ M: retryable execute-statement* ( statement type -- )
 
 : recreate-table ( class -- )
     [
-        drop-sql-statement make-nonthrowable
-        [ execute-statement ] with-disposals
+        [ drop-sql-statement [ execute-statement ] with-disposals
+        ] curry ignore-errors
     ] [ create-table ] bi ;
 
 : ensure-table ( class -- )
@@ -141,9 +143,12 @@ M: retryable execute-statement* ( statement type -- )
         [ bind-tuple ] keep execute-statement
     ] with-disposal ;
 
+: do-select ( exemplar-tuple statement -- tuples )
+    [ [ bind-tuple ] [ query-tuples ] 2bi ] with-disposal ;
+
 : select-tuples ( tuple -- tuples )
-    dup dup class <select-by-slots-statement> [
-        [ bind-tuple ] keep query-tuples
-    ] with-disposal ;
+    dup dup class <select-by-slots-statement> do-select ;
 
-: select-tuple ( tuple -- tuple/f ) select-tuples ?first ;
+: select-tuple ( tuple -- tuple/f )
+    dup dup class f f f 1 <advanced-select-statement>
+    do-select ?first ;
index 8dbf6786bc197fc9fa45ae7df191921f0baa5152..03e6b15bdb3c2e6a35b13563b2a6be2a713066e1 100755 (executable)
@@ -142,7 +142,8 @@ HOOK: bind% db ( spec -- )
 HOOK: bind# db ( spec obj -- )
 
 : offset-of-slot ( str obj -- n )
-    class "slots" word-prop slot-named slot-spec-offset ;
+    class superclasses [ "slots" word-prop ] map concat
+    slot-named slot-spec-offset ;
 
 : get-slot-named ( name obj -- value )
     tuck offset-of-slot slot ;
index 75bbf9de9d071f46b0f5054655a278095ddead7a..5c4539b913f991e6579f04f0272a4b4883ec4b89 100644 (file)
@@ -68,7 +68,7 @@ SYMBOL: NX
 
 : expired? ( entry -- ? ) time>> time->ttl 0 <= ;
 
-: cache-get ( query -- result )
+: cache-get* ( query -- rrs/NX/f )
   dup table-get               ! query result
     {
       { [ dup f = ]      [ 2drop f ]          } ! not in the cache
@@ -80,6 +80,11 @@ SYMBOL: NX
 
 ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
+: cache-get ( query -- rrs/f )
+  dup cache-get* dup NX = [ drop name>> name-error ] [ nip ] if ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
 : rr->entry ( rr -- entry )
   [ ttl>> ttl->time ] [ rdata>> {1} >vector ] bi entry boa ;
 
@@ -110,3 +115,31 @@ SYMBOL: NX
 : cache-add-rr ( rr -- ) [ rr->query ] [ ] bi cache-add ;
 
 : cache-add-rrs ( rrs -- ) [ cache-add-rr ] each ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+! cache-name-error
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: message-soa ( message -- rr/soa )
+  authority-section>> [ type>> SOA = ] filter 1st ;
+
+: cache-name-error ( message -- message )
+  dup
+    [ message-query ] [ message-soa ttl>> ] bi
+  cache-nx ;
+
+: cache-message-records ( message -- message )
+  dup
+    {
+      [ answer-section>>     cache-add-rrs ]
+      [ authority-section>>  cache-add-rrs ]
+      [ additional-section>> cache-add-rrs ]
+    }
+  cleave ;
+
+: cache-message ( message -- message )
+  dup rcode>> NAME-ERROR = [ cache-name-error ] when
+  cache-message-records ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
index 560db69bb2f70cf71bcda4a625fca52a00aa78fd..6386655a4e833560db3801c7e00e59d2cee6f246 100644 (file)
@@ -38,7 +38,7 @@ TUPLE: message
 ! TYPE
 ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
-SYMBOLS: A NS MD MF CNAME SOA MB MG MR NULL WKS PTR HINFO MINFO MX TXT ;
+SYMBOLS: A NS MD MF CNAME SOA MB MG MR NULL WKS PTR HINFO MINFO MX TXT AAAA ;
 
 : type-table ( -- table )
   {
@@ -58,6 +58,7 @@ SYMBOLS: A NS MD MF CNAME SOA MB MG MR NULL WKS PTR HINFO MINFO MX TXT ;
     { MINFO 14 }
     { MX    15 }
     { TXT   16 }
+    { AAAA  28 }
   } ;
 
 ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@@ -126,6 +127,8 @@ SYMBOLS: NO-ERROR FORMAT-ERROR SERVER-FAILURE NAME-ERROR NOT-IMPLEMENTED
 
 : ip->ba ( ip -- ba ) "." split [ string>number ] map >byte-array ;
 
+: ipv6->ba ( ip -- ba ) ":" split [ 16 base> ] map [ 2 >be ] map concat ;
+
 : label->ba ( label -- ba ) [ >byte-array ] [ length ] bi prefix ;
 
 ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@@ -330,6 +333,13 @@ SYMBOLS: NO-ERROR FORMAT-ERROR SERVER-FAILURE NAME-ERROR NOT-IMPLEMENTED
 
 : get-ip ( ba i -- ip ) dup 4 + subseq >array [ number>string ] map "." join ;
 
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: get-ipv6 ( ba i -- ip )
+  dup 16 + subseq 2 group [ be> 16 >base ] map ":" join ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
 : get-rdata ( ba i type -- rdata )
     {
       { CNAME [ get-name ] }
@@ -338,6 +348,7 @@ SYMBOLS: NO-ERROR FORMAT-ERROR SERVER-FAILURE NAME-ERROR NOT-IMPLEMENTED
       { MX    [ get-mx   ] }
       { SOA   [ get-soa  ] }
       { A     [ get-ip   ] }
+      { AAAA  [ get-ipv6 ] }
     }
   case ;
 
@@ -459,4 +470,22 @@ SYMBOLS: NO-ERROR FORMAT-ERROR SERVER-FAILURE NAME-ERROR NOT-IMPLEMENTED
 
 : ask ( message -- message ) dns-server ask-server ;
 
-: <query-message> ( query -- message ) <message> swap {1} >>question-section ;
\ No newline at end of file
+: query->message ( query -- message ) <message> swap {1} >>question-section ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: message-query ( message -- query ) question-section>> 1st ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ERROR: name-error name ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: fully-qualified ( name -- name )
+    {
+      { [ dup empty?         ] [ "." append ] }
+      { [ dup peek CHAR: . = ] [            ] }
+      { [ t                  ] [ "." append ] }
+    }
+  cond ;
diff --git a/extra/dns/forwarding/forwarding.factor b/extra/dns/forwarding/forwarding.factor
new file mode 100644 (file)
index 0000000..1c60532
--- /dev/null
@@ -0,0 +1,109 @@
+
+USING: kernel
+       combinators
+       vectors
+       sequences
+       io.sockets
+       accessors
+       combinators.lib
+       newfx
+       dns dns.cache dns.misc ;
+
+IN: dns.forwarding
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+! DNS server - caching, forwarding
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: (socket) ( -- vec ) V{ f } ;
+
+: socket ( -- socket ) (socket) 1st ;
+
+: init-socket-on-port ( port -- )
+  f swap <inet4> <datagram> 0 (socket) as-mutate ;
+
+: init-socket ( -- ) 53 init-socket-on-port ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: (upstream-server) ( -- vec ) V{ f } ;
+
+: upstream-server ( -- ip ) (upstream-server) 1st ;
+
+: set-upstream-server ( ip -- ) 0 (upstream-server) as-mutate ;
+
+: init-upstream-server ( -- )
+  upstream-server not
+    [ resolv-conf-server set-upstream-server ]
+  when ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: 1&& <-&& ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: rrs? ( obj -- ? ) { [ NX = not ] [ f = not ] } 1&& ;
+
+: query->answer/cache ( query -- rrs/NX/f )
+  dup cache-get* dup { [ rrs? ] [ NX = ] } 1||
+    [ nip ]
+    [
+      drop
+      dup clone CNAME >>type cache-get* dup { [ NX = ] [ f = ] } 1||
+        [ nip ]
+        [                                       ! query rrs
+          tuck                                  ! rrs query rrs
+          1st                                   ! rrs query rr/cname
+          rdata>>                               ! rrs query name
+          >r clone r> >>name                    ! rrs query
+          query->answer/cache                   ! rrs rrs/NX/f
+          dup rrs? [ append ] [ nip ] if
+        ]
+      if
+    ]
+  if ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: answer-from-cache ( message -- message/f )
+  dup message-query                        ! message query
+  dup query->answer/cache                  ! message query rrs/NX/f
+    {
+      { [ dup f = ]  [ 3drop f ] }
+      { [ dup NX = ] [ 2drop NAME-ERROR >>rcode ] }
+      { [ t ]        [ nip >>answer-section ] }
+    }
+  cond ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: answer-from-server ( message -- message )
+  upstream-server ask-server
+  cache-message ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: find-answer ( message -- message )
+  dup answer-from-cache dup
+    [ nip ]
+    [ drop answer-from-server ]
+  if ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: loop ( -- )
+  socket receive                              ! byte-array addr-spec
+  swap                                        ! addr-spec byte-array
+  parse-message                               ! addr-spec message
+  find-answer                                 ! addr-spec message
+  message->ba                                 ! addr-spec byte-array
+  swap                                        ! byte-array addr-spec
+  socket send
+  loop ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: start ( -- ) init-socket init-upstream-server loop ;
+
+MAIN: start
\ No newline at end of file
diff --git a/extra/dns/misc/misc.factor b/extra/dns/misc/misc.factor
new file mode 100644 (file)
index 0000000..90731ce
--- /dev/null
@@ -0,0 +1,12 @@
+
+USING: kernel sequences splitting io.files io.encodings.utf8 random newfx ;
+
+IN: dns.misc
+
+: resolv-conf-servers ( -- seq )
+  "/etc/resolv.conf" utf8 file-lines
+  [ " " split ] map
+  [ 1st "nameserver" = ] filter
+  [ 2nd ] map ;
+
+: resolv-conf-server ( -- ip ) resolv-conf-servers random ;
\ No newline at end of file
diff --git a/extra/dns/recursive/recursive.factor b/extra/dns/recursive/recursive.factor
new file mode 100644 (file)
index 0000000..3a74667
--- /dev/null
@@ -0,0 +1,185 @@
+
+USING: kernel continuations
+       combinators
+       sequences
+       math
+       random
+       unicode.case
+       accessors symbols
+       combinators.lib combinators.cleave
+       newfx
+       dns dns.cache ;
+
+IN: dns.recursive
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: root-dns-servers ( -- servers )
+  {
+    "192.5.5.241"
+    "192.112.36.4"
+    "128.63.2.53"
+    "192.36.148.17"
+    "192.58.128.30"
+    "193.0.14.129"
+    "199.7.83.42"
+    "202.12.27.33"
+    "198.41.0.4"
+  } ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: {name-type-class} ( obj -- seq )
+  [ name>> >lower ] [ type>> ] [ class>> ] tri {3} ;
+
+: rr=query? ( rr query -- ? ) [ {name-type-class} ] bi@ = ;
+
+: rr-filter ( rrs query -- rrs ) [ rr=query? ] curry filter ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: answer-hits ( message -- rrs )
+  [ answer-section>> ] [ message-query ] bi rr-filter ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: name-hits ( message -- rrs )
+  [ answer-section>> ] [ message-query clone A >>type ] bi rr-filter ;
+
+: cname-hits ( message -- rrs )
+  [ answer-section>> ] [ message-query clone CNAME >>type ] bi rr-filter ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: authority-hits ( message -- rrs )
+  authority-section>> [ type>> NS = ] filter ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+SYMBOLS: ANSWERED NO-NAME-SERVERS UNCLASSIFIED ;
+
+: classify-message ( message -- symbol )
+    {
+      { [ dup rcode>> NAME-ERROR     = ] [ drop NAME-ERROR      ] }
+      { [ dup rcode>> SERVER-FAILURE = ] [ drop SERVER-FAILURE  ] }
+      { [ dup answer-hits empty? not   ] [ drop ANSWERED        ] }
+      { [ dup cname-hits  empty? not   ] [ drop CNAME           ] }
+      { [ dup authority-hits empty?    ] [ drop NO-NAME-SERVERS ] }
+      { [ t                            ] [ drop UNCLASSIFIED    ] }
+    }
+  cond ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+DEFER: name->ip
+
+! : name->ip/f ( name -- ip/f ) [ name->ip ] [ drop f ] recover ;
+
+! : extract-ns-ips ( message -- ips )
+!   authority-hits [ rdata>> name->ip/f ] map [ ] filter ;
+
+: extract-ns-ips ( message -- ips )
+  authority-hits [ rdata>> name->ip ] map [ ] filter ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: (recursive-query) ( query servers -- message )
+  dup random                                 ! query servers server
+  pick query->message 0 >>rd                 ! query servers server message
+  over ask-server                            ! query servers server message
+  cache-message                              ! query servers server message
+  dup classify-message                       ! query servers server message sym
+    {
+      { NAME-ERROR      [ -roll 3drop ] }
+      { ANSWERED        [ -roll 3drop ] }
+      { CNAME           [ -roll 3drop ] }
+      { NO-NAME-SERVERS [ -roll 3drop ] }
+      {
+        SERVER-FAILURE
+        [
+          -roll                              ! message query servers server
+          remove                             ! message query servers
+          dup empty?
+            [ 2drop ]
+            [ rot drop (recursive-query) ]
+          if
+        ]
+      }
+      [                                      ! query servers server message sym
+        drop nip nip                         ! query message
+        extract-ns-ips                       ! query ips
+        (recursive-query)
+      ]
+    }
+  case ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: cdr-name ( name -- name ) dup CHAR: . index 1+ tail ;
+
+: cache-get-ns ( name -- seq/f ) NS IN query boa cache-get ;
+
+: name->servers ( name -- servers )
+    {
+      { [ dup "" = ]         [ drop root-dns-servers ] }
+      { [ dup cache-get-ns ] [ cache-get-ns [ rdata>> name->ip ] map ] }
+      { [ t ]                [ cdr-name name->servers ] }
+    }
+  cond ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: recursive-query ( query -- message )
+  dup name>> name->servers (recursive-query) ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: canonical/cache ( name -- name )
+  dup CNAME IN query boa cache-get dup [ nip 1st rdata>> ] [ drop ] if ;
+
+: name->ip/cache ( name -- ip/f )
+  canonical/cache
+  A IN query boa cache-get dup [ random rdata>> ] [ ] if ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+:  name-hits? ( message -- message ? ) dup  name-hits empty? not ;
+: cname-hits? ( message -- message ? ) dup cname-hits empty? not ;
+
+! : name->ip/server ( name -- ip-or-f )
+!   A IN query boa root-dns-servers recursive-query ! message
+!     {
+!       { [ name-hits? ]  [ name-hits  random rdata>>          ] }
+!       { [ cname-hits? ] [ cname-hits random rdata>> name->ip ] }
+!       { [ t           ] [ drop f ] }
+!     }
+!   cond ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+: name->ip/server ( name -- ip-or-f )
+  A IN query boa recursive-query ! message
+    {
+      { [ name-hits? ]  [ name-hits  random rdata>>          ] }
+      { [ cname-hits? ] [ cname-hits random rdata>> name->ip ] }
+      { [ t           ] [ drop f ] }
+    }
+  cond ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+! : name->ip ( name -- ip )
+!   { [ name->ip/cache ] [ name->ip/server ] [ name-error ] } 1|| ;
+
+: name->ip ( name -- ip )
+  dup name->ip/cache dup
+    [ nip ]
+    [
+      drop dup name->ip/server dup
+        [ nip ]
+        [ drop name-error ]
+      if
+    ]
+  if ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
index c8a9f22d08c942f40df44a4e427c9c90f18084e3..2dae43b5d4f320c85d51e8c41592eb72d72d466f 100644 (file)
@@ -6,34 +6,6 @@ IN: dns.resolver
 
 ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
-! Need to cache records even in the case of name error
-
-: cache-message ( message -- message )
-  dup dup rcode>> NAME-ERROR =
-    [
-      [ question-section>> 1st ]
-      [ authority-section>> [ type>> SOA = ] filter random ttl>> ]
-      bi
-      cache-nx
-    ]
-    [
-        {
-          [ answer-section>>     cache-add-rrs ]
-          [ authority-section>>  cache-add-rrs ]
-          [ additional-section>> cache-add-rrs ]
-        }
-      cleave
-    ]
-  if ;
-
-! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
-! Ask and cache the records
-
-: ask* ( message -- message ) ask cache-message ;
-
-! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
 : canonical/cache ( name -- name )
   dup CNAME IN query boa cache-get dup vector? ! name result ?
     [ nip 1st rdata>> ]
@@ -43,26 +15,17 @@ IN: dns.resolver
 : name->ip/cache ( name -- ip )
   canonical/cache
   dup A IN query boa cache-get ! name result
-  {
-    {
-      [ dup NX = ]
-      [ 2drop f ]
-    }
-    {
-      [ dup f = ]
-      [ 2drop f ]
-    }
     {
-      [ t ]
-      [ nip random rdata>> ]
+      { [ dup NX = ] [ 2drop f ] }
+      { [ dup f = ]  [ 2drop f ] }
+      { [ t ]        [ nip random rdata>> ] }
     }
-  }
-    cond ;
+  cond ;
 
 ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
 : canonical/server ( name -- name )
-  dup CNAME IN query boa <query-message> ask* answer-section>>
+  dup CNAME IN query boa query->message ask cache-message answer-section>>
   [ type>> CNAME = ] filter dup empty? not
     [ nip 1st rdata>> ]
     [ drop ]
@@ -70,7 +33,7 @@ IN: dns.resolver
 
 : name->ip/server ( name -- ip )
   canonical/server
-  dup A IN query boa <query-message> ask* answer-section>>
+  dup A IN query boa query->message ask cache-message answer-section>>
   [ type>> A = ] filter dup empty? not
     [ nip random rdata>> ]
     [ 2drop f ]
@@ -78,16 +41,6 @@ IN: dns.resolver
 
 ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
-: fully-qualified ( name -- name )
-    {
-      { [ dup empty?         ] [ "." append ] }
-      { [ dup peek CHAR: . = ] [            ] }
-      { [ t                  ] [ "." append ] }
-    }
-  cond ;
-
-! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
 : name->ip ( name -- ip )
   fully-qualified
   dup name->ip/cache dup
diff --git a/extra/dns/stub/stub.factor b/extra/dns/stub/stub.factor
new file mode 100644 (file)
index 0000000..a15feb5
--- /dev/null
@@ -0,0 +1,20 @@
+
+USING: kernel sequences random accessors dns ;
+
+IN: dns.stub
+
+! Stub resolver
+! 
+! Generally useful, but particularly when running a forwarding,
+! caching, nameserver on localhost with multiple Factor instances
+! querying it.
+
+: name->ip ( name -- ip )
+  A IN query boa
+  query->message
+  ask
+  dup rcode>> NAME-ERROR =
+    [ message-query name>> name-error ]
+    [ answer-section>> [ type>> A = ] filter random rdata>> ]
+  if ;
+
index 9a3862d097d5c9de81ba37b8952c4284673fb13e..17d286252e426df83608c5f7df722278123cbf00 100755 (executable)
@@ -62,12 +62,23 @@ IN: farkup.tests
 [ "<p>=</p><h2>foo</h2>" ] [ "===foo==" convert-farkup ] unit-test
 [ "<h1>foo</h1><p>=</p>" ] [ "=foo==" convert-farkup ] unit-test
 
-[ "<div style='white-space: pre; font-family: monospace; '><span class='KEYWORD3'>int</span> <span class='FUNCTION'>main</span><span class='OPERATOR'>(</span><span class='OPERATOR'>)</span><br/></div>" ]
+[ "<pre><span class='KEYWORD3'>int</span> <span class='FUNCTION'>main</span><span class='OPERATOR'>(</span><span class='OPERATOR'>)</span>\n</pre>" ]
 [ "[c{int main()}]" convert-farkup ] unit-test
 
 [ "<p><img src=\"lol.jpg\"/></p>" ] [ "[[image:lol.jpg]]" convert-farkup ] unit-test
 [ "<p><img src=\"lol.jpg\" alt=\"teh lol\"/></p>" ] [ "[[image:lol.jpg|teh lol]]" convert-farkup ] unit-test
-[ "<p><a href=\"lol.com\"></a></p>" ] [ "[[lol.com]]" convert-farkup ] unit-test
+[ "<p><a href=\"lol.com\">lol.com</a></p>" ] [ "[[lol.com]]" convert-farkup ] unit-test
 [ "<p><a href=\"lol.com\">haha</a></p>" ] [ "[[lol.com|haha]]" convert-farkup ] unit-test
 
 [ ] [ "[{}]" convert-farkup drop ] unit-test
+
+[
+    "<p>Feature comparison:\n<table><tr><td>a</td><td>Factor</td><td>Java</td><td>Lisp</td></tr><tr><td>Coolness</td><td>Yes</td><td>No</td><td>No</td></tr><tr><td>Badass</td><td>Yes</td><td>No</td><td>No</td></tr><tr><td>Enterprise</td><td>Yes</td><td>Yes</td><td>No</td></tr><tr><td>Kosher</td><td>Yes</td><td>No</td><td>Yes</td></tr></table></p>"
+] [ "Feature comparison:\n|a|Factor|Java|Lisp|\n|Coolness|Yes|No|No|\n|Badass|Yes|No|No|\n|Enterprise|Yes|Yes|No|\n|Kosher|Yes|No|Yes|\n" convert-farkup ] unit-test
+
+[
+    "<p>Feature comparison:\n\n<table><tr><td>a</td><td>Factor</td><td>Java</td><td>Lisp</td></tr><tr><td>Coolness</td><td>Yes</td><td>No</td><td>No</td></tr><tr><td>Badass</td><td>Yes</td><td>No</td><td>No</td></tr><tr><td>Enterprise</td><td>Yes</td><td>Yes</td><td>No</td></tr><tr><td>Kosher</td><td>Yes</td><td>No</td><td>Yes</td></tr></table></p>"
+] [ "Feature comparison:\n\n|a|Factor|Java|Lisp|\n|Coolness|Yes|No|No|\n|Badass|Yes|No|No|\n|Enterprise|Yes|Yes|No|\n|Kosher|Yes|No|Yes|\n" convert-farkup ] unit-test
+
+[ "<p>a-b</p>" ] [ "a-b" convert-farkup ] unit-test
+[ "<ul><li>a-b</li></ul>" ] [ "-a-b" convert-farkup ] unit-test
index 98f0d0245f2d4ebe3a5e259fbc4ec4cd7689789d..1b51bb57524efb283a311751537aaf493454f924 100755 (executable)
@@ -2,10 +2,13 @@
 ! See http://factorcode.org/license.txt for BSD license.
 USING: arrays io io.styles kernel memoize namespaces peg
 sequences strings html.elements xml.entities xmode.code2html
-splitting io.streams.string html peg.parsers html.elements
+splitting io.streams.string peg.parsers
 sequences.deep unicode.categories ;
 IN: farkup
 
+SYMBOL: relative-link-prefix
+SYMBOL: link-no-follow?
+
 <PRIVATE
 
 : delimiters ( -- string )
@@ -59,25 +62,30 @@ MEMO: eq ( -- parser )
 : render-code ( string mode -- string' )
     >r string-lines r>
     [
-        [
-            H{ { wrap-margin f } } [
-                htmlize-lines
-            ] with-nesting
-        ] with-html-stream
+        <pre>
+            htmlize-lines
+        </pre>
     ] with-string-writer ;
 
 : check-url ( href -- href' )
     CHAR: : over member? [
         dup { "http://" "https://" "ftp://" } [ head? ] with contains?
         [ drop "/" ] unless
-    ] when ;
+    ] [
+        relative-link-prefix get prepend
+    ] if ;
 
 : escape-link ( href text -- href-esc text-esc )
     >r check-url escape-quoted-string r> escape-string ;
 
 : make-link ( href text -- seq )
     escape-link
-    [ "<a href=\"" , >r , r> "\">" , [ , ] when* "</a>" , ] { } make ;
+    [
+        "<a" ,
+        " href=\"" , >r , r>
+        link-no-follow? get [ " nofollow=\"true\"" , ] when
+        "\">" , , "</a>" ,
+    ] { } make ;
 
 : make-image-link ( href alt -- seq )
     escape-link
@@ -102,7 +110,7 @@ MEMO: simple-link ( -- parser )
         "[[" token hide ,
         [ "|]" member? not ] satisfy repeat1 ,
         "]]" token hide ,
-    ] seq* [ first f make-link ] action ;
+    ] seq* [ first dup make-link ] action ;
 
 MEMO: labelled-link ( -- parser )
     [
@@ -113,12 +121,14 @@ MEMO: labelled-link ( -- parser )
         "]]" token hide ,
     ] seq* [ first2 make-link ] action ;
 
-MEMO: link ( -- parser ) [ image-link , simple-link , labelled-link , ] choice* ;
+MEMO: link ( -- parser )
+    [ image-link , simple-link , labelled-link , ] choice* ;
 
 DEFER: line
 MEMO: list-item ( -- parser )
     [
-        "-" token hide , line ,
+        "-" token hide , ! text ,
+        [ "\r\n" member? not ] satisfy repeat1 [ >string escape-string ] action ,
     ] seq* [ "li" surround-with-foo ] action ;
 
 MEMO: list ( -- parser )
@@ -149,6 +159,8 @@ MEMO: code ( -- parser )
 
 MEMO: line ( -- parser )
     [
+        nl table 2seq ,
+        nl list 2seq ,
         text , strong , emphasis , link ,
         superscript , subscript , inline-code ,
         escaped-char , delimiter , eq ,
index eb59ffae4e7282c19ace7f8b9d11adc2196ebbc0..6d6abba23c7cb02bc57b7ce10d2de96a242b623b 100755 (executable)
@@ -52,3 +52,13 @@ sequences ;
 [ { 1 { 2 { 3 } } } ] [
     1 2 3 '[ , [ , [ , 1array ] call 2array ] call 2array ] call
 ] unit-test
+
+{ 1 1 } [ '[ [ [ , ] ] ] ] must-infer-as
+
+[ { { { 3 } } } ] [
+    3 '[ [ [ , 1array ] call 1array ] call 1array ] call
+] unit-test
+
+[ { { { 3 } } } ] [
+    3 '[ [ [ , 1array ] call 1array ] call 1array ] call
+] unit-test
index 27a321ed921f84b771e1feb9d433a98de799a4de..4581c048fdef3cc8390101fac80d8a90600ae7dd 100755 (executable)
@@ -46,15 +46,22 @@ DEFER: (shallow-fry)
         shallow-fry
     ] if* ;
 
+: fry-specifier? ( obj -- ? ) { , namespaces:, @ } member? ;
+
+: count-inputs ( quot -- n )
+    [
+        {
+            { [ dup callable? ] [ count-inputs ] }
+            { [ dup fry-specifier? ] [ drop 1 ] }
+            [ drop 0 ]
+        } cond
+    ] map sum ;
+
 : fry ( quot -- quot' )
     [
         [
             dup callable? [
-                [
-                    [ { , namespaces:, @ } member? ] filter length
-                    \ , <repetition> %
-                ]
-                [ fry % ] bi
+                [ count-inputs \ , <repetition> % ] [ fry % ] bi
             ] [ namespaces:, ] if
         ] each
     ] [ ] make deep-fry ;
index 4fa56bcf938410991ecc310a623a5920ed5a2f7e..db1921d86dbab209ac7cdc86e049cbf162716528 100755 (executable)
@@ -1,6 +1,6 @@
 ! Copyright (C) 2007 Slava Pestov.
 ! See http://factorcode.org/license.txt for BSD license.
-USING: parser-combinators regexp lazy-lists sequences kernel
+USING: parser-combinators regexp lists lists.lazy sequences kernel
 promises strings unicode.case ;
 IN: globs
 
diff --git a/extra/html/authors.txt b/extra/html/authors.txt
deleted file mode 100644 (file)
index 65da810..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-Slava Pestov
-Matthew Willis
-Chris Double
diff --git a/extra/html/components/components-tests.factor b/extra/html/components/components-tests.factor
new file mode 100644 (file)
index 0000000..1a0f849
--- /dev/null
@@ -0,0 +1,180 @@
+IN: html.components.tests
+USING: tools.test kernel io.streams.string
+io.streams.null accessors inspector html.streams
+html.components namespaces ;
+
+[ ] [ blank-values ] unit-test
+
+[ ] [ 3 "hi" set-value ] unit-test
+
+[ 3 ] [ "hi" value ] unit-test
+
+TUPLE: color red green blue ;
+
+[ ] [ 1 2 3 color boa from-tuple ] unit-test
+
+[ 1 ] [ "red" value ] unit-test
+
+[ ] [ "jimmy" "red" set-value ] unit-test
+
+[ "123.5" ] [ 123.5 object>string ] unit-test
+
+[ "jimmy" ] [
+    [
+        "red" label render
+    ] with-string-writer
+] unit-test
+
+[ ] [ "<jimmy>" "red" set-value ] unit-test
+
+[ "&lt;jimmy&gt;" ] [
+    [
+        "red" label render
+    ] with-string-writer
+] unit-test
+
+[ "<input type='hidden' name='red' value='<jimmy>'/>" ] [
+    [
+        "red" hidden render
+    ] with-string-writer
+] unit-test
+
+[ ] [ "'jimmy'" "red" set-value ] unit-test
+
+[ "<input type='text' size='5' name='red' value='&apos;jimmy&apos;'/>" ] [
+    [
+        "red" <field> 5 >>size render
+    ] with-string-writer
+] unit-test
+
+[ "<input type='password' size='5' name='red' value=''/>" ] [
+    [
+        "red" <password> 5 >>size render
+    ] with-string-writer
+] unit-test
+
+[ ] [
+    [
+        "green" <textarea> render
+    ] with-null-writer
+] unit-test
+
+[ ] [
+    [
+        "green" <textarea> 25 >>rows 30 >>cols render
+    ] with-null-writer
+] unit-test
+
+[ ] [ blank-values ] unit-test
+
+[ ] [ "new york" "city1" set-value ] unit-test
+
+[ ] [ { "new york" "los angeles" "chicago" } "cities" set-value ] unit-test
+
+[ ] [
+    [
+        "city1"
+        <choice>
+            "cities" >>choices
+        render
+    ] with-null-writer
+] unit-test
+
+[ ] [ { "los angeles" "new york" } "city2" set-value ] unit-test
+
+[ ] [
+    [
+        "city2"
+        <choice>
+            "cities" >>choices
+            t >>multiple
+        render
+    ] with-null-writer
+] unit-test
+
+[ ] [
+    [
+        "city2"
+        <choice>
+            "cities" >>choices
+            t >>multiple
+            5 >>size
+        render
+    ] with-null-writer
+] unit-test
+
+[ ] [ blank-values ] unit-test
+
+[ ] [ t "delivery" set-value ] unit-test
+
+[ "<input type='checkbox' name='delivery' selected='true'>Delivery</input>" ] [
+    [
+        "delivery"
+        <checkbox>
+            "Delivery" >>label
+        render
+    ] with-string-writer
+] unit-test
+
+[ ] [ f "delivery" set-value ] unit-test
+
+[ "<input type='checkbox' name='delivery'>Delivery</input>" ] [
+    [
+        "delivery"
+        <checkbox>
+            "Delivery" >>label
+        render
+    ] with-string-writer
+] unit-test
+
+SINGLETON: link-test
+
+M: link-test link-title drop "<Link Title>" ;
+
+M: link-test link-href drop "http://www.apple.com/foo&bar" ;
+
+[ ] [ link-test "link" set-value ] unit-test
+
+[ "<a href='http://www.apple.com/foo&amp;bar'>&lt;Link Title&gt;</a>" ] [
+    [ "link" link render ] with-string-writer
+] unit-test
+
+[ ] [
+    "<html>arbitrary <b>markup</b> for the win!</html>" "html" set-value
+] unit-test
+
+[ "<html>arbitrary <b>markup</b> for the win!</html>" ] [
+    [ "html" html render ] with-string-writer
+] unit-test
+
+[ ] [ "int x = 4;" "code" set-value ] unit-test
+
+[ ] [ "java" "mode" set-value ] unit-test
+
+[ "<span class='KEYWORD3'>int</span> x <span class='OPERATOR'>=</span> <span class='DIGIT'>4</span>;\n" ] [
+    [ "code" <code> "mode" >>mode render ] with-string-writer
+] unit-test
+
+[ ] [ "-foo\n-bar" "farkup" set-value ] unit-test
+
+[ "<ul><li>foo</li><li>bar</li></ul>" ] [
+    [ "farkup" farkup render ] with-string-writer
+] unit-test
+
+[ ] [ { 1 2 3 } "object" set-value ] unit-test
+
+[ t ] [
+    [ "object" inspector render ] with-string-writer
+    [ "object" value [ describe ] with-html-stream ] with-string-writer
+    =
+] unit-test
+
+[ ] [ blank-values ] unit-test
+
+[ ] [
+    "factor" [
+        "concatenative" "model" set-value
+    ] nest-values
+] unit-test
+
+[ H{ { "factor" H{ { "model" "concatenative" } } } } ] [ values get ] unit-test
diff --git a/extra/html/components/components.factor b/extra/html/components/components.factor
new file mode 100644 (file)
index 0000000..efac730
--- /dev/null
@@ -0,0 +1,224 @@
+! Copyright (C) 2008 Slava Pestov
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors kernel namespaces io math.parser assocs classes
+classes.tuple words arrays sequences sequences.lib splitting
+mirrors hashtables combinators continuations math strings
+fry locals calendar calendar.format xml.entities validators
+html.elements html.streams xmode.code2html farkup inspector
+lcs.diff2html ;
+IN: html.components
+
+SYMBOL: values
+
+: value values get at ;
+
+: set-value values get set-at ;
+
+: blank-values H{ } clone values set ;
+
+: prepare-value ( name object -- value name object )
+    [ [ value ] keep ] dip ; inline
+
+: from-assoc ( assoc -- ) values get swap update ;
+
+: from-tuple ( tuple -- ) <mirror> from-assoc ;
+
+: deposit-values ( destination names -- )
+    [ dup value ] H{ } map>assoc update ;
+
+: deposit-slots ( destination names -- )
+    [ <mirror> ] dip deposit-values ;
+
+: with-each-index ( seq quot -- )
+    '[
+        [
+            blank-values 1+ "index" set-value @
+        ] with-scope
+    ] each-index ; inline
+
+: with-each-value ( seq quot -- )
+    '[ "value" set-value @ ] with-each-index ; inline
+
+: with-each-assoc ( seq quot -- )
+    '[ from-assoc @ ] with-each-index ; inline
+
+: with-each-tuple ( seq quot -- )
+    '[ from-tuple @ ] with-each-index ; inline
+
+: with-assoc-values ( assoc quot -- )
+    '[ blank-values , from-assoc @ ] with-scope ; inline
+
+: with-tuple-values ( assoc quot -- )
+    '[ blank-values , from-tuple @ ] with-scope ; inline
+
+: nest-values ( name quot -- )
+    swap [
+        [
+            H{ } clone [ values set call ] keep
+        ] with-scope
+    ] dip set-value ; inline
+
+: nest-tuple ( name quot -- )
+    swap [
+        [
+            H{ } clone [ <mirror> values set call ] keep
+        ] with-scope
+    ] dip set-value ; inline
+
+: object>string ( object -- string )
+    {
+        { [ dup real? ] [ number>string ] }
+        { [ dup timestamp? ] [ timestamp>string ] }
+        { [ dup string? ] [ ] }
+        { [ dup word? ] [ word-name ] }
+        { [ dup not ] [ drop "" ] }
+    } cond ;
+
+GENERIC: render* ( value name render -- )
+
+: render ( name renderer -- )
+    over named-validation-messages get at [
+        [ value>> ] [ message>> ] bi
+        [ -rot render* ] dip
+        render-error
+    ] [
+        prepare-value render*
+    ] if* ;
+
+<PRIVATE
+
+: render-input ( value name type -- )
+    <input =type =name object>string =value input/> ;
+
+PRIVATE>
+
+SINGLETON: label
+
+M: label render* 2drop object>string escape-string write ;
+
+SINGLETON: hidden
+
+M: hidden render* drop "hidden" render-input ;
+
+: render-field ( value name size type -- )
+    <input
+        =type
+        [ object>string =size ] when*
+        =name
+        object>string =value
+    input/> ;
+
+TUPLE: field size ;
+
+: <field> ( -- field )
+    field new ;
+
+M: field render* size>> "text" render-field ;
+
+TUPLE: password size ;
+
+: <password> ( -- password )
+    password new ;
+
+M: password render*
+    #! Don't send passwords back to the user
+    [ drop "" ] 2dip size>> "password" render-field ;
+
+! Text areas
+TUPLE: textarea rows cols ;
+
+: <textarea> ( -- renderer )
+    textarea new ;
+
+M: textarea render*
+    <textarea
+        [ rows>> [ object>string =rows ] when* ]
+        [ cols>> [ object>string =cols ] when* ] bi
+        =name
+    textarea>
+        object>string escape-string write
+    </textarea> ;
+
+! Choice
+TUPLE: choice size multiple choices ;
+
+: <choice> ( -- choice )
+    choice new ;
+
+: render-option ( text selected? -- )
+    <option [ "true" =selected ] when option>
+        object>string escape-string write
+    </option> ;
+
+: render-options ( options selected -- )
+    '[ dup , member? render-option ] each ;
+
+M: choice render*
+    <select
+        swap =name
+        dup size>> [ object>string =size ] when*
+        dup multiple>> [ "true" =multiple ] when
+    select>
+        [ choices>> value ] [ multiple>> ] bi
+        [ swap ] [ swap 1array ] if
+        render-options
+    </select> ;
+
+! Checkboxes
+TUPLE: checkbox label ;
+
+: <checkbox> ( -- checkbox )
+    checkbox new ;
+
+M: checkbox render*
+    <input
+        "checkbox" =type
+        swap =name
+        swap [ "true" =selected ] when
+    input>
+        label>> escape-string write
+    </input> ;
+
+! Link components
+GENERIC: link-title ( obj -- string )
+GENERIC: link-href ( obj -- url )
+
+SINGLETON: link
+
+M: link render*
+    2drop
+    <a dup link-href =href a>
+        link-title object>string escape-string write
+    </a> ;
+
+! XMode code component
+TUPLE: code mode ;
+
+: <code> ( -- code )
+    code new ;
+
+M: code render*
+    [ string-lines ] [ drop ] [ mode>> value ] tri* htmlize-lines ;
+
+! Farkup component
+SINGLETON: farkup
+
+M: farkup render*
+    2drop string-lines "\n" join convert-farkup write ;
+
+! Inspector component
+SINGLETON: inspector
+
+M: inspector render*
+    2drop [ describe ] with-html-stream ;
+
+! Diff component
+SINGLETON: comparison
+
+M: comparison render*
+    2drop htmlize-diff ;
+
+! HTML component
+SINGLETON: html
+
+M: html render* 2drop write ;
index aa6a017540e08707f2a44cd9b68c9584dd56f10d..1178deab3838cb5a8ee7142e0e89c929f2ec0c16 100644 (file)
@@ -1,8 +1,5 @@
 IN: html.elements.tests
-USING: tools.test html html.elements io.streams.string ;
-
-: make-html-string
-    [ with-html-stream ] with-string-writer ;
+USING: tools.test html.elements io.streams.string ;
 
 [ "<a href='h&amp;o'>" ]
-[ [ <a "h&o" =href a> ] make-html-string ] unit-test
+[ [ <a "h&o" =href a> ] with-string-writer ] unit-test
index 49782fa305e4c611e61d72543ed0a901c0b6a670..e5377cedf8f168dfbb65d22817d0f5189a29135e 100644 (file)
@@ -57,6 +57,8 @@ SYMBOL: html
 : print-html ( str -- )
     write-html "\n" write-html ;
 
+<<
+
 : html-word ( name def effect -- )
     #! Define 'word creating' word to allow
     #! dynamically creating words.
@@ -137,30 +139,46 @@ SYMBOL: html
     dup "=" prepend swap
     [ write-attr ] curry attribute-effect html-word ;
 
+! Define some closed HTML tags
+[
+    "h1" "h2" "h3" "h4" "h5" "h6" "h7" "h8" "h9"
+    "ol" "li" "form" "a" "p" "html" "head" "body" "title"
+    "b" "i" "ul" "table" "tbody" "tr" "td" "th" "pre" "textarea"
+    "script" "div" "span" "select" "option" "style" "input"
+] [ define-closed-html-word ] each
+
+! Define some open HTML tags
+[
+    "input"
+    "br"
+    "link"
+    "img"
+] [ define-open-html-word ] each
+
+! Define some attributes
 [
-    ! Define some closed HTML tags
-    [
-        "h1" "h2" "h3" "h4" "h5" "h6" "h7" "h8" "h9"
-        "ol" "li" "form" "a" "p" "html" "head" "body" "title"
-        "b" "i" "ul" "table" "tbody" "tr" "td" "th" "pre" "textarea"
-        "script" "div" "span" "select" "option" "style" "input"
-    ] [ define-closed-html-word ] each
-
-    ! Define some open HTML tags
-    [
-        "input"
-        "br"
-        "link"
-        "img"
-    ] [ define-open-html-word ] each
-
-    ! Define some attributes
-    [
-        "method" "action" "type" "value" "name"
-        "size" "href" "class" "border" "rows" "cols"
-        "id" "onclick" "style" "valign" "accesskey"
-        "src" "language" "colspan" "onchange" "rel"
-        "width" "selected" "onsubmit" "xmlns" "lang" "xml:lang"
-        "media" "title" "multiple"
-    ] [ define-attribute-word ] each
-] with-compilation-unit
+    "method" "action" "type" "value" "name"
+    "size" "href" "class" "border" "rows" "cols"
+    "id" "onclick" "style" "valign" "accesskey"
+    "src" "language" "colspan" "onchange" "rel"
+    "width" "selected" "onsubmit" "xmlns" "lang" "xml:lang"
+    "media" "title" "multiple"
+] [ define-attribute-word ] each
+
+>>
+
+: xhtml-preamble ( -- )
+    "<?xml version=\"1.0\"?>" write-html
+    "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" write-html ;
+
+: simple-page ( title quot -- )
+    #! Call the quotation, with all output going to the
+    #! body of an html page with the given title.
+    xhtml-preamble
+    <html "http://www.w3.org/1999/xhtml" =xmlns "en" =xml:lang "en" =lang html>
+        <head> <title> swap write </title> </head>
+        <body> call </body>
+    </html> ;
+
+: render-error ( message -- )
+    <span "error" =class span> escape-string write </span> ;
diff --git a/extra/html/html-tests.factor b/extra/html/html-tests.factor
deleted file mode 100644 (file)
index 9f1ce6b..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-USING: html http io io.streams.string io.styles kernel
-namespaces tools.test xml.writer sbufs sequences html.private ;
-IN: html.tests
-
-: make-html-string
-    [ with-html-stream ] with-string-writer ; inline
-
-[ [ ] make-html-string ] must-infer
-
-[ ] [
-    512 <sbuf> <html-stream> drop
-] unit-test
-
-[ "" ] [
-    [ "" write ] make-html-string
-] unit-test
-
-[ "a" ] [
-    [ CHAR: a write1 ] make-html-string
-] unit-test
-
-[ "&lt;" ] [
-    [ "<" write ] make-html-string
-] unit-test
-
-[ "<" ] [
-    [ "<" H{ } output-stream get format-html-span ] make-html-string
-] unit-test
-
-TUPLE: funky town ;
-
-M: funky browser-link-href
-    "http://www.funky-town.com/" swap funky-town append ;
-
-[ "<a href='http://www.funky-town.com/austin'>&lt;</a>" ] [
-    [
-        "<" "austin" funky boa write-object
-    ] make-html-string
-] unit-test
-
-[ "<span style='font-family: monospace; '>car</span>" ]
-[
-    [
-        "car"
-        H{ { font "monospace" } }
-        format
-    ] make-html-string
-] unit-test
-
-[ "<span style='color: #ff00ff; '>car</span>" ]
-[
-    [
-        "car"
-        H{ { foreground { 1 0 1 1 } } }
-        format
-    ] make-html-string
-] unit-test
-
-[ "<div style='background-color: #ff00ff; white-space: pre; font-family: monospace; '>cdr</div>" ]
-[
-    [
-        H{ { page-color { 1 0 1 1 } } }
-        [ "cdr" write ] with-nesting
-    ] make-html-string
-] unit-test
-
-[
-    "<div style='white-space: pre; font-family: monospace; '></div>"
-] [
-    [ H{ } [ ] with-nesting nl ] make-html-string
-] unit-test
diff --git a/extra/html/html.factor b/extra/html/html.factor
deleted file mode 100755 (executable)
index 71862b0..0000000
+++ /dev/null
@@ -1,267 +0,0 @@
-! Copyright (C) 2004, 2006 Slava Pestov.
-! See http://factorcode.org/license.txt for BSD license.
-USING: generic assocs help http io io.styles io.files continuations
-io.streams.string kernel math math.order math.parser namespaces
-quotations assocs sequences strings words html.elements
-xml.entities sbufs continuations destructors ;
-IN: html
-
-GENERIC: browser-link-href ( presented -- href )
-
-M: object browser-link-href drop f ;
-
-TUPLE: html-stream last-div? ;
-
-! A hack: stream-nl after with-nesting or tabular-output is
-! ignored, so that HTML stream output looks like UI pane output
-: test-last-div? ( stream -- ? )
-    dup html-stream-last-div?
-    f rot set-html-stream-last-div? ;
-
-: not-a-div ( stream -- stream )
-    dup test-last-div? drop ; inline
-
-: a-div ( stream -- straem )
-    t over set-html-stream-last-div? ; inline
-
-: <html-stream> ( stream -- stream )
-    html-stream construct-delegate ;
-
-<PRIVATE
-
-TUPLE: html-sub-stream style stream ;
-
-: (html-sub-stream) ( style stream -- stream )
-    html-sub-stream boa
-    512 <sbuf> <html-stream> over set-delegate ;
-
-: <html-sub-stream> ( style stream class -- stream )
-    >r (html-sub-stream) r> construct-delegate ; inline
-
-: end-sub-stream ( substream -- string style stream )
-    dup delegate >string
-    over html-sub-stream-style
-    rot html-sub-stream-stream ;
-
-: delegate-write ( string -- )
-    output-stream get delegate stream-write ;
-
-: object-link-tag ( style quot -- )
-    presented pick at [
-        browser-link-href [
-            <a =href a> call </a>
-        ] [ call ] if*
-    ] [ call ] if* ; inline
-
-: hex-color, ( triplet -- )
-    3 head-slice
-    [ 255 * >fixnum >hex 2 CHAR: 0 pad-left % ] each ;
-
-: fg-css, ( color -- )
-    "color: #" % hex-color, "; " % ;
-
-: bg-css, ( color -- )
-    "background-color: #" % hex-color, "; " % ;
-
-: style-css, ( flag -- )
-    dup
-    { italic bold-italic } member?
-    "font-style: " % "italic" "normal" ? % "; " %
-    { bold bold-italic } member?
-    "font-weight: " % "bold" "normal" ? % "; " % ;
-
-: size-css, ( size -- )
-    "font-size: " % # "pt; " % ;
-
-: font-css, ( font -- )
-    "font-family: " % % "; " % ;
-
-: apply-style ( style key quot -- style gadget )
-    >r over at r> when* ; inline
-
-: make-css ( style quot -- str )
-    "" make nip ; inline
-
-: span-css-style ( style -- str )
-    [
-        foreground [ fg-css,    ] apply-style
-        background [ bg-css,    ] apply-style
-        font       [ font-css,  ] apply-style
-        font-style [ style-css, ] apply-style
-        font-size  [ size-css,  ] apply-style
-    ] make-css ;
-
-: span-tag ( style quot -- )
-    over span-css-style dup empty? [
-        drop call
-    ] [
-        <span =style span> call </span>
-    ] if ; inline
-
-: format-html-span ( string style stream -- )
-    [
-        [ [ drop delegate-write ] span-tag ] object-link-tag
-    ] with-output-stream* ;
-
-TUPLE: html-span-stream ;
-
-M: html-span-stream dispose
-    end-sub-stream not-a-div format-html-span ;
-
-: border-css, ( border -- )
-    "border: 1px solid #" % hex-color, "; " % ;
-
-: padding-css, ( padding -- ) "padding: " % # "px; " % ;
-
-: pre-css, ( margin -- )
-    [ "white-space: pre; font-family: monospace; " % ] unless ;
-
-: div-css-style ( style -- str )
-    [
-        page-color   [ bg-css,      ] apply-style
-        border-color [ border-css,  ] apply-style
-        border-width [ padding-css, ] apply-style
-        wrap-margin over at pre-css,
-    ] make-css ;
-
-: div-tag ( style quot -- )
-    swap div-css-style dup empty? [
-        drop call
-    ] [
-        <div =style div> call </div>
-    ] if ; inline
-
-: format-html-div ( string style stream -- )
-    [
-        [ [ delegate-write ] div-tag ] object-link-tag
-    ] with-output-stream* ;
-
-TUPLE: html-block-stream ;
-
-M: html-block-stream dispose ( quot style stream -- )
-    end-sub-stream a-div format-html-div ;
-
-: border-spacing-css,
-    "padding: " % first2 max 2 /i # "px; " % ;
-
-: table-style ( style -- str )
-    [
-        table-border [ border-css,         ] apply-style
-        table-gap    [ border-spacing-css, ] apply-style
-    ] make-css ;
-
-: table-attrs ( style -- )
-    table-style " border-collapse: collapse;" append =style ;
-
-: do-escaping ( string style -- string )
-    html swap at [ escape-string ] unless ;
-
-PRIVATE>
-
-! Stream protocol
-M: html-stream stream-write1 ( char stream -- )
-    >r 1string r> stream-write ;
-
-M: html-stream stream-write ( str stream -- )
-    not-a-div >r escape-string r> delegate stream-write ;
-
-M: html-stream make-span-stream ( style stream -- stream' )
-    html-span-stream <html-sub-stream> ;
-
-M: html-stream stream-format ( str style stream -- )
-    >r html over at [ >r escape-string r> ] unless r>
-    format-html-span ;
-
-M: html-stream make-block-stream ( style stream -- stream' )
-    html-block-stream <html-sub-stream> ;
-
-M: html-stream stream-write-table ( grid style stream -- )
-    a-div [
-        <table dup table-attrs table> swap [
-            <tr> [
-                <td "top" =valign swap table-style =style td>
-                    >string write-html
-                </td>
-            ] with each </tr>
-        ] with each </table>
-    ] with-output-stream* ;
-
-M: html-stream make-cell-stream ( style stream -- stream' )
-    (html-sub-stream) ;
-
-M: html-stream stream-nl ( stream -- )
-    dup test-last-div? [ drop ] [ [ <br/> ] with-output-stream* ] if ;
-
-! Utilities
-: with-html-stream ( quot -- )
-    output-stream get <html-stream> swap with-output-stream* ; inline
-
-: xhtml-preamble
-    "<?xml version=\"1.0\"?>" write-html
-    "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" write-html ;
-
-: html-document ( body-quot head-quot -- )
-    #! head-quot is called to produce output to go
-    #! in the html head portion of the document.
-    #! body-quot is called to produce output to go
-    #! in the html body portion of the document.
-    xhtml-preamble
-    <html "http://www.w3.org/1999/xhtml" =xmlns "en" =xml:lang "en" =lang html>
-        <head> call </head>
-        <body> call </body>
-    </html> ;
-
-: default-css ( -- )
-    <link
-    "stylesheet" =rel "text/css" =type
-    "/responder/resources/extra/html/stylesheet.css" =href
-    link/> ;
-
-: simple-html-document ( title quot -- )
-    swap [
-        <title> write </title>
-        default-css
-    ] html-document ;
-
-: vertical-layout ( list -- )
-    #! Given a list of HTML components, arrange them vertically.
-    <table>
-    [ <tr> <td> call </td> </tr> ] each
-    </table> ;
-
-: horizontal-layout ( list -- )
-    #! Given a list of HTML components, arrange them horizontally.
-    <table>
-     <tr "top" =valign tr> [ <td> call </td> ] each </tr>
-    </table> ;
-
-: button ( label -- )
-    #! Output an HTML submit button with the given label.
-    <input "submit" =type =value input/> ;
-
-: paragraph ( str -- )
-    #! Output the string as an html paragraph
-    <p> write </p> ;
-
-: simple-page ( title quot -- )
-    #! Call the quotation, with all output going to the
-    #! body of an html page with the given title.
-    <html>
-        <head> <title> swap write </title> </head>
-        <body> call </body>
-    </html> ;
-
-: styled-page ( title stylesheet-quot quot -- )
-    #! Call the quotation, with all output going to the
-    #! body of an html page with the given title. stylesheet-quot
-    #! is called to generate the required stylesheet.
-    <html>
-        <head>
-             <title> rot write </title>
-             swap call
-        </head>
-        <body> call </body>
-    </html> ;
-
-: render-error ( message -- )
-    <span "error" =class span> escape-string write </span> ;
diff --git a/extra/html/streams/authors.txt b/extra/html/streams/authors.txt
new file mode 100644 (file)
index 0000000..65da810
--- /dev/null
@@ -0,0 +1,3 @@
+Slava Pestov
+Matthew Willis
+Chris Double
diff --git a/extra/html/streams/streams-tests.factor b/extra/html/streams/streams-tests.factor
new file mode 100644 (file)
index 0000000..14f1621
--- /dev/null
@@ -0,0 +1,74 @@
+USING: html.streams html.streams.private
+io io.streams.string io.styles kernel
+namespaces tools.test xml.writer sbufs sequences inspector ;
+IN: html.streams.tests
+
+: make-html-string
+    [ with-html-stream ] with-string-writer ; inline
+
+[ [ ] make-html-string ] must-infer
+
+[ ] [
+    512 <sbuf> <html-stream> drop
+] unit-test
+
+[ "" ] [
+    [ "" write ] make-html-string
+] unit-test
+
+[ "a" ] [
+    [ CHAR: a write1 ] make-html-string
+] unit-test
+
+[ "&lt;" ] [
+    [ "<" write ] make-html-string
+] unit-test
+
+[ "<" ] [
+    [ "<" H{ } output-stream get format-html-span ] make-html-string
+] unit-test
+
+TUPLE: funky town ;
+
+M: funky browser-link-href
+    "http://www.funky-town.com/" swap funky-town append ;
+
+[ "<a href='http://www.funky-town.com/austin'>&lt;</a>" ] [
+    [
+        "<" "austin" funky boa write-object
+    ] make-html-string
+] unit-test
+
+[ "<span style='font-family: monospace; '>car</span>" ]
+[
+    [
+        "car"
+        H{ { font "monospace" } }
+        format
+    ] make-html-string
+] unit-test
+
+[ "<span style='color: #ff00ff; '>car</span>" ]
+[
+    [
+        "car"
+        H{ { foreground { 1 0 1 1 } } }
+        format
+    ] make-html-string
+] unit-test
+
+[ "<div style='background-color: #ff00ff; white-space: pre; font-family: monospace; '>cdr</div>" ]
+[
+    [
+        H{ { page-color { 1 0 1 1 } } }
+        [ "cdr" write ] with-nesting
+    ] make-html-string
+] unit-test
+
+[
+    "<div style='white-space: pre; font-family: monospace; '></div>"
+] [
+    [ H{ } [ ] with-nesting nl ] make-html-string
+] unit-test
+
+[ ] [ [ { 1 2 3 } describe ] with-html-stream ] unit-test
diff --git a/extra/html/streams/streams.factor b/extra/html/streams/streams.factor
new file mode 100755 (executable)
index 0000000..e3f45e4
--- /dev/null
@@ -0,0 +1,195 @@
+! Copyright (C) 2004, 2008 Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+USING: generic assocs help http io io.styles io.files continuations
+io.streams.string kernel math math.order math.parser namespaces
+quotations assocs sequences strings words html.elements
+xml.entities sbufs continuations destructors accessors ;
+IN: html.streams
+
+GENERIC: browser-link-href ( presented -- href )
+
+M: object browser-link-href drop f ;
+
+TUPLE: html-stream stream last-div ;
+
+! stream-nl after with-nesting or tabular-output is
+! ignored, so that HTML stream output looks like
+! UI pane output
+: last-div? ( stream -- ? )
+    [ f ] change-last-div drop ;
+
+: not-a-div ( stream -- stream )
+    f >>last-div ; inline
+
+: a-div ( stream -- straem )
+    t >>last-div ; inline
+
+: <html-stream> ( stream -- stream )
+    f html-stream boa ;
+
+<PRIVATE
+
+TUPLE: html-sub-stream < html-stream style parent ;
+
+: new-html-sub-stream ( style stream class -- stream )
+    new
+        512 <sbuf> >>stream
+        swap >>parent
+        swap >>style ; inline
+
+: end-sub-stream ( substream -- string style stream )
+    [ stream>> >string ] [ style>> ] [ parent>> ] tri ;
+
+: object-link-tag ( style quot -- )
+    presented pick at [
+        browser-link-href [
+            <a =href a> call </a>
+        ] [ call ] if*
+    ] [ call ] if* ; inline
+
+: hex-color, ( triplet -- )
+    3 head-slice
+    [ 255 * >fixnum >hex 2 CHAR: 0 pad-left % ] each ;
+
+: fg-css, ( color -- )
+    "color: #" % hex-color, "; " % ;
+
+: bg-css, ( color -- )
+    "background-color: #" % hex-color, "; " % ;
+
+: style-css, ( flag -- )
+    dup
+    { italic bold-italic } member?
+    "font-style: " % "italic" "normal" ? % "; " %
+    { bold bold-italic } member?
+    "font-weight: " % "bold" "normal" ? % "; " % ;
+
+: size-css, ( size -- )
+    "font-size: " % # "pt; " % ;
+
+: font-css, ( font -- )
+    "font-family: " % % "; " % ;
+
+: apply-style ( style key quot -- style gadget )
+    >r over at r> when* ; inline
+
+: make-css ( style quot -- str )
+    "" make nip ; inline
+
+: span-css-style ( style -- str )
+    [
+        foreground [ fg-css,    ] apply-style
+        background [ bg-css,    ] apply-style
+        font       [ font-css,  ] apply-style
+        font-style [ style-css, ] apply-style
+        font-size  [ size-css,  ] apply-style
+    ] make-css ;
+
+: span-tag ( style quot -- )
+    over span-css-style dup empty? [
+        drop call
+    ] [
+        <span =style span> call </span>
+    ] if ; inline
+
+: format-html-span ( string style stream -- )
+    stream>> [
+        [ [ drop write ] span-tag ] object-link-tag
+    ] with-output-stream* ;
+
+TUPLE: html-span-stream < html-sub-stream ;
+
+M: html-span-stream dispose
+    end-sub-stream not-a-div format-html-span ;
+
+: border-css, ( border -- )
+    "border: 1px solid #" % hex-color, "; " % ;
+
+: padding-css, ( padding -- ) "padding: " % # "px; " % ;
+
+: pre-css, ( margin -- )
+    [ "white-space: pre; font-family: monospace; " % ] unless ;
+
+: div-css-style ( style -- str )
+    [
+        page-color   [ bg-css,      ] apply-style
+        border-color [ border-css,  ] apply-style
+        border-width [ padding-css, ] apply-style
+        wrap-margin over at pre-css,
+    ] make-css ;
+
+: div-tag ( style quot -- )
+    swap div-css-style dup empty? [
+        drop call
+    ] [
+        <div =style div> call </div>
+    ] if ; inline
+
+: format-html-div ( string style stream -- )
+    stream>> [
+        [ [ write ] div-tag ] object-link-tag
+    ] with-output-stream* ;
+
+TUPLE: html-block-stream < html-sub-stream ;
+
+M: html-block-stream dispose ( quot style stream -- )
+    end-sub-stream a-div format-html-div ;
+
+: border-spacing-css,
+    "padding: " % first2 max 2 /i # "px; " % ;
+
+: table-style ( style -- str )
+    [
+        table-border [ border-css,         ] apply-style
+        table-gap    [ border-spacing-css, ] apply-style
+    ] make-css ;
+
+: table-attrs ( style -- )
+    table-style " border-collapse: collapse;" append =style ;
+
+: do-escaping ( string style -- string )
+    html swap at [ escape-string ] unless ;
+
+PRIVATE>
+
+! Stream protocol
+M: html-stream stream-flush
+    stream>> stream-flush ;
+
+M: html-stream stream-write1
+    >r 1string r> stream-write ;
+
+M: html-stream stream-write
+    not-a-div >r escape-string r> stream>> stream-write ;
+
+M: html-stream stream-format
+    >r html over at [ >r escape-string r> ] unless r>
+    format-html-span ;
+
+M: html-stream stream-nl
+    dup last-div? [ drop ] [ [ <br/> ] with-output-stream* ] if ;
+
+M: html-stream make-span-stream
+    html-span-stream new-html-sub-stream ;
+
+M: html-stream make-block-stream
+    html-block-stream new-html-sub-stream ;
+
+M: html-stream make-cell-stream
+    html-sub-stream new-html-sub-stream ;
+
+M: html-stream stream-write-table
+    a-div stream>> [
+        <table dup table-attrs table> swap [
+            <tr> [
+                <td "top" =valign swap table-style =style td>
+                    stream>> >string write
+                </td>
+            ] with each </tr>
+        ] with each </table>
+    ] with-output-stream* ;
+
+M: html-stream dispose stream>> dispose ;
+
+: with-html-stream ( quot -- )
+    output-stream get <html-stream> swap with-output-stream* ; inline
diff --git a/extra/html/streams/summary.txt b/extra/html/streams/summary.txt
new file mode 100644 (file)
index 0000000..29ec8d3
--- /dev/null
@@ -0,0 +1 @@
+HTML reader, writer and utilities
diff --git a/extra/html/streams/tags.txt b/extra/html/streams/tags.txt
new file mode 100644 (file)
index 0000000..c077218
--- /dev/null
@@ -0,0 +1 @@
+web
diff --git a/extra/html/stylesheet.css b/extra/html/stylesheet.css
deleted file mode 100644 (file)
index a1afce7..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-a:link { text-decoration: none; color: black; }
-a:visited { text-decoration: none; color: black; }
-a:active { text-decoration: none; color: black; }
-a:hover { text-decoration: underline; color: black; }
diff --git a/extra/html/summary.txt b/extra/html/summary.txt
deleted file mode 100644 (file)
index 29ec8d3..0000000
+++ /dev/null
@@ -1 +0,0 @@
-HTML reader, writer and utilities
diff --git a/extra/html/tags.txt b/extra/html/tags.txt
deleted file mode 100644 (file)
index c077218..0000000
+++ /dev/null
@@ -1 +0,0 @@
-web
diff --git a/extra/html/templates/chloe/chloe-tests.factor b/extra/html/templates/chloe/chloe-tests.factor
new file mode 100644 (file)
index 0000000..eaa0f0d
--- /dev/null
@@ -0,0 +1,161 @@
+USING: html.templates html.templates.chloe
+tools.test io.streams.string kernel sequences ascii boxes
+namespaces xml html.components
+splitting unicode.categories ;
+IN: html.templates.chloe.tests
+
+[ f ] [ f parse-query-attr ] unit-test
+
+[ f ] [ "" parse-query-attr ] unit-test
+
+[ H{ { "a" "b" } } ] [
+    blank-values
+    "b" "a" set-value
+    "a" parse-query-attr
+] unit-test
+
+[ H{ { "a" "b" } { "c" "d" } } ] [
+    blank-values
+    "b" "a" set-value
+    "d" "c" set-value
+    "a,c" parse-query-attr
+] unit-test
+
+: run-template
+    with-string-writer [ "\r\n\t" member? not ] filter
+    "?>" split1 nip ; inline
+
+: test-template ( name -- template )
+    "resource:extra/html/templates/chloe/test/"
+    swap
+    ".xml" 3append <chloe> ;
+
+[ "Hello world" ] [
+    [
+        "test1" test-template call-template
+    ] run-template
+] unit-test
+
+[ "Blah blah" "Hello world" ] [
+    [
+        <box> title set
+        [
+            "test2" test-template call-template
+        ] run-template
+        title get box>
+    ] with-scope
+] unit-test
+
+[ "<html><head><title>Hello world</title></head><body>Blah blah</body></html>" ] [
+    [
+        [
+            "test2" test-template call-template
+        ] "test3" test-template with-boilerplate
+    ] run-template
+] unit-test
+
+: test4-aux? t ;
+
+[ "True" ] [
+    [
+        "test4" test-template call-template
+    ] run-template
+] unit-test
+
+: test5-aux? f ;
+
+[ "" ] [
+    [
+        "test5" test-template call-template
+    ] run-template
+] unit-test
+
+SYMBOL: test6-aux?
+
+[ "True" ] [
+    [
+        test6-aux? on
+        "test6" test-template call-template
+    ] run-template
+] unit-test
+
+SYMBOL: test7-aux?
+
+[ "" ] [
+    [
+        test7-aux? off
+        "test7" test-template call-template
+    ] run-template
+] unit-test
+
+[ ] [ blank-values ] unit-test
+
+[ ] [ "A label" "label" set-value ] unit-test
+
+SINGLETON: link-test
+
+M: link-test link-title drop "<Link Title>" ;
+
+M: link-test link-href drop "http://www.apple.com/foo&bar" ;
+
+[ ] [ link-test "link" set-value ] unit-test
+
+[ ] [ "int x = 5;" "code" set-value ] unit-test
+
+[ ] [ "c" "mode" set-value ] unit-test
+
+[ ] [ { 1 2 3 } "inspector" set-value ] unit-test
+
+[ ] [ "<p>a paragraph</p>" "html" set-value ] unit-test
+
+[ ] [ "sheeple" "field" set-value ] unit-test
+
+[ ] [ "a password" "password" set-value ] unit-test
+
+[ ] [ "a\nb\nc" "textarea" set-value ] unit-test
+
+[ ] [ "new york" "choice" set-value ] unit-test
+
+[ ] [ { "new york" "detroit" "minneapolis" } "choices" set-value ] unit-test
+
+[ ] [
+    [
+        "test8" test-template call-template
+    ] run-template drop
+] unit-test
+
+[ ] [ { 1 2 3 } "numbers" set-value ] unit-test
+
+[ "<ul><li>1</li><li>2</li><li>3</li></ul>" ] [
+    [
+        "test9" test-template call-template
+    ] run-template [ blank? not ] filter
+] unit-test
+
+TUPLE: person first-name last-name ;
+
+[ ] [
+    {
+        T{ person f "RBaxter" "Unknown" }
+        T{ person f "Doug" "Coleman" }
+    } "people" set-value
+] unit-test
+
+[ "<table><tr><td>RBaxter</td><td>Unknown</td></tr><tr><td>Doug</td><td>Coleman</td></tr></table>" ] [
+    [
+        "test10" test-template call-template
+    ] run-template [ blank? not ] filter
+] unit-test
+
+[ ] [
+    {
+        H{ { "first-name" "RBaxter" } { "last-name" "Unknown" } }
+        H{ { "first-name" "Doug"    } { "last-name" "Coleman" } }
+    } "people" set-value
+] unit-test
+
+[ "<table><tr><td>RBaxter</td><td>Unknown</td></tr><tr><td>Doug</td><td>Coleman</td></tr></table>" ] [
+    [
+        "test11" test-template call-template
+    ] run-template [ blank? not ] filter
+] unit-test
diff --git a/extra/html/templates/chloe/chloe.factor b/extra/html/templates/chloe/chloe.factor
new file mode 100644 (file)
index 0000000..092f79b
--- /dev/null
@@ -0,0 +1,339 @@
+! Copyright (C) 2008 Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors kernel sequences combinators kernel namespaces
+classes.tuple assocs splitting words arrays memoize
+io io.files io.encodings.utf8 io.streams.string
+unicode.case tuple-syntax mirrors fry math
+multiline xml xml.data xml.writer xml.utilities
+html.elements
+html.components
+html.templates
+http.server
+http.server.auth
+http.server.flows
+http.server.actions
+http.server.sessions ;
+IN: html.templates.chloe
+
+! Chloe is Ed's favorite web designer
+
+TUPLE: chloe path ;
+
+C: <chloe> chloe
+
+DEFER: process-template
+
+: chloe-ns "http://factorcode.org/chloe/1.0" ; inline
+
+: chloe-attrs-only ( assoc -- assoc' )
+    [ drop name-url chloe-ns = ] assoc-filter ;
+
+: non-chloe-attrs-only ( assoc -- assoc' )
+    [ drop name-url chloe-ns = not ] assoc-filter ;
+
+: chloe-tag? ( tag -- ? )
+    {
+        { [ dup tag? not ] [ f ] }
+        { [ dup url>> chloe-ns = not ] [ f ] }
+        [ t ]
+    } cond nip ;
+
+SYMBOL: tags
+
+MEMO: chloe-name ( string -- name )
+    name new
+        swap >>tag
+        chloe-ns >>url ;
+
+: required-attr ( tag name -- value )
+    dup chloe-name rot at*
+    [ nip ] [ drop " attribute is required" append throw ] if ;
+
+: optional-attr ( tag name -- value )
+    chloe-name swap at ;
+
+: process-tag-children ( tag -- )
+    [ process-template ] each ;
+
+: children>string ( tag -- string )
+    [ process-tag-children ] with-string-writer ;
+
+: title-tag ( tag -- )
+    children>string set-title ;
+
+: write-title-tag ( tag -- )
+    drop
+    "head" tags get member? "title" tags get member? not and
+    [ <title> write-title </title> ] [ write-title ] if ;
+
+: style-tag ( tag -- )
+    dup "include" optional-attr dup [
+        swap children>string empty? [
+            "style tag cannot have both an include attribute and a body" throw
+        ] unless
+        utf8 file-contents
+    ] [
+        drop children>string
+    ] if add-style ;
+
+: write-style-tag ( tag -- )
+    drop <style> write-style </style> ;
+
+: atom-tag ( tag -- )
+    [ "title" required-attr ]
+    [ "href" required-attr ]
+    bi set-atom-feed ;
+
+: write-atom-tag ( tag -- )
+    drop
+    "head" tags get member? [
+        write-atom-feed
+    ] [
+        atom-feed get value>> second write
+    ] if ;
+
+: parse-query-attr ( string -- assoc )
+    dup empty?
+    [ drop f ] [ "," split [ dup value ] H{ } map>assoc ] if ;
+
+: flow-attr ( tag -- )
+    "flow" optional-attr {
+        { "none" [ flow-id off ] }
+        { "begin" [ begin-flow ] }
+        { "current" [ ] }
+        { f [ ] }
+    } case ;
+
+: session-attr ( tag -- )
+    "session" optional-attr {
+        { "none" [ session off flow-id off ] }
+        { "current" [ ] }
+        { f [ ] }
+    } case ;
+
+: a-start-tag ( tag -- )
+    [
+        <a
+        dup flow-attr
+        dup session-attr
+        dup "value" optional-attr [ value f ] [
+            [ "href" required-attr ]
+            [ "query" optional-attr parse-query-attr ]
+            bi
+        ] ?if link>string =href
+        a>
+    ] with-scope ;
+
+: a-tag ( tag -- )
+    [ a-start-tag ]
+    [ process-tag-children ]
+    [ drop </a> ]
+    tri ;
+
+: form-start-tag ( tag -- )
+    [
+        [
+            <form
+            "POST" =method
+            {
+                [ flow-attr ]
+                [ session-attr ]
+                [ "action" required-attr resolve-base-path =action ]
+                [ tag-attrs non-chloe-attrs-only print-attrs ]
+            } cleave
+            form>
+        ] [
+            hidden-form-field
+            "for" optional-attr [ hidden render ] when*
+        ] bi
+    ] with-scope ;
+
+: form-tag ( tag -- )
+    [ form-start-tag ]
+    [ process-tag-children ]
+    [ drop </form> ]
+    tri ;
+
+DEFER: process-chloe-tag
+
+STRING: button-tag-markup
+<t:form class="inline" xmlns:t="http://factorcode.org/chloe/1.0">
+    <button type="submit"></button>
+</t:form>
+;
+
+: add-tag-attrs ( attrs tag -- )
+    tag-attrs swap update ;
+
+: button-tag ( tag -- )
+    button-tag-markup string>xml delegate
+    {
+        [ [ tag-attrs chloe-attrs-only ] dip add-tag-attrs ]
+        [ [ tag-attrs non-chloe-attrs-only ] dip "button" tag-named add-tag-attrs ]
+        [ [ children>string 1array ] dip "button" tag-named set-tag-children ]
+        [ nip ]
+    } 2cleave process-chloe-tag ;
+
+: attr>word ( value -- word/f )
+    dup ":" split1 swap lookup
+    [ ] [ "No such word: " swap append throw ] ?if ;
+
+: attr>var ( value -- word/f )
+    attr>word dup symbol? [
+        "Must be a symbol: " swap append throw
+    ] unless ;
+
+: if-satisfied? ( tag -- ? )
+    t swap
+    {
+        [ "code"  optional-attr [ attr>word execute and ] when* ]
+        [  "var"  optional-attr [ attr>var      get and ] when* ]
+        [ "svar"  optional-attr [ attr>var     sget and ] when* ]
+        [ "uvar"  optional-attr [ attr>var     uget and ] when* ]
+        [ "value" optional-attr [ value             and ] when* ]
+    } cleave ;
+
+: if-tag ( tag -- )
+    dup if-satisfied? [ process-tag-children ] [ drop ] if ;
+
+: even-tag ( tag -- )
+    "index" value even? [ process-tag-children ] [ drop ] if ;
+
+: odd-tag ( tag -- )
+    "index" value odd? [ process-tag-children ] [ drop ] if ;
+
+: (each-tag) ( tag quot -- )
+    [
+        [ "values" required-attr value ] keep
+        '[ , process-tag-children ]
+    ] dip call ; inline
+
+: each-tag ( tag -- )
+    [ with-each-value ] (each-tag) ;
+
+: each-tuple-tag ( tag -- )
+    [ with-each-tuple ] (each-tag) ;
+
+: each-assoc-tag ( tag -- )
+    [ with-each-assoc ] (each-tag) ;
+
+: (bind-tag) ( tag quot -- )
+    [
+        [ "name" required-attr value ] keep
+        '[ , process-tag-children ]
+    ] dip call ; inline
+
+: bind-tuple-tag ( tag -- )
+    [ with-tuple-values ] (bind-tag) ;
+
+: bind-assoc-tag ( tag -- )
+    [ with-assoc-values ] (bind-tag) ;
+
+: error-message-tag ( tag -- )
+    children>string render-error ;
+
+: validation-messages-tag ( tag -- )
+    drop render-validation-messages ;
+
+: singleton-component-tag ( tag class -- )
+    [ "name" required-attr ] dip render ;
+
+: attrs>slots ( tag tuple -- )
+    [ attrs>> ] [ <mirror> ] bi*
+    '[
+        swap tag>> dup "name" =
+        [ 2drop ] [ , set-at ] if
+    ] assoc-each ;
+
+: tuple-component-tag ( tag class -- )
+    [ drop "name" required-attr ]
+    [ new [ attrs>slots ] keep ]
+    2bi render ;
+
+: process-chloe-tag ( tag -- )
+    dup name-tag {
+        { "chloe" [ process-tag-children ] }
+
+        ! HTML head
+        { "title" [ title-tag ] }
+        { "write-title" [ write-title-tag ] }
+        { "style" [ style-tag ] }
+        { "write-style" [ write-style-tag ] }
+        { "atom" [ atom-tag ] }
+        { "write-atom" [ write-atom-tag ] }
+
+        ! HTML elements
+        { "a" [ a-tag ] }
+        { "button" [ button-tag ] }
+
+        ! Components
+        { "label" [ label singleton-component-tag ] }
+        { "link" [ link singleton-component-tag ] }
+        { "code" [ code tuple-component-tag ] }
+        { "farkup" [ farkup singleton-component-tag ] }
+        { "inspector" [ inspector singleton-component-tag ] }
+        { "comparison" [ comparison singleton-component-tag ] }
+        { "html" [ html singleton-component-tag ] }
+
+        ! Forms
+        { "form" [ form-tag ] }
+        { "error-message" [ error-message-tag ] }
+        { "validation-messages" [ validation-messages-tag ] }
+        { "hidden" [ hidden singleton-component-tag ] }
+        { "field" [ field tuple-component-tag ] }
+        { "password" [ password tuple-component-tag ] }
+        { "textarea" [ textarea tuple-component-tag ] }
+        { "choice" [ choice tuple-component-tag ] }
+        { "checkbox" [ checkbox tuple-component-tag ] }
+
+        ! Control flow
+        { "if" [ if-tag ] }
+        { "even" [ even-tag ] }
+        { "odd" [ odd-tag ] }
+        { "each" [ each-tag ] }
+        { "each-assoc" [ each-assoc-tag ] }
+        { "each-tuple" [ each-tuple-tag ] }
+        { "bind-assoc" [ bind-assoc-tag ] }
+        { "bind-tuple" [ bind-tuple-tag ] }
+        { "comment" [ drop ] }
+        { "call-next-template" [ drop call-next-template ] }
+
+        [ "Unknown chloe tag: " prepend throw ]
+    } case ;
+
+: process-tag ( tag -- )
+    {
+        [ name-tag >lower tags get push ]
+        [ write-start-tag ]
+        [ process-tag-children ]
+        [ write-end-tag ]
+        [ drop tags get pop* ]
+    } cleave ;
+
+: process-template ( xml -- )
+    {
+        { [ dup [ chloe-tag? ] is? ] [ process-chloe-tag ] }
+        { [ dup [ tag? ] is? ] [ process-tag ] }
+        { [ t ] [ write-item ] }
+    } cond ;
+
+: process-chloe ( xml -- )
+    [
+        V{ } clone tags set
+
+        nested-template? get [
+            process-template
+        ] [
+            {
+                [ xml-prolog write-prolog ]
+                [ xml-before write-chunk  ]
+                [ process-template        ]
+                [ xml-after write-chunk   ]
+            } cleave
+        ] if
+    ] with-scope ;
+
+M: chloe call-template*
+    path>> utf8 <file-reader> read-xml process-chloe ;
+
+INSTANCE: chloe template
diff --git a/extra/html/templates/chloe/test/test1.xml b/extra/html/templates/chloe/test/test1.xml
new file mode 100644 (file)
index 0000000..daccd57
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+       Hello world
+</t:chloe>
diff --git a/extra/html/templates/chloe/test/test10.xml b/extra/html/templates/chloe/test/test10.xml
new file mode 100644 (file)
index 0000000..afded93
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <table>
+               <t:each-tuple t:values="people">
+                       <tr>
+                               <td><t:label t:name="first-name"/></td>
+                               <td><t:label t:name="last-name"/></td>
+                       </tr>
+               </t:each-tuple>
+       </table>
+
+</t:chloe>
diff --git a/extra/html/templates/chloe/test/test11.xml b/extra/html/templates/chloe/test/test11.xml
new file mode 100644 (file)
index 0000000..17e31b1
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <table>
+               <t:each-assoc t:values="people">
+                       <tr>
+                               <td><t:label t:name="first-name"/></td>
+                               <td><t:label t:name="last-name"/></td>
+                       </tr>
+               </t:each-assoc>
+       </table>
+
+</t:chloe>
diff --git a/extra/html/templates/chloe/test/test2.xml b/extra/html/templates/chloe/test/test2.xml
new file mode 100644 (file)
index 0000000..05b9dde
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+       <t:title>Hello world</t:title>
+       Blah blah
+</t:chloe>
diff --git a/extra/html/templates/chloe/test/test3-aux.xml b/extra/html/templates/chloe/test/test3-aux.xml
new file mode 100644 (file)
index 0000000..99f61af
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+       <t:title>Hello world</t:title>
+</t:chloe>
diff --git a/extra/html/templates/chloe/test/test3.xml b/extra/html/templates/chloe/test/test3.xml
new file mode 100644 (file)
index 0000000..845dd35
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+       <html>
+               <head>
+                       <t:write-title />
+               </head>
+               <body>
+                       <t:call-next-template />
+               </body>
+       </html>
+</t:chloe>
diff --git a/extra/html/templates/chloe/test/test4.xml b/extra/html/templates/chloe/test/test4.xml
new file mode 100644 (file)
index 0000000..5561236
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:if t:code="html.templates.chloe.tests:test4-aux?">
+               True
+       </t:if>
+
+</t:chloe>
diff --git a/extra/html/templates/chloe/test/test5.xml b/extra/html/templates/chloe/test/test5.xml
new file mode 100644 (file)
index 0000000..edcbe8f
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:if t:code="html.templates.chloe.tests:test5-aux?">
+               True
+       </t:if>
+
+</t:chloe>
diff --git a/extra/html/templates/chloe/test/test6.xml b/extra/html/templates/chloe/test/test6.xml
new file mode 100644 (file)
index 0000000..b3f6493
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:if t:var="html.templates.chloe.tests:test6-aux?">
+               True
+       </t:if>
+
+</t:chloe>
diff --git a/extra/html/templates/chloe/test/test7.xml b/extra/html/templates/chloe/test/test7.xml
new file mode 100644 (file)
index 0000000..338595e
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:if t:var="html.templates.chloe.tests:test7-aux?">
+               True
+       </t:if>
+
+</t:chloe>
diff --git a/extra/html/templates/chloe/test/test8.xml b/extra/html/templates/chloe/test/test8.xml
new file mode 100644 (file)
index 0000000..8e2ff2e
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:label t:name="label" />
+
+       <t:link t:name="link" />
+
+       <t:code t:name="code" mode="mode" />
+
+       <t:farkup t:name="farkup" />
+
+       <t:inspector t:name="inspector" />
+
+       <t:html t:name="html" />
+
+       <t:field t:name="field" t:size="13" />
+
+       <t:password t:name="password" t:size="10" />
+
+       <t:textarea t:name="textarea" t:rows="5" t:cols="10" />
+
+       <t:choice t:name="choice" t:choices="choices" />
+
+       <t:checkbox t:name="checkbox">Checkbox</t:checkbox>
+
+</t:chloe>
diff --git a/extra/html/templates/chloe/test/test9.xml b/extra/html/templates/chloe/test/test9.xml
new file mode 100644 (file)
index 0000000..bcfc468
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <ul>
+               <t:each t:values="numbers">
+                       <li><t:label t:name="value"/></li>
+               </t:each>
+       </ul>
+
+</t:chloe>
diff --git a/extra/html/templates/fhtml/authors.txt b/extra/html/templates/fhtml/authors.txt
new file mode 100644 (file)
index 0000000..b47eafb
--- /dev/null
@@ -0,0 +1,2 @@
+Slava Pestov
+Matthew Willis
diff --git a/extra/html/templates/fhtml/fhtml-tests.factor b/extra/html/templates/fhtml/fhtml-tests.factor
new file mode 100755 (executable)
index 0000000..43ea28f
--- /dev/null
@@ -0,0 +1,20 @@
+USING: io io.files io.streams.string io.encodings.utf8
+html.templates html.templates.fhtml kernel
+tools.test sequences parser ;
+IN: html.templates.fhtml.tests
+
+: test-template ( path -- ? )
+    "resource:extra/html/templates/fhtml/test/"
+    prepend
+    [
+        ".fhtml" append <fhtml> [ call-template ] with-string-writer
+    ] keep
+    ".html" append utf8 file-contents = ;
+
+[ t ] [ "example" test-template ] unit-test
+[ t ] [ "bug" test-template ] unit-test
+[ t ] [ "stack" test-template ] unit-test
+
+[
+    [ ] [ "<%\n%>" parse-template drop ] unit-test
+] with-file-vocabs
diff --git a/extra/html/templates/fhtml/fhtml.factor b/extra/html/templates/fhtml/fhtml.factor
new file mode 100755 (executable)
index 0000000..74e5c37
--- /dev/null
@@ -0,0 +1,79 @@
+! Copyright (C) 2005 Alex Chapman
+! Copyright (C) 2006, 2008 Slava Pestov
+! See http://factorcode.org/license.txt for BSD license.
+USING: continuations sequences kernel namespaces debugger
+combinators math quotations generic strings splitting
+accessors assocs fry
+parser io io.files io.streams.string io.encodings.utf8
+html.elements
+html.templates ;
+IN: html.templates.fhtml
+
+! We use a custom lexer so that %> ends a token even if not
+! followed by whitespace
+TUPLE: template-lexer < lexer ;
+
+: <template-lexer> ( lines -- lexer )
+    template-lexer new-lexer ;
+
+M: template-lexer skip-word
+    [
+        {
+            { [ 2dup nth CHAR: " = ] [ drop 1+ ] }
+            { [ 2dup swap tail-slice "%>" head? ] [ drop 2 + ] }
+            [ f skip ]
+        } cond
+    ] change-lexer-column ;
+
+DEFER: <% delimiter
+
+: check-<% ( lexer -- col )
+    "<%" over line-text>> rot column>> start* ;
+
+: found-<% ( accum lexer col -- accum )
+    [
+        over line-text>>
+        [ column>> ] 2dip subseq parsed
+        \ write-html parsed
+    ] 2keep 2 + >>column drop ;
+
+: still-looking ( accum lexer -- accum )
+    [
+        [ line-text>> ] [ column>> ] bi tail
+        parsed \ print-html parsed
+    ] keep next-line ;
+
+: parse-%> ( accum lexer -- accum )
+    dup still-parsing? [
+        dup check-<%
+        [ found-<% ] [ [ still-looking ] keep parse-%> ] if*
+    ] [
+        drop
+    ] if ;
+
+: %> lexer get parse-%> ; parsing
+
+: parse-template-lines ( lines -- quot )
+    <template-lexer> [
+        V{ } clone lexer get parse-%> f (parse-until)
+    ] with-parser ;
+
+: parse-template ( string -- quot )
+    [
+        "quiet" on
+        parser-notes off
+        "html.templates.fhtml" use+
+        string-lines parse-template-lines
+    ] with-file-vocabs ;
+
+: eval-template ( string -- )
+    parse-template call ;
+
+TUPLE: fhtml path ;
+
+C: <fhtml> fhtml
+
+M: fhtml call-template* ( filename -- )
+    '[ , path>> utf8 file-contents eval-template ] assert-depth ;
+
+INSTANCE: fhtml template
diff --git a/extra/html/templates/fhtml/test/bug.fhtml b/extra/html/templates/fhtml/test/bug.fhtml
new file mode 100644 (file)
index 0000000..cb66599
--- /dev/null
@@ -0,0 +1,5 @@
+<%
+    USING: prettyprint ;
+    ! Hello world
+    5 pprint
+%>
diff --git a/extra/html/templates/fhtml/test/bug.html b/extra/html/templates/fhtml/test/bug.html
new file mode 100644 (file)
index 0000000..51d7b8d
--- /dev/null
@@ -0,0 +1,2 @@
+5
+
diff --git a/extra/html/templates/fhtml/test/example.fhtml b/extra/html/templates/fhtml/test/example.fhtml
new file mode 100644 (file)
index 0000000..211f44a
--- /dev/null
@@ -0,0 +1,8 @@
+<% USING: math ; %>
+
+<html>
+    <head><title>Simple Embedded Factor Example</title></head>
+    <body>
+        <% 5 [ %><p>I like repetition</p><% ] times %>
+    </body>
+</html>
diff --git a/extra/html/templates/fhtml/test/example.html b/extra/html/templates/fhtml/test/example.html
new file mode 100644 (file)
index 0000000..9bf4a08
--- /dev/null
@@ -0,0 +1,9 @@
+
+
+<html>
+    <head><title>Simple Embedded Factor Example</title></head>
+    <body>
+        <p>I like repetition</p><p>I like repetition</p><p>I like repetition</p><p>I like repetition</p><p>I like repetition</p>
+    </body>
+</html>
+
diff --git a/extra/html/templates/fhtml/test/stack.fhtml b/extra/html/templates/fhtml/test/stack.fhtml
new file mode 100644 (file)
index 0000000..399711a
--- /dev/null
@@ -0,0 +1 @@
+The stack: <% USING: prettyprint ;  .s %>
diff --git a/extra/html/templates/fhtml/test/stack.html b/extra/html/templates/fhtml/test/stack.html
new file mode 100644 (file)
index 0000000..ee923a6
--- /dev/null
@@ -0,0 +1,2 @@
+The stack: 
+
diff --git a/extra/html/templates/templates.factor b/extra/html/templates/templates.factor
new file mode 100644 (file)
index 0000000..580af58
--- /dev/null
@@ -0,0 +1,85 @@
+! Copyright (C) 2008 Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors kernel fry io io.encodings.utf8 io.files
+debugger prettyprint continuations namespaces boxes sequences
+arrays strings html.elements io.streams.string quotations ;
+IN: html.templates
+
+MIXIN: template
+
+GENERIC: call-template* ( template -- )
+
+M: string call-template* write ;
+
+M: callable call-template* call ;
+
+M: object call-template* output-stream get stream-copy ;
+
+ERROR: template-error template error ;
+
+M: template-error error.
+    "Error while processing template " write
+    [ template>> short. ":" print nl ]
+    [ error>> error. ]
+    bi ;
+
+: call-template ( template -- )
+    [ call-template* ] [ \ template-error boa rethrow ] recover ;
+
+SYMBOL: title
+
+: set-title ( string -- )
+    title get >box ;
+
+: write-title ( -- )
+    title get value>> write ;
+
+SYMBOL: style
+
+: add-style ( string -- )
+    "\n" style get push-all
+         style get push-all ;
+
+: write-style ( -- )
+    style get >string write ;
+
+SYMBOL: atom-feed
+
+: set-atom-feed ( title url -- )
+    2array atom-feed get >box ;
+
+: write-atom-feed ( -- )
+    atom-feed get value>> [
+        <link "alternate" =rel "application/atom+xml" =type
+        [ first =title ] [ second =href ] bi
+        link/>
+    ] when* ;
+
+SYMBOL: nested-template?
+
+SYMBOL: next-template
+
+: call-next-template ( -- )
+    next-template get write-html ;
+
+M: f call-template* drop call-next-template ;
+
+: with-boilerplate ( body template -- )
+    [
+        title get [ <box> title set ] unless
+        atom-feed get [ <box> atom-feed set ] unless
+        style get [ SBUF" " clone style set ] unless
+
+        [
+            [
+                nested-template? on
+                call-template
+            ] with-string-writer
+            next-template set
+        ]
+        [ call-template ]
+        bi*
+    ] with-scope ; inline
+
+: template-convert ( template output -- )
+    utf8 [ call-template ] with-file-writer ;
index c455c8c5f1f42739ea627e77c0af776f56f534a9..7b156a4b9b2f76135687ac5cf3c832c892ad6256 100755 (executable)
@@ -93,7 +93,7 @@ M: download-failed error.
 
 : download-to ( url file -- )
     #! Downloads the contents of a URL to a file.
-    >r http-get r> latin1 [ write ] with-file-writer ;
+    [ http-get ] dip latin1 [ write ] with-file-writer ;
 
 : download ( url -- )
     dup download-name download-to ;
index 89480b43ba740019aa7a9d298fdd45da28e06260..151d1ce84f2cd4e12dd0bc96bf1cf3a866f78be8 100755 (executable)
@@ -237,7 +237,7 @@ test-db [
 [ ] [
     [
         <dispatcher>
-            <action> [ "text/plain" <content> [ "Hi" write ] >>body ] >>display
+            <action> [ [ "Hi" write ] <text-content> ] >>display
             <login>
             <sessions>
             "" add-responder
index 7587cb0fe9f3e24d90f55f621b88cd81d8a17dde..89c8f62d5c3d86fb6333578810ba43ddf269c428 100755 (executable)
@@ -9,7 +9,9 @@ math.parser calendar calendar.format
 io io.streams.string io.encodings.utf8 io.encodings.string
 io.sockets io.sockets.secure
 
-unicode.case unicode.categories qualified ;
+unicode.case unicode.categories qualified
+
+html.templates ;
 
 EXCLUDE: fry => , ;
 
@@ -65,14 +67,14 @@ M: https protocol>string drop "https" ;
     2dup length 2 - >= [
         2drop
     ] [
-        >r 1+ dup 2 + r> subseq  hex> [ , ] when*
+        [ 1+ dup 2 + ] dip subseq  hex> [ , ] when*
     ] if ;
 
 : url-decode-% ( index str -- index str )
-    2dup url-decode-hex >r 3 + r> ;
+    2dup url-decode-hex [ 3 + ] dip ;
 
 : url-decode-+-or-other ( index str ch -- index str )
-    dup CHAR: + = [ drop CHAR: \s ] when , >r 1+ r> ;
+    dup CHAR: + = [ drop CHAR: \s ] when , [ 1+ ] dip ;
 
 : url-decode-iter ( index str -- )
     2dup length >= [
@@ -158,7 +160,7 @@ M: https protocol>string drop "https" ;
     dup [
         "&" split H{ } clone [
             [
-                >r "=" split1 [ dup [ url-decode ] when ] bi@ swap r>
+                [ "=" split1 [ dup [ url-decode ] when ] bi@ swap ] dip
                 add-query-param
             ] curry each
         ] keep
@@ -174,7 +176,7 @@ M: https protocol>string drop "https" ;
     ] assoc-map
     [
         [
-            >r url-encode r>
+            [ url-encode ] dip
             [ url-encode "=" swap 3append , ] with each
         ] assoc-each
     ] { } make "&" join ;
@@ -342,7 +344,7 @@ SYMBOL: max-post-request
     dup "cookie" header [ parse-cookies >>cookies ] when* ;
 
 : parse-content-type-attributes ( string -- attributes )
-    " " split harvest [ "=" split1 >r >lower r> ] { } map>assoc ;
+    " " split harvest [ "=" split1 [ >lower ] dip ] { } map>assoc ;
 
 : parse-content-type ( content-type -- type encoding )
     ";" split1 parse-content-type-attributes "charset" swap at ;
@@ -521,18 +523,8 @@ body ;
     over unparse-content-type "content-type" pick set-at
     write-header ;
 
-GENERIC: write-response-body* ( body -- )
-
-M: f write-response-body* drop ;
-
-M: string write-response-body* write ;
-
-M: callable write-response-body* call ;
-
-M: object write-response-body* output-stream get stream-copy ;
-
 : write-response-body ( response -- response )
-    dup body>> write-response-body* ;
+    dup body>> call-template ;
 
 M: response write-response ( respose -- )
     write-response-version
@@ -547,10 +539,10 @@ M: response write-full-response ( request response -- )
     swap method>> "HEAD" = [ write-response-body ] unless ;
 
 : get-cookie ( request/response name -- cookie/f )
-    >r cookies>> r> '[ , _ name>> = ] find nip ;
+    [ cookies>> ] dip '[ , _ name>> = ] find nip ;
 
 : delete-cookie ( request/response name -- )
-    over cookies>> >r get-cookie r> delete ;
+    over cookies>> [ get-cookie ] dip delete ;
 
 : put-cookie ( request/response cookie -- request/response )
     [ name>> dupd get-cookie [ dupd delete-cookie ] when* ] keep
index 5aa761603fc33c94ac72889b1398b76e1f240eb9..480cbc8e9618be2755d38c4fd7ec4042cc44e2e6 100755 (executable)
@@ -1,16 +1,10 @@
-USING: http.server.actions http.server.validators
+USING: kernel http.server.actions validators
 tools.test math math.parser multiline namespaces http
 io.streams.string http.server sequences splitting accessors ;
 IN: http.server.actions.tests
 
-[
-    "a" [ v-number ] { { "a" "123" } } validate-param
-    [ 123 ] [ "a" get ] unit-test
-] with-scope
-
 <action>
-    [ "a" get "b" get + ] >>display
-    { { "a" [ v-number ] } { "b" [ v-number ] } } >>get-params
+    [ "a" param "b" param [ string>number ] bi@ + ] >>display
 "action-1" set
 
 : lf>crlf "\n" split "\r\n" join ;
index 2d73cb46a786ed0b58b812b65b193385fde561a1..eb5b8bfe688d164e52439d341cb7a6c6b534cb82 100755 (executable)
@@ -1,68 +1,94 @@
 ! Copyright (C) 2008 Slava Pestov.\r
 ! See http://factorcode.org/license.txt for BSD license.\r
-USING: accessors sequences kernel assocs combinators\r
-http.server http.server.validators http hashtables namespaces\r
-fry continuations locals boxes xml.entities html.elements io ;\r
+USING: accessors sequences kernel assocs combinators http.server\r
+validators http hashtables namespaces fry continuations locals\r
+boxes xml.entities html.elements html.components io arrays math ;\r
 IN: http.server.actions\r
 \r
 SYMBOL: params\r
 \r
-SYMBOL: validation-message\r
+SYMBOL: rest-param\r
 \r
-: render-validation-message ( -- )\r
-    validation-message get value>> [\r
-        <span "error" =class span>\r
-            escape-string write\r
-        </span>\r
-    ] when* ;\r
+: render-validation-messages ( -- )\r
+    validation-messages get\r
+    dup empty? [ drop ] [\r
+        <ul "errors" =class ul>\r
+            [ <li> message>> escape-string write </li> ] each\r
+        </ul>\r
+    ] if ;\r
 \r
-TUPLE: action init display submit get-params post-params ;\r
+TUPLE: action rest-param init display validate submit ;\r
 \r
-: <action>\r
-    action new\r
+: new-action ( class -- action )\r
+    new\r
         [ ] >>init\r
         [ <400> ] >>display\r
+        [ ] >>validate\r
         [ <400> ] >>submit ;\r
 \r
-:: validate-param ( name validator assoc -- )\r
-    name assoc at validator with-validator name set ; inline\r
-\r
-: action-params ( validators -- error? )\r
-    validation-failed? off\r
-    params get '[ , validate-param ] assoc-each\r
-    validation-failed? get ;\r
-\r
-: handle-get ( -- response )\r
-    action get get-params>> action-params [ <400> ] [\r
-        action get [ init>> call ] [ display>> call ] bi\r
-    ] if ;\r
+: <action> ( -- action )\r
+    action new-action ;\r
 \r
-: handle-post ( -- response )\r
-    action get post-params>> action-params\r
-    [ <400> ] [ action get submit>> call ] if ;\r
+: handle-get ( action -- response )\r
+    blank-values\r
+    [ init>> call ]\r
+    [ display>> call ]\r
+    bi ;\r
 \r
 : validation-failed ( -- * )\r
-    action get display>> call exit-with ;\r
+    request get method>> "POST" =\r
+    [ action get display>> call ] [ <400> ] if exit-with ;\r
 \r
-: validation-failed-with ( string -- * )\r
-    validation-message get >box\r
-    validation-failed ;\r
+: handle-post ( action -- response )\r
+    init-validation\r
+    blank-values\r
+    [ validate>> call ]\r
+    [ submit>> call ] bi ;\r
+\r
+: handle-rest-param ( arg -- )\r
+    dup length 1 > action get rest-param>> not or\r
+    [ <404> exit-with ] [\r
+        action get rest-param>> associate rest-param set\r
+    ] if ;\r
 \r
 M: action call-responder* ( path action -- response )\r
+    dup action set\r
     '[\r
-        , [ CHAR: / = ] right-trim empty? [\r
-            , action set\r
-            request get\r
-            <box> validation-message set\r
-            [ request-params params set ]\r
-            [\r
-                method>> {\r
-                    { "GET" [ handle-get ] }\r
-                    { "HEAD" [ handle-get ] }\r
-                    { "POST" [ handle-post ] }\r
-                } case\r
-            ] bi\r
-        ] [\r
-            <404>\r
-        ] if\r
+        , dup empty? [ drop ] [ handle-rest-param ] if\r
+\r
+        init-validation\r
+        ,\r
+        request get\r
+        [ request-params rest-param get assoc-union params set ]\r
+        [ method>> ] bi\r
+        {\r
+            { "GET" [ handle-get ] }\r
+            { "HEAD" [ handle-get ] }\r
+            { "POST" [ handle-post ] }\r
+        } case\r
     ] with-exit-continuation ;\r
+\r
+: param ( name -- value )\r
+    params get at ;\r
+\r
+: check-validation ( -- )\r
+    validation-failed? [ validation-failed ] when ;\r
+\r
+: validate-params ( validators -- )\r
+    params get swap validate-values from-assoc\r
+    check-validation ;\r
+\r
+: validate-integer-id ( -- )\r
+    { { "id" [ v-number ] } } validate-params ;\r
+\r
+TUPLE: page-action < action template ;\r
+\r
+: <page-action> ( -- page )\r
+    page-action new-action\r
+        dup '[ , template>> <html-content> ] >>display ;\r
+\r
+TUPLE: feed-action < action feed ;\r
+\r
+: <feed-action> ( -- feed )\r
+    feed-action new\r
+        dup '[ , feed>> call <feed-content> ] >>display ;\r
diff --git a/extra/http/server/auth/admin/admin.factor b/extra/http/server/auth/admin/admin.factor
deleted file mode 100644 (file)
index 21e1a61..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-! Copyright (C) 2008 Slava Pestov.
-! See http://factorcode.org/license.txt for BSD license.
-USING: kernel sequences accessors namespaces combinators words
-assocs locals db.tuples arrays splitting strings qualified
-
-http.server.templating.chloe
-http.server.boilerplate
-http.server.auth.providers
-http.server.auth.providers.db
-http.server.auth.login
-http.server.auth
-http.server.forms
-http.server.components.inspector
-http.server.validators
-http.server.sessions
-http.server.actions
-http.server.crud
-http.server ;
-EXCLUDE: http.server.components => string? number? ;
-IN: http.server.auth.admin
-
-: admin-template ( name -- template )
-    "resource:extra/http/server/auth/admin/" swap ".xml" 3append <chloe> ;
-
-: words>strings ( seq -- seq' )
-    [ [ word-vocabulary ] [ drop ":" ] [ word-name ] tri 3append ] map ;
-
-: strings>words ( seq -- seq' )
-    [ ":" split1 swap lookup ] map ;
-
-: <capabilities> ( id -- component )
-    capabilities get words>strings <menu> ;
-
-: <new-user-form> ( -- form )
-    "user" <form>
-        "new-user" admin-template >>edit-template
-        "username" <string> add-field
-        "realname" <string> add-field
-        "new-password" <password> t >>required add-field
-        "verify-password" <password> t >>required add-field
-        "email" <email> add-field
-        "capabilities" <capabilities> add-field ;
-
-: <edit-user-form> ( -- form )
-    "user" <form>
-        "edit-user" admin-template >>edit-template
-        "user-summary" admin-template >>summary-template
-        "username" <string> hidden >>renderer add-field
-        "realname" <string> add-field
-        "new-password" <password> add-field
-        "verify-password" <password> add-field
-        "email" <email> add-field
-        "profile" <inspector> add-field
-        "capabilities" <capabilities> add-field ;
-
-: <user-list-form> ( -- form )
-    "user-list" <form>
-        "user-list" admin-template >>view-template
-        "list" <edit-user-form> +unordered+ <list> add-field ;
-
-:: <new-user-action> ( form ctor next -- action )
-    <action>
-        [
-            blank-values
-
-            "username" get ctor call
-
-            {
-                [ username>> "username" set-value ]
-                [ realname>> "realname" set-value ]
-                [ email>> "email" set-value ]
-                [ profile>> "profile" set-value ]
-            } cleave
-        ] >>init
-
-        [ form edit-form ] >>display
-
-        [
-            blank-values
-
-            form validate-form
-
-            same-password-twice
-
-            user new "username" value >>username select-tuple
-            [ user-exists ] when
-
-            "username" value <user>
-                "realname" value >>realname
-                "email" value >>email
-                "new-password" value >>encoded-password
-                H{ } clone >>profile
-
-            insert-tuple
-
-            next f <standard-redirect>
-        ] >>submit ;
-    
-:: <edit-user-action> ( form ctor next -- action )
-    <action>
-        { { "username" [ v-required ] } } >>get-params
-
-        [
-            blank-values
-
-            "username" get ctor call select-tuple
-
-            {
-                [ username>> "username" set-value ]
-                [ realname>> "realname" set-value ]
-                [ email>> "email" set-value ]
-                [ profile>> "profile" set-value ]
-                [ capabilities>> words>strings "capabilities" set-value ]
-            } cleave
-        ] >>init
-
-        [ form edit-form ] >>display
-
-        [
-            blank-values
-
-            form validate-form
-
-            "username" value <user> select-tuple
-                "realname" value >>realname
-                "email" value >>email
-
-            { "new-password" "verify-password" }
-            [ value empty? ] all? [
-                same-password-twice
-                "new-password" value >>encoded-password
-            ] unless
-
-            "capabilities" value {
-                { [ dup string? ] [ 1array ] }
-                { [ dup array? ] [ ] }
-            } cond strings>words >>capabilities
-
-            update-tuple
-
-            next f <standard-redirect>
-        ] >>submit ;
-
-:: <delete-user-action> ( ctor next -- action )
-    <action>
-        { { "username" [ ] } } >>post-params
-
-        [
-            "username" get
-            [ <user> select-tuple 1 >>deleted update-tuple ]
-            [ logout-all-sessions ]
-            bi
-
-            next f <standard-redirect>
-        ] >>submit ;
-
-TUPLE: user-admin < dispatcher ;
-
-SYMBOL: can-administer-users?
-
-can-administer-users? define-capability
-
-:: <user-admin> ( -- responder )
-    [let | ctor [ [ <user> ] ] |
-        user-admin new-dispatcher
-            <user-list-form> ctor <list-action> "" add-responder
-            <new-user-form> ctor "$user-admin" <new-user-action> "new" add-responder
-            <edit-user-form> ctor "$user-admin" <edit-user-action> "edit" add-responder
-            ctor "$user-admin" <delete-user-action> "delete" add-responder
-        <boilerplate>
-            "admin" admin-template >>template
-        { can-administer-users? } <protected>
-    ] ;
-
-: make-admin ( username -- )
-    <user>
-    select-tuple
-    [ can-administer-users? suffix ] change-capabilities
-    update-tuple ;
diff --git a/extra/http/server/auth/admin/admin.xml b/extra/http/server/auth/admin/admin.xml
deleted file mode 100644 (file)
index 0581756..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <div class="navbar">
-                 <t:a t:href="$user-admin">List Users</t:a>
-               | <t:a t:href="$user-admin/new">Add User</t:a>
-
-               <t:if t:code="http.server.auth.login:allow-edit-profile?">
-                       | <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
-               </t:if>
-
-               | <t:button t:action="$login/logout" t:flow="begin" class="link-button link">Logout</t:button>
-       </div>
-
-       <h1><t:write-title /></h1>
-
-       <t:call-next-template />
-
-</t:chloe>
diff --git a/extra/http/server/auth/admin/edit-user.xml b/extra/http/server/auth/admin/edit-user.xml
deleted file mode 100644 (file)
index 9c0fe70..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <t:title>Edit User</t:title>
-
-       <t:form t:action="$user-admin/edit" t:for="username">
-
-       <table>
-       
-       <tr>
-               <th class="field-label">User name:</th>
-               <td><t:view t:component="username" /></td>
-       </tr>
-       
-       <tr>
-               <th class="field-label">Real name:</th>
-               <td><t:edit t:component="realname" /></td>
-       </tr>
-       
-       <tr>
-               <th class="field-label">New password:</th>
-               <td><t:edit t:component="new-password" /></td>
-       </tr>
-       
-       <tr>
-               <th class="field-label">Verify:</th>
-               <td><t:edit t:component="verify-password" /></td>
-       </tr>
-       
-       <tr>
-               <th class="field-label">E-mail:</th>
-               <td><t:edit t:component="email" /></td>
-       </tr>
-       
-       <tr>
-               <th class="field-label big-field-label">Capabilities:</th>
-               <td><t:edit t:component="capabilities" /></td>
-       </tr>
-       
-       <tr>
-               <th class="field-label">Profile:</th>
-               <td><t:view t:component="profile" /></td>
-       </tr>
-
-       </table>
-       
-       <p>
-               <button type="submit" class="link-button link">Update</button>
-               <t:validation-message />
-       </p>
-
-       </t:form>
-
-       <t:button t:action="$user-admin/delete" t:for="username" class="link-button link">Delete</t:button>
-</t:chloe>
diff --git a/extra/http/server/auth/admin/new-user.xml b/extra/http/server/auth/admin/new-user.xml
deleted file mode 100644 (file)
index 2d67639..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <t:title>New User</t:title>
-
-       <t:form t:action="$user-admin/new">
-
-       <table>
-       
-       <tr>
-               <th class="field-label">User name:</th>
-               <td><t:edit t:component="username" /></td>
-       </tr>
-       
-       <tr>
-               <th class="field-label">Real name:</th>
-               <td><t:edit t:component="realname" /></td>
-       </tr>
-       
-       <tr>
-               <th class="field-label">New password:</th>
-               <td><t:edit t:component="new-password" /></td>
-       </tr>
-       
-       <tr>
-               <th class="field-label">Verify:</th>
-               <td><t:edit t:component="verify-password" /></td>
-       </tr>
-       
-       <tr>
-               <th class="field-label">E-mail:</th>
-               <td><t:edit t:component="email" /></td>
-       </tr>
-       
-       <tr>
-               <th class="field-label big-field-label">Capabilities:</th>
-               <td><t:edit t:component="capabilities" /></td>
-       </tr>
-
-       </table>
-       
-       <p>
-               <button type="submit" class="link-button link">Create</button>
-               <t:validation-message />
-       </p>
-
-       </t:form>
-</t:chloe>
diff --git a/extra/http/server/auth/admin/user-list.xml b/extra/http/server/auth/admin/user-list.xml
deleted file mode 100644 (file)
index 520b7f2..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <t:title>Users</t:title>
-
-       <t:summary t:component="list" />
-
-</t:chloe>
diff --git a/extra/http/server/auth/admin/user-summary.xml b/extra/http/server/auth/admin/user-summary.xml
deleted file mode 100644 (file)
index c426e7c..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <t:a t:href="$user-admin/edit" t:query="username">
-               <t:view t:component="username" />
-       </t:a>
-
-</t:chloe>
index 36fcff4b2ef47da0d70ba5e6a358f9536e91cd4e..4b34fbe804f36ddf56dfde05a196cb272a340be4 100755 (executable)
@@ -1,6 +1,6 @@
 ! Copyright (c) 2008 Slava Pestov\r
 ! See http://factorcode.org/license.txt for BSD license.\r
-USING: accessors assocs namespaces kernel sequences\r
+USING: accessors assocs namespaces kernel sequences sets\r
 http.server\r
 http.server.sessions\r
 http.server.auth.providers ;\r
@@ -38,4 +38,4 @@ SYMBOL: capabilities
 \r
 V{ } clone capabilities set-global\r
 \r
-: define-capability ( word -- ) capabilities get push-new ;\r
+: define-capability ( word -- ) capabilities get adjoin ;\r
index 1eaf65fa07e09d52519edf9b3457525f717db1c7..6beaf5de6d792a1eae3522ff7af012126821c2cf 100644 (file)
        
        <tr>
                <th class="field-label">User name:</th>
-               <td><t:view t:component="username" /></td>
+               <td><t:label t:name="username" /></td>
        </tr>
        
        <tr>
                <th class="field-label">Real name:</th>
-               <td><t:edit t:component="realname" /></td>
+               <td><t:field t:name="realname" /></td>
        </tr>
        
        <tr>
@@ -25,7 +25,7 @@
        
        <tr>
                <th class="field-label">Current password:</th>
-               <td><t:edit t:component="password" /></td>
+               <td><t:password t:name="password" /></td>
        </tr>
        
        <tr>
        
        <tr>
                <th class="field-label">New password:</th>
-               <td><t:edit t:component="new-password" /></td>
+               <td><t:password t:name="new-password" /></td>
        </tr>
        
        <tr>
                <th class="field-label">Verify:</th>
-               <td><t:edit t:component="verify-password" /></td>
+               <td><t:password t:name="verify-password" /></td>
        </tr>
        
        <tr>
@@ -50,7 +50,7 @@
        
        <tr>
                <th class="field-label">E-mail:</th>
-               <td><t:edit t:component="email" /></td>
+               <td><t:field t:name="email" /></td>
        </tr>
        
        <tr>
@@ -62,7 +62,7 @@
 
        <p>
                <input type="submit" value="Update" />
-               <t:validation-message />
+               <t:validation-messages />
        </p>
 
        </t:form>
index bb77532a22d0ad1e128eabbc7b553842bc8cc490..fd4fbab8e81b27260fc288da7098fb9d189cdbd5 100755 (executable)
@@ -2,7 +2,7 @@
 ! See http://factorcode.org/license.txt for BSD license.\r
 USING: accessors quotations assocs kernel splitting\r
 combinators sequences namespaces hashtables sets\r
-fry arrays threads locals qualified random\r
+fry arrays threads qualified random validators\r
 io\r
 io.sockets\r
 io.encodings.utf8\r
@@ -12,23 +12,22 @@ continuations
 destructors\r
 checksums\r
 checksums.sha2\r
+validators\r
+html.components\r
 html.elements\r
+html.templates\r
+html.templates.chloe\r
 http\r
 http.server\r
 http.server.auth\r
 http.server.auth.providers\r
 http.server.auth.providers.db\r
 http.server.actions\r
-http.server.components\r
 http.server.flows\r
-http.server.forms\r
 http.server.sessions\r
-http.server.boilerplate\r
-http.server.templating\r
-http.server.templating.chloe\r
-http.server.validators ;\r
-IN: http.server.auth.login\r
+http.server.boilerplate ;\r
 QUALIFIED: smtp\r
+IN: http.server.auth.login\r
 \r
 TUPLE: login < dispatcher users checksum ;\r
 \r
@@ -65,149 +64,122 @@ M: user-saver dispose
     3append <chloe> ;\r
 \r
 ! ! ! Login\r
-\r
-: <login-form>\r
-    "login" <form>\r
-        "login" login-template >>edit-template\r
-        "username" <username>\r
-            t >>required\r
-            add-field\r
-        "password" <password>\r
-            t >>required\r
-            add-field ;\r
-\r
 : successful-login ( user -- response )\r
-    username>> set-uid\r
-    "$login" end-flow ;\r
-\r
-: login-failed "invalid username or password" validation-failed-with ;\r
-\r
-:: <login-action> ( -- action )\r
-    [let | form [ <login-form> ] |\r
-        <action>\r
-            [ blank-values ] >>init\r
+    username>> set-uid "$login" end-flow ;\r
 \r
-            [ form edit-form ] >>display\r
+: login-failed ( -- * )\r
+    "invalid username or password" validation-error\r
+    validation-failed ;\r
 \r
-            [\r
-                blank-values\r
+: <login-action> ( -- action )\r
+    <action>\r
+        [ "login" login-template <html-content> ] >>display\r
 \r
-                form validate-form\r
+        [\r
+            {\r
+                { "username" [ v-required ] }\r
+                { "password" [ v-required ] }\r
+            } validate-params\r
 \r
-                "password" value "username" value check-login\r
-                [ successful-login ] [ login-failed ] if*\r
-            ] >>submit\r
-    ] ;\r
+            "password" value\r
+            "username" value check-login\r
+            [ successful-login ] [ login-failed ] if*\r
+        ] >>submit ;\r
 \r
 ! ! ! New user registration\r
 \r
-: <register-form> ( -- form )\r
-    "register" <form>\r
-        "register" login-template >>edit-template\r
-        "username" <username>\r
-            t >>required\r
-            add-field\r
-        "realname" <string> add-field\r
-        "new-password" <password>\r
-            t >>required\r
-            add-field\r
-        "verify-password" <password>\r
-            t >>required\r
-            add-field\r
-        "email" <email> add-field\r
-        "captcha" <captcha> add-field ;\r
-\r
-: password-mismatch "passwords do not match" validation-failed-with ;\r
-\r
-: user-exists "username taken" validation-failed-with ;\r
+: user-exists ( -- * )\r
+    "username taken" validation-error\r
+    validation-failed ;\r
+\r
+: password-mismatch ( -- * )\r
+    "passwords do not match" validation-error\r
+    validation-failed ;\r
 \r
 : same-password-twice ( -- )\r
     "new-password" value "verify-password" value =\r
     [ password-mismatch ] unless ;\r
 \r
-:: <register-action> ( -- action )\r
-    [let | form [ <register-form> ] |\r
-        <action>\r
-            [ blank-values ] >>init\r
-\r
-            [ form edit-form ] >>display\r
+: <register-action> ( -- action )\r
+    <page-action>\r
+        "register" login-template >>template\r
 \r
-            [\r
-                blank-values\r
-\r
-                form validate-form\r
-\r
-                same-password-twice\r
+        [\r
+            {\r
+                { "username" [ v-username ] }\r
+                { "realname" [ [ v-one-line ] v-optional ] }\r
+                { "new-password" [ v-password ] }\r
+                { "verify-password" [ v-password ] }\r
+                { "email" [ [ v-email ] v-optional ] }\r
+                { "captcha" [ v-captcha ] }\r
+            } validate-params\r
+\r
+            same-password-twice\r
+        ] >>validate\r
 \r
-                "username" value <user>\r
-                    "realname" value >>realname\r
-                    "new-password" value >>encoded-password\r
-                    "email" value >>email\r
-                    H{ } clone >>profile\r
+        [\r
+            "username" value <user>\r
+                "realname" value >>realname\r
+                "new-password" value >>encoded-password\r
+                "email" value >>email\r
+                H{ } clone >>profile\r
 \r
-                users new-user [ user-exists ] unless*\r
+            users new-user [ user-exists ] unless*\r
 \r
-                successful-login\r
+            login get init-user-profile\r
 \r
-                login get init-user-profile\r
-            ] >>submit\r
-    ] ;\r
+            successful-login\r
+        ] >>submit ;\r
 \r
 ! ! ! Editing user profile\r
 \r
-: <edit-profile-form> ( -- form )\r
-    "edit-profile" <form>\r
-        "edit-profile" login-template >>edit-template\r
-        "username" <username> add-field\r
-        "realname" <string> add-field\r
-        "password" <password> add-field\r
-        "new-password" <password> add-field\r
-        "verify-password" <password> add-field\r
-        "email" <email> add-field ;\r
-\r
-:: <edit-profile-action> ( -- action )\r
-    [let | form [ <edit-profile-form> ] |\r
-        <action>\r
-            [\r
-                blank-values\r
-\r
-                logged-in-user get\r
-                [ username>> "username" set-value ]\r
-                [ realname>> "realname" set-value ]\r
-                [ email>> "email" set-value ]\r
-                tri\r
-            ] >>init\r
+: <edit-profile-action> ( -- action )\r
+    <action>\r
+        [\r
+            logged-in-user get\r
+            [ username>> "username" set-value ]\r
+            [ realname>> "realname" set-value ]\r
+            [ email>> "email" set-value ]\r
+            tri\r
+        ] >>init\r
 \r
-            [ form edit-form ] >>display\r
+        [ "edit-profile" login-template <html-content> ] >>display\r
 \r
-            [\r
-                blank-values\r
-                uid "username" set-value\r
+        [\r
+            uid "username" set-value\r
 \r
-                form validate-form\r
+            {\r
+                { "realname" [ [ v-one-line ] v-optional ] }\r
+                { "password" [ ] }\r
+                { "new-password" [ [ v-password ] v-optional ] }\r
+                { "verify-password" [ [ v-password ] v-optional ] } \r
+                { "email" [ [ v-email ] v-optional ] }\r
+            } validate-params\r
 \r
-                logged-in-user get\r
+            { "password" "new-password" "verify-password" }\r
+            [ value empty? not ] contains? [\r
+                "password" value uid check-login\r
+                [ "incorrect password" validation-error ] unless\r
 \r
-                { "password" "new-password" "verify-password" }\r
-                [ value empty? ] all? [\r
-                    same-password-twice\r
+                same-password-twice\r
+            ] when\r
+        ] >>validate\r
 \r
-                    "password" value uid check-login\r
-                    [ login-failed ] unless\r
+        [\r
+            logged-in-user get\r
 \r
-                    "new-password" value >>encoded-password\r
-                ] unless\r
+            "new-password" value dup empty?\r
+            [ drop ] [ >>encoded-password ] if\r
 \r
-                "realname" value >>realname\r
-                "email" value >>email\r
+            "realname" value >>realname\r
+            "email" value >>email\r
 \r
-                t >>changed?\r
+            t >>changed?\r
 \r
-                drop\r
+            drop\r
 \r
-                "$login" end-flow\r
-            ] >>submit\r
-    ] ;\r
+            "$login" end-flow\r
+        ] >>submit ;\r
 \r
 ! ! ! Password recovery\r
 \r
@@ -250,92 +222,61 @@ SYMBOL: lost-password-from
     '[ , password-email smtp:send-email ]\r
     "E-mail send thread" spawn drop ;\r
 \r
-: <recover-form-1> ( -- form )\r
-    "register" <form>\r
-        "recover-1" login-template >>edit-template\r
-        "username" <username>\r
-            t >>required\r
-            add-field\r
-        "email" <email>\r
-            t >>required\r
-            add-field\r
-        "captcha" <captcha> add-field ;\r
-\r
-:: <recover-action-1> ( -- action )\r
-    [let | form [ <recover-form-1> ] |\r
-        <action>\r
-            [ blank-values ] >>init\r
-\r
-            [ form edit-form ] >>display\r
-\r
-            [\r
-                blank-values\r
-\r
-                form validate-form\r
-\r
-                "email" value "username" value\r
-                users issue-ticket [\r
-                    send-password-email\r
-                ] when*\r
-\r
-                "recover-2" login-template serve-template\r
-            ] >>submit\r
-    ] ;\r
-\r
-: <recover-form-3>\r
-    "new-password" <form>\r
-        "recover-3" login-template >>edit-template\r
-        "username" <username>\r
-            hidden >>renderer\r
-            t >>required\r
-            add-field\r
-        "new-password" <password>\r
-            t >>required\r
-            add-field\r
-        "verify-password" <password>\r
-            t >>required\r
-            add-field\r
-        "ticket" <string>\r
-            hidden >>renderer\r
-            t >>required\r
-            add-field ;\r
-\r
-:: <recover-action-3> ( -- action )\r
-    [let | form [ <recover-form-3> ] |\r
-        <action>\r
-            [\r
-                { "username" [ v-required ] }\r
-                { "ticket" [ v-required ] }\r
-            ] >>get-params\r
+: <recover-action-1> ( -- action )\r
+    <action>\r
+        [ "recover-1" login-template <html-content> ] >>display\r
 \r
-            [\r
-                [\r
-                    "username" [ get ] keep set\r
-                    "ticket" [ get ] keep set\r
-                ] H{ } make-assoc values set\r
-            ] >>init\r
+        [\r
+            {\r
+                { "username" [ v-username ] }\r
+                { "email" [ v-email ] }\r
+                { "captcha" [ v-captcha ] }\r
+            } validate-params\r
+        ] >>validate\r
 \r
-            [ <recover-form-3> edit-form ] >>display\r
+        [\r
+            "email" value "username" value\r
+            users issue-ticket [\r
+                send-password-email\r
+            ] when*\r
 \r
-            [\r
-                blank-values\r
+            "recover-2" login-template <html-content>\r
+        ] >>submit ;\r
 \r
-                form validate-form\r
+: <recover-action-3> ( -- action )\r
+    <action>\r
+        [\r
+            {\r
+                { "username" [ v-username ] }\r
+                { "ticket" [ v-required ] }\r
+            } validate-params\r
+        ] >>init\r
 \r
-                same-password-twice\r
+        [ "recover-3" login-template <html-content> ] >>display\r
 \r
-                "ticket" value\r
-                "username" value\r
-                users claim-ticket [\r
-                    "new-password" value >>encoded-password\r
-                    users update-user\r
-\r
-                    "recover-4" login-template serve-template\r
-                ] [\r
-                    <400>\r
-                ] if*\r
-            ] >>submit\r
-    ] ;\r
+        [\r
+            {\r
+                { "username" [ v-username ] }\r
+                { "ticket" [ v-required ] }\r
+                { "new-password" [ v-password ] }\r
+                { "verify-password" [ v-password ] }\r
+            } validate-params\r
+\r
+            same-password-twice\r
+        ] >>validate\r
+\r
+        [\r
+            "ticket" value\r
+            "username" value\r
+            users claim-ticket [\r
+                "new-password" value >>encoded-password\r
+                users update-user\r
+\r
+                "recover-4" login-template <html-content>\r
+            ] [\r
+                <400>\r
+            ] if*\r
+        ] >>submit ;\r
 \r
 ! ! ! Logout\r
 : <logout-action> ( -- action )\r
index d0a73a4d8b07046b19660899ffbb36f41f70a93a..545d7e0990e40f6d9fed5205bd05ebd39bb61c4d 100644 (file)
 
                        <tr>
                                <th class="field-label">User name:</th>
-                               <td><t:edit t:component="username" /></td>
+                               <td><t:field t:name="username" /></td>
                        </tr>
 
                        <tr>
                                <th class="field-label">Password:</th>
-                               <td><t:edit t:component="password" /></td>
+                               <td><t:password t:name="password" /></td>
                        </tr>
 
                </table>
@@ -23,7 +23,7 @@
                <p>
 
                        <input type="submit" value="Log in" />
-                       <t:validation-message />
+                       <t:validation-messages />
 
                </p>
 
index 7c72181c10e27ff5cc8b35b4c752d3d4aff03aee..21fbe6fd398890fbcda5a10a3f1af13d2131b0bc 100644 (file)
 
                <table>
 
-               <tr>
-               <th class="field-label">User name:</th>
-               <td><t:edit t:component="username" /></td>
-               </tr>
-
-               <tr>
-               <th class="field-label">E-mail:</th>
-               <td><t:edit t:component="email" /></td>
-               </tr>
-
-               <tr>
-               <th class="field-label">Captcha:</th>
-               <td><t:edit t:component="captcha" /></td>
-               </tr>
-
-               <tr>
-               <td></td>
-               <td>Leave the captcha blank. Spam-bots will fill it indiscriminantly, so their attempts to e-mail you will be blocked.</td>
-               </tr>
+                       <tr>
+                               <th class="field-label">User name:</th>
+                               <td><t:field t:name="username" /></td>
+                       </tr>
+
+                       <tr>
+                               <th class="field-label">E-mail:</th>
+                               <td><t:field t:name="email" /></td>
+                       </tr>
+
+                       <tr>
+                               <th class="field-label">Captcha:</th>
+                               <td><t:field t:name="captcha" /></td>
+                       </tr>
+
+                       <tr>
+                               <td></td>
+                               <td>Leave the captcha blank. Spam-bots will fill it indiscriminantly, so their attempts to e-mail you will be blocked.</td>
+                       </tr>
 
                </table>
 
index 6c60b257a890bdd5fd80a677c8c8d9487435bb9c..2e412d1f180eb3b012d03b99731f7d697c419efc 100644 (file)
 
                <table>
 
-                       <t:edit t:component="username" />
-                       <t:edit t:component="ticket" />
+                       <t:hidden t:name="username" />
+                       <t:hidden t:name="ticket" />
 
                        <tr>
-                       <th class="field-label">Password:</th>
-                       <td><t:edit t:component="new-password" /></td>
+                               <th class="field-label">Password:</th>
+                               <td><t:password t:name="new-password" /></td>
                        </tr>
 
                        <tr>
-                       <th class="field-label">Verify password:</th>
-                       <td><t:edit t:component="verify-password" /></td>
+                               <th class="field-label">Verify password:</th>
+                               <td><t:password t:name="verify-password" /></td>
                        </tr>
 
                        <tr>
-                       <td></td>
-                       <td>Enter your password twice to ensure it is correct.</td>
+                               <td></td>
+                               <td>Enter your password twice to ensure it is correct.</td>
                        </tr>
 
                </table>
 
                <p>
                        <input type="submit" value="Set password" />
-                       <t:validation-message />
+                       <t:validation-messages />
                </p>
 
        </t:form>
index 9b45a7f0876d70a7bc66286886adb903c2c71213..9815f21945824b941e71b559d6ef3531d47c527f 100644 (file)
@@ -8,62 +8,62 @@
 
                <table>
 
-               <tr>
-               <th class="field-label">User name:</th>
-               <td><t:edit t:component="username" /></td>
-               </tr>
-
-               <tr>
-               <th class="field-label">Real name:</th>
-               <td><t:edit t:component="realname" /></td>
-               </tr>
-
-               <tr>
-               <td></td>
-               <td>Specifying a real name is optional.</td>
-               </tr>
-
-               <tr>
-               <th class="field-label">Password:</th>
-               <td><t:edit t:component="new-password" /></td>
-               </tr>
-
-               <tr>
-               <th class="field-label">Verify:</th>
-               <td><t:edit t:component="verify-password" /></td>
-               </tr>
-
-               <tr>
-               <td></td>
-               <td>Enter your password twice to ensure it is correct.</td>
-               </tr>
-
-               <tr>
-               <th class="field-label">E-mail:</th>
-               <td><t:edit t:component="email" /></td>
-               </tr>
-
-               <tr>
-               <td></td>
-               <td>Specifying an e-mail address is optional. It enables the "recover password" feature.</td>
-               </tr>
-
-               <tr>
-               <th class="field-label">Captcha:</th>
-               <td><t:edit t:component="captcha" /></td>
-               </tr>
-
-               <tr>
-               <td></td>
-               <td>Leave the captcha blank. Spam-bots will fill it indiscriminantly, so their attempts to register will be blocked.</td>
-               </tr>
+                       <tr>
+                               <th class="field-label">User name:</th>
+                               <td><t:field t:name="username" /></td>
+                       </tr>
+
+                       <tr>
+                               <th class="field-label">Real name:</th>
+                               <td><t:field t:name="realname" /></td>
+                       </tr>
+
+                       <tr>
+                               <td></td>
+                               <td>Specifying a real name is optional.</td>
+                       </tr>
+
+                       <tr>
+                               <th class="field-label">Password:</th>
+                               <td><t:password t:name="new-password" /></td>
+                       </tr>
+
+                       <tr>
+                               <th class="field-label">Verify:</th>
+                               <td><t:password t:name="verify-password" /></td>
+                       </tr>
+
+                       <tr>
+                               <td></td>
+                               <td>Enter your password twice to ensure it is correct.</td>
+                       </tr>
+
+                       <tr>
+                               <th class="field-label">E-mail:</th>
+                               <td><t:field t:name="email" /></td>
+                       </tr>
+
+                       <tr>
+                               <td></td>
+                               <td>Specifying an e-mail address is optional. It enables the "recover password" feature.</td>
+                       </tr>
+
+                       <tr>
+                               <th class="field-label">Captcha:</th>
+                               <td><t:field t:name="captcha" /></td>
+                       </tr>
+
+                       <tr>
+                               <td></td>
+                               <td>Leave the captcha blank. Spam-bots will fill it indiscriminantly, so their attempts to register will be blocked.</td>
+                       </tr>
 
                </table>
 
                <p>
 
                        <input type="submit" value="Register" />
-                       <t:validation-message />
+                       <t:validation-messages />
 
                </p>
 
index 54f96480bca68961127685a8f4d9ddf1a6f20b48..d6ba587aa0415c890b410b788757df82cfa4bfbc 100755 (executable)
@@ -15,5 +15,5 @@ M: users-in-memory get-user ( username provider -- user/f )
 M: users-in-memory update-user ( user provider -- ) 2drop ;\r
 \r
 M: users-in-memory new-user ( user provider -- user/f )\r
-    >r dup username>> r> assoc>>\r
-    2dup key? [ 3drop f ] [ pick >r set-at r> ] if ;\r
+    [ dup username>> ] dip assoc>>\r
+    2dup key? [ 3drop f ] [ pick [ set-at ] dip ] if ;\r
index e0a4037e31897cab7d51898dfec20dafe2165566..96c59edd10ac2daf7a334199cdaf6360507cb0cf 100644 (file)
@@ -1,73 +1,13 @@
 ! Copyright (c) 2008 Slava Pestov
 ! See http://factorcode.org/license.txt for BSD license.
-USING: accessors kernel namespaces boxes sequences strings
-io io.streams.string arrays locals
-html.elements
-http
-http.server
-http.server.sessions
-http.server.templating ;
+USING: accessors kernel namespaces http.server html.templates
+locals ;
 IN: http.server.boilerplate
 
 TUPLE: boilerplate < filter-responder template ;
 
 : <boilerplate> f boilerplate boa ;
 
-SYMBOL: title
-
-: set-title ( string -- )
-    title get >box ;
-
-: write-title ( -- )
-    title get value>> write ;
-
-SYMBOL: style
-
-: add-style ( string -- )
-    "\n" style get push-all
-         style get push-all ;
-
-: write-style ( -- )
-    style get >string write ;
-
-SYMBOL: atom-feed
-
-: set-atom-feed ( title url -- )
-    2array atom-feed get >box ;
-
-: write-atom-feed ( -- )
-    atom-feed get value>> [
-        <link "alternate" =rel "application/atom+xml" =type
-        [ first =title ] [ second =href ] bi
-        link/>
-    ] when* ;
-
-SYMBOL: nested-template?
-
-SYMBOL: next-template
-
-: call-next-template ( -- )
-    next-template get write-html ;
-
-M: f call-template* drop call-next-template ;
-
-: with-boilerplate ( body template -- )
-    [
-        title get [ <box> title set ] unless
-        atom-feed get [ <box> atom-feed set ] unless
-        style get [ SBUF" " clone style set ] unless
-
-        [
-            [
-                nested-template? on
-                write-response-body*
-            ] with-string-writer
-            next-template set
-        ]
-        [ call-template ]
-        bi*
-    ] with-scope ; inline
-
 M:: boilerplate call-responder* ( path responder -- )
     path responder call-next-method
     dup content-type>> "text/html" = [
index 5325ee3b55c66e50a967ad3b59358cbb369bc00f..3b819e067b6198578fc04601c8c5bb37a2a4a197 100755 (executable)
@@ -1,7 +1,7 @@
 ! Copyright (C) 2004 Chris Double.\r
 ! Copyright (C) 2006, 2008 Slava Pestov.\r
 ! See http://factorcode.org/license.txt for BSD license.\r
-USING: html http http.server io kernel math namespaces\r
+USING: http http.server io kernel math namespaces\r
 continuations calendar sequences assocs hashtables\r
 accessors arrays alarms quotations combinators fry assocs.lib ;\r
 IN: http.server.callbacks\r
@@ -90,7 +90,7 @@ SYMBOL: current-show
     [ restore-request store-current-show ] when* ;\r
 \r
 : show-final ( quot -- * )\r
-    >r redirect-to-here store-current-show r>\r
+    [ redirect-to-here store-current-show ] dip\r
     call exit-with ; inline\r
 \r
 : resuming-callback ( responder request -- id )\r
@@ -111,7 +111,7 @@ M: callback-responder call-responder* ( path responder -- response )
     ] with-exit-continuation ;\r
 \r
 : show-page ( quot -- )\r
-    >r redirect-to-here store-current-show r>\r
+    [ redirect-to-here store-current-show ] dip\r
     [\r
         [ ] t register-callback swap call exit-with\r
     ] callcc1 restore-request ; inline\r
diff --git a/extra/http/server/components/code/code.factor b/extra/http/server/components/code/code.factor
deleted file mode 100644 (file)
index 19fc8c5..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-! Copyright (C) 2008 Slava Pestov
-! See http://factorcode.org/license.txt for BSD license.
-USING: splitting kernel io sequences xmode.code2html accessors
-http.server.components html xml.entities ;
-IN: http.server.components.code
-
-TUPLE: code-renderer < text-renderer mode ;
-
-: <code-renderer> ( mode -- renderer )
-    code-renderer new-text-renderer
-        swap >>mode ;
-
-M: code-renderer render-view*
-    [
-        [ string-lines ] [ mode>> value ] bi* htmlize-lines
-    ] with-html-stream ;
-
-: <code> ( id mode -- component )
-    swap <text>
-        swap <code-renderer> >>renderer ;
diff --git a/extra/http/server/components/components-tests.factor b/extra/http/server/components/components-tests.factor
deleted file mode 100755 (executable)
index ff87bb7..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-IN: http.server.components.tests\r
-USING: http.server.components http.server.forms\r
-http.server.validators namespaces tools.test kernel accessors\r
-tuple-syntax mirrors\r
-http http.server.actions http.server.templating.fhtml\r
-io.streams.string io.streams.null ;\r
-\r
-validation-failed? off\r
-\r
-[ 3 ] [ "3" "n" <number> validate ] unit-test\r
-\r
-[ 123 ] [\r
-    ""\r
-    "n" <number>\r
-        123 >>default\r
-    validate\r
-] unit-test\r
-\r
-[ f ] [ validation-failed? get ] unit-test\r
-\r
-[ t ] [ "3x" "n" <number> validate validation-error? ] unit-test\r
-\r
-[ t ] [ validation-failed? get ] unit-test\r
-\r
-[ "" ] [ "" "email" <email> validate ] unit-test\r
-\r
-[ "slava@jedit.org" ] [ "slava@jedit.org" "email" <email> validate ] unit-test\r
-\r
-[ "slava@jedit.org" ] [\r
-    "slava@jedit.org"\r
-    "email" <email>\r
-        t >>required\r
-    validate\r
-] unit-test\r
-\r
-[ t ] [\r
-    "a"\r
-    "email" <email>\r
-        t >>required\r
-    validate validation-error?\r
-] unit-test\r
-\r
-[ t ] [ "a" "email" <email> validate validation-error? ] unit-test\r
-\r
-TUPLE: test-tuple text number more-text ;\r
-\r
-: <test-tuple> test-tuple new ;\r
-\r
-: <test-form> ( -- form )\r
-    "test" <form>\r
-        "resource:extra/http/server/components/test/form.fhtml" <fhtml> >>view-template\r
-        "resource:extra/http/server/components/test/form.fhtml" <fhtml> >>edit-template\r
-        "text" <string>\r
-            t >>required\r
-            add-field\r
-        "number" <number>\r
-            123 >>default\r
-            t >>required\r
-            0 >>min-value\r
-            10 >>max-value\r
-            add-field\r
-        "more-text" <text>\r
-            "hi" >>default\r
-            add-field ;\r
-\r
-[ ] [ <test-tuple> <mirror> values set <test-form> view-form write-response-body drop ] unit-test\r
-\r
-[ ] [ <test-tuple> <mirror> values set <test-form> edit-form write-response-body drop ] unit-test\r
-\r
-[ TUPLE{ test-tuple number: 123 more-text: "hi" } ] [\r
-    <test-tuple> from-tuple\r
-    <test-form> set-defaults\r
-    values-tuple\r
-] unit-test\r
-\r
-[\r
-    H{\r
-        { "text" "fdafsa" }\r
-        { "number" "xxx" }\r
-        { "more-text" "" }\r
-    } params set\r
-\r
-    H{ } clone values set\r
-\r
-    [ t ] [ <test-form> (validate-form) ] unit-test\r
-\r
-    [ "fdafsa" ] [ "text" value ] unit-test\r
-\r
-    [ t ] [ "number" value validation-error? ] unit-test\r
-] with-scope\r
-\r
-[\r
-    [ ] [\r
-        "n" <number>\r
-            0 >>min-value\r
-            10 >>max-value\r
-        "n" set\r
-    ] unit-test\r
-\r
-    [ "123" ] [\r
-        "123" "n" get validate value>>\r
-    ] unit-test\r
-    \r
-    [ ] [ "i" <integer> "i" set ] unit-test\r
-\r
-    [ 3 ] [\r
-        "3" "i" get validate\r
-    ] unit-test\r
-    \r
-    [ t ] [\r
-        "3.9" "i" get validate validation-error?\r
-    ] unit-test\r
-\r
-    H{ } clone values set\r
-\r
-    [ ] [ 3 "i" set-value ] unit-test\r
-\r
-    [ "3" ] [ [ "i" get render-view ] with-string-writer ] unit-test\r
-\r
-    [ ] [ [ "i" get render-edit ] with-null-stream ] unit-test\r
-\r
-    [ ] [ "t" <text> "t" set ] unit-test\r
-\r
-    [ ] [ "hello world" "t" set-value ] unit-test\r
-\r
-    [ ] [ [ "t" get render-edit ] with-null-stream ] unit-test\r
-] with-scope\r
-\r
-[ t ] [ "wake up sheeple" dup "n" <text> validate = ] unit-test\r
-\r
-[ ] [ "password" <password> "p" set ] unit-test\r
-\r
-[ ] [ "pub-date" <date> "d" set ] unit-test\r
diff --git a/extra/http/server/components/components.factor b/extra/http/server/components/components.factor
deleted file mode 100755 (executable)
index 7f2a5a9..0000000
+++ /dev/null
@@ -1,401 +0,0 @@
-! Copyright (C) 2008 Slava Pestov
-! See http://factorcode.org/license.txt for BSD license.
-USING: accessors namespaces kernel io math.parser assocs classes
-words classes.tuple arrays sequences splitting mirrors
-hashtables fry locals combinators continuations math
-calendar.format html html.elements xml.entities
-http.server.validators ;
-IN: http.server.components
-
-! Renderer protocol
-GENERIC: render-summary* ( value renderer -- )
-GENERIC: render-view* ( value renderer -- )
-GENERIC: render-edit* ( value id renderer -- )
-
-M: object render-summary* render-view* ;
-
-TUPLE: field type ;
-
-C: <field> field
-
-M: field render-view*
-    drop escape-string write ;
-
-M: field render-edit*
-    <input type>> =type =name =value input/> ;
-
-TUPLE: hidden < field ;
-
-: hidden ( -- renderer ) T{ hidden f "hidden" } ; inline
-
-! Component protocol
-SYMBOL: components
-
-TUPLE: component id required default renderer ;
-
-: component ( name -- component )
-    dup components get at
-    [ ] [ "No such component: " prepend throw ] ?if ;
-
-GENERIC: init ( component -- component )
-
-M: component init ;
-
-GENERIC: validate* ( value component -- result )
-GENERIC: component-string ( value component -- string )
-
-SYMBOL: values
-
-: value values get at ;
-
-: set-value values get set-at ;
-
-: blank-values H{ } clone values set ;
-
-: from-tuple <mirror> values set ;
-
-: values-tuple values get mirror-object ;
-
-: render-view-or-summary ( component -- value renderer )
-    [ id>> value ] [ component-string ] [ renderer>> ] tri ;
-
-: render-view ( component -- )
-    render-view-or-summary render-view* ;
-
-: render-summary ( component -- )
-    render-view-or-summary render-summary* ;
-
-<PRIVATE
-
-: render-edit-string ( string component -- )
-    [ id>> ] [ renderer>> ] bi render-edit* ;
-
-: render-edit-error ( component -- )
-    [ id>> value ] keep
-    [ [ value>> ] dip render-edit-string ]
-    [ drop reason>> render-error ] 2bi ;
-
-: value-or-default ( component -- value )
-    [ id>> value ] [ default>> ] bi or ;
-
-: render-edit-value ( component -- )
-    [ value-or-default ]
-    [ component-string ]
-    [ render-edit-string ]
-    tri ;
-
-PRIVATE>
-
-: render-edit ( component -- )
-    dup id>> value validation-error?
-    [ render-edit-error ] [ render-edit-value ] if ;
-
-: validate ( value component -- result )
-    '[
-        ,
-        over empty? [
-            [ default>> [ v-default ] when* ]
-            [ required>> [ v-required ] when ]
-            bi
-        ] [ validate* ] if
-    ] with-validator ;
-
-: new-component ( id class renderer -- component )
-    swap new
-        swap >>renderer
-        swap >>id
-        init ; inline
-
-! String input fields
-TUPLE: string < component one-line min-length max-length ;
-
-: new-string ( id class -- component )
-    "text" <field> new-component
-        t >>one-line ; inline
-
-: <string> ( id -- component )
-    string new-string ;
-
-M: string validate*
-    [   one-line>> [ v-one-line   ] when  ]
-    [ min-length>> [ v-min-length ] when* ]
-    [ max-length>> [ v-max-length ] when* ]
-    tri ;
-
-M: string component-string
-    drop ;
-
-! Username fields
-TUPLE: username < string ;
-
-M: username init
-    2 >>min-length
-    20 >>max-length ;
-
-: <username> ( id -- component )
-    username new-string ;
-
-M: username validate*
-    call-next-method v-one-word ;
-
-! E-mail fields
-TUPLE: email < string ;
-
-: <email> ( id -- component )
-    email new-string
-        5 >>min-length
-        60 >>max-length ;
-
-M: email validate*
-    call-next-method dup empty? [ v-email ] unless ;
-
-! URL fields
-TUPLE: url < string ;
-
-: <url> ( id -- component )
-    url new-string
-        5 >>min-length
-        60 >>max-length ;
-
-M: url validate*
-    call-next-method dup empty? [ v-url ] unless ;
-
-! Don't send passwords back to the user
-TUPLE: password-renderer < field ;
-
-: password-renderer T{ password-renderer f "password" } ;
-
-: blank-password >r >r drop "" r> r> ;
-
-M: password-renderer render-edit*
-    blank-password call-next-method ;
-
-! Password fields
-TUPLE: password < string ;
-
-M: password init
-    6 >>min-length
-    60 >>max-length ;
-
-: <password> ( id -- component )
-    password new-string
-        password-renderer >>renderer ;
-
-M: password validate*
-    call-next-method v-one-word ;
-
-! Number fields
-TUPLE: number < string min-value max-value ;
-
-: <number> ( id -- component )
-    number new-string ;
-
-M: number validate*
-    [ v-number ] [
-        [ min-value>> [ v-min-value ] when* ]
-        [ max-value>> [ v-max-value ] when* ]
-        bi
-    ] bi* ;
-
-M: number component-string
-    drop dup [ number>string ] when ;
-
-! Integer fields
-TUPLE: integer < number ;
-
-: <integer> ( id -- component )
-    integer new-string ;
-
-M: integer validate*
-    call-next-method v-integer ;
-
-! Simple captchas
-TUPLE: captcha < string ;
-
-: <captcha> ( id -- component )
-    captcha new-string ;
-
-M: captcha validate*
-    drop v-captcha ;
-
-! Text areas
-TUPLE: text-renderer rows cols ;
-
-: new-text-renderer ( class -- renderer )
-    new
-        60 >>cols
-        20 >>rows ;
-
-: <text-renderer> ( -- renderer )
-    text-renderer new-text-renderer ;
-
-M: text-renderer render-view*
-    drop escape-string write ;
-
-M: text-renderer render-edit*
-    <textarea
-        [ rows>> [ number>string =rows ] when* ]
-        [ cols>> [ number>string =cols ] when* ] bi
-        [ =id   ]
-        [ =name ] bi
-    textarea>
-        escape-string write
-    </textarea> ;
-
-TUPLE: text < string ;
-
-: new-text ( id class -- component )
-    new-string
-        f >>one-line
-        <text-renderer> >>renderer ;
-
-: <text> ( id -- component )
-    text new-text ;
-
-! HTML text component
-TUPLE: html-text-renderer < text-renderer ;
-
-: <html-text-renderer> ( -- renderer )
-    html-text-renderer new-text-renderer ;
-
-M: html-text-renderer render-view*
-    drop escape-string write ;
-
-TUPLE: html-text < text ;
-
-: <html-text> ( id -- component )
-    html-text new-text
-        <html-text-renderer> >>renderer ;
-
-! Date component
-TUPLE: date < string ;
-
-: <date> ( id -- component )
-    date new-string ;
-
-M: date component-string
-    drop timestamp>string ;
-
-! Link components
-
-GENERIC: link-title ( obj -- string )
-GENERIC: link-href ( obj -- url )
-
-SINGLETON: link-renderer
-
-M: link-renderer render-view*
-    drop <a dup link-href =href a> link-title escape-string write </a> ;
-
-TUPLE: link < string ;
-
-: <link> ( id -- component )
-    link new-string
-        link-renderer >>renderer ;
-
-! List components
-SYMBOL: +plain+
-SYMBOL: +ordered+
-SYMBOL: +unordered+
-
-TUPLE: list-renderer component type ;
-
-C: <list-renderer> list-renderer
-
-: render-plain-list ( seq component quot -- )
-    '[ , component>> renderer>> @ ] each ; inline
-
-: render-li-list ( seq component quot -- )
-    '[ <li> @ </li> ] render-plain-list ; inline
-
-: render-ordered-list ( seq quot component -- )
-    <ol> render-li-list </ol> ; inline
-
-: render-unordered-list ( seq quot component -- )
-    <ul> render-li-list </ul> ; inline
-
-: render-list ( value renderer quot -- )
-    over type>> {
-        { +plain+     [ render-plain-list ] }
-        { +ordered+   [ render-ordered-list ] }
-        { +unordered+ [ render-unordered-list ] }
-    } case ; inline
-
-M: list-renderer render-view*
-    [ render-view* ] render-list ;
-
-M: list-renderer render-summary*
-    [ render-summary* ] render-list ;
-
-TUPLE: list < component ;
-
-: <list> ( id component type -- list )
-    <list-renderer> list swap new-component ;
-
-M: list component-string drop ;
-
-! Choice
-TUPLE: choice-renderer choices ;
-
-C: <choice-renderer> choice-renderer
-
-M: choice-renderer render-view*
-    drop escape-string write ;
-
-: render-option ( text selected? -- )
-    <option [ "true" =selected ] when option>
-        escape-string write
-    </option> ;
-
-: render-options ( options selected -- )
-    '[ dup , member? render-option ] each ;
-
-M: choice-renderer render-edit*
-    <select swap =name select>
-        choices>> swap 1array render-options
-    </select> ;
-
-TUPLE: choice < string ;
-
-: <choice> ( id choices -- component )
-    swap choice new-string
-        swap <choice-renderer> >>renderer ;
-
-! Menu
-TUPLE: menu-renderer choices size ;
-
-: <menu-renderer> ( choices -- renderer )
-    5 menu-renderer boa ;
-
-M:: menu-renderer render-edit* ( value id renderer -- )
-    <select
-        renderer size>> [ number>string =size ] when*
-        id =name
-        "true" =multiple
-    select>
-        renderer choices>> value render-options
-    </select> ;
-
-TUPLE: menu < string ;
-
-: <menu> ( id choices -- component )
-    swap menu new-string
-        swap <menu-renderer> >>renderer ;
-
-! Checkboxes
-TUPLE: checkbox-renderer label ;
-
-C: <checkbox-renderer> checkbox-renderer
-
-M: checkbox-renderer render-edit*
-    <input
-        "checkbox" =type
-        swap =id
-        swap [ "true" =selected ] when
-    input>
-        label>> escape-string write
-    </input> ;
-
-TUPLE: checkbox < string ;
-
-: <checkbox> ( id label -- component )
-    checkbox swap <checkbox-renderer> new-component ;
diff --git a/extra/http/server/components/farkup/farkup.factor b/extra/http/server/components/farkup/farkup.factor
deleted file mode 100755 (executable)
index 87b7170..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-! Copyright (C) 2008 Slava Pestov\r
-! See http://factorcode.org/license.txt for BSD license.\r
-USING: splitting kernel io sequences farkup accessors\r
-http.server.components xml.entities ;\r
-IN: http.server.components.farkup\r
-\r
-TUPLE: farkup-renderer < text-renderer ;\r
-\r
-: <farkup-renderer> ( -- renderer )\r
-    farkup-renderer new-text-renderer ;\r
-\r
-M: farkup-renderer render-view*\r
-    drop string-lines "\n" join convert-farkup write ;\r
-\r
-: <farkup> ( id -- component )\r
-    <text>\r
-        <farkup-renderer> >>renderer ;\r
diff --git a/extra/http/server/components/inspector/inspector.factor b/extra/http/server/components/inspector/inspector.factor
deleted file mode 100644 (file)
index 42366b5..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-! Copyright (C) 2008 Slava Pestov
-! See http://factorcode.org/license.txt for BSD license.
-USING: splitting kernel io sequences inspector accessors
-http.server.components xml.entities html ;
-IN: http.server.components.inspector
-
-SINGLETON: inspector-renderer
-
-M: inspector-renderer render-view*
-    drop [ describe ] with-html-stream ;
-
-TUPLE: inspector < component ;
-
-M: inspector component-string drop ;
-
-: <inspector> ( id -- component )
-    inspector inspector-renderer new-component ;
diff --git a/extra/http/server/components/test/form.fhtml b/extra/http/server/components/test/form.fhtml
deleted file mode 100755 (executable)
index d3f5a12..0000000
+++ /dev/null
@@ -1 +0,0 @@
-\r
diff --git a/extra/http/server/crud/crud.factor b/extra/http/server/crud/crud.factor
deleted file mode 100755 (executable)
index 28c1b02..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-! Copyright (C) 2008 Slava Pestov.
-! See http://factorcode.org/license.txt for BSD license.
-USING: kernel namespaces db.tuples math.parser
-accessors fry locals hashtables
-http.server
-http.server.actions
-http.server.components
-http.server.forms
-http.server.validators ;
-IN: http.server.crud
-
-:: <view-action> ( form ctor -- action )
-    <action>
-        { { "id" [ v-number ] } } >>get-params
-
-        [ "id" get ctor call select-tuple from-tuple ] >>init
-
-        [ form view-form ] >>display ;
-
-: <id-redirect> ( id next -- response )
-    swap number>string "id" associate <standard-redirect> ;
-
-:: <edit-action> ( form ctor next -- action )
-    <action>
-        { { "id" [ [ v-number ] v-optional ] } } >>get-params
-
-        [
-            "id" get ctor call
-
-            "id" get
-            [ select-tuple from-tuple ]
-            [ from-tuple form set-defaults ]
-            if
-        ] >>init
-
-        [ form edit-form ] >>display
-
-        [
-            f ctor call from-tuple
-
-            form validate-form
-
-            values-tuple
-            "id" value [ update-tuple ] [ insert-tuple ] if
-
-            "id" value next <id-redirect>
-        ] >>submit ;
-
-:: <delete-action> ( ctor next -- action )
-    <action>
-        { { "id" [ v-number ] } } >>post-params
-
-        [
-            "id" get ctor call delete-tuples
-
-            next f <standard-redirect>
-        ] >>submit ;
-
-:: <list-action> ( form ctor -- action )
-    <action>
-        [
-            blank-values
-
-            f ctor call select-tuples "list" set-value
-
-            form view-form
-        ] >>display ;
index d0bd4494570a50df77ae0bf4f584d17ddb35cc90..73d4c35e2c98b4e9b53f151a58af2613dcb45912 100755 (executable)
@@ -6,7 +6,7 @@ IN: http.server.db
 \r
 TUPLE: db-persistence < filter-responder pool ;\r
 \r
-: <db-persistence> ( responder db params -- responder' )\r
+: <db-persistence> ( responder params db -- responder' )\r
     <db-pool> db-persistence boa ;\r
 \r
 M: db-persistence call-responder*\r
diff --git a/extra/http/server/forms/forms.factor b/extra/http/server/forms/forms.factor
deleted file mode 100644 (file)
index 92fb25b..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-! Copyright (C) 2008 Slava Pestov
-! See http://factorcode.org/license.txt for BSD license.
-USING: kernel accessors assocs namespaces io.files sequences fry
-http.server
-http.server.actions
-http.server.components
-http.server.validators
-http.server.templating ;
-IN: http.server.forms
-
-TUPLE: form < component
-view-template edit-template summary-template
-components ;
-
-M: form init V{ } clone >>components ;
-
-: <form> ( id -- form )
-    form f new-component
-        dup >>renderer ;
-
-: add-field ( form component -- form )
-    dup id>> pick components>> set-at ;
-
-: set-components ( form -- )
-    components>> components set ;
-
-: with-form ( form quot -- )
-    [ [ set-components ] [ call ] bi* ] with-scope ; inline
-
-: set-defaults ( form -- )
-    [
-        components get [
-            swap values get [
-                swap default>> or
-            ] change-at
-        ] assoc-each
-    ] with-form ;
-
-: <form-response> ( form template -- response )
-    [ components>> components set ] [ <html-content> ] bi* ;
-
-: view-form ( form -- response )
-    dup view-template>> <form-response> ;
-
-: edit-form ( form -- response )
-    dup edit-template>> <form-response> ;
-
-: validate-param ( id component -- )
-    [ [ params get at ] [ validate ] bi* ]
-    [ drop set-value ] 2bi ;
-
-: (validate-form) ( form -- error? )
-    [
-        validation-failed? off
-        components get [ validate-param ] assoc-each
-        validation-failed? get
-    ] with-form ;
-
-: validate-form ( form -- )
-    (validate-form) [ validation-failed ] when ;
-
-: render-form ( value form template -- )
-    [
-        [ from-tuple ]
-        [ set-components ]
-        [ call-template ]
-        tri*
-    ] with-scope ;
-
-M: form component-string drop ;
-
-M: form render-summary*
-    dup summary-template>> render-form ;
-
-M: form render-view*
-    dup view-template>> render-form ;
-
-M: form render-edit*
-    nip dup edit-template>> render-form ;
index af27eda527d7524ed83244ebe49e579eafdea6b7..0aed425adecd321d4744026baa9c48ec96e2dff8 100755 (executable)
@@ -31,7 +31,7 @@ C: <mock-responder> mock-responder
 M: mock-responder call-responder*
     nip
     path>> on
-    "text/plain" <content> ;
+    [ ] <text-content> ;
 
 : check-dispatch ( tag path -- ? )
     H{ } clone base-paths set
@@ -84,7 +84,7 @@ C: <path-check-responder> path-check-responder
 
 M: path-check-responder call-responder*
     drop
-    "text/plain" <content> swap >array >>body ;
+    >array <text-content> ;
 
 [ { "c" } ] [
     H{ } clone base-paths set
@@ -125,7 +125,7 @@ C: <base-path-check-responder> base-path-check-responder
 M: base-path-check-responder call-responder*
     2drop
     "$funny-dispatcher" resolve-base-path
-    "text/plain" <content> swap >>body ;
+    <text-content> ;
 
 [ ] [
     <dispatcher>
index 4e561220f9b9e304cbd1b1169e1cb9343bca785e..d68c66b829643629a4060e740c668e424069818d 100755 (executable)
@@ -1,10 +1,11 @@
 ! Copyright (C) 2003, 2008 Slava Pestov.
 ! See http://factorcode.org/license.txt for BSD license.
 USING: assocs kernel namespaces io io.timeouts strings splitting
-threads sequences prettyprint io.server logging calendar
-http html html.elements accessors math.parser combinators.lib
-tools.vocabs debugger continuations random combinators
-destructors io.encodings.8-bit fry classes words ;
+threads sequences prettyprint io.server logging calendar http
+html.streams html.elements accessors math.parser
+combinators.lib tools.vocabs debugger continuations random
+combinators destructors io.encodings.8-bit fry classes words
+math rss json.writer ;
 IN: http.server
 
 ! path is a sequence of path component strings
@@ -18,14 +19,27 @@ GENERIC: call-responder* ( path responder -- response )
         { "POST" [ post-data>> ] }
     } case ;
 
-: <content> ( content-type -- response )
+: <content> ( body content-type -- response )
     <response>
         200 >>code
         "Document follows" >>message
-        swap >>content-type ;
+        swap >>content-type
+        swap >>body ;
 
-: <html-content> ( quot -- response )
-    "text/html" <content> swap >>body ;
+: <text-content> ( body -- response )
+    "text/plain" <content> ;
+
+: <html-content> ( body -- response )
+    "text/html" <content> ;
+
+: <xml-content> ( body -- response )
+    "text/xml" <content> ;
+
+: <feed-content> ( feed -- response )
+    '[ , feed>xml ] "text/xml" <content> ;
+
+: <json-content> ( obj -- response )
+    '[ , >json ] "application/json" <content> ;
 
 TUPLE: trivial-responder response ;
 
@@ -86,9 +100,7 @@ SYMBOL: link-hook
 : resolve-base-path ( string -- string' )
     "$" ?head [
         [
-            "/" split1 >r
-            base-path [ "/" % % ] each "/" %
-            r> %
+            "/" split1 [ base-path [ "/" % % ] each "/" % ] dip %
         ] "" make
     ] when ;
 
@@ -115,7 +127,7 @@ SYMBOL: form-hook
     request-url ;
 
 : replace-last-component ( path with -- path' )
-    >r "/" last-split1 drop "/" r> 3append ;
+    [ "/" last-split1 drop "/" ] dip 3append ;
 
 : relative-redirect ( to query -- url )
     request get clone
@@ -128,7 +140,7 @@ SYMBOL: form-hook
     {
         { [ over "http://" head? ] [ link>string ] }
         { [ over "/" head? ] [ absolute-redirect ] }
-        { [ over "$" head? ] [ >r resolve-base-path r> derive-url ] }
+        { [ over "$" head? ] [ [ resolve-base-path ] dip derive-url ] }
         [ relative-redirect ]
     } cond ;
 
@@ -163,7 +175,7 @@ TUPLE: dispatcher default responders ;
         [ nip ] [ drop default>> ] if
     ] [
         over first over responders>> at*
-        [ >r drop rest-slice r> ] [ drop default>> ] if
+        [ [ drop rest-slice ] dip ] [ drop default>> ] if
     ] if ;
 
 M: dispatcher call-responder* ( path dispatcher -- response )
@@ -274,9 +286,11 @@ SYMBOL: exit-continuation
     ] with-destructors ;
 
 : httpd ( port -- )
-    internet-server "http.server"
-    latin1 [ handle-client ] with-server ;
+    dup integer? [ internet-server ] when
+    "http.server" latin1
+    [ handle-client ] with-server ;
 
-: httpd-main ( -- ) 8888 httpd ;
+: httpd-main ( -- )
+    8888 httpd ;
 
 MAIN: httpd-main
index 0d98bf2150cb27da5534438200fa759b3436239e..8ea312dcb51ed69f15f62991c6983d2817e3aa35 100755 (executable)
@@ -6,7 +6,7 @@ sequences db db.sqlite continuations ;
 \r
 : with-session\r
     [\r
-        >r [ save-session-after ] [ session set ] bi r> call\r
+        [ [ save-session-after ] [ session set ] bi ] dip call\r
     ] with-destructors ; inline\r
 \r
 TUPLE: foo ;\r
@@ -18,7 +18,7 @@ M: foo init-session* drop 0 "x" sset ;
 M: foo call-responder*\r
     2drop\r
     "x" [ 1+ ] schange\r
-    "text/html" <content> [ "x" sget pprint ] >>body ;\r
+    [ "x" sget pprint ] <html-content> ;\r
 \r
 : url-responder-mock-test\r
     [\r
@@ -44,9 +44,7 @@ M: foo call-responder*
 \r
 : <exiting-action>\r
     <action>\r
-        [\r
-            "text/plain" <content> exit-with\r
-        ] >>display ;\r
+        [ [ ] <text-content> exit-with ] >>display ;\r
 \r
 [ "auth-test.db" temp-file sqlite-db delete-file ] ignore-errors\r
 \r
index 2f7a6eb221e5e319a60ee8065300a8c6365e234b..8814004589529f0e308b378e608f40d2fb58e657 100755 (executable)
@@ -1,9 +1,10 @@
 ! Copyright (C) 2004, 2008 Slava Pestov.\r
 ! See http://factorcode.org/license.txt for BSD license.\r
-USING: calendar html io io.files kernel math math.order\r
+USING: calendar io io.files kernel math math.order\r
 math.parser http http.server namespaces parser sequences strings\r
 assocs hashtables debugger http.mime sorting html.elements\r
-logging calendar.format accessors io.encodings.binary fry ;\r
+html.templates.fhtml logging calendar.format accessors\r
+io.encodings.binary fry xml.entities destructors ;\r
 IN: http.server.static\r
 \r
 ! special maps mime types to quots with effect ( path -- )\r
@@ -28,16 +29,14 @@ TUPLE: file-responder root hook special allow-listings ;
         swap >>root\r
         H{ } clone >>special ;\r
 \r
+: (serve-static) ( path mime-type -- response )\r
+    [ [ binary <file-reader> &dispose ] dip <content> ]\r
+    [ drop file-info [ size>> ] [ modified>> ] bi ] 2bi\r
+    [ "content-length" set-header ]\r
+    [ "last-modified" set-header ] bi* ;\r
+\r
 : <static> ( root -- responder )\r
-    [\r
-        <content>\r
-        swap [\r
-            file-info\r
-            [ size>> "content-length" set-header ]\r
-            [ modified>> "last-modified" set-header ] bi\r
-        ]\r
-        [ '[ , binary <file-reader> output-stream get stream-copy ] >>body ] bi\r
-    ] <file-responder> ;\r
+    [ (serve-static) ] <file-responder> ;\r
 \r
 : serve-static ( filename mime-type -- response )\r
     over modified-since?\r
@@ -57,18 +56,18 @@ TUPLE: file-responder root hook special allow-listings ;
 \r
 : file. ( name dirp -- )\r
     [ "/" append ] when\r
-    dup <a =href a> write </a> ;\r
+    dup <a =href a> escape-string write </a> ;\r
 \r
 : directory. ( path -- )\r
     dup file-name [\r
-        [ <h1> file-name write </h1> ]\r
+        [ <h1> file-name escape-string write </h1> ]\r
         [\r
             <ul>\r
                 directory sort-keys\r
                 [ <li> file. </li> ] assoc-each\r
             </ul>\r
         ] bi\r
-    ] simple-html-document ;\r
+    ] simple-page ;\r
 \r
 : list-directory ( directory -- response )\r
     file-responder get allow-listings>> [\r
@@ -99,3 +98,9 @@ M: file-responder call-responder* ( path responder -- response )
     file-responder set\r
     ".." over member?\r
     [ drop <400> ] [ "/" join serve-object ] if ;\r
+\r
+! file responder integration\r
+: enable-fhtml ( responder -- responder )\r
+    [ <fhtml> <html-content> ]\r
+    "application/x-factor-server-page"\r
+    pick special>> set-at ;\r
diff --git a/extra/http/server/templating/chloe/chloe-tests.factor b/extra/http/server/templating/chloe/chloe-tests.factor
deleted file mode 100644 (file)
index 61f72a2..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-USING: http.server.templating http.server.templating.chloe
-http.server.components http.server.boilerplate tools.test
-io.streams.string kernel sequences ascii boxes namespaces xml
-splitting ;
-IN: http.server.templating.chloe.tests
-
-[ f ] [ f parse-query-attr ] unit-test
-
-[ f ] [ "" parse-query-attr ] unit-test
-
-[ H{ { "a" "b" } } ] [
-    blank-values
-    "b" "a" set-value
-    "a" parse-query-attr
-] unit-test
-
-[ H{ { "a" "b" } { "c" "d" } } ] [
-    blank-values
-    "b" "a" set-value
-    "d" "c" set-value
-    "a,c" parse-query-attr
-] unit-test
-
-: run-template
-    with-string-writer [ "\r\n\t" member? not ] filter
-    "?>" split1 nip ; inline
-
-: test-template ( name -- template )
-    "resource:extra/http/server/templating/chloe/test/"
-    swap
-    ".xml" 3append <chloe> ;
-
-[ "Hello world" ] [
-    [
-        "test1" test-template call-template
-    ] run-template
-] unit-test
-
-[ "Blah blah" "Hello world" ] [
-    [
-        <box> title set
-        [
-            "test2" test-template call-template
-        ] run-template
-        title get box>
-    ] with-scope
-] unit-test
-
-[ "<html><head><title>Hello world</title></head><body>Blah blah</body></html>" ] [
-    [
-        [
-            "test2" test-template call-template
-        ] "test3" test-template with-boilerplate
-    ] run-template
-] unit-test
-
-: test4-aux? t ;
-
-[ "True" ] [
-    [
-        "test4" test-template call-template
-    ] run-template
-] unit-test
-
-: test5-aux? f ;
-
-[ "" ] [
-    [
-        "test5" test-template call-template
-    ] run-template
-] unit-test
-
-SYMBOL: test6-aux?
-
-[ "True" ] [
-    [
-        test6-aux? on
-        "test6" test-template call-template
-    ] run-template
-] unit-test
-
-SYMBOL: test7-aux?
-
-[ "" ] [
-    [
-        test7-aux? off
-        "test7" test-template call-template
-    ] run-template
-] unit-test
diff --git a/extra/http/server/templating/chloe/chloe.factor b/extra/http/server/templating/chloe/chloe.factor
deleted file mode 100644 (file)
index c3d93f5..0000000
+++ /dev/null
@@ -1,272 +0,0 @@
-! Copyright (C) 2008 Slava Pestov.
-! See http://factorcode.org/license.txt for BSD license.
-USING: accessors kernel sequences combinators kernel namespaces
-classes.tuple assocs splitting words arrays memoize
-io io.files io.encodings.utf8 io.streams.string
-unicode.case tuple-syntax html html.elements
-multiline xml xml.data xml.writer xml.utilities
-http.server
-http.server.auth
-http.server.flows
-http.server.actions
-http.server.components
-http.server.sessions
-http.server.templating
-http.server.boilerplate ;
-IN: http.server.templating.chloe
-
-! Chloe is Ed's favorite web designer
-
-TUPLE: chloe path ;
-
-C: <chloe> chloe
-
-DEFER: process-template
-
-: chloe-ns "http://factorcode.org/chloe/1.0" ; inline
-
-: chloe-attrs-only ( assoc -- assoc' )
-    [ drop name-url chloe-ns = ] assoc-filter ;
-
-: non-chloe-attrs-only ( assoc -- assoc' )
-    [ drop name-url chloe-ns = not ] assoc-filter ;
-
-: chloe-tag? ( tag -- ? )
-    {
-        { [ dup tag? not ] [ f ] }
-        { [ dup url>> chloe-ns = not ] [ f ] }
-        [ t ]
-    } cond nip ;
-
-SYMBOL: tags
-
-MEMO: chloe-name ( string -- name )
-    name new
-        swap >>tag
-        chloe-ns >>url ;
-
-: required-attr ( tag name -- value )
-    dup chloe-name rot at*
-    [ nip ] [ drop " attribute is required" append throw ] if ;
-
-: optional-attr ( tag name -- value )
-    chloe-name swap at ;
-
-: children>string ( tag -- string )
-    [ [ process-template ] each ] with-string-writer ;
-
-: title-tag ( tag -- )
-    children>string set-title ;
-
-: write-title-tag ( tag -- )
-    drop
-    "head" tags get member? "title" tags get member? not and
-    [ <title> write-title </title> ] [ write-title ] if ;
-
-: style-tag ( tag -- )
-    dup "include" optional-attr dup [
-        swap children>string empty? [
-            "style tag cannot have both an include attribute and a body" throw
-        ] unless
-        utf8 file-contents
-    ] [
-        drop children>string
-    ] if add-style ;
-
-: write-style-tag ( tag -- )
-    drop <style> write-style </style> ;
-
-: atom-tag ( tag -- )
-    [ "title" required-attr ]
-    [ "href" required-attr ]
-    bi set-atom-feed ;
-
-: write-atom-tag ( tag -- )
-    drop
-    "head" tags get member? [
-        write-atom-feed
-    ] [
-        atom-feed get value>> second write
-    ] if ;
-
-: component-attr ( tag -- name )
-    "component" required-attr ;
-
-: view-tag ( tag -- )
-    component-attr component render-view ;
-
-: edit-tag ( tag -- )
-    component-attr component render-edit ;
-
-: summary-tag ( tag -- )
-    component-attr component render-summary ;
-
-: parse-query-attr ( string -- assoc )
-    dup empty?
-    [ drop f ] [ "," split [ dup value ] H{ } map>assoc ] if ;
-
-: flow-attr ( tag -- )
-    "flow" optional-attr {
-        { "none" [ flow-id off ] }
-        { "begin" [ begin-flow ] }
-        { "current" [ ] }
-        { f [ ] }
-    } case ;
-
-: session-attr ( tag -- )
-    "session" optional-attr {
-        { "none" [ session off flow-id off ] }
-        { "current" [ ] }
-        { f [ ] }
-    } case ;
-
-: a-start-tag ( tag -- )
-    [
-        <a
-        dup flow-attr
-        dup session-attr
-        dup "value" optional-attr [ value f ] [
-            [ "href" required-attr ]
-            [ "query" optional-attr parse-query-attr ]
-            bi
-        ] ?if link>string =href
-        a>
-    ] with-scope ;
-
-: process-tag-children ( tag -- )
-    [ process-template ] each ;
-
-: a-tag ( tag -- )
-    [ a-start-tag ]
-    [ process-tag-children ]
-    [ drop </a> ]
-    tri ;
-
-: form-start-tag ( tag -- )
-    [
-        [
-            <form
-            "POST" =method
-            {
-                [ flow-attr ]
-                [ session-attr ]
-                [ "action" required-attr resolve-base-path =action ]
-                [ tag-attrs non-chloe-attrs-only print-attrs ]
-            } cleave
-            form>
-        ] [
-            hidden-form-field
-            "for" optional-attr [ component render-edit ] when*
-        ] bi
-    ] with-scope ;
-
-: form-tag ( tag -- )
-    [ form-start-tag ]
-    [ process-tag-children ]
-    [ drop </form> ]
-    tri ;
-
-DEFER: process-chloe-tag
-
-STRING: button-tag-markup
-<t:form class="inline" xmlns:t="http://factorcode.org/chloe/1.0">
-    <button type="submit"></button>
-</t:form>
-;
-
-: add-tag-attrs ( attrs tag -- )
-    tag-attrs swap update ;
-
-: button-tag ( tag -- )
-    button-tag-markup string>xml delegate
-    {
-        [ >r tag-attrs chloe-attrs-only r> add-tag-attrs ]
-        [ >r tag-attrs non-chloe-attrs-only r> "button" tag-named add-tag-attrs ]
-        [ >r children>string 1array r> "button" tag-named set-tag-children ]
-        [ nip ]
-    } 2cleave process-chloe-tag ;
-
-: attr>word ( value -- word/f )
-    dup ":" split1 swap lookup
-    [ ] [ "No such word: " swap append throw ] ?if ;
-
-: attr>var ( value -- word/f )
-    attr>word dup symbol? [
-        "Must be a symbol: " swap append throw
-    ] unless ;
-
-: if-satisfied? ( tag -- ? )
-    t swap
-    {
-        [ "code"  optional-attr [ attr>word execute and ] when* ]
-        [  "var"  optional-attr [ attr>var      get and ] when* ]
-        [ "svar"  optional-attr [ attr>var     sget and ] when* ]
-        [ "uvar"  optional-attr [ attr>var     uget and ] when* ]
-        [ "value" optional-attr [ value             and ] when* ]
-    } cleave ;
-
-: if-tag ( tag -- )
-    dup if-satisfied? [ process-tag-children ] [ drop ] if ;
-
-: error-message-tag ( tag -- )
-    children>string render-error ;
-
-: process-chloe-tag ( tag -- )
-    dup name-tag {
-        { "chloe" [ [ process-template ] each ] }
-        { "title" [ title-tag ] }
-        { "write-title" [ write-title-tag ] }
-        { "style" [ style-tag ] }
-        { "write-style" [ write-style-tag ] }
-        { "atom" [ atom-tag ] }
-        { "write-atom" [ write-atom-tag ] }
-        { "view" [ view-tag ] }
-        { "edit" [ edit-tag ] }
-        { "summary" [ summary-tag ] }
-        { "a" [ a-tag ] }
-        { "form" [ form-tag ] }
-        { "button" [ button-tag ] }
-        { "error-message" [ error-message-tag ] }
-        { "validation-message" [ drop render-validation-message ] }
-        { "if" [ if-tag ] }
-        { "comment" [ drop ] }
-        { "call-next-template" [ drop call-next-template ] }
-        [ "Unknown chloe tag: " swap append throw ]
-    } case ;
-
-: process-tag ( tag -- )
-    {
-        [ name-tag >lower tags get push ]
-        [ write-start-tag ]
-        [ process-tag-children ]
-        [ write-end-tag ]
-        [ drop tags get pop* ]
-    } cleave ;
-
-: process-template ( xml -- )
-    {
-        { [ dup [ chloe-tag? ] is? ] [ process-chloe-tag ] }
-        { [ dup [ tag? ] is? ] [ process-tag ] }
-        { [ t ] [ write-item ] }
-    } cond ;
-
-: process-chloe ( xml -- )
-    [
-        V{ } clone tags set
-
-        nested-template? get [
-            process-template
-        ] [
-            {
-                [ xml-prolog write-prolog ]
-                [ xml-before write-chunk  ]
-                [ process-template        ]
-                [ xml-after write-chunk   ]
-            } cleave
-        ] if
-    ] with-scope ;
-
-M: chloe call-template*
-    path>> utf8 <file-reader> read-xml process-chloe ;
-
-INSTANCE: chloe template
diff --git a/extra/http/server/templating/chloe/test/test1.xml b/extra/http/server/templating/chloe/test/test1.xml
deleted file mode 100644 (file)
index daccd57..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-       Hello world
-</t:chloe>
diff --git a/extra/http/server/templating/chloe/test/test2.xml b/extra/http/server/templating/chloe/test/test2.xml
deleted file mode 100644 (file)
index 05b9dde..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-       <t:title>Hello world</t:title>
-       Blah blah
-</t:chloe>
diff --git a/extra/http/server/templating/chloe/test/test3-aux.xml b/extra/http/server/templating/chloe/test/test3-aux.xml
deleted file mode 100644 (file)
index 99f61af..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-       <t:title>Hello world</t:title>
-</t:chloe>
diff --git a/extra/http/server/templating/chloe/test/test3.xml b/extra/http/server/templating/chloe/test/test3.xml
deleted file mode 100644 (file)
index 845dd35..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-       <html>
-               <head>
-                       <t:write-title />
-               </head>
-               <body>
-                       <t:call-next-template />
-               </body>
-       </html>
-</t:chloe>
diff --git a/extra/http/server/templating/chloe/test/test4.xml b/extra/http/server/templating/chloe/test/test4.xml
deleted file mode 100644 (file)
index dd9b232..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <t:if t:code="http.server.templating.chloe.tests:test4-aux?">
-               True
-       </t:if>
-
-</t:chloe>
diff --git a/extra/http/server/templating/chloe/test/test5.xml b/extra/http/server/templating/chloe/test/test5.xml
deleted file mode 100644 (file)
index 3bd39e4..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <t:if t:code="http.server.templating.chloe.tests:test5-aux?">
-               True
-       </t:if>
-
-</t:chloe>
diff --git a/extra/http/server/templating/chloe/test/test6.xml b/extra/http/server/templating/chloe/test/test6.xml
deleted file mode 100644 (file)
index 56234a5..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <t:if t:var="http.server.templating.chloe.tests:test6-aux?">
-               True
-       </t:if>
-
-</t:chloe>
diff --git a/extra/http/server/templating/chloe/test/test7.xml b/extra/http/server/templating/chloe/test/test7.xml
deleted file mode 100644 (file)
index a4f8e06..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <t:if t:var="http.server.templating.chloe.tests:test7-aux?">
-               True
-       </t:if>
-
-</t:chloe>
diff --git a/extra/http/server/templating/fhtml/authors.txt b/extra/http/server/templating/fhtml/authors.txt
deleted file mode 100644 (file)
index b47eafb..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-Slava Pestov
-Matthew Willis
diff --git a/extra/http/server/templating/fhtml/fhtml-tests.factor b/extra/http/server/templating/fhtml/fhtml-tests.factor
deleted file mode 100755 (executable)
index 42bec43..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-USING: io io.files io.streams.string io.encodings.utf8
-http.server.templating http.server.templating.fhtml kernel
-tools.test sequences parser ;
-IN: http.server.templating.fhtml.tests
-
-: test-template ( path -- ? )
-    "resource:extra/http/server/templating/fhtml/test/"
-    prepend
-    [
-        ".fhtml" append <fhtml> [ call-template ] with-string-writer
-    ] keep
-    ".html" append utf8 file-contents = ;
-
-[ t ] [ "example" test-template ] unit-test
-[ t ] [ "bug" test-template ] unit-test
-[ t ] [ "stack" test-template ] unit-test
-
-[
-    [ ] [ "<%\n%>" parse-template drop ] unit-test
-] with-file-vocabs
diff --git a/extra/http/server/templating/fhtml/fhtml.factor b/extra/http/server/templating/fhtml/fhtml.factor
deleted file mode 100755 (executable)
index 2cc053a..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-! Copyright (C) 2005 Alex Chapman
-! Copyright (C) 2006, 2008 Slava Pestov
-! See http://factorcode.org/license.txt for BSD license.
-USING: continuations sequences kernel namespaces debugger
-combinators math quotations generic strings splitting
-accessors assocs fry
-parser io io.files io.streams.string io.encodings.utf8 source-files
-html html.elements
-http.server.static http.server http.server.templating ;
-IN: http.server.templating.fhtml
-
-: templating-vocab ( -- vocab-name ) "http.server.templating.fhtml" ;
-
-! We use a custom lexer so that %> ends a token even if not
-! followed by whitespace
-TUPLE: template-lexer < lexer ;
-
-: <template-lexer> ( lines -- lexer )
-    template-lexer new-lexer ;
-
-M: template-lexer skip-word
-    [
-        {
-            { [ 2dup nth CHAR: " = ] [ drop 1+ ] }
-            { [ 2dup swap tail-slice "%>" head? ] [ drop 2 + ] }
-            [ f skip ]
-        } cond
-    ] change-lexer-column ;
-
-DEFER: <% delimiter
-
-: check-<% ( lexer -- col )
-    "<%" over line-text>> rot column>> start* ;
-
-: found-<% ( accum lexer col -- accum )
-    [
-        over line-text>>
-        >r >r column>> r> r> subseq parsed
-        \ write-html parsed
-    ] 2keep 2 + >>column drop ;
-
-: still-looking ( accum lexer -- accum )
-    [
-        [ line-text>> ] [ column>> ] bi tail
-        parsed \ print-html parsed
-    ] keep next-line ;
-
-: parse-%> ( accum lexer -- accum )
-    dup still-parsing? [
-        dup check-<%
-        [ found-<% ] [ [ still-looking ] keep parse-%> ] if*
-    ] [
-        drop
-    ] if ;
-
-: %> lexer get parse-%> ; parsing
-
-: parse-template-lines ( lines -- quot )
-    <template-lexer> [
-        V{ } clone lexer get parse-%> f (parse-until)
-    ] with-parser ;
-
-: parse-template ( string -- quot )
-    [
-        use [ clone ] change
-        templating-vocab use+
-        string-lines parse-template-lines
-    ] with-scope ;
-
-: eval-template ( string -- ) parse-template call ;
-
-: html-error. ( error -- )
-    <pre> error. </pre> ;
-
-TUPLE: fhtml path ;
-
-C: <fhtml> fhtml
-
-M: fhtml call-template* ( filename -- )
-    '[
-        , path>> [
-            "quiet" on
-            parser-notes off
-            templating-vocab use+
-            ! so that reload works properly
-            dup source-file file set
-            utf8 file-contents
-            [ eval-template ] [ html-error. drop ] recover
-        ] with-file-vocabs
-    ] assert-depth ;
-
-! file responder integration
-: enable-fhtml ( responder -- responder )
-    [ <fhtml> serve-template ]
-    "application/x-factor-server-page"
-    pick special>> set-at ;
-
-INSTANCE: fhtml template
diff --git a/extra/http/server/templating/fhtml/test/bug.fhtml b/extra/http/server/templating/fhtml/test/bug.fhtml
deleted file mode 100644 (file)
index cb66599..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<%
-    USING: prettyprint ;
-    ! Hello world
-    5 pprint
-%>
diff --git a/extra/http/server/templating/fhtml/test/bug.html b/extra/http/server/templating/fhtml/test/bug.html
deleted file mode 100644 (file)
index 51d7b8d..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-5
-
diff --git a/extra/http/server/templating/fhtml/test/example.fhtml b/extra/http/server/templating/fhtml/test/example.fhtml
deleted file mode 100644 (file)
index 211f44a..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<% USING: math ; %>
-
-<html>
-    <head><title>Simple Embedded Factor Example</title></head>
-    <body>
-        <% 5 [ %><p>I like repetition</p><% ] times %>
-    </body>
-</html>
diff --git a/extra/http/server/templating/fhtml/test/example.html b/extra/http/server/templating/fhtml/test/example.html
deleted file mode 100644 (file)
index 9bf4a08..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-<html>
-    <head><title>Simple Embedded Factor Example</title></head>
-    <body>
-        <p>I like repetition</p><p>I like repetition</p><p>I like repetition</p><p>I like repetition</p><p>I like repetition</p>
-    </body>
-</html>
-
diff --git a/extra/http/server/templating/fhtml/test/stack.fhtml b/extra/http/server/templating/fhtml/test/stack.fhtml
deleted file mode 100644 (file)
index 399711a..0000000
+++ /dev/null
@@ -1 +0,0 @@
-The stack: <% USING: prettyprint ;  .s %>
diff --git a/extra/http/server/templating/fhtml/test/stack.html b/extra/http/server/templating/fhtml/test/stack.html
deleted file mode 100644 (file)
index ee923a6..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-The stack: 
-
diff --git a/extra/http/server/templating/templating.factor b/extra/http/server/templating/templating.factor
deleted file mode 100644 (file)
index 73f6095..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-USING: accessors kernel fry io io.encodings.utf8 io.files
-http http.server debugger prettyprint continuations ;
-IN: http.server.templating
-
-MIXIN: template
-
-GENERIC: call-template* ( template -- )
-
-ERROR: template-error template error ;
-
-M: template-error error.
-    "Error while processing template " write
-    [ template>> pprint ":" print nl ]
-    [ error>> error. ]
-    bi ;
-
-: call-template ( template -- )
-    [ call-template* ] [ template-error ] recover ;
-
-M: template write-response-body* call-template ;
-
-: template-convert ( template output -- )
-    utf8 [ call-template ] with-file-writer ;
-
-! responder integration
-: serve-template ( template -- response )
-    '[ , call-template ] <html-content> ;
diff --git a/extra/http/server/validators/validators-tests.factor b/extra/http/server/validators/validators-tests.factor
deleted file mode 100755 (executable)
index 5e84570..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-IN: http.server.validators.tests
-USING: kernel sequences tools.test http.server.validators
-accessors ;
-
-[ "foo" v-number ] must-fail
-[ 123 ] [ "123" v-number ] unit-test
-
-[ "slava@factorcode.org" ] [
-    "slava@factorcode.org" v-email
-] unit-test
-
-[ "slava+foo@factorcode.org" ] [
-    "slava+foo@factorcode.org" v-email
-] unit-test
-
-[ "slava@factorcode.o" v-email ]
-[ "invalid e-mail" = ] must-fail-with
-
-[ "sla@@factorcode.o" v-email ]
-[ "invalid e-mail" = ] must-fail-with
-
-[ "slava@factorcodeorg" v-email ]
-[ "invalid e-mail" = ] must-fail-with
-
-[ "http://www.factorcode.org" ]
-[ "http://www.factorcode.org" v-url ] unit-test
-
-[ "http:/www.factorcode.org" v-url ]
-[ "invalid URL" = ] must-fail-with
diff --git a/extra/http/server/validators/validators.factor b/extra/http/server/validators/validators.factor
deleted file mode 100755 (executable)
index 7415787..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-! Copyright (C) 2006, 2008 Slava Pestov
-! See http://factorcode.org/license.txt for BSD license.
-USING: kernel continuations sequences math namespaces sets
-math.parser assocs regexp fry unicode.categories sequences ;
-IN: http.server.validators
-
-SYMBOL: validation-failed?
-
-TUPLE: validation-error value reason ;
-
-C: <validation-error> validation-error
-
-: with-validator ( value quot -- result )
-    [ validation-failed? on <validation-error> ] recover ; inline
-
-: v-default ( str def -- str )
-    over empty? spin ? ;
-
-: v-required ( str -- str )
-    dup empty? [ "required" throw ] when ;
-
-: v-optional ( str quot -- str )
-    over empty? [ 2drop f ] [ call ] if ; inline
-
-: v-min-length ( str n -- str )
-    over length over < [
-        [ "must be at least " % # " characters" % ] "" make
-        throw
-    ] [
-        drop
-    ] if ;
-
-: v-max-length ( str n -- str )
-    over length over > [
-        [ "must be no more than " % # " characters" % ] "" make
-        throw
-    ] [
-        drop
-    ] if ;
-
-: v-number ( str -- n )
-    dup string>number [ ] [ "must be a number" throw ] ?if ;
-
-: v-integer ( n -- n )
-    dup integer? [ "must be an integer" throw ] unless ;
-
-: v-min-value ( x n -- x )
-    2dup < [
-        [ "must be at least " % # ] "" make throw
-    ] [
-        drop
-    ] if ;
-
-: v-max-value ( x n -- x )
-    2dup > [
-        [ "must be no more than " % # ] "" make throw
-    ] [
-        drop
-    ] if ;
-
-: v-regexp ( str what regexp -- str )
-    >r over r> matches?
-    [ drop ] [ "invalid " prepend throw ] if ;
-
-: v-email ( str -- str )
-    #! From http://www.regular-expressions.info/email.html
-    "e-mail"
-    R' [A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}'i
-    v-regexp ;
-
-: v-url ( str -- str )
-    "URL"
-    R' (ftp|http|https)://(\w+:?\w*@)?(\S+)(:[0-9]+)?(/|/([\w#!:.?+=&%@!\-/]))?'
-    v-regexp ;
-
-: v-captcha ( str -- str )
-    dup empty? [ "must remain blank" throw ] unless ;
-
-: v-one-line ( str -- str )
-    dup "\r\n" intersect empty?
-    [ "must be a single line" throw ] unless ;
-
-: v-one-word ( str -- str )
-    dup [ alpha? ] all?
-    [ "must be a single word" throw ] unless ;
index 993aff52009fdd93d34223bcbe72b2698c5e2e9a..1a7462f304ed3960fe2e0c1ccf77daebdcb9ed2c 100755 (executable)
@@ -64,9 +64,11 @@ M: winnt add-completion ( win32-handle -- )
 
 : handle-overlapped ( timeout -- ? )
     wait-for-overlapped [
-        >r drop GetLastError
-        [ 1array ] [ expected-io-error? ] bi
-        [ r> 2drop f ] [ r> resume-callback t ] if
+        dup [
+            >r drop GetLastError 1array r> resume-callback t
+        ] [
+            2drop f
+        ] if
     ] [
         resume-callback t
     ] if ;
diff --git a/extra/irc/authors.txt b/extra/irc/authors.txt
deleted file mode 100644 (file)
index 5674120..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-Doug Coleman
-Slava Pestov
diff --git a/extra/irc/client/authors.txt b/extra/irc/client/authors.txt
new file mode 100644 (file)
index 0000000..5674120
--- /dev/null
@@ -0,0 +1,2 @@
+Doug Coleman
+Slava Pestov
diff --git a/extra/irc/client/client.factor b/extra/irc/client/client.factor
new file mode 100644 (file)
index 0000000..19dca48
--- /dev/null
@@ -0,0 +1,275 @@
+! Copyright (C) 2007 Doug Coleman, Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+USING: arrays combinators concurrency.mailboxes concurrency.futures io
+       io.encodings.8-bit io.sockets kernel namespaces sequences
+       sequences.lib splitting threads calendar classes.tuple
+       ascii assocs accessors destructors ;
+IN: irc.client
+
+! ======================================
+! Setup and running objects
+! ======================================
+
+SYMBOL: current-irc-client
+
+: irc-port 6667 ; ! Default irc port
+
+! "setup" objects
+TUPLE: irc-profile server port nickname password ;
+C: <irc-profile> irc-profile
+
+TUPLE: irc-channel-profile name password ;
+: <irc-channel-profile> ( -- irc-channel-profile ) irc-channel-profile new ;
+
+! "live" objects
+TUPLE: nick name channels log ;
+C: <nick> nick
+
+TUPLE: irc-client profile nick stream in-messages out-messages join-messages
+       listeners is-running ;
+: <irc-client> ( profile -- irc-client )
+    f V{ } clone V{ } clone <nick>
+    f <mailbox> <mailbox> <mailbox> H{ } clone f irc-client boa ;
+
+TUPLE: irc-listener in-messages out-messages ;
+: <irc-listener> ( -- irc-listener )
+    <mailbox> <mailbox> irc-listener boa ;
+
+! ======================================
+! Message objects
+! ======================================
+
+SINGLETON: irc-end ! Message used when the client isn't running anymore
+
+TUPLE: irc-message line prefix command parameters trailing timestamp ;
+TUPLE: logged-in < irc-message name ;
+TUPLE: ping < irc-message ;
+TUPLE: join < irc-message ;
+TUPLE: part < irc-message name channel ;
+TUPLE: quit < irc-message ;
+TUPLE: privmsg < irc-message name ;
+TUPLE: kick < irc-message channel who ;
+TUPLE: roomlist < irc-message channel names ;
+TUPLE: nick-in-use < irc-message asterisk name ;
+TUPLE: notice < irc-message type ;
+TUPLE: mode < irc-message name channel mode ;
+TUPLE: unhandled < irc-message ;
+
+<PRIVATE
+
+! ======================================
+! Shortcuts
+! ======================================
+
+: irc-client> ( -- irc-client ) current-irc-client get ;
+: irc-stream> ( -- stream ) irc-client> stream>> ;
+: irc-write ( s -- ) irc-stream> stream-write ;
+: irc-print ( s -- ) irc-stream> [ stream-print ] keep stream-flush ;
+
+! ======================================
+! IRC client messages
+! ======================================
+
+: /NICK ( nick -- )
+    "NICK " irc-write irc-print ;
+
+: /LOGIN ( nick -- )
+    dup /NICK
+    "USER " irc-write irc-write
+    " hostname servername :irc.factor" irc-print ;
+
+: /CONNECT ( server port -- stream )
+    <inet> latin1 <client> drop ;
+
+: /JOIN ( channel password -- )
+    "JOIN " irc-write
+    [ " :" swap 3append ] when* irc-print ;
+
+: /PART ( channel text -- )
+    [ "PART " irc-write irc-write ] dip
+    " :" irc-write irc-print ;
+
+: /KICK ( channel who -- )
+    [ "KICK " irc-write irc-write ] dip
+    " " irc-write irc-print ;
+
+: /PRIVMSG ( nick line -- )
+    [ "PRIVMSG " irc-write irc-write ] dip
+    " :" irc-write irc-print ;
+
+: /ACTION ( nick line -- )
+    [ 1 , "ACTION " % % 1 , ] "" make /PRIVMSG ;
+
+: /QUIT ( text -- )
+    "QUIT :" irc-write irc-print ;
+
+: /PONG ( text -- )
+    "PONG " irc-write irc-print ;
+
+! ======================================
+! Server message handling
+! ======================================
+
+USE: prettyprint
+
+GENERIC: handle-incoming-irc ( irc-message -- )
+
+M: irc-message handle-incoming-irc ( irc-message -- )
+    . ;
+
+M: logged-in handle-incoming-irc ( logged-in -- )
+    name>> irc-client> nick>> (>>name) ;
+
+M: ping handle-incoming-irc ( ping -- )
+    trailing>> /PONG ;
+
+M: nick-in-use handle-incoming-irc ( nick-in-use -- )
+    name>> "_" append /NICK ;
+
+M: privmsg handle-incoming-irc ( privmsg -- )
+    dup name>> irc-client> listeners>> at
+    [ in-messages>> mailbox-put ] [ drop ] if* ;
+
+M: join handle-incoming-irc ( join -- )
+    irc-client> join-messages>> mailbox-put ;
+
+! ======================================
+! Client message handling
+! ======================================
+
+GENERIC: handle-outgoing-irc ( obj -- )
+
+M: privmsg handle-outgoing-irc ( privmsg -- )
+   [ name>> ] [ trailing>> ] bi /PRIVMSG ;
+
+! ======================================
+! Message parsing
+! ======================================
+
+: split-at-first ( seq separators -- before after )
+    dupd [ member? ] curry find
+        [ cut 1 tail ]
+        [ swap ]
+    if ;
+
+: remove-heading-: ( seq -- seq ) dup ":" head? [ 1 tail ] when ;
+
+: parse-name ( string -- string )
+    remove-heading-: "!" split-at-first drop ;
+
+: split-prefix ( string -- string/f string )
+    dup ":" head?
+        [ remove-heading-: " " split1 ]
+        [ f swap ]
+    if ;
+
+: split-trailing ( string -- string string/f )
+    ":" split1 ;
+
+: string>irc-message ( string -- object )
+    dup split-prefix split-trailing
+    [ [ blank? ] trim " " split unclip swap ] dip
+    now irc-message boa ;
+
+: parse-irc-line ( string -- message )
+    string>irc-message
+    dup command>> {
+        { "PING" [ \ ping ] }
+        { "NOTICE" [ \ notice ] }
+        { "001" [ \ logged-in ] }
+        { "433" [ \ nick-in-use ] }
+        { "JOIN" [ \ join ] }
+        { "PART" [ \ part ] }
+        { "PRIVMSG" [ \ privmsg ] }
+        { "QUIT" [ \ quit ] }
+        { "MODE" [ \ mode ] }
+        { "KICK" [ \ kick ] }
+        [ drop \ unhandled ]
+    } case
+    [ [ tuple-slots ] [ parameters>> ] bi append ] dip prefix >tuple ;
+
+! ======================================
+! Reader/Writer
+! ======================================
+
+: stream-readln-or-close ( stream -- str/f )
+    dup stream-readln [ nip ] [ dispose f ] if* ;
+
+: handle-reader-message ( irc-message -- )
+    irc-client> in-messages>> mailbox-put ;
+
+: handle-stream-close ( -- )
+    irc-client> f >>is-running in-messages>> irc-end swap mailbox-put ;
+
+: reader-loop ( -- )
+    irc-client> stream>> stream-readln-or-close [
+        parse-irc-line handle-reader-message
+    ] [
+        handle-stream-close
+    ] if* ;
+
+: writer-loop ( -- )
+    irc-client> out-messages>> mailbox-get handle-outgoing-irc ;
+
+! ======================================
+! Processing loops
+! ======================================
+
+: in-multiplexer-loop ( -- )
+    irc-client> in-messages>> mailbox-get handle-incoming-irc ;
+
+! FIXME: Hack, this should be handled better
+GENERIC: add-name ( name obj -- obj )
+M: object add-name nip ;
+M: privmsg add-name swap >>name ;
+    
+: listener-loop ( name -- ) ! FIXME: take different values from the stack?
+    dup irc-client> listeners>> at [
+        out-messages>> mailbox-get add-name
+        irc-client> out-messages>>
+        mailbox-put
+    ] [ drop ] if* ;
+
+: spawn-irc-loop ( quot name -- )
+    [ [ irc-client> is-running>> ] compose ] dip
+    spawn-server drop ;
+
+: spawn-irc ( -- )
+    [ reader-loop ] "irc-reader-loop" spawn-irc-loop
+    [ writer-loop ] "irc-writer-loop" spawn-irc-loop
+    [ in-multiplexer-loop ] "in-multiplexer-loop" spawn-irc-loop ;
+
+! ======================================
+! Listener join request handling
+! ======================================
+
+: make-registered-listener ( join -- listener )
+    <irc-listener> swap trailing>>
+    dup [ listener-loop ] curry "listener" spawn-irc-loop
+    [ irc-client> listeners>> set-at ] curry keep ;
+
+: make-join-future ( name -- future )
+    [ [ swap trailing>> = ] curry ! compare name with channel name
+      irc-client> join-messages>> 60 seconds rot mailbox-get-timeout?
+      make-registered-listener ]
+    curry future ;
+
+PRIVATE>
+
+: (connect-irc) ( irc-client -- )
+    [ profile>> [ server>> ] keep port>> /CONNECT ] keep
+    swap >>stream
+    t >>is-running drop ;
+
+: connect-irc ( irc-client -- )
+    dup current-irc-client [
+        [ (connect-irc) ] [ profile>> nickname>> /LOGIN ] bi
+        spawn-irc
+    ] with-variable ;
+
+: listen-to ( irc-client name -- future )
+    swap current-irc-client [ [ f /JOIN ] keep make-join-future ] with-variable ;
+
+! shorcut for privmsgs, etc
+: sender>> ( obj -- string )
+    prefix>> parse-name ;
diff --git a/extra/irc/client/summary.txt b/extra/irc/client/summary.txt
new file mode 100644 (file)
index 0000000..a263cfe
--- /dev/null
@@ -0,0 +1 @@
+An IRC client framework
diff --git a/extra/irc/irc.factor b/extra/irc/irc.factor
deleted file mode 100755 (executable)
index 9a278fb..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-! Copyright (C) 2007 Doug Coleman, Slava Pestov.
-! See http://factorcode.org/license.txt for BSD license.
-USING: arrays calendar combinators channels concurrency.messaging fry io
-       io.encodings.8-bit io.sockets kernel math namespaces sequences
-       sequences.lib splitting strings threads
-       continuations destructors classes.tuple ascii accessors ;
-IN: irc
-
-! utils
-: split-at-first ( seq separators -- before after )
-    dupd '[ , member? ] find
-        [ cut rest ]
-        [ swap ]
-    if ;
-
-: spawn-server-linked ( quot name -- thread )
-    >r '[ , [ ] [ ] while ] r>
-    spawn-linked ;
-! ---
-
-! Default irc port
-: irc-port 6667 ;
-
-! Message used when the client isn't running anymore
-SINGLETON: irc-end
-
-! "setup" objects
-TUPLE: irc-profile server port nickname password default-channels  ;
-C: <irc-profile> irc-profile
-
-TUPLE: irc-channel-profile name password auto-rejoin ;
-C: <irc-channel-profile> irc-channel-profile
-
-! "live" objects
-TUPLE: nick name channels log ;
-C: <nick> nick
-
-TUPLE: irc-client profile nick stream stream-channel controller-channel
-       listeners is-running ;
-: <irc-client> ( profile -- irc-client )
-    f V{ } clone V{ } clone <nick>
-    f <channel> <channel> V{ } clone f irc-client boa ;
-
-USE: prettyprint
-TUPLE: irc-listener channel ;
-! FIXME: spawn-server-linked con manejo de excepciones, mandar un mensaje final (ya se maneja esto al recibir mensajes del channel? )
-! tener la opción de dejar de correr un client??
-: <irc-listener> ( quot -- irc-listener )
-    <channel> irc-listener boa swap
-    [
-        [ channel>> '[ , from ] ]
-        [ '[ , curry f spawn drop ] ]
-        bi* compose "irc-listener" spawn-server-linked drop
-    ] [ drop ] 2bi ;
-
-! TUPLE: irc-channel name topic members log attributes ;
-! C: <irc-channel> irc-channel
-
-! the delegate of all irc messages
-TUPLE: irc-message line prefix command parameters trailing timestamp ;
-C: <irc-message> irc-message
-
-! "irc message" objects
-TUPLE: logged-in < irc-message name ;
-C: <logged-in> logged-in
-
-TUPLE: ping < irc-message ;
-C: <ping> ping
-
-TUPLE: join_ < irc-message ;
-C: <join> join_
-
-TUPLE: part < irc-message name channel ;
-C: <part> part
-
-TUPLE: quit ;
-C: <quit> quit
-
-TUPLE: privmsg < irc-message name ;
-C: <privmsg> privmsg
-
-TUPLE: kick < irc-message channel who ;
-C: <kick> kick
-
-TUPLE: roomlist < irc-message channel names ;
-C: <roomlist> roomlist
-
-TUPLE: nick-in-use < irc-message name ;
-C: <nick-in-use> nick-in-use
-
-TUPLE: notice < irc-message type ;
-C: <notice> notice
-
-TUPLE: mode < irc-message name channel mode ;
-C: <mode> mode
-
-TUPLE: unhandled < irc-message ;
-C: <unhandled> unhandled
-
-SYMBOL: irc-client
-: irc-client> ( -- irc-client ) irc-client get ;
-: irc-stream> ( -- stream ) irc-client> stream>> ;
-
-: remove-heading-: ( seq -- seq ) dup ":" head? [ rest ] when ;
-
-: parse-name ( string -- string )
-    remove-heading-: "!" split-at-first drop ;
-
-: sender>> ( obj -- string )
-    prefix>> parse-name ;
-
-: split-prefix ( string -- string/f string )
-    dup ":" head?
-        [ remove-heading-: " " split1 ]
-        [ f swap ]
-    if ;
-
-: split-trailing ( string -- string string/f )
-    ":" split1 ;
-
-: string>irc-message ( string -- object )
-    dup split-prefix split-trailing
-    [ [ blank? ] trim " " split unclip swap ] dip
-    now <irc-message> ;
-
-: me? ( name -- ? )
-    irc-client> nick>> name>> = ;
-
-: irc-write ( s -- )
-    irc-stream> stream-write ;
-
-: irc-print ( s -- )
-    irc-stream> [ stream-print ] keep stream-flush ;
-
-! Irc commands    
-
-: NICK ( nick -- )
-    "NICK " irc-write irc-print ;
-
-: LOGIN ( nick -- )
-    dup NICK
-    "USER " irc-write irc-write
-    " hostname servername :irc.factor" irc-print ;
-
-: CONNECT ( server port -- stream )
-    <inet> latin1 <client> drop ;
-
-: JOIN ( channel password -- )
-    "JOIN " irc-write
-    [ " :" swap 3append ] when* irc-print ;
-
-: PART ( channel text -- )
-    [ "PART " irc-write irc-write ] dip
-    " :" irc-write irc-print ;
-
-: KICK ( channel who -- )
-    [ "KICK " irc-write irc-write ] dip
-    " " irc-write irc-print ;
-    
-: PRIVMSG ( nick line -- )
-    [ "PRIVMSG " irc-write irc-write ] dip
-    " :" irc-write irc-print ;
-
-: SAY ( nick line -- )
-    PRIVMSG ;
-
-: ACTION ( nick line -- )
-    [ 1 , "ACTION " % % 1 , ] "" make PRIVMSG ;
-
-: QUIT ( text -- )
-    "QUIT :" irc-write irc-print ;
-
-: join-channel ( channel-profile -- )
-    [ name>> ] keep password>> JOIN ;
-
-: irc-connect ( irc-client -- )
-    [ profile>> [ server>> ] keep port>> CONNECT ] keep
-    swap >>stream t >>is-running drop ;
-    
-GENERIC: handle-irc ( obj -- )
-
-M: object handle-irc ( obj -- )
-    drop ;
-
-M: logged-in handle-irc ( obj -- )
-    name>>
-    irc-client> [ nick>> swap >>name drop ] keep 
-    profile>> default-channels>> [ join-channel ] each ;
-
-M: ping handle-irc ( obj -- )
-    "PONG " irc-write
-    trailing>> irc-print ;
-
-M: nick-in-use handle-irc ( obj -- )
-    name>> "_" append NICK ;
-
-: parse-irc-line ( string -- message )
-    string>irc-message
-    dup command>> {
-        { "PING" [ \ ping ] }
-        { "NOTICE" [ \ notice ] }
-        { "001" [ \ logged-in ] }
-        { "433" [ \ nick-in-use ] }
-        { "JOIN" [ \ join_ ] }
-        { "PART" [ \ part ] }
-        { "PRIVMSG" [ \ privmsg ] }
-        { "QUIT" [ \ quit ] }
-        { "MODE" [ \ mode ] }
-        { "KICK" [ \ kick ] }
-        [ drop \ unhandled ]
-    } case
-    [ [ tuple-slots ] [ parameters>> ] bi append ] dip prefix >tuple ;
-
-! Reader
-: handle-reader-message ( irc-client irc-message -- )
-    dup handle-irc swap stream-channel>> to ;
-
-: reader-loop ( irc-client -- )
-    dup stream>> stream-readln [
-        dup print parse-irc-line handle-reader-message
-    ] [
-        f >>is-running
-        dup stream>> dispose
-        irc-end over controller-channel>> to
-        stream-channel>> irc-end swap to
-    ] if* ;
-
-! Controller commands
-GENERIC: handle-command ( obj -- )
-
-M: object handle-command ( obj -- )
-    . ;
-
-TUPLE: send-message to text ;
-C: <send-message> send-message
-M: send-message handle-command ( obj -- )
-    dup to>> swap text>> SAY ;
-
-TUPLE: send-action to text ;
-C: <send-action> send-action
-M: send-action handle-command ( obj -- )
-    dup to>> swap text>> ACTION ;
-
-TUPLE: send-quit text ;
-C: <send-quit> send-quit
-M: send-quit handle-command ( obj -- )
-    text>> QUIT ;
-
-: irc-listen ( irc-client quot -- )
-    [ listeners>> ] [ <irc-listener> ] bi* swap push ;
-
-! Controller loop
-: controller-loop ( irc-client -- )
-    controller-channel>> from handle-command ;
-
-! Multiplexer
-: multiplex-message ( irc-client message -- )
-    swap listeners>> [ channel>> ] map
-    [ '[ , , to ] "message" spawn drop ] each-with ;
-
-: multiplexer-loop ( irc-client -- )
-    dup stream-channel>> from multiplex-message ;
-
-! process looping and starting
-: (spawn-irc-loop) ( irc-client quot name -- )
-    [ over >r curry r> '[ @ , is-running>> ] ] dip
-    spawn-server-linked drop ;
-
-: spawn-irc-loop ( irc-client quot name -- )
-    '[ , , , [ (spawn-irc-loop) receive ] [ print ] recover ]
-    f spawn drop ;
-
-: spawn-irc ( irc-client -- )
-    [ [ reader-loop ] "reader-loop" spawn-irc-loop ]
-    [ [ controller-loop ] "controller-loop" spawn-irc-loop ]
-    [ [ multiplexer-loop ] "multiplexer-loop" spawn-irc-loop ]
-    tri ;
-    
-: do-irc ( irc-client -- )
-    irc-client [
-        irc-client>
-        [ irc-connect ]
-        [ profile>> nickname>> LOGIN ]
-        [ spawn-irc ]
-        tri
-    ] with-variable ;
\ No newline at end of file
diff --git a/extra/irc/summary.txt b/extra/irc/summary.txt
deleted file mode 100644 (file)
index a263cfe..0000000
+++ /dev/null
@@ -1 +0,0 @@
-An IRC client framework
diff --git a/extra/json/reader/reader-tests.factor b/extra/json/reader/reader-tests.factor
new file mode 100644 (file)
index 0000000..4b7bd56
--- /dev/null
@@ -0,0 +1,43 @@
+USING: arrays json.reader kernel multiline strings tools.test ;
+IN: json.reader.tests
+
+{ f } [ "false" json> ] unit-test
+{ t } [ "true" json> ] unit-test
+{ json-null } [ "null" json> ] unit-test
+{ 0 } [ "0" json> ] unit-test
+{ 102 } [ "102" json> ] unit-test
+{ -102 } [ "-102" json> ] unit-test
+{ 102 } [ "+102" json> ] unit-test
+{ 102.0 } [ "102.0" json> ] unit-test
+{ 102.5 } [ "102.5" json> ] unit-test
+{ 102.5 } [ "102.50" json> ] unit-test
+{ -10250 } [ "-102.5e2" json> ] unit-test
+{ -10250 } [ "-102.5E+2" json> ] unit-test
+{ 10.25 } [ "1025e-2" json> ] unit-test
+{ 0.125 } [ "0.125" json> ] unit-test
+{ -0.125 } [ "-0.125" json> ] unit-test
+
+{ " fuzzy  pickles " } [ <" " fuzzy  pickles " "> json> ] unit-test
+{ "while 1:\n\tpass" } [ <" "while 1:\n\tpass" "> json> ] unit-test
+{ 8 9 10 12 13 34 47 92 } >string 1array [ <" "\b\t\n\f\r\"\/\\" "> json> ] unit-test
+{ HEX: abcd } >string 1array [ <" "\uaBCd" "> json> ] unit-test
+
+{ { 1 "two" 3.0 } } [ <" [1, "two", 3.0] "> json> ] unit-test
+{ H{ { "US$" 1.0 } { "EU€" 1.5 } } } [ <" { "US$":1.00, "EU\u20AC":1.50 } "> json> ] unit-test
+{ H{
+    { "fib" { 1 1 2 3 5 8 H{ { "etc" "etc" } } } }
+    { "prime" { 2 3 5 7 11 13 } }
+} } [ <" {
+    "fib": [1, 1,  2,   3,     5,         8,
+        { "etc":"etc" } ],
+    "prime":
+    [ 2,3,     5,7,
+11,
+13
+]      }
+"> json> ] unit-test
+
+{ 0 } [ "      0" json> ] unit-test
+{ 0 } [ "0      " json> ] unit-test
+{ 0 } [ "   0   " json> ] unit-test
+
index 17c1b272df8c7f9c515092b7c7ddc6a3759806d5..9d6155ea78cf0488fa7a2e6c3bc85dd419dd1b6a 100755 (executable)
@@ -2,11 +2,13 @@
 ! See http://factorcode.org/license.txt for BSD license.
 USING: kernel parser-combinators namespaces sequences promises strings 
        assocs math math.parser math.vectors math.functions math.order
-       lazy-lists hashtables ascii ;
+       lists lists.lazy hashtables ascii ;
 IN: json.reader
 
 ! Grammar for JSON from RFC 4627
 
+SYMBOL: json-null
+
 : [<&>] ( quot -- quot )
   { } make unclip [ <&> ] reduce ;
 
@@ -17,8 +19,7 @@ LAZY: 'ws' ( -- parser )
   " " token 
   "\n" token <|>
   "\r" token <|>
-  "\t" token <|> 
-  "" token <|> ;
+  "\t" token <|> <*> ;
 
 LAZY: spaced ( parser -- parser )
   'ws' swap &> 'ws' <& ;
@@ -42,24 +43,39 @@ LAZY: 'value-separator' ( -- parser )
   "," token spaced ;
 
 LAZY: 'false' ( -- parser )
-  "false" token ;
+  "false" token [ drop f ] <@ ;
 
 LAZY: 'null' ( -- parser )
-  "null" token ;
+  "null" token [ drop json-null ] <@ ;
 
 LAZY: 'true' ( -- parser )
-  "true" token ;
+  "true" token [ drop t ] <@ ;
 
 LAZY: 'quot' ( -- parser )
   "\"" token ;
 
+LAZY: 'hex-digit' ( -- parser )
+  [ digit> ] satisfy [ digit> ] <@ ;
+
+: hex-digits>ch ( digits -- ch )
+    0 [ swap 16 * + ] reduce ;
+
+LAZY: 'string-char' ( -- parser )
+  [ quotable? ] satisfy
+  "\\b" token [ drop 8 ] <@ <|>
+  "\\t" token [ drop CHAR: \t ] <@ <|>
+  "\\n" token [ drop CHAR: \n ] <@ <|>
+  "\\f" token [ drop 12 ] <@ <|>
+  "\\r" token [ drop CHAR: \r ] <@ <|>
+  "\\\"" token [ drop CHAR: " ] <@ <|>
+  "\\/" token [ drop CHAR: / ] <@ <|>
+  "\\\\" token [ drop CHAR: \\ ] <@ <|>
+  "\\u" token 'hex-digit' 4 exactly-n &>
+  [ hex-digits>ch ] <@ <|> ;
+
 LAZY: 'string' ( -- parser )
   'quot' 
-  [ 
-    [ quotable? ] keep
-    [ CHAR: \\ = or ] keep 
-    CHAR: " = not and 
-  ] satisfy <*> &> 
+  'string-char' <*> &> 
   'quot' <& [ >string ] <@  ;
 
 DEFER: 'value'
@@ -86,6 +102,9 @@ LAZY: 'minus' ( -- parser )
 LAZY: 'plus' ( -- parser )
   "+" token ;
 
+LAZY: 'sign' ( -- parser )
+  'minus' 'plus' <|> ;
+
 LAZY: 'zero' ( -- parser )
   "0" token [ drop 0 ] <@ ;
 
@@ -116,11 +135,11 @@ LAZY: 'e' ( -- parser )
 : sign-number ( pair -- number )
   #! Pair is { minus? num }
   #! Convert the json number value to a factor number
-  dup second swap first [ -1 * ] when ;
+  dup second swap first [ first "-" = [ -1 * ] when ] when* ;
 
 LAZY: 'exp' ( -- parser )
     'e' 
-    'minus' 'plus' <|> <?> &>
+    'sign' <?> &>
     'digit0-9' <+> [ decimal>integer ] <@ <&> [ sign-number ] <@ ;
 
 : sequence>frac ( seq -- num ) 
@@ -136,7 +155,7 @@ LAZY: 'frac' ( -- parser )
   dup second dup [ 10 swap first ^ swap first * ] [ drop first ] if ;
 
 LAZY: 'number' ( -- parser )
-  'minus' <?>
+  'sign' <?>
   [ 'int' , 'frac' 0 succeed <|> , ] [<&>] [ sum ] <@ 
   'exp' <?> <&> [ raise-to-power ] <@ <&> [ sign-number ] <@ ;
 
@@ -149,7 +168,7 @@ LAZY: 'value' ( -- parser )
     'object' ,
     'array' ,
     'number' ,
-  ] [<|>] ;
+  ] [<|>] spaced ;
 
 : json> ( string -- object )
   #! Parse a json formatted string to a factor object
diff --git a/extra/lazy-lists/authors.txt b/extra/lazy-lists/authors.txt
deleted file mode 100644 (file)
index f6ba9ba..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-Chris Double
-Samuel Tardieu
-Matthew Willis
diff --git a/extra/lazy-lists/examples/authors.txt b/extra/lazy-lists/examples/authors.txt
deleted file mode 100755 (executable)
index 44b06f9..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Chris Double
diff --git a/extra/lazy-lists/examples/examples-tests.factor b/extra/lazy-lists/examples/examples-tests.factor
deleted file mode 100644 (file)
index d4e3ed7..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-USING: lazy-lists.examples lazy-lists tools.test ;
-IN: lazy-lists.examples.tests
-
-[ { 1 3 5 7 } ] [ 4 odds ltake list>array ] unit-test
-[ { 0 1 4 9 16 } ] [ first-five-squares ] unit-test
diff --git a/extra/lazy-lists/examples/examples.factor b/extra/lazy-lists/examples/examples.factor
deleted file mode 100644 (file)
index 844ae31..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-! Rewritten by Matthew Willis, July 2006
-! Copyright (C) 2004 Chris Double.
-! See http://factorcode.org/license.txt for BSD license.
-
-USING: lazy-lists math kernel sequences quotations ;
-IN: lazy-lists.examples
-
-: naturals 0 lfrom ;
-: positives 1 lfrom ;
-: evens 0 [ 2 + ] lfrom-by ;
-: odds 1 lfrom [ 2 mod 1 = ] lfilter ;
-: powers-of-2 1 [ 2 * ] lfrom-by ;
-: ones 1 [ ] lfrom-by ;
-: squares naturals [ dup * ] lmap ;
-: first-five-squares 5 squares ltake list>array ;
diff --git a/extra/lazy-lists/lazy-lists-docs.factor b/extra/lazy-lists/lazy-lists-docs.factor
deleted file mode 100644 (file)
index b240b3f..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-! Copyright (C) 2006 Chris Double.
-! See http://factorcode.org/license.txt for BSD license.
-
-USING: help.markup help.syntax sequences strings ;
-IN: lazy-lists 
-
-{ car cons cdr nil nil? list? uncons } related-words
-
-HELP: cons 
-{ $values { "car" "the head of the lazy list" } { "cdr" "the tail of the lazy list" } { "cons" "a cons object" } }
-{ $description "Constructs a cons cell." } ;
-
-HELP: car
-{ $values { "cons" "a cons object" } { "car" "the first item in the list" } }
-{ $description "Returns the first item in the list." } ;
-
-HELP: cdr
-{ $values { "cons" "a cons object" } { "cdr" "a cons object" } }
-{ $description "Returns the tail of the list." } ;
-
-HELP: nil 
-{ $values { "cons" "An empty cons" } }
-{ $description "Returns a representation of an empty list" } ;
-
-HELP: nil? 
-{ $values { "cons" "a cons object" } { "?" "a boolean" } }
-{ $description "Return true if the cons object is the nil cons." } ;
-
-HELP: list? ( object -- ? )
-{ $values { "object" "an object" } { "?" "a boolean" } }
-{ $description "Returns true if the object conforms to the list protocol." } ;
-
-{ 1list 2list 3list } related-words
-
-HELP: 1list
-{ $values { "obj" "an object" } { "cons" "a cons object" } }
-{ $description "Create a list with 1 element." } ;
-
-HELP: 2list
-{ $values { "a" "an object" } { "b" "an object" } { "cons" "a cons object" } }
-{ $description "Create a list with 2 elements." } ;
-
-HELP: 3list
-{ $values { "a" "an object" } { "b" "an object" } { "c" "an object" } { "cons" "a cons object" } }
-{ $description "Create a list with 3 elements." } ;
-
-HELP: lazy-cons
-{ $values { "car" "a quotation with stack effect ( -- X )" } { "cdr" "a quotation with stack effect ( -- cons )" } { "promise" "the resulting cons object" } }
-{ $description "Constructs a cons object for a lazy list from two quotations. The " { $snippet "car" } " quotation should return the head of the list, and the " { $snippet "cons" } " quotation the tail when called. When " { $link cons } " or " { $link cdr } " are called on the lazy-cons object then the appropriate quotation is called." } 
-{ $see-also cons car cdr nil nil? } ;
-
-{ 1lazy-list 2lazy-list 3lazy-list } related-words
-
-HELP: 1lazy-list
-{ $values { "a" "a quotation with stack effect ( -- X )" } { "lazy-cons" "a lazy-cons object" } }
-{ $description "Create a lazy list with 1 element. The element is the result of calling the quotation. The quotation is only called when the list element is requested." } ;
-
-HELP: 2lazy-list
-{ $values { "a" "a quotation with stack effect ( -- X )" } { "b" "a quotation with stack effect ( -- X )" } { "lazy-cons" "a lazy-cons object" } }
-{ $description "Create a lazy list with 2 elements. The elements are the result of calling the quotations. The quotations are only called when the list elements are requested." } ;
-
-HELP: 3lazy-list
-{ $values { "a" "a quotation with stack effect ( -- X )" } { "b" "a quotation with stack effect ( -- X )" } { "c" "a quotation with stack effect ( -- X )" } { "lazy-cons" "a lazy-cons object" } }
-{ $description "Create a lazy list with 3 elements. The elements are the result of calling the quotations. The quotations are only called when the list elements are requested." } ;
-
-HELP: <memoized-cons>
-{ $values { "cons" "a cons object" } { "memoized-cons" "the resulting memoized-cons object" } }
-{ $description "Constructs a cons object that wraps an existing cons object. Requests for the car, cdr and nil? will be remembered after the first call, and the previous result returned on subsequent calls." } 
-{ $see-also cons car cdr nil nil? } ;
-
-HELP: lnth
-{ $values { "n" "an integer index" } { "list" "a cons object" } { "elt" "the element at the nth index" } }
-{ $description "Outputs the nth element of the list." } 
-{ $see-also llength cons car cdr } ;
-
-HELP: llength
-{ $values { "list" "a cons object" } { "n" "a non-negative integer" } }
-{ $description "Outputs the length of the list. This should not be called on an infinite list." } 
-{ $see-also lnth cons car cdr } ;
-
-HELP: uncons
-{ $values { "cons" "a cons object" } { "car" "the head of the list" } { "cdr" "the tail of the list" } }
-{ $description "Put the head and tail of the list on the stack." } ;
-
-{ leach lreduce lmap lmap-with ltake lfilter lappend lfrom lfrom-by lconcat lcartesian-product lcartesian-product* lcomp lcomp* lmerge lreduce lwhile luntil } related-words
-
-HELP: leach
-{ $values { "list" "a cons object" } { "quot" "a quotation with stack effect ( obj -- )" } }
-{ $description "Call the quotation for each item in the list." } ;
-
-HELP: lreduce
-{ $values { "list" "a cons object" } { "identity" "an object" } { "quot" "a quotation with stack effect ( prev elt -- next )" } { "result" "the final result" } }
-{ $description "Combines successive elements of the list using a binary operation, and outputs the final result." } ;
-
-HELP: lmap
-{ $values { "list" "a cons object" } { "quot" "a quotation with stack effect ( obj -- X )" } { "result" "resulting cons object" } }
-{ $description "Perform a similar functionality to that of the " { $link map } " word, but in a lazy manner. No evaluation of the list elements occurs initially but a " { $link <lazy-map> } " object is returned which conforms to the list protocol. Calling " { $link car } ", " { $link cdr } " or " { $link nil? } " on this will evaluate elements as required." } ;
-
-HELP: lmap-with
-{ $values { "value" "an object" } { "list" "a cons object" } { "quot" "a quotation with stack effect ( obj elt -- X )" } { "result" "resulting cons object" } }
-{ $description "Variant of " { $link lmap } " which pushes a retained object on each invocation of the quotation." } ;
-
-HELP: ltake
-{ $values { "n" "a non negative integer" } { "list" "a cons object" } { "result" "resulting cons object" } }
-{ $description "Outputs a lazy list containing the first n items in the list. This is done a lazy manner. No evaluation of the list elements occurs initially but a " { $link <lazy-take> } " object is returned which conforms to the list protocol. Calling " { $link car } ", " { $link cdr } " or " { $link nil? } " on this will evaluate elements as required." } ;
-
-HELP: lfilter
-{ $values { "list" "a cons object" } { "quot" "a quotation with stack effect ( -- X )" } { "result" "resulting cons object" } }
-{ $description "Perform a similar functionality to that of the " { $link filter } " word, but in a lazy manner. No evaluation of the list elements occurs initially but a " { $link <lazy-filter> } " object is returned which conforms to the list protocol. Calling " { $link car } ", " { $link cdr } " or " { $link nil? } " on this will evaluate elements as required." } ;
-
-HELP: lwhile
-{ $values { "list" "a cons object" } { "quot" "a quotation with stack effect ( X -- bool )" } { "result" "resulting cons object" } }
-{ $description "Outputs a lazy list containing the first items in the list as long as " { $snippet "quot" } " evaluates to t. No evaluation of the list elements occurs initially but a " { $link <lazy-while> } " object is returned with conforms to the list protocol. Calling " { $link car } ", " { $link cdr } " or " { $link nil? } " on this will evaluate elements as required." } ;
-
-HELP: luntil
-{ $values { "list" "a cons object" } { "quot" "a quotation with stack effect ( X -- bool )" } { "result" "resulting cons object" } }
-{ $description "Outputs a lazy list containing the first items in the list until after " { $snippet "quot" } " evaluates to t. No evaluation of the list elements occurs initially but a " { $link <lazy-while> } " object is returned with conforms to the list protocol. Calling " { $link car } ", " { $link cdr } " or " { $link nil? } " on this will evaluate elements as required." } ;
-
-HELP: list>vector
-{ $values { "list" "a cons object" } { "vector" "the list converted to a vector" } }
-{ $description "Convert a list to a vector. If the list is a lazy infinite list then this will enter an infinite loop." } 
-{ $see-also list>array } ;
-
-HELP: list>array
-{ $values { "list" "a cons object" } { "array" "the list converted to an array" } }
-{ $description "Convert a list to an array. If the list is a lazy infinite list then this will enter an infinite loop." } 
-{ $see-also list>vector } ;
-
-HELP: lappend
-{ $values { "list1" "a cons object" } { "list2" "a cons object" } { "result" "a lazy list of list2 appended to list1" } }
-{ $description "Perform a similar functionality to that of the " { $link append } " word, but in a lazy manner. No evaluation of the list elements occurs initially but a " { $link <lazy-append> } " object is returned which conforms to the list protocol. Calling " { $link car } ", " { $link cdr } " or " { $link nil? } " on this will evaluate elements as required. Successive calls to " { $link cdr } " will iterate through list1, followed by list2." } ;
-
-HELP: lfrom-by
-{ $values { "n" "an integer" } { "quot" "a quotation with stack effect ( -- int )" } { "list" "a lazy list of integers" } }
-{ $description "Return an infinite lazy list of values starting from n, with each successive value being the result of applying quot to n." } ;
-
-HELP: lfrom
-{ $values { "n" "an integer" } { "list" "a lazy list of integers" } }
-{ $description "Return an infinite lazy list of incrementing integers starting from n." } ;
-
-HELP: seq>list
-{ $values { "index" "an integer 0 or greater" } { "seq" "a sequence" } { "list" "a list" } }
-{ $description "Convert the sequence into a list, starting from the 'index' offset into the sequence." } 
-{ $see-also >list } ;
-
-HELP: >list
-{ $values { "object" "an object" } { "list" "a list" } }
-{ $description "Convert the object into a list. Existing lists are passed through intact, sequences are converted using " { $link seq>list } " and other objects cause an error to be thrown." } 
-{ $see-also seq>list } ;
-
-HELP: lconcat
-{ $values { "list" "a list of lists" } { "result" "a list" } }
-{ $description "Concatenates a list of lists together into one list." } ;
-
-HELP: lcartesian-product
-{ $values { "list1" "a list" } { "list2" "a list" } { "result" "list of cartesian products" } }
-{ $description "Given two lists, return a list containing the cartesian product of those lists." } ;
-
-HELP: lcartesian-product*
-{ $values { "lists" "a list of lists" } { "result" "list of cartesian products" } }
-{ $description "Given a list of lists, return a list containing the cartesian product of those lists." } ;
-
-HELP: lcomp
-{ $values { "list" "a list of lists" } { "quot" "a quotation with stack effect ( seq -- X )" } { "result" "the resulting list" } }
-{ $description "Get the cartesian product of the lists in " { $snippet "list" } " and call " { $snippet "quot" } " call with each element from the cartesian product on the stack, the result of which is returned in the final " { $snippet "list" } "." } ;
-
-HELP: lcomp*
-{ $values { "list" "a list of lists" } { "guards" "a sequence of quotations with stack effect ( seq -- bool )" } { "quot" "a quotation with stack effect ( seq -- X )" } { "list" "the resulting list" } { "result" "a list" } }
-{ $description "Get the cartesian product of the lists in " { $snippet "list" } ", filter it by applying each guard quotation to it and call " { $snippet "quot" } " call with each element from the remaining cartesian product items on the stack, the result of which is returned in the final " { $snippet "list" } "." }
-{ $examples
-  { $code "{ 1 2 3 } >list { 4 5 6 } >list 2list { [ first odd? ] } [ first2 + ] lcomp*" }
-} ;
-
-HELP: lmerge
-{ $values { "list1" "a list" } { "list2" "a list" } { "result" "lazy list merging list1 and list2" } }
-{ $description "Return the result of merging the two lists in a lazy manner." } 
-{ $examples
-  { $example "USING: lazy-lists prettyprint ;" "{ 1 2 3 } >list { 4 5 6 } >list lmerge list>array ." "{ 1 4 2 5 3 6 }" }
-} ;
-
-HELP: lcontents
-{ $values { "stream" "a stream" } { "result" string } }
-{ $description "Returns a lazy list of all characters in the file. " { $link car } " returns the next character in the file, " { $link cdr } " returns the remaining characters as a lazy list. " { $link nil? } " indicates end of file." } 
-{ $see-also llines } ;
-
-HELP: llines
-{ $values { "stream" "a stream" } { "result" "a list" } }
-{ $description "Returns a lazy list of all lines in the file. " { $link car } " returns the next lines in the file, " { $link cdr } " returns the remaining lines as a lazy list. " { $link nil? } " indicates end of file." } 
-{ $see-also lcontents } ;
-
diff --git a/extra/lazy-lists/lazy-lists-tests.factor b/extra/lazy-lists/lazy-lists-tests.factor
deleted file mode 100644 (file)
index 302299b..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-! Copyright (C) 2006 Matthew Willis and Chris Double.
-! See http://factorcode.org/license.txt for BSD license.
-!
-USING: lazy-lists tools.test kernel math io sequences ;
-IN: lazy-lists.tests
-
-[ { 1 2 3 4 } ] [
-  { 1 2 3 4 } >list list>array
-] unit-test
-
-[ { { 1 4 } { 1 5 } { 2 4 } { 2 5 } { 3 4 } { 3 5 } } ] [
-  { 1 2 3 } >list { 4 5 } >list 2list lcartesian-product* list>array
-] unit-test
-
-[ { { 1 4 } { 1 5 } { 2 4 } { 2 5 } { 3 4 } { 3 5 } } ] [
-  { 1 2 3 } >list { 4 5 } >list lcartesian-product list>array
-] unit-test
-
-[ { 5 6 6 7 7 8 } ] [ 
-  { 1 2 3 } >list { 4 5 } >list 2list [ first2 + ] lcomp list>array
-] unit-test
-
-[ { 5 6 7 8 } ] [ 
-  { 1 2 3 } >list { 4 5 } >list 2list { [ first odd? ] } [ first2 + ] lcomp* list>array
-] unit-test
-
-[ { 4 5 6 } ] [ 
-    3 { 1 2 3 } >list [ + ] lmap-with list>array
-] unit-test
diff --git a/extra/lazy-lists/lazy-lists.factor b/extra/lazy-lists/lazy-lists.factor
deleted file mode 100644 (file)
index 6db82ed..0000000
+++ /dev/null
@@ -1,445 +0,0 @@
-! Copyright (C) 2004 Chris Double.
-! See http://factorcode.org/license.txt for BSD license.
-!
-! Updated by Matthew Willis, July 2006
-! Updated by Chris Double, September 2006
-!
-USING: kernel sequences math vectors arrays namespaces
-quotations promises combinators io ;
-IN: lazy-lists
-
-! Lazy List Protocol
-MIXIN: list
-GENERIC: car   ( cons -- car )
-GENERIC: cdr   ( cons -- cdr )
-GENERIC: nil?  ( cons -- ? )
-
-M: promise car ( promise -- car )
-  force car ;
-
-M: promise cdr ( promise -- cdr )
-  force cdr ;
-
-M: promise nil? ( cons -- bool )
-  force nil? ;
-
-TUPLE: cons car cdr ;
-
-C: cons cons
-
-M: cons car ( cons -- car )
-    cons-car ;
-
-M: cons cdr ( cons -- cdr )
-    cons-cdr ;
-
-: nil ( -- cons )
-  T{ cons f f f } ;
-
-M: cons nil? ( cons -- bool )
-    nil eq? ;
-
-: 1list ( obj -- cons )
-    nil cons ;
-
-: 2list ( a b -- cons )
-    nil cons cons ;
-
-: 3list ( a b c -- cons )
-    nil cons cons cons ;
-
-! Both 'car' and 'cdr' are promises
-TUPLE: lazy-cons car cdr ;
-
-: lazy-cons ( car cdr -- promise )
-    [ promise ] bi@ \ lazy-cons boa
-    T{ promise f f t f } clone
-    [ set-promise-value ] keep ;
-
-M: lazy-cons car ( lazy-cons -- car )
-    lazy-cons-car force ;
-
-M: lazy-cons cdr ( lazy-cons -- cdr )
-    lazy-cons-cdr force ;
-
-M: lazy-cons nil? ( lazy-cons -- bool )
-    nil eq? ;
-
-: 1lazy-list ( a -- lazy-cons )
-  [ nil ] lazy-cons ;
-
-: 2lazy-list ( a b -- lazy-cons )
-  1lazy-list 1quotation lazy-cons ;
-
-: 3lazy-list ( a b c -- lazy-cons )
-  2lazy-list 1quotation lazy-cons ;
-
-: lnth ( n list -- elt )
-  swap [ cdr ] times car ;
-
-: (llength) ( list acc -- n )
-  over nil? [ nip ] [ [ cdr ] dip 1+ (llength) ] if ;
-
-: llength ( list -- n )
-  0 (llength) ;
-
-: uncons ( cons -- car cdr )
-    #! Return the car and cdr of the lazy list
-    dup car swap cdr ;
-
-: leach ( list quot -- )
-  swap dup nil? [ 2drop ] [ uncons swapd over 2slip leach ] if ; inline
-
-: lreduce ( list identity quot -- result )
-  swapd leach ; inline
-
-TUPLE: memoized-cons original car cdr nil? ;
-
-: not-memoized ( -- obj )
-  { } ;
-
-: not-memoized? ( obj -- bool )
-  not-memoized eq? ;
-
-: <memoized-cons> ( cons -- memoized-cons )
-  not-memoized not-memoized not-memoized
-  memoized-cons boa ;
-
-M: memoized-cons car ( memoized-cons -- car )
-  dup memoized-cons-car not-memoized? [
-    dup memoized-cons-original car [ swap set-memoized-cons-car ] keep
-  ] [
-    memoized-cons-car
-  ] if ;
-
-M: memoized-cons cdr ( memoized-cons -- cdr )
-  dup memoized-cons-cdr not-memoized? [
-    dup memoized-cons-original cdr [ swap set-memoized-cons-cdr ] keep
-  ] [
-    memoized-cons-cdr
-  ] if ;
-
-M: memoized-cons nil? ( memoized-cons -- bool )
-  dup memoized-cons-nil? not-memoized? [
-    dup memoized-cons-original nil? [ swap set-memoized-cons-nil? ] keep
-  ] [
-    memoized-cons-nil?
-  ] if ;
-
-TUPLE: lazy-map cons quot ;
-
-C: <lazy-map> lazy-map
-
-: lmap ( list quot -- result )
-    over nil? [ 2drop nil ] [ <lazy-map> <memoized-cons> ] if ;
-
-M: lazy-map car ( lazy-map -- car )
-  [ lazy-map-cons car ] keep
-  lazy-map-quot call ;
-
-M: lazy-map cdr ( lazy-map -- cdr )
-  [ lazy-map-cons cdr ] keep
-  lazy-map-quot lmap ;
-
-M: lazy-map nil? ( lazy-map -- bool )
-  lazy-map-cons nil? ;
-
-: lmap-with ( value list quot -- result )
-  with lmap ;
-
-TUPLE: lazy-take n cons ;
-
-C: <lazy-take> lazy-take
-
-: ltake ( n list -- result )
-    over zero? [ 2drop nil ] [ <lazy-take> ] if ;
-
-M: lazy-take car ( lazy-take -- car )
-  lazy-take-cons car ;
-
-M: lazy-take cdr ( lazy-take -- cdr )
-  [ lazy-take-n 1- ] keep
-  lazy-take-cons cdr ltake ;
-
-M: lazy-take nil? ( lazy-take -- bool )
-  dup lazy-take-n zero? [
-    drop t
-  ] [
-    lazy-take-cons nil?
-  ] if ;
-
-TUPLE: lazy-until cons quot ;
-
-C: <lazy-until> lazy-until
-
-: luntil ( list quot -- result )
-  over nil? [ drop ] [ <lazy-until> ] if ;
-
-M: lazy-until car ( lazy-until -- car )
-   lazy-until-cons car ;
-
-M: lazy-until cdr ( lazy-until -- cdr )
-   [ lazy-until-cons uncons swap ] keep lazy-until-quot tuck call
-   [ 2drop nil ] [ luntil ] if ;
-
-M: lazy-until nil? ( lazy-until -- bool )
-   drop f ;
-
-TUPLE: lazy-while cons quot ;
-
-C: <lazy-while> lazy-while
-
-: lwhile ( list quot -- result )
-  over nil? [ drop ] [ <lazy-while> ] if ;
-
-M: lazy-while car ( lazy-while -- car )
-   lazy-while-cons car ;
-
-M: lazy-while cdr ( lazy-while -- cdr )
-   [ lazy-while-cons cdr ] keep lazy-while-quot lwhile ;
-
-M: lazy-while nil? ( lazy-while -- bool )
-   [ car ] keep lazy-while-quot call not ;
-
-TUPLE: lazy-filter cons quot ;
-
-C: <lazy-filter> lazy-filter
-
-: lfilter ( list quot -- result )
-    over nil? [ 2drop nil ] [ <lazy-filter> <memoized-cons> ] if ;
-
-: car-filter?  ( lazy-filter -- ? )
-  [ lazy-filter-cons car ] keep
-  lazy-filter-quot call ;
-
-: skip ( lazy-filter -- )
-  [ lazy-filter-cons cdr ] keep
-  set-lazy-filter-cons ;
-
-M: lazy-filter car ( lazy-filter -- car )
-  dup car-filter? [ lazy-filter-cons ] [ dup skip ] if car ;
-
-M: lazy-filter cdr ( lazy-filter -- cdr )
-  dup car-filter? [
-    [ lazy-filter-cons cdr ] keep
-    lazy-filter-quot lfilter
-  ] [
-    dup skip cdr
-  ] if ;
-
-M: lazy-filter nil? ( lazy-filter -- bool )
-  dup lazy-filter-cons nil? [
-    drop t
-  ] [
-    dup car-filter? [
-      drop f
-    ] [
-      dup skip nil?
-    ] if
-  ] if ;
-
-: list>vector ( list -- vector )
-  [ [ , ] leach ] V{ } make ;
-
-: list>array ( list -- array )
-  [ [ , ] leach ] { } make ;
-
-TUPLE: lazy-append list1 list2 ;
-
-C: <lazy-append> lazy-append
-
-: lappend ( list1 list2 -- result )
-  over nil? [ nip ] [ <lazy-append> ] if ;
-
-M: lazy-append car ( lazy-append -- car )
-  lazy-append-list1 car ;
-
-M: lazy-append cdr ( lazy-append -- cdr )
-  [ lazy-append-list1 cdr  ] keep
-  lazy-append-list2 lappend ;
-
-M: lazy-append nil? ( lazy-append -- bool )
-   drop f ;
-
-TUPLE: lazy-from-by n quot ;
-
-C: lfrom-by lazy-from-by ( n quot -- list )
-
-: lfrom ( n -- list )
-  [ 1+ ] lfrom-by ;
-
-M: lazy-from-by car ( lazy-from-by -- car )
-  lazy-from-by-n ;
-
-M: lazy-from-by cdr ( lazy-from-by -- cdr )
-  [ lazy-from-by-n ] keep
-  lazy-from-by-quot dup slip lfrom-by ;
-
-M: lazy-from-by nil? ( lazy-from-by -- bool )
-  drop f ;
-
-TUPLE: lazy-zip list1 list2 ;
-
-C: <lazy-zip> lazy-zip
-
-: lzip ( list1 list2 -- lazy-zip )
-    over nil? over nil? or
-    [ 2drop nil ] [ <lazy-zip> ] if ;
-
-M: lazy-zip car ( lazy-zip -- car )
-    [ lazy-zip-list1 car ] keep lazy-zip-list2 car 2array ;
-
-M: lazy-zip cdr ( lazy-zip -- cdr )
-    [ lazy-zip-list1 cdr ] keep lazy-zip-list2 cdr lzip ;
-
-M: lazy-zip nil? ( lazy-zip -- bool )
-    drop f ;
-
-TUPLE: sequence-cons index seq ;
-
-C: <sequence-cons> sequence-cons
-
-: seq>list ( index seq -- list )
-  2dup length >= [
-    2drop nil
-  ] [
-    <sequence-cons>
-  ] if ;
-
-M: sequence-cons car ( sequence-cons -- car )
-  [ sequence-cons-index ] keep
-  sequence-cons-seq nth ;
-
-M: sequence-cons cdr ( sequence-cons -- cdr )
-  [ sequence-cons-index 1+ ] keep
-  sequence-cons-seq seq>list ;
-
-M: sequence-cons nil? ( sequence-cons -- bool )
-    drop f ;
-
-: >list ( object -- list )
-  {
-    { [ dup sequence? ] [ 0 swap seq>list ] }
-    { [ dup list?     ] [ ] }
-    [ "Could not convert object to a list" throw ]
-  } cond ;
-
-TUPLE: lazy-concat car cdr ;
-
-C: <lazy-concat> lazy-concat
-
-DEFER: lconcat
-
-: (lconcat) ( car cdr -- list )
-  over nil? [
-    nip lconcat
-  ] [
-    <lazy-concat>
-  ] if ;
-
-: lconcat ( list -- result )
-  dup nil? [
-    drop nil
-  ] [
-    uncons (lconcat)
-  ] if ;
-
-M: lazy-concat car ( lazy-concat -- car )
-  lazy-concat-car car ;
-
-M: lazy-concat cdr ( lazy-concat -- cdr )
-  [ lazy-concat-car cdr ] keep lazy-concat-cdr (lconcat) ;
-
-M: lazy-concat nil? ( lazy-concat -- bool )
-  dup lazy-concat-car nil? [
-    lazy-concat-cdr nil?
-  ] [
-    drop f
-  ] if ;
-
-: lcartesian-product ( list1 list2 -- result )
-  swap [ swap [ 2array ] lmap-with ] lmap-with lconcat ;
-
-: lcartesian-product* ( lists -- result )
-  dup nil? [
-    drop nil
-  ] [
-    [ car ] keep cdr [ car lcartesian-product ] keep cdr list>array swap [
-      swap [ swap [ suffix ] lmap-with ] lmap-with lconcat
-    ] reduce
-  ] if ;
-
-: lcomp ( list quot -- result )
-  [ lcartesian-product* ] dip lmap ;
-
-: lcomp* ( list guards quot -- result )
-  [ [ lcartesian-product* ] dip [ lfilter ] each ] dip lmap ;
-
-DEFER: lmerge
-
-: (lmerge) ( list1 list2 -- result )
-  over [ car ] curry -rot
-  [
-    dup [ car ] curry -rot
-    [
-      [ cdr ] bi@ lmerge
-    ] 2curry lazy-cons
-  ] 2curry lazy-cons ;
-
-: lmerge ( list1 list2 -- result )
-  {
-    { [ over nil? ] [ nip   ] }
-    { [ dup nil?  ]  [ drop ] }
-    { [ t         ]  [ (lmerge) ] }
-  } cond ;
-
-TUPLE: lazy-io stream car cdr quot ;
-
-C: <lazy-io> lazy-io
-
-: lcontents ( stream -- result )
-  f f [ stream-read1 ] <lazy-io> ;
-
-: llines ( stream -- result )
-  f f [ stream-readln ] <lazy-io> ;
-
-M: lazy-io car ( lazy-io -- car )
-  dup lazy-io-car dup [
-    nip
-  ] [
-    drop dup lazy-io-stream over lazy-io-quot call
-    swap dupd set-lazy-io-car
-  ] if ;
-
-M: lazy-io cdr ( lazy-io -- cdr )
-  dup lazy-io-cdr dup [
-    nip
-  ] [
-    drop dup
-    [ lazy-io-stream ] keep
-    [ lazy-io-quot ] keep
-    car [
-      [ f f ] dip <lazy-io> [ swap set-lazy-io-cdr ] keep
-    ] [
-      3drop nil
-    ] if
-  ] if ;
-
-M: lazy-io nil? ( lazy-io -- bool )
-  car not ;
-
-INSTANCE: cons list
-INSTANCE: sequence-cons list
-INSTANCE: memoized-cons list
-INSTANCE: promise list
-INSTANCE: lazy-io list
-INSTANCE: lazy-concat list
-INSTANCE: lazy-cons list
-INSTANCE: lazy-map list
-INSTANCE: lazy-take list
-INSTANCE: lazy-append list
-INSTANCE: lazy-from-by list
-INSTANCE: lazy-zip list
-INSTANCE: lazy-while list
-INSTANCE: lazy-until list
-INSTANCE: lazy-filter list
diff --git a/extra/lazy-lists/old-doc.html b/extra/lazy-lists/old-doc.html
deleted file mode 100644 (file)
index 4c04301..0000000
+++ /dev/null
@@ -1,361 +0,0 @@
-<html>
-  <head>
-    <title>Lazy Evaluation</title>
-    <link rel="stylesheet" type="text/css" href="style.css">
-      </head>
-  <body>
-    <h1>Lazy Evaluation</h1>
-<p>The 'lazy' vocabulary adds lazy lists to Factor. This provides the
-    ability to describe infinite structures, and to delay execution of
-    expressions until they are actually used.</p>
-<p>Lazy lists, like normal lists, are composed of a head and tail. In
-    a lazy list the head and tail are something called a 'promise'. 
-    To convert a
-    'promise' into its actual value a word called 'force' is used. To
-    convert a value into a 'promise' the word to use is 'delay'.</p>
-<table border="1">
-<tr><td><a href="#delay">delay</a></td></tr>
-<tr><td><a href="#force">force</a></td></tr>
-</table>
-
-<p>Many of the lazy list words are named similar to the standard list
-    words but with an 'l' suffixed to it. Here are the commonly used
-    words and their equivalent list operation:</p>
-<table border="1">
-<tr><th>Lazy List</th><th>Normal List</th></tr>
-<tr><td><a href="#lnil">lnil</a></td><td>[ ]</td></tr>
-<tr><td><a href="#lnilp">lnil?</a></td><td>Test for nil value</td></tr>
-<tr><td><a href="#lcons">lcons</a></td><td>cons</td></tr>
-<tr><td><a href="#lunit">lunit</a></td><td>unit</td></tr>
-<tr><td><a href="#lcar">lcar</a></td><td>car</td></tr>
-<tr><td><a href="#lcdr">lcdr</a></td><td>cdr</td></tr>
-<tr><td><a href="#lnth">lnth</a></td><td>nth</td></tr>
-<tr><td><a href="#luncons">luncons</a></td><td>uncons</td></tr>
-<tr><td><a href="#lmap">lmap</a></td><td>map</td></tr>
-<tr><td><a href="#lsubset">lsubset</a></td><td>subset</td></tr>
-<tr><td><a href="#leach">leach</a></td><td>each</td></tr>
-<tr><td><a href="#lappend">lappend</a></td><td>append</td></tr>
-</table>
-<p>A few additional words specific to lazy lists are:</p>
-<table border="1">
-<tr><td><a href="#ltake">ltake</a></td><td>Returns a normal list containing a specified
-number of items from the lazy list.</td></tr>
-<tr><td><a href="#lappendstar">lappend*</a></td><td>Given a lazy list of lazy lists,
-concatenate them together in a lazy manner, returning a single lazy
-list.</td></tr>
-<tr><td><a href="#list>llist">list>llist</a></td><td>Given a normal list, return a lazy list
-that contains the same elements as the normal list.</td></tr>
-</table>
-<h2>Reference</h2>
-<!-- delay description -->
-<a name="delay">
-<h3>delay ( quot -- &lt;promise&gt; )</h3>
-<p>'delay' is used to convert a value or expression into a promise.
-   The word 'force' is used to convert that promise back to its
-   value, or to force evaluation of the expression to return a value.
-</p>
-<p>The value on the stack that 'delay' expects must be quoted. This is
-   a requirement to prevent it from being evaluated.
-</p>
-<pre class="code">
-  ( 1 ) [ 42 ] <a href="#delay">delay</a> dup .
-       => &lt;&lt; promise [ ] [ 42 ] [ ] [ ] &gt;&gt;
-  ( 2 ) <a href="#force">force</a> .
-       => 42
-</pre>
-
-<!-- force description -->
-<a name="force">
-<h3>force ( &lt;promise&gt; -- value )</h3>
-<p>'force' will evaluate a promises original expression
-   and leave the value of that expression on the stack.
-</p>
-<p>A promise can be forced multiple times but the expression
-   is only evaluated once. Future calls of 'force' on the promise
-   will returned the cached value of the original force. If the
-   expression contains side effects, such as i/o, then that i/o
-   will only occur on the first 'force'. See below for an example
-   (steps 3-5).
-</p>
-<p>If a promise is itself delayed, a force will evaluate all promises
-   until a value is returned. Due to this behaviour it is generally not
-   possible to delay a promise. The example below shows what happens
-   in this case.
-</p>
-<pre class="code">       
-  ( 1 ) [ 42 ] <a href="#delay">delay</a> dup .
-       => &lt;&lt; promise [ ] [ 42 ] [ ] [ ] &gt;&gt;
-  ( 2 ) <a href="#force">force</a> .
-       => 42
-       
-        #! Multiple forces on a promise returns cached value
-  ( 3 ) [ "hello" print 42 ] <a href="#delay">delay</a> dup .
-       => << promise [ ] [ "hello" print 42 ] [ ] [ ] >>
-  ( 4 ) dup <a href="#force">force</a> .
-       => hello
-          42
-  ( 5 ) <a href="#force">force</a> .
-       => 42
-
-        #! Forcing a delayed promise cascades up to return
-        #! original value, rather than the promise.
-  ( 6 ) [ [ 42 ] <a href="#delay">delay</a> ] <a href="#delay">delay</a> dup .
-       => << promise [ ] [ [ 42 ] delay ] [ ] [ ] >>
-  ( 7 ) <a href="#force">force</a> .
-       => 42
-</pre>
-
-<!-- lnil description -->
-<a name="lnil">
-<h3>lnil ( -- lcons )</h3>
-<p>Returns a value representing the empty lazy list.</p>
-<pre class="code">
-  ( 1 ) <a href="#lnil">lnil</a> .
-       => << promise [ ] [ [ ] ] t [ ] >>
-</pre>
-
-<!-- lnil description -->
-<a name="lnilp">
-<h3>lnil? ( lcons -- bool )</h3>
-<p>Returns true if the given lazy cons is the value representing 
-   the empty lazy list.</p>
-<pre class="code">
-  ( 1 ) <a href="#lnil">lnil</a> <a href="#lnilp">lnil?</a> .
-       => t
-  ( 2 ) [ 1 ] <a href="#list2llist">list&gt;llist</a> dup <a href="#lnilp">lnil?</a> .
-       => [ ]
-  ( 3 ) <a href="#lcdr">lcdr</a> <a href="#lnilp">lnil?</a> .
-       => t
-</pre>
-
-<!-- lcons description -->
-<a name="lcons">
-<h3>lcons ( car-promise cdr-promise -- lcons )</h3>
-<p>Provides the same effect as 'cons' does for normal lists. 
-   Both values provided must be promises (ie. expressions that have
-   had <a href="#delay">delay</a> called on them).
-</p>
-<p>As the car and cdr passed on the stack are promises, they are not
-   evaluated until <a href="#lcar">lcar</a> or <a href="#lcdr">lcdr</a>
-   are called on the lazy cons.</p>
-<pre class="code">
-  ( 1 ) [ "car" ] <a href="#delay">delay</a> [ "cdr" ] <a href="#delay">delay</a> <a href="#lcons">lcons</a> dup .
-       => &lt;&lt; promise ... &gt;&gt;
-  ( 2 ) dup <a href="#lcar">lcar</a> .
-       => "car"
-  ( 3 ) dup <a href="#lcdr">lcdr</a> .
-       => "cdr"
-</pre>
-  
-<!-- lunit description -->
-<a name="lunit">
-<h3>lunit ( value-promise -- llist )</h3>
-<p>Provides the same effect as 'unit' does for normal lists. It
-creates a lazy list where the first element is the value given.</p>
-<p>Like <a href="#lcons">lcons</a>, the value on the stack must be
-   a promise and is not evaluated until the <a href="#lcar">lcar</a>
-   of the list is requested.</a>
-<pre class="code">
-  ( 1 ) [ 42 ] <a href="#delay">delay</a> <a href="#lunit">lunit</a> dup .
-       => &lt;&lt; promise ... &gt;&gt;
-  ( 2 ) dup <a href="#lcar">lcar</a> .
-       => 42
-  ( 3 ) dup <a href="#lcdr">lcdr</a> <a href="#lnilp">lnil?</a> .
-       => t
-  ( 4 ) [ . ] <a href="#leach">leach</a>
-       => 42
-</pre>
-
-<!-- lcar description -->
-<a name="lcar">
-<h3>lcar ( lcons -- value )</h3>
-<p>Provides the same effect as 'car' does for normal lists. It
-returns the first element in a lazy cons cell. This will force
-the evaluation of that element.</p>
-<pre class="code">
-  ( 1 ) [ 42 ] <a href="#delay">delay</a> <a href="#lunit">lunit</a> dup .
-       => &lt;&lt; promise ... &gt;&gt;
-  ( 2 ) <a href="#lcar">lcar</a> .
-       => 42
-</pre>
-
-<!-- lcdr description -->
-<a name="lcdr">
-<h3>lcdr ( lcons -- value )</h3>
-<p>Provides the same effect as 'cdr' does for normal lists. It
-returns the second element in a lazy cons cell and forces it. This
-causes that element to be evaluated immediately.</p>
-<pre class="code">
-  ( 1 ) [ 1 ] <a href="#delay">delay</a> [ 5 6 + ] <a href="#delay">delay</a> <a href="#lcons">lcons</a> dup .
-       => &lt;&lt; promise ... &gt;&gt;
-  ( 2 ) <a href="#lcdr">lcdr</a> .
-       => 11
-</pre>
-
-<pre class="code">
-  ( 1 ) 5 <a href="#lfrom">lfrom</a> dup .
-       => &lt;&lt; promise ... &gt;&gt;
-  ( 2 ) <a href="#lcdr">lcdr</a> dup <a href="#lcar">lcar</a> .
-       => 6
-  ( 3 ) <a href="#lcdr">lcdr</a> dup <a href="#lcar">lcar</a> .
-       => 7
-  ( 4 ) <a href="#lcdr">lcdr</a> dup <a href="#lcar">lcar</a> .
-       => 8
-</pre>
-
-<!-- lnth description -->
-<a name="lnth">
-<h3>lnth ( n llist -- value )</h3>
-<p>Provides the same effect as 'nth' does for normal lists. It
-returns the nth value in the lazy list. It causes all the values up to
-'n' to be evaluated.</p>
-<pre class="code">
-  ( 1 ) 1 <a href="#lfrom">lfrom</a> dup .
-       => &lt;&lt; promise ... &gt;&gt;
-  ( 2 ) 5 swap <a href="#lnth">lnth</a> .
-       => 6
-</pre>
-
-<!-- luncons description -->
-<a name="luncons">
-<h3>luncons ( lcons -- car cdr )</h3>
-<p>Provides the same effect as 'uncons' does for normal lists. It
-returns the car and cdr of the lazy list.</p>
-<pre class="code">
-  ( 1 ) [ 5 ] <a href="#delay">delay</a> [ 6 ] <a  href="#delay">delay</a> <a href="#lcons">lcons</a> dup .
-       => &lt;&lt; promise ... &gt;&gt;
-  ( 2 ) <a href="#luncons">luncons</a> . .
-       => 6
-          5
-</pre>
-
-<!-- lmap description -->
-<a name="lmap">
-<h3>lmap ( llist quot -- llist )</h3>
-<p>Lazily maps over a lazy list applying the quotation to each element.
-A new lazy list is returned which contains the results of the
-quotation.</p>
-<p>When intially called nothing in the original lazy list is
-evaluated. Only when <a href="#lcar">lcar</a> is called will the item
-in the list be evaluated and applied to the quotation. Ditto with <a
-href="#lcdr">lcdr</a>, thus allowing infinite lists to be mapped over.</p>
-<pre class="code">
-  ( 1 ) 1 <a href="#lfrom">lfrom</a>
-       => < infinite list of incrementing numbers >
-  ( 2 ) [ 2 * ] <a href="#lmap">lmap</a>
-       => < infinite list of numbers incrementing by 2 >
-  ( 3 ) 5 swap <a href="#ltake">ltake</a> <a  href="#llist2list">llist&gt;list</a> .
-       => [ 2 4 6 8 10 ]
-</pre>
-
-<!-- lsubset description -->
-<a name="lsubset">
-<h3>lsubset ( llist pred -- llist )</h3>
-<p>Provides the same effect as 'subset' does for normal lists. It
-lazily iterates over a lazy list applying the predicate quotation to each
-element. If that quotation returns true, the element will be included
-in the resulting lazy list. If it is false, the element will be skipped.
-A new lazy list is returned which contains  all elements where the
-predicate returned true.</p>
-<p>Like <a href="#lmap">lmap</a>, when initially called no evaluation
-will occur. A lazy list is returned that when values are retrieved
-from in then items are evaluated and checked against the predicate.</p>
-<pre class="code">
-  ( 1 ) 1 <a href="#lfrom">lfrom</a>
-       => < infinite list of incrementing numbers >
-  ( 2 ) [ <a href="#primep">prime?</a> ] <a href="#lsubset">lsubset</a>
-       => < infinite list of prime numbers >
-  ( 3 ) 5 swap <a href="#ltake">ltake</a> <a  href="#llist2list">llist&gt;list</a> .
-       => [ 2 3 5 7 11 ]
-</pre>
-
-<!-- leach description -->
-<a name="leach">
-<h3>leach ( llist quot --  )</h3>
-<p>Provides the same effect as 'each' does for normal lists. It
-lazily iterates over a lazy list applying the quotation to each
-element. If this operation is applied to an infinite list it will
-never return unless the quotation escapes out by calling a continuation.</p>
-<pre class="code">
-  ( 1 ) 1 <a href="#lfrom">lfrom</a>
-       => < infinite list of incrementing numbers >
-  ( 2 ) [ 2 mod 1 = ] <a href="#lsubset">lsubset</a>
-       => < infinite list of odd numbers >
-  ( 3 ) [ . ] <a href="#leach">leach</a> 
-       => 1
-          3
-          5
-          7
-          ... for ever ...
-</pre>
-
-<!-- ltake description -->
-<a name="ltake">
-<h3>ltake ( n llist -- llist )</h3>
-<p>Iterates over the lazy list 'n' times, appending each element to a
-lazy list. This provides a convenient way of getting elements out of
-an infinite lazy list.</p>
-<pre class="code">
-  ( 1 ) : ones [ 1 ] delay [ ones ] delay <a href="#lcons">lcons</a> ;
-  ( 2 ) 5 ones <a href="#ltake">ltake</a> <a  href="#llist2list">llist&gt;list</a> .
-       => [ 1 1 1 1 1  ]
-</pre>
-
-<!-- lappend description -->
-<a name="lappend">
-<h3>lappend ( llist1 llist2 -- llist )</h3>
-<p>Lazily appends two lists together. The actual appending is done
-lazily on iteration rather than immediately so it works very fast no
-matter how large the list.</p>
-<pre class="code">
-  ( 1 ) [ 1 2 3 ] <a href="#list2llist">list&gt;llist</a> [ 4 5 6 ] <a href="#list2llist">list&gt;llist</a> <a href="#lappend">lappend</a>
-  ( 2 ) [ . ] <a href="#leach">leach</a>
-       => 1
-          2
-          3
-          4
-          5
-          6
-</pre>
-
-<!-- lappend* description -->
-<a name="lappendstar">
-<h3>lappend* ( llists -- llist )</h3>
-<p>Given a lazy list of lazy lists, concatenate them together in a
-lazy fashion. The actual appending is done lazily on iteration rather
-than immediately so it works very fast no matter how large the lists.</p>
-<pre class="code">
-  ( 1 ) [ 1 2 3 ] <a href="#list2>llist">list&gt;llist</a> 
-  ( 2 ) [ 4 5 6 ] <a href="#list2llist">list&gt;llist</a> 
-  ( 3 ) [ 7 8 9 ] <a href="#list2llist">list&gt;llist</a>
-  ( 4 ) 3list <a href="#list2llist">list&gt;llist</a> <a href="#lappendstar">lappend*</a>
-  ( 5 ) [ . ] <a href="#leach">leach</a>
-       => 1
-          2
-          3
-          4
-          5
-          6
-          7
-          8
-          9
-</pre>
-
-<!-- list>llist description -->
-<a name="list2llist">
-<h3>list&gt;llist ( list  -- llist )</h3>
-<p>Converts a normal list into a lazy list. This is done lazily so the
-initial list is not iterated through immediately.</p>
-<pre class="code">
-  ( 1 ) [ 1 2 3 ] <a href="#list2llist">list&gt;llist</a> 
-  ( 2 ) [ . ] <a href="#leach">leach</a>
-       => 1
-          2
-          3
-</pre>
-
-<p class="footer">
-News and updates to this software can be obtained from the authors
-weblog: <a href="http://radio.weblogs.com/0102385">Chris Double</a>.</p>
-<p id="copyright">Copyright (c) 2004, Chris Double. All Rights Reserved.</p>
-</body> </html>
diff --git a/extra/lazy-lists/summary.txt b/extra/lazy-lists/summary.txt
deleted file mode 100644 (file)
index 5d2f302..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Lazy lists
diff --git a/extra/lazy-lists/tags.txt b/extra/lazy-lists/tags.txt
deleted file mode 100644 (file)
index dd23829..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-extensions
-collections
diff --git a/extra/lcs/diff2html/diff2html.factor b/extra/lcs/diff2html/diff2html.factor
new file mode 100644 (file)
index 0000000..a8f649e
--- /dev/null
@@ -0,0 +1,44 @@
+! Copyright (C) 2008 Slava Pestov
+! See http://factorcode.org/license.txt for BSD license.
+USING: lcs html.elements kernel qualified ;
+FROM: accessors => item>> ;
+FROM: io => write ;
+FROM: sequences => each empty? ;
+FROM: xml.entities => escape-string ;
+IN: lcs.diff2html
+
+GENERIC: diff-line ( obj -- )
+
+: write-item ( item -- )
+    item>> dup empty? [ drop "&nbsp;" ] [ escape-string ] if write ;
+
+M: retain diff-line
+    <tr>
+        dup [
+            <td "retain" =class td>
+                write-item
+            </td>
+        ] bi@
+    </tr> ;
+
+M: insert diff-line
+    <tr>
+        <td> </td>
+        <td "insert" =class td>
+            write-item
+        </td>
+    </tr> ;
+
+M: delete diff-line
+    <tr>
+        <td "delete" =class td>
+            write-item
+        </td>
+        <td> </td>
+    </tr> ;
+
+: htmlize-diff ( diff -- )
+    <table "comparison" =class table>
+        <tr> <th> "Old" write </th> <th> "New" write </th> </tr>
+        [ diff-line ] each
+    </table> ;
index f1db203a7877ea0161321550408f2e5f87e14afa..2358fa3f7e4dcd86a6eda8644009afd6daaabf7a 100644 (file)
@@ -1,17 +1,19 @@
 ! Copyright (C) 2008 James Cash
 ! See http://factorcode.org/license.txt for BSD license.
-USING: lisp lisp.parser tools.test sequences math kernel parser ;
+USING: lisp lisp.parser tools.test sequences math kernel parser arrays ;
 
 IN: lisp.test
 
 [
     init-env
     
-    "#f" [ f ] lisp-define 
+    "#f" [ f ] lisp-define
     "#t" [ t ] lisp-define
     
-    "+" "math" "+" define-primitve
-    "-" "math" "-" define-primitve
+    "+" "math" "+" define-primitive
+    "-" "math" "-" define-primitive
+    
+    "list" [ >array ] lisp-define
     
     { 5 } [
       [ 2 3 ] "+" <lisp-symbol> funcall
@@ -22,26 +24,31 @@ IN: lisp.test
     ] unit-test
     
     { 3 } [
-      "((lambda (x y) (+ x y)) 1 2)" lisp-string>factor call
+      "((lambda (x y) (+ x y)) 1 2)" lisp-eval
     ] unit-test
     
     { 42 } [
-      "((lambda (x y z) (+ x (- y z))) 40 3 1)" lisp-string>factor call
+      "((lambda (x y z) (+ x (- y z))) 40 3 1)" lisp-eval
     ] unit-test
     
     { 1 } [
-      "(if #t 1 2)" lisp-string>factor call
+      "(if #t 1 2)" lisp-eval
     ] unit-test
     
     { "b" } [
-      "(cond (#f \"a\") (#t \"b\"))" lisp-string>factor call
+      "(cond (#f \"a\") (#t \"b\"))" lisp-eval
     ] unit-test
     
     { 5 } [
-      "(begin (+ 1 4))" lisp-string>factor call
+      "(begin (+ 1 4))" lisp-eval
     ] unit-test
     
     { 3 } [
-       "((lambda (x) (if x (begin (+ 1 2)) (- 3 5))) #t)" lisp-string>factor call
+       "((lambda (x) (if x (begin (+ 1 2)) (- 3 5))) #t)" lisp-eval
+    ] unit-test
+    
+    { { 1 2 3 4 5 } } [
+      "(list 1 2 3 4 5)" lisp-eval
     ] unit-test
-] with-interactive-vocabs
\ No newline at end of file
+
+] with-interactive-vocabs
index 0f5e4b4d2e2c0611f2bcd212d3ecc9c4f6368929..b034619d0d990a2689cce61879ced73103dfa05c 100644 (file)
 ! Copyright (C) 2008 James Cash
 ! See http://factorcode.org/license.txt for BSD license.
 USING: kernel peg sequences arrays strings combinators.lib
-namespaces combinators math bake locals locals.private accessors
-vectors syntax lisp.parser assocs parser sequences.lib words quotations ;
+namespaces combinators math locals locals.private accessors
+vectors syntax lisp.parser assocs parser sequences.lib words quotations
+fry lists ;
 IN: lisp
 
 DEFER: convert-form
 DEFER: funcall
 DEFER: lookup-var
+DEFER: lisp-macro?
+DEFER: lookup-macro
+DEFER: macro-call
 
 ! Functions to convert s-exps to quotations
 ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-: convert-body ( s-exp -- quot )
-  [ convert-form ] map [ ] [ compose ] reduce ; inline
-  
-: convert-if ( s-exp -- quot )
-  rest [ convert-form ] map reverse first3  [ % , , if ] bake ;
-  
-: convert-begin ( s-exp -- quot )  
-  rest [ convert-form ] map >quotation [ , [ funcall ] each ] bake ;
-  
-: convert-cond ( s-exp -- quot )  
-  rest [ body>> >array [ convert-form ] map first2 swap `{ [ % funcall ] , } bake ]
-  map >array [ , cond ] bake ;
-  
-: convert-general-form ( s-exp -- quot )
-  unclip convert-form swap convert-body [ , % funcall ] bake ;
+: convert-body ( cons -- quot )
+    [ ] [ convert-form compose ] reduce-cons ; inline
+  
+: convert-if ( cons -- quot )
+    cdr first3 [ convert-form ] tri@ '[ @ , , if ] ;
+    
+: convert-begin ( cons -- quot )  
+    cdr [ convert-form ] [ ] map-as '[ , [ funcall ] each ] ;
+    
+: convert-cond ( cons -- quot )  
+    cdr [ body>> first2 [ convert-form ] bi@ [ '[ @ funcall ] ] dip 2array ]
+    { } map-as '[ , cond ]  ;
+    
+: convert-general-form ( cons -- quot )
+    uncons convert-form swap convert-body swap '[ , @ funcall ] ;
 
 ! words for convert-lambda  
 <PRIVATE  
 : localize-body ( assoc body -- assoc newbody )  
-  [ dup lisp-symbol? [ over dupd [ name>> ] dip at swap or ]
-                     [ dup s-exp? [ body>> localize-body <s-exp> ] when ] if
-                   ] map ;
-  
+    [ dup lisp-symbol? [ over dupd [ name>> ] dip at swap or ]
+                     [ dup cons? [ localize-body ] when ] if
+                   ] map-cons ;
+    
 : localize-lambda ( body vars -- newbody newvars )
-  make-locals dup push-locals swap
-  [ swap localize-body <s-exp> convert-form swap pop-locals ] dip swap ;
+    make-locals dup push-locals swap
+    [ swap localize-body cons convert-form swap pop-locals ] dip swap ;
                    
-: split-lambda ( s-exp -- body vars )                   
-  first3 -rot nip [ body>> ] bi@ [ name>> ] map ; inline
-  
+: split-lambda ( cons -- body vars )                   
+    first3 -rot nip [ body>> ] bi@ [ name>> ] map ; inline
+    
 : rest-lambda ( body vars -- quot )  
-  "&rest" swap [ remove ] [ index ] 2bi
-  [ localize-lambda <lambda> ] dip
-  [ , cut swap [ % , ] bake , compose ] bake ;
-  
+    "&rest" swap [ index ] [ remove ] 2bi
+    localize-lambda <lambda>
+    '[ , cut '[ @ , ] , compose ] ;
+    
 : normal-lambda ( body vars -- quot )
-  localize-lambda <lambda> [ , compose ] bake ;
+    localize-lambda <lambda> '[ , compose ] ;
 PRIVATE>
-  
-: convert-lambda ( s-exp -- quot )  
-  split-lambda dup "&rest"  swap member? [ rest-lambda ] [ normal-lambda ] if ;
-  
-: convert-quoted ( s-exp -- quot )  
-  second [ , ] bake ;
-  
-: convert-list-form ( s-exp -- quot )  
-  dup first dup lisp-symbol?
-    [ name>>
-      { { "lambda" [ convert-lambda ] }
-        { "quote" [ convert-quoted ] }
-        { "if" [ convert-if ] }
-        { "begin" [ convert-begin ] }
-        { "cond" [ convert-cond ] }
-       [ drop convert-general-form ]
-      } case ]
-    [ drop convert-general-form ] if ;
-  
+    
+: convert-lambda ( cons -- quot )  
+    split-lambda "&rest" over member? [ rest-lambda ] [ normal-lambda ] if ;
+    
+: convert-quoted ( cons -- quot )  
+    cdr 1quotation ;
+    
+: form-dispatch ( lisp-symbol -- quot )
+    name>>
+    { { "lambda" [ convert-lambda ] }
+      { "quote" [ convert-quoted ] }
+      { "if" [ convert-if ] }
+      { "begin" [ convert-begin ] }
+      { "cond" [ convert-cond ] }
+     [ drop convert-general-form ]
+    } case ;
+    
+: macro-expand ( cons -- quot )
+    uncons lookup-macro macro-call convert-form ;
+    
+: convert-list-form ( cons -- quot )  
+    dup car
+    { { [ dup lisp-macro?  ] [ macro-expand ] }
+      { [ dup lisp-symbol? ] [ form-dispatch ] } 
+     [ drop convert-general-form ]
+    } cond ;
+    
 : convert-form ( lisp-form -- quot )
-  { { [ dup s-exp? ] [ body>> convert-list-form ] }
-    { [ dup lisp-symbol? ] [ [ , lookup-var ] bake ] }
-    [ [ , ] bake ]
-  } cond ;
-                
+    {
+      { [ dup cons? ] [ convert-list-form ] }
+      { [ dup lisp-symbol? ] [ '[ , lookup-var ] ] }
+     [ 1quotation ]
+    } cond ;
+    
 : lisp-string>factor ( str -- quot )
-  lisp-expr parse-result-ast convert-form lambda-rewrite call ;
-  
+    lisp-expr parse-result-ast convert-form lambda-rewrite call ;
+    
+: lisp-eval ( str -- * )    
+  lisp-string>factor call ;
+    
 ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
 SYMBOL: lisp-env
 ERROR: no-such-var var ;
 
 : init-env ( -- )
-  H{ } clone lisp-env set ;
+    H{ } clone lisp-env set ;
 
 : lisp-define ( name quot -- )
-  swap lisp-env get set-at ;
-  
+    swap lisp-env get set-at ;
+    
 : lisp-get ( name -- word )
-  dup lisp-env get at [ ] [ no-such-var throw ] ?if ;
-  
+    dup lisp-env get at [ ] [ no-such-var throw ] ?if ;
+    
 : lookup-var ( lisp-symbol -- quot )
-  name>> lisp-get ;
-  
+    name>> lisp-get ;
+    
 : funcall ( quot sym -- * )
-  dup lisp-symbol?  [ lookup-var ] when call ; inline
-  
-: define-primitve ( name vocab word -- )  
-  swap lookup [ [ , ] compose call ] bake lisp-define ;
\ No newline at end of file
+    dup lisp-symbol?  [ lookup-var ] when call ; inline
+    
+: define-primitive ( name vocab word -- )  
+    swap lookup 1quotation '[ , compose call ] lisp-define ;
\ No newline at end of file
index 98a6d2a6ba113523496b135d50e22cbe628492ed..41254db5b3c3c26075a0a73214ae3d2518cbfc3c 100644 (file)
@@ -1,6 +1,6 @@
 ! Copyright (C) 2008 James Cash
 ! See http://factorcode.org/license.txt for BSD license.
-USING: lisp.parser tools.test peg peg.ebnf ;
+USING: lisp.parser tools.test peg peg.ebnf lists ;
 
 IN: lisp.parser.tests
 
@@ -9,38 +9,61 @@ IN: lisp.parser.tests
 ] unit-test
 
 { -42  }  [
-  "-42" "atom" \ lisp-expr rule parse parse-result-ast
+    "-42" "atom" \ lisp-expr rule parse parse-result-ast
 ] unit-test
 
 { 37/52 } [
-  "37/52" "atom" \ lisp-expr rule parse parse-result-ast
+    "37/52" "atom" \ lisp-expr rule parse parse-result-ast
 ] unit-test
 
 { 123.98 } [
-  "123.98" "atom" \ lisp-expr rule parse parse-result-ast
+    "123.98" "atom" \ lisp-expr rule parse parse-result-ast
 ] unit-test
 
 { "" } [
-  "\"\"" "atom" \ lisp-expr rule parse parse-result-ast
+    "\"\"" "atom" \ lisp-expr rule parse parse-result-ast
 ] unit-test
 
 { "aoeu" } [
-  "\"aoeu\"" "atom" \ lisp-expr rule parse parse-result-ast
+    "\"aoeu\"" "atom" \ lisp-expr rule parse parse-result-ast
 ] unit-test
 
 { "aoeu\"de" } [
-  "\"aoeu\\\"de\"" "atom" \ lisp-expr rule parse parse-result-ast
+    "\"aoeu\\\"de\"" "atom" \ lisp-expr rule parse parse-result-ast
 ] unit-test
 
 { T{ lisp-symbol f "foobar" } } [
-  "foobar" "atom" \ lisp-expr rule parse parse-result-ast
+    "foobar" "atom" \ lisp-expr rule parse parse-result-ast
 ] unit-test
 
 { T{ lisp-symbol f "+" } } [
-  "+" "atom" \ lisp-expr rule parse parse-result-ast
+    "+" "atom" \ lisp-expr rule parse parse-result-ast
 ] unit-test
 
-{ T{ s-exp f
-     V{ T{ lisp-symbol f "foo" } 1 2 "aoeu" } } } [
-  "(foo 1 2 \"aoeu\")" lisp-expr parse-result-ast
+{ T{ cons f f f }
+} [
+    "()" lisp-expr parse-result-ast
+] unit-test
+
+{ T{
+    cons
+    f
+    T{ lisp-symbol f "foo" }
+    T{
+        cons
+        f
+        1
+        T{ cons f 2 T{ cons f "aoeu" T{ cons f f f } } }
+    } } } [
+    "(foo 1 2 \"aoeu\")" lisp-expr parse-result-ast
+] unit-test
+
+{ T{ cons f
+       1
+       T{ cons f
+           T{ cons f 3 T{ cons f 4 T{ cons f f f } } }
+           T{ cons f 2 T{ cons f f } } }
+   }
+} [
+    "(1 (3 4) 2)" lisp-expr parse-result-ast
 ] unit-test
\ No newline at end of file
index cf5ff56331c8664363fcc505e828b4dd4be499ba..1e37193d3a0c2e6dba749a0a403efb73b8ed7522 100644 (file)
@@ -1,16 +1,13 @@
 ! Copyright (C) 2008 James Cash
 ! See http://factorcode.org/license.txt for BSD license.
-USING: kernel peg.ebnf peg.expr math.parser sequences arrays strings
-combinators.lib math ;
+USING: kernel peg peg.ebnf peg.expr math.parser sequences arrays strings
+combinators.lib math fry accessors lists ;
 
 IN: lisp.parser
 
 TUPLE: lisp-symbol name ;
 C: <lisp-symbol> lisp-symbol
 
-TUPLE: s-exp body ;
-C: <s-exp> s-exp
-
 EBNF: lisp-expr
 _            = (" " | "\t" | "\n")*
 LPAREN       = "("
@@ -24,8 +21,9 @@ rational     = integer "/" (digit)+                      => [[ first3 nip string
 number       = float
               | rational
               | integer
-id-specials  = "!" | "$" | "%" | "&" | "*" | "/" | ":" | "<" | "#"
-              | " =" | ">" | "?" | "^" | "_" | "~" | "+" | "-" | "." | "@"
+id-specials  = "!" | "$" | "%" | "&" | "*" | "/" | ":"
+              | "<" | "#" | " =" | ">" | "?" | "^" | "_"
+              | "~" | "+" | "-" | "." | "@"
 letters      = [a-zA-Z]                                  => [[ 1array >string ]]
 initials     = letters | id-specials
 numbers      = [0-9]                                     => [[ 1array >string ]]
@@ -36,6 +34,6 @@ string       = dquote ( escaped | !(dquote) . )*  dquote => [[ second >string ]]
 atom         = number
               | identifier
               | string
-list-item    = _ (atom|s-expression) _                   => [[ second ]]
-s-expression = LPAREN (list-item)* RPAREN                => [[ second <s-exp> ]]
+list-item    = _ ( atom | s-expression ) _               => [[ second ]]
+s-expression = LPAREN (list-item)* RPAREN                => [[ second seq>cons ]]
 ;EBNF
\ No newline at end of file
diff --git a/extra/lists/authors.txt b/extra/lists/authors.txt
new file mode 100644 (file)
index 0000000..4b7af4a
--- /dev/null
@@ -0,0 +1 @@
+James Cash
diff --git a/extra/lists/lazy/authors.txt b/extra/lists/lazy/authors.txt
new file mode 100644 (file)
index 0000000..f6ba9ba
--- /dev/null
@@ -0,0 +1,3 @@
+Chris Double
+Samuel Tardieu
+Matthew Willis
diff --git a/extra/lists/lazy/examples/authors.txt b/extra/lists/lazy/examples/authors.txt
new file mode 100755 (executable)
index 0000000..44b06f9
--- /dev/null
@@ -0,0 +1 @@
+Chris Double
diff --git a/extra/lists/lazy/examples/examples-tests.factor b/extra/lists/lazy/examples/examples-tests.factor
new file mode 100644 (file)
index 0000000..d4e3ed7
--- /dev/null
@@ -0,0 +1,5 @@
+USING: lazy-lists.examples lazy-lists tools.test ;
+IN: lazy-lists.examples.tests
+
+[ { 1 3 5 7 } ] [ 4 odds ltake list>array ] unit-test
+[ { 0 1 4 9 16 } ] [ first-five-squares ] unit-test
diff --git a/extra/lists/lazy/examples/examples.factor b/extra/lists/lazy/examples/examples.factor
new file mode 100644 (file)
index 0000000..844ae31
--- /dev/null
@@ -0,0 +1,15 @@
+! Rewritten by Matthew Willis, July 2006
+! Copyright (C) 2004 Chris Double.
+! See http://factorcode.org/license.txt for BSD license.
+
+USING: lazy-lists math kernel sequences quotations ;
+IN: lazy-lists.examples
+
+: naturals 0 lfrom ;
+: positives 1 lfrom ;
+: evens 0 [ 2 + ] lfrom-by ;
+: odds 1 lfrom [ 2 mod 1 = ] lfilter ;
+: powers-of-2 1 [ 2 * ] lfrom-by ;
+: ones 1 [ ] lfrom-by ;
+: squares naturals [ dup * ] lmap ;
+: first-five-squares 5 squares ltake list>array ;
diff --git a/extra/lists/lazy/lazy-docs.factor b/extra/lists/lazy/lazy-docs.factor
new file mode 100644 (file)
index 0000000..1de9897
--- /dev/null
@@ -0,0 +1,150 @@
+! Copyright (C) 2006 Chris Double.
+! See http://factorcode.org/license.txt for BSD license.
+
+USING: help.markup help.syntax sequences strings lists ;
+IN: lists.lazy 
+
+HELP: lazy-cons
+{ $values { "car" "a quotation with stack effect ( -- X )" } { "cdr" "a quotation with stack effect ( -- cons )" } { "promise" "the resulting cons object" } }
+{ $description "Constructs a cons object for a lazy list from two quotations. The " { $snippet "car" } " quotation should return the head of the list, and the " { $snippet "cons" } " quotation the tail when called. When " { $link cons } " or " { $link cdr } " are called on the lazy-cons object then the appropriate quotation is called." } 
+{ $see-also cons car cdr nil nil? } ;
+
+{ 1lazy-list 2lazy-list 3lazy-list } related-words
+
+HELP: 1lazy-list
+{ $values { "a" "a quotation with stack effect ( -- X )" } { "lazy-cons" "a lazy-cons object" } }
+{ $description "Create a lazy list with 1 element. The element is the result of calling the quotation. The quotation is only called when the list element is requested." } ;
+
+HELP: 2lazy-list
+{ $values { "a" "a quotation with stack effect ( -- X )" } { "b" "a quotation with stack effect ( -- X )" } { "lazy-cons" "a lazy-cons object" } }
+{ $description "Create a lazy list with 2 elements. The elements are the result of calling the quotations. The quotations are only called when the list elements are requested." } ;
+
+HELP: 3lazy-list
+{ $values { "a" "a quotation with stack effect ( -- X )" } { "b" "a quotation with stack effect ( -- X )" } { "c" "a quotation with stack effect ( -- X )" } { "lazy-cons" "a lazy-cons object" } }
+{ $description "Create a lazy list with 3 elements. The elements are the result of calling the quotations. The quotations are only called when the list elements are requested." } ;
+
+HELP: <memoized-cons>
+{ $values { "cons" "a cons object" } { "memoized-cons" "the resulting memoized-cons object" } }
+{ $description "Constructs a cons object that wraps an existing cons object. Requests for the car, cdr and nil? will be remembered after the first call, and the previous result returned on subsequent calls." } 
+{ $see-also cons car cdr nil nil? } ;
+
+HELP: lnth
+{ $values { "n" "an integer index" } { "list" "a cons object" } { "elt" "the element at the nth index" } }
+{ $description "Outputs the nth element of the list." } 
+{ $see-also llength cons car cdr } ;
+
+HELP: llength
+{ $values { "list" "a cons object" } { "n" "a non-negative integer" } }
+{ $description "Outputs the length of the list. This should not be called on an infinite list." } 
+{ $see-also lnth cons car cdr } ;
+
+HELP: uncons
+{ $values { "cons" "a cons object" } { "car" "the head of the list" } { "cdr" "the tail of the list" } }
+{ $description "Put the head and tail of the list on the stack." } ;
+
+{ leach lreduce lmap lmap-with ltake lfilter lappend lfrom lfrom-by lconcat lcartesian-product lcartesian-product* lcomp lcomp* lmerge lreduce lwhile luntil } related-words
+
+HELP: leach
+{ $values { "list" "a cons object" } { "quot" "a quotation with stack effect ( obj -- )" } }
+{ $description "Call the quotation for each item in the list." } ;
+
+HELP: lreduce
+{ $values { "list" "a cons object" } { "identity" "an object" } { "quot" "a quotation with stack effect ( prev elt -- next )" } { "result" "the final result" } }
+{ $description "Combines successive elements of the list using a binary operation, and outputs the final result." } ;
+
+HELP: lmap
+{ $values { "list" "a cons object" } { "quot" "a quotation with stack effect ( obj -- X )" } { "result" "resulting cons object" } }
+{ $description "Perform a similar functionality to that of the " { $link map } " word, but in a lazy manner. No evaluation of the list elements occurs initially but a " { $link <lazy-map> } " object is returned which conforms to the list protocol. Calling " { $link car } ", " { $link cdr } " or " { $link nil? } " on this will evaluate elements as required." } ;
+
+HELP: lmap-with
+{ $values { "value" "an object" } { "list" "a cons object" } { "quot" "a quotation with stack effect ( obj elt -- X )" } { "result" "resulting cons object" } }
+{ $description "Variant of " { $link lmap } " which pushes a retained object on each invocation of the quotation." } ;
+
+HELP: ltake
+{ $values { "n" "a non negative integer" } { "list" "a cons object" } { "result" "resulting cons object" } }
+{ $description "Outputs a lazy list containing the first n items in the list. This is done a lazy manner. No evaluation of the list elements occurs initially but a " { $link <lazy-take> } " object is returned which conforms to the list protocol. Calling " { $link car } ", " { $link cdr } " or " { $link nil? } " on this will evaluate elements as required." } ;
+
+HELP: lfilter
+{ $values { "list" "a cons object" } { "quot" "a quotation with stack effect ( -- X )" } { "result" "resulting cons object" } }
+{ $description "Perform a similar functionality to that of the " { $link filter } " word, but in a lazy manner. No evaluation of the list elements occurs initially but a " { $link <lazy-filter> } " object is returned which conforms to the list protocol. Calling " { $link car } ", " { $link cdr } " or " { $link nil? } " on this will evaluate elements as required." } ;
+
+HELP: lwhile
+{ $values { "list" "a cons object" } { "quot" "a quotation with stack effect ( X -- bool )" } { "result" "resulting cons object" } }
+{ $description "Outputs a lazy list containing the first items in the list as long as " { $snippet "quot" } " evaluates to t. No evaluation of the list elements occurs initially but a " { $link <lazy-while> } " object is returned with conforms to the list protocol. Calling " { $link car } ", " { $link cdr } " or " { $link nil? } " on this will evaluate elements as required." } ;
+
+HELP: luntil
+{ $values { "list" "a cons object" } { "quot" "a quotation with stack effect ( X -- bool )" } { "result" "resulting cons object" } }
+{ $description "Outputs a lazy list containing the first items in the list until after " { $snippet "quot" } " evaluates to t. No evaluation of the list elements occurs initially but a " { $link <lazy-while> } " object is returned with conforms to the list protocol. Calling " { $link car } ", " { $link cdr } " or " { $link nil? } " on this will evaluate elements as required." } ;
+
+HELP: list>vector
+{ $values { "list" "a cons object" } { "vector" "the list converted to a vector" } }
+{ $description "Convert a list to a vector. If the list is a lazy infinite list then this will enter an infinite loop." } 
+{ $see-also list>array } ;
+
+HELP: list>array
+{ $values { "list" "a cons object" } { "array" "the list converted to an array" } }
+{ $description "Convert a list to an array. If the list is a lazy infinite list then this will enter an infinite loop." } 
+{ $see-also list>vector } ;
+
+HELP: lappend
+{ $values { "list1" "a cons object" } { "list2" "a cons object" } { "result" "a lazy list of list2 appended to list1" } }
+{ $description "Perform a similar functionality to that of the " { $link append } " word, but in a lazy manner. No evaluation of the list elements occurs initially but a " { $link <lazy-append> } " object is returned which conforms to the list protocol. Calling " { $link car } ", " { $link cdr } " or " { $link nil? } " on this will evaluate elements as required. Successive calls to " { $link cdr } " will iterate through list1, followed by list2." } ;
+
+HELP: lfrom-by
+{ $values { "n" "an integer" } { "quot" "a quotation with stack effect ( -- int )" } { "list" "a lazy list of integers" } }
+{ $description "Return an infinite lazy list of values starting from n, with each successive value being the result of applying quot to n." } ;
+
+HELP: lfrom
+{ $values { "n" "an integer" } { "list" "a lazy list of integers" } }
+{ $description "Return an infinite lazy list of incrementing integers starting from n." } ;
+
+HELP: seq>list
+{ $values { "index" "an integer 0 or greater" } { "seq" "a sequence" } { "list" "a list" } }
+{ $description "Convert the sequence into a list, starting from the 'index' offset into the sequence." } 
+{ $see-also >list } ;
+
+HELP: >list
+{ $values { "object" "an object" } { "list" "a list" } }
+{ $description "Convert the object into a list. Existing lists are passed through intact, sequences are converted using " { $link seq>list } " and other objects cause an error to be thrown." } 
+{ $see-also seq>list } ;
+
+HELP: lconcat
+{ $values { "list" "a list of lists" } { "result" "a list" } }
+{ $description "Concatenates a list of lists together into one list." } ;
+
+HELP: lcartesian-product
+{ $values { "list1" "a list" } { "list2" "a list" } { "result" "list of cartesian products" } }
+{ $description "Given two lists, return a list containing the cartesian product of those lists." } ;
+
+HELP: lcartesian-product*
+{ $values { "lists" "a list of lists" } { "result" "list of cartesian products" } }
+{ $description "Given a list of lists, return a list containing the cartesian product of those lists." } ;
+
+HELP: lcomp
+{ $values { "list" "a list of lists" } { "quot" "a quotation with stack effect ( seq -- X )" } { "result" "the resulting list" } }
+{ $description "Get the cartesian product of the lists in " { $snippet "list" } " and call " { $snippet "quot" } " call with each element from the cartesian product on the stack, the result of which is returned in the final " { $snippet "list" } "." } ;
+
+HELP: lcomp*
+{ $values { "list" "a list of lists" } { "guards" "a sequence of quotations with stack effect ( seq -- bool )" } { "quot" "a quotation with stack effect ( seq -- X )" } { "list" "the resulting list" } { "result" "a list" } }
+{ $description "Get the cartesian product of the lists in " { $snippet "list" } ", filter it by applying each guard quotation to it and call " { $snippet "quot" } " call with each element from the remaining cartesian product items on the stack, the result of which is returned in the final " { $snippet "list" } "." }
+{ $examples
+  { $code "{ 1 2 3 } >list { 4 5 6 } >list 2list { [ first odd? ] } [ first2 + ] lcomp*" }
+} ;
+
+HELP: lmerge
+{ $values { "list1" "a list" } { "list2" "a list" } { "result" "lazy list merging list1 and list2" } }
+{ $description "Return the result of merging the two lists in a lazy manner." } 
+{ $examples
+  { $example "USING: lazy-lists prettyprint ;" "{ 1 2 3 } >list { 4 5 6 } >list lmerge list>array ." "{ 1 4 2 5 3 6 }" }
+} ;
+
+HELP: lcontents
+{ $values { "stream" "a stream" } { "result" string } }
+{ $description "Returns a lazy list of all characters in the file. " { $link car } " returns the next character in the file, " { $link cdr } " returns the remaining characters as a lazy list. " { $link nil? } " indicates end of file." } 
+{ $see-also llines } ;
+
+HELP: llines
+{ $values { "stream" "a stream" } { "result" "a list" } }
+{ $description "Returns a lazy list of all lines in the file. " { $link car } " returns the next lines in the file, " { $link cdr } " returns the remaining lines as a lazy list. " { $link nil? } " indicates end of file." } 
+{ $see-also lcontents } ;
+
diff --git a/extra/lists/lazy/lazy-tests.factor b/extra/lists/lazy/lazy-tests.factor
new file mode 100644 (file)
index 0000000..f4bb7b5
--- /dev/null
@@ -0,0 +1,29 @@
+! Copyright (C) 2006 Matthew Willis and Chris Double.
+! See http://factorcode.org/license.txt for BSD license.
+!
+USING: lists lists.lazy tools.test kernel math io sequences ;
+IN: lists.lazy.tests
+
+[ { 1 2 3 4 } ] [
+  { 1 2 3 4 } >list list>array
+] unit-test
+
+[ { { 1 4 } { 1 5 } { 2 4 } { 2 5 } { 3 4 } { 3 5 } } ] [
+  { 1 2 3 } >list { 4 5 } >list 2list lcartesian-product* list>array
+] unit-test
+
+[ { { 1 4 } { 1 5 } { 2 4 } { 2 5 } { 3 4 } { 3 5 } } ] [
+  { 1 2 3 } >list { 4 5 } >list lcartesian-product list>array
+] unit-test
+
+[ { 5 6 6 7 7 8 } ] [ 
+  { 1 2 3 } >list { 4 5 } >list 2list [ first2 + ] lcomp list>array
+] unit-test
+
+[ { 5 6 7 8 } ] [ 
+  { 1 2 3 } >list { 4 5 } >list 2list { [ first odd? ] } [ first2 + ] lcomp* list>array
+] unit-test
+
+[ { 4 5 6 } ] [ 
+    3 { 1 2 3 } >list [ + ] lmap-with list>array
+] unit-test
diff --git a/extra/lists/lazy/lazy.factor b/extra/lists/lazy/lazy.factor
new file mode 100644 (file)
index 0000000..f8b1a6e
--- /dev/null
@@ -0,0 +1,409 @@
+! Copyright (C) 2004 Chris Double.
+! See http://factorcode.org/license.txt for BSD license.
+!
+! Updated by Matthew Willis, July 2006
+! Updated by Chris Double, September 2006
+! Updated by James Cash, June 2008
+!
+USING: kernel sequences math vectors arrays namespaces
+quotations promises combinators io lists accessors ;
+IN: lists.lazy
+
+M: promise car ( promise -- car )
+    force car ;
+
+M: promise cdr ( promise -- cdr )
+    force cdr ;
+
+M: promise nil? ( cons -- bool )
+    force nil? ;
+    
+! Both 'car' and 'cdr' are promises
+TUPLE: lazy-cons car cdr ;
+
+: lazy-cons ( car cdr -- promise )
+    [ promise ] bi@ \ lazy-cons boa
+    T{ promise f f t f } clone
+    [ set-promise-value ] keep ;
+
+M: lazy-cons car ( lazy-cons -- car )
+    car>> force ;
+
+M: lazy-cons cdr ( lazy-cons -- cdr )
+    cdr>> force ;
+
+M: lazy-cons nil? ( lazy-cons -- bool )
+    nil eq? ;
+
+: 1lazy-list ( a -- lazy-cons )
+    [ nil ] lazy-cons ;
+
+: 2lazy-list ( a b -- lazy-cons )
+    1lazy-list 1quotation lazy-cons ;
+
+: 3lazy-list ( a b c -- lazy-cons )
+    2lazy-list 1quotation lazy-cons ;
+
+: lnth ( n list -- elt )
+    swap [ cdr ] times car ;
+
+: (llength) ( list acc -- n )
+    over nil? [ nip ] [ [ cdr ] dip 1+ (llength) ] if ;
+
+: llength ( list -- n )
+    0 (llength) ;
+
+: leach ( list quot -- )
+    over nil? [ 2drop ] [ [ uncons ] dip tuck call leach ] if ; inline
+
+: lreduce ( list identity quot -- result )
+    swapd leach ; inline
+
+TUPLE: memoized-cons original car cdr nil? ;
+
+: not-memoized ( -- obj )
+    { } ;
+
+: not-memoized? ( obj -- bool )
+    not-memoized eq? ;
+
+: <memoized-cons> ( cons -- memoized-cons )
+    not-memoized not-memoized not-memoized
+    memoized-cons boa ;
+
+M: memoized-cons car ( memoized-cons -- car )
+    dup car>> not-memoized? [
+        dup original>> car [ >>car drop ] keep
+    ] [
+        car>>
+    ] if ;
+
+M: memoized-cons cdr ( memoized-cons -- cdr )
+    dup cdr>> not-memoized? [
+        dup original>> cdr [ >>cdr drop ] keep
+    ] [
+        cdr>>
+    ] if ;
+
+M: memoized-cons nil? ( memoized-cons -- bool )
+    dup nil?>> not-memoized? [
+        dup original>> nil? [ >>nil? drop ] keep
+    ] [
+        nil?>>
+    ] if ;
+
+TUPLE: lazy-map cons quot ;
+
+C: <lazy-map> lazy-map
+
+: lmap ( list quot -- result )
+        over nil? [ 2drop nil ] [ <lazy-map> <memoized-cons> ] if ;
+
+M: lazy-map car ( lazy-map -- car )
+    [ cons>> car ] keep
+    quot>> call ;
+
+M: lazy-map cdr ( lazy-map -- cdr )
+    [ cons>> cdr ] keep
+    quot>> lmap ;
+
+M: lazy-map nil? ( lazy-map -- bool )
+    cons>> nil? ;
+
+: lmap-with ( value list quot -- result )
+    with lmap ;
+
+TUPLE: lazy-take n cons ;
+
+C: <lazy-take> lazy-take
+
+: ltake ( n list -- result )
+        over zero? [ 2drop nil ] [ <lazy-take> ] if ;
+
+M: lazy-take car ( lazy-take -- car )
+    cons>> car ;
+
+M: lazy-take cdr ( lazy-take -- cdr )
+    [ n>> 1- ] keep
+    cons>> cdr ltake ;
+
+M: lazy-take nil? ( lazy-take -- bool )
+    dup n>> zero? [
+        drop t
+    ] [
+        cons>> nil?
+    ] if ;
+
+TUPLE: lazy-until cons quot ;
+
+C: <lazy-until> lazy-until
+
+: luntil ( list quot -- result )
+    over nil? [ drop ] [ <lazy-until> ] if ;
+
+M: lazy-until car ( lazy-until -- car )
+     cons>> car ;
+
+M: lazy-until cdr ( lazy-until -- cdr )
+     [ cons>> uncons ] keep quot>> tuck call
+     [ 2drop nil ] [ luntil ] if ;
+
+M: lazy-until nil? ( lazy-until -- bool )
+     drop f ;
+
+TUPLE: lazy-while cons quot ;
+
+C: <lazy-while> lazy-while
+
+: lwhile ( list quot -- result )
+    over nil? [ drop ] [ <lazy-while> ] if ;
+
+M: lazy-while car ( lazy-while -- car )
+     cons>> car ;
+
+M: lazy-while cdr ( lazy-while -- cdr )
+     [ cons>> cdr ] keep quot>> lwhile ;
+
+M: lazy-while nil? ( lazy-while -- bool )
+     [ car ] keep quot>> call not ;
+
+TUPLE: lazy-filter cons quot ;
+
+C: <lazy-filter> lazy-filter
+
+: lfilter ( list quot -- result )
+        over nil? [ 2drop nil ] [ <lazy-filter> <memoized-cons> ] if ;
+
+: car-filter?    ( lazy-filter -- ? )
+    [ cons>> car ] keep
+    quot>> call ;
+
+: skip ( lazy-filter -- )
+    dup cons>> cdr >>cons ;
+
+M: lazy-filter car ( lazy-filter -- car )
+    dup car-filter? [ cons>> ] [ dup skip ] if car ;
+
+M: lazy-filter cdr ( lazy-filter -- cdr )
+    dup car-filter? [
+        [ cons>> cdr ] keep
+        quot>> lfilter
+    ] [
+        dup skip cdr
+    ] if ;
+
+M: lazy-filter nil? ( lazy-filter -- bool )
+    dup cons>> nil? [
+        drop t
+    ] [
+        dup car-filter? [
+            drop f
+        ] [
+            dup skip nil?
+        ] if
+    ] if ;
+
+: list>vector ( list -- vector )
+    [ [ , ] leach ] V{ } make ;
+
+: list>array ( list -- array )
+    [ [ , ] leach ] { } make ;
+
+TUPLE: lazy-append list1 list2 ;
+
+C: <lazy-append> lazy-append
+
+: lappend ( list1 list2 -- result )
+    over nil? [ nip ] [ <lazy-append> ] if ;
+
+M: lazy-append car ( lazy-append -- car )
+    list1>> car ;
+
+M: lazy-append cdr ( lazy-append -- cdr )
+    [ list1>> cdr    ] keep
+    list2>> lappend ;
+
+M: lazy-append nil? ( lazy-append -- bool )
+     drop f ;
+
+TUPLE: lazy-from-by n quot ;
+
+C: lfrom-by lazy-from-by ( n quot -- list )
+
+: lfrom ( n -- list )
+    [ 1+ ] lfrom-by ;
+
+M: lazy-from-by car ( lazy-from-by -- car )
+    n>> ;
+
+M: lazy-from-by cdr ( lazy-from-by -- cdr )
+    [ n>> ] keep
+    quot>> dup slip lfrom-by ;
+
+M: lazy-from-by nil? ( lazy-from-by -- bool )
+    drop f ;
+
+TUPLE: lazy-zip list1 list2 ;
+
+C: <lazy-zip> lazy-zip
+
+: lzip ( list1 list2 -- lazy-zip )
+        over nil? over nil? or
+        [ 2drop nil ] [ <lazy-zip> ] if ;
+
+M: lazy-zip car ( lazy-zip -- car )
+        [ list1>> car ] keep list2>> car 2array ;
+
+M: lazy-zip cdr ( lazy-zip -- cdr )
+        [ list1>> cdr ] keep list2>> cdr lzip ;
+
+M: lazy-zip nil? ( lazy-zip -- bool )
+        drop f ;
+
+TUPLE: sequence-cons index seq ;
+
+C: <sequence-cons> sequence-cons
+
+: seq>list ( index seq -- list )
+    2dup length >= [
+        2drop nil
+    ] [
+        <sequence-cons>
+    ] if ;
+
+M: sequence-cons car ( sequence-cons -- car )
+    [ index>> ] keep
+    seq>> nth ;
+
+M: sequence-cons cdr ( sequence-cons -- cdr )
+    [ index>> 1+ ] keep
+    seq>> seq>list ;
+
+M: sequence-cons nil? ( sequence-cons -- bool )
+        drop f ;
+
+: >list ( object -- list )
+    {
+        { [ dup sequence? ] [ 0 swap seq>list ] }
+        { [ dup list?         ] [ ] }
+        [ "Could not convert object to a list" throw ]
+    } cond ;
+
+TUPLE: lazy-concat car cdr ;
+
+C: <lazy-concat> lazy-concat
+
+DEFER: lconcat
+
+: (lconcat) ( car cdr -- list )
+    over nil? [
+        nip lconcat
+    ] [
+        <lazy-concat>
+    ] if ;
+
+: lconcat ( list -- result )
+    dup nil? [
+        drop nil
+    ] [
+        uncons swap (lconcat)
+    ] if ;
+
+M: lazy-concat car ( lazy-concat -- car )
+    car>> car ;
+
+M: lazy-concat cdr ( lazy-concat -- cdr )
+    [ car>> cdr ] keep cdr>> (lconcat) ;
+
+M: lazy-concat nil? ( lazy-concat -- bool )
+    dup car>> nil? [
+        cdr>> nil?
+    ] [
+        drop f
+    ] if ;
+
+: lcartesian-product ( list1 list2 -- result )
+    swap [ swap [ 2array ] lmap-with ] lmap-with lconcat ;
+
+: lcartesian-product* ( lists -- result )
+    dup nil? [
+        drop nil
+    ] [
+        [ car ] keep cdr [ car lcartesian-product ] keep cdr list>array swap [
+            swap [ swap [ suffix ] lmap-with ] lmap-with lconcat
+        ] reduce
+    ] if ;
+
+: lcomp ( list quot -- result )
+    [ lcartesian-product* ] dip lmap ;
+
+: lcomp* ( list guards quot -- result )
+    [ [ lcartesian-product* ] dip [ lfilter ] each ] dip lmap ;
+
+DEFER: lmerge
+
+: (lmerge) ( list1 list2 -- result )
+    over [ car ] curry -rot
+    [
+        dup [ car ] curry -rot
+        [
+            [ cdr ] bi@ lmerge
+        ] 2curry lazy-cons
+    ] 2curry lazy-cons ;
+
+: lmerge ( list1 list2 -- result )
+    {
+        { [ over nil? ] [ nip     ] }
+        { [ dup nil?    ]    [ drop ] }
+        { [ t                 ]    [ (lmerge) ] }
+    } cond ;
+
+TUPLE: lazy-io stream car cdr quot ;
+
+C: <lazy-io> lazy-io
+
+: lcontents ( stream -- result )
+    f f [ stream-read1 ] <lazy-io> ;
+
+: llines ( stream -- result )
+    f f [ stream-readln ] <lazy-io> ;
+
+M: lazy-io car ( lazy-io -- car )
+    dup car>> dup [
+        nip
+    ] [
+        drop dup stream>> over quot>> call
+        swap dupd set-lazy-io-car
+    ] if ;
+
+M: lazy-io cdr ( lazy-io -- cdr )
+    dup cdr>> dup [
+        nip
+    ] [
+        drop dup
+        [ stream>> ] keep
+        [ quot>> ] keep
+        car [
+            [ f f ] dip <lazy-io> [ >>cdr drop ] keep
+        ] [
+            3drop nil
+        ] if
+    ] if ;
+
+M: lazy-io nil? ( lazy-io -- bool )
+    car not ;
+
+INSTANCE: sequence-cons list
+INSTANCE: memoized-cons list
+INSTANCE: promise list
+INSTANCE: lazy-io list
+INSTANCE: lazy-concat list
+INSTANCE: lazy-cons list
+INSTANCE: lazy-map list
+INSTANCE: lazy-take list
+INSTANCE: lazy-append list
+INSTANCE: lazy-from-by list
+INSTANCE: lazy-zip list
+INSTANCE: lazy-while list
+INSTANCE: lazy-until list
+INSTANCE: lazy-filter list
diff --git a/extra/lists/lazy/old-doc.html b/extra/lists/lazy/old-doc.html
new file mode 100644 (file)
index 0000000..4c04301
--- /dev/null
@@ -0,0 +1,361 @@
+<html>
+  <head>
+    <title>Lazy Evaluation</title>
+    <link rel="stylesheet" type="text/css" href="style.css">
+      </head>
+  <body>
+    <h1>Lazy Evaluation</h1>
+<p>The 'lazy' vocabulary adds lazy lists to Factor. This provides the
+    ability to describe infinite structures, and to delay execution of
+    expressions until they are actually used.</p>
+<p>Lazy lists, like normal lists, are composed of a head and tail. In
+    a lazy list the head and tail are something called a 'promise'. 
+    To convert a
+    'promise' into its actual value a word called 'force' is used. To
+    convert a value into a 'promise' the word to use is 'delay'.</p>
+<table border="1">
+<tr><td><a href="#delay">delay</a></td></tr>
+<tr><td><a href="#force">force</a></td></tr>
+</table>
+
+<p>Many of the lazy list words are named similar to the standard list
+    words but with an 'l' suffixed to it. Here are the commonly used
+    words and their equivalent list operation:</p>
+<table border="1">
+<tr><th>Lazy List</th><th>Normal List</th></tr>
+<tr><td><a href="#lnil">lnil</a></td><td>[ ]</td></tr>
+<tr><td><a href="#lnilp">lnil?</a></td><td>Test for nil value</td></tr>
+<tr><td><a href="#lcons">lcons</a></td><td>cons</td></tr>
+<tr><td><a href="#lunit">lunit</a></td><td>unit</td></tr>
+<tr><td><a href="#lcar">lcar</a></td><td>car</td></tr>
+<tr><td><a href="#lcdr">lcdr</a></td><td>cdr</td></tr>
+<tr><td><a href="#lnth">lnth</a></td><td>nth</td></tr>
+<tr><td><a href="#luncons">luncons</a></td><td>uncons</td></tr>
+<tr><td><a href="#lmap">lmap</a></td><td>map</td></tr>
+<tr><td><a href="#lsubset">lsubset</a></td><td>subset</td></tr>
+<tr><td><a href="#leach">leach</a></td><td>each</td></tr>
+<tr><td><a href="#lappend">lappend</a></td><td>append</td></tr>
+</table>
+<p>A few additional words specific to lazy lists are:</p>
+<table border="1">
+<tr><td><a href="#ltake">ltake</a></td><td>Returns a normal list containing a specified
+number of items from the lazy list.</td></tr>
+<tr><td><a href="#lappendstar">lappend*</a></td><td>Given a lazy list of lazy lists,
+concatenate them together in a lazy manner, returning a single lazy
+list.</td></tr>
+<tr><td><a href="#list>llist">list>llist</a></td><td>Given a normal list, return a lazy list
+that contains the same elements as the normal list.</td></tr>
+</table>
+<h2>Reference</h2>
+<!-- delay description -->
+<a name="delay">
+<h3>delay ( quot -- &lt;promise&gt; )</h3>
+<p>'delay' is used to convert a value or expression into a promise.
+   The word 'force' is used to convert that promise back to its
+   value, or to force evaluation of the expression to return a value.
+</p>
+<p>The value on the stack that 'delay' expects must be quoted. This is
+   a requirement to prevent it from being evaluated.
+</p>
+<pre class="code">
+  ( 1 ) [ 42 ] <a href="#delay">delay</a> dup .
+       => &lt;&lt; promise [ ] [ 42 ] [ ] [ ] &gt;&gt;
+  ( 2 ) <a href="#force">force</a> .
+       => 42
+</pre>
+
+<!-- force description -->
+<a name="force">
+<h3>force ( &lt;promise&gt; -- value )</h3>
+<p>'force' will evaluate a promises original expression
+   and leave the value of that expression on the stack.
+</p>
+<p>A promise can be forced multiple times but the expression
+   is only evaluated once. Future calls of 'force' on the promise
+   will returned the cached value of the original force. If the
+   expression contains side effects, such as i/o, then that i/o
+   will only occur on the first 'force'. See below for an example
+   (steps 3-5).
+</p>
+<p>If a promise is itself delayed, a force will evaluate all promises
+   until a value is returned. Due to this behaviour it is generally not
+   possible to delay a promise. The example below shows what happens
+   in this case.
+</p>
+<pre class="code">       
+  ( 1 ) [ 42 ] <a href="#delay">delay</a> dup .
+       => &lt;&lt; promise [ ] [ 42 ] [ ] [ ] &gt;&gt;
+  ( 2 ) <a href="#force">force</a> .
+       => 42
+       
+        #! Multiple forces on a promise returns cached value
+  ( 3 ) [ "hello" print 42 ] <a href="#delay">delay</a> dup .
+       => << promise [ ] [ "hello" print 42 ] [ ] [ ] >>
+  ( 4 ) dup <a href="#force">force</a> .
+       => hello
+          42
+  ( 5 ) <a href="#force">force</a> .
+       => 42
+
+        #! Forcing a delayed promise cascades up to return
+        #! original value, rather than the promise.
+  ( 6 ) [ [ 42 ] <a href="#delay">delay</a> ] <a href="#delay">delay</a> dup .
+       => << promise [ ] [ [ 42 ] delay ] [ ] [ ] >>
+  ( 7 ) <a href="#force">force</a> .
+       => 42
+</pre>
+
+<!-- lnil description -->
+<a name="lnil">
+<h3>lnil ( -- lcons )</h3>
+<p>Returns a value representing the empty lazy list.</p>
+<pre class="code">
+  ( 1 ) <a href="#lnil">lnil</a> .
+       => << promise [ ] [ [ ] ] t [ ] >>
+</pre>
+
+<!-- lnil description -->
+<a name="lnilp">
+<h3>lnil? ( lcons -- bool )</h3>
+<p>Returns true if the given lazy cons is the value representing 
+   the empty lazy list.</p>
+<pre class="code">
+  ( 1 ) <a href="#lnil">lnil</a> <a href="#lnilp">lnil?</a> .
+       => t
+  ( 2 ) [ 1 ] <a href="#list2llist">list&gt;llist</a> dup <a href="#lnilp">lnil?</a> .
+       => [ ]
+  ( 3 ) <a href="#lcdr">lcdr</a> <a href="#lnilp">lnil?</a> .
+       => t
+</pre>
+
+<!-- lcons description -->
+<a name="lcons">
+<h3>lcons ( car-promise cdr-promise -- lcons )</h3>
+<p>Provides the same effect as 'cons' does for normal lists. 
+   Both values provided must be promises (ie. expressions that have
+   had <a href="#delay">delay</a> called on them).
+</p>
+<p>As the car and cdr passed on the stack are promises, they are not
+   evaluated until <a href="#lcar">lcar</a> or <a href="#lcdr">lcdr</a>
+   are called on the lazy cons.</p>
+<pre class="code">
+  ( 1 ) [ "car" ] <a href="#delay">delay</a> [ "cdr" ] <a href="#delay">delay</a> <a href="#lcons">lcons</a> dup .
+       => &lt;&lt; promise ... &gt;&gt;
+  ( 2 ) dup <a href="#lcar">lcar</a> .
+       => "car"
+  ( 3 ) dup <a href="#lcdr">lcdr</a> .
+       => "cdr"
+</pre>
+  
+<!-- lunit description -->
+<a name="lunit">
+<h3>lunit ( value-promise -- llist )</h3>
+<p>Provides the same effect as 'unit' does for normal lists. It
+creates a lazy list where the first element is the value given.</p>
+<p>Like <a href="#lcons">lcons</a>, the value on the stack must be
+   a promise and is not evaluated until the <a href="#lcar">lcar</a>
+   of the list is requested.</a>
+<pre class="code">
+  ( 1 ) [ 42 ] <a href="#delay">delay</a> <a href="#lunit">lunit</a> dup .
+       => &lt;&lt; promise ... &gt;&gt;
+  ( 2 ) dup <a href="#lcar">lcar</a> .
+       => 42
+  ( 3 ) dup <a href="#lcdr">lcdr</a> <a href="#lnilp">lnil?</a> .
+       => t
+  ( 4 ) [ . ] <a href="#leach">leach</a>
+       => 42
+</pre>
+
+<!-- lcar description -->
+<a name="lcar">
+<h3>lcar ( lcons -- value )</h3>
+<p>Provides the same effect as 'car' does for normal lists. It
+returns the first element in a lazy cons cell. This will force
+the evaluation of that element.</p>
+<pre class="code">
+  ( 1 ) [ 42 ] <a href="#delay">delay</a> <a href="#lunit">lunit</a> dup .
+       => &lt;&lt; promise ... &gt;&gt;
+  ( 2 ) <a href="#lcar">lcar</a> .
+       => 42
+</pre>
+
+<!-- lcdr description -->
+<a name="lcdr">
+<h3>lcdr ( lcons -- value )</h3>
+<p>Provides the same effect as 'cdr' does for normal lists. It
+returns the second element in a lazy cons cell and forces it. This
+causes that element to be evaluated immediately.</p>
+<pre class="code">
+  ( 1 ) [ 1 ] <a href="#delay">delay</a> [ 5 6 + ] <a href="#delay">delay</a> <a href="#lcons">lcons</a> dup .
+       => &lt;&lt; promise ... &gt;&gt;
+  ( 2 ) <a href="#lcdr">lcdr</a> .
+       => 11
+</pre>
+
+<pre class="code">
+  ( 1 ) 5 <a href="#lfrom">lfrom</a> dup .
+       => &lt;&lt; promise ... &gt;&gt;
+  ( 2 ) <a href="#lcdr">lcdr</a> dup <a href="#lcar">lcar</a> .
+       => 6
+  ( 3 ) <a href="#lcdr">lcdr</a> dup <a href="#lcar">lcar</a> .
+       => 7
+  ( 4 ) <a href="#lcdr">lcdr</a> dup <a href="#lcar">lcar</a> .
+       => 8
+</pre>
+
+<!-- lnth description -->
+<a name="lnth">
+<h3>lnth ( n llist -- value )</h3>
+<p>Provides the same effect as 'nth' does for normal lists. It
+returns the nth value in the lazy list. It causes all the values up to
+'n' to be evaluated.</p>
+<pre class="code">
+  ( 1 ) 1 <a href="#lfrom">lfrom</a> dup .
+       => &lt;&lt; promise ... &gt;&gt;
+  ( 2 ) 5 swap <a href="#lnth">lnth</a> .
+       => 6
+</pre>
+
+<!-- luncons description -->
+<a name="luncons">
+<h3>luncons ( lcons -- car cdr )</h3>
+<p>Provides the same effect as 'uncons' does for normal lists. It
+returns the car and cdr of the lazy list.</p>
+<pre class="code">
+  ( 1 ) [ 5 ] <a href="#delay">delay</a> [ 6 ] <a  href="#delay">delay</a> <a href="#lcons">lcons</a> dup .
+       => &lt;&lt; promise ... &gt;&gt;
+  ( 2 ) <a href="#luncons">luncons</a> . .
+       => 6
+          5
+</pre>
+
+<!-- lmap description -->
+<a name="lmap">
+<h3>lmap ( llist quot -- llist )</h3>
+<p>Lazily maps over a lazy list applying the quotation to each element.
+A new lazy list is returned which contains the results of the
+quotation.</p>
+<p>When intially called nothing in the original lazy list is
+evaluated. Only when <a href="#lcar">lcar</a> is called will the item
+in the list be evaluated and applied to the quotation. Ditto with <a
+href="#lcdr">lcdr</a>, thus allowing infinite lists to be mapped over.</p>
+<pre class="code">
+  ( 1 ) 1 <a href="#lfrom">lfrom</a>
+       => < infinite list of incrementing numbers >
+  ( 2 ) [ 2 * ] <a href="#lmap">lmap</a>
+       => < infinite list of numbers incrementing by 2 >
+  ( 3 ) 5 swap <a href="#ltake">ltake</a> <a  href="#llist2list">llist&gt;list</a> .
+       => [ 2 4 6 8 10 ]
+</pre>
+
+<!-- lsubset description -->
+<a name="lsubset">
+<h3>lsubset ( llist pred -- llist )</h3>
+<p>Provides the same effect as 'subset' does for normal lists. It
+lazily iterates over a lazy list applying the predicate quotation to each
+element. If that quotation returns true, the element will be included
+in the resulting lazy list. If it is false, the element will be skipped.
+A new lazy list is returned which contains  all elements where the
+predicate returned true.</p>
+<p>Like <a href="#lmap">lmap</a>, when initially called no evaluation
+will occur. A lazy list is returned that when values are retrieved
+from in then items are evaluated and checked against the predicate.</p>
+<pre class="code">
+  ( 1 ) 1 <a href="#lfrom">lfrom</a>
+       => < infinite list of incrementing numbers >
+  ( 2 ) [ <a href="#primep">prime?</a> ] <a href="#lsubset">lsubset</a>
+       => < infinite list of prime numbers >
+  ( 3 ) 5 swap <a href="#ltake">ltake</a> <a  href="#llist2list">llist&gt;list</a> .
+       => [ 2 3 5 7 11 ]
+</pre>
+
+<!-- leach description -->
+<a name="leach">
+<h3>leach ( llist quot --  )</h3>
+<p>Provides the same effect as 'each' does for normal lists. It
+lazily iterates over a lazy list applying the quotation to each
+element. If this operation is applied to an infinite list it will
+never return unless the quotation escapes out by calling a continuation.</p>
+<pre class="code">
+  ( 1 ) 1 <a href="#lfrom">lfrom</a>
+       => < infinite list of incrementing numbers >
+  ( 2 ) [ 2 mod 1 = ] <a href="#lsubset">lsubset</a>
+       => < infinite list of odd numbers >
+  ( 3 ) [ . ] <a href="#leach">leach</a> 
+       => 1
+          3
+          5
+          7
+          ... for ever ...
+</pre>
+
+<!-- ltake description -->
+<a name="ltake">
+<h3>ltake ( n llist -- llist )</h3>
+<p>Iterates over the lazy list 'n' times, appending each element to a
+lazy list. This provides a convenient way of getting elements out of
+an infinite lazy list.</p>
+<pre class="code">
+  ( 1 ) : ones [ 1 ] delay [ ones ] delay <a href="#lcons">lcons</a> ;
+  ( 2 ) 5 ones <a href="#ltake">ltake</a> <a  href="#llist2list">llist&gt;list</a> .
+       => [ 1 1 1 1 1  ]
+</pre>
+
+<!-- lappend description -->
+<a name="lappend">
+<h3>lappend ( llist1 llist2 -- llist )</h3>
+<p>Lazily appends two lists together. The actual appending is done
+lazily on iteration rather than immediately so it works very fast no
+matter how large the list.</p>
+<pre class="code">
+  ( 1 ) [ 1 2 3 ] <a href="#list2llist">list&gt;llist</a> [ 4 5 6 ] <a href="#list2llist">list&gt;llist</a> <a href="#lappend">lappend</a>
+  ( 2 ) [ . ] <a href="#leach">leach</a>
+       => 1
+          2
+          3
+          4
+          5
+          6
+</pre>
+
+<!-- lappend* description -->
+<a name="lappendstar">
+<h3>lappend* ( llists -- llist )</h3>
+<p>Given a lazy list of lazy lists, concatenate them together in a
+lazy fashion. The actual appending is done lazily on iteration rather
+than immediately so it works very fast no matter how large the lists.</p>
+<pre class="code">
+  ( 1 ) [ 1 2 3 ] <a href="#list2>llist">list&gt;llist</a> 
+  ( 2 ) [ 4 5 6 ] <a href="#list2llist">list&gt;llist</a> 
+  ( 3 ) [ 7 8 9 ] <a href="#list2llist">list&gt;llist</a>
+  ( 4 ) 3list <a href="#list2llist">list&gt;llist</a> <a href="#lappendstar">lappend*</a>
+  ( 5 ) [ . ] <a href="#leach">leach</a>
+       => 1
+          2
+          3
+          4
+          5
+          6
+          7
+          8
+          9
+</pre>
+
+<!-- list>llist description -->
+<a name="list2llist">
+<h3>list&gt;llist ( list  -- llist )</h3>
+<p>Converts a normal list into a lazy list. This is done lazily so the
+initial list is not iterated through immediately.</p>
+<pre class="code">
+  ( 1 ) [ 1 2 3 ] <a href="#list2llist">list&gt;llist</a> 
+  ( 2 ) [ . ] <a href="#leach">leach</a>
+       => 1
+          2
+          3
+</pre>
+
+<p class="footer">
+News and updates to this software can be obtained from the authors
+weblog: <a href="http://radio.weblogs.com/0102385">Chris Double</a>.</p>
+<p id="copyright">Copyright (c) 2004, Chris Double. All Rights Reserved.</p>
+</body> </html>
diff --git a/extra/lists/lazy/summary.txt b/extra/lists/lazy/summary.txt
new file mode 100644 (file)
index 0000000..5d2f302
--- /dev/null
@@ -0,0 +1 @@
+Lazy lists
diff --git a/extra/lists/lazy/tags.txt b/extra/lists/lazy/tags.txt
new file mode 100644 (file)
index 0000000..dd23829
--- /dev/null
@@ -0,0 +1,2 @@
+extensions
+collections
diff --git a/extra/lists/lists-docs.factor b/extra/lists/lists-docs.factor
new file mode 100644 (file)
index 0000000..9440776
--- /dev/null
@@ -0,0 +1,45 @@
+! Copyright (C) 2006 Chris Double.
+! See http://factorcode.org/license.txt for BSD license.
+
+IN: lists
+USING: help.markup help.syntax ;
+
+{ car cons cdr nil nil? list? uncons } related-words
+
+HELP: cons 
+{ $values { "car" "the head of the lazy list" } { "cdr" "the tail of the lazy list" } { "cons" "a cons object" } }
+{ $description "Constructs a cons cell." } ;
+
+HELP: car
+{ $values { "cons" "a cons object" } { "car" "the first item in the list" } }
+{ $description "Returns the first item in the list." } ;
+
+HELP: cdr
+{ $values { "cons" "a cons object" } { "cdr" "a cons object" } }
+{ $description "Returns the tail of the list." } ;
+
+HELP: nil 
+{ $values { "cons" "An empty cons" } }
+{ $description "Returns a representation of an empty list" } ;
+
+HELP: nil? 
+{ $values { "cons" "a cons object" } { "?" "a boolean" } }
+{ $description "Return true if the cons object is the nil cons." } ;
+
+HELP: list? ( object -- ? )
+{ $values { "object" "an object" } { "?" "a boolean" } }
+{ $description "Returns true if the object conforms to the list protocol." } ;
+
+{ 1list 2list 3list } related-words
+
+HELP: 1list
+{ $values { "obj" "an object" } { "cons" "a cons object" } }
+{ $description "Create a list with 1 element." } ;
+
+HELP: 2list
+{ $values { "a" "an object" } { "b" "an object" } { "cons" "a cons object" } }
+{ $description "Create a list with 2 elements." } ;
+
+HELP: 3list
+{ $values { "a" "an object" } { "b" "an object" } { "c" "an object" } { "cons" "a cons object" } }
+{ $description "Create a list with 3 elements." } ;
\ No newline at end of file
diff --git a/extra/lists/lists-tests.factor b/extra/lists/lists-tests.factor
new file mode 100644 (file)
index 0000000..41f2d1d
--- /dev/null
@@ -0,0 +1,21 @@
+! Copyright (C) 2008 James Cash
+! See http://factorcode.org/license.txt for BSD license.
+USING: tools.test lists math ;
+
+IN: lists.tests
+
+{ { 3 4 5 6 } } [
+    T{ cons f 1       
+        T{ cons f 2 
+            T{ cons f 3
+                T{ cons f 4
+                T{ cons f f f } } } } } [ 2 + ] map-cons
+] unit-test
+
+{ 10 } [
+ T{ cons f 1       
+        T{ cons f 2 
+            T{ cons f 3
+                T{ cons f 4
+                T{ cons f f f } } } } } 0 [ + ] reduce-cons
+] unit-test
\ No newline at end of file
diff --git a/extra/lists/lists.factor b/extra/lists/lists.factor
new file mode 100644 (file)
index 0000000..4b8cc77
--- /dev/null
@@ -0,0 +1,58 @@
+! Copyright (C) 2008 James Cash
+! See http://factorcode.org/license.txt for BSD license.
+USING: kernel sequences accessors ;
+
+IN: lists
+
+! Lazy List Protocol
+MIXIN: list
+GENERIC: car   ( cons -- car )
+GENERIC: cdr   ( cons -- cdr )
+GENERIC: nil?  ( cons -- ? )
+
+TUPLE: cons car cdr ;
+
+C: cons cons
+
+M: cons car ( cons -- car )
+    car>> ;
+
+M: cons cdr ( cons -- cdr )
+    cdr>> ;
+
+: nil ( -- cons )
+  T{ cons f f f } ;
+
+M: cons nil? ( cons -- bool )
+    nil eq? ;
+
+: 1list ( obj -- cons )
+    nil cons ;
+
+: 2list ( a b -- cons )
+    nil cons cons ;
+
+: 3list ( a b c -- cons )
+    nil cons cons cons ;
+    
+: uncons ( cons -- cdr car )
+    [ cdr ] [ car ] bi ;
+
+: seq>cons ( seq -- cons )
+    <reversed> nil [ f cons swap >>cdr ] reduce ;
+    
+: (map-cons) ( acc cons quot -- seq )    
+    over nil? [ 2drop ]
+    [ [ uncons ] dip [ call ] keep swapd [ suffix ] 2dip (map-cons) ] if ;
+    
+: map-cons ( cons quot -- seq )
+    [ { } clone ] 2dip (map-cons) ;
+    
+: cons>seq ( cons -- array )    
+    [ ] map-cons ;
+    
+: reduce-cons ( cons identity quot -- result )    
+    pick nil? [ drop nip ]
+    [ [ uncons ] 2dip swapd [ call ] keep reduce-cons ] if ;
+    
+INSTANCE: cons list
\ No newline at end of file
diff --git a/extra/lists/summary.txt b/extra/lists/summary.txt
new file mode 100644 (file)
index 0000000..60a1886
--- /dev/null
@@ -0,0 +1 @@
+Implementation of lisp-style linked lists
diff --git a/extra/lists/tags.txt b/extra/lists/tags.txt
new file mode 100644 (file)
index 0000000..e44334b
--- /dev/null
@@ -0,0 +1,3 @@
+cons
+lists
+sequences
index c5adaa5e5ef5fffb0fa2ff534d3297b954a81558..4e670cdac06cc83bae3830e1f8b386c7a7396c3f 100755 (executable)
@@ -254,3 +254,14 @@ M:: fixnum next-method-test ( a -- b ) a call-next-method 1 + ;
 [ 3 ] [ 1 [let | a [ ] | 3 ] ] unit-test
 
 [ ] [ 1 2 [let | a [ ] b [ ] | ] ] unit-test
+
+:: a-word-with-locals ( a b -- ) ;
+
+: new-definition "USING: math ;\nIN: locals.tests\n: a-word-with-locals ( -- x ) 2 3 + ;\n" ;
+
+[ ] [ new-definition eval ] unit-test
+
+[ t ] [
+    [ \ a-word-with-locals see ] with-string-writer
+    new-definition =
+] unit-test
index af4f1a77b6ac8172d15e7a8a81e5579d2fd3a12c..e74d0b60784cf410ffcb29ae057f779b72642343 100755 (executable)
@@ -364,6 +364,9 @@ M: lambda-word definer drop \ :: \ ; ;
 M: lambda-word definition
     "lambda" word-prop body>> ;
 
+M: lambda-word reset-word
+    [ f "lambda" set-word-prop ] [ call-next-method ] bi ;
+
 INTERSECTION: lambda-macro macro lambda-word ;
 
 M: lambda-macro definer drop \ MACRO:: \ ; ;
@@ -371,6 +374,9 @@ M: lambda-macro definer drop \ MACRO:: \ ; ;
 M: lambda-macro definition
     "lambda" word-prop body>> ;
 
+M: lambda-macro reset-word
+    [ f "lambda" set-word-prop ] [ call-next-method ] bi ;
+
 INTERSECTION: lambda-method method-body lambda-word ;
 
 M: lambda-method definer drop \ M:: \ ; ;
@@ -378,6 +384,9 @@ M: lambda-method definer drop \ M:: \ ; ;
 M: lambda-method definition
     "lambda" word-prop body>> ;
 
+M: lambda-method reset-word
+    [ f "lambda" set-word-prop ] [ call-next-method ] bi ;
+
 INTERSECTION: lambda-memoized memoized lambda-word ;
 
 M: lambda-memoized definer drop \ MEMO:: \ ; ;
@@ -385,6 +394,9 @@ M: lambda-memoized definer drop \ MEMO:: \ ; ;
 M: lambda-memoized definition
     "lambda" word-prop body>> ;
 
+M: lambda-memoized reset-word
+    [ f "lambda" set-word-prop ] [ call-next-method ] bi ;
+
 : method-stack-effect ( method -- effect )
     dup "lambda" word-prop vars>>
     swap "method-generic" word-prop stack-effect
diff --git a/extra/logging/logging-tests.factor b/extra/logging/logging-tests.factor
new file mode 100644 (file)
index 0000000..796c876
--- /dev/null
@@ -0,0 +1,24 @@
+IN: logging.tests
+USING: tools.test logging math ;
+
+: input-logging-test ( a b -- c ) + ;
+
+\ input-logging-test NOTICE add-input-logging
+
+: output-logging-test ( a b -- c ) + ;
+
+\ output-logging-test DEBUG add-output-logging
+
+: error-logging-test ( a b -- c ) / ;
+
+\ error-logging-test ERROR add-error-logging
+
+"logging-test" [
+    [ 4 ] [ 1 3 input-logging-test ] unit-test
+    
+    [ 4 ] [ 1 3 output-logging-test ] unit-test
+    
+    [ 4/3 ] [ 4 3 error-logging-test ] unit-test
+    
+    [ f ] [ 1 0 error-logging-test ] unit-test
+] with-logging
index f54ab05bbd2e9835c2ad0defd47400493b86ddef..df03bf320b7fbc4ccd9115dcbc820ec0487502b8 100755 (executable)
@@ -4,33 +4,26 @@ USING: logging.server sequences namespaces concurrency.messaging
 words kernel arrays shuffle tools.annotations\r
 prettyprint.config prettyprint debugger io.streams.string\r
 splitting continuations effects arrays.lib parser strings\r
-combinators.lib quotations ;\r
+combinators.lib quotations fry symbols accessors ;\r
 IN: logging\r
 \r
-SYMBOL: DEBUG\r
-SYMBOL: NOTICE\r
-SYMBOL: WARNING\r
-SYMBOL: ERROR\r
-SYMBOL: CRITICAL\r
+SYMBOLS: DEBUG NOTICE WARNING ERROR CRITICAL ;\r
 \r
-: log-levels\r
-    { DEBUG NOTICE NOTICE WARNING ERROR CRITICAL } ;\r
+: log-levels { DEBUG NOTICE NOTICE WARNING ERROR CRITICAL } ;\r
 \r
 : send-to-log-server ( array string -- )\r
     prefix "log-server" get send ;\r
 \r
 SYMBOL: log-service\r
 \r
-: check-log-message\r
-    pick string?\r
-    pick word?\r
-    pick word? and and\r
-    [ "Bad parameters to log-message" throw ] unless ;\r
+: check-log-message ( msg word level -- msg word level )\r
+    3dup [ string? ] [ word? ] [ word? ] tri* and and\r
+    [ "Bad parameters to log-message" throw ] unless ; inline\r
 \r
 : log-message ( msg word level -- )\r
     check-log-message\r
     log-service get dup [\r
-        >r >r >r string-lines r> word-name r> word-name r>\r
+        [ [ string-lines ] [ word-name ] [ word-name ] tri* ] dip\r
         4array "log-message" send-to-log-server\r
     ] [\r
         4drop\r
@@ -69,7 +62,7 @@ SYMBOL: log-service
 PRIVATE>\r
 \r
 : (define-logging) ( word level quot -- )\r
-    >r >r dup r> r> 2curry annotate ;\r
+    [ dup ] 2dip 2curry annotate ;\r
 \r
 : call-logging-quot ( quot word level -- quot' )\r
     "called" -rot [ log-message ] 3curry prepose ;\r
@@ -79,31 +72,30 @@ PRIVATE>
 \r
 : log-stack ( n word level -- )\r
     log-service get [\r
-        >r >r [ ndup ] keep narray stack>message\r
-        r> r> log-message\r
+        [ [ ndup ] keep narray stack>message ] 2dip log-message\r
     ] [\r
         3drop\r
     ] if ; inline\r
 \r
-: input# stack-effect effect-in length ;\r
+: input# stack-effect in>> length ;\r
 \r
 : input-logging-quot ( quot word level -- quot' )\r
-    over input# -rot [ log-stack ] 3curry prepose ;\r
+    rot [ [ input# ] keep ] 2dip '[ , , , log-stack @ ] ;\r
 \r
 : add-input-logging ( word level -- )\r
     [ input-logging-quot ] (define-logging) ;\r
 \r
-: output# stack-effect effect-out length ;\r
+: output# stack-effect out>> length ;\r
 \r
 : output-logging-quot ( quot word level -- quot' )\r
-    over output# -rot [ log-stack ] 3curry compose ;\r
+    [ [ output# ] keep ] dip '[ @ , , , log-stack ] ;\r
 \r
 : add-output-logging ( word level -- )\r
     [ output-logging-quot ] (define-logging) ;\r
 \r
 : (log-error) ( object word level -- )\r
     log-service get [\r
-        >r >r [ print-error ] with-string-writer r> r> log-message\r
+        [ [ print-error ] with-string-writer ] 2dip log-message\r
     ] [\r
         2drop rethrow\r
     ] if ;\r
@@ -112,22 +104,21 @@ PRIVATE>
 \r
 : log-critical ( error word -- ) CRITICAL (log-error) ;\r
 \r
-: stack-balancer ( effect word -- quot )\r
-    >r dup effect-in length r> [ over >r ERROR log-stack r> ndrop ] 2curry\r
-    swap effect-out length f <repetition> append >quotation ;\r
+: stack-balancer ( effect -- quot )\r
+    [ in>> length [ ndrop ] curry ]\r
+    [ out>> length f <repetition> >quotation ]\r
+    bi append ;\r
 \r
 : error-logging-quot ( quot word -- quot' )\r
-    [ [ log-error ] curry ] keep\r
-    [ stack-effect ] keep stack-balancer compose\r
-    [ recover ] 2curry ;\r
+    dup stack-effect stack-balancer\r
+    '[ , [ , log-error @ ] recover ] ;\r
 \r
 : add-error-logging ( word level -- )\r
-    [ over >r input-logging-quot r> error-logging-quot ]\r
+    [ [ input-logging-quot ] 2keep drop error-logging-quot ]\r
     (define-logging) ;\r
 \r
 : LOG:\r
     #! Syntax: name level\r
-    CREATE-WORD\r
-    dup scan-word\r
-    [ >r >r 1array stack>message r> r> log-message ] 2curry\r
+    CREATE-WORD dup scan-word\r
+    '[ 1array stack>message , , log-message ]\r
     define ; parsing\r
index 59a53afb70ba4c33479c3a66863c063716d5e5f0..d5011b0ecbd7e3c39879c9acb825638a60d27429 100644 (file)
@@ -1,4 +1,14 @@
 IN: macros.tests
 USING: tools.test macros math kernel arrays
-vectors ;
+vectors io.streams.string prettyprint parser ;
 
+MACRO: see-test ( a b -- c ) + ;
+
+[ "USING: macros math ;\nIN: macros.tests\nMACRO: see-test ( a b -- c ) + ;\n" ]
+[ [ \ see-test see ] with-string-writer ]
+unit-test
+
+[ t ] [
+    "USING: math ;\nIN: macros.tests\n: see-test ( a b -- c ) - ;\n" dup eval
+    [ \ see-test see ] with-string-writer =
+] unit-test
index b242f91d3bc6fb5a1dd06314cd2ac02fd7ca896c..88bfd01fbec29b244243476f0cc8ea5f2cd50465 100755 (executable)
@@ -23,6 +23,9 @@ M: macro definer drop \ MACRO: \ ; ;
 
 M: macro definition "macro" word-prop ;
 
+M: macro reset-word
+    [ f "macro" set-word-prop ] [ call-next-method ] bi ;
+
 : macro-expand ( ... word -- quot ) "macro" word-prop call ;
 
 : n*quot ( n seq -- seq' ) <repetition> concat >quotation ;
index 9244fa62e2f18182b28d2f6fa329332e9ecde8aa..1f59659fa9901bfc9150a3fe719b7784715a8681 100644 (file)
@@ -1,6 +1,6 @@
 ! Copyright (c) 2007 Samuel Tardieu.
 ! See http://factorcode.org/license.txt for BSD license.
-USING: lazy-lists math.erato tools.test ;
+USING: lists lists.lazy math.erato tools.test ;
 IN: math.erato.tests
 
 [ { 2 3 5 7 11 13 17 19 } ] [ 20 lerato list>array ] unit-test
index 40de92e3b1d322866b2bfa86f31f9ebb463fd4f7..292cec8deff0439ed73148a6c595c82adee13b43 100644 (file)
@@ -1,6 +1,6 @@
 ! Copyright (c) 2007 Samuel Tardieu.
 ! See http://factorcode.org/license.txt for BSD license.
-USING: bit-arrays kernel lazy-lists math math.functions math.primes.list
+USING: bit-arrays kernel lists lists.lazy math math.functions math.primes.list
        math.ranges sequences ;
 IN: math.erato
 
index c9215d8de739d9adadc25b34ecd6a94454987016..6176c12d21a0e476485b87aa4ab7ddb9b28cecfb 100755 (executable)
@@ -39,6 +39,13 @@ IN: math.functions.tests
 [ 0.0 ] [ 0 sin ] unit-test
 [ 0.0 ] [ 0 asin ] unit-test
 
+[ t ] [ 10 atan real? ] unit-test
+[ f ] [ 10 atanh real? ] unit-test
+
+[ t ] [ 10 asin sin 10 1.e-10 ~ ] unit-test
+[ t ] [ -1 sqrt neg dup acos cos 1.e-10 ~ ] unit-test
+[ t ] [ 10 asinh sinh 10 1.e-10 ~ ] unit-test
+
 [ 100 ] [ 100 100 gcd nip ] unit-test
 [ 100 ] [ 1000 100 gcd nip ] unit-test
 [ 100 ] [ 100 1000 gcd nip ] unit-test
index bce93fbb11b062932ef20fc8a3cbc586902ed66f..bb43e4a72166228611f9cd67c81817a83677e90f 100755 (executable)
@@ -125,74 +125,90 @@ M: real absq sq ;
 M: number (^)
     swap >polar 3dup ^theta >r ^mag r> polar> ;
 
+: [-1,1]? ( x -- ? )
+    dup complex? [ drop f ] [ abs 1 <= ] if ; inline
+
+: >=1? ( x -- ? )
+    dup complex? [ drop f ] [ 1 >= ] if ; inline
+
 : exp ( x -- y ) >rect swap fexp swap polar> ; inline
 
 : log ( x -- y ) >polar swap flog swap rect> ; inline
 
 : cos ( x -- y )
-    >float-rect 2dup
-    fcosh swap fcos * -rot
-    fsinh swap fsin neg * rect> ; foldable
+    dup complex? [
+        >float-rect 2dup
+        fcosh swap fcos * -rot
+        fsinh swap fsin neg * rect>
+    ] [ fcos ] if ; foldable
 
 : sec ( x -- y ) cos recip ; inline
 
 : cosh ( x -- y )
-    >float-rect 2dup
-    fcos swap fcosh * -rot
-    fsin swap fsinh * rect> ; foldable
+    dup complex? [
+        >float-rect 2dup
+        fcos swap fcosh * -rot
+        fsin swap fsinh * rect>
+    ] [ fcosh ] if ; foldable
 
 : sech ( x -- y ) cosh recip ; inline
 
 : sin ( x -- y )
-    >float-rect 2dup
-    fcosh swap fsin * -rot
-    fsinh swap fcos * rect> ; foldable
+    dup complex? [
+        >float-rect 2dup
+        fcosh swap fsin * -rot
+        fsinh swap fcos * rect>
+    ] [ fsin ] if ; foldable
 
 : cosec ( x -- y ) sin recip ; inline
 
 : sinh ( x -- y )
-    >float-rect 2dup
-    fcos swap fsinh * -rot
-    fsin swap fcosh * rect> ; foldable
+    dup complex? [
+        >float-rect 2dup
+        fcos swap fsinh * -rot
+        fsin swap fcosh * rect>
+    ] [ fsinh ] if ; foldable
 
 : cosech ( x -- y ) sinh recip ; inline
 
-: tan ( x -- y ) dup sin swap cos / ; inline
+: tan ( x -- y )
+    dup complex? [ dup sin swap cos / ] [ ftan ] if ; inline
 
-: tanh ( x -- y ) dup sinh swap cosh / ; inline
+: tanh ( x -- y )
+    dup complex? [ dup sinh swap cosh / ] [ ftanh ] if ; inline
 
-: cot ( x -- y ) dup cos swap sin / ; inline
+: cot ( x -- y ) tan recip ; inline
 
-: coth ( x -- y ) dup cosh swap sinh / ; inline
+: coth ( x -- y ) tanh recip ; inline
 
-: acosh ( x -- y ) dup sq 1- sqrt + log ; inline
+: acosh ( x -- y )
+    dup >=1? [ facosh ] [ dup sq 1- sqrt + log ] if ; inline
 
 : asech ( x -- y ) recip acosh ; inline
 
-: asinh ( x -- y ) dup sq 1+ sqrt + log ; inline
+: asinh ( x -- y )
+    dup complex? [ dup sq 1+ sqrt + log ] [ fasinh ] if ; inline
 
 : acosech ( x -- y ) recip asinh ; inline
 
-: atanh ( x -- y ) dup 1+ swap 1- neg / log 2 / ; inline
+: atanh ( x -- y )
+    dup [-1,1]? [ fatanh ] [ dup 1+ swap 1- neg / log 2 / ] if ; inline
 
 : acoth ( x -- y ) recip atanh ; inline
 
-: [-1,1]? ( x -- ? )
-    dup complex? [ drop f ] [ abs 1 <= ] if ; inline
-
 : i* ( x -- y ) >rect neg swap rect> ;
 
 : -i* ( x -- y ) >rect swap neg rect> ;
 
 : asin ( x -- y )
-    dup [-1,1]? [ >float fasin ] [ i* asinh -i* ] if ; inline
+    dup [-1,1]? [ fasin ] [ i* asinh -i* ] if ; inline
 
 : acos ( x -- y )
-    dup [-1,1]? [ >float facos ] [ asin pi 2 / swap - ] if ;
+    dup [-1,1]? [ facos ] [ asin pi 2 / swap - ] if ;
     inline
 
 : atan ( x -- y )
-    dup [-1,1]? [ >float fatan ] [ i* atanh i* ] if ; inline
+    dup complex? [ i* atanh i* ] [ fatan ] if ; inline
 
 : asec ( x -- y ) recip acos ; inline
 
index 0cc402e6e5ac5c003ce053fcdd0e0d42378c1210..f70c8d2a77802742c7f9d9c6ee25fe924934934e 100644 (file)
@@ -15,6 +15,18 @@ IN: math.libm
     "double" "libm" "atan" { "double" } alien-invoke ;
     foldable
 
+: facosh ( x -- y )
+    "double" "libm" "acosh" { "double" } alien-invoke ;
+    foldable
+
+: fasinh ( x -- y )
+    "double" "libm" "asinh" { "double" } alien-invoke ;
+    foldable
+
+: fatanh ( x -- y )
+    "double" "libm" "atanh" { "double" } alien-invoke ;
+    foldable
+
 : fatan2 ( x y -- z )
     "double" "libm" "atan2" { "double" "double" } alien-invoke ;
     foldable
@@ -27,6 +39,10 @@ IN: math.libm
     "double" "libm" "sin" { "double" } alien-invoke ;
     foldable
 
+: ftan ( x -- y )
+    "double" "libm" "tan" { "double" } alien-invoke ;
+    foldable
+
 : fcosh ( x -- y )
     "double" "libm" "cosh" { "double" } alien-invoke ;
     foldable
@@ -35,6 +51,10 @@ IN: math.libm
     "double" "libm" "sinh" { "double" } alien-invoke ;
     foldable
 
+: ftanh ( x -- y )
+    "double" "libm" "tanh" { "double" } alien-invoke ;
+    foldable
+
 : fexp ( x -- y )
     "double" "libm" "exp" { "double" } alien-invoke ;
     foldable
index 2f70ab24b474b959ddf95a2a952c0b636f2a54a1..7413f9701bff67cce44e8916f87d68b529930b29 100644 (file)
@@ -1,6 +1,6 @@
 ! Copyright (C) 2007 Samuel Tardieu.
 ! See http://factorcode.org/license.txt for BSD license.
-USING: arrays kernel lazy-lists math math.primes namespaces sequences ;
+USING: arrays kernel lists lists.lazy math math.primes namespaces sequences ;
 IN: math.primes.factors
 
 <PRIVATE
index b1bcf79a49b7efdeeb6b994da3c25d6f0d8a700a..2db98af893ff6502837447a17405581da627cbb9 100644 (file)
@@ -1,4 +1,4 @@
-USING: arrays math.primes tools.test lazy-lists ;
+USING: arrays math.primes tools.test lists lists.lazy ;
 
 { 1237 } [ 1234 next-prime ] unit-test
 { f t } [ 1234 prime? 1237 prime? ] unit-test
index 2eeaca6c921314532e9bf209754a2a1099ece686..e42bb8d82d110ae50368bb16a69b0bd31a26b920 100644 (file)
@@ -1,6 +1,6 @@
 ! Copyright (C) 2007 Samuel Tardieu.
 ! See http://factorcode.org/license.txt for BSD license.
-USING: combinators kernel lazy-lists math math.functions math.miller-rabin
+USING: combinators kernel lists lists.lazy math math.functions math.miller-rabin
        math.order math.primes.list math.ranges sequences sorting ;
 IN: math.primes
 
index 43428efbe004e6541dbffe59758d58a0342324a2..c2592b38ca2246badead5cb6917e2d681d0b36f9 100644 (file)
@@ -1,6 +1,7 @@
 ! Copyright (C) 2007 Slava Pestov, Daniel Ehrenberg.
 ! See http://factorcode.org/license.txt for BSD license.
-USING: math kernel memoize tools.test parser ;
+USING: math kernel memoize tools.test parser
+prettyprint io.streams.string sequences ;
 IN: memoize.tests
 
 MEMO: fib ( m -- n )
@@ -9,3 +10,13 @@ MEMO: fib ( m -- n )
 [ 89 ] [ 10 fib ] unit-test
 
 [ "USING: kernel math memoize ; IN: memoize.tests MEMO: x ( a b c d e -- f g h i j ) >r >r >r >r 1+ r> r> r> r> ;" eval ] must-fail
+
+MEMO: see-test ( a -- b ) reverse ;
+
+[ "USING: memoize sequences ;\nIN: memoize.tests\nMEMO: see-test ( a -- b ) reverse ;\n" ]
+[ [ \ see-test see ] with-string-writer ]
+unit-test
+
+[ ] [ "IN: memoize.tests : fib ;" eval ] unit-test
+
+[ "IN: memoize.tests\n: fib ( -- ) ;\n" ] [ [ \ fib see ] with-string-writer ] unit-test
index 4136f9eaffc630373e2c6a4dbd0cc731fffc8b00..1c0491a7ab0e62ada99e9f0bc223a913dfecb472 100755 (executable)
@@ -44,11 +44,20 @@ IN: memoize
 PREDICATE: memoized < word "memoize" word-prop ;
 
 M: memoized definer drop \ MEMO: \ ; ;
+
 M: memoized definition "memo-quot" word-prop ;
 
+M: memoized reset-word
+    [ { "memoize" "memo-quot" } reset-props ]
+    [ call-next-method ]
+    bi ;
+
 : memoize-quot ( quot effect -- memo-quot )
     gensym swap dupd "declared-effect" set-word-prop
     dup rot define-memoized 1quotation ;
 
 : reset-memoized ( word -- )
     "memoize" word-prop clear-assoc ;
+
+: invalidate-memoized ! ( inputs... word )
+    [ #in packer call ] [ "memoize" word-prop delete-at ] bi ;
index 52cdc47ac6a6e8063b5a50253ccea788f23e837b..98cc403910afb1c7603601ec53d449e1f3edb7ff 100644 (file)
@@ -1,4 +1,4 @@
-USING: tools.test monads math kernel sequences lazy-lists promises ;
+USING: tools.test monads math kernel sequences lists lists.lazy promises ;
 IN: monads.tests
 
 [ 5 ] [ 1 identity-monad return [ 4 + ] fmap run-identity ] unit-test
index 0f4138c9853a87299d1db0a073fa37424d1ad069..18820d1b53dbdaf44c4481af864d0a892f650065 100644 (file)
@@ -1,7 +1,7 @@
 ! Copyright (C) 2008 Slava Pestov
 ! See http://factorcode.org/license.txt for BSD license.
 USING: arrays kernel sequences sequences.deep splitting
-accessors fry locals combinators namespaces lazy-lists
+accessors fry locals combinators namespaces lists lists.lazy
 shuffle ;
 IN: monads
 
index 9d335896be8c9d5ec66a7bab2f1c8671e112c1fc..71b72493519db61c5581f5889526c4f32a6ae7e0 100644 (file)
@@ -1,6 +1,6 @@
 ! Copyright (C) 2007, 2008 Alex Chapman
 ! See http://factorcode.org/license.txt for BSD license.
-USING: accessors assocs combinators hashtables kernel lazy-lists math namespaces openal parser-combinators promises sequences strings symbols synth synth.buffers unicode.case ;
+USING: accessors assocs combinators hashtables kernel lists lists.lazy math namespaces openal parser-combinators promises sequences strings symbols synth synth.buffers unicode.case ;
 IN: morse
 
 <PRIVATE
index 59e8049232b27a78c2336378aa84221c44c2a6b6..46ad6fc58e93014e396210166d0688ba89cff466 100755 (executable)
@@ -4,7 +4,7 @@ USING: kernel math sequences vectors classes classes.algebra
 combinators arrays words assocs parser namespaces definitions
 prettyprint prettyprint.backend quotations arrays.lib
 debugger io compiler.units kernel.private effects accessors
-hashtables sorting shuffle math.order ;
+hashtables sorting shuffle math.order sets ;
 IN: multi-methods
 
 ! PART I: Converting hook specializers
@@ -25,7 +25,7 @@ SYMBOL: total
     ]
     [
         [ pair? ] filter
-        [ keys [ hooks get push-new ] each ] keep
+        [ keys [ hooks get adjoin ] each ] keep
     ] bi append ;
 
 : canonicalize-specializer-2 ( specializer -- specializer' )
index 47b6b33a9a6f989209b05d9e7942ffc2acf7982c..851f60d126ebd039c8130e27a58ab9803a582d84 100755 (executable)
@@ -2,7 +2,7 @@
 ! USING: kernel quotations namespaces sequences assocs.lib ;
 
 USING: kernel namespaces namespaces.private quotations sequences
-       assocs.lib math.parser math sequences.lib locals ;
+       assocs.lib math.parser math sequences.lib locals mirrors ;
 
 IN: namespaces.lib
 
@@ -58,3 +58,9 @@ MACRO:: nmake ( quot exemplars -- )
             ] with-scope
         ]
     ] ;
+
+: make-object ( quot class -- object )
+    new [ <mirror> swap bind ] keep ; inline
+
+: with-object ( object quot -- )
+    [ <mirror> ] dip bind ; inline
index abe0449d0622b29e36f6c96f97c22e3f9a139dc0..e017dc4b2b08acd1b28948fe8034db709557f225 100644 (file)
@@ -170,6 +170,11 @@ METHOD: as-mutate { object object assoc }       set-at ;
 
 ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
+: index    ( seq obj -- i ) swap sequences:index ;
+: index-of ( obj seq -- i )      sequences:index ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
 : 1st 0 at ;
 : 2nd 1 at ;
 : 3rd 2 at ;
diff --git a/extra/opengl/gadgets/gadgets.factor b/extra/opengl/gadgets/gadgets.factor
new file mode 100644 (file)
index 0000000..1a15283
--- /dev/null
@@ -0,0 +1,72 @@
+! Copyright (C) 2008 Matthew Willis.
+! See http://factorcode.org/license.txt for BSD license.
+USING: locals math.functions math namespaces
+opengl.gl accessors kernel opengl ui.gadgets
+destructors sequences ui.render colors ;
+IN: opengl.gadgets
+
+TUPLE: texture-gadget bytes format dim tex ;
+
+: 2^-ceil ( x -- y )
+    dup 2 < [ 2 * ] [ 1- log2 1+ 2^ ] if ; foldable flushable
+
+: 2^-bounds ( dim -- dim' )
+    [ 2^-ceil ] map ; foldable flushable
+
+: <texture-gadget> ( bytes format dim -- gadget )
+    texture-gadget construct-gadget
+        swap >>dim
+        swap >>format
+        swap >>bytes ;
+
+:: render ( gadget -- )
+    GL_ENABLE_BIT [
+        GL_TEXTURE_2D glEnable
+        GL_TEXTURE_2D gadget tex>> glBindTexture
+        GL_TEXTURE_2D
+        0
+        GL_RGBA
+        gadget dim>> 2^-bounds first2
+        0
+        gadget format>>
+        GL_UNSIGNED_BYTE
+        gadget bytes>>
+        glTexImage2D
+        init-texture
+        GL_TEXTURE_2D 0 glBindTexture
+    ] do-attribs ;
+
+:: four-corners ( dim -- )
+    [let* | w [ dim first ]
+            h [ dim second ]
+            dim' [ dim dup 2^-bounds [ /f ] 2map ]
+            w' [ dim' first ]
+            h' [ dim' second ] |
+        0  0  glTexCoord2d 0 0 glVertex2d
+        0  h' glTexCoord2d 0 h glVertex2d
+        w' h' glTexCoord2d w h glVertex2d
+        w' 0  glTexCoord2d w 0 glVertex2d
+    ] ;
+
+M: texture-gadget draw-gadget* ( gadget -- )
+    origin get [
+        GL_ENABLE_BIT [
+            white gl-color
+            1.0 -1.0 glPixelZoom
+            GL_TEXTURE_2D glEnable
+            GL_TEXTURE_2D over tex>> glBindTexture
+            GL_QUADS [
+                dim>> four-corners
+            ] do-state
+            GL_TEXTURE_2D 0 glBindTexture
+        ] do-attribs
+    ] with-translation ;
+
+M: texture-gadget graft* ( gadget -- )
+    gen-texture >>tex [ render ]
+    [ f >>bytes f >>format drop ] bi ;
+
+M: texture-gadget ungraft* ( gadget -- )
+    tex>> delete-texture ;
+
+M: texture-gadget pref-dim* ( gadget -- dim ) dim>> ;
index a6e76cdc9e344da0287629e668cf2e8ff3e6f522..79470131f3f4514842c0e4c71e3c9cb30fd08769 100755 (executable)
@@ -203,9 +203,7 @@ TUPLE: sprite loc dim dim2 dlist texture ;
     dup sprite-loc gl-translate
     GL_TEXTURE_2D over sprite-texture glBindTexture
     init-texture
-    GL_QUADS [ dup sprite-dim2 four-sides ] do-state
-    dup sprite-dim { 1 0 } v*
-    swap sprite-loc v- gl-translate
+    GL_QUADS [ sprite-dim2 four-sides ] do-state
     GL_TEXTURE_2D 0 glBindTexture ;
 
 : rect-vertices ( lower-left upper-right -- )
index ca5a4e8846ab1e81b9a0a18b6df456a78c4ea671..03343820db648539bf6a3e9945c5a7cbacdd46d7 100755 (executable)
@@ -38,15 +38,15 @@ M: TLSv1  ssl-method drop TLSv1_method ;
     OpenSSL_add_all_digests
     OpenSSL_add_all_ciphers ;
 
-SYMBOL: ssl-initiazed?
+SYMBOL: ssl-initialized?
 
 : maybe-init-ssl ( -- )
-    ssl-initiazed? get-global [
+    ssl-initialized? get-global [
         init-ssl
-        t ssl-initiazed? set-global
+        t ssl-initialized? set-global
     ] unless ;
 
-[ f ssl-initiazed? set-global ] "openssl" add-init-hook
+[ f ssl-initialized? set-global ] "openssl" add-init-hook
 
 TUPLE: openssl-context < secure-context aliens ;
 
diff --git a/extra/pango/cairo/cairo.factor b/extra/pango/cairo/cairo.factor
new file mode 100644 (file)
index 0000000..889052c
--- /dev/null
@@ -0,0 +1,131 @@
+! Copyright (C) 2008 Matthew Willis.
+! See http://factorcode.org/license.txt for BSD license.
+!
+! pangocairo bindings, from pango/pangocairo.h
+USING: cairo.ffi alien.c-types math
+alien.syntax system combinators alien
+arrays pango pango.fonts ;
+IN: pango.cairo
+
+<< "pangocairo" {
+!    { [ os winnt? ] [ "libpangocairo-1.dll" ] }
+!    { [ os macosx? ] [ "libpangocairo.dylib" ] }
+    { [ os unix? ] [ "libpangocairo-1.0.so" ] }
+} cond "cdecl" add-library >>
+
+LIBRARY: pangocairo
+
+FUNCTION: PangoFontMap*
+pango_cairo_font_map_new  ( ) ;
+
+FUNCTION: PangoFontMap*
+pango_cairo_font_map_new_for_font_type ( cairo_font_type_t fonttype ) ;
+
+FUNCTION: PangoFontMap*
+pango_cairo_font_map_get_default ( ) ;
+
+FUNCTION: cairo_font_type_t
+pango_cairo_font_map_get_font_type ( PangoCairoFontMap* fontmap ) ;
+
+FUNCTION: void
+pango_cairo_font_map_set_resolution ( PangoCairoFontMap* fontmap, double dpi ) ;
+
+FUNCTION: double
+pango_cairo_font_map_get_resolution ( PangoCairoFontMap* fontmap ) ;
+
+FUNCTION: PangoContext*
+pango_cairo_font_map_create_context ( PangoCairoFontMap* fontmap ) ;
+
+FUNCTION: cairo_scaled_font_t*
+pango_cairo_font_get_scaled_font ( PangoCairoFont* font ) ;
+
+! Update a Pango context for the current state of a cairo context
+FUNCTION: void
+pango_cairo_update_context ( cairo_t* cr, PangoContext* context ) ;
+
+FUNCTION: void
+pango_cairo_context_set_font_options ( PangoContext* context, cairo_font_options_t* options ) ;
+
+FUNCTION: cairo_font_options_t*
+pango_cairo_context_get_font_options ( PangoContext* context ) ;
+
+FUNCTION: void
+pango_cairo_context_set_resolution ( PangoContext* context, double dpi ) ;
+
+FUNCTION: double
+pango_cairo_context_get_resolution ( PangoContext* context ) ;
+
+! Convenience
+FUNCTION: PangoLayout*
+pango_cairo_create_layout ( cairo_t* cr ) ;
+
+FUNCTION: void
+pango_cairo_update_layout ( cairo_t* cr, PangoLayout* layout ) ;
+
+! Rendering
+FUNCTION: void
+pango_cairo_show_glyph_string ( cairo_t* cr, PangoFont* font, PangoGlyphString* glyphs ) ;
+
+FUNCTION: void
+pango_cairo_show_layout_line ( cairo_t* cr, PangoLayoutLine* line ) ;
+
+FUNCTION: void
+pango_cairo_show_layout ( cairo_t* cr, PangoLayout* layout ) ;
+
+FUNCTION: void
+pango_cairo_show_error_underline ( cairo_t* cr, double x, double y, double width, double height ) ;
+
+! Rendering to a path
+FUNCTION: void
+pango_cairo_glyph_string_path ( cairo_t* cr, PangoFont* font, PangoGlyphString* glyphs ) ;
+
+FUNCTION: void
+pango_cairo_layout_line_path  ( cairo_t* cr, PangoLayoutLine* line ) ;
+
+FUNCTION: void
+pango_cairo_layout_path ( cairo_t* cr, PangoLayout* layout ) ;
+
+FUNCTION: void
+pango_cairo_error_underline_path ( cairo_t* cr, double x, double y, double width, double height ) ;
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+! Higher level words and combinators
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+USING: destructors accessors namespaces kernel cairo ;
+
+TUPLE: pango-layout alien ;
+C: <pango-layout> pango-layout
+M: pango-layout dispose ( alien -- ) alien>> g_object_unref ;
+
+: layout ( -- pango-layout ) pango-layout get ;
+
+: (with-pango) ( layout quot -- )
+    >r alien>> pango-layout r> with-variable ; inline
+
+: with-pango ( quot -- )
+    cr pango_cairo_create_layout <pango-layout> swap
+    [ (with-pango) ] curry with-disposal ; inline
+
+: pango-layout-get-pixel-size ( layout -- width height )
+    0 <int> 0 <int> [ pango_layout_get_pixel_size ] 2keep
+    [ *int ] bi@ ;
+
+: dummy-pango ( quot -- )
+    >r CAIRO_FORMAT_ARGB32 0 0 cairo_image_surface_create
+    r> [ with-pango ] curry with-cairo-from-surface ; inline
+
+: layout-size ( quot -- dim )
+    [ layout pango-layout-get-pixel-size 2array ] compose dummy-pango ; inline
+
+: layout-font ( str -- )
+    pango_font_description_from_string
+    dup zero? [ "pango: not a valid font." throw ] when
+    layout over pango_layout_set_font_description
+    pango_font_description_free ;
+
+: layout-text ( str -- )
+    layout swap -1 pango_layout_set_text ;
+
+: families ( -- families )
+    pango_cairo_font_map_get_default list-families ;
diff --git a/extra/pango/cairo/gadgets/gadgets.factor b/extra/pango/cairo/gadgets/gadgets.factor
new file mode 100644 (file)
index 0000000..9e8a995
--- /dev/null
@@ -0,0 +1,30 @@
+! Copyright (C) 2008 Matthew Willis.
+! See http://factorcode.org/license.txt for BSD license.
+USING: pango.cairo cairo cairo.ffi cairo.gadgets
+alien.c-types kernel math ;
+IN: pango.cairo.gadgets
+
+: (pango-gadget) ( setup show -- gadget )
+    [ drop layout-size ]
+    [ compose [ with-pango ] curry <cairo-gadget> ] 2bi ;
+
+: <pango-gadget> ( quot -- gadget )
+    [ cr layout pango_cairo_show_layout ] (pango-gadget) ;
+
+USING: prettyprint sequences ui.gadgets.panes
+threads io.backend io.encodings.utf8 io.files ;
+: hello-pango ( -- )
+    50 [ 6 + ] map [
+        "Sans " swap unparse append
+        [ 
+            cr 0 1 0.2 0.6 cairo_set_source_rgba
+            layout-font "今日は、 Pango!" layout-text
+        ] curry
+        <pango-gadget> gadget. yield
+    ] each
+    [ 
+        "resource:extra/pango/cairo/gadgets/gadgets.factor"
+        normalize-path utf8 file-contents layout-text
+    ] <pango-gadget> gadget. ;
+
+MAIN: hello-pango
diff --git a/extra/pango/fonts/fonts.factor b/extra/pango/fonts/fonts.factor
new file mode 100644 (file)
index 0000000..d07c712
--- /dev/null
@@ -0,0 +1,40 @@
+! Copyright (C) 2008 Matthew Willis.
+! See http://factorcode.org/license.txt for BSD license
+USING: pango alien.syntax alien.c-types
+kernel ;
+IN: pango.fonts
+
+LIBRARY: pango
+
+FUNCTION: void
+pango_font_map_list_families ( PangoFontMap* fontmap, PangoFontFamily*** families, int* n_families ) ;
+
+FUNCTION: char*
+pango_font_family_get_name ( PangoFontFamily* family ) ;
+
+FUNCTION: int
+pango_font_family_is_monospace ( PangoFontFamily* family ) ;
+
+FUNCTION: void
+pango_font_family_list_faces ( PangoFontFamily* family, PangoFontFace*** faces, int* n_faces ) ;
+
+FUNCTION: char*
+pango_font_face_get_face_name ( PangoFontFace* face ) ;
+
+FUNCTION: void
+pango_font_face_list_sizes ( PangoFontFace* face, int** sizes, int* n_sizes ) ;
+
+: list-families ( PangoFontMap* -- PangoFontFamily*-seq )
+    0 <int> 0 <int> [ pango_font_map_list_families ] 2keep
+    *int swap *void* [ swap c-void*-array> ] [ g_free ] bi ;
+
+: list-faces ( PangoFontFamily* -- PangoFontFace*-seq )
+    0 <int> 0 <int> [ pango_font_family_list_faces ] 2keep
+    *int swap *void* [ swap c-void*-array> ] [ g_free ] bi ;
+
+: list-sizes ( PangoFontFace* -- ints )
+    0 <int> 0 <int> [ pango_font_face_list_sizes ] 2keep
+    *int swap *void* [ swap c-int-array> ] [ g_free ] bi ;
+
+: monospace? ( PangoFontFamily* -- ? )
+    pango_font_family_is_monospace 1 = ;
diff --git a/extra/pango/pango.factor b/extra/pango/pango.factor
new file mode 100644 (file)
index 0000000..3549d9a
--- /dev/null
@@ -0,0 +1,59 @@
+! Copyright (C) 2008 Matthew Willis.
+! See http://factorcode.org/license.txt for BSD license
+USING: system
+alien.c-types alien.syntax alien combinators ;
+IN: pango
+
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+! Helpful functions from other parts of pango
+! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+<< "pango" {
+!    { [ os winnt? ] [ "libpango-1.dll" ] }
+!    { [ os macosx? ] [ "libpango.dylib" ] }
+    { [ os unix? ] [ "libpango-1.0.so" ] }
+} cond "cdecl" add-library >>
+
+LIBRARY: pango
+
+: PANGO_SCALE 1024 ;
+
+FUNCTION: void
+pango_layout_set_text ( PangoLayout* layout, char* text, int length ) ;
+
+FUNCTION: char*
+pango_layout_get_text ( PangoLayout* layout ) ;
+
+FUNCTION: void
+pango_layout_get_size ( PangoLayout* layout, int* width, int* height ) ;
+
+FUNCTION: PangoFontDescription*
+pango_font_description_from_string ( char* str ) ;
+
+FUNCTION: char*
+pango_font_description_to_string ( PangoFontDescription* desc ) ;
+
+FUNCTION: char*
+pango_font_description_to_filename ( PangoFontDescription* desc ) ;
+
+FUNCTION: void
+pango_layout_set_font_description ( PangoLayout* layout, PangoFontDescription* desc ) ;
+
+FUNCTION: PangoFontDescription*
+pango_layout_get_font_description ( PangoLayout* layout ) ;
+
+FUNCTION: void
+pango_layout_get_pixel_size ( PangoLayout* layout, int* width, int* height ) ;
+
+FUNCTION: void
+pango_font_description_free ( PangoFontDescription* desc ) ;
+
+! glib functions
+
+TYPEDEF: void* gpointer
+
+FUNCTION: void
+g_object_unref ( gpointer object ) ;
+
+FUNCTION: void
+g_free ( gpointer mem ) ;
index 41171ce822618d08f6718c0093840e19f83684bb..eea1b271bcca58a11d3a1c0939c308c394427db2 100755 (executable)
@@ -23,4 +23,4 @@ HELP: any-char-parser
     "from the input string. The value consumed is the "
     "result of the parse." }
 { $examples
-{ $example "USING: lazy-lists parser-combinators prettyprint ;" "\"foo\" any-char-parser parse-1 ." "102" } } ;
+{ $example "USING: lists lists.lazy parser-combinators prettyprint ;" "\"foo\" any-char-parser parse-1 ." "102" } } ;
index 2dd3fd911cf348a8207b449ea68bad169894abf4..062277ec4d6f2b42dff2ae3d466397d97fa71cd3 100755 (executable)
@@ -1,6 +1,6 @@
 ! Copyright (C) 2005 Chris Double.
 ! See http://factorcode.org/license.txt for BSD license.
-USING: kernel lazy-lists tools.test strings math
+USING: kernel lists lists.lazy tools.test strings math
 sequences parser-combinators arrays math.parser unicode.categories ;
 IN: parser-combinators.tests
 
index 9537a0c88c7d4cb5afb9e389de2c1dab83d025c9..0a7ea49c4c77fc006a237f3c256ae79936a910b6 100755 (executable)
@@ -1,6 +1,6 @@
 ! Copyright (C) 2004 Chris Double.
 ! See http://factorcode.org/license.txt for BSD license.
-USING: lazy-lists promises kernel sequences strings math
+USING: lists lists.lazy promises kernel sequences strings math
 arrays splitting quotations combinators namespaces
 unicode.case unicode.categories sequences.deep ;
 IN: parser-combinators
index 78b731f5b0e0089e12b3bd2b3bebcd50181be3f9..a973206ab72b5a0d1dcc160dec958e5e6c8d235f 100755 (executable)
@@ -11,7 +11,7 @@ HELP: 'digit'
     "the input string. The numeric value of the digit "
     " consumed is the result of the parse." }
 { $examples
-{ $example "USING: lazy-lists parser-combinators parser-combinators.simple prettyprint ;" "\"123\" 'digit' parse-1 ." "1" } } ;
+{ $example "USING: lists lists.lazy parser-combinators parser-combinators.simple prettyprint ;" "\"123\" 'digit' parse-1 ." "1" } } ;
 
 HELP: 'integer'
 { $values 
@@ -21,7 +21,7 @@ HELP: 'integer'
     "the input string. The numeric value of the integer "
     " consumed is the result of the parse." }
 { $examples
-{ $example "USING: lazy-lists parser-combinators parser-combinators.simple prettyprint ;" "\"123\" 'integer' parse-1 ." "123" } } ;
+{ $example "USING: lists lists.lazy parser-combinators parser-combinators.simple prettyprint ;" "\"123\" 'integer' parse-1 ." "123" } } ;
 HELP: 'string'
 { $values 
   { "parser" "a parser object" } }
@@ -30,7 +30,7 @@ HELP: 'string'
     "quotations from the input string. The string value "
     " consumed is the result of the parse." }
 { $examples
-{ $example "USING: lazy-lists parser-combinators parser-combinators.simple prettyprint ;" "\"\\\"foo\\\"\" 'string' parse-1 ." "\"foo\"" } } ;
+{ $example "USING: lists lists.lazy parser-combinators parser-combinators.simple prettyprint ;" "\"\\\"foo\\\"\" 'string' parse-1 ." "\"foo\"" } } ;
 
 HELP: 'bold'
 { $values 
@@ -62,6 +62,6 @@ HELP: comma-list
     "'element' should be a parser that can parse the elements. The "
     "result of the parser is a sequence of the parsed elements." }
 { $examples
-{ $example "USING: lazy-lists parser-combinators parser-combinators.simple prettyprint ;" "\"1,2,3,4\" 'integer' comma-list parse-1 ." "{ 1 2 3 4 }" } } ;
+{ $example "USING: lists lists.lazy parser-combinators parser-combinators.simple prettyprint ;" "\"1,2,3,4\" 'integer' comma-list parse-1 ." "{ 1 2 3 4 }" } } ;
 
 { $see-also 'digit' 'integer' 'string' 'bold' 'italic' comma-list } related-words
index 745442610cc3cbab4bfb12d61182d877c8c03676..5182260e982690a1aaea4156513423249d2e1f21 100755 (executable)
@@ -1,6 +1,6 @@
 ! Copyright (C) 2006 Chris Double.
 ! See http://factorcode.org/license.txt for BSD license.
-USING: kernel strings math sequences lazy-lists words
+USING: kernel strings math sequences lists lists.lazy words
 math.parser promises parser-combinators unicode.categories ;
 IN: parser-combinators.simple
 
index 93754b69d1d95cc392850da38eb6df9ae3df940e..10e95bd2b5c3ab56020bb8e15ac0424bc754e467 100644 (file)
@@ -1,6 +1,6 @@
 ! Copyright (c) 2007 Aaron Schaefer.
 ! See http://factorcode.org/license.txt for BSD license.
-USING: lazy-lists math math.primes ;
+USING: lists lists.lazy math math.primes ;
 IN: project-euler.007
 
 ! http://projecteuler.net/index.php?section=problems&id=7
index 11af1960ed9f09341f51b16cc6d4865eacc9351a..ddba76d5a066619dd33b66e0ac006451297bc3d3 100644 (file)
@@ -1,6 +1,6 @@
 ! Copyright (c) 2007 Samuel Tardieu.
 ! See http://factorcode.org/license.txt for BSD license.
-USING: arrays kernel lazy-lists math.algebra math math.functions
+USING: arrays kernel lists lists.lazy math.algebra math math.functions
     math.order math.primes math.ranges project-euler.common sequences ;
 IN: project-euler.134
 
index d336d31114a0f5d0c0b4a685f2248f020e2a5614..d62f696a7490c9bc5e0e443f48a264bf025cbaab 100755 (executable)
@@ -8,26 +8,26 @@ HELP: QUALIFIED:
     "QUALIFIED: math\n1 2 math:+ ! ==> 3" } } ;
 
 HELP: QUALIFIED-WITH:
-{ $syntax "QUALIFIED-WITH: vocab prefix" }
-{ $description "Works like " { $link POSTPONE: QUALIFIED: } " but uses the specified prefix." }
+{ $syntax "QUALIFIED-WITH: vocab word-prefix" }
+{ $description "Works like " { $link POSTPONE: QUALIFIED: } " but uses " { $snippet "word-prefix" } " as prefix." }
 { $examples { $code
     "QUALIFIED-WITH: math m\n1 2 m:+ ! ==> 3" } } ;
 
 HELP: FROM:
 { $syntax "FROM: vocab => words ... ;" }
-{ $description "Imports the specified words from vocab." }
+{ $description "Imports " { $snippet "words" } " from " { $snippet "vocab" } "." }
 { $examples { $code
     "FROM: math.parser => bin> hex> ; ! imports only bin> and hex>" } } ;
 
 HELP: EXCLUDE:
 { $syntax "EXCLUDE: vocab => words ... ;" }
-{ $description "Imports everything from vocab excluding the specified words" }
+{ $description "Imports everything from " { $snippet "vocab" } " excluding " { $snippet "words" } "." }
 { $examples { $code
-    "EXCLUDE: math.parser => bin> hex> ; ! imports everythin but bin> and hex>" } } ;
+    "EXCLUDE: math.parser => bin> hex> ; ! imports everything but bin> and hex>" } } ;
 
 HELP: RENAME:
 { $syntax "RENAME: word vocab => newname " }
-{ $description "Imports word from vocab, but renamed to newname." }
+{ $description "Imports " { $snippet "word" } " from " { $snippet "vocab" } ", but renamed to " { $snippet "newname" } "." }
 { $examples { $code
     "RENAME: + math => -"
     "2 3 - ! => 5" } } ;
index 78ffaf5eeb9663ead1e016a56772849d81123b25..3c6e9f3bbd4dfeeace330c1c14e242ff64aad636 100755 (executable)
@@ -1,4 +1,4 @@
-USING: arrays combinators kernel lazy-lists math math.parser
+USING: arrays combinators kernel lists lists.lazy math math.parser
 namespaces parser parser-combinators parser-combinators.simple
 promises quotations sequences combinators.lib strings math.order
 assocs prettyprint.backend memoize unicode.case unicode.categories ;
index 6e616e51a94ac84ba0b5f9d18a32e45fe89ded4d..364c24b91fb9f85ee0aae10704e2830d76a485ae 100644 (file)
@@ -18,51 +18,67 @@ TUPLE: entry title link description pub-date ;
 
 C: <entry> entry
 
+: try-parsing-timestamp ( string -- timestamp )
+    [ rfc822>timestamp ] [ drop rfc3339>timestamp ] recover ;
+
 : rss1.0-entry ( tag -- entry )
-    [ "title" tag-named children>string ] keep   
-    [ "link" tag-named children>string ] keep
-    [ "description" tag-named children>string ] keep
-    f "date" "http://purl.org/dc/elements/1.1/" <name>
-    tag-named dup [ children>string rfc822>timestamp ] when
-    <entry> ;
+    {
+        [ "title" tag-named children>string ]
+        [ "link" tag-named children>string ]
+        [ "description" tag-named children>string ]
+        [
+            f "date" "http://purl.org/dc/elements/1.1/" <name>
+            tag-named dup [ children>string try-parsing-timestamp ] when
+        ]
+    } cleave <entry> ;
 
 : rss1.0 ( xml -- feed )
     [
         "channel" tag-named
-        [ "title" tag-named children>string ] keep
-        "link" tag-named children>string
-    ] keep
-    "item" tags-named [ rss1.0-entry ] map <feed> ;
+        [ "title" tag-named children>string ]
+        [ "link" tag-named children>string ] bi
+    ] [ "item" tags-named [ rss1.0-entry ] map ] bi
+    <feed> ;
 
 : rss2.0-entry ( tag -- entry )
-    [ "title" tag-named children>string ] keep
-    [ "link" tag-named ] keep
-    [ "guid" tag-named dupd ? children>string ] keep
-    [ "description" tag-named children>string ] keep
-    "pubDate" tag-named children>string rfc822>timestamp <entry> ;
+    {
+        [ "title" tag-named children>string ]
+        [ { "link" "guid" } any-tag-named children>string ]
+        [ "description" tag-named children>string ]
+        [
+            { "date" "pubDate" } any-tag-named
+            children>string try-parsing-timestamp
+        ]
+    } cleave <entry> ;
 
 : rss2.0 ( xml -- feed )
     "channel" tag-named 
-    [ "title" tag-named children>string ] keep
-    [ "link" tag-named children>string ] keep
-    "item" tags-named [ rss2.0-entry ] map <feed> ;
+    [ "title" tag-named children>string ]
+    [ "link" tag-named children>string ]
+    [ "item" tags-named [ rss2.0-entry ] map ]
+    tri <feed> ;
 
 : atom1.0-entry ( tag -- entry )
-    [ "title" tag-named children>string ] keep
-    [ "link" tag-named "href" swap at ] keep
-    [
-        { "content" "summary" } any-tag-named
-        dup tag-children [ string? not ] contains?
-        [ tag-children [ write-chunk ] with-string-writer ]
-        [ children>string ] if
-    ] keep
-    { "published" "updated" "issued" "modified" } any-tag-named
-    children>string rfc3339>timestamp <entry> ;
+    {
+        [ "title" tag-named children>string ]
+        [ "link" tag-named "href" swap at ]
+        [
+            { "content" "summary" } any-tag-named
+            dup tag-children [ string? not ] contains?
+            [ tag-children [ write-chunk ] with-string-writer ]
+            [ children>string ] if
+        ]
+        [
+            { "published" "updated" "issued" "modified" } 
+            any-tag-named children>string try-parsing-timestamp
+        ]
+    } cleave <entry> ;
 
 : atom1.0 ( xml -- feed )
-    [ "title" tag-named children>string ] keep
-    [ "link" tag-named "href" swap at ] keep
-    "entry" tags-named [ atom1.0-entry ] map <feed> ;
+    [ "title" tag-named children>string ]
+    [ "link" tag-named "href" swap at ]
+    [ "entry" tags-named [ atom1.0-entry ] map ]
+    tri <feed> ;
 
 : xml>feed ( xml -- feed )
     dup name-tag {
index 8e7d8c24e17addcf2aba2b5c3f7075d24004842c..88ad74840076e9c676d5f457c1ebecec3424544b 100644 (file)
@@ -1,4 +1,4 @@
-USING: html kernel semantic-db tangle.html tools.test ;
+USING: kernel semantic-db tangle.html tools.test ;
 IN: tangle.html.tests
 
 [ "test" ] [ "test" >html ] unit-test
index fc604f4d468d911556e0919eb6e90d510410e9c4..2ec6b526092826d7e875d55c65040a7bb081b1ae 100644 (file)
@@ -1,6 +1,6 @@
 ! Copyright (C) 2008 Alex Chapman
 ! See http://factorcode.org/license.txt for BSD license.
-USING: accessors html html.elements io io.streams.string kernel namespaces semantic-db sequences strings tangle.path ;
+USING: accessors html.elements io io.streams.string kernel namespaces semantic-db sequences strings tangle.path ;
 IN: tangle.html
 
 TUPLE: element attributes ;
index 52c454f97f3bbfae98880f06f3ec3b2b71f9aea7..8a4c6146deb7af57c79b3370632f3af0037de173 100644 (file)
@@ -19,11 +19,8 @@ C: <tangle> tangle
 : with-tangle ( tangle quot -- )
     [ [ db>> ] [ seq>> ] bi ] dip with-db ;
 
-: <text-response> ( text -- response )
-    "text/plain" <content> swap >>body ;
-
 : node-response ( id -- response )
-    load-node [ node-content <text-response> ] [ <404> ] if* ;
+    load-node [ node-content <text-content> ] [ <404> ] if* ;
 
 : display-node ( params -- response )
     [
@@ -39,7 +36,7 @@ C: <tangle> tangle
 : submit-node ( params -- response )
     [
         "node_content" swap at* [
-            create-node id>> number>string <text-response>
+            create-node id>> number>string <text-content>
         ] [
             drop <400>
         ] if
@@ -55,10 +52,7 @@ TUPLE: path-responder ;
 C: <path-responder> path-responder
 
 M: path-responder call-responder* ( path responder -- response )
-    drop path>file [ node-content <text-response> ] [ <404> ] if* ;
-
-: <json-response> ( obj -- response )
-    "application/json" <content> swap >json >>body ;
+    drop path>file [ node-content <text-content> ] [ <404> ] if* ;
 
 TUPLE: tangle-dispatcher < dispatcher tangle ;
 
@@ -67,7 +61,7 @@ TUPLE: tangle-dispatcher < dispatcher tangle ;
     <path-responder> >>default
     "resource:extra/tangle/resources" <static> "resources" add-responder
     <node-responder> "node" add-responder
-    <action> [ all-node-ids <json-response> ] >>display "all" add-responder ;
+    <action> [ all-node-ids <json-content> ] >>display "all" add-responder ;
 
 M: tangle-dispatcher call-responder* ( path dispatcher -- response )
     dup tangle>> [
index 644a9be1b52e829b4bc022f255cfc67ecbf32b93..a58c41eab6a849035cd8116ea85c4971b0b0fdfc 100644 (file)
@@ -1,7 +1,7 @@
 ! Copyright (C) 2006, 2007 Alex Chapman
 ! See http://factorcode.org/license.txt for BSD license.
 USING: kernel sequences math math.functions tetris.board
-tetris.piece tetris.tetromino lazy-lists combinators system ;
+tetris.piece tetris.tetromino lists lists.lazy combinators system ;
 IN: tetris.game
 
 TUPLE: tetris pieces last-update update-interval rows score game-state paused? running? ;
index 981b509bfa15c7d95fc901d4533d29a1a89bcef4..0117148d82f9dbc92dc862250b306d28a118b0b3 100644 (file)
@@ -1,7 +1,7 @@
 ! Copyright (C) 2006, 2007 Alex Chapman
 ! See http://factorcode.org/license.txt for BSD license.
 USING: kernel arrays tetris.tetromino math math.vectors 
-sequences quotations lazy-lists ;
+sequences quotations lists lists.lazy ;
 IN: tetris.piece
 
 #! A piece adds state to the tetromino that is the piece's delegate. The
index 37689f749f30ea2c2a3d8c2c64aacacc420ce96c..8ff22fb1ad8382afb779aaedceca66b9664c9cda 100755 (executable)
@@ -23,7 +23,7 @@ namespaces continuations layouts accessors ;
 [ ] [ "sudoku" shake-and-bake ] unit-test\r
 \r
 [ t ] [\r
-    cell 8 = 30 15 ? 100000 * small-enough?\r
+    cell 8 = 20 10 ? 100000 * small-enough?\r
 ] unit-test\r
 \r
 [ ] [ "hello-ui" shake-and-bake ] unit-test\r
@@ -37,6 +37,12 @@ namespaces continuations layouts accessors ;
     cell 8 = 40 20 ? 100000 * small-enough?\r
 ] unit-test\r
 \r
+[ ] [ "maze" shake-and-bake ] unit-test\r
+\r
+[ t ] [\r
+    cell 8 = 30 15 ? 100000 * small-enough?\r
+] unit-test\r
+\r
 [ ] [ "bunny" shake-and-bake ] unit-test\r
 \r
 [ t ] [\r
index 4f0d6ac036fc50120775b4801eb2d475e4fb7454..e8675f58910f1666c0ed05f38b1bc09f1a070444 100755 (executable)
@@ -108,6 +108,8 @@ IN: tools.deploy.shaker
 
 : stripped-globals ( -- seq )
     [
+        "callbacks" "alien.compiler" lookup ,
+
         {
             bootstrap.stage2:bootstrap-time
             continuations:error
@@ -142,6 +144,7 @@ IN: tools.deploy.shaker
 
             {
                 gensym
+                name>char-hook
                 classes:class-and-cache
                 classes:class-not-cache
                 classes:class-or-cache
@@ -167,6 +170,8 @@ IN: tools.deploy.shaker
                 vocabs:load-vocab-hook
                 word
             } %
+
+            { } { "optimizer.math.partial" } strip-vocab-globals %
         ] when
 
         strip-prettyprint? [
index ef6dac66f665e140cc243bd8e43f120622a045c2..2417e7ac3930ab33af266a7a4025f1839bbdbf42 100755 (executable)
@@ -4,7 +4,7 @@ USING: threads kernel namespaces continuations combinators
 sequences math namespaces.private continuations.private
 concurrency.messaging quotations kernel.private words
 sequences.private assocs models arrays accessors
-generic generic.standard ;
+generic generic.standard definitions ;
 IN: tools.walker
 
 SYMBOL: show-walker-hook ! ( status continuation thread -- )
@@ -73,6 +73,7 @@ M: object add-breakpoint ;
         { [ dup "step-into" word-prop ] [ "step-into" word-prop call ] }
         { [ dup standard-generic? ] [ effective-method (step-into-execute) ] }
         { [ dup hook-generic? ] [ effective-method (step-into-execute) ] }
+        { [ dup uses \ suspend swap member? ] [ execute break ] }
         { [ dup primitive? ] [ execute break ] }
         [ word-def (step-into-quot) ]
     } cond ;
@@ -89,7 +90,6 @@ SYMBOL: step-into
 SYMBOL: step-all
 SYMBOL: step-into-all
 SYMBOL: step-back
-SYMBOL: detach
 SYMBOL: abandon
 SYMBOL: call-in
 
@@ -137,7 +137,7 @@ SYMBOL: +stopped+
 {
     >n ndrop >c c>
     continue continue-with
-    stop yield suspend sleep (spawn)
+    stop suspend (spawn)
 } [
     dup [ execute break ] curry
     "step-into" set-word-prop
@@ -168,10 +168,7 @@ SYMBOL: +stopped+
     +running+ set-status ;
 
 : walker-stopped ( -- )
-    +stopped+ set-status
-    [ status +stopped+ eq? ]
-    [ [ drop f ] handle-synchronous ]
-    [ ] while ;
+    +stopped+ set-status ;
 
 : step-into-all-loop ( -- )
     +running+ set-status
index 29ea2eee2dce4dcbc10f60d08d3278a284ac5a10..e54e3cd538cf92392b4b38c1eaa4b765a38caa92 100644 (file)
@@ -1,14 +1,14 @@
 ! Copyright (c) 2005 Mackenzie Straight.
 ! See http://factorcode.org/license.txt for BSD license.
 USING: kernel tools.test trees.splay math namespaces assocs
-sequences random ;
+sequences random sets ;
 IN: trees.splay.tests
 
 : randomize-numeric-splay-tree ( splay-tree -- )
     100 [ drop 100 random swap at drop ] with each ;
 
 : make-numeric-splay-tree ( n -- splay-tree )
-    <splay> [ [ dupd set-at ] curry each ] keep ;
+    <splay> [ [ conjoin ] curry each ] keep ;
 
 [ t ] [
     100 make-numeric-splay-tree dup randomize-numeric-splay-tree
index f463a7c0e7350005dc0e1371c3c9b0ac4fc8c62e..855df9f564328695922ddca1c9d64f08edc6e2c4 100755 (executable)
@@ -38,7 +38,7 @@ HELP: render-glyph
 { $description "Renders a character and outputs a pointer to the bitmap." } ;
 
 HELP: <char-sprite>
-{ $values { "font" font } { "char" "a non-negative integer" } { "sprite" sprite } }
+{ $values { "open-font" font } { "char" "a non-negative integer" } { "sprite" sprite } }
 { $description "Renders a character to an OpenGL texture and records a display list which draws a quad with this texture. This word allocates native resources which must be freed by " { $link free-sprites } "." } ;
 
 HELP: (draw-string)
index 1c83bc9713ac76534880d6c25dfa12967decee37..3512bbf67000448202a6af652f298bbb4d687349 100755 (executable)
@@ -3,7 +3,8 @@
 USING: alien alien.accessors alien.c-types arrays io kernel libc
 math math.vectors namespaces opengl opengl.gl prettyprint assocs
 sequences io.files io.styles continuations freetype
-ui.gadgets.worlds ui.render ui.backend byte-arrays ;
+ui.gadgets.worlds ui.render ui.backend byte-arrays accessors
+locals ;
 
 IN: ui.freetype
 
@@ -41,8 +42,8 @@ M: font hashcode* drop font hashcode* ;
     ] bind ;
 
 M: freetype-renderer free-fonts ( world -- )
-    dup world-handle select-gl-context
-    world-fonts [ nip second free-sprites ] assoc-each ;
+    [ handle>> select-gl-context ]
+    [ fonts>> [ nip second free-sprites ] assoc-each ] bi ;
 
 : ttf-name ( font style -- name )
     2array H{
@@ -67,7 +68,7 @@ M: freetype-renderer free-fonts ( world -- )
     #! We use FT_New_Memory_Face, not FT_New_Face, since
     #! FT_New_Face only takes an ASCII path name and causes
     #! problems on localized versions of Windows
-    freetype -rot 0 f <void*> [
+    [ freetype ] 2dip 0 f <void*> [
         FT_New_Memory_Face freetype-error
     ] keep *void* ;
 
@@ -85,29 +86,29 @@ SYMBOL: dpi
 : font-units>pixels ( n font -- n )
     face-size face-size-y-scale FT_MulFix ;
 
-: init-ascent ( font face -- )
-    dup face-y-max swap font-units>pixels swap set-font-ascent ;
+: init-ascent ( font face -- font )
+    dup face-y-max swap font-units>pixels >>ascent ; inline
 
-: init-descent ( font face -- )
-    dup face-y-min swap font-units>pixels swap set-font-descent ;
+: init-descent ( font face -- font )
+    dup face-y-min swap font-units>pixels >>descent ; inline
 
-: init-font ( font -- )
-    dup font-handle 2dup init-ascent dupd init-descent
-    dup font-ascent over font-descent - ft-ceil
-    swap set-font-height ;
+: init-font ( font -- font )
+    dup handle>> init-ascent
+    dup handle>> init-descent
+    dup [ ascent>> ] [ descent>> ] bi - ft-ceil >>height ; inline
 
-: <font> ( handle -- font )
-    H{ } clone
-    { set-font-handle set-font-widths } font construct
-    dup init-font ;
+: set-char-size ( handle size -- )
+    0 swap 6 shift dpi get-global dup FT_Set_Char_Size freetype-error ;
 
-: (open-font) ( font -- open-font )
-    first3 >r open-face dup 0 r> 6 shift
-    dpi get-global dpi get-global FT_Set_Char_Size
-    freetype-error <font> ;
+: <font> ( handle -- font )
+    font new
+        H{ } clone >>widths
+        over first2 open-face >>handle
+        dup handle>> rot third set-char-size
+        init-font ;
 
 M: freetype-renderer open-font ( font -- open-font )
-    freetype drop open-fonts get [ (open-font) ] cache ;
+    freetype drop open-fonts get [ <font> ] cache ;
 
 : load-glyph ( font char -- glyph )
     >r font-handle dup r> 0 FT_Load_Char
@@ -132,30 +133,35 @@ M: freetype-renderer string-height ( open-font string -- h )
     load-glyph dup
     FT_RENDER_MODE_NORMAL FT_Render_Glyph freetype-error ;
 
-: copy-pixel ( bit tex -- bit tex )
-    255 f pick set-alien-unsigned-1 1+
-    f pick alien-unsigned-1
-    f pick set-alien-unsigned-1 >r 1+ r> 1+ ;
-
-: (copy-row) ( bit tex bitend texend -- bitend texend )
-    >r pick over >= [
-        2nip r>
-    ] [
-        >r copy-pixel r> r> (copy-row)
-    ] if ;
-
-: copy-row ( bit tex width width2 -- bitend texend width width2 )
-    [ pick + >r pick + r> (copy-row) ] 2keep ;
-
-: copy-bitmap ( glyph texture -- )
-    over glyph-bitmap-rows >r
-    over glyph-bitmap-width dup next-power-of-2 2 *
-    >r >r >r glyph-bitmap-buffer alien-address r> r> r> r> 
-    [ copy-row ] times 2drop 2drop ;
+:: copy-pixel ( i j bitmap texture -- i j )
+    255 j texture set-char-nth
+    i bitmap char-nth j 1 + texture set-char-nth
+    i 1 + j 2 + ; inline
+
+:: (copy-row) ( i j bitmap texture end -- )
+    i end < [
+        i j bitmap texture copy-pixel
+            bitmap texture end (copy-row)
+    ] when ; inline
+
+:: copy-row ( i j bitmap texture width width2 -- i j )
+    i j bitmap texture i width + (copy-row)
+    i width +
+    j width2 + ; inline
+
+:: copy-bitmap ( glyph texture -- )
+    [let* | bitmap [ glyph glyph-bitmap-buffer ]
+            rows [ glyph glyph-bitmap-rows ]
+            width [ glyph glyph-bitmap-width ]
+            width2 [ width next-power-of-2 2 * ] |
+        0 0
+        rows [ bitmap texture width width2 copy-row ] times
+        2drop
+    ] ;
 
 : bitmap>texture ( glyph sprite -- id )
     tuck sprite-size2 * 2 * [
-        alien-address [ copy-bitmap ] keep <alien> gray-texture
+        [ copy-bitmap ] keep gray-texture
     ] with-malloc ;
 
 : glyph-texture-loc ( glyph font -- loc )
@@ -163,34 +169,47 @@ M: freetype-renderer string-height ( open-font string -- h )
     font-ascent swap glyph-hori-bearing-y - ft-floor 2array ;
 
 : glyph-texture-size ( glyph -- dim )
-    dup glyph-bitmap-width next-power-of-2
-    swap glyph-bitmap-rows next-power-of-2 2array ;
+    [ glyph-bitmap-width next-power-of-2 ]
+    [ glyph-bitmap-rows next-power-of-2 ]
+    bi 2array ;
 
-: <char-sprite> ( font char -- sprite )
+: <char-sprite> ( open-font char -- sprite )
     over >r render-glyph dup r> glyph-texture-loc
     over glyph-size pick glyph-texture-size <sprite>
     [ bitmap>texture ] keep [ init-sprite ] keep ;
 
-: draw-char ( open-font char sprites -- )
-    [ dupd <char-sprite> ] cache nip
-    sprite-dlist glCallList ;
+:: char-sprite ( open-font sprites char -- sprite )
+    char sprites [ open-font swap <char-sprite> ] cache ;
+
+: draw-char ( open-font sprites char loc -- )
+    GL_MODELVIEW [
+        0 0 glTranslated
+        char-sprite sprite-dlist glCallList
+    ] do-matrix ;
+
+: char-widths ( open-font string -- widths )
+    [ char-width ] with { } map-as ;
+
+: scan-sums ( seq -- seq' )
+    0 [ + ] accumulate nip ;
 
-: (draw-string) ( open-font sprites string loc -- )
+:: (draw-string) ( open-font sprites string loc -- )
     GL_TEXTURE_2D [
-        [
-            [ >r 2dup r> swap draw-char ] each 2drop
+        loc [
+            string open-font string char-widths scan-sums [
+                [ open-font sprites ] 2dip draw-char
+            ] 2each
         ] with-translation
     ] do-enabled ;
 
-: font-sprites ( open-font world -- pair )
-    world-fonts [ open-font H{ } clone 2array ] cache ;
+: font-sprites ( font world -- open-font sprites )
+    world-fonts [ open-font H{ } clone 2array ] cache first2 ;
 
 M: freetype-renderer draw-string ( font string loc -- )
-    >r >r world get font-sprites first2 r> r> (draw-string) ;
+    >r >r world get font-sprites r> r> (draw-string) ;
 
 : run-char-widths ( open-font string -- widths )
-    [ char-width ] with { } map-as
-    dup 0 [ + ] accumulate nip swap 2 v/n v+ ;
+    char-widths [ scan-sums ] [ 2 v/n ] bi v+ ;
 
 M: freetype-renderer x>offset ( x open-font string -- n )
     dup >r run-char-widths [ <= ] with find drop
index 2e59363531f5b9ce42014fdb407660400097dd23..400169908b59554acbea37eed2b56ec6c7763fa8 100755 (executable)
@@ -6,7 +6,7 @@ models namespaces parser prettyprint quotations sequences
 strings threads listener classes.tuple ui.commands ui.gadgets
 ui.gadgets.editors ui.gadgets.presentations ui.gadgets.worlds
 ui.gestures definitions calendar concurrency.flags
-concurrency.mailboxes ui.tools.workspace accessors ;
+concurrency.mailboxes ui.tools.workspace accessors sets ;
 IN: ui.tools.interactor
 
 ! If waiting is t, we're waiting for user input, and invoking
@@ -76,7 +76,7 @@ M: interactor model-changed
     ] with-output-stream* ;
 
 : add-interactor-history ( str interactor -- )
-    over empty? [ 2drop ] [ interactor-history push-new ] if ;
+    over empty? [ 2drop ] [ interactor-history adjoin ] if ;
 
 : interactor-continue ( obj interactor -- )
     mailbox>> mailbox-put ;
index c9d6cb808f9b49800f7662da0516fb3ecd3e03ae..b4a54bb11de61843e5835cbdb49db3fc6d07ae47 100755 (executable)
@@ -20,7 +20,7 @@ IN: unicode.collation.tests
     [ execute ] 2with each ;\r
 \r
 [ f f f f ] [ "hello" "hi" test-equality ] unit-test\r
-[ t f f f ] [ "hello" "h\8ello" test-equality ] unit-test\r
+[ t f f f ] [ "hello" "h\u0000e9llo" test-equality ] unit-test\r
 [ t t f f ] [ "hello" "HELLO" test-equality ] unit-test\r
 [ t t t f ] [ "hello" "h e l l o." test-equality ] unit-test\r
 [ t t t t ] [ "hello" "\0hello\0" test-equality ] unit-test\r
diff --git a/extra/urls/authors.txt b/extra/urls/authors.txt
new file mode 100644 (file)
index 0000000..1901f27
--- /dev/null
@@ -0,0 +1 @@
+Slava Pestov
diff --git a/extra/urls/summary.txt b/extra/urls/summary.txt
new file mode 100644 (file)
index 0000000..caeda3d
--- /dev/null
@@ -0,0 +1 @@
+Tools for working with URLs (uniform resource locators)
diff --git a/extra/urls/tags.txt b/extra/urls/tags.txt
new file mode 100644 (file)
index 0000000..93e65ae
--- /dev/null
@@ -0,0 +1,2 @@
+web
+network
diff --git a/extra/urls/urls-tests.factor b/extra/urls/urls-tests.factor
new file mode 100644 (file)
index 0000000..e28816f
--- /dev/null
@@ -0,0 +1,194 @@
+IN: urls.tests
+USING: urls tools.test tuple-syntax arrays kernel assocs ;
+
+[ "hello%20world" ] [ "hello world" url-encode ] unit-test
+[ "hello world" ] [ "hello%20world" url-decode ] unit-test
+[ "~hello world" ] [ "%7ehello+world" url-decode ] unit-test
+[ f ] [ "%XX%XX%XX" url-decode ] unit-test
+[ f ] [ "%XX%XX%X" url-decode ] unit-test
+
+[ "hello world"   ] [ "hello+world"    url-decode ] unit-test
+[ "hello world"   ] [ "hello%20world"  url-decode ] unit-test
+[ " ! "           ] [ "%20%21%20"      url-decode ] unit-test
+[ "hello world"   ] [ "hello world%"   url-decode ] unit-test
+[ "hello world"   ] [ "hello world%x"  url-decode ] unit-test
+[ "hello%20world" ] [ "hello world"    url-encode ] unit-test
+[ "%20%21%20"     ] [ " ! "            url-encode ] unit-test
+
+[ "\u001234hi\u002045" ] [ "\u001234hi\u002045" url-encode url-decode ] unit-test
+
+[ "a=b&a=c" ] [ { { "a" { "b" "c" } } } assoc>query ] unit-test
+
+[ H{ { "a" "b" } } ] [ "a=b" query>assoc ] unit-test
+
+[ H{ { "a" { "b" "c" } } } ] [ "a=b&a=c" query>assoc ] unit-test
+
+[ "a=3" ] [ { { "a" 3 } } assoc>query ] unit-test
+
+: urls
+    {
+        {
+            TUPLE{ url
+                protocol: "http"
+                host: "www.apple.com"
+                port: 1234
+                path: "/a/path"
+                query: H{ { "a" "b" } }
+                anchor: "foo"
+            }
+            "http://www.apple.com:1234/a/path?a=b#foo"
+        }
+        {
+            TUPLE{ url
+                protocol: "http"
+                host: "www.apple.com"
+                path: "/a/path"
+                query: H{ { "a" "b" } }
+                anchor: "foo"
+            }
+            "http://www.apple.com/a/path?a=b#foo"
+        }
+        {
+            TUPLE{ url
+                protocol: "http"
+                host: "www.apple.com"
+                port: 1234
+                path: "/another/fine/path"
+                anchor: "foo"
+            }
+            "http://www.apple.com:1234/another/fine/path#foo"
+        }
+        {
+            TUPLE{ url
+                path: "/a/relative/path"
+                anchor: "foo"
+            }
+            "/a/relative/path#foo"
+        }
+        {
+            TUPLE{ url
+                path: "/a/relative/path"
+            }
+            "/a/relative/path"
+        }
+        {
+            TUPLE{ url
+                path: "a/relative/path"
+            }
+            "a/relative/path"
+        }
+    } ;
+
+urls [
+    [ 1array ] [ [ string>url ] curry ] bi* unit-test
+] assoc-each
+
+urls [
+    swap [ 1array ] [ [ url>string ] curry ] bi* unit-test
+] assoc-each
+
+[ "b" ] [ "a" "b" url-append-path ] unit-test
+
+[ "a/b" ] [ "a/c" "b" url-append-path ] unit-test
+
+[ "a/b" ] [ "a/" "b" url-append-path ] unit-test
+
+[ "/b" ] [ "a" "/b" url-append-path ] unit-test
+
+[ "/b" ] [ "a/b/" "/b" url-append-path ] unit-test
+
+[ "/xxx/bar" ] [ "/xxx/baz" "bar" url-append-path ] unit-test
+
+[
+    TUPLE{ url
+        protocol: "http"
+        host: "www.apple.com"
+        port: 1234
+        path: "/a/path"
+    }
+] [
+    TUPLE{ url
+        protocol: "http"
+        host: "www.apple.com"
+        port: 1234
+        path: "/foo"
+    }
+
+    TUPLE{ url
+        path: "/a/path"
+    }
+
+    derive-url
+] unit-test
+
+[
+    TUPLE{ url
+        protocol: "http"
+        host: "www.apple.com"
+        port: 1234
+        path: "/a/path/relative/path"
+        query: H{ { "a" "b" } }
+        anchor: "foo"
+    }
+] [
+    TUPLE{ url
+        protocol: "http"
+        host: "www.apple.com"
+        port: 1234
+        path: "/a/path/"
+    }
+
+    TUPLE{ url
+        path: "relative/path"
+        query: H{ { "a" "b" } }
+        anchor: "foo"
+    }
+
+    derive-url
+] unit-test
+
+[
+    TUPLE{ url
+        protocol: "http"
+        host: "www.apple.com"
+        port: 1234
+        path: "/a/path/relative/path"
+        query: H{ { "a" "b" } }
+        anchor: "foo"
+    }
+] [
+    TUPLE{ url
+        protocol: "http"
+        host: "www.apple.com"
+        port: 1234
+        path: "/a/path/"
+    }
+
+    TUPLE{ url
+        path: "relative/path"
+        query: H{ { "a" "b" } }
+        anchor: "foo"
+    }
+
+    derive-url
+] unit-test
+
+[
+    TUPLE{ url
+        protocol: "http"
+        host: "www.apple.com"
+        path: "/xxx/baz"
+    }
+] [
+    TUPLE{ url
+        protocol: "http"
+        host: "www.apple.com"
+        path: "/xxx/bar"
+    }
+
+    TUPLE{ url
+        path: "baz"
+    }
+
+    derive-url
+] unit-test
diff --git a/extra/urls/urls.factor b/extra/urls/urls.factor
new file mode 100644 (file)
index 0000000..e20df65
--- /dev/null
@@ -0,0 +1,160 @@
+! Copyright (C) 2008 Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+USING: kernel unicode.categories combinators sequences splitting
+fry namespaces assocs arrays strings mirrors
+io.encodings.string io.encodings.utf8
+math math.parser accessors namespaces.lib ;
+IN: urls
+
+: url-quotable? ( ch -- ? )
+    #! In a URL, can this character be used without
+    #! URL-encoding?
+    {
+        { [ dup letter? ] [ t ] }
+        { [ dup LETTER? ] [ t ] }
+        { [ dup digit? ] [ t ] }
+        { [ dup "/_-.:" member? ] [ t ] }
+        [ f ]
+    } cond nip ; foldable
+
+: push-utf8 ( ch -- )
+    1string utf8 encode
+    [ CHAR: % , >hex 2 CHAR: 0 pad-left % ] each ;
+
+: url-encode ( str -- str )
+    [
+        [ dup url-quotable? [ , ] [ push-utf8 ] if ] each
+    ] "" make ;
+
+: url-decode-hex ( index str -- )
+    2dup length 2 - >= [
+        2drop
+    ] [
+        [ 1+ dup 2 + ] dip subseq  hex> [ , ] when*
+    ] if ;
+
+: url-decode-% ( index str -- index str )
+    2dup url-decode-hex [ 3 + ] dip ;
+
+: url-decode-+-or-other ( index str ch -- index str )
+    dup CHAR: + = [ drop CHAR: \s ] when , [ 1+ ] dip ;
+
+: url-decode-iter ( index str -- )
+    2dup length >= [
+        2drop
+    ] [
+        2dup nth dup CHAR: % = [
+            drop url-decode-%
+        ] [
+            url-decode-+-or-other
+        ] if url-decode-iter
+    ] if ;
+
+: url-decode ( str -- str )
+    [ 0 swap url-decode-iter ] "" make utf8 decode ;
+
+: add-query-param ( value key assoc -- )
+    [
+        at [
+            {
+                { [ dup string? ] [ swap 2array ] }
+                { [ dup array? ] [ swap suffix ] }
+                { [ dup not ] [ drop ] }
+            } cond
+        ] when*
+    ] 2keep set-at ;
+
+: query>assoc ( query -- assoc )
+    dup [
+        "&" split H{ } clone [
+            [
+                [ "=" split1 [ dup [ url-decode ] when ] bi@ swap ] dip
+                add-query-param
+            ] curry each
+        ] keep
+    ] when ;
+
+: assoc>query ( hash -- str )
+    [
+        {
+            { [ dup number? ] [ number>string 1array ] }
+            { [ dup string? ] [ 1array ] }
+            { [ dup sequence? ] [ ] }
+        } cond
+    ] assoc-map
+    [
+        [
+            [ url-encode ] dip
+            [ url-encode "=" swap 3append , ] with each
+        ] assoc-each
+    ] { } make "&" join ;
+
+TUPLE: url protocol host port path query anchor ;
+
+: query-param ( request key -- value )
+    swap query>> at ;
+
+: set-query-param ( request value key -- request )
+    pick query>> set-at ;
+
+: parse-host ( string -- host port )
+    ":" split1 [ url-decode ] [
+        dup [
+            string>number
+            dup [ "Invalid port" throw ] unless
+        ] when
+    ] bi* ;
+
+: parse-host-part ( protocol rest -- string' )
+    [ "protocol" set ] [
+        "//" ?head [ "Invalid URL" throw ] unless
+        "/" split1 [
+            parse-host [ "host" set ] [ "port" set ] bi*
+        ] [ "/" prepend ] bi*
+    ] bi* ;
+
+: string>url ( string -- url )
+    [
+        ":" split1 [ parse-host-part ] when*
+        "#" split1 [
+            "?" split1 [ query>assoc "query" set ] when*
+            url-decode "path" set
+        ] [
+            url-decode "anchor" set
+        ] bi*
+    ] url make-object ;
+
+: unparse-host-part ( protocol -- )
+    %
+    "://" %
+    "host" get url-encode %
+    "port" get [ ":" % # ] when*
+    "path" get "/" head? [ "Invalid URL" throw ] unless ;
+
+: url>string ( url -- string )
+    [
+        <mirror> [
+            "protocol" get [ unparse-host-part ] when*
+            "path" get url-encode %
+            "query" get [ "?" % assoc>query % ] when*
+            "anchor" get [ "#" % url-encode % ] when*
+        ] bind
+    ] "" make ;
+
+: url-append-path ( path1 path2 -- path )
+    {
+        { [ dup "/" head? ] [ nip ] }
+        { [ dup empty? ] [ drop ] }
+        { [ over "/" tail? ] [ append ] }
+        { [ "/" pick start not ] [ nip ] }
+        [ [ "/" last-split1 drop "/" ] dip 3append ]
+    } cond ;
+
+: derive-url ( base url -- url' )
+    [ clone dup ] dip
+    2dup [ path>> ] bi@ url-append-path
+    [ [ <mirror> ] bi@ [ nip ] assoc-filter update ] dip
+    >>path ;
+
+: relative-url ( url -- url' )
+    clone f >>protocol f >>host f >>port ;
diff --git a/extra/validators/validators-tests.factor b/extra/validators/validators-tests.factor
new file mode 100644 (file)
index 0000000..7d4325c
--- /dev/null
@@ -0,0 +1,118 @@
+IN: validators.tests
+USING: kernel sequences tools.test validators accessors
+namespaces assocs ;
+
+: with-validation ( quot -- messages )
+    [
+        init-validation
+        call
+        validation-messages get
+        named-validation-messages get >alist append
+    ] with-scope ; inline
+
+[ "" v-one-line ] must-fail
+[ "hello world" ] [ "hello world" v-one-line ] unit-test
+[ "hello\nworld" v-one-line ] must-fail
+
+[ "" v-one-word ] must-fail
+[ "hello" ] [ "hello" v-one-word ] unit-test
+[ "hello world" v-one-word ] must-fail
+
+[ "foo" v-number ] must-fail
+[ 123 ] [ "123" v-number ] unit-test
+[ 123 ] [ "123" v-integer ] unit-test
+
+[ "1.0" v-integer ] [ "must be an integer" = ] must-fail-with
+
+[ "slava@factorcode.org" ] [
+    "slava@factorcode.org" v-email
+] unit-test
+
+[ "slava+foo@factorcode.org" ] [
+    "slava+foo@factorcode.org" v-email
+] unit-test
+
+[ "slava@factorcode.o" v-email ]
+[ "invalid e-mail" = ] must-fail-with
+
+[ "sla@@factorcode.o" v-email ]
+[ "invalid e-mail" = ] must-fail-with
+
+[ "slava@factorcodeorg" v-email ]
+[ "invalid e-mail" = ] must-fail-with
+
+[ "http://www.factorcode.org" ]
+[ "http://www.factorcode.org" v-url ] unit-test
+
+[ "http:/www.factorcode.org" v-url ]
+[ "invalid URL" = ] must-fail-with
+
+[ 4561261212345467 ] [ "4561261212345467" v-credit-card ] unit-test
+
+[ 4561261212345467 ] [ "4561-2612-1234-5467" v-credit-card ] unit-test
+
+[ 0 ] [ "0000000000000000" v-credit-card ] unit-test
+
+[ "000000000" v-credit-card ] must-fail
+
+[ "0000000000000000000000000" v-credit-card ] must-fail
+
+[ "4561_2612_1234_5467" v-credit-card ] must-fail
+
+[ "4561-2621-1234-5467" v-credit-card ] must-fail
+
+
+[ 14 V{ } ] [
+    [
+        "14" "age" [ v-number 13 v-min-value 100 v-max-value ] validate
+    ] with-validation
+] unit-test
+
+[ f t ] [
+    [
+        "140" "age" [ v-number 13 v-min-value 100 v-max-value ] validate
+    ] with-validation first
+    [ first "age" = ]
+    [ second validation-error? ]
+    [ second value>> "140" = ]
+    tri and and
+] unit-test
+
+TUPLE: person name age ;
+
+person {
+    { "name" [ ] }
+    { "age" [ v-number 13 v-min-value 100 v-max-value ] }
+} define-validators
+
+[ t t ] [
+    [
+        { { "age" "" } } required-values
+        validation-failed?
+    ] with-validation first
+    [ first "age" = ]
+    [ second validation-error? ]
+    [ second message>> "required" = ]
+    tri and and
+] unit-test
+
+[ H{ { "a" 123 } } f V{ } ] [
+    [
+        H{
+            { "a" "123" }
+            { "b" "c" }
+            { "c" "d" }
+        }
+        H{
+            { "a" [ v-integer ] }
+        } validate-values
+        validation-failed?
+    ] with-validation
+] unit-test
+
+[ t "foo" ] [
+    [
+        "foo" validation-error
+        validation-failed?
+    ] with-validation first message>>
+] unit-test
diff --git a/extra/validators/validators.factor b/extra/validators/validators.factor
new file mode 100644 (file)
index 0000000..aeb2dc2
--- /dev/null
@@ -0,0 +1,159 @@
+! Copyright (C) 2006, 2008 Slava Pestov
+! See http://factorcode.org/license.txt for BSD license.
+USING: kernel continuations sequences sequences.lib math
+namespaces sets math.parser math.ranges assocs regexp fry
+unicode.categories arrays hashtables words combinators mirrors
+classes quotations xmode.catalog ;
+IN: validators
+
+: v-default ( str def -- str )
+    over empty? spin ? ;
+
+: v-required ( str -- str )
+    dup empty? [ "required" throw ] when ;
+
+: v-optional ( str quot -- str )
+    over empty? [ 2drop f ] [ call ] if ; inline
+
+: v-min-length ( str n -- str )
+    over length over < [
+        [ "must be at least " % # " characters" % ] "" make
+        throw
+    ] [
+        drop
+    ] if ;
+
+: v-max-length ( str n -- str )
+    over length over > [
+        [ "must be no more than " % # " characters" % ] "" make
+        throw
+    ] [
+        drop
+    ] if ;
+
+: v-number ( str -- n )
+    dup string>number [ ] [ "must be a number" throw ] ?if ;
+
+: v-integer ( str -- n )
+    v-number dup integer? [ "must be an integer" throw ] unless ;
+
+: v-min-value ( x n -- x )
+    2dup < [
+        [ "must be at least " % # ] "" make throw
+    ] [
+        drop
+    ] if ;
+
+: v-max-value ( x n -- x )
+    2dup > [
+        [ "must be no more than " % # ] "" make throw
+    ] [
+        drop
+    ] if ;
+
+: v-regexp ( str what regexp -- str )
+    >r over r> matches?
+    [ drop ] [ "invalid " prepend throw ] if ;
+
+: v-email ( str -- str )
+    #! From http://www.regular-expressions.info/email.html
+    60 v-max-length
+    "e-mail"
+    R' [A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}'i
+    v-regexp ;
+
+: v-url ( str -- str )
+    "URL"
+    R' (ftp|http|https)://(\w+:?\w*@)?(\S+)(:[0-9]+)?(/|/([\w#!:.?+=&%@!\-/]))?'
+    v-regexp ;
+
+: v-captcha ( str -- str )
+    dup empty? [ "must remain blank" throw ] unless ;
+
+: v-one-line ( str -- str )
+    v-required
+    dup "\r\n" intersect empty?
+    [ "must be a single line" throw ] unless ;
+
+: v-one-word ( str -- str )
+    v-required
+    dup [ alpha? ] all?
+    [ "must be a single word" throw ] unless ;
+
+: v-username ( str -- str )
+    2 v-min-length 16 v-max-length v-one-word ;
+
+: v-password ( str -- str )
+    6 v-min-length 40 v-max-length v-one-line ;
+
+: v-mode ( str -- str )
+    dup mode-names member? [
+        "not a valid syntax mode" throw 
+    ] unless ;
+
+: luhn? ( n -- ? )
+    string>digits <reversed>
+    [ odd? [ 2 * 10 /mod + ] when ] map-index
+    sum 10 mod 0 = ;
+
+: v-credit-card ( str -- n )
+    "- " diff
+    dup CHAR: 0 CHAR: 9 [a,b] diff empty? [
+        13 v-min-length
+        16 v-max-length
+        dup luhn? [ string>number ] [
+            "card number check failed" throw
+        ] if
+    ] [
+        "invalid credit card number format" throw
+    ] if ;
+
+SYMBOL: validation-messages
+SYMBOL: named-validation-messages
+
+: init-validation ( -- )
+    V{ } clone validation-messages set
+    H{ } clone named-validation-messages set ;
+
+: (validation-message) ( obj -- )
+    validation-messages get push ;
+
+: (validation-message-for) ( obj name -- )
+    named-validation-messages get set-at ;
+
+TUPLE: validation-message message ;
+
+C: <validation-message> validation-message
+
+: validation-message ( string -- )
+    <validation-message> (validation-message) ;
+
+: validation-message-for ( string name -- )
+    [ <validation-message> ] dip (validation-message-for) ;
+
+TUPLE: validation-error message value ;
+
+C: <validation-error> validation-error
+
+: validation-error ( message -- )
+    f <validation-error> (validation-message) ;
+
+: validation-error-for ( message value name -- )
+    [ <validation-error> ] dip (validation-message-for) ;
+
+: validation-failed? ( -- ? )
+    validation-messages get [ validation-error? ] contains?
+    named-validation-messages get [ nip validation-error? ] assoc-contains?
+    or ;
+
+: define-validators ( class validators -- )
+    >hashtable "validators" set-word-prop ;
+
+: validate ( value name quot -- result )
+    '[ drop @ ] [ -rot validation-error-for f ] recover ; inline
+
+: required-values ( assoc -- )
+    [ swap [ v-required ] validate drop ] assoc-each ;
+
+: validate-values ( assoc validators -- assoc' )
+    swap '[ [ [ dup , at ] keep ] dip validate ] assoc-map ;
index 3cc1eb567b1fb5ccdbaac1ee00fe4eb9ed199549..04194adb293a7cb81d38991b0f98c3dee7be12a6 100644 (file)
@@ -1,26 +1,25 @@
-USING: math kernel accessors http.server http.server.actions
-http.server.sessions http.server.templating
-http.server.templating.fhtml locals ;
+USING: math kernel accessors html.components
+http.server http.server.actions
+http.server.sessions html.templates.chloe fry ;
 IN: webapps.counter
 
 SYMBOL: count
 
 TUPLE: counter-app < dispatcher ;
 
-M: counter-app init-session*
-    drop 0 count sset ;
+M: counter-app init-session* drop 0 count sset ;
 
-:: <counter-action> ( quot -- action )
-    <action> [
-        count quot schange
-        "" f <standard-redirect>
-    ] >>display ;
+: <counter-action> ( quot -- action )
+    <action>
+        swap '[ count , schange "" f <standard-redirect> ] >>submit ;
 
 : counter-template ( -- template )
-    "resource:extra/webapps/counter/counter.fhtml" <fhtml> ;
+    "resource:extra/webapps/counter/counter.xml" <chloe> ;
 
 : <display-action> ( -- action )
-    <action> [ counter-template serve-template ] >>display ;
+    <page-action>
+        [ count sget "counter" set-value ] >>init
+        counter-template >>template ;
 
 : <counter-app> ( -- responder )
     counter-app new-dispatcher
diff --git a/extra/webapps/counter/counter.fhtml b/extra/webapps/counter/counter.fhtml
deleted file mode 100644 (file)
index 521096f..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<% USING: io math.parser http.server.sessions webapps.counter ; %>
-
-<html>
-    <body>
-        <h1><% count sget number>string write %></h1>
-
-        <a href="inc">++</a>
-        <a href="dec">--</a>
-    </body>
-</html>
diff --git a/extra/webapps/counter/counter.xml b/extra/webapps/counter/counter.xml
new file mode 100644 (file)
index 0000000..75e7cf3
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+
+       <body>
+               <h1><t:label t:name="counter" /></h1>
+
+               <t:button t:action="$counter-app/inc">++</t:button>
+               <t:button t:action="$counter-app/dec">--</t:button>
+       </body>
+
+</t:chloe>
index 9b3ce57d022bbc27d95f53c9cba1205ad7d73da6..9ad4a054922c01bf14819f2e3ea487d1656168b2 100644 (file)
@@ -1,19 +1,21 @@
 ! Copyright (c) 2008 Slava Pestov
 ! See http://factorcode.org/license.txt for BSD license.
 USING: accessors kernel sequences assocs io.files io.sockets
+io.server
 namespaces db db.sqlite smtp
 http.server
 http.server.db
 http.server.flows
 http.server.sessions
-http.server.auth.admin
 http.server.auth.login
 http.server.auth.providers.db
 http.server.boilerplate
-http.server.templating.chloe
+html.templates.chloe
 webapps.pastebin
 webapps.planet
-webapps.todo ;
+webapps.todo
+webapps.wiki
+webapps.user-admin ;
 IN: webapps.factor-website
 
 : test-db "resource:test.db" sqlite-db ;
@@ -30,15 +32,20 @@ IN: webapps.factor-website
         init-annotations-table
 
         init-blog-table
+        init-postings-table
 
         init-todo-table
+
+        init-articles-table
+        init-revisions-table
     ] with-db ;
 
 : <factor-website> ( -- responder )
-    <dispatcher>
+    <dispatcher> 
         <todo-list> "todo" add-responder
         <pastebin> "pastebin" add-responder
         <planet-factor> "planet" add-responder
+        <wiki> "wiki" add-responder
         <user-admin> "user-admin" add-responder
     <login>
         users-in-db >>users
@@ -59,7 +66,7 @@ IN: webapps.factor-website
 
     <factor-website> main-responder set-global ;
 
-: start-factor-website
+: start-factor-website ( -- )
     test-db start-expiring-sessions
-    "planet" main-responder get responders>> at test-db start-update-task
+    test-db start-update-task
     8812 httpd ;
index 55721d7bef3c696f19d03b0c209e05d9b24aa77d..49e26883adddffd84abc5615c5f0312492589b8a 100644 (file)
@@ -21,6 +21,8 @@ a:hover, .link:hover {
 
 .error { color: #a00; }
 
+.errors li { color: #a00; }
+
 .field-label {
        text-align: right;
 }
@@ -40,12 +42,15 @@ a:hover, .link:hover {
 }
 
 .description {
-       border: 1px dashed #ccc;
-       background-color: #f5f5f5;
        padding: 5px;
        color: #000;
 }
 
+.description pre {
+       border: 1px dashed #ccc;
+       background-color: #f5f5f5;
+}
+
 .description p:first-child {
        margin-top: 0px;
 }
@@ -53,3 +58,21 @@ a:hover, .link:hover {
 .description p:last-child {
        margin-bottom: 0px;
 }
+
+.description table, .description td {
+    border-color: #666;
+    border-style: solid;
+}
+
+.description table {
+    border-width: 0 0 1px 1px;
+    border-spacing: 0;
+    border-collapse: collapse;
+}
+
+.description td {
+    margin: 0;
+    padding: 4px;
+    border-width: 1px 1px 0 0;
+}
+
diff --git a/extra/webapps/pastebin/annotation.xml b/extra/webapps/pastebin/annotation.xml
deleted file mode 100644 (file)
index d5b4ea8..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <h2>Annotation: <t:view t:component="summary" /></h2>
-
-       <table>
-               <tr><th class="field-label">Author:  </th><td><t:view t:component="author"  /></td></tr>
-               <tr><th class="field-label">Mode:    </th><td><t:view t:component="mode"    /></td></tr>
-               <tr><th class="field-label">Date:    </th><td><t:view t:component="date"    /></td></tr>
-       </table>
-
-       <pre class="description"><t:view t:component="contents" /></pre>
-
-       <t:button t:action="$pastebin/delete-annotation" t:for="aid" class="link-button link">Delete Annotation</t:button>
-
-</t:chloe>
diff --git a/extra/webapps/pastebin/new-annotation.xml b/extra/webapps/pastebin/new-annotation.xml
deleted file mode 100644 (file)
index 5d18860..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <t:title>New Annotation</t:title>
-
-       <t:form t:action="$pastebin/annotate" t:for="id">
-
-               <table>
-                       <tr><th class="field-label">Summary: </th><td><t:edit t:component="summary" /></td></tr>
-                       <tr><th class="field-label">Author: </th><td><t:edit t:component="author" /></td></tr>
-                       <tr><th class="field-label">Mode: </th><td><t:edit t:component="mode" /></td></tr>
-                       <tr><th class="field-label big-field-label">Description:</th><td><t:edit t:component="contents" /></td></tr>
-                       <tr><th class="field-label">Captcha: </th><td><t:edit t:component="captcha" /></td></tr>
-                       <tr>
-                       <td></td>
-                       <td>Leave the captcha blank. Spam-bots will fill it indiscriminantly, so their attempts to register will be blocked.</td>
-                       </tr>
-               </table>
-
-               <input type="SUBMIT" value="Done" />
-       </t:form>
-
-</t:chloe>
index 86daf09aeb5560112c699250d5af158d1f30800a..6abae4895ba502f74415a6f7a9178c46ff41502b 100644 (file)
@@ -7,11 +7,11 @@
        <t:form t:action="$pastebin/new-paste">
 
                <table>
-                       <tr><th class="field-label">Summary: </th><td><t:edit t:component="summary" /></td></tr>
-                       <tr><th class="field-label">Author: </th><td><t:edit t:component="author" /></td></tr>
-                       <tr><th class="field-label">Mode: </th><td><t:edit t:component="mode" /></td></tr>
-                       <tr><th class="field-label big-field-label">Description: </th><td><t:edit t:component="contents" /></td></tr>
-                       <tr><th class="field-label">Captcha: </th><td><t:edit t:component="captcha" /></td></tr>
+                       <tr><th class="field-label">Summary: </th><td><t:field t:name="summary" /></td></tr>
+                       <tr><th class="field-label">Author: </th><td><t:field t:name="author" /></td></tr>
+                       <tr><th class="field-label">Mode: </th><td><t:choice t:name="mode" t:choices="modes" /></td></tr>
+                       <tr><th class="field-label big-field-label">Body: </th><td><t:textarea t:name="contents" t:rows="20" t:cols="60" /></td></tr>
+                       <tr><th class="field-label">Captcha: </th><td><t:field t:name="captcha" /></td></tr>
                        <tr>
                        <td></td>
                        <td>Leave the captcha blank. Spam-bots will fill it indiscriminantly, so their attempts to register will be blocked.</td>
diff --git a/extra/webapps/pastebin/paste-list.xml b/extra/webapps/pastebin/paste-list.xml
deleted file mode 100644 (file)
index c91aa6f..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <t:title>Pastebin</t:title>
-
-       <table width="100%">
-               <th align="left" width="50%">Summary:</th>
-               <th align="left" width="100">Paste by:</th>
-               <th align="left" width="200">Date:</th>
-
-               <t:summary t:component="pastes" />
-       </table>
-
-</t:chloe>
diff --git a/extra/webapps/pastebin/paste-summary.xml b/extra/webapps/pastebin/paste-summary.xml
deleted file mode 100644 (file)
index c751b11..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <tr>
-               <td><t:a t:href="$pastebin/view-paste" t:query="id"><t:view t:component="summary" /></t:a></td>
-               <td><t:view t:component="author" /></td>
-               <td><t:view t:component="date" /></td>
-       </tr>
-
-</t:chloe>
index 9141ee4ef1b8d3ba338792bf92fd75d565919a09..57c2fdb7c2b27418dab789eb7efc817a012e60e8 100644 (file)
@@ -2,19 +2,59 @@
 
 <t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
 
-       <t:title>Paste: <t:view t:component="summary" /></t:title>
+       <t:atom t:title="Paste - Atom" t:href="$pastebin/paste.atom" t:query="id" />
+
+       <t:title>Paste: <t:label t:name="summary" /></t:title>
 
        <table>
-               <tr><th class="field-label">Author:  </th><td><t:view t:component="author"  /></td></tr>
-               <tr><th class="field-label">Mode:    </th><td><t:view t:component="mode"    /></td></tr>
-               <tr><th class="field-label">Date:    </th><td><t:view t:component="date"    /></td></tr>
+               <tr><th class="field-label">Author: </th><td><t:label t:name="author" /></td></tr>
+               <tr><th class="field-label">Mode: </th><td><t:label t:name="mode" /></td></tr>
+               <tr><th class="field-label">Date: </th><td><t:label t:name="date" /></td></tr>
        </table>
 
-       <pre class="description"><t:view t:component="contents" /></pre>
+       <pre class="description"><t:code t:name="contents" t:mode="modes" /></pre>
 
        <t:button t:action="$pastebin/delete-paste" t:for="id" class="link-button link">Delete Paste</t:button>
        |
        <t:a t:href="$pastebin/annotate" t:query="id">Annotate</t:a>
 
-       <t:view t:component="annotations" />
+       <t:each-tuple t:values="annotations">
+
+               <h2>Annotation: <t:label t:name="summary" /></h2>
+
+               <table>
+                       <tr><th class="field-label">Author: </th><td><t:label t:name="author" /></td></tr>
+                       <tr><th class="field-label">Mode: </th><td><t:label t:name="mode" /></td></tr>
+                       <tr><th class="field-label">Date: </th><td><t:label t:name="date" /></td></tr>
+               </table>
+
+               <pre class="description"><t:code t:name="contents" t:mode="mode"/></pre>
+
+               <t:button t:action="$pastebin/delete-annotation" t:for="aid" class="link-button link">Delete Annotation</t:button>
+
+       </t:each-tuple>
+
+       <t:bind-assoc t:name="new-annotation">
+
+               <h2>New Annotation</h2>
+
+               <t:form t:action="$pastebin/new-annotation" t:for="id">
+
+                       <table>
+                               <tr><th class="field-label">Summary: </th><td><t:field t:name="summary" /></td></tr>
+                               <tr><th class="field-label">Author: </th><td><t:field t:name="author" /></td></tr>
+                               <tr><th class="field-label">Mode: </th><td><t:choice t:name="mode" t:choices="modes" /></td></tr>
+                               <tr><th class="field-label big-field-label">Body:</th><td><t:textarea t:name="contents" t:rows="20" t:cols="60" /></td></tr>
+                               <tr><th class="field-label">Captcha: </th><td><t:field t:name="captcha" /></td></tr>
+                               <tr>
+                               <td></td>
+                               <td>Leave the captcha blank. Spam-bots will fill it indiscriminantly, so their attempts to register will be blocked.</td>
+                               </tr>
+                       </table>
+
+                       <input type="SUBMIT" value="Done" />
+               </t:form>
+
+       </t:bind-assoc>
+
 </t:chloe>
diff --git a/extra/webapps/pastebin/pastebin-common.xml b/extra/webapps/pastebin/pastebin-common.xml
new file mode 100644 (file)
index 0000000..f785fce
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:style t:include="resource:extra/webapps/pastebin/pastebin.css" />
+
+       <div class="navbar">
+
+                 <t:a t:href="$pastebin/list">Pastes</t:a>
+               | <t:a t:href="$pastebin/new-paste">New Paste</t:a>
+
+               <t:if t:code="http.server.sessions:uid">
+
+                       <t:if t:code="http.server.auth.login:allow-edit-profile?">
+                               | <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
+                       </t:if>
+
+                       | <t:button t:action="$login/logout" t:flow="begin" class="link-button link">Logout</t:button>
+
+               </t:if>
+
+       </div>
+
+       <h1><t:write-title /></h1>
+
+        <t:call-next-template />
+
+</t:chloe>
index 273b250695867762b1834f30cae85113a6baa389..43cae74ec870876a30faea7e72ffdef573fe6198 100644 (file)
@@ -1,29 +1,23 @@
+! Copyright (C) 2007, 2008 Slava Pestov
+! See http://factorcode.org/license.txt for BSD license.
 USING: namespaces assocs sorting sequences kernel accessors
-hashtables sequences.lib locals db.types db.tuples db
-calendar calendar.format rss xml.writer
-xmode.catalog
+hashtables sequences.lib db.types db.tuples db combinators
+calendar calendar.format math.parser rss xml.writer
+xmode.catalog validators html.components html.templates.chloe
 http.server
-http.server.crud
 http.server.actions
-http.server.components
-http.server.components.code
-http.server.templating.chloe
 http.server.auth
 http.server.auth.login
-http.server.boilerplate
-http.server.validators
-http.server.forms ;
+http.server.boilerplate ;
 IN: webapps.pastebin
 
-: <mode> ( id -- component )
-    modes keys natural-sort <choice> ;
+! ! !
+! DOMAIN MODEL
+! ! !
 
-: pastebin-template ( name -- template )
-    "resource:extra/webapps/pastebin/" swap ".xml" 3append <chloe> ;
-
-TUPLE: paste id summary author mode date contents annotations captcha ;
+TUPLE: entity id summary author mode date contents ;
 
-paste "PASTE"
+entity f
 {
     { "id" "ID" INTEGER +db-assigned-id+ }
     { "summary" "SUMMARY" { VARCHAR 256 } +not-null+ }
@@ -33,205 +27,202 @@ paste "PASTE"
     { "contents" "CONTENTS" TEXT +not-null+ }
 } define-persistent
 
+TUPLE: paste < entity annotations ;
+
+\ paste "PASTES" { } define-persistent
+
 : <paste> ( id -- paste )
-    paste new
+    paste new
         swap >>id ;
 
 : pastes ( -- pastes )
     f <paste> select-tuples ;
 
-TUPLE: annotation aid id summary author mode contents date captcha ;
+TUPLE: annotation < entity parent ;
 
-annotation "ANNOTATION"
+annotation "ANNOTATIONS"
 {
-    { "aid" "AID" INTEGER +db-assigned-id+ }
-    { "id" "ID" INTEGER +not-null+ }
-    { "summary" "SUMMARY" { VARCHAR 256 } +not-null+ }
-    { "author" "AUTHOR" { VARCHAR 256 } +not-null+ }
-    { "mode" "MODE" { VARCHAR 256 } +not-null+ }
-    { "date" "DATE" DATETIME +not-null+ }
-    { "contents" "CONTENTS" TEXT +not-null+ }
+    { "parent" "PARENT" INTEGER +not-null+ }
 } define-persistent
 
-: <annotation> ( id aid -- annotation )
+: <annotation> ( parent id -- annotation )
     annotation new
-        swap >>aid
-        swap >>id ;
+        swap >>id
+        swap >>parent ;
 
 : fetch-annotations ( paste -- paste )
     dup annotations>> [
         dup id>> f <annotation> select-tuples >>annotations
     ] unless ;
 
-: <annotation-form> ( -- form )
-    "annotation" <form>
-        "annotation" pastebin-template >>view-template
-        "id" <integer>
-            hidden >>renderer
-            add-field
-        "aid" <integer>
-            hidden >>renderer
-            add-field
-        "summary" <string> add-field
-        "author" <string> add-field
-        "mode" <mode> add-field
-        "contents" "mode" <code> add-field
-        "date" <date> add-field ;
-
-: <new-annotation-form> ( -- form )
-    "annotation" <form>
-        "new-annotation" pastebin-template >>edit-template
-        "id" <integer>
-            hidden >>renderer
-            t >>required add-field
-        "summary" <string>
-            t >>required add-field
-        "author" <string>
-            t >>required
-            add-field
-        "mode" <mode>
-            "factor" >>default
-            t >>required
-            add-field
-        "contents" "mode" <code>
-            t >>required add-field
-        "captcha" <captcha> add-field ;
-
-: <paste-form> ( -- form )
-    "paste" <form>
-        "paste" pastebin-template >>view-template
-        "paste-summary" pastebin-template >>summary-template
-        "id" <integer>
-            hidden >>renderer add-field
-        "summary" <string> add-field
-        "author" <string> add-field
-        "mode" <mode> add-field
-        "date" <date> add-field
-        "contents" "mode" <code> add-field
-        "annotations" <annotation-form> +plain+ <list> add-field ;
-
-: <new-paste-form> ( -- form )
-    "paste" <form>
-        "new-paste" pastebin-template >>edit-template
-        "summary" <string>
-            t >>required add-field
-        "author" <string>
-            t >>required add-field
-        "mode" <mode>
-            "factor" >>default
-            t >>required
-            add-field
-        "contents" "mode" <code>
-            t >>required add-field
-        "captcha" <captcha> add-field ;
-
-: <paste-list-form> ( -- form )
-    "pastebin" <form>
-        "paste-list" pastebin-template >>view-template
-        "pastes" <paste-form> +plain+ <list> add-field ;
-
-:: <paste-list-action> ( -- action )
-    [let | form [ <paste-list-form> ] |
-        <action>
-            [
-                blank-values
+: paste ( id -- paste )
+    <paste> select-tuple fetch-annotations ;
 
-                pastes "pastes" set-value
+: <id-redirect> ( id next -- response )
+    swap "id" associate <standard-redirect> ;
 
-                form view-form
-            ] >>display
-    ] ;
+! ! !
+! LINKS, ETC
+! ! !
 
-:: <annotate-action> ( form ctor next -- action )
-    <action>
-        { { "id" [ v-number ] } } >>get-params
+: pastebin-link ( -- url )
+    "$pastebin/list" f link>string ;
 
-        [
-            "id" get f ctor call
-
-            from-tuple form set-defaults
-        ] >>init
+GENERIC: entity-link ( entity -- url )
 
-        [ form edit-form ] >>display
+M: paste entity-link
+    id>> "id" associate "$pastebin/paste" swap link>string ;
 
-        [
-            f f ctor call from-tuple
+M: annotation entity-link
+    [ parent>> "parent" associate "$pastebin/paste" swap link>string ]
+    [ id>> number>string "#" prepend ] bi
+    append ;
 
-            form validate-form
+: pastebin-template ( name -- template )
+    "resource:extra/webapps/pastebin/" swap ".xml" 3append <chloe> ;
 
-            values-tuple insert-tuple
+! ! !
+! PASTE LIST
+! ! !
 
-            "id" value next <id-redirect>
-        ] >>submit ;
+: <pastebin-action> ( -- action )
+    <page-action>
+        [ pastes "pastes" set-value ] >>init
+        "pastebin" pastebin-template >>template ;
 
-: pastebin-feed-entries ( -- entries )
-    pastes <reversed> 20 short head [
-        [ summary>> ]
-        [ "$pastebin/view-paste" swap id>> "id" associate link>string ]
-        [ date>> ] tri
-        f swap <entry>
+: pastebin-feed-entries ( seq -- entries )
+    <reversed> 20 short head [
+        entry new
+            swap
+            [ summary>> >>title ]
+            [ date>> >>pub-date ]
+            [ entity-link >>link ]
+            tri
     ] map ;
 
 : pastebin-feed ( -- feed )
     feed new
         "Factor Pastebin" >>title
-        "http://paste.factorcode.org" >>link
-        pastebin-feed-entries >>entries ;
+        pastebin-link >>link
+        pastes pastebin-feed-entries >>entries ;
 
-: <feed-action> ( -- action )
-    <action>
-        [
-            "text/xml" <content>
-            [ pastebin-feed feed>xml write-xml ] >>body
-        ] >>display ;
+: <pastebin-feed-action> ( -- action )
+    <feed-action> [ pastebin-feed ] >>feed ;
 
-:: <view-paste-action> ( form ctor -- action )
-    <action>
-        { { "id" [ v-number ] } } >>get-params
+! ! !
+! PASTES
+! ! !
 
-        [ "id" get ctor call select-tuple fetch-annotations from-tuple ] >>init
+: <paste-action> ( -- action )
+    <page-action>
+        [
+            validate-integer-id
+            "id" value paste from-tuple
+
+            "id" value
+            "new-annotation" [
+                "id" set-value
+                mode-names "modes" set-value
+                "factor" "mode" set-value
+            ] nest-values
+        ] >>init
 
-        [ form view-form ] >>display ;
+        "paste" pastebin-template >>template ;
 
-:: <delete-paste-action> ( ctor next -- action )
-    <action>
-        { { "id" [ v-number ] } } >>post-params
+: paste-feed-entries ( paste -- entries )
+    fetch-annotations annotations>> pastebin-feed-entries ;
 
+: paste-feed ( paste -- feed )
+    feed new
+        swap
+        [ "Paste #" swap id>> number>string append >>title ]
+        [ entity-link >>link ]
+        [ paste-feed-entries >>entries ]
+        tri ;
+
+: <paste-feed-action> ( -- action )
+    <feed-action>
+        [ validate-integer-id ] >>init
+        [ "id" value paste annotations>> paste-feed ] >>feed ;
+
+: validate-entity ( -- )
+    {
+        { "summary" [ v-one-line ] }
+        { "author" [ v-one-line ] }
+        { "mode" [ v-mode ] }
+        { "contents" [ v-required ] }
+        { "captcha" [ v-captcha ] }
+    } validate-params ;
+
+: deposit-entity-slots ( tuple -- )
+    now >>date
+    { "summary" "author" "mode" "contents" } deposit-slots ;
+
+: <new-paste-action> ( -- action )
+    <page-action>
         [
-            "id" get ctor call delete-tuples
+            "factor" "mode" set-value
+            mode-names "modes" set-value
+        ] >>init
 
-            "id" get f <annotation> delete-tuples
+        "new-paste" pastebin-template >>template
 
-            next f <permanent-redirect>
+        [
+            validate-entity
+
+            f <paste>
+            [ deposit-entity-slots ]
+            [ insert-tuple ]
+            [ id>> "$pastebin/paste" <id-redirect> ]
+            tri
         ] >>submit ;
 
-:: <delete-annotation-action> ( ctor next -- action )
+: <delete-paste-action> ( -- action )
     <action>
-        { { "aid" [ v-number ] } } >>post-params
+        [ validate-integer-id ] >>validate
 
         [
-            f "aid" get ctor call select-tuple
-            [ delete-tuples ] [ id>> next <id-redirect> ] bi
+            "id" value <paste> delete-tuples
+            "id" value f <annotation> delete-tuples
+            "$pastebin/list" f <permanent-redirect>
         ] >>submit ;
 
-:: <new-paste-action> ( form ctor next -- action )
-    <action>
-        [
-            f ctor call from-tuple
-
-            form set-defaults
-        ] >>init
+! ! !
+! ANNOTATIONS
+! ! !
 
-        [ form edit-form ] >>display
+: <new-annotation-action> ( -- action )
+    <page-action>
+        [
+            { { "id" [ v-integer ] } } validate-params
+            "id" value "$pastebin/paste" <id-redirect>
+        ] >>display
 
         [
-            f ctor call from-tuple
+            { { "id" [ v-integer ] } } validate-params
+            validate-entity
+        ] >>validate
 
-            form validate-form
+        [
+            "id" value f <annotation>
+            [ deposit-entity-slots ]
+            [ insert-tuple ]
+            [
+                ! Add anchor here
+                parent>> "$pastebin/paste" <id-redirect>
+            ]
+            tri
+        ] >>submit ;
 
-            values-tuple insert-tuple
+: <delete-annotation-action> ( -- action )
+    <action>
+        [ { { "id" [ v-number ] } } validate-params ] >>validate
 
-            "id" value next <id-redirect>
+        [
+            f "id" value <annotation> select-tuple
+            [ delete-tuples ]
+            [ parent>> "$pastebin/paste" <id-redirect> ]
+            bi
         ] >>submit ;
 
 TUPLE: pastebin < dispatcher ;
@@ -242,17 +233,17 @@ can-delete-pastes? define-capability
 
 : <pastebin> ( -- responder )
     pastebin new-dispatcher
-        <paste-list-action> "list" add-main-responder
-        <feed-action> "feed.xml" add-responder
-        <paste-form> [ <paste> ] <view-paste-action> "view-paste" add-responder
-        [ <paste> ] "$pastebin/list" <delete-paste-action> { can-delete-pastes? } <protected> "delete-paste" add-responder
-        [ <annotation> ] "$pastebin/view-paste" <delete-annotation-action> { can-delete-pastes? } <protected> "delete-annotation" add-responder
-        <paste-form> [ <paste> ]    <view-paste-action>     "$pastebin/view-paste"   add-responder
-        <new-paste-form> [ <paste> now >>date ] "$pastebin/view-paste" <new-paste-action>     "new-paste"    add-responder
-        <new-annotation-form> [ <annotation> now >>date ] "$pastebin/view-paste" <annotate-action> "annotate" add-responder
+        <pastebin-action> "list" add-main-responder
+        <pastebin-feed-action> "list.atom" add-responder
+        <paste-action> "paste" add-responder
+        <paste-feed-action> "paste.atom" add-responder
+        <new-paste-action> "new-paste" add-responder
+        <delete-paste-action> { can-delete-pastes? } <protected> "delete-paste" add-responder
+        <new-annotation-action> "new-annotation" add-responder
+        <delete-annotation-action> { can-delete-pastes? } <protected> "delete-annotation" add-responder
     <boilerplate>
-        "pastebin" pastebin-template >>template ;
+        "pastebin-common" pastebin-template >>template ;
 
-: init-pastes-table paste ensure-table ;
+: init-pastes-table paste ensure-table ;
 
 : init-annotations-table annotation ensure-table ;
index 7ca4c95f8e518b257fe85e5da8f3cb10d2c03b66..9ec2cb7976dca830ec746ab3a059a5e25a5bc8f4 100644 (file)
@@ -2,29 +2,22 @@
 
 <t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
 
-       <t:atom t:title="Pastebin - Atom" t:href="$pastebin/feed.xml" />
-
-       <t:style t:include="resource:extra/webapps/pastebin/pastebin.css" />
-
-       <div class="navbar">
-                 <t:a t:href="$pastebin/list">Pastes</t:a>
-               | <t:a t:href="$pastebin/new-paste">New Paste</t:a>
-               | <t:a t:href="$pastebin/feed.xml">Atom Feed</t:a>
-
-               <t:if t:code="http.server.sessions:uid">
-
-                       <t:if t:code="http.server.auth.login:allow-edit-profile?">
-                               | <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
-                       </t:if>
-
-                       | <t:button t:action="$login/logout" t:flow="begin" class="link-button link">Logout</t:button>
-
-               </t:if>
-
-       </div>
-
-       <h1><t:write-title /></h1>
-
-        <t:call-next-template />
+       <t:atom t:title="Pastebin - Atom" t:href="$pastebin/list.atom" />
+
+       <t:title>Pastebin</t:title>
+
+       <table width="100%">
+               <th align="left" width="50%">Summary:</th>
+               <th align="left" width="100">Paste by:</th>
+               <th align="left" width="200">Date:</th>
+
+               <t:each-tuple t:values="pastes">
+                       <tr>
+                               <td><t:a t:href="$pastebin/paste" t:query="id"><t:label t:name="summary" /></t:a></td>
+                               <td><t:label t:name="author" /></td>
+                               <td><t:label t:name="date" /></td>
+                       </tr>
+               </t:each-tuple>
+       </table>
 
 </t:chloe>
index c79fe2efd1db52702ad64df1fdf43555445303b5..4711ca4716d5ea1db834e313a31897edc9066f6f 100644 (file)
@@ -4,11 +4,19 @@
 
        <t:title>Planet Factor Administration</t:title>
 
-       <t:summary t:component="blogroll" />
+       <ul>
+               <t:each-tuple t:values="blogroll">
+                       <li>
+                               <t:a t:href="$planet-factor/admin/edit-blog" t:query="id">
+                                       <t:label t:name="name" />
+                               </t:a>
+                       </li>
+               </t:each-tuple>
+       </ul>
 
        <p>
-               <t:a t:href="$planet-factor/admin/edit-blog">Add Blog</t:a>
-               | <t:a t:href="$planet-factor/admin/update">Update</t:a>
+               <t:a t:href="$planet-factor/admin/new-blog">Add Blog</t:a>
+               | <t:button t:action="$planet-factor/admin/update" class="link-button link">Update</t:button>
        </p>
 
 </t:chloe>
diff --git a/extra/webapps/planet/blog-admin-link.xml b/extra/webapps/planet/blog-admin-link.xml
deleted file mode 100644 (file)
index 8d6c890..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <t:a t:href="$planet-factor/admin/edit-blog" t:query="id"><t:view t:component="name" /></t:a>
-
-</t:chloe>
index ebfccc47de901e7584073f97c2ab309c04b012f9..fd9c659f59835226cfc5ed19ddfcf541d3d25d24 100644 (file)
 
                        <tr>
                                <th class="field-label">Blog name:</th>
-                               <td><t:edit t:component="name" /></td>
+                               <td><t:field t:name="name" /></td>
                        </tr>
 
                        <tr>
                                <th class="field-label">Home page:</th>
-                               <td><t:edit t:component="www-url" /></td>
+                               <td><t:field t:name="www-url" /></td>
                        </tr>
 
                        <tr>
                                <th class="field-label">Feed:</th>
-                               <td><t:edit t:component="feed-url" /></td>
+                               <td><t:field t:name="feed-url" /></td>
                        </tr>
 
                </table>
@@ -30,4 +30,5 @@
        </t:form>
 
        <t:button t:action="$planet-factor/admin/delete-blog" t:for="id" class="link-button link">Delete</t:button>
+
 </t:chloe>
diff --git a/extra/webapps/planet/mini-planet.xml b/extra/webapps/planet/mini-planet.xml
new file mode 100644 (file)
index 0000000..1338463
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:each-tuple t:values="postings">
+
+               <p class="news">
+                       <strong><t:view t:component="title" /></strong> <br/>
+                       <t:a value="link" t:session="none" class="more">Read More...</t:a>
+               </p>
+
+       </t:each-tuple>
+
+</t:chloe>
diff --git a/extra/webapps/planet/new-blog.xml b/extra/webapps/planet/new-blog.xml
new file mode 100644 (file)
index 0000000..4a9638d
--- /dev/null
@@ -0,0 +1,32 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:title>Edit Blog</t:title>
+
+       <t:form t:action="$planet-factor/admin/new-blog">
+
+               <table>
+
+                       <tr>
+                               <th class="field-label">Blog name:</th>
+                               <td><t:field t:name="name" /></td>
+                       </tr>
+
+                       <tr>
+                               <th class="field-label">Home page:</th>
+                               <td><t:field t:name="www-url" /></td>
+                       </tr>
+
+                       <tr>
+                               <th class="field-label">Feed:</th>
+                               <td><t:field t:name="feed-url" /></td>
+                       </tr>
+
+               </table>
+
+               <input type="SUBMIT" value="Done" />
+
+       </t:form>
+
+</t:chloe>
diff --git a/extra/webapps/planet/planet-common.xml b/extra/webapps/planet/planet-common.xml
new file mode 100644 (file)
index 0000000..29609e1
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:style t:include="resource:extra/webapps/planet/planet.css" />
+
+       <div class="navbar">
+                 <t:a t:href="$planet-factor/list">Front Page</t:a>
+               | <t:a t:href="$planet-factor/feed.xml">Atom Feed</t:a>
+               | <t:a t:href="$planet-factor/admin">Admin</t:a>
+
+               <t:if t:code="http.server.sessions:uid">
+                       <t:if t:code="http.server.auth.login:allow-edit-profile?">
+                               | <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
+                       </t:if>
+       
+                       | <t:button t:action="$login/logout" t:flow="begin" class="link-button link">Logout</t:button>
+               </t:if>
+       </div>
+
+       <h1><t:write-title /></h1>
+
+        <t:call-next-template />
+
+</t:chloe>
index c8aeab35a8f8b865b9a7165ec5653ba26b951229..414a59f3b2a1aa97b0c78a04f0f69cdd20b3edf3 100755 (executable)
@@ -1,22 +1,18 @@
 ! Copyright (C) 2008 Slava Pestov.
 ! See http://factorcode.org/license.txt for BSD license.
-USING: kernel accessors sequences sorting locals math math.order
+USING: kernel accessors sequences sorting math math.order
 calendar alarms logging concurrency.combinators namespaces
-sequences.lib db.types db.tuples db fry
+sequences.lib db.types db.tuples db fry locals hashtables
+html.components html.templates.chloe
 rss xml.writer
+validators
 http.server
-http.server.crud
-http.server.forms
 http.server.actions
 http.server.boilerplate
-http.server.templating.chloe
-http.server.components
 http.server.auth.login
 http.server.auth ;
 IN: webapps.planet
 
-TUPLE: planet-factor < dispatcher postings ;
-
 : planet-template ( name -- template )
     "resource:extra/webapps/planet/" swap ".xml" 3append <chloe> ;
 
@@ -34,92 +30,63 @@ blog "BLOGS"
     { "feed-url" "FEEDURL" { VARCHAR 256 } +not-null+ }
 } define-persistent
 
+! TUPLE: posting < entry id ;
+TUPLE: posting id title link description pub-date ;
+
+posting "POSTINGS"
+{
+    { "id" "ID" INTEGER +db-assigned-id+ }
+    { "title" "TITLE" { VARCHAR 256 } +not-null+ }
+    { "link" "LINK" { VARCHAR 256 } +not-null+ }
+    { "description" "DESCRIPTION" TEXT +not-null+ }
+    { "pub-date" "DATE" TIMESTAMP +not-null+ }
+} define-persistent
+
 : init-blog-table blog ensure-table ;
 
+: init-postings-table posting ensure-table ;
+
 : <blog> ( id -- todo )
     blog new
         swap >>id ;
 
 : blogroll ( -- seq )
-    f <blog> select-tuples [ [ name>> ] compare ] sort ;
-
-: <entry-form> ( -- form )
-    "entry" <form>
-        "entry" planet-template >>view-template
-        "entry-summary" planet-template >>summary-template
-        "title" <string> add-field
-        "description" <html-text> add-field
-        "pub-date" <date> add-field ;
-
-: <blog-form> ( -- form )
-    "blog" <form>
-        "edit-blog" planet-template >>edit-template
-        "blog-admin-link" planet-template >>summary-template
-        "id" <integer>
-            hidden >>renderer
-            add-field
-        "name" <string>
-            t >>required
-            add-field
-        "www-url" <url>
-            t >>required
-            add-field
-        "feed-url" <url>
-            t >>required
-            add-field ;
-
-: <planet-factor-form> ( -- form )
-    "planet-factor" <form>
-        "postings" planet-template >>view-template
-        "postings-summary" planet-template >>summary-template
-        "postings" <entry-form> +plain+ <list> add-field
-        "blogroll" "blog" <link> +unordered+ <list> add-field ;
-
-: <admin-form> ( -- form )
-    "admin" <form>
-        "admin" planet-template >>view-template
-        "blogroll" <blog-form> +unordered+ <list> add-field ;
-
-:: <edit-blogroll-action> ( planet -- action )
-    [let | form [ <admin-form> ] |
-        <action>
-            [
-                blank-values
-
-                blogroll "blogroll" set-value
-
-                form view-form
-            ] >>display
-    ] ;
-
-:: <planet-action> ( planet -- action )
-    [let | form [ <planet-factor-form> ] |
-        <action>
-            [
-                blank-values
-
-                planet postings>> "postings" set-value
-                blogroll "blogroll" set-value
-
-                form view-form
-            ] >>display
-    ] ;
-
-:: planet-feed ( planet -- feed )
+    f <blog> select-tuples
+    [ [ name>> ] compare ] sort ;
+
+: postings ( -- seq )
+    posting new select-tuples
+    [ [ pub-date>> ] compare invert-comparison ] sort ;
+
+: <edit-blogroll-action> ( -- action )
+    <page-action>
+        [ blogroll "blogroll" set-value ] >>init
+        "admin" planet-template >>template ;
+
+: <planet-action> ( -- action )
+    <page-action>
+        [
+            blogroll "blogroll" set-value
+            postings "postings" set-value
+        ] >>init
+
+        "planet" planet-template >>template ;
+
+: planet-feed ( -- feed )
     feed new
         "Planet Factor" >>title
         "http://planet.factorcode.org" >>link
-        planet postings>> 16 short head >>entries ;
+        postings >>entries ;
 
-:: <feed-action> ( planet -- action )
-    <action>
-        [
-            "text/xml" <content>
-            [ planet planet-feed feed>xml write-xml ] >>body
-        ] >>display ;
+: <planet-feed-action> ( -- action )
+    <feed-action> [ planet-feed ] >>feed ;
 
-: <posting> ( name entry -- entry' )
-    clone [ ": " swap 3append ] change-title ;
+:: <posting> ( entry name -- entry' )
+    posting new
+        name ": " entry title>> 3append >>title
+        entry link>> >>link
+        entry description>> >>description
+        entry pub-date>> >>pub-date ;
 
 : fetch-feed ( url -- feed )
     download-feed entries>> ;
@@ -127,55 +94,106 @@ blog "BLOGS"
 \ fetch-feed DEBUG add-error-logging
 
 : fetch-blogroll ( blogroll -- entries )
-    dup
-    [ feed-url>> fetch-feed ] parallel-map
-    [ >r name>> r> [ <posting> ] with map ] 2map concat ;
+    [ [ feed-url>> fetch-feed ] parallel-map ] [ [ name>> ] map ] bi
+    [ '[ , <posting> ] map ] 2map concat ;
 
 : sort-entries ( entries -- entries' )
-    [ [ pub-date>> ] compare ] sort <reversed> ;
+    [ [ pub-date>> ] compare invert-comparison ] sort ;
+
+: update-cached-postings ( -- )
+    blogroll fetch-blogroll sort-entries 8 short head [
+        posting new delete-tuples
+        [ insert-tuple ] each
+    ] with-transaction ;
 
-: update-cached-postings ( planet -- )
-    "webapps.planet" [
-        blogroll fetch-blogroll sort-entries 8 short head
-        >>postings drop
-    ] with-logging ;
+: <update-action> ( -- action )
+    <action>
+        [
+            update-cached-postings
+            "" f <permanent-redirect>
+        ] >>submit ;
 
-:: <update-action> ( planet -- action )
+: <delete-blog-action> ( -- action )
     <action>
+        [ validate-integer-id ] >>validate
+
         [
-            planet update-cached-postings
-            "" f <temporary-redirect>
-        ] >>display ;
+            "id" value <blog> delete-tuples
+            "$planet-factor/admin" f <standard-redirect>
+        ] >>submit ;
+
+: validate-blog ( -- )
+    {
+        { "name" [ v-one-line ] }
+        { "www-url" [ v-url ] }
+        { "feed-url" [ v-url ] }
+    } validate-params ;
 
-:: <planet-factor-admin> ( planet-factor -- responder )
-    [let | blog-form [ <blog-form> ]
-           blog-ctor [ [ <blog> ] ] |
-        <dispatcher>
-            planet-factor <edit-blogroll-action> >>default
+: <id-redirect> ( id next -- response )
+    swap "id" associate <standard-redirect> ;
 
-            planet-factor <update-action> "update" add-responder
+: deposit-blog-slots ( blog -- )
+    { "name" "www-url" "feed-url" } deposit-slots ;
 
-            ! Administrative CRUD
-                      blog-ctor "$planet-factor/admin"          <delete-action> "delete-blog" add-responder
-            blog-form blog-ctor "$planet-factor/admin" <edit-action>   "edit-blog"   add-responder
-    ] ;
+: <new-blog-action> ( -- action )
+    <page-action>
+        "new-blog" planet-template >>template
+
+        [ validate-blog ] >>validate
+
+        [
+            f <blog>
+            [ deposit-blog-slots ]
+            [ insert-tuple ]
+            [ id>> "$planet-factor/admin/edit-blog" <id-redirect> ]
+            tri
+        ] >>submit ;
+    
+: <edit-blog-action> ( -- action )
+    <page-action>
+        [
+            validate-integer-id
+            "id" value <blog> select-tuple from-tuple
+        ] >>init
+
+        "edit-blog" planet-template >>template
+
+        [
+            validate-integer-id
+            validate-blog
+        ] >>validate
+
+        [
+            f <blog>
+            [ deposit-blog-slots ]
+            [ update-tuple ]
+            [ id>> "$planet-factor/admin" <id-redirect> ]
+            tri
+        ] >>submit ;
+
+TUPLE: planet-factor-admin < dispatcher ;
+
+: <planet-factor-admin> ( -- responder )
+    planet-factor-admin new-dispatcher
+        <edit-blogroll-action> "blogroll" add-main-responder
+        <update-action> "update" add-responder
+        <new-blog-action> "new-blog" add-responder
+        <edit-blog-action> "edit-blog" add-responder
+        <delete-blog-action> "delete-blog" add-responder ;
 
 SYMBOL: can-administer-planet-factor?
 
 can-administer-planet-factor? define-capability
 
+TUPLE: planet-factor < dispatcher ;
+
 : <planet-factor> ( -- responder )
     planet-factor new-dispatcher
-        dup <planet-action> "list" add-main-responder
-        dup <feed-action> "feed.xml" add-responder
-        dup <planet-factor-admin> { can-administer-planet-factor? } <protected> "admin" add-responder
+        <planet-action> "list" add-main-responder
+        <feed-action> "feed.xml" add-responder
+        <planet-factor-admin> { can-administer-planet-factor? } <protected> "admin" add-responder
     <boilerplate>
-        "planet" planet-template >>template ;
+        "planet-common" planet-template >>template ;
 
-: start-update-task ( planet db seq -- )
-    '[
-        , , , [
-            dup filter-responder? [ responder>> ] when
-            update-cached-postings
-        ] with-db
-    ] 10 minutes every drop ;
+: start-update-task ( db params -- )
+    '[ , , [ update-cached-postings ] with-db ] 10 minutes every drop ;
index 29609e12ba6873829d1c980fe7c07399f2495bee..526a9b306b6af7d3e98ade9fb3957203c7f48b46 100644 (file)
@@ -2,24 +2,44 @@
 
 <t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
 
-       <t:style t:include="resource:extra/webapps/planet/planet.css" />
+       <t:title>Planet Factor</t:title>
 
-       <div class="navbar">
-                 <t:a t:href="$planet-factor/list">Front Page</t:a>
-               | <t:a t:href="$planet-factor/feed.xml">Atom Feed</t:a>
-               | <t:a t:href="$planet-factor/admin">Admin</t:a>
+       <table width="100%" cellpadding="10">
+               <tr>
+                       <td>
 
-               <t:if t:code="http.server.sessions:uid">
-                       <t:if t:code="http.server.auth.login:allow-edit-profile?">
-                               | <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
-                       </t:if>
-       
-                       | <t:button t:action="$login/logout" t:flow="begin" class="link-button link">Logout</t:button>
-               </t:if>
-       </div>
+                               <t:each-tuple t:values="postings">
 
-       <h1><t:write-title /></h1>
+                                       <h2 class="posting-title">
+                                               <t:a t:value="link" t:session="none"><t:label t:name="title" /></t:a>
+                                       </h2>
 
-        <t:call-next-template />
+                                       <p class="posting-body">
+                                               <t:html t:name="description" />
+                                       </p>
+
+                                       <p class="posting-date">
+                                               <t:a t:value="link" t:session="none"><t:label t:name="pub-date" /></t:a>
+                                       </p>
+
+                               </t:each-tuple>
+
+                       </td>
+
+                       <td valign="top" width="25%" class="infobox">
+
+                               <h2>Blogroll</h2>
+
+                               <ul>
+                                       <t:each t:values="blogroll">
+                                               <li>
+                                                       <t:link t:name="value"/>
+                                               </li>
+                                       </t:each>
+                               </ul>
+
+                       </td>
+               </tr>
+       </table>
 
 </t:chloe>
diff --git a/extra/webapps/planet/postings-summary.xml b/extra/webapps/planet/postings-summary.xml
deleted file mode 100644 (file)
index 765c3a8..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <t:summary t:component="postings" />
-
-</t:chloe>
diff --git a/extra/webapps/planet/postings.xml b/extra/webapps/planet/postings.xml
deleted file mode 100644 (file)
index c2c73d7..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <t:title>Planet Factor</t:title>
-
-       <table width="100%" cellpadding="10">
-                <tr>
-                        <td> <t:view t:component="postings" /> </td>
-  
-                        <td valign="top" width="25%" class="infobox">
-                                <h2>Blogroll</h2>
-  
-                                <t:summary t:component="blogroll" />
-                        </td>
-                </tr>
-        </table>
-
-</t:chloe>
index e1d4c40e236bb0a372f0c3aa98b8b4b601f4d7c1..0974c8ce1bb7bddeb97fa39b3d5eef14ed1bc75d 100644 (file)
@@ -6,9 +6,9 @@
 
        <t:form t:action="$todo-list/edit" t:for="id">
                <table>
-                       <tr><th class="field-label">Summary: </th><td><t:edit t:component="summary" /></td></tr>
-                       <tr><th class="field-label">Priority: </th><td><t:edit t:component="priority" /></td></tr>
-                       <tr><th class="field-label big-field-label">Description:</th><td><t:edit t:component="description" /></td></tr>
+                       <tr><th class="field-label">Summary: </th><td><t:field t:name="summary" /></td></tr>
+                       <tr><th class="field-label">Priority: </th><td><t:field t:name="priority" /></td></tr>
+                       <tr><th class="field-label big-field-label">Description:</th><td><t:textarea t:name="description" t:rows="20" t:cols="60" /></td></tr>
                </table>
 
                <input type="SUBMIT" value="Done" />
index 66abeafc868b7a6c5058ce633285b6dc042b7a6d..845c38dbf7c9f6d354ca88f598c477f6d78a886b 100644 (file)
@@ -5,8 +5,33 @@
        <t:title>My Todo List</t:title>
 
        <table class="todo-list">
-               <tr><th>Summary</th><th>Priority</th><th>View</th><th>Edit</th></tr>
-               <t:summary t:component="list" />
+
+               <tr>
+                       <th>Summary</th>
+                       <th>Priority</th>
+                       <th>View</th>
+                       <th>Edit</th>
+               </tr>
+
+               <t:each-tuple t:values="items">
+
+                       <tr>
+                               <td>
+                                       <t:label t:name="summary" />
+                               </td>
+                               <td>
+                                       <t:label t:name="priority" />
+                               </td>
+                               <td>
+                                       <t:a t:href="$todo-list/view" t:query="id">View</t:a>
+                               </td>
+                               <td>
+                                       <t:a t:href="$todo-list/edit" t:query="id">Edit</t:a>
+                               </td>
+                       </tr>
+
+               </t:each-tuple>
+
        </table>
 
 </t:chloe>
diff --git a/extra/webapps/todo/todo-summary.xml b/extra/webapps/todo/todo-summary.xml
deleted file mode 100644 (file)
index 056c9ca..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <tr>
-               <td>
-                       <t:view t:component="summary" />
-               </td>
-               <td>
-                       <t:view t:component="priority" />
-               </td>
-               <td>
-                       <t:a t:href="$todo-list/view" t:query="id">View</t:a>
-               </td>
-               <td>
-                       <t:a t:href="$todo-list/edit" t:query="id">Edit</t:a>
-               </td>
-       </tr>
-
-</t:chloe>
index 8bfda1aad563f862c2b7787f00172748e92c6b26..e3b174eaea76afd66047288c43ca86b0109ca10e 100755 (executable)
@@ -1,14 +1,11 @@
 ! Copyright (c) 2008 Slava Pestov
 ! See http://factorcode.org/license.txt for BSD license.
-USING: accessors kernel locals sequences namespaces
-db db.types db.tuples
+USING: accessors kernel sequences namespaces
+db db.types db.tuples validators hashtables
+html.components
+html.templates.chloe
 http.server.sessions
-http.server.components
-http.server.components.farkup
-http.server.forms
-http.server.templating.chloe
 http.server.boilerplate
-http.server.crud
 http.server.auth
 http.server.actions
 http.server.db
@@ -37,44 +34,86 @@ todo "TODO"
 : todo-template ( name -- template )
     "resource:extra/webapps/todo/" swap ".xml" 3append <chloe> ;
 
-: <todo-form> ( -- form )
-    "todo" <form>
-        "view-todo" todo-template >>view-template
-        "edit-todo" todo-template >>edit-template
-        "todo-summary" todo-template >>summary-template
-        "id" <integer>
-            hidden >>renderer
-            add-field
-        "summary" <string>
-            t >>required
-            add-field
-        "priority" <integer>
-            t >>required
-            0 >>default
-            0 >>min-value
-            10 >>max-value
-            add-field
-        "description" <farkup>
-            add-field ;
-
-: <todo-list-form> ( -- form )
-    "todo-list" <form>
-        "todo-list" todo-template >>view-template
-        "list" <todo-form> +plain+ <list>
-        add-field ;
+: <view-action> ( -- action )
+    <page-action>
+        [
+            validate-integer-id
+            "id" value <todo> select-tuple from-tuple
+        ] >>init
+        
+        "view-todo" todo-template >>template ;
+
+: <id-redirect> ( id next -- response )
+    swap "id" associate <standard-redirect> ;
+
+: validate-todo ( -- )
+    {
+        { "summary" [ v-one-line ] }
+        { "priority" [ v-integer 0 v-min-value 10 v-max-value ] }
+        { "description" [ v-required ] }
+    } validate-params ;
+
+: <new-action> ( -- action )
+    <page-action>
+        [ 0 "priority" set-value ] >>init
+
+        "edit-todo" todo-template >>template
+
+        [ validate-todo ] >>validate
+
+        [
+            f <todo>
+                dup { "summary" "description" } deposit-slots
+            [ insert-tuple ]
+            [ id>> "$todo-list/view" <id-redirect> ]
+            bi
+        ] >>submit ;
+
+: <edit-action> ( -- action )
+    <page-action>
+        [
+            validate-integer-id
+            "id" value <todo> select-tuple from-tuple
+        ] >>init
+
+        "edit-todo" todo-template >>template
+
+        [
+            validate-integer-id
+            validate-todo
+        ] >>validate
+
+        [
+            f <todo>
+                dup { "id" "summary" "priority" "description" } deposit-slots
+            [ update-tuple ]
+            [ id>> "$todo-list/view" <id-redirect> ]
+            bi
+        ] >>submit ;
+
+: <delete-action> ( -- action )
+    <action>
+        [ validate-integer-id ] >>validate
+
+        [
+            "id" get <todo> delete-tuples
+            "$todo-list/list" f <standard-redirect>
+        ] >>submit ;
+
+: <list-action> ( -- action )
+    <page-action>
+        [ f <todo> select-tuples "items" set-value ] >>init
+        "todo-list" todo-template >>template ;
 
 TUPLE: todo-list < dispatcher ;
 
-:: <todo-list> ( -- responder )
-    [let | todo-form [ <todo-form> ]
-           list-form [ <todo-list-form> ]
-           ctor [ [ <todo> ] ] |
-        todo-list new-dispatcher
-            list-form ctor        <list-action>   "list"   add-main-responder
-            todo-form ctor        <view-action>   "view"   add-responder
-            todo-form ctor "$todo-list/view" <edit-action>   "edit"   add-responder
-                      ctor "$todo-list/list" <delete-action> "delete" add-responder
-        <boilerplate>
-            "todo" todo-template >>template
-        f <protected>
-    ] ;
+: <todo-list> ( -- responder )
+    todo-list new-dispatcher
+        <list-action>   "list"   add-main-responder
+        <view-action>   "view"   add-responder
+        <new-action>    "new"    add-responder
+        <edit-action>   "edit"   add-responder
+        <delete-action> "delete" add-responder
+    <boilerplate>
+        "todo" todo-template >>template
+    f <protected> ;
index 651e29d867279af213a8d410d8c65cdc26cfb823..39ab5cda8b9597e06099c73bf8bcf8b065a7420b 100644 (file)
@@ -12,7 +12,7 @@
                        | <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
                </t:if>
 
-               <t:button t:action="$login/logout" t:flow="begin" class="link-button link">Logout</t:button>
+               <t:button t:action="$login/logout" t:flow="begin" class="link-button link">Logout</t:button>
        </div>
 
        <h1><t:write-title /></h1>
index 8c90ba9056bc7473164494ac81060d5649e76e60..a443528bac146abad5c2803a9f27b28f140301c4 100644 (file)
@@ -5,12 +5,12 @@
        <t:title>View Item</t:title>
 
        <table>
-               <tr><th class="field-label">Summary:    </th><td><t:view t:component="summary"     /></td></tr>
-               <tr><th class="field-label">Priority:   </th><td><t:view t:component="priority"    /></td></tr>
+               <tr><th class="field-label">Summary: </th><td><t:label t:name="summary" /></td></tr>
+               <tr><th class="field-label">Priority: </th><td><t:label t:name="priority" /></td></tr>
        </table>
 
        <div class="description">
-               <t:view t:component="description" />
+               <t:farkup t:name="description" />
        </div>
 
        <t:a t:href="$todo-list/edit" t:query="id">Edit</t:a>
diff --git a/extra/webapps/user-admin/edit-user.xml b/extra/webapps/user-admin/edit-user.xml
new file mode 100644 (file)
index 0000000..3f9ac8d
--- /dev/null
@@ -0,0 +1,56 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:title>Edit User</t:title>
+
+       <t:form t:action="$user-admin/edit" t:for="username">
+
+       <table>
+       
+       <tr>
+               <th class="field-label">User name:</th>
+               <td><t:label t:name="username" /></td>
+       </tr>
+       
+       <tr>
+               <th class="field-label">Real name:</th>
+               <td><t:field t:name="realname" /></td>
+       </tr>
+       
+       <tr>
+               <th class="field-label">New password:</th>
+               <td><t:password t:name="new-password" /></td>
+       </tr>
+       
+       <tr>
+               <th class="field-label">Verify:</th>
+               <td><t:password t:name="verify-password" /></td>
+       </tr>
+       
+       <tr>
+               <th class="field-label">E-mail:</th>
+               <td><t:field t:name="email" /></td>
+       </tr>
+       
+       <tr>
+               <th class="field-label big-field-label">Capabilities:</th>
+               <td><t:choice t:name="capabilities" t:choices="all-capabilities" t:multiple="true" /></td>
+       </tr>
+       
+       <tr>
+               <th class="field-label">Profile:</th>
+               <td><t:inspector t:name="profile" /></td>
+       </tr>
+
+       </table>
+       
+       <p>
+               <button type="submit" class="link-button link">Update</button>
+               <t:validation-messages />
+       </p>
+
+       </t:form>
+
+       <t:button t:action="$user-admin/delete" t:for="username" class="link-button link">Delete</t:button>
+</t:chloe>
diff --git a/extra/webapps/user-admin/new-user.xml b/extra/webapps/user-admin/new-user.xml
new file mode 100644 (file)
index 0000000..881dca9
--- /dev/null
@@ -0,0 +1,49 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:title>New User</t:title>
+
+       <t:form t:action="$user-admin/new">
+
+       <table>
+       
+       <tr>
+               <th class="field-label">User name:</th>
+               <td><t:field t:name="username" /></td>
+       </tr>
+       
+       <tr>
+               <th class="field-label">Real name:</th>
+               <td><t:field t:name="realname" /></td>
+       </tr>
+       
+       <tr>
+               <th class="field-label">New password:</th>
+               <td><t:password t:name="new-password" /></td>
+       </tr>
+       
+       <tr>
+               <th class="field-label">Verify:</th>
+               <td><t:password t:name="verify-password" /></td>
+       </tr>
+       
+       <tr>
+               <th class="field-label">E-mail:</th>
+               <td><t:field t:name="email" /></td>
+       </tr>
+       
+       <tr>
+               <th class="field-label big-field-label">Capabilities:</th>
+               <td><t:choice t:name="capabilities" t:choices="all-capabilities" t:multiple="true" /></td>
+       </tr>
+
+       </table>
+       
+       <p>
+               <button type="submit" class="link-button link">Create</button>
+               <t:validation-messages />
+       </p>
+
+       </t:form>
+</t:chloe>
diff --git a/extra/webapps/user-admin/user-admin.factor b/extra/webapps/user-admin/user-admin.factor
new file mode 100644 (file)
index 0000000..cdaf3f5
--- /dev/null
@@ -0,0 +1,156 @@
+! Copyright (C) 2008 Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+USING: kernel sequences accessors namespaces combinators words
+assocs db.tuples arrays splitting strings validators
+html.elements
+html.components
+html.templates.chloe
+http.server.boilerplate
+http.server.auth.providers
+http.server.auth.providers.db
+http.server.auth.login
+http.server.auth
+http.server.sessions
+http.server.actions
+http.server ;
+IN: webapps.user-admin
+
+: admin-template ( name -- template )
+    "resource:extra/webapps/user-admin/" swap ".xml" 3append <chloe> ;
+
+: words>strings ( seq -- seq' )
+    [ [ word-vocabulary ] [ drop ":" ] [ word-name ] tri 3append ] map ;
+
+: strings>words ( seq -- seq' )
+    [ ":" split1 swap lookup ] map ;
+
+: <user-list-action> ( -- action )
+    <page-action>
+        [ f <user> select-tuples "users" set-value ] >>init
+        "user-list" admin-template >>template ;
+
+: <new-user-action> ( -- action )
+    <page-action>
+        [
+            "username" param <user> from-tuple
+            capabilities get words>strings "all-capabilities" set-value
+        ] >>init
+
+        "new-user" admin-template >>template
+
+        [
+            capabilities get words>strings "all-capabilities" set-value
+
+            {
+                { "username" [ v-username ] }
+                { "realname" [ v-one-line ] }
+                { "new-password" [ v-password ] }
+                { "verify-password" [ v-password ] }
+                { "email" [ [ v-email ] v-optional ] }
+                { "capabilities" [ ] }
+            } validate-params
+
+            same-password-twice
+
+            user new "username" value >>username select-tuple
+            [ user-exists ] when
+        ] >>validate
+
+        [
+            "username" value <user>
+                "realname" value >>realname
+                "email" value >>email
+                "new-password" value >>encoded-password
+                H{ } clone >>profile
+
+            insert-tuple
+
+            "$user-admin" f <standard-redirect>
+        ] >>submit ;
+
+: validate-username ( -- )
+    { { "username" [ v-username ] } } validate-params ;
+
+: <edit-user-action> ( -- action )
+    <page-action>
+        [
+            validate-username
+
+            "username" value <user> select-tuple
+            [ from-tuple ] [ capabilities>> words>strings "capabilities" set-value ] bi
+
+            capabilities get words>strings "all-capabilities" set-value
+        ] >>init
+
+        "edit-user" admin-template >>template
+
+        [
+            capabilities get words>strings "all-capabilities" set-value
+
+            {
+                { "username" [ v-username ] }
+                { "realname" [ v-one-line ] }
+                { "new-password" [ [ v-password ] v-optional ] }
+                { "verify-password" [ [ v-password ] v-optional ] }
+                { "email" [ [ v-email ] v-optional ] }
+                { "capabilities" [ ] }
+            } validate-params
+
+            "new-password" "verify-password"
+            [ value empty? not ] either? [
+                same-password-twice
+            ] when
+        ] >>validate
+
+        [
+            "username" value <user> select-tuple
+                "realname" value >>realname
+                "email" value >>email
+
+            "new-password" value empty? [
+                "new-password" value >>encoded-password
+            ] unless
+
+            "capabilities" value {
+                { [ dup string? ] [ 1array ] }
+                { [ dup array? ] [ ] }
+            } cond strings>words >>capabilities
+
+            update-tuple
+
+            "$user-admin" f <standard-redirect>
+        ] >>submit ;
+
+: <delete-user-action> ( -- action )
+    <action>
+        [
+            validate-username
+
+            [ <user> select-tuple 1 >>deleted update-tuple ]
+            [ logout-all-sessions ]
+            bi
+
+            "$user-admin" f <standard-redirect>
+        ] >>submit ;
+
+TUPLE: user-admin < dispatcher ;
+
+SYMBOL: can-administer-users?
+
+can-administer-users? define-capability
+
+: <user-admin> ( -- responder )
+    user-admin new-dispatcher
+        <user-list-action> "list" add-main-responder
+        <new-user-action> "new" add-responder
+        <edit-user-action> "edit" add-responder
+        <delete-user-action> "delete" add-responder
+    <boilerplate>
+        "user-admin" admin-template >>template
+    { can-administer-users? } <protected> ;
+
+: make-admin ( username -- )
+    <user>
+    select-tuple
+    [ can-administer-users? suffix ] change-capabilities
+    update-tuple ;
diff --git a/extra/webapps/user-admin/user-admin.xml b/extra/webapps/user-admin/user-admin.xml
new file mode 100644 (file)
index 0000000..0581756
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <div class="navbar">
+                 <t:a t:href="$user-admin">List Users</t:a>
+               | <t:a t:href="$user-admin/new">Add User</t:a>
+
+               <t:if t:code="http.server.auth.login:allow-edit-profile?">
+                       | <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
+               </t:if>
+
+               | <t:button t:action="$login/logout" t:flow="begin" class="link-button link">Logout</t:button>
+       </div>
+
+       <h1><t:write-title /></h1>
+
+       <t:call-next-template />
+
+</t:chloe>
diff --git a/extra/webapps/user-admin/user-list.xml b/extra/webapps/user-admin/user-list.xml
new file mode 100644 (file)
index 0000000..020d053
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:title>Users</t:title>
+
+       <ul>
+
+               <t:each-tuple t:values="users">
+                       <li>
+                               <t:a t:href="$user-admin/edit" t:query="username">
+                                       <t:label t:name="username" />
+                               </t:a>
+                       </li>
+               </t:each-tuple>
+
+       </ul>
+
+</t:chloe>
diff --git a/extra/webapps/wiki/articles.xml b/extra/webapps/wiki/articles.xml
new file mode 100644 (file)
index 0000000..a552c26
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:title>All Articles</t:title>
+
+       <ul>
+               <t:each-tuple t:values="articles">
+                       <li>
+                               <t:a t:href="view" t:query="title"><t:label t:name="title"/></t:a>
+                       </li>
+               </t:each-tuple>
+       </ul>
+
+</t:chloe>
diff --git a/extra/webapps/wiki/changes.xml b/extra/webapps/wiki/changes.xml
new file mode 100644 (file)
index 0000000..5efa0c0
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:title>Recent Changes</t:title>
+
+       <ul>
+               <t:each-tuple t:values="changes">
+                       <li>
+                               <t:a t:href="title" t:query="title"><t:label t:name="title" /></t:a>
+                               on
+                               <t:a t:href="revision" t:query="id"><t:label t:name="date" /></t:a>
+                               by
+                               <t:a t:href="user-edits" t:query="author"><t:label t:name="author" /></t:a>
+                       </li>
+               </t:each-tuple>
+       </ul>
+
+</t:chloe>
diff --git a/extra/webapps/wiki/diff.xml b/extra/webapps/wiki/diff.xml
new file mode 100644 (file)
index 0000000..0fb0d6b
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:bind-tuple t:name="old">
+               <t:title>Diff: <t:label t:name="title" /></t:title>
+       </t:bind-tuple>
+
+       <table>
+               <tr>
+                       <th class="field-label">Old revision:</th>
+                       <t:bind-tuple t:name="old">
+                               <td>Created on <t:label t:name="date" /> by <t:a t:href="user-edits" t:query="author"><t:label t:name="author" /></t:a>.</td>
+                       </t:bind-tuple>
+               </tr>
+               <tr>
+                       <th class="field-label">New revision:</th>
+                       <t:bind-tuple t:name="old">
+                               <td>Created on <t:label t:name="date" /> by <t:a t:href="user-edits" t:query="author"><t:label t:name="author" /></t:a>.</td>
+                       </t:bind-tuple>
+               </tr>
+       </table>
+
+       <t:comparison t:name="diff" />
+
+       <t:bind-tuple t:name="old">
+               <div class="navbar">
+                       <t:a t:href="$wiki/view" t:query="title">Latest</t:a>
+                       | <t:a t:href="$wiki/revisions" t:query="title">Revisions</t:a>
+                       | <t:a t:href="$wiki/edit" t:query="title">Edit</t:a>
+                       | <t:button t:action="$wiki/delete" t:for="title" class="link-button link">Delete</t:button>
+               </div>
+       </t:bind-tuple>
+
+</t:chloe>
diff --git a/extra/webapps/wiki/edit.xml b/extra/webapps/wiki/edit.xml
new file mode 100644 (file)
index 0000000..85c8490
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:title>Edit: <t:label t:name="title" /></t:title>
+
+       <t:form t:action="$wiki/edit" t:for="title">
+
+               <p>
+                       <t:textarea t:name="content" t:rows="30" t:cols="80" />
+               </p>
+
+               <p>
+                       <input type="submit" value="Save" />
+               </p>
+
+       </t:form>
+
+       <t:button t:action="$wiki/delete" t:for="title" class="link-button link">Delete</t:button>
+</t:chloe>
diff --git a/extra/webapps/wiki/revisions.xml b/extra/webapps/wiki/revisions.xml
new file mode 100644 (file)
index 0000000..4b7bdad
--- /dev/null
@@ -0,0 +1,48 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:title>Revisions of <t:label t:name="title" /></t:title>
+
+       <ul>
+               <t:each-tuple t:values="revisions">
+                       <li>
+                               <t:a t:href="revision" t:query="id"><t:label t:name="date" /></t:a>
+                               by
+                               <t:a t:href="user-edits" t:query="author"><t:label t:name="author" /></t:a>
+                       </li>
+               </t:each-tuple>
+       </ul>
+
+       <h2>View Differences</h2>
+
+       <form action="diff" method="get">
+               <table>
+                       <tr>
+                               <th class="field-label">Old revision:</th>
+                               
+                               <td>
+                                       <select name="old-id">
+                                               <t:each-tuple t:values="revisions">
+                                                       <option> <t:label t:name="id" /> </option>
+                                               </t:each-tuple>
+                                       </select>
+                               </td>
+                       </tr>
+                       <tr>
+                               <th class="field-label">New revision:</th>
+                               
+                               <td>
+                                       <select name="new-id">
+                                               <t:each-tuple t:values="revisions">
+                                                       <option> <t:label t:name="id" /> </option>
+                                               </t:each-tuple>
+                                       </select>
+                               </td>
+                       </tr>
+               </table>
+
+               <input type="submit" value="View" />
+       </form>
+
+</t:chloe>
diff --git a/extra/webapps/wiki/user-edits.xml b/extra/webapps/wiki/user-edits.xml
new file mode 100644 (file)
index 0000000..cf19a38
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:title>Edits by <t:label t:name="author" /></t:title>
+
+       <ul>
+               <t:each-tuple t:values="user-edits">
+                       <li>
+                               <t:a t:href="view" t:query="title"><t:label t:name="title" /></t:a>
+                               on
+                               <t:a t:href="revision" t:query="id"><t:label t:name="date" /></t:a>
+                       </li>
+               </t:each-tuple>
+       </ul>
+
+</t:chloe>
diff --git a/extra/webapps/wiki/view.xml b/extra/webapps/wiki/view.xml
new file mode 100644 (file)
index 0000000..56c8b37
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:title><t:label t:name="title" /></t:title>
+
+       <div class="description">
+               <t:farkup t:name="content" />
+       </div>
+
+       <div class="navbar">
+               <t:a t:href="$wiki/view" t:query="title">Latest</t:a>
+               | <t:a t:href="$wiki/revisions" t:query="title">Revisions</t:a>
+               | <t:a t:href="$wiki/edit" t:query="title">Edit</t:a>
+               | <t:button t:action="$wiki/delete" t:for="title" class="link-button link">Delete</t:button>
+               | This revision created on <t:label t:name="date" /> by <t:a t:href="user-edits" t:query="author"><t:label t:name="author" /></t:a>.
+       </div>
+
+</t:chloe>
diff --git a/extra/webapps/wiki/wiki-common.xml b/extra/webapps/wiki/wiki-common.xml
new file mode 100644 (file)
index 0000000..23e61e5
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:style t:include="resource:extra/webapps/wiki/wiki.css" />
+
+       <div class="navbar">
+
+               <t:a t:href="$wiki">Front Page</t:a>
+               | <t:a t:href="$wiki/articles">All Articles</t:a>
+               | <t:a t:href="$wiki/changes">Recent Changes</t:a>
+
+               <t:if t:code="http.server.sessions:uid">
+
+                       <t:if t:code="http.server.auth.login:allow-edit-profile?">
+                               | <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
+                       </t:if>
+
+                       | <t:button t:action="$login/logout" t:flow="begin" class="link-button link">Logout</t:button>
+
+               </t:if>
+
+       </div>
+
+       <h1><t:write-title /></h1>
+
+        <t:call-next-template />
+
+</t:chloe>
diff --git a/extra/webapps/wiki/wiki.css b/extra/webapps/wiki/wiki.css
new file mode 100644 (file)
index 0000000..e737cdd
--- /dev/null
@@ -0,0 +1,25 @@
+.comparison table, {
+    border-color: #666;
+    border-style: solid;
+}
+
+.comparison th {
+    border-width: 1px;
+    border-color: #666;
+    border-style: solid;
+}
+
+.comparison table {
+    border-width: 1px;
+    border-spacing: 0;
+    border-collapse: collapse;
+}
+
+
+.insert {
+    background-color: #9f9;
+}
+
+.delete {
+    background-color: #f99;
+}
diff --git a/extra/webapps/wiki/wiki.factor b/extra/webapps/wiki/wiki.factor
new file mode 100644 (file)
index 0000000..344a3d4
--- /dev/null
@@ -0,0 +1,202 @@
+! Copyright (C) 2008 Slava Pestov
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors kernel hashtables calendar
+namespaces splitting sequences sorting math.order
+html.components
+html.templates.chloe
+http.server
+http.server.actions
+http.server.auth
+http.server.auth.login
+http.server.boilerplate
+validators
+db.types db.tuples lcs farkup ;
+IN: webapps.wiki
+
+TUPLE: article title revision ;
+
+article "ARTICLES" {
+    { "title" "TITLE" { VARCHAR 256 } +not-null+ +user-assigned-id+ }
+    ! { "AUTHOR" INTEGER +not-null+ } ! uid
+    ! { "PROTECTED" BOOLEAN +not-null+ }
+    { "revision" "REVISION" INTEGER +not-null+ } ! revision id
+} define-persistent
+
+: <article> ( title -- article ) article new swap >>title ;
+
+: init-articles-table article ensure-table ;
+
+TUPLE: revision id title author date content ;
+
+revision "REVISIONS" {
+    { "id" "ID" INTEGER +db-assigned-id+ }
+    { "title" "TITLE" { VARCHAR 256 } +not-null+ } ! article id
+    { "author" "AUTHOR" { VARCHAR 256 } +not-null+ } ! uid
+    { "date" "DATE" TIMESTAMP +not-null+ }
+    { "content" "CONTENT" TEXT +not-null+ }
+} define-persistent
+
+: <revision> ( id -- revision )
+    revision new swap >>id ;
+
+: init-revisions-table revision ensure-table ;
+
+: wiki-template ( name -- template )
+    "resource:extra/webapps/wiki/" swap ".xml" 3append <chloe> ;
+
+: <title-redirect> ( title next -- response )
+    swap "title" associate <standard-redirect> ;
+
+: validate-title ( -- )
+    { { "title" [ v-one-line ] } } validate-params ;
+
+: <main-article-action> ( -- action )
+    <action>
+        [ "Front Page" "$wiki/view" <title-redirect> ] >>display ;
+
+: <view-article-action> ( -- action )
+    <action>
+        "title" >>rest-param
+
+        [
+            validate-title
+            "view?title=" relative-link-prefix set
+        ] >>init
+
+        [
+            "title" value dup <article> select-tuple [
+                revision>> <revision> select-tuple from-tuple
+                "view" wiki-template <html-content>
+            ] [
+                "$wiki/edit" <title-redirect>
+            ] ?if
+        ] >>display ;
+
+: <view-revision-action> ( -- action )
+    <page-action>
+        [
+            { { "id" [ v-integer ] } } validate-params
+            "id" value <revision>
+            select-tuple from-tuple
+        ] >>init
+
+        "view" wiki-template >>template ;
+
+: add-revision ( revision -- )
+    [ insert-tuple ]
+    [
+        dup title>> <article> select-tuple [
+            swap id>> >>revision update-tuple
+        ] [
+            [ title>> ] [ id>> ] bi article boa insert-tuple
+        ] if*
+    ] bi ;
+
+: <edit-article-action> ( -- action )
+    <page-action>
+        [
+            validate-title
+            "title" value <article> select-tuple [
+                revision>> <revision> select-tuple from-tuple
+            ] when*
+        ] >>init
+
+        "edit" wiki-template >>template
+        
+        [
+            validate-title
+            { { "content" [ v-required ] } } validate-params
+
+            f <revision>
+                "title" value >>title
+                now >>date
+                logged-in-user get username>> >>author
+                "content" value >>content
+            [ add-revision ]
+            [ title>> "$wiki/view" <title-redirect> ] bi
+        ] >>submit ;
+
+: <list-revisions-action> ( -- action )
+    <page-action>
+        [
+            validate-title
+            f <revision> "title" value >>title select-tuples
+            [ [ date>> ] compare invert-comparison ] sort
+            "revisions" set-value
+        ] >>init
+
+        "revisions" wiki-template >>template ;
+
+: <list-changes-action> ( -- action )
+    <page-action>
+        [
+            f <revision> select-tuples
+            [ [ date>> ] compare invert-comparison ] sort
+            "changes" set-value
+        ] >>init
+
+        "changes" wiki-template >>template ;
+
+: <delete-action> ( -- action )
+    <action>
+        [ validate-title ] >>validate
+
+        [
+            "title" value <article> delete-tuples
+            f <revision> "title" value >>title delete-tuples
+            "" f <standard-redirect>
+        ] >>submit ;
+
+: <diff-action> ( -- action )
+    <page-action>
+        [
+            {
+                { "old-id" [ v-integer ] }
+                { "new-id" [ v-integer ] }
+            } validate-params
+
+            "old-id" "new-id"
+            [ value <revision> select-tuple ] bi@
+            [ [ "old" set-value ] [ "new" set-value ] bi* ]
+            [ [ content>> string-lines ] bi@ diff "diff" set-value ]
+            2bi
+        ] >>init
+
+        "diff" wiki-template >>template ;
+
+: <list-articles-action> ( -- action )
+    <page-action>
+        [
+            f <article> select-tuples
+            [ [ title>> ] compare ] sort
+            "articles" set-value
+        ] >>init
+
+        "articles" wiki-template >>template ;
+
+: <user-edits-action> ( -- action )
+    <page-action>
+        [
+            { { "author" [ v-username ] } } validate-params
+            f <revision> "author" value >>author
+            select-tuples "user-edits" set-value
+        ] >>init
+
+        "user-edits" wiki-template >>template ;
+
+TUPLE: wiki < dispatcher ;
+
+: <wiki> ( -- dispatcher )
+    wiki new-dispatcher
+        <main-article-action> "" add-responder
+        <view-article-action> "view" add-responder
+        <view-revision-action> "revision" add-responder
+        <list-revisions-action> "revisions" add-responder
+        <user-edits-action> "user-edits" add-responder
+        <diff-action> "diff" add-responder
+        <list-articles-action> "articles" add-responder
+        <list-changes-action> "changes" add-responder
+        <edit-article-action> { } <protected> "edit" add-responder
+        <delete-action> { } <protected> "delete" add-responder
+    <boilerplate>
+        "wiki-common" wiki-template >>template ;
index 277439c0cdb1bf3241b9693b569c387be9406fda..8c6025f726932a4f1ebce3da9ef839768dc291f0 100755 (executable)
@@ -1,6 +1,6 @@
 USING: xmode.loader xmode.utilities xmode.rules namespaces
 strings splitting assocs sequences kernel io.files xml memoize
-words globs combinators io.encodings.utf8 ;
+words globs combinators io.encodings.utf8 sorting ;
 IN: xmode.catalog
 
 TUPLE: mode file file-name-glob first-line-glob ;
@@ -23,17 +23,15 @@ TAGS>
         swap child-tags [ parse-mode-tag ] with each
     ] keep ;
 
-: load-catalog ( -- modes )
+MEMO: modes ( -- modes )
     "resource:extra/xmode/modes/catalog"
     file>xml parse-modes-tag ;
 
-: modes ( -- assoc )
-    \ modes get-global [
-        load-catalog dup \ modes set-global
-    ] unless* ;
+MEMO: mode-names ( -- modes )
+    modes keys natural-sort ;
 
 : reset-catalog ( -- )
-    f \ modes set-global ;
+    \ modes reset-memoized ;
 
 MEMO: (load-mode) ( name -- rule-sets )
     modes at [
index 3977f4277c37a7f0bd9b33881560c59b8c90e319..6eccddc94af049d2d5304f5e4d187cd83fec3851 100755 (executable)
@@ -1,12 +1,12 @@
-USING: xmode.tokens xmode.marker xmode.catalog kernel html
+USING: xmode.tokens xmode.marker xmode.catalog kernel
 html.elements io io.files sequences words io.encodings.utf8
-namespaces ;
+namespaces xml.entities ;
 IN: xmode.code2html
 
 : htmlize-tokens ( tokens -- )
     [
         dup token-str swap token-id [
-            <span word-name =class span> write </span>
+            <span word-name =class span> escape-string write </span>
         ] [
             write
         ] if*
@@ -21,7 +21,7 @@ IN: xmode.code2html
 : default-stylesheet ( -- )
     <style>
         "resource:extra/xmode/code2html/stylesheet.css"
-        utf8 file-contents write
+        utf8 file-contents escape-string write
     </style> ;
 
 : htmlize-stream ( path stream -- )
@@ -29,7 +29,7 @@ IN: xmode.code2html
     <html>
         <head>
             default-stylesheet
-            <title> dup write </title>
+            <title> dup escape-string write </title>
         </head>
         <body>
             <pre>
index 7b2bdd992a59d0a826b04150a16aad5321a2dc8d..2f56a5b8194a13d7a4f50213dea39fee08a18a6e 100755 (executable)
@@ -1,21 +1,16 @@
 ! Copyright (C) 2007, 2008 Slava Pestov.\r
 ! See http://factorcode.org/license.txt for BSD license.\r
-USING: io.files io.encodings.utf8 namespaces http.server\r
-http.server.static http xmode.code2html kernel html sequences\r
+USING: io io.files io.encodings.utf8 namespaces http.server\r
+http.server.static http xmode.code2html kernel sequences\r
 accessors fry ;\r
 IN: xmode.code2html.responder\r
 \r
 : <sources> ( root -- responder )\r
     [\r
         drop\r
-        "text/html" <content> swap\r
-        [ "last-modified" set-header ]\r
-        [\r
-            '[\r
-                ,\r
-                dup file-name swap utf8\r
-                <file-reader>\r
-                [ htmlize-stream ] with-html-stream\r
-            ] >>body\r
-        ] bi\r
+        dup '[\r
+            , utf8 [\r
+                , file-name input-stream get htmlize-stream\r
+            ] with-file-reader\r
+        ] <html-content>\r
     ] <file-responder> ;\r
index 25219d1569bc087b4db3303c96716f3d0571e263..df4063d149ac8361bd43b1e4332523b0a6740c30 100755 (executable)
@@ -109,9 +109,7 @@ CELL frame_executing(F_STACK_FRAME *frame)
 {
        F_COMPILED *compiled = frame_code(frame);
        CELL code_start = (CELL)(compiled + 1);
-       CELL literal_start = code_start
-               + compiled->code_length
-               + compiled->reloc_length;
+       CELL literal_start = code_start + compiled->code_length;
 
        return get(literal_start);
 }
index 141f4abbfe065a9942fec62c312d0b1ac15156e5..e0abdc5a61bbe3f7826099e303d5c6b2aea3a840 100755 (executable)
@@ -257,12 +257,13 @@ void iterate_code_heap(CODE_HEAP_ITERATOR iter)
 }
 
 /* Copy all literals referenced from a code block to newspace */
-void collect_literals_step(F_COMPILED *compiled, CELL code_start,
-       CELL reloc_start, CELL literals_start)
+void collect_literals_step(F_COMPILED *compiled, CELL code_start, CELL literals_start)
 {
        CELL scan;
        CELL literal_end = literals_start + compiled->literals_length;
 
+       copy_handle(&compiled->relocation);
+
        for(scan = literals_start; scan < literal_end; scan += CELLS)
                copy_handle((CELL*)scan);
 }
index 658dc990ae3be07fc97a9655ae626f00bd96c8a0..f93cba9c7aec3f6b8f2ce53ef3964ccd727960da 100644 (file)
@@ -17,9 +17,6 @@ typedef struct _F_BLOCK
 
        /* Used during compaction */
        struct _F_BLOCK *forwarding;
-
-       /* Alignment padding */
-       CELL padding[4];
 } F_BLOCK;
 
 typedef struct {
@@ -47,16 +44,14 @@ INLINE F_BLOCK *next_block(F_HEAP *heap, F_BLOCK *block)
 /* compiled code */
 F_HEAP code_heap;
 
-typedef void (*CODE_HEAP_ITERATOR)(F_COMPILED *compiled, CELL code_start,
-       CELL reloc_start, CELL literals_start);
+typedef void (*CODE_HEAP_ITERATOR)(F_COMPILED *compiled, CELL code_start, CELL literals_start);
 
 INLINE void iterate_code_heap_step(F_COMPILED *compiled, CODE_HEAP_ITERATOR iter)
 {
        CELL code_start = (CELL)(compiled + 1);
-       CELL reloc_start = code_start + compiled->code_length;
-       CELL literals_start = reloc_start + compiled->reloc_length;
+       CELL literals_start = code_start + compiled->code_length;
 
-       iter(compiled,code_start,reloc_start,literals_start);
+       iter(compiled,code_start,literals_start);
 }
 
 INLINE F_BLOCK *compiled_to_block(F_COMPILED *compiled)
index 92915e49d151a1c45ad39ab213d0f514988e7835..69ffdeb2aa2a1fdb45b883c3827c375871d8e8d9 100755 (executable)
@@ -139,13 +139,14 @@ void apply_relocation(CELL class, CELL offset, F_FIXNUM absolute_value)
 }
 
 /* Perform all fixups on a code block */
-void relocate_code_block(F_COMPILED *relocating, CELL code_start,
-       CELL reloc_start, CELL literals_start)
+void relocate_code_block(F_COMPILED *compiled, CELL code_start, CELL literals_start)
 {
-       if(reloc_start != literals_start)
+       if(compiled->relocation != F)
        {
-               F_REL *rel = (F_REL *)reloc_start;
-               F_REL *rel_end = (F_REL *)literals_start;
+               F_BYTE_ARRAY *relocation = untag_object(compiled->relocation);
+
+               F_REL *rel = (F_REL *)(relocation + 1);
+               F_REL *rel_end = (F_REL *)((char *)rel + byte_array_capacity(relocation));
 
                while(rel < rel_end)
                {
@@ -160,7 +161,7 @@ void relocate_code_block(F_COMPILED *relocating, CELL code_start,
                }
        }
 
-       flush_icache(code_start,reloc_start - code_start);
+       flush_icache(code_start,literals_start - code_start);
 }
 
 /* Fixup labels. This is done at compile time, not image load time */
@@ -249,34 +250,32 @@ F_COMPILED *add_compiled_block(
        CELL type,
        F_ARRAY *code,
        F_ARRAY *labels,
-       F_ARRAY *relocation,
+       CELL relocation,
        F_ARRAY *literals)
 {
        CELL code_format = compiled_code_format();
 
        CELL code_length = align8(array_capacity(code) * code_format);
-       CELL rel_length = array_capacity(relocation) * sizeof(unsigned int);
        CELL literals_length = array_capacity(literals) * CELLS;
 
+       REGISTER_ROOT(relocation);
        REGISTER_UNTAGGED(code);
        REGISTER_UNTAGGED(labels);
-       REGISTER_UNTAGGED(relocation);
        REGISTER_UNTAGGED(literals);
 
-       CELL here = allot_code_block(sizeof(F_COMPILED) + code_length
-               + rel_length + literals_length);
+       CELL here = allot_code_block(sizeof(F_COMPILED) + code_length + literals_length);
 
        UNREGISTER_UNTAGGED(literals);
-       UNREGISTER_UNTAGGED(relocation);
        UNREGISTER_UNTAGGED(labels);
        UNREGISTER_UNTAGGED(code);
+       UNREGISTER_ROOT(relocation);
 
        /* compiled header */
        F_COMPILED *header = (void *)here;
        header->type = type;
        header->code_length = code_length;
-       header->reloc_length = rel_length;
        header->literals_length = literals_length;
+       header->relocation = relocation;
 
        here += sizeof(F_COMPILED);
 
@@ -286,10 +285,6 @@ F_COMPILED *add_compiled_block(
        deposit_integers(here,code,code_format);
        here += code_length;
 
-       /* relation info */
-       deposit_integers(here,relocation,sizeof(unsigned int));
-       here += rel_length;
-
        /* literals */
        deposit_objects(here,literals);
        here += literals_length;
@@ -353,7 +348,7 @@ DEFINE_PRIMITIVE(modify_code_heap)
                        F_ARRAY *compiled_code = untag_array(data);
 
                        F_ARRAY *literals = untag_array(array_nth(compiled_code,0));
-                       F_ARRAY *relocation = untag_array(array_nth(compiled_code,1));
+                       CELL relocation = array_nth(compiled_code,1);
                        F_ARRAY *labels = untag_array(array_nth(compiled_code,2));
                        F_ARRAY *code = untag_array(array_nth(compiled_code,3));
 
index 4e65313d3beb5646bdc0e156789017b270968653..80605b1d28164d04a5393d9990d86225d0cae9a1 100755 (executable)
@@ -53,8 +53,7 @@ typedef struct {
        unsigned int offset;
 } F_REL;
 
-void relocate_code_block(F_COMPILED *relocating, CELL code_start,
-       CELL reloc_start, CELL literals_start);
+void relocate_code_block(F_COMPILED *relocating, CELL code_start, CELL literals_start);
 
 void default_word_code(F_WORD *word, bool relocate);
 
@@ -64,7 +63,7 @@ F_COMPILED *add_compiled_block(
        CELL type,
        F_ARRAY *code,
        F_ARRAY *labels,
-       F_ARRAY *rel,
+       CELL relocation,
        F_ARRAY *literals);
 
 CELL compiled_code_format(void);
index a52f2490e96978b81ec298835f098642a9872ced..54ad1168a08d25a3ad70d50558044b7f13659713 100755 (executable)
@@ -930,22 +930,22 @@ DEFINE_PRIMITIVE(gc_stats)
        for(i = 0; i < MAX_GEN_COUNT; i++)
        {
                F_GC_STATS *s = &gc_stats[i];
-               GROWABLE_ADD(stats,allot_cell(s->collections));
-               GROWABLE_ADD(stats,allot_cell(s->gc_time));
-               GROWABLE_ADD(stats,allot_cell(s->max_gc_time));
-               GROWABLE_ADD(stats,allot_cell(s->collections == 0 ? 0 : s->gc_time / s->collections));
-               GROWABLE_ADD(stats,allot_cell(s->object_count));
-               GROWABLE_ADD(stats,tag_bignum(long_long_to_bignum(s->bytes_copied)));
+               GROWABLE_ARRAY_ADD(stats,allot_cell(s->collections));
+               GROWABLE_ARRAY_ADD(stats,allot_cell(s->gc_time));
+               GROWABLE_ARRAY_ADD(stats,allot_cell(s->max_gc_time));
+               GROWABLE_ARRAY_ADD(stats,allot_cell(s->collections == 0 ? 0 : s->gc_time / s->collections));
+               GROWABLE_ARRAY_ADD(stats,allot_cell(s->object_count));
+               GROWABLE_ARRAY_ADD(stats,tag_bignum(long_long_to_bignum(s->bytes_copied)));
 
                total_gc_time += s->gc_time;
        }
 
-       GROWABLE_ADD(stats,allot_cell(total_gc_time));
-       GROWABLE_ADD(stats,tag_bignum(long_long_to_bignum(cards_scanned)));
-       GROWABLE_ADD(stats,tag_bignum(long_long_to_bignum(decks_scanned)));
-       GROWABLE_ADD(stats,allot_cell(code_heap_scans));
+       GROWABLE_ARRAY_ADD(stats,allot_cell(total_gc_time));
+       GROWABLE_ARRAY_ADD(stats,tag_bignum(long_long_to_bignum(cards_scanned)));
+       GROWABLE_ARRAY_ADD(stats,tag_bignum(long_long_to_bignum(decks_scanned)));
+       GROWABLE_ARRAY_ADD(stats,allot_cell(code_heap_scans));
 
-       GROWABLE_TRIM(stats);
+       GROWABLE_ARRAY_TRIM(stats);
        dpush(stats);
 }
 
@@ -986,13 +986,13 @@ CELL find_all_words(void)
        while((obj = next_object()) != F)
        {
                if(type_of(obj) == WORD_TYPE)
-                       GROWABLE_ADD(words,obj);
+                       GROWABLE_ARRAY_ADD(words,obj);
        }
 
        /* End heap scan */
        gc_off = false;
 
-       GROWABLE_TRIM(words);
+       GROWABLE_ARRAY_TRIM(words);
 
        return words;
 }
index b86ec808bc5ce1560326a2fd29a9637cf781f095..027842689562e2dc9ee8b1104743482d87cf5cd0 100755 (executable)
@@ -296,8 +296,7 @@ void find_data_references(CELL look_for_)
 
 CELL look_for;
 
-void find_code_references_step(F_COMPILED *compiled, CELL code_start,
-               CELL reloc_start, CELL literals_start)
+void find_code_references_step(F_COMPILED *compiled, CELL code_start, CELL literals_start)
 {
        CELL scan;
        CELL literal_end = literals_start + compiled->literals_length;
@@ -305,9 +304,7 @@ void find_code_references_step(F_COMPILED *compiled, CELL code_start,
        for(scan = literals_start; scan < literal_end; scan += CELLS)
        {
                CELL code_start = (CELL)(compiled + 1);
-               CELL literal_start = code_start
-                       + compiled->code_length
-                       + compiled->reloc_length;
+               CELL literal_start = code_start + compiled->code_length;
 
                CELL obj = get(literal_start);
 
index 653891fdfe8cda9863bb47b986345c3518740515..141594f01f44a5e00732a6922864d3f746a45747 100755 (executable)
@@ -288,18 +288,18 @@ void relocate_data()
        }
 }
 
-void fixup_code_block(F_COMPILED *relocating, CELL code_start,
-       CELL reloc_start, CELL literals_start)
+void fixup_code_block(F_COMPILED *compiled, CELL code_start, CELL literals_start)
 {
        /* relocate literal table data */
        CELL scan;
-       CELL literal_end = literals_start + relocating->literals_length;
+       CELL literal_end = literals_start + compiled->literals_length;
+
+       data_fixup(&compiled->relocation);
 
        for(scan = literals_start; scan < literal_end; scan += CELLS)
                data_fixup((CELL*)scan);
 
-       if(reloc_start != literals_start)
-               relocate_code_block(relocating,code_start,reloc_start,literals_start);
+       relocate_code_block(compiled,code_start,literals_start);
 }
 
 void relocate_code()
index 89af0a306cb842e3ee29eba2190d5316b962e3ea..1aee94357bc74d49706326821e7da5be2dfc1773 100755 (executable)
@@ -113,8 +113,8 @@ typedef struct
 {
        CELL type; /* this is WORD_TYPE or QUOTATION_TYPE */
        CELL code_length; /* # bytes */
-       CELL reloc_length; /* # bytes */
        CELL literals_length; /* # bytes */
+       CELL relocation; /* tagged pointer to byte-array or f */
 } F_COMPILED;
 
 /* Assembly code makes assumptions about the layout of this struct */
index 6363ce68a9224ac76fa598e3e5423b98f3bbc5de..1f63ea7ab18f42b6f8fce9b880b10eba2a2b87ba 100755 (executable)
@@ -73,14 +73,14 @@ DEFINE_PRIMITIVE(read_dir)
                while((file = readdir(dir)) != NULL)
                {
                        CELL pair = parse_dir_entry(file);
-                       GROWABLE_ADD(result,pair);
+                       GROWABLE_ARRAY_ADD(result,pair);
                }
 
                closedir(dir);
        }
 
        UNREGISTER_ROOT(result);
-       GROWABLE_TRIM(result);
+       GROWABLE_ARRAY_TRIM(result);
 
        dpush(result);
 }
@@ -104,12 +104,12 @@ DEFINE_PRIMITIVE(os_envs)
        while(*env)
        {
                CELL string = tag_object(from_char_string(*env));
-               GROWABLE_ADD(result,string);
+               GROWABLE_ARRAY_ADD(result,string);
                env++;
        }
 
        UNREGISTER_ROOT(result);
-       GROWABLE_TRIM(result);
+       GROWABLE_ARRAY_TRIM(result);
        dpush(result);
 }
 
index cc7b128941a764dc93adf30fb1f17fc027210a83..4f5778d0c4e0e4d782da482999902fb4b73aa04a 100755 (executable)
@@ -25,7 +25,7 @@ DEFINE_PRIMITIVE(os_envs)
                        break;
 
                CELL string = tag_object(from_u16_string(finger));
-               GROWABLE_ADD(result,string);
+               GROWABLE_ARRAY_ADD(result,string);
 
                finger = scan + 1;
        }
@@ -33,7 +33,7 @@ DEFINE_PRIMITIVE(os_envs)
        FreeEnvironmentStrings(env);
 
        UNREGISTER_ROOT(result);
-       GROWABLE_TRIM(result);
+       GROWABLE_ARRAY_TRIM(result);
        dpush(result);
 }
 
index 59c14d98f5a47f6c821921f819298f54a9525604..dc931d31c807e64785773255dd91beb522779cde 100755 (executable)
@@ -152,14 +152,14 @@ DEFINE_PRIMITIVE(read_dir)
                        CELL name = tag_object(from_u16_string(find_data.cFileName));
                        CELL dirp = tag_boolean(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
                        CELL pair = allot_array_2(name,dirp);
-                       GROWABLE_ADD(result,pair);
+                       GROWABLE_ARRAY_ADD(result,pair);
                }
                while (FindNextFile(dir, &find_data));
                FindClose(dir);
        }
 
        UNREGISTER_ROOT(result);
-       GROWABLE_TRIM(result);
+       GROWABLE_ARRAY_TRIM(result);
 
        dpush(result);
 }
index 08bb846c85053d2f72f0ca61f3e2a47140a23262..58a4aa035e8050ab35208977d22d48476abf3c69 100755 (executable)
@@ -11,11 +11,12 @@ F_COMPILED *compile_profiling_stub(F_WORD *word)
        CELL code = array_nth(quadruple,0);
        REGISTER_ROOT(code);
 
-       CELL rel_type = allot_cell(to_fixnum(array_nth(quadruple,2))
-               | (to_fixnum(array_nth(quadruple,1)) << 8));
-       CELL rel_offset = array_nth(quadruple,3) * compiled_code_format();
+       F_REL rel;
+       rel.type = to_fixnum(array_nth(quadruple,2)) | (to_fixnum(array_nth(quadruple,1)) << 8);
+       rel.offset = to_fixnum(array_nth(quadruple,3)) * compiled_code_format();
 
-       CELL relocation = allot_array_2(rel_type,rel_offset);
+       F_BYTE_ARRAY *relocation = allot_byte_array(sizeof(F_REL));
+       memcpy((void *)BREF(relocation,0),&rel,sizeof(F_REL));
 
        UNREGISTER_ROOT(code);
        UNREGISTER_ROOT(literals);
@@ -24,7 +25,7 @@ F_COMPILED *compile_profiling_stub(F_WORD *word)
                WORD_TYPE,
                untag_object(code),
                NULL, /* no labels */
-               untag_object(relocation),
+               tag_object(relocation),
                untag_object(literals));
 }
 
index c3b50dbd472818ca9c09b7659c4c6c06412c8eb8..e092aab4bf455458a27271d97ebde7adc4958bdd 100755 (executable)
@@ -60,14 +60,9 @@ F_REL rel_to_emit(CELL name, CELL code_format, CELL code_length,
 
 #define EMIT(name,rel_argument) { \
                bool rel_p; \
-               F_REL rel = rel_to_emit(name,code_format,code_count, \
-                       rel_argument,&rel_p); \
-               if(rel_p) \
-               { \
-                       GROWABLE_ADD(relocation,allot_cell(rel.type)); \
-                       GROWABLE_ADD(relocation,allot_cell(rel.offset)); \
-               } \
-               GROWABLE_APPEND(code,code_to_emit(name)); \
+               F_REL rel = rel_to_emit(name,code_format,code_count,rel_argument,&rel_p); \
+               if(rel_p) GROWABLE_BYTE_ARRAY_APPEND(relocation,&rel,sizeof(F_REL)); \
+               GROWABLE_ARRAY_APPEND(code,code_to_emit(name)); \
        }
 
 bool jit_stack_frame_p(F_ARRAY *array)
@@ -110,13 +105,13 @@ void jit_compile(CELL quot, bool relocate)
        GROWABLE_ARRAY(code);
        REGISTER_ROOT(code);
 
-       GROWABLE_ARRAY(relocation);
+       GROWABLE_BYTE_ARRAY(relocation);
        REGISTER_ROOT(relocation);
 
        GROWABLE_ARRAY(literals);
        REGISTER_ROOT(literals);
 
-       GROWABLE_ADD(literals,stack_traces_p() ? quot : F);
+       GROWABLE_ARRAY_ADD(literals,stack_traces_p() ? quot : F);
 
        bool stack_frame = jit_stack_frame_p(untag_object(array));
 
@@ -141,7 +136,7 @@ void jit_compile(CELL quot, bool relocate)
                        current stack frame. */
                        word = untag_object(obj);
 
-                       GROWABLE_ADD(literals,array_nth(untag_object(array),i));
+                       GROWABLE_ARRAY_ADD(literals,array_nth(untag_object(array),i));
 
                        if(i == length - 1)
                        {
@@ -157,7 +152,7 @@ void jit_compile(CELL quot, bool relocate)
                        break;
                case WRAPPER_TYPE:
                        wrapper = untag_object(obj);
-                       GROWABLE_ADD(literals,wrapper->object);
+                       GROWABLE_ARRAY_ADD(literals,wrapper->object);
                        EMIT(JIT_PUSH_LITERAL,literals_count - 1);
                        break;
                case FIXNUM_TYPE:
@@ -176,8 +171,8 @@ void jit_compile(CELL quot, bool relocate)
                                if(stack_frame)
                                        EMIT(JIT_EPILOG,0);
 
-                               GROWABLE_ADD(literals,array_nth(untag_object(array),i));
-                               GROWABLE_ADD(literals,array_nth(untag_object(array),i + 1));
+                               GROWABLE_ARRAY_ADD(literals,array_nth(untag_object(array),i));
+                               GROWABLE_ARRAY_ADD(literals,array_nth(untag_object(array),i + 1));
                                EMIT(JIT_IF_JUMP,literals_count - 2);
 
                                i += 2;
@@ -191,7 +186,7 @@ void jit_compile(CELL quot, bool relocate)
                                if(stack_frame)
                                        EMIT(JIT_EPILOG,0);
 
-                               GROWABLE_ADD(literals,array_nth(untag_object(array),i));
+                               GROWABLE_ARRAY_ADD(literals,array_nth(untag_object(array),i));
                                EMIT(JIT_DISPATCH,literals_count - 1);
 
                                i++;
@@ -200,7 +195,7 @@ void jit_compile(CELL quot, bool relocate)
                                break;
                        }
                default:
-                       GROWABLE_ADD(literals,obj);
+                       GROWABLE_ARRAY_ADD(literals,obj);
                        EMIT(JIT_PUSH_LITERAL,literals_count - 1);
                        break;
                }
@@ -214,15 +209,15 @@ void jit_compile(CELL quot, bool relocate)
                EMIT(JIT_RETURN,0);
        }
 
-       GROWABLE_TRIM(code);
-       GROWABLE_TRIM(relocation);
-       GROWABLE_TRIM(literals);
+       GROWABLE_ARRAY_TRIM(code);
+       GROWABLE_ARRAY_TRIM(literals);
+       GROWABLE_BYTE_ARRAY_TRIM(relocation);
 
        F_COMPILED *compiled = add_compiled_block(
                QUOTATION_TYPE,
                untag_object(code),
                NULL,
-               untag_object(relocation),
+               relocation,
                untag_object(literals));
 
        set_quot_xt(untag_object(quot),compiled);
index b4e5269f4e36e6d1661269168c141a29afcfec4c..adfdea41a5d8ed9b3e4f091a0514758b884684f5 100755 (executable)
@@ -197,7 +197,7 @@ DEFINE_PRIMITIVE(resize_array)
        dpush(tag_object(reallot_array(array,capacity,F)));
 }
 
-F_ARRAY *growable_add(F_ARRAY *result, CELL elt, CELL *result_count)
+F_ARRAY *growable_array_add(F_ARRAY *result, CELL elt, CELL *result_count)
 {
        REGISTER_ROOT(elt);
 
@@ -209,12 +209,12 @@ F_ARRAY *growable_add(F_ARRAY *result, CELL elt, CELL *result_count)
 
        UNREGISTER_ROOT(elt);
        set_array_nth(result,*result_count,elt);
-       *result_count = *result_count + 1;
+       (*result_count)++;
 
        return result;
 }
 
-F_ARRAY *growable_append(F_ARRAY *result, F_ARRAY *elts, CELL *result_count)
+F_ARRAY *growable_array_append(F_ARRAY *result, F_ARRAY *elts, CELL *result_count)
 {
        REGISTER_UNTAGGED(elts);
 
@@ -228,7 +228,7 @@ F_ARRAY *growable_append(F_ARRAY *result, F_ARRAY *elts, CELL *result_count)
 
        write_barrier((CELL)result);
 
-       memcpy((void*)AREF(result,*result_count),(void*)AREF(elts,0),elts_size * CELLS);
+       memcpy((void *)AREF(result,*result_count),(void *)AREF(elts,0),elts_size * CELLS);
 
        *result_count += elts_size;
 
@@ -283,6 +283,33 @@ DEFINE_PRIMITIVE(resize_byte_array)
        dpush(tag_object(reallot_byte_array(array,capacity)));
 }
 
+F_BYTE_ARRAY *growable_byte_array_add(F_BYTE_ARRAY *result, CELL elt, CELL *result_count)
+{
+       if(*result_count == byte_array_capacity(result))
+       {
+               result = reallot_byte_array(result,*result_count * 2);
+       }
+
+       bput(BREF(result,*result_count),elt);
+       *result_count++;
+
+       return result;
+}
+
+F_BYTE_ARRAY *growable_byte_array_append(F_BYTE_ARRAY *result, void *elts, CELL len, CELL *result_count)
+{
+       CELL new_size = *result_count + len;
+
+       if(new_size >= byte_array_capacity(result))
+               result = reallot_byte_array(result,new_size * 2);
+
+       memcpy((void *)BREF(result,*result_count),elts,len);
+
+       *result_count = new_size;
+
+       return result;
+}
+
 /* Bit arrays */
 
 /* size is in bits */
index 3ce1838b8b20b02ea11e40aca5ac31f9c3b20777..bbf7fb203d4e76b32a72ac1607bc6dd2faf6876f 100755 (executable)
@@ -146,6 +146,7 @@ DECLARE_PRIMITIVE(float_array);
 DECLARE_PRIMITIVE(clone);
 
 F_ARRAY *reallot_array(F_ARRAY* array, CELL capacity, CELL fill);
+F_BYTE_ARRAY *reallot_byte_array(F_BYTE_ARRAY *array, CELL capacity);
 DECLARE_PRIMITIVE(resize_array);
 DECLARE_PRIMITIVE(resize_byte_array);
 DECLARE_PRIMITIVE(resize_bit_array);
@@ -193,15 +194,33 @@ DECLARE_PRIMITIVE(wrapper);
        CELL result##_count = 0; \
        CELL result = tag_object(allot_array(ARRAY_TYPE,100,F))
 
-F_ARRAY *growable_add(F_ARRAY *result, CELL elt, CELL *result_count);
+F_ARRAY *growable_array_add(F_ARRAY *result, CELL elt, CELL *result_count);
 
-#define GROWABLE_ADD(result,elt) \
-       result = tag_object(growable_add(untag_object(result),elt,&result##_count))
+#define GROWABLE_ARRAY_ADD(result,elt) \
+       result = tag_object(growable_array_add(untag_object(result),elt,&result##_count))
 
-F_ARRAY *growable_append(F_ARRAY *result, F_ARRAY *elts, CELL *result_count);
+F_ARRAY *growable_array_append(F_ARRAY *result, F_ARRAY *elts, CELL *result_count);
 
-#define GROWABLE_APPEND(result,elts) \
-       result = tag_object(growable_append(untag_object(result),elts,&result##_count))
+#define GROWABLE_ARRAY_APPEND(result,elts) \
+       result = tag_object(growable_array_append(untag_object(result),elts,&result##_count))
 
-#define GROWABLE_TRIM(result) \
+#define GROWABLE_ARRAY_TRIM(result) \
        result = tag_object(reallot_array(untag_object(result),result##_count,F))
+
+/* Macros to simulate a byte vector in C */
+#define GROWABLE_BYTE_ARRAY(result) \
+       CELL result##_count = 0; \
+       CELL result = tag_object(allot_byte_array(100))
+
+F_ARRAY *growable_byte_array_add(F_BYTE_ARRAY *result, CELL elt, CELL *result_count);
+
+#define GROWABLE_BYTE_ARRAY_ADD(result,elt) \
+       result = tag_object(growable_byte_array_add(untag_object(result),elt,&result##_count))
+
+F_ARRAY *growable_byte_array_append(F_BYTE_ARRAY *result, void *elts, CELL len, CELL *result_count);
+
+#define GROWABLE_BYTE_ARRAY_APPEND(result,elts,len) \
+       result = tag_object(growable_byte_array_append(untag_object(result),elts,len,&result##_count))
+
+#define GROWABLE_BYTE_ARRAY_TRIM(result) \
+       result = tag_object(reallot_byte_array(untag_object(result),result##_count))