Merge branch 'master' of git://factorcode.org/git/factor

db4
Doug Coleman 2008-04-15 22:34:59 -05:00
commit 6937e9b367
108 changed files with 1765 additions and 805 deletions

View File

@ -6,7 +6,6 @@ implementation. It is not an introduction to the language itself.
* Contents * Contents
- Platform support
- Compiling the Factor VM - Compiling the Factor VM
- Libraries needed for compilation - Libraries needed for compilation
- Bootstrapping the Factor image - Bootstrapping the Factor image
@ -19,80 +18,50 @@ implementation. It is not an introduction to the language itself.
- Source organization - Source organization
- Community - Community
* Platform support
Factor supports the following platforms:
Linux/x86
Linux/AMD64
Linux/PowerPC
Linux/ARM
Mac OS X/x86
Mac OS X/PowerPC
FreeBSD/x86
FreeBSD/AMD64
OpenBSD/x86
OpenBSD/AMD64
Solaris/x86
Solaris/AMD64
MS Windows/x86 (XP and above)
MS Windows CE/ARM
Please donate time or hardware if you wish to see Factor running on
other platforms. In particular, we are interested in:
Windows/AMD64
Mac OS X/AMD64
Solaris/UltraSPARC
Linux/MIPS
* Compiling the Factor VM * Compiling the Factor VM
The Factor runtime is written in GNU C99, and is built with GNU make and The Factor runtime is written in GNU C99, and is built with GNU make and
gcc. gcc.
Factor requires gcc 3.4 or later. On x86, it /will not/ build using gcc Factor supports various platforms. For an up-to-date list, see
3.3 or earlier. If you are using gcc 4.3, you might get an unusable <http://factorcode.org/getfactor.fhtml>.
Factor binary unless you add 'SITE_CFLAGS=-fno-forward-propagate' to the
command-line arguments for make.
Run 'make' (or 'gmake' on *BSD) with no parameters to see a list of Factor requires gcc 3.4 or later.
targets and build options. Then run 'make' with the appropriate target
for your platform. On x86, Factor /will not/ build using gcc 3.3 or earlier.
If you are using gcc 4.3, you might get an unusable Factor binary unless
you add 'SITE_CFLAGS=-fno-forward-propagate' to the command-line
arguments for make.
Run 'make' ('gmake' on *BSD) with no parameters to build the Factor VM.
Compilation will yield an executable named 'factor' on Unix, Compilation will yield an executable named 'factor' on Unix,
'factor-nt.exe' on Windows XP/Vista, and 'factor-ce.exe' on Windows CE. 'factor.exe' on Windows XP/Vista, and 'factor-ce.exe' on Windows CE.
* Libraries needed for compilation * Libraries needed for compilation
For X11 support, you need recent development libraries for libc, Freetype, For X11 support, you need recent development libraries for libc,
X11, OpenGL and GLUT. On a Debian-derived Linux distribution (like Ubuntu), Freetype, X11, OpenGL and GLUT. On a Debian-derived Linux distribution
you can use the line (like Ubuntu), you can use the line
sudo apt-get install libc6-dev libfreetype6-dev libx11-dev glutg3-dev sudo apt-get install libc6-dev libfreetype6-dev libx11-dev glutg3-dev
to grab everything (if you're on a non-debian-derived distro please tell us to grab everything (if you're on a non-debian-derived distro please tell
what the equivalent command is on there and it can be added :) us what the equivalent command is on there and it can be added).
* Bootstrapping the Factor image * Bootstrapping the Factor image
The boot images are no longer included with the Factor distribution
due to size concerns. Instead, download a boot image from:
http://factorcode.org/images/
Once you have compiled the Factor runtime, you must bootstrap the Factor Once you have compiled the Factor runtime, you must bootstrap the Factor
system using the image that corresponds to your CPU architecture. system using the image that corresponds to your CPU architecture.
Once you download the right image, bootstrap the system with the Boot images can be obtained from <http://factorcode.org/images/latest/>.
Once you download the right image, bootstrap Factor with the
following command line: following command line:
./factor -i=boot.<cpu>.image ./factor -i=boot.<cpu>.image
Or this command for Mac OS X systems:
./Factor.app/Contents/MacOS/factor -i=boot.<cpu>.image
Bootstrap can take a while, depending on your system. When the process Bootstrap can take a while, depending on your system. When the process
completes, a 'factor.image' file will be generated. Note that this image completes, a 'factor.image' file will be generated. Note that this image
is both CPU and OS-specific, so in general cannot be shared between is both CPU and OS-specific, so in general cannot be shared between
@ -122,9 +91,8 @@ The latter keeps the terminal listener running.
* Running Factor on Mac OS X - Cocoa UI * Running Factor on Mac OS X - Cocoa UI
On Mac OS X 10.4 and later, a Cocoa UI is available in addition to the On Mac OS X, a Cocoa UI is available in addition to the terminal
terminal listener. If you are using Mac OS X 10.3, you can only run the listener.
X11 UI, as documented in the next section.
The 'factor' executable runs the terminal listener: The 'factor' executable runs the terminal listener:
@ -136,17 +104,16 @@ contains factor.image and the library sources.
* Running Factor on Mac OS X - X11 UI * Running Factor on Mac OS X - X11 UI
The X11 UI is available on Mac OS X, however its use is not recommended The X11 UI is also available on Mac OS X, however its use is not
since it does not integrate with the host OS. However, if you are recommended since it does not integrate with the host OS.
running Mac OS X 10.3, it is your only choice.
When compiling Factor, pass the X11=1 parameter: When compiling Factor, pass the X11=1 parameter:
make macosx-ppc X11=1 make X11=1
Then bootstrap with the following switches: Then bootstrap with the following switches:
./factor -i=boot.ppc.image -ui-backend=x11 ./factor -i=boot.<cpu>.image -ui-backend=x11
Now if $DISPLAY is set, running ./factor will start the UI. Now if $DISPLAY is set, running ./factor will start the UI.
@ -155,40 +122,36 @@ Now if $DISPLAY is set, running ./factor will start the UI.
If you did not download the binary package, you can bootstrap Factor in If you did not download the binary package, you can bootstrap Factor in
the command prompt: the command prompt:
factor-nt.exe -i=boot.x86.32.image factor.exe -i=boot.<cpu>.image
Once bootstrapped, double-clicking factor.exe starts the Factor UI. Once bootstrapped, double-clicking factor.exe starts the Factor UI.
To run the listener in the command prompt: To run the listener in the command prompt:
factor-nt.exe -run=listener factor.exe -run=listener
* The Factor FAQ * The Factor FAQ
The Factor FAQ lives online at http://factorcode.org/faq.fhtml The Factor FAQ is available at <http://factorcode.org/faq.fhtml>.
* Command line usage * Command line usage
The Factor VM supports a number of command line switches. To read Factor supports a number of command line switches. To read command line
command line usage documentation, either enter the following in the UI usage documentation, enter the following in the UI listener:
listener:
"command-line" about "command-line" about
* Source organization * Source organization
The following two directories are managed by the module system; consult The Factor source tree is organized as follows:
the documentation for details:
build-support/ - scripts used for compiling Factor
core/ - Factor core library and compiler core/ - Factor core library and compiler
extra/ - more libraries extra/ - more libraries
The following directories contain additional files:
misc/ - editor modes, icons, etc
vm/ - sources for the Factor runtime, written in C
fonts/ - TrueType fonts used by UI fonts/ - TrueType fonts used by UI
misc/ - editor modes, icons, etc
unmaintained/ - unmaintained contributions, please help! unmaintained/ - unmaintained contributions, please help!
vm/ - sources for the Factor VM, written in C
* Community * Community

View File

@ -18,12 +18,12 @@ boxer prep unboxer
getter setter getter setter
reg-class size align stack-align? ; reg-class size align stack-align? ;
: construct-c-type ( class -- type ) : new-c-type ( class -- type )
new new
int-regs >>reg-class ; int-regs >>reg-class ;
: <c-type> ( -- type ) : <c-type> ( -- type )
\ c-type construct-c-type ; \ c-type new-c-type ;
SYMBOL: c-types SYMBOL: c-types
@ -189,7 +189,7 @@ DEFER: >c-ushort-array
TUPLE: long-long-type < c-type ; TUPLE: long-long-type < c-type ;
: <long-long-type> ( -- type ) : <long-long-type> ( -- type )
long-long-type construct-c-type ; long-long-type new-c-type ;
M: long-long-type unbox-parameter ( n type -- ) M: long-long-type unbox-parameter ( n type -- )
c-type-unboxer %unbox-long-long ; c-type-unboxer %unbox-long-long ;

View File

@ -76,7 +76,7 @@ ARTICLE: "assocs-sets" "Set-theoretic operations on assocs"
{ $subsection remove-all } { $subsection remove-all }
{ $subsection substitute } { $subsection substitute }
{ $subsection substitute-here } { $subsection substitute-here }
{ $see-also key? } ; { $see-also key? assoc-contains? assoc-all? "sets" } ;
ARTICLE: "assocs-mutation" "Storing keys and values in assocs" ARTICLE: "assocs-mutation" "Storing keys and values in assocs"
"Utility operations built up from the " { $link "assocs-protocol" } ":" "Utility operations built up from the " { $link "assocs-protocol" } ":"
@ -97,6 +97,7 @@ $nl
{ $subsection assoc-map } { $subsection assoc-map }
{ $subsection assoc-push-if } { $subsection assoc-push-if }
{ $subsection assoc-subset } { $subsection assoc-subset }
{ $subsection assoc-contains? }
{ $subsection assoc-all? } { $subsection assoc-all? }
"Three additional combinators:" "Three additional combinators:"
{ $subsection cache } { $subsection cache }
@ -206,9 +207,13 @@ HELP: assoc-subset
{ $values { "assoc" assoc } { "quot" "a quotation with stack effect " { $snippet "( key value -- ? )" } } { "subassoc" "a new assoc" } } { $values { "assoc" assoc } { "quot" "a quotation with stack effect " { $snippet "( key value -- ? )" } } { "subassoc" "a new assoc" } }
{ $description "Outputs an assoc of the same type as " { $snippet "assoc" } " consisting of all entries for which the predicate quotation yields true." } ; { $description "Outputs an assoc of the same type as " { $snippet "assoc" } " consisting of all entries for which the predicate quotation yields true." } ;
HELP: assoc-contains?
{ $values { "assoc" assoc } { "quot" "a quotation with stack effect " { $snippet "( key value -- ? )" } } { "?" "a boolean" } }
{ $description "Tests if the assoc contains an entry satisfying a predicate by applying the quotation to each entry in turn. Iteration stops if an entry is found for which the quotation outputs a true value." } ;
HELP: assoc-all? HELP: assoc-all?
{ $values { "assoc" assoc } { "quot" "a quotation with stack effect " { $snippet "( key value -- ? )" } } { "?" "a boolean" } } { $values { "assoc" assoc } { "quot" "a quotation with stack effect " { $snippet "( key value -- ? )" } } { "?" "a boolean" } }
{ $description "Applies a predicate quotation to entry in the assoc. Outputs true if the assoc yields true for each entry (which includes the case where the assoc is empty)." } ; { $description "Tests if all entries in the assoc satisfy a predicate by applying the quotation to each entry in turn. a predicate quotation to entry in the assoc. Iteration stops if an entry is found for which the quotation outputs " { $link f } ". If the assoc is empty, always outputs " { $link t } "." } ;
HELP: subassoc? HELP: subassoc?
{ $values { "assoc1" assoc } { "assoc2" assoc } { "?" "a new assoc" } } { $values { "assoc1" assoc } { "assoc2" assoc } { "?" "a new assoc" } }

View File

@ -390,7 +390,7 @@ define-builtin
! Create special tombstone values ! Create special tombstone values
"tombstone" "hashtables.private" create "tombstone" "hashtables.private" create
"tuple" "kernel" lookup tuple
{ } define-tuple-class { } define-tuple-class
"((empty))" "hashtables.private" create "((empty))" "hashtables.private" create
@ -403,7 +403,7 @@ define-builtin
! Some tuple classes ! Some tuple classes
"hashtable" "hashtables" create "hashtable" "hashtables" create
"tuple" "kernel" lookup tuple
{ {
{ {
{ "array-capacity" "sequences.private" } { "array-capacity" "sequences.private" }
@ -424,7 +424,7 @@ define-builtin
} define-tuple-class } define-tuple-class
"sbuf" "sbufs" create "sbuf" "sbufs" create
"tuple" "kernel" lookup tuple
{ {
{ {
{ "string" "strings" } { "string" "strings" }
@ -440,7 +440,7 @@ define-builtin
} define-tuple-class } define-tuple-class
"vector" "vectors" create "vector" "vectors" create
"tuple" "kernel" lookup tuple
{ {
{ {
{ "array" "arrays" } { "array" "arrays" }
@ -456,7 +456,7 @@ define-builtin
} define-tuple-class } define-tuple-class
"byte-vector" "byte-vectors" create "byte-vector" "byte-vectors" create
"tuple" "kernel" lookup tuple
{ {
{ {
{ "byte-array" "byte-arrays" } { "byte-array" "byte-arrays" }
@ -472,7 +472,7 @@ define-builtin
} define-tuple-class } define-tuple-class
"bit-vector" "bit-vectors" create "bit-vector" "bit-vectors" create
"tuple" "kernel" lookup tuple
{ {
{ {
{ "bit-array" "bit-arrays" } { "bit-array" "bit-arrays" }
@ -488,7 +488,7 @@ define-builtin
} define-tuple-class } define-tuple-class
"float-vector" "float-vectors" create "float-vector" "float-vectors" create
"tuple" "kernel" lookup tuple
{ {
{ {
{ "float-array" "float-arrays" } { "float-array" "float-arrays" }
@ -504,7 +504,7 @@ define-builtin
} define-tuple-class } define-tuple-class
"curry" "kernel" create "curry" "kernel" create
"tuple" "kernel" lookup tuple
{ {
{ {
{ "object" "kernel" } { "object" "kernel" }
@ -525,7 +525,7 @@ define-builtin
[ tuple-layout [ <tuple-boa> ] curry ] tri define [ tuple-layout [ <tuple-boa> ] curry ] tri define
"compose" "kernel" create "compose" "kernel" create
"tuple" "kernel" lookup tuple
{ {
{ {
{ "object" "kernel" } { "object" "kernel" }

View File

@ -32,23 +32,23 @@ $nl
"" ""
": add-occupant ( person vehicle -- ) occupants>> push ;" ": add-occupant ( person vehicle -- ) occupants>> push ;"
"" ""
": construct-vehicle ( class -- vehicle )" ": new-vehicle ( class -- vehicle )"
" new" " new"
" V{ } clone >>occupants ;" " V{ } clone >>occupants ;"
"" ""
"TUPLE: car < vehicle engine ;" "TUPLE: car < vehicle engine ;"
": <car> ( max-speed engine -- car )" ": <car> ( max-speed engine -- car )"
" car construct-vehicle" " car new-vehicle"
" swap >>engine" " swap >>engine"
" swap >>max-speed ;" " swap >>max-speed ;"
"" ""
"TUPLE: aeroplane < vehicle max-altitude ;" "TUPLE: aeroplane < vehicle max-altitude ;"
": <aeroplane> ( max-speed max-altitude -- aeroplane )" ": <aeroplane> ( max-speed max-altitude -- aeroplane )"
" aeroplane construct-vehicle" " aeroplane new-vehicle"
" swap >>max-altitude" " swap >>max-altitude"
" swap >>max-speed ;" " swap >>max-speed ;"
} }
"The naming convention for parametrized constructors is " { $snippet "construct-" { $emphasis "class" } } "." ; "The naming convention for parametrized constructors is " { $snippet "new-" { $emphasis "class" } } "." ;
ARTICLE: "tuple-constructors" "Tuple constructors" ARTICLE: "tuple-constructors" "Tuple constructors"
"Tuples are created by calling one of two constructor primitives:" "Tuples are created by calling one of two constructor primitives:"
@ -64,13 +64,16 @@ $nl
{ $code { $code
"TUPLE: color red green blue alpha ;" "TUPLE: color red green blue alpha ;"
"" ""
"! The following two are equivalent"
"C: <rgba> rgba" "C: <rgba> rgba"
": <rgba> color boa ; ! identical to above" ": <rgba> color boa ;"
"" ""
"! We can define constructors which call other constructors"
": <rgb> f <rgba> ;" ": <rgb> f <rgba> ;"
"" ""
": <color> new ;" "! The following two are equivalent"
": <color> f f f f <rgba> ; ! identical to above" ": <color> color new ;"
": <color> f f f f <rgba> ;"
} }
{ $subsection "parametrized-constructors" } ; { $subsection "parametrized-constructors" } ;

View File

@ -538,3 +538,6 @@ TUPLE: another-forget-accessors-test ;
] with-string-writer empty? ] with-string-writer empty?
] with-variable ] with-variable
] unit-test ] unit-test
! Missing error check
[ "IN: tuples.test USE: words TUPLE: wrong-superclass < word ;" eval ] must-fail

View File

@ -58,6 +58,8 @@ PRIVATE>
: all-slot-names ( class -- slots ) : all-slot-names ( class -- slots )
superclasses [ slot-names ] map concat \ class prefix ; superclasses [ slot-names ] map concat \ class prefix ;
ERROR: bad-superclass class ;
<PRIVATE <PRIVATE
: tuple= ( tuple1 tuple2 -- ? ) : tuple= ( tuple1 tuple2 -- ? )
@ -185,16 +187,23 @@ M: tuple-class update-class
: tuple-class-unchanged? ( class superclass slots -- ? ) : tuple-class-unchanged? ( class superclass slots -- ? )
rot tuck [ superclass = ] [ slot-names = ] 2bi* and ; rot tuck [ superclass = ] [ slot-names = ] 2bi* and ;
: valid-superclass? ( class -- ? )
[ tuple-class? ] [ tuple eq? ] bi or ;
: check-superclass ( superclass -- )
dup valid-superclass? [ bad-superclass ] unless drop ;
PRIVATE> PRIVATE>
GENERIC# define-tuple-class 2 ( class superclass slots -- ) GENERIC# define-tuple-class 2 ( class superclass slots -- )
M: word define-tuple-class M: word define-tuple-class
over check-superclass
define-new-tuple-class ; define-new-tuple-class ;
M: tuple-class define-tuple-class M: tuple-class define-tuple-class
3dup tuple-class-unchanged? 3dup tuple-class-unchanged?
[ 3dup redefine-tuple-class ] unless [ over check-superclass 3dup redefine-tuple-class ] unless
3drop ; 3drop ;
: define-error-class ( class superclass slots -- ) : define-error-class ( class superclass slots -- )

View File

@ -215,7 +215,10 @@ M: check-method summary
drop "Invalid parameters for create-method" ; drop "Invalid parameters for create-method" ;
M: no-tuple-class summary M: no-tuple-class summary
drop "Invalid class for define-constructor" ; drop "BOA constructors can only be defined for tuple classes" ;
M: bad-superclass summary
drop "Tuple classes can only inherit from other tuple classes" ;
M: no-cond summary M: no-cond summary
drop "Fall-through in cond" ; drop "Fall-through in cond" ;

View File

@ -1,5 +1,5 @@
USING: dlists dlists.private kernel tools.test random assocs USING: dlists dlists.private kernel tools.test random assocs
hashtables sequences namespaces sorting debugger io prettyprint sets sequences namespaces sorting debugger io prettyprint
math ; math ;
IN: dlists.tests IN: dlists.tests

View File

@ -237,7 +237,7 @@ M: phantom-stack clone
GENERIC: finalize-height ( stack -- ) GENERIC: finalize-height ( stack -- )
: construct-phantom-stack ( class -- stack ) : new-phantom-stack ( class -- stack )
>r 0 V{ } clone r> boa ; inline >r 0 V{ } clone r> boa ; inline
: (loc) : (loc)
@ -257,7 +257,7 @@ GENERIC: <loc> ( n stack -- loc )
TUPLE: phantom-datastack < phantom-stack ; TUPLE: phantom-datastack < phantom-stack ;
: <phantom-datastack> ( -- stack ) : <phantom-datastack> ( -- stack )
phantom-datastack construct-phantom-stack ; phantom-datastack new-phantom-stack ;
M: phantom-datastack <loc> (loc) <ds-loc> ; M: phantom-datastack <loc> (loc) <ds-loc> ;
@ -267,7 +267,7 @@ M: phantom-datastack finalize-height
TUPLE: phantom-retainstack < phantom-stack ; TUPLE: phantom-retainstack < phantom-stack ;
: <phantom-retainstack> ( -- stack ) : <phantom-retainstack> ( -- stack )
phantom-retainstack construct-phantom-stack ; phantom-retainstack new-phantom-stack ;
M: phantom-retainstack <loc> (loc) <rs-loc> ; M: phantom-retainstack <loc> (loc) <rs-loc> ;

View File

@ -17,9 +17,14 @@ TUPLE: lexer text line line-text line-length column ;
0 >>column 0 >>column
drop ; drop ;
: new-lexer ( text class -- lexer )
new
0 >>line
swap >>text
dup next-line ; inline
: <lexer> ( text -- lexer ) : <lexer> ( text -- lexer )
0 { set-lexer-text set-lexer-line } lexer construct lexer new-lexer ;
dup next-line ;
: location ( -- loc ) : location ( -- loc )
file get lexer get lexer-line 2dup and file get lexer get lexer-line 2dup and

View File

@ -60,8 +60,8 @@ $nl
{ $subsection short-section } { $subsection short-section }
{ $subsection long-section } { $subsection long-section }
"Utilities to use when implementing sections:" "Utilities to use when implementing sections:"
{ $subsection construct-section } { $subsection new-section }
{ $subsection construct-block } { $subsection new-block }
{ $subsection add-section } ; { $subsection add-section } ;
ARTICLE: "prettyprint-sections" "Prettyprinter sections" ARTICLE: "prettyprint-sections" "Prettyprinter sections"

View File

@ -78,7 +78,7 @@ HELP: section
{ { $snippet "overhang" } " - number of columns which must be left blank before the wrap margin for the prettyprinter to consider emitting this section as a " { $link short-section } ". Avoids lone hanging closing brackets" } { { $snippet "overhang" } " - number of columns which must be left blank before the wrap margin for the prettyprinter to consider emitting this section as a " { $link short-section } ". Avoids lone hanging closing brackets" }
} } ; } } ;
HELP: construct-section HELP: new-section
{ $values { "length" integer } { "class" "a subclass of " { $link section } } { "section" section } } { $values { "length" integer } { "class" "a subclass of " { $link section } } { "section" section } }
{ $description "Creates a new section with the given length starting from " { $link position } ", advancing " { $link position } "." } ; { $description "Creates a new section with the given length starting from " { $link position } ", advancing " { $link position } "." } ;

View File

@ -71,7 +71,7 @@ start end
start-group? end-group? start-group? end-group?
style overhang ; style overhang ;
: construct-section ( length class -- section ) : new-section ( length class -- section )
new new
position get >>start position get >>start
swap position [ + ] change swap position [ + ] change
@ -127,7 +127,7 @@ M: object short-section? section-fits? ;
TUPLE: line-break < section type ; TUPLE: line-break < section type ;
: <line-break> ( type -- section ) : <line-break> ( type -- section )
0 \ line-break construct-section 0 \ line-break new-section
swap >>type ; swap >>type ;
M: line-break short-section drop ; M: line-break short-section drop ;
@ -137,13 +137,13 @@ M: line-break long-section drop ;
! Block sections ! Block sections
TUPLE: block < section sections ; TUPLE: block < section sections ;
: construct-block ( style class -- block ) : new-block ( style class -- block )
0 swap construct-section 0 swap new-section
V{ } clone >>sections V{ } clone >>sections
swap >>style ; inline swap >>style ; inline
: <block> ( style -- block ) : <block> ( style -- block )
block construct-block ; block new-block ;
: pprinter-block ( -- block ) pprinter-stack get peek ; : pprinter-block ( -- block ) pprinter-stack get peek ;
@ -200,7 +200,7 @@ M: block short-section ( block -- )
TUPLE: text < section string ; TUPLE: text < section string ;
: <text> ( string style -- text ) : <text> ( string style -- text )
over length 1+ \ text construct-section over length 1+ \ text new-section
swap >>style swap >>style
swap >>string ; swap >>string ;
@ -216,7 +216,7 @@ M: text long-section short-section ;
TUPLE: inset < block narrow? ; TUPLE: inset < block narrow? ;
: <inset> ( narrow? -- block ) : <inset> ( narrow? -- block )
H{ } inset construct-block H{ } inset new-block
2 >>overhang 2 >>overhang
swap >>narrow? ; swap >>narrow? ;
@ -237,7 +237,7 @@ M: inset newline-after? drop t ;
TUPLE: flow < block ; TUPLE: flow < block ;
: <flow> ( -- block ) : <flow> ( -- block )
H{ } flow construct-block ; H{ } flow new-block ;
M: flow short-section? ( section -- ? ) M: flow short-section? ( section -- ? )
#! If we can make room for this entire block by inserting #! If we can make room for this entire block by inserting
@ -253,7 +253,7 @@ M: flow short-section? ( section -- ? )
TUPLE: colon < block ; TUPLE: colon < block ;
: <colon> ( -- block ) : <colon> ( -- block )
H{ } colon construct-block ; H{ } colon new-block ;
M: colon long-section short-section ; M: colon long-section short-section ;

View File

@ -528,12 +528,7 @@ HELP: contains?
HELP: all? HELP: all?
{ $values { "seq" sequence } { "quot" "a quotation with stack effect " { $snippet "( elt -- ? )" } } { "?" "a boolean" } } { $values { "seq" sequence } { "quot" "a quotation with stack effect " { $snippet "( elt -- ? )" } } { "?" "a boolean" } }
{ $description "Tests if all elements in the sequence satisfy the predicate by checking each element in turn. Given an empty sequence, vacuously outputs " { $link t } "." } { $description "Tests if all elements in the sequence satisfy the predicate by checking each element in turn. Given an empty sequence, vacuously outputs " { $link t } "." } ;
{ $notes
"The implementation makes use of a well-known logical identity:"
$nl
{ $snippet "P[x] for all x <==> not ((not P[x]) for some x)" }
} ;
HELP: push-if HELP: push-if
{ $values { "elt" object } { "quot" "a quotation with stack effect " { $snippet "( elt -- ? )" } } { "accum" "a resizable mutable sequence" } } { $values { "elt" object } { "quot" "a quotation with stack effect " { $snippet "( elt -- ? )" } } { "accum" "a resizable mutable sequence" } }

2
core/sets/authors.txt Normal file
View File

@ -0,0 +1,2 @@
Slava Pestov
Doug Coleman

View File

@ -1,7 +1,9 @@
USING: kernel help.markup help.syntax sequences ; USING: kernel help.markup help.syntax sequences ;
IN: sets IN: sets
ARTICLE: "sets" "Set theoretic operations" ARTICLE: "sets" "Set-theoretic operations on sequences"
"Set-theoretic operations on sequences are defined on the " { $vocab-link "sets" } " vocabulary. These operations use hashtables internally to achieve linear running time."
$nl
"Remove duplicates:" "Remove duplicates:"
{ $subsection prune } { $subsection prune }
"Test for duplicates:" "Test for duplicates:"
@ -9,7 +11,8 @@ ARTICLE: "sets" "Set theoretic operations"
"Set operations on sequences:" "Set operations on sequences:"
{ $subsection diff } { $subsection diff }
{ $subsection intersect } { $subsection intersect }
{ $subsection union } ; { $subsection union }
{ $see-also member? memq? contains? all? "assocs-sets" } ;
HELP: unique HELP: unique
{ $values { "seq" "a sequence" } { "assoc" "an assoc" } } { $values { "seq" "a sequence" } { "assoc" "an assoc" } }
@ -22,14 +25,14 @@ HELP: prune
{ $values { "seq" "a sequence" } { "newseq" "a sequence" } } { $values { "seq" "a sequence" } { "newseq" "a sequence" } }
{ $description "Outputs a new sequence with each distinct element of " { $snippet "seq" } " appearing only once. Elements are compared for equality using " { $link = } " and elements are ordered according to their position in " { $snippet "seq" } "." } { $description "Outputs a new sequence with each distinct element of " { $snippet "seq" } " appearing only once. Elements are compared for equality using " { $link = } " and elements are ordered according to their position in " { $snippet "seq" } "." }
{ $examples { $examples
{ $example "USING: sequences prettyprint ;" "{ 1 1 t 3 t } prune ." "V{ 1 t 3 }" } { $example "USING: sets prettyprint ;" "{ 1 1 t 3 t } prune ." "V{ 1 t 3 }" }
} ; } ;
HELP: all-unique? HELP: all-unique?
{ $values { "seq" sequence } { "?" "a boolean" } } { $values { "seq" sequence } { "?" "a boolean" } }
{ $description "Tests whether a sequence contains any repeated elements." } { $description "Tests whether a sequence contains any repeated elements." }
{ $example { $example
"USING: hashtables prettyprint ;" "USING: sets prettyprint ;"
"{ 0 1 1 2 3 5 } all-unique? ." "{ 0 1 1 2 3 5 } all-unique? ."
"f" "f"
} ; } ;
@ -38,21 +41,21 @@ HELP: diff
{ $values { "seq1" sequence } { "seq2" sequence } { "newseq" sequence } } { $values { "seq1" sequence } { "seq2" sequence } { "newseq" sequence } }
{ $description "Outputs a sequence consisting of elements present in " { $snippet "seq2" } " but not " { $snippet "seq1" } ", comparing elements for equality." { $description "Outputs a sequence consisting of elements present in " { $snippet "seq2" } " but not " { $snippet "seq1" } ", comparing elements for equality."
} { $examples } { $examples
{ $example "USING: sequences prettyprint ;" "{ 1 2 3 } { 2 3 4 } diff ." "{ 4 }" } { $example "USING: sets prettyprint ;" "{ 1 2 3 } { 2 3 4 } diff ." "{ 4 }" }
} ; } ;
HELP: intersect HELP: intersect
{ $values { "seq1" sequence } { "seq2" sequence } { "newseq" sequence } } { $values { "seq1" sequence } { "seq2" sequence } { "newseq" sequence } }
{ $description "Outputs a sequence consisting of elements present in both " { $snippet "seq1" } " and " { $snippet "seq2" } "." } { $description "Outputs a sequence consisting of elements present in both " { $snippet "seq1" } " and " { $snippet "seq2" } "." }
{ $examples { $examples
{ $example "USING: sequences prettyprint ;" "{ 1 2 3 } { 2 3 4 } intersect ." "{ 2 3 }" } { $example "USING: sets prettyprint ;" "{ 1 2 3 } { 2 3 4 } intersect ." "{ 2 3 }" }
} ; } ;
HELP: union HELP: union
{ $values { "seq1" sequence } { "seq2" sequence } { "newseq" sequence } } { $values { "seq1" sequence } { "seq2" sequence } { "newseq" sequence } }
{ $description "Outputs a sequence consisting of elements present in " { $snippet "seq1" } " and " { $snippet "seq2" } " which does not contain duplicate values." } { $description "Outputs a sequence consisting of elements present in " { $snippet "seq1" } " and " { $snippet "seq2" } " which does not contain duplicate values." }
{ $examples { $examples
{ $example "USING: sequences prettyprint ;" "{ 1 2 3 } { 2 3 4 } union ." "{ 1 2 3 4 }" } { $example "USING: sets prettyprint ;" "{ 1 2 3 } { 2 3 4 } union ." "V{ 1 2 3 4 }" }
} ; } ;
{ diff intersect union } related-words { diff intersect union } related-words

1
core/sets/summary.txt Normal file
View File

@ -0,0 +1 @@
Set-theoretic operations on sequences

1
core/sets/tags.txt Normal file
View File

@ -0,0 +1 @@
collections

View File

@ -96,7 +96,7 @@ unit-test
[ ] [ [ ] [
[ [
4 [ 4 [
100 [ drop "obdurak" ] map 100 [ drop "obdurak" clone ] map
gc gc
dup [ dup [
1234 0 rot set-string-nth 1234 0 rot set-string-nth

View File

@ -56,13 +56,16 @@ mailbox variables sleep-entry ;
PRIVATE> PRIVATE>
: <thread> ( quot name -- thread ) : new-thread ( quot name class -- thread )
\ thread new new
swap >>name swap >>name
swap >>quot swap >>quot
\ thread counter >>id \ thread counter >>id
<box> >>continuation <box> >>continuation
[ ] >>exit-handler ; [ ] >>exit-handler ; inline
: <thread> ( quot name -- thread )
\ thread new-thread ;
: run-queue 42 getenv ; : run-queue 42 getenv ;

View File

@ -0,0 +1 @@
Non-core array words

View File

@ -8,6 +8,8 @@ IN: builder.cleanup
SYMBOL: builder-debug SYMBOL: builder-debug
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
: compress-image ( -- ) { "bzip2" my-boot-image-name } to-strings try-process ; : compress-image ( -- ) { "bzip2" my-boot-image-name } to-strings try-process ;
: delete-child-factor ( -- ) : delete-child-factor ( -- )

View File

@ -7,6 +7,10 @@ IN: builder.common
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
SYMBOL: upload-to-factorcode
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
SYMBOL: builds-dir SYMBOL: builds-dir
: builds ( -- path ) : builds ( -- path )
@ -21,15 +25,6 @@ VAR: stamp
: builds/factor ( -- path ) builds "factor" append-path ; : builds/factor ( -- path ) builds "factor" append-path ;
: build-dir ( -- path ) builds stamp> append-path ; : build-dir ( -- path ) builds stamp> append-path ;
: create-build-dir ( -- )
datestamp >stamp
build-dir make-directory ;
: enter-build-dir ( -- ) build-dir set-current-directory ;
: clone-builds-factor ( -- )
{ "git" "clone" builds/factor } to-strings try-process ;
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
: prepare-build-machine ( -- ) : prepare-build-machine ( -- )
@ -57,8 +52,3 @@ SYMBOL: status
{ status-vm status-boot status-test status-build status-release status } { status-vm status-boot status-test status-build status-release status }
[ off ] [ off ]
each ; each ;
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
SYMBOL: upload-to-factorcode

View File

@ -8,6 +8,8 @@ IN: builder.email
SYMBOL: builder-from SYMBOL: builder-from
SYMBOL: builder-recipients SYMBOL: builder-recipients
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
: subject-status ( -- str ) status get [ "report" ] [ "error" ] if ; : subject-status ( -- str ) status get [ "report" ] [ "error" ] if ;
: subject ( -- str ) { "builder@" host-name* ": " subject-status } to-string ; : subject ( -- str ) { "builder@" host-name* ": " subject-status } to-string ;

View File

@ -1,6 +1,6 @@
IN: concurrency.combinators.tests IN: concurrency.combinators.tests
USING: concurrency.combinators tools.test random kernel math USING: concurrency.combinators tools.test random kernel math
concurrency.mailboxes threads sequences ; concurrency.mailboxes threads sequences accessors ;
[ [ drop ] parallel-each ] must-infer [ [ drop ] parallel-each ] must-infer
[ [ ] parallel-map ] must-infer [ [ ] parallel-map ] must-infer
@ -11,7 +11,7 @@ concurrency.mailboxes threads sequences ;
[ { 1 4 9 } ] [ { 1 2 3 } [ 1000 random sleep sq ] parallel-map ] unit-test [ { 1 4 9 } ] [ { 1 2 3 } [ 1000 random sleep sq ] parallel-map ] unit-test
[ { 1 2 3 } [ dup 2 mod 0 = [ "Even" throw ] when ] parallel-map ] [ { 1 2 3 } [ dup 2 mod 0 = [ "Even" throw ] when ] parallel-map ]
[ delegate "Even" = ] must-fail-with [ error>> "Even" = ] must-fail-with
[ V{ 0 3 6 9 } ] [ V{ 0 3 6 9 } ]
[ 10 [ 3 mod zero? ] parallel-subset ] unit-test [ 10 [ 3 mod zero? ] parallel-subset ] unit-test

View File

@ -81,23 +81,19 @@ M: mailbox dispose
: wait-for-close ( mailbox -- ) : wait-for-close ( mailbox -- )
f wait-for-close-timeout ; f wait-for-close-timeout ;
TUPLE: linked-error thread ; TUPLE: linked-error error thread ;
: <linked-error> ( error thread -- linked ) C: <linked-error> linked-error
{ set-delegate set-linked-error-thread }
linked-error construct ;
: ?linked dup linked-error? [ rethrow ] when ; : ?linked dup linked-error? [ rethrow ] when ;
TUPLE: linked-thread supervisor ; TUPLE: linked-thread < thread supervisor ;
M: linked-thread error-in-thread M: linked-thread error-in-thread
[ <linked-error> ] keep [ <linked-error> ] [ supervisor>> ] bi mailbox-put ;
linked-thread-supervisor mailbox-put ;
: <linked-thread> ( quot name mailbox -- thread' ) : <linked-thread> ( quot name mailbox -- thread' )
>r <thread> linked-thread construct-delegate r> >r linked-thread new-thread r> >>supervisor ;
over set-linked-thread-supervisor ;
: spawn-linked-to ( quot name mailbox -- thread ) : spawn-linked-to ( quot name mailbox -- thread )
<linked-thread> [ (spawn) ] keep ; <linked-thread> [ (spawn) ] keep ;

View File

@ -4,7 +4,7 @@
USING: kernel threads vectors arrays sequences USING: kernel threads vectors arrays sequences
namespaces tools.test continuations dlists strings math words namespaces tools.test continuations dlists strings math words
match quotations concurrency.messaging concurrency.mailboxes match quotations concurrency.messaging concurrency.mailboxes
concurrency.count-downs ; concurrency.count-downs accessors ;
IN: concurrency.messaging.tests IN: concurrency.messaging.tests
[ ] [ my-mailbox mailbox-data dlist-delete-all ] unit-test [ ] [ my-mailbox mailbox-data dlist-delete-all ] unit-test
@ -30,7 +30,7 @@ IN: concurrency.messaging.tests
"crash" throw "crash" throw
] "Linked test" spawn-linked drop ] "Linked test" spawn-linked drop
receive receive
] [ delegate "crash" = ] must-fail-with ] [ error>> "crash" = ] must-fail-with
MATCH-VARS: ?from ?to ?value ; MATCH-VARS: ?from ?to ?value ;
SYMBOL: increment SYMBOL: increment

View File

@ -9,7 +9,7 @@ TUPLE: mysql-statement ;
TUPLE: mysql-result-set ; TUPLE: mysql-result-set ;
M: mysql-db db-open ( mysql-db -- ) M: mysql-db db-open ( mysql-db -- )
drop ; ;
M: mysql-db dispose ( mysql-db -- ) M: mysql-db dispose ( mysql-db -- )
mysql-db-handle mysql_close ; mysql-db-handle mysql_close ;

View File

@ -1,9 +1,10 @@
! Generate a new factor.vim file for syntax highlighting ! Generate a new factor.vim file for syntax highlighting
USING: http.server.templating.fhtml io.files ; USING: http.server.templating http.server.templating.fhtml
io.files ;
IN: editors.vim.generate-syntax IN: editors.vim.generate-syntax
: generate-vim-syntax ( -- ) : generate-vim-syntax ( -- )
"misc/factor.vim.fgen" resource-path "misc/factor.vim.fgen" resource-path <fhtml>
"misc/factor.vim" resource-path "misc/factor.vim" resource-path
template-convert ; template-convert ;

View File

@ -54,7 +54,7 @@ IN: farkup.tests
[ "<p>=</p><h2>foo</h2>" ] [ "===foo==" convert-farkup ] unit-test [ "<p>=</p><h2>foo</h2>" ] [ "===foo==" convert-farkup ] unit-test
[ "<h1>foo</h1><p>=</p>" ] [ "=foo==" convert-farkup ] unit-test [ "<h1>foo</h1><p>=</p>" ] [ "=foo==" convert-farkup ] unit-test
[ "<span class='KEYWORD3'>int</span> <span class='FUNCTION'>main</span><span class='OPERATOR'>(</span><span class='OPERATOR'>)</span><br/>" ] [ "<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>" ]
[ "[c{int main()}]" convert-farkup ] unit-test [ "[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\"/></p>" ] [ "[[image:lol.jpg]]" convert-farkup ] unit-test

View File

@ -1,11 +1,13 @@
! Copyright (C) 2008 Doug Coleman. ! Copyright (C) 2008 Doug Coleman.
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: arrays io kernel memoize namespaces peg sequences strings USING: arrays io io.styles kernel memoize namespaces peg
html.elements xml.entities xmode.code2html splitting sequences strings html.elements xml.entities xmode.code2html
io.streams.string html peg.parsers html.elements sequences.deep splitting io.streams.string html peg.parsers html.elements
unicode.categories ; sequences.deep unicode.categories ;
IN: farkup IN: farkup
<PRIVATE
: delimiters ( -- string ) : delimiters ( -- string )
"*_^~%[-=|\\\n" ; inline "*_^~%[-=|\\\n" ; inline
@ -53,7 +55,13 @@ MEMO: eq ( -- parser )
: render-code ( string mode -- string' ) : render-code ( string mode -- string' )
>r string-lines r> >r string-lines r>
[ [ htmlize-lines ] with-html-stream ] with-string-writer ; [
[
H{ { wrap-margin f } } [
htmlize-lines
] with-nesting
] with-html-stream
] with-string-writer ;
: escape-link ( href text -- href-esc text-esc ) : escape-link ( href text -- href-esc text-esc )
>r escape-quoted-string r> escape-string ; >r escape-quoted-string r> escape-string ;
@ -144,6 +152,8 @@ MEMO: paragraph ( -- parser )
[ "<p>" swap "</p>" 3array ] unless [ "<p>" swap "</p>" 3array ] unless
] action ; ] action ;
PRIVATE>
PEG: parse-farkup ( -- parser ) PEG: parse-farkup ( -- parser )
[ [
list , table , h1 , h2 , h3 , h4 , code , paragraph , 2nl , nl , list , table , h1 , h2 , h3 , h4 , code , paragraph , 2nl , nl ,

View File

@ -3,7 +3,7 @@ namespaces words sequences classes assocs vocabs kernel arrays
prettyprint.backend kernel.private io generic math system prettyprint.backend kernel.private io generic math system
strings sbufs vectors byte-arrays bit-arrays float-arrays strings sbufs vectors byte-arrays bit-arrays float-arrays
quotations io.streams.byte-array io.encodings.string quotations io.streams.byte-array io.encodings.string
classes.builtin ; classes.builtin parser ;
IN: help.handbook IN: help.handbook
ARTICLE: "conventions" "Conventions" ARTICLE: "conventions" "Conventions"
@ -25,6 +25,7 @@ $nl
{ { $snippet { $emphasis "foo" } "?" } "outputs a boolean" { { $link empty? } } } { { $snippet { $emphasis "foo" } "?" } "outputs a boolean" { { $link empty? } } }
{ { $snippet "?" { $emphasis "foo" } } { "conditionally performs " { $snippet { $emphasis "foo" } } } { { $links ?nth } } } { { $snippet "?" { $emphasis "foo" } } { "conditionally performs " { $snippet { $emphasis "foo" } } } { { $links ?nth } } }
{ { $snippet "<" { $emphasis "foo" } ">" } { "creates a new " { $snippet "foo" } } { { $link <array> } } } { { $snippet "<" { $emphasis "foo" } ">" } { "creates a new " { $snippet "foo" } } { { $link <array> } } }
{ { $snippet "new-" { $emphasis "foo" } } { "creates a new " { $snippet "foo" } ", taking some kind of parameter from the stack which determines the type of the object to be created" } { { $link new-sequence } ", " { $link new-lexer } ", " { $link new } } }
{ { $snippet { $emphasis "foo" } "*" } { "alternative form of " { $snippet "foo" } ", or a generic word called by " { $snippet "foo" } } { { $links at* pprint* } } } { { $snippet { $emphasis "foo" } "*" } { "alternative form of " { $snippet "foo" } ", or a generic word called by " { $snippet "foo" } } { { $links at* pprint* } } }
{ { $snippet "(" { $emphasis "foo" } ")" } { "implementation detail word used by " { $snippet "foo" } } { { $link (clone) } } } { { $snippet "(" { $emphasis "foo" } ")" } { "implementation detail word used by " { $snippet "foo" } } { { $link (clone) } } }
{ { $snippet "set-" { $emphasis "foo" } } { "sets " { $snippet "foo" } " to a new value" } { $links set-length } } { { $snippet "set-" { $emphasis "foo" } } { "sets " { $snippet "foo" } " to a new value" } { $links set-length } }

View File

@ -3,7 +3,9 @@ namespaces tools.test xml.writer sbufs sequences html.private ;
IN: html.tests IN: html.tests
: make-html-string : make-html-string
[ with-html-stream ] with-string-writer ; [ with-html-stream ] with-string-writer ; inline
[ [ ] make-html-string ] must-infer
[ ] [ [ ] [
512 <sbuf> <html-stream> drop 512 <sbuf> <html-stream> drop

View File

@ -194,7 +194,7 @@ M: html-stream stream-nl ( stream -- )
! Utilities ! Utilities
: with-html-stream ( quot -- ) : with-html-stream ( quot -- )
stdio get <html-stream> swap with-stream* ; stdio get <html-stream> swap with-stream* ; inline
: xhtml-preamble : xhtml-preamble
"<?xml version=\"1.0\"?>" write-html "<?xml version=\"1.0\"?>" write-html

View File

@ -1,5 +1,6 @@
USING: http tools.test multiline tuple-syntax USING: http tools.test multiline tuple-syntax
io.streams.string kernel arrays splitting sequences ; io.streams.string kernel arrays splitting sequences
assocs io.sockets ;
IN: http.tests IN: http.tests
[ "hello%20world" ] [ "hello world" url-encode ] unit-test [ "hello%20world" ] [ "hello world" url-encode ] unit-test
@ -139,7 +140,9 @@ io.encodings.ascii ;
<action> <action>
[ stop-server "text/html" <content> [ "Goodbye" write ] >>body ] >>display [ stop-server "text/html" <content> [ "Goodbye" write ] >>body ] >>display
"quit" add-responder "quit" add-responder
<dispatcher>
"extra/http/test" resource-path <static> >>default "extra/http/test" resource-path <static> >>default
"nested" add-responder
main-responder set main-responder set
[ 1237 httpd ] "HTTPD test" spawn drop [ 1237 httpd ] "HTTPD test" spawn drop
@ -148,7 +151,17 @@ io.encodings.ascii ;
[ t ] [ [ t ] [
"extra/http/test/foo.html" resource-path ascii file-contents "extra/http/test/foo.html" resource-path ascii file-contents
"http://localhost:1237/foo.html" http-get = "http://localhost:1237/nested/foo.html" http-get =
] unit-test
! Try with a slightly malformed request
[ t ] [
"localhost" 1237 <inet> ascii <client> [
"GET nested HTTP/1.0\r\n" write flush
"\r\n" write flush
readln drop
read-header USE: prettyprint
] with-stream dup . "location" swap at "/" head?
] unit-test ] unit-test
[ "Goodbye" ] [ [ "Goodbye" ] [

View File

@ -394,14 +394,17 @@ body ;
[ unparse-cookies "set-cookie" pick set-at ] when* [ unparse-cookies "set-cookie" pick set-at ] when*
write-header ; write-header ;
: write-response-body ( response -- response ) : body>quot ( body -- quot )
dup body>> { {
{ [ dup not ] [ drop ] } { [ dup not ] [ drop [ ] ] }
{ [ dup string? ] [ write ] } { [ dup string? ] [ [ write ] curry ] }
{ [ dup callable? ] [ call ] } { [ dup callable? ] [ ] }
[ stdio get stream-copy ] [ [ stdio get stream-copy ] curry ]
} cond ; } cond ;
: write-response-body ( response -- response )
dup body>> body>quot call ;
M: response write-response ( respose -- ) M: response write-response ( respose -- )
write-response-version write-response-version
write-response-code write-response-code

View File

@ -0,0 +1,9 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<h1><t:write-title /></h1>
<t:call-next-template />
</t:chloe>

View File

@ -1,77 +0,0 @@
<% USING: http.server.components http.server.auth.login
http.server namespaces kernel combinators ; %>
<html>
<body>
<h1>Edit profile</h1>
<form method="POST" action="edit-profile">
<% hidden-form-field %>
<table>
<tr>
<td>User name:</td>
<td><% "username" component render-view %></td>
</tr>
<tr>
<td>Real name:</td>
<td><% "realname" component render-edit %></td>
</tr>
<tr>
<td></td>
<td>Specifying a real name is optional.</td>
</tr>
<tr>
<td>Current password:</td>
<td><% "password" component render-edit %></td>
</tr>
<tr>
<td></td>
<td>If you don't want to change your current password, leave this field blank.</td>
</tr>
<tr>
<td>New password:</td>
<td><% "new-password" component render-edit %></td>
</tr>
<tr>
<td>Verify:</td>
<td><% "verify-password" component render-edit %></td>
</tr>
<tr>
<td></td>
<td>If you are changing your password, enter it twice to ensure it is correct.</td>
</tr>
<tr>
<td>E-mail:</td>
<td><% "email" component render-edit %></td>
</tr>
<tr>
<td></td>
<td>Specifying an e-mail address is optional. It enables the "recover password" feature.</td>
</tr>
</table>
<p><input type="submit" value="Update" />
<% {
{ [ login-failed? get ] [ "invalid password" render-error ] }
{ [ password-mismatch? get ] [ "passwords do not match" render-error ] }
{ [ t ] [ ] }
} cond %>
</p>
</form>
</body>
</html>

View File

@ -0,0 +1,77 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:title>Edit Profile</t:title>
<t:form action="edit-profile">
<table>
<tr>
<th class="field-label">User name:</th>
<td><t:view component="username" /></td>
</tr>
<tr>
<th class="field-label">Real name:</th>
<td><t:edit component="realname" /></td>
</tr>
<tr>
<td></td>
<td>Specifying a real name is optional.</td>
</tr>
<tr>
<th class="field-label">Current password:</th>
<td><t:edit component="password" /></td>
</tr>
<tr>
<td></td>
<td>If you don't want to change your current password, leave this field blank.</td>
</tr>
<tr>
<th class="field-label">New password:</th>
<td><t:edit component="new-password" /></td>
</tr>
<tr>
<th class="field-label">Verify:</th>
<td><t:edit component="verify-password" /></td>
</tr>
<tr>
<td></td>
<td>If you are changing your password, enter it twice to ensure it is correct.</td>
</tr>
<tr>
<th class="field-label">E-mail:</th>
<td><t:edit component="email" /></td>
</tr>
<tr>
<td></td>
<td>Specifying an e-mail address is optional. It enables the "recover password" feature.</td>
</tr>
</table>
<p>
<input type="submit" value="Update" />
<t:if var="http.server.auth.login:login-failed?">
<t:error>invalid password</t:error>
</t:if>
<t:if var="http.server.auth.login:password-mismatch?">
<t:error>passwords do not match</t:error>
</t:if>
</p>
</t:form>
</t:chloe>

View File

@ -1,20 +1,31 @@
! Copyright (c) 2008 Slava Pestov ! Copyright (c) 2008 Slava Pestov
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: accessors quotations assocs kernel splitting USING: accessors quotations assocs kernel splitting
base64 html.elements io combinators http.server base64 io combinators sequences io.files namespaces hashtables
http.server.auth.providers http.server.auth.providers.null
http.server.actions http.server.components http.server.sessions
http.server.templating.fhtml http.server.validators
http.server.auth http sequences io.files namespaces hashtables
fry io.sockets arrays threads locals qualified continuations fry io.sockets arrays threads locals qualified continuations
destructors ; destructors
html.elements
http
http.server
http.server.auth
http.server.auth.providers
http.server.auth.providers.null
http.server.actions
http.server.components
http.server.forms
http.server.sessions
http.server.boilerplate
http.server.templating
http.server.templating.chloe
http.server.validators ;
IN: http.server.auth.login IN: http.server.auth.login
QUALIFIED: smtp QUALIFIED: smtp
SYMBOL: post-login-url SYMBOL: post-login-url
SYMBOL: login-failed? SYMBOL: login-failed?
TUPLE: login users ; TUPLE: login < dispatcher users ;
: users login get users>> ; : users login get users>> ;
@ -31,11 +42,15 @@ M: user-saver dispose
: save-user-after ( user -- ) : save-user-after ( user -- )
<user-saver> add-always-destructor ; <user-saver> add-always-destructor ;
: login-template ( name -- template )
"resource:extra/http/server/auth/login/" swap ".xml"
3append <chloe> ;
! ! ! Login ! ! ! Login
: <login-form> : <login-form>
"login" <form> "login" <form>
"resource:extra/http/server/auth/login/login.fhtml" >>edit-template "login" login-template >>edit-template
"username" <username> "username" <username>
t >>required t >>required
add-field add-field
@ -77,7 +92,7 @@ M: user-saver dispose
: <register-form> ( -- form ) : <register-form> ( -- form )
"register" <form> "register" <form>
"resource:extra/http/server/auth/login/register.fhtml" >>edit-template "register" login-template >>edit-template
"username" <username> "username" <username>
t >>required t >>required
add-field add-field
@ -130,7 +145,7 @@ SYMBOL: user-exists?
successful-login successful-login
login get responder>> init-user-profile login get default>> responder>> init-user-profile
] >>submit ] >>submit
] ; ] ;
@ -138,7 +153,7 @@ SYMBOL: user-exists?
: <edit-profile-form> ( -- form ) : <edit-profile-form> ( -- form )
"edit-profile" <form> "edit-profile" <form>
"resource:extra/http/server/auth/login/edit-profile.fhtml" >>edit-template "edit-profile" login-template >>edit-template
"username" <username> add-field "username" <username> add-field
"realname" <string> add-field "realname" <string> add-field
"password" <password> add-field "password" <password> add-field
@ -178,7 +193,7 @@ SYMBOL: previous-page
"password" value uid users check-login "password" value uid users check-login
[ login-failed? on validation-failed ] unless [ login-failed? on validation-failed ] unless
"new-password" value set-password "new-password" value >>password
] unless ] unless
"realname" value >>realname "realname" value >>realname
@ -233,7 +248,7 @@ SYMBOL: lost-password-from
: <recover-form-1> ( -- form ) : <recover-form-1> ( -- form )
"register" <form> "register" <form>
"resource:extra/http/server/auth/login/recover-1.fhtml" >>edit-template "recover-1" login-template >>edit-template
"username" <username> "username" <username>
t >>required t >>required
add-field add-field
@ -262,14 +277,15 @@ SYMBOL: lost-password-from
send-password-email send-password-email
] when* ] when*
"resource:extra/http/server/auth/login/recover-2.fhtml" serve-template "recover-2" login-template serve-template
] >>submit ] >>submit
] ; ] ;
: <recover-form-3> : <recover-form-3>
"new-password" <form> "new-password" <form>
"resource:extra/http/server/auth/login/recover-3.fhtml" >>edit-template "recover-3" login-template >>edit-template
"username" <username> <hidden> "username" <username>
hidden >>renderer
t >>required t >>required
add-field add-field
"new-password" <password> "new-password" <password>
@ -278,7 +294,8 @@ SYMBOL: lost-password-from
"verify-password" <password> "verify-password" <password>
t >>required t >>required
add-field add-field
"ticket" <string> <hidden> "ticket" <string>
hidden >>renderer
t >>required t >>required
add-field ; add-field ;
@ -315,8 +332,7 @@ SYMBOL: lost-password-from
"new-password" value >>password "new-password" value >>password
users update-user users update-user
"resource:extra/http/server/auth/login/recover-4.fhtml" "recover-4" login-template serve-template
serve-template
] [ ] [
<400> <400>
] if* ] if*
@ -342,38 +358,46 @@ C: <protected> protected
"login" f <permanent-redirect> ; "login" f <permanent-redirect> ;
M: protected call-responder ( path responder -- response ) M: protected call-responder ( path responder -- response )
logged-in-user sget [ logged-in-user sget dup [
dup save-user-after save-user-after
request get request-url previous-page sset request get request-url previous-page sset
responder>> call-responder responder>> call-responder
] [ ] [
2drop 3drop
request get method>> { "GET" "HEAD" } member? request get method>> { "GET" "HEAD" } member?
[ show-login-page ] [ <400> ] if [ show-login-page ] [ <400> ] if
] if ; ] if ;
M: login call-responder ( path responder -- response ) M: login call-responder ( path responder -- response )
dup login set dup login set
delegate call-responder ; call-next-method ;
: <login-boilerplate> ( responder -- responder' )
<boilerplate>
"boilerplate" login-template >>template ;
: <login> ( responder -- auth ) : <login> ( responder -- auth )
login <webapp> login new-dispatcher
swap <protected> >>default swap <protected> >>default
<login-action> "login" add-responder <login-action> <login-boilerplate> "login" add-responder
<logout-action> "logout" add-responder <logout-action> <login-boilerplate> "logout" add-responder
no-users >>users ; no-users >>users ;
! ! ! Configuration ! ! ! Configuration
: allow-edit-profile ( login -- login ) : allow-edit-profile ( login -- login )
<edit-profile-action> <protected> "edit-profile" add-responder ; <edit-profile-action> <protected> <login-boilerplate>
"edit-profile" add-responder ;
: allow-registration ( login -- login ) : allow-registration ( login -- login )
<register-action> "register" add-responder ; <register-action> <login-boilerplate>
"register" add-responder ;
: allow-password-recovery ( login -- login ) : allow-password-recovery ( login -- login )
<recover-action-1> "recover-password" add-responder <recover-action-1> <login-boilerplate>
<recover-action-3> "new-password" add-responder ; "recover-password" add-responder
<recover-action-3> <login-boilerplate>
"new-password" add-responder ;
: allow-edit-profile? ( -- ? ) : allow-edit-profile? ( -- ? )
login get responders>> "edit-profile" swap key? ; login get responders>> "edit-profile" swap key? ;

View File

@ -1,46 +0,0 @@
<% USING: http.server.auth.login http.server.components http.server
kernel namespaces ; %>
<html>
<body>
<h1>Login required</h1>
<form method="POST" action="login">
<% hidden-form-field %>
<table>
<tr>
<td>User name:</td>
<td><% "username" component render-edit %></td>
</tr>
<tr>
<td>Password:</td>
<td><% "password" component render-edit %></td>
</tr>
</table>
<p><input type="submit" value="Log in" />
<%
login-failed? get
[ "Invalid username or password" render-error ] when
%>
</p>
</form>
<p>
<% allow-registration? [ %>
<a href="<% "register" f write-link %>">Register</a>
<% ] when %>
<% allow-password-recovery? [ %>
<a href="<% "recover-password" f write-link %>">
Recover Password
</a>
<% ] when %>
</p>
</body>
</html>

View File

@ -0,0 +1,44 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:title>Login</t:title>
<t:form action="login">
<table>
<tr>
<th class="field-label">User name:</th>
<td><t:edit component="username" /></td>
</tr>
<tr>
<th class="field-label">Password:</th>
<td><t:edit component="password" /></td>
</tr>
</table>
<p>
<input type="submit" value="Log in" />
<t:if var="http.server.auth.login:login-failed?">
<t:error>invalid username or password</t:error>
</t:if>
</p>
</t:form>
<p>
<t:if code="http.server.auth.login:login-failed?">
<t:a href="register">Register</t:a>
</t:if>
|
<t:if code="http.server.auth.login:allow-password-recovery?">
<t:a href="recover-password">Recover Password</t:a>
</t:if>
</p>
</t:chloe>

View File

@ -1,41 +0,0 @@
<% USING: http.server.components http.server ; %>
<html>
<body>
<h1>Recover lost password: step 1 of 4</h1>
<p>Enter the username and e-mail address you used to register for this site, and you will receive a link for activating a new password.</p>
<form method="POST" action="recover-password">
<% hidden-form-field %>
<table>
<tr>
<td>User name:</td>
<td><% "username" component render-edit %></td>
</tr>
<tr>
<td>E-mail:</td>
<td><% "email" component render-edit %></td>
</tr>
<tr>
<td>Captcha:</td>
<td><% "captcha" component render-edit %></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>
<input type="submit" value="Recover password" />
</form>
</body>
</html>

View File

@ -0,0 +1,39 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:title>Recover lost password: step 1 of 4</t:title>
<p>Enter the username and e-mail address you used to register for this site, and you will receive a link for activating a new password.</p>
<t:form action="recover-password">
<table>
<tr>
<th class="field-label">User name:</th>
<td><t:edit component="username" /></td>
</tr>
<tr>
<th class="field-label">E-mail:</th>
<td><t:edit component="email" /></td>
</tr>
<tr>
<th class="field-label">Captcha:</th>
<td><t:edit 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>
</table>
<input type="submit" value="Recover password" />
</t:form>
</t:chloe>

View File

@ -1,9 +0,0 @@
<% USING: http.server.components ; %>
<html>
<body>
<h1>Recover lost password: step 2 of 4</h1>
<p>If you entered the correct username and e-mail address, you should receive an e-mail shortly. Click the link in the e-mail for further instructions.</p>
</body>
</html>

View File

@ -0,0 +1,9 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:title>Recover lost password: step 2 of 4</t:title>
<p>If you entered the correct username and e-mail address, you should receive an e-mail shortly. Click the link in the e-mail for further instructions.</p>
</t:chloe>

View File

@ -1,46 +0,0 @@
<% USING: http.server.components http.server.auth.login http.server
namespaces kernel combinators ; %>
<html>
<body>
<h1>Recover lost password: step 3 of 4</h1>
<p>Choose a new password for your account.</p>
<form method="POST" action="new-password">
<% hidden-form-field %>
<table>
<% "username" component render-edit %>
<% "ticket" component render-edit %>
<tr>
<td>Password:</td>
<td><% "new-password" component render-edit %></td>
</tr>
<tr>
<td>Verify password:</td>
<td><% "verify-password" component render-edit %></td>
</tr>
<tr>
<td></td>
<td>Enter your password twice to ensure it is correct.</td>
</tr>
</table>
<p><input type="submit" value="Set password" />
<% password-mismatch? get [
"passwords do not match" render-error
] when %>
</p>
</form>
</body>
</html>

View File

@ -0,0 +1,43 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:title>Recover lost password: step 3 of 4</t:title>
<p>Choose a new password for your account.</p>
<t:form action="new-password">
<table>
<t:edit component="username" />
<t:edit component="ticket" />
<tr>
<th class="field-label">Password:</th>
<td><t:edit component="new-password" /></td>
</tr>
<tr>
<th class="field-label">Verify password:</th>
<td><t:edit component="verify-password" /></td>
</tr>
<tr>
<td></td>
<td>Enter your password twice to ensure it is correct.</td>
</tr>
</table>
<p>
<input type="submit" value="Set password" />
<t:if var="http.server.auth.login:password-mismatch?">
<t:error>passwords do not match</t:error>
</t:if>
</p>
</t:form>
</t:chloe>

View File

@ -1,10 +0,0 @@
<% USING: http.server ; %>
<html>
<body>
<h1>Recover lost password: step 4 of 4</h1>
<p>Your password has been reset.
You may now <a href="<% "login" f write-link %>">log in</a>.</p>
</body>
</html>

View File

@ -0,0 +1,9 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:title>Recover lost password: step 4 of 4</t:title>
<p>Your password has been reset. You may now <t:a href="login">log in</t:a>.</p>
</t:chloe>

View File

@ -1,77 +0,0 @@
<% USING: http.server.components http.server.auth.login
http.server namespaces kernel combinators ; %>
<html>
<body>
<h1>New user registration</h1>
<form method="POST" action="register">
<% hidden-form-field %>
<table>
<tr>
<td>User name:</td>
<td><% "username" component render-edit %></td>
</tr>
<tr>
<td>Real name:</td>
<td><% "realname" component render-edit %></td>
</tr>
<tr>
<td></td>
<td>Specifying a real name is optional.</td>
</tr>
<tr>
<td>Password:</td>
<td><% "new-password" component render-edit %></td>
</tr>
<tr>
<td>Verify:</td>
<td><% "verify-password" component render-edit %></td>
</tr>
<tr>
<td></td>
<td>Enter your password twice to ensure it is correct.</td>
</tr>
<tr>
<td>E-mail:</td>
<td><% "email" component render-edit %></td>
</tr>
<tr>
<td></td>
<td>Specifying an e-mail address is optional. It enables the "recover password" feature.</td>
</tr>
<tr>
<td>Captcha:</td>
<td><% "captcha" component render-edit %></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" />
<% {
{ [ password-mismatch? get ] [ "passwords do not match" render-error ] }
{ [ user-exists? get ] [ "username taken" render-error ] }
{ [ t ] [ ] }
} cond %>
</p>
</form>
</body>
</html>

View File

@ -0,0 +1,79 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:title>New User Registration</t:title>
<t:form action="register">
<table>
<tr>
<th class="field-label">User name:</th>
<td><t:edit component="username" /></td>
</tr>
<tr>
<th class="field-label">Real name:</th>
<td><t:edit 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 component="new-password" /></td>
</tr>
<tr>
<th class="field-label">Verify:</th>
<td><t:edit 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 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 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>
<p>
<input type="submit" value="Register" />
<t:if var="http.server.auth.login:user-exists?">
<t:error>username taken</t:error>
</t:if>
<t:if var="http.server.auth.login:password-mismatch?">
<t:error>passwords do not match</t:error>
</t:if>
</p>
</t:form>
</t:chloe>

View File

@ -26,7 +26,7 @@ namespaces accessors kernel ;
[ t ] [ "user" get >boolean ] unit-test [ t ] [ "user" get >boolean ] unit-test
[ ] [ "user" get "fdasf" set-password drop ] unit-test [ ] [ "user" get "fdasf" >>password drop ] unit-test
[ t ] [ "fdasf" "slava" "provider" get check-login >boolean ] unit-test [ t ] [ "fdasf" "slava" "provider" get check-login >boolean ] unit-test

View File

@ -31,7 +31,7 @@ users-in-db "provider" set
[ t ] [ "user" get >boolean ] unit-test [ t ] [ "user" get >boolean ] unit-test
[ ] [ "user" get "fdasf" set-password drop ] unit-test [ ] [ "user" get "fdasf" >>password drop ] unit-test
[ ] [ "user" get "provider" get update-user ] unit-test [ ] [ "user" get "provider" get update-user ] unit-test

View File

@ -17,8 +17,6 @@ GENERIC: new-user ( user provider -- user/f )
: check-login ( password username provider -- user/f ) : check-login ( password username provider -- user/f )
get-user dup [ [ password>> = ] keep and ] [ 2drop f ] if ; get-user dup [ [ password>> = ] keep and ] [ 2drop f ] if ;
: set-password ( user password -- user ) >>password ;
! Password recovery support ! Password recovery support
:: issue-ticket ( email username provider -- user/f ) :: issue-ticket ( email username provider -- user/f )

View File

@ -0,0 +1,49 @@
! 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
http.server
http.server.templating ;
IN: http.server.boilerplate
TUPLE: boilerplate 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: next-template
: call-next-template ( -- )
next-template get write ;
M: f call-template drop call-next-template ;
: with-boilerplate ( body template -- )
[
title get [ <box> title set ] unless
style get [ SBUF" " clone style set ] unless
swap with-string-writer next-template set
call-template
] with-scope ; inline
M: boilerplate call-responder
[ responder>> call-responder clone ] [ template>> ] bi
[ [ with-boilerplate ] 2curry ] curry change-body ;

View File

@ -1,7 +1,11 @@
IN: http.server.components.tests IN: http.server.components.tests
USING: http.server.components http.server.validators USING: http.server.components http.server.forms
namespaces tools.test kernel accessors http.server.validators namespaces tools.test kernel accessors
tuple-syntax mirrors http.server.actions ; tuple-syntax mirrors http.server.actions
http.server.templating.fhtml
io.streams.string io.streams.null ;
\ render-edit must-infer
validation-failed? off validation-failed? off
@ -46,8 +50,8 @@ TUPLE: test-tuple text number more-text ;
: <test-form> ( -- form ) : <test-form> ( -- form )
"test" <form> "test" <form>
"resource:extra/http/server/components/test/form.fhtml" >>view-template "resource:extra/http/server/components/test/form.fhtml" <fhtml> >>view-template
"resource:extra/http/server/components/test/form.fhtml" >>edit-template "resource:extra/http/server/components/test/form.fhtml" <fhtml> >>edit-template
"text" <string> "text" <string>
t >>required t >>required
add-field add-field
@ -99,11 +103,31 @@ TUPLE: test-tuple text number more-text ;
"123" "n" get validate value>> "123" "n" get validate value>>
] unit-test ] unit-test
[ ] [ "n" get t >>integer drop ] unit-test [ ] [ "i" <integer> "i" set ] unit-test
[ 3 ] [ [ 3 ] [
"3" "n" get validate "3" "i" get validate
] unit-test ] unit-test
[ t ] [
"3.9" "i" get validate validation-error?
] unit-test
H{ } clone values set
[ ] [ 3 "i" set-value ] unit-test
[ "3" ] [ [ "i" get render-view ] with-string-writer ] unit-test
[ ] [ [ "i" get render-edit ] with-null-stream ] unit-test
[ ] [ "t" <text> "t" set ] unit-test
[ ] [ "hello world" "t" set-value ] unit-test
[ ] [ [ "t" get render-edit ] with-null-stream ] unit-test
] with-scope ] with-scope
[ t ] [ "wake up sheeple" dup "n" <text> validate = ] unit-test [ t ] [ "wake up sheeple" dup "n" <text> validate = ] unit-test
[ ] [ "password" <password> "p" set ] unit-test

View File

@ -2,23 +2,47 @@
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: html.elements http.server.validators accessors namespaces USING: html.elements http.server.validators accessors namespaces
kernel io math.parser assocs classes words classes.tuple arrays kernel io math.parser assocs classes words classes.tuple arrays
sequences io.files http.server.templating.fhtml sequences splitting mirrors hashtables fry combinators
http.server.actions splitting mirrors hashtables fry
continuations math ; continuations math ;
IN: http.server.components IN: http.server.components
! Renderer protocol
GENERIC: render-view* ( value renderer -- )
GENERIC: render-edit* ( value id renderer -- )
TUPLE: field type ;
C: <field> field
M: field render-view* drop write ;
M: field render-edit*
<input type>> =type [ =id ] [ =name ] bi =value input/> ;
: render-error ( message -- )
<span "error" =class span> write </span> ;
TUPLE: hidden < field ;
: hidden ( -- renderer ) T{ hidden f "hidden" } ; inline
M: hidden render-view* 2drop ;
! Component protocol
SYMBOL: components SYMBOL: components
TUPLE: component id required default ; TUPLE: component id required default renderer ;
: component ( name -- component ) : component ( name -- component )
dup components get at dup components get at
[ ] [ "No such component: " prepend throw ] ?if ; [ ] [ "No such component: " prepend throw ] ?if ;
GENERIC: init ( component -- component )
M: component init ;
GENERIC: validate* ( value component -- result ) GENERIC: validate* ( value component -- result )
GENERIC: render-view* ( value component -- ) GENERIC: component-string ( value component -- string )
GENERIC: render-edit* ( value component -- )
GENERIC: render-error* ( reason value component -- )
SYMBOL: values SYMBOL: values
@ -26,6 +50,41 @@ SYMBOL: values
: set-value values get set-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 ( component -- )
[ id>> value ] [ component-string ] [ renderer>> ] tri
render-view* ;
<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 ) : validate ( value component -- result )
'[ '[
, ,
@ -36,206 +95,143 @@ SYMBOL: values
] [ validate* ] if ] [ validate* ] if
] with-validator ; ] with-validator ;
: render-view ( component -- ) : new-component ( id class renderer -- component )
[ id>> value ] [ render-view* ] bi ; swap new
swap >>renderer
: render-error ( error -- ) swap >>id
<span "error" =class span> write </span> ; init ; inline
: render-edit ( component -- )
dup id>> value dup validation-error? [
[ reason>> ] [ value>> ] bi rot render-error*
] [
swap [ default>> or ] keep render-edit*
] if ;
: <component> ( id class -- component )
\ component new
swap construct-delegate
swap >>id ; inline
! Forms
TUPLE: form view-template edit-template components ;
: <form> ( id -- form )
form <component>
V{ } clone >>components ;
: add-field ( form component -- form )
dup id>> pick components>> set-at ;
: with-form ( form quot -- )
>r components>> components r> with-variable ; inline
: set-defaults ( form -- )
[
components get [
swap values get [
swap default>> or
] change-at
] assoc-each
] with-form ;
: view-form ( form -- )
dup view-template>> '[ , run-template ] with-form ;
: edit-form ( form -- )
dup edit-template>> '[ , run-template ] with-form ;
: 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 ;
: blank-values H{ } clone values set ;
: from-tuple <mirror> values set ;
: values-tuple values get mirror-object ;
! ! !
! Canned components: for simple applications and prototyping
! ! !
: render-input ( value component type -- )
<input
=type
id>> [ =id ] [ =name ] bi
=value
input/> ;
! Hidden fields
TUPLE: hidden ;
: <hidden> ( component -- component )
hidden construct-delegate ;
M: hidden render-view*
2drop ;
M: hidden render-edit*
>r dup number? [ number>string ] when r>
"hidden" render-input ;
! String input fields ! String input fields
TUPLE: string min-length max-length ; TUPLE: string < component one-line min-length max-length ;
: <string> ( id -- component ) string <component> ; : new-string ( id class -- component )
"text" <field> new-component
t >>one-line ; inline
: <string> ( id -- component )
string new-string ;
M: string validate* M: string validate*
[ v-one-line ] [ [ one-line>> [ v-one-line ] when ]
[ min-length>> [ v-min-length ] when* ] [ min-length>> [ v-min-length ] when* ]
[ max-length>> [ v-max-length ] when* ] [ max-length>> [ v-max-length ] when* ]
bi tri ;
] bi* ;
M: string render-view* M: string component-string
drop write ; drop ;
M: string render-edit*
"text" render-input ;
M: string render-error*
"text" render-input render-error ;
! Username fields ! Username fields
TUPLE: username ; TUPLE: username < string ;
: <username> ( id -- component ) M: username init
<string> username construct-delegate
2 >>min-length 2 >>min-length
20 >>max-length ; 20 >>max-length ;
: <username> ( id -- component )
username new-string ;
M: username validate* M: username validate*
delegate validate* v-one-word ; call-next-method v-one-word ;
! E-mail fields ! E-mail fields
TUPLE: email ; TUPLE: email < string ;
: <email> ( id -- component ) : <email> ( id -- component )
<string> email construct-delegate email new-string
5 >>min-length 5 >>min-length
60 >>max-length ; 60 >>max-length ;
M: email validate* M: email validate*
delegate validate* dup empty? [ v-email ] unless ; call-next-method dup empty? [ v-email ] 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 ! Password fields
TUPLE: password ; TUPLE: password < string ;
: <password> ( id -- component ) M: password init
<string> password construct-delegate
6 >>min-length 6 >>min-length
60 >>max-length ; 60 >>max-length ;
: <password> ( id -- component )
password new-string
password-renderer >>renderer ;
M: password validate* M: password validate*
delegate validate* v-one-word ; call-next-method v-one-word ;
M: password render-edit*
>r drop f r> "password" render-input ;
M: password render-error*
render-edit* render-error ;
! Number fields ! Number fields
TUPLE: number min-value max-value integer ; TUPLE: number < string min-value max-value ;
: <number> ( id -- component ) number <component> ; : <number> ( id -- component )
number new-string ;
M: number validate* M: number validate*
[ v-number ] [ [ v-number ] [
[ integer>> [ v-integer ] when ]
[ min-value>> [ v-min-value ] when* ] [ min-value>> [ v-min-value ] when* ]
[ max-value>> [ v-max-value ] when* ] [ max-value>> [ v-max-value ] when* ]
tri bi
] bi* ; ] bi* ;
M: number render-view* M: number component-string
drop number>string write ; drop dup [ number>string ] when ;
M: number render-edit* ! Integer fields
>r number>string r> "text" render-input ; TUPLE: integer < number ;
M: number render-error* : <integer> ( id -- component )
"text" render-input render-error ; 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 ! Text areas
TUPLE: text ; TUPLE: textarea-renderer rows cols ;
: <text> ( id -- component ) text <component> ; : new-textarea-renderer ( class -- renderer )
new
60 >>cols
20 >>rows ;
M: text validate* drop ; : <textarea-renderer> ( -- renderer )
textarea-renderer new-textarea-renderer ;
M: text render-view* M: textarea-renderer render-view*
drop write ; drop write ;
: render-textarea M: textarea-renderer render-edit*
<textarea <textarea
id>> [ =id ] [ =name ] bi [ rows>> [ number>string =rows ] when* ]
[ cols>> [ number>string =cols ] when* ] bi
[ =id ]
[ =name ] bi
textarea> textarea>
write write
</textarea> ; </textarea> ;
M: text render-edit* TUPLE: text < string ;
render-textarea ;
M: text render-error* : new-text ( id class -- component )
render-textarea render-error ; new-string
f >>one-line
<textarea-renderer> >>renderer ;
! Simple captchas : <text> ( id -- component )
TUPLE: captcha ; text new-text ;
: <captcha> ( id -- component )
<string> captcha construct-delegate ;
M: captcha validate*
drop v-captcha ;

View File

@ -1,13 +1,17 @@
! Copyright (C) 2008 Slava Pestov ! Copyright (C) 2008 Slava Pestov
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: splitting http.server.components kernel io sequences USING: splitting kernel io sequences farkup accessors
farkup ; http.server.components ;
IN: http.server.components.farkup IN: http.server.components.farkup
TUPLE: farkup ; TUPLE: farkup-renderer < textarea-renderer ;
: <farkup-renderer>
farkup-renderer new-textarea-renderer ;
M: farkup-renderer render-view*
drop string-lines "\n" join convert-farkup write ;
: <farkup> ( id -- component ) : <farkup> ( id -- component )
<text> farkup construct-delegate ; <text>
<farkup-renderer> >>renderer ;
M: farkup render-view*
drop string-lines "\n" join convert-farkup write ;

View File

@ -1,9 +1,13 @@
! Copyright (C) 2008 Slava Pestov. ! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license. ! 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 IN: http.server.crud
USING: kernel namespaces db.tuples math.parser http.server
http.server.actions http.server.components
http.server.validators accessors fry locals hashtables ;
:: <view-action> ( form ctor -- action ) :: <view-action> ( form ctor -- action )
<action> <action>
@ -19,29 +23,18 @@ http.server.validators accessors fry locals hashtables ;
: <id-redirect> ( id next -- response ) : <id-redirect> ( id next -- response )
swap number>string "id" associate <permanent-redirect> ; swap number>string "id" associate <permanent-redirect> ;
:: <create-action> ( form ctor next -- action )
<action>
[ f ctor call from-tuple form set-defaults ] >>init
[
"text/html" <content>
[ form edit-form ] >>body
] >>display
[
f ctor call from-tuple
form validate-form
values-tuple insert-tuple
"id" value next <id-redirect>
] >>submit ;
:: <edit-action> ( form ctor next -- action ) :: <edit-action> ( form ctor next -- action )
<action> <action>
{ { "id" [ v-number ] } } >>get-params { { "id" [ [ v-number ] v-optional ] } } >>get-params
[ "id" get ctor call select-tuple from-tuple ] >>init
[
"id" get ctor call
"id" get
[ select-tuple from-tuple ]
[ from-tuple form set-defaults ]
if
] >>init
[ [
"text/html" <content> "text/html" <content>
@ -53,7 +46,8 @@ http.server.validators accessors fry locals hashtables ;
form validate-form form validate-form
values-tuple update-tuple values-tuple
"id" value [ update-tuple ] [ insert-tuple ] if
"id" value next <id-redirect> "id" value next <id-redirect>
] >>submit ; ] >>submit ;
@ -67,3 +61,16 @@ http.server.validators accessors fry locals hashtables ;
next f <permanent-redirect> next f <permanent-redirect>
] >>submit ; ] >>submit ;
:: <list-action> ( form ctor -- action )
<action>
[
"text/html" <content>
[
blank-values
f ctor call select-tuples "list" set-value
form view-form
] >>body
] >>display ;

View File

@ -9,8 +9,8 @@ TUPLE: db-persistence responder db params ;
C: <db-persistence> db-persistence C: <db-persistence> db-persistence
: connect-db ( db-persistence -- ) : connect-db ( db-persistence -- )
[ db>> ] [ params>> ] bi make-db [ db>> ] [ params>> ] bi make-db db-open
[ db set ] [ db-open ] [ add-always-destructor ] tri ; [ db set ] [ add-always-destructor ] bi ;
M: db-persistence call-responder M: db-persistence call-responder
[ connect-db ] [ responder>> call-responder ] bi ; [ connect-db ] [ responder>> call-responder ] bi ;

View File

@ -0,0 +1,70 @@
USING: kernel accessors assocs namespaces io.files sequences fry
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 ;
: add-field ( form component -- form )
dup id>> pick components>> set-at ;
: with-form ( form quot -- )
>r components>> components r> with-variable ; inline
: set-defaults ( form -- )
[
components get [
swap values get [
swap default>> or
] change-at
] assoc-each
] with-form ;
: view-form ( form -- )
dup view-template>> '[ , call-template ] with-form ;
: edit-form ( form -- )
dup edit-template>> '[ , call-template ] with-form ;
: summary-form ( form -- )
dup summary-template>> '[ , call-template ] with-form ;
: 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 ;
! List components
TUPLE: list-renderer form ;
C: <list-renderer> list-renderer
M: list-renderer render-view*
form>> [
[ >r from-tuple r> summary-form ] with-scope
] curry each ;
TUPLE: list < component ;
: <list> ( id form -- list )
list swap <list-renderer> new-component ;
M: list component-string drop ;

View File

@ -105,8 +105,13 @@ SYMBOL: form-hook
TUPLE: dispatcher default responders ; TUPLE: dispatcher default responders ;
: new-dispatcher ( class -- dispatcher )
new
404-responder get >>default
H{ } clone >>responders ; inline
: <dispatcher> ( -- dispatcher ) : <dispatcher> ( -- dispatcher )
404-responder get H{ } clone dispatcher boa ; dispatcher new-dispatcher ;
: split-path ( path -- rest first ) : split-path ( path -- rest first )
[ CHAR: / = ] left-trim "/" split1 swap ; [ CHAR: / = ] left-trim "/" split1 swap ;
@ -125,9 +130,6 @@ M: dispatcher call-responder ( path dispatcher -- response )
2drop redirect-with-/ 2drop redirect-with-/
] if ; ] if ;
: <webapp> ( class -- dispatcher )
<dispatcher> swap construct-delegate ; inline
TUPLE: vhost-dispatcher default responders ; TUPLE: vhost-dispatcher default responders ;
: <vhost-dispatcher> ( -- dispatcher ) : <vhost-dispatcher> ( -- dispatcher )

View File

@ -17,9 +17,10 @@ M: object init-session* drop ;
TUPLE: session-manager responder sessions ; TUPLE: session-manager responder sessions ;
: <session-manager> ( responder class -- responder' ) : new-session-manager ( responder class -- responder' )
>r <sessions-in-memory> session-manager boa new
r> construct-delegate ; inline <sessions-in-memory> >>sessions
swap >>responder ; inline
SYMBOLS: session session-id session-changed? ; SYMBOLS: session session-id session-changed? ;
@ -64,18 +65,18 @@ M: session-saver dispose
[ [ session-id set ] [ session set ] bi* ] 2bi [ [ session-id set ] [ session set ] bi* ] 2bi
[ session-manager set ] [ responder>> call-responder ] bi ; [ session-manager set ] [ responder>> call-responder ] bi ;
TUPLE: null-sessions ; TUPLE: null-sessions < session-manager ;
: <null-sessions> : <null-sessions>
null-sessions <session-manager> ; null-sessions new-session-manager ;
M: null-sessions call-responder ( path responder -- response ) M: null-sessions call-responder ( path responder -- response )
H{ } clone f call-responder/session ; H{ } clone f call-responder/session ;
TUPLE: url-sessions ; TUPLE: url-sessions < session-manager ;
: <url-sessions> ( responder -- responder' ) : <url-sessions> ( responder -- responder' )
url-sessions <session-manager> ; url-sessions new-session-manager ;
: session-id-key "factorsessid" ; : session-id-key "factorsessid" ;
@ -107,10 +108,10 @@ M: url-sessions call-responder ( path responder -- response )
2drop nip new-url-session 2drop nip new-url-session
] if ; ] if ;
TUPLE: cookie-sessions ; TUPLE: cookie-sessions < session-manager ;
: <cookie-sessions> ( responder -- responder' ) : <cookie-sessions> ( responder -- responder' )
cookie-sessions <session-manager> ; cookie-sessions new-session-manager ;
: current-cookie-session ( responder -- id namespace/f ) : current-cookie-session ( responder -- id namespace/f )
request get session-id-key get-cookie dup request get session-id-key get-cookie dup

View File

@ -0,0 +1,97 @@
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
[ "foo" ]
[ "<a href=\"foo\">blah</a>" string>xml "href" required-attr ]
unit-test
[ "<a name=\"foo\">blah</a>" string>xml "href" required-attr ]
[ "href attribute is required" = ]
must-fail-with
[ 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 ] subset
"?>" 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

View File

@ -0,0 +1,168 @@
USING: accessors kernel sequences combinators kernel namespaces
classes.tuple assocs splitting words arrays
io.files io.encodings.utf8 html.elements unicode.case
tuple-syntax xml xml.data xml.writer xml.utilities
http.server
http.server.auth
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 TUPLE{ name url: "http://factorcode.org/chloe/1.0" } ;
: chloe-tag? ( tag -- ? )
{
{ [ dup tag? not ] [ f ] }
{ [ dup chloe-ns names-match? not ] [ f ] }
[ t ]
} cond nip ;
SYMBOL: tags
: required-attr ( tag name -- value )
dup rot at*
[ nip ] [ drop " attribute is required" append throw ] if ;
: optional-attr ( tag name -- value )
swap at ;
: 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> ;
: component-attr ( tag -- name )
"component" required-attr ;
: view-tag ( tag -- )
component-attr component render-view ;
: edit-tag ( tag -- )
component-attr component render-edit ;
: parse-query-attr ( string -- assoc )
dup empty?
[ drop f ] [ "," split [ dup value ] H{ } map>assoc ] if ;
: a-start-tag ( tag -- )
<a
[ "href" required-attr ]
[ "query" optional-attr parse-query-attr ]
bi link>string =href
a> ;
: 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
tag-attrs print-attrs
form>
hidden-form-field ;
: form-tag ( tag -- )
[ form-start-tag ]
[ process-tag-children ]
[ drop </form> ]
tri ;
: 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 -- ? )
{
[ "code" optional-attr [ attr>word execute ] [ t ] if* ]
[ "var" optional-attr [ attr>var get ] [ t ] if* ]
[ "svar" optional-attr [ attr>var sget ] [ t ] if* ]
[ "uvar" optional-attr [ attr>var uget ] [ t ] if* ]
} cleave 4array [ ] all? ;
: if-tag ( tag -- )
dup if-satisfied? [ process-tag-children ] [ drop ] if ;
: error-tag ( tag -- )
children>string render-error ;
: process-chloe-tag ( tag -- )
dup name-tag {
{ "chloe" [ [ process-template ] each ] }
{ "title" [ children>string set-title ] }
{ "write-title" [ write-title-tag ] }
{ "style" [ style-tag ] }
{ "write-style" [ write-style-tag ] }
{ "view" [ view-tag ] }
{ "edit" [ edit-tag ] }
{ "a" [ a-tag ] }
{ "form" [ form-tag ] }
{ "error" [ error-tag ] }
{ "if" [ if-tag ] }
{ "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
{
[ xml-prolog write-prolog ]
[ xml-before write-chunk ]
[ process-template ]
[ xml-after write-chunk ]
} cleave
] with-scope ;
M: chloe call-template
path>> utf8 <file-reader> read-xml process-chloe ;

View File

@ -0,0 +1,5 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
Hello world
</t:chloe>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,9 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:if code="http.server.templating.chloe.tests:test4-aux?">
True
</t:if>
</t:chloe>

View File

@ -0,0 +1,9 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:if code="http.server.templating.chloe.tests:test5-aux?">
True
</t:if>
</t:chloe>

View File

@ -0,0 +1,9 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:if var="http.server.templating.chloe.tests:test6-aux?">
True
</t:if>
</t:chloe>

View File

@ -0,0 +1,9 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:if var="http.server.templating.chloe.tests:test7-aux?">
True
</t:if>
</t:chloe>

View File

@ -1,13 +1,13 @@
USING: io io.files io.streams.string io.encodings.utf8 USING: io io.files io.streams.string io.encodings.utf8
http.server.templating.fhtml kernel tools.test sequences http.server.templating http.server.templating.fhtml kernel
parser ; tools.test sequences parser ;
IN: http.server.templating.fhtml.tests IN: http.server.templating.fhtml.tests
: test-template ( path -- ? ) : test-template ( path -- ? )
"resource:extra/http/server/templating/fhtml/test/" "resource:extra/http/server/templating/fhtml/test/"
prepend prepend
[ [
".fhtml" append [ run-template ] with-string-writer ".fhtml" append <fhtml> [ call-template ] with-string-writer
] keep ] keep
".html" append utf8 file-contents = ; ".html" append utf8 file-contents = ;

View File

@ -1,25 +1,22 @@
! Copyright (C) 2005 Alex Chapman ! Copyright (C) 2005 Alex Chapman
! Copyright (C) 2006, 2007 Slava Pestov ! Copyright (C) 2006, 2008 Slava Pestov
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: continuations sequences kernel parser namespaces io USING: continuations sequences kernel namespaces debugger
io.files io.streams.string html html.elements source-files combinators math quotations generic strings splitting
debugger combinators math quotations generic strings splitting accessors assocs fry
accessors http.server.static http.server assocs parser io io.files io.streams.string io.encodings.utf8 source-files
io.encodings.utf8 fry ; html html.elements
http.server.static http.server http.server.templating ;
IN: http.server.templating.fhtml IN: http.server.templating.fhtml
: templating-vocab ( -- vocab-name ) "http.server.templating.fhtml" ; : templating-vocab ( -- vocab-name ) "http.server.templating.fhtml" ;
! See apps/http-server/test/ or libs/furnace/ for template usage
! examples
! We use a custom lexer so that %> ends a token even if not ! We use a custom lexer so that %> ends a token even if not
! followed by whitespace ! followed by whitespace
TUPLE: template-lexer ; TUPLE: template-lexer < lexer ;
: <template-lexer> ( lines -- lexer ) : <template-lexer> ( lines -- lexer )
<lexer> template-lexer construct-delegate ; template-lexer new-lexer ;
M: template-lexer skip-word M: template-lexer skip-word
[ [
@ -33,18 +30,18 @@ M: template-lexer skip-word
DEFER: <% delimiter DEFER: <% delimiter
: check-<% ( lexer -- col ) : check-<% ( lexer -- col )
"<%" over lexer-line-text rot lexer-column start* ; "<%" over line-text>> rot column>> start* ;
: found-<% ( accum lexer col -- accum ) : found-<% ( accum lexer col -- accum )
[ [
over lexer-line-text over line-text>>
>r >r lexer-column r> r> subseq parsed >r >r column>> r> r> subseq parsed
\ write-html parsed \ write-html parsed
] 2keep 2 + swap set-lexer-column ; ] 2keep 2 + >>column drop ;
: still-looking ( accum lexer -- accum ) : still-looking ( accum lexer -- accum )
[ [
dup lexer-line-text swap lexer-column tail [ line-text>> ] [ column>> ] bi tail
parsed \ print-html parsed parsed \ print-html parsed
] keep next-line ; ] keep next-line ;
@ -75,9 +72,13 @@ DEFER: <% delimiter
: html-error. ( error -- ) : html-error. ( error -- )
<pre> error. </pre> ; <pre> error. </pre> ;
: run-template ( filename -- ) TUPLE: fhtml path ;
C: <fhtml> fhtml
M: fhtml call-template ( filename -- )
'[ '[
, [ , path>> [
"quiet" on "quiet" on
parser-notes off parser-notes off
templating-vocab use+ templating-vocab use+
@ -88,16 +89,8 @@ DEFER: <% delimiter
] with-file-vocabs ] with-file-vocabs
] assert-depth ; ] assert-depth ;
: template-convert ( infile outfile -- )
utf8 [ run-template ] with-file-writer ;
! responder integration
: serve-template ( name -- response )
"text/html" <content>
swap '[ , run-template ] >>body ;
! file responder integration ! file responder integration
: enable-fhtml ( responder -- responder ) : enable-fhtml ( responder -- responder )
[ serve-template ] [ <fhtml> serve-template ]
"application/x-factor-server-page" "application/x-factor-server-page"
pick special>> set-at ; pick special>> set-at ;

View File

@ -0,0 +1,13 @@
USING: accessors kernel fry io.encodings.utf8 io.files
http.server ;
IN: http.server.templating
GENERIC: call-template ( template -- )
: template-convert ( template output -- )
utf8 [ call-template ] with-file-writer ;
! responder integration
: serve-template ( template -- response )
"text/html" <content>
swap '[ , call-template ] >>body ;

View File

@ -11,8 +11,7 @@ TUPLE: validation-error value reason ;
C: <validation-error> validation-error C: <validation-error> validation-error
: with-validator ( value quot -- result ) : with-validator ( value quot -- result )
[ validation-failed? on <validation-error> ] recover ; [ validation-failed? on <validation-error> ] recover ; inline
inline
: v-default ( str def -- str ) : v-default ( str def -- str )
over empty? spin ? ; over empty? spin ? ;
@ -20,6 +19,9 @@ C: <validation-error> validation-error
: v-required ( str -- str ) : v-required ( str -- str )
dup empty? [ "required" throw ] when ; dup empty? [ "required" throw ] when ;
: v-optional ( str quot -- str )
over empty? [ 2drop f ] [ call ] if ; inline
: v-min-length ( str n -- str ) : v-min-length ( str n -- str )
over length over < [ over length over < [
[ "must be at least " % # " characters" % ] "" make [ "must be at least " % # " characters" % ] "" make

View File

@ -27,7 +27,7 @@ M: monitor timeout timeout>> ;
M: monitor set-timeout (>>timeout) ; M: monitor set-timeout (>>timeout) ;
: construct-monitor ( path mailbox class -- monitor ) : new-monitor ( path mailbox class -- monitor )
new new
swap >>queue swap >>queue
swap >>path ; inline swap >>path ; inline

View File

@ -21,7 +21,7 @@ M: dummy-monitor dispose
M: mock-io-backend (monitor) M: mock-io-backend (monitor)
nip nip
over exists? [ over exists? [
dummy-monitor construct-monitor dummy-monitor new-monitor
dummy-monitor-created get [ 1+ ] change-i drop dummy-monitor-created get [ 1+ ] change-i drop
] [ ] [
"Does not exist" throw "Does not exist" throw

View File

@ -98,7 +98,7 @@ M: recursive-monitor dispose
: <recursive-monitor> ( path mailbox -- monitor ) : <recursive-monitor> ( path mailbox -- monitor )
>r (normalize-path) r> >r (normalize-path) r>
recursive-monitor construct-monitor recursive-monitor new-monitor
H{ } clone >>children H{ } clone >>children
<promise> >>ready <promise> >>ready
dup start-pump-thread dup start-pump-thread

View File

@ -32,7 +32,7 @@ M: input-task io-task-container drop reads>> ;
M: output-task io-task-container drop writes>> ; M: output-task io-task-container drop writes>> ;
: construct-mx ( class -- obj ) : new-mx ( class -- obj )
new new
H{ } clone >>reads H{ } clone >>reads
H{ } clone >>writes ; inline H{ } clone >>writes ; inline

View File

@ -13,7 +13,7 @@ TUPLE: epoll-mx < mx events ;
256 ; inline 256 ; inline
: <epoll-mx> ( -- mx ) : <epoll-mx> ( -- mx )
epoll-mx construct-mx epoll-mx new-mx
max-events epoll_create dup io-error over set-mx-fd max-events epoll_create dup io-error over set-mx-fd
max-events "epoll-event" <c-array> over set-epoll-mx-events ; max-events "epoll-event" <c-array> over set-epoll-mx-events ;

View File

@ -16,7 +16,7 @@ TUPLE: kqueue-mx < mx events monitors ;
256 ; inline 256 ; inline
: <kqueue-mx> ( -- mx ) : <kqueue-mx> ( -- mx )
kqueue-mx construct-mx kqueue-mx new-mx
H{ } clone >>monitors H{ } clone >>monitors
kqueue dup io-error >>fd kqueue dup io-error >>fd
max-events "kevent" <c-array> >>events ; max-events "kevent" <c-array> >>events ;
@ -142,7 +142,7 @@ TUPLE: vnode-monitor < monitor fd ;
: <vnode-monitor> ( path mailbox -- monitor ) : <vnode-monitor> ( path mailbox -- monitor )
>r [ O_RDONLY 0 open dup io-error ] keep r> >r [ O_RDONLY 0 open dup io-error ] keep r>
vnode-monitor construct-monitor swap >>fd vnode-monitor new-monitor swap >>fd
[ dup kqueue-mx get register-monitor ] [ ] [ fd>> close ] cleanup ; [ dup kqueue-mx get register-monitor ] [ ] [ fd>> close ] cleanup ;
M: vnode-monitor dispose M: vnode-monitor dispose

View File

@ -10,7 +10,7 @@ IN: io.unix.linux.monitors
TUPLE: linux-monitor < monitor wd ; TUPLE: linux-monitor < monitor wd ;
: <linux-monitor> ( wd path mailbox -- monitor ) : <linux-monitor> ( wd path mailbox -- monitor )
linux-monitor construct-monitor linux-monitor new-monitor
swap >>wd ; swap >>wd ;
SYMBOL: watches SYMBOL: watches

View File

@ -13,7 +13,7 @@ TUPLE: macosx-monitor < monitor handle ;
] curry each ; ] curry each ;
M:: macosx (monitor) ( path recursive? mailbox -- monitor ) M:: macosx (monitor) ( path recursive? mailbox -- monitor )
path mailbox macosx-monitor construct-monitor path mailbox macosx-monitor new-monitor
dup [ enqueue-notifications ] curry dup [ enqueue-notifications ] curry
path 1array 0 0 <event-stream> >>handle ; path 1array 0 0 <event-stream> >>handle ;

View File

@ -14,7 +14,7 @@ TUPLE: select-mx < mx read-fdset write-fdset ;
little-endian? [ BIN: 11000 bitxor ] unless ; inline little-endian? [ BIN: 11000 bitxor ] unless ; inline
: <select-mx> ( -- mx ) : <select-mx> ( -- mx )
select-mx construct-mx select-mx new-mx
FD_SETSIZE 8 * <bit-array> >>read-fdset FD_SETSIZE 8 * <bit-array> >>read-fdset
FD_SETSIZE 8 * <bit-array> >>write-fdset ; FD_SETSIZE 8 * <bit-array> >>write-fdset ;

View File

@ -98,7 +98,7 @@ TUPLE: win32-monitor < monitor port ;
M:: winnt (monitor) ( path recursive? mailbox -- monitor ) M:: winnt (monitor) ( path recursive? mailbox -- monitor )
[ [
path mailbox win32-monitor construct-monitor path mailbox win32-monitor new-monitor
path open-directory \ win32-monitor-port <buffered-port> path open-directory \ win32-monitor-port <buffered-port>
recursive? >>recursive recursive? >>recursive
>>port >>port

View File

@ -295,3 +295,5 @@ main = Primary
{ V{ V{ V{ "x" "[" "i" "]" } "[" "j" "]" } "." "y" } } [ { V{ V{ V{ "x" "[" "i" "]" } "[" "j" "]" } "." "y" } } [
"x[i][j].y" primary parse-result-ast "x[i][j].y" primary parse-result-ast
] unit-test ] unit-test
'ebnf' compile must-infer

View File

@ -100,21 +100,21 @@ C: <head> peg-head
: setup-growth ( h p -- ) : setup-growth ( h p -- )
pos set dup involved-set>> clone >>eval-set drop ; pos set dup involved-set>> clone >>eval-set drop ;
:: (grow-lr) ( h p r m -- ) : (grow-lr) ( h p r m -- )
h p setup-growth >r >r [ setup-growth ] 2keep r> r>
r eval-rule >r dup eval-rule r> swap
dup m stop-growth? [ dup pick stop-growth? [
drop 4drop drop
] [ ] [
m update-m over update-m
h p r m (grow-lr) (grow-lr)
] if ; inline ] if ; inline
:: grow-lr ( h p r m -- ast ) : grow-lr ( h p r m -- ast )
h p heads get set-at >r >r [ heads get set-at ] 2keep r> r>
h p r m (grow-lr) pick over >r >r (grow-lr) r> r>
p heads get delete-at swap heads get delete-at
m pos>> pos set m ans>> dup pos>> pos set ans>>
; inline ; inline
:: (setup-lr) ( r l s -- ) :: (setup-lr) ( r l s -- )
@ -240,8 +240,21 @@ GENERIC: (compile) ( parser -- quot )
gensym tuck >>compiled 2dup parser-body 0 1 <effect> define-declared dupd "peg" set-word-prop gensym tuck >>compiled 2dup parser-body 0 1 <effect> define-declared dupd "peg" set-word-prop
] if* ; ] if* ;
SYMBOL: delayed
: fixup-delayed ( -- )
#! Work through all delayed parsers and recompile their
#! words to have the correct bodies.
delayed get [
call compiled-parser 1quotation 0 1 <effect> define-declared
] assoc-each ;
: compile ( parser -- word ) : compile ( parser -- word )
[ compiled-parser ] with-compilation-unit ; [
H{ } clone delayed [
compiled-parser fixup-delayed
] with-variable
] with-compilation-unit ;
: compiled-parse ( state word -- result ) : compiled-parse ( state word -- result )
swap [ execute ] with-packrat ; inline swap [ execute ] with-packrat ; inline
@ -451,7 +464,7 @@ M: delay-parser (compile) ( parser -- quot )
#! For efficiency we memoize the quotation. #! For efficiency we memoize the quotation.
#! This way it is run only once and the #! This way it is run only once and the
#! parser constructed once at run time. #! parser constructed once at run time.
quot>> '[ @ compile ] { } { "word" } <effect> memoize-quot '[ @ execute ] ; quot>> gensym [ delayed get set-at ] keep 1quotation ;
TUPLE: box-parser quot ; TUPLE: box-parser quot ;

View File

@ -0,0 +1,33 @@
! Copyright (c) 2008 Eric Mertens
! See http://factorcode.org/license.txt for BSD license.
USING: arrays assocs kernel math math.ranges sequences ;
IN: project-euler.164
! http://projecteuler.net/index.php?section=problems&id=164
! DESCRIPTION
! -----------
! How many 20 digit numbers n (without any leading zero) exist such
! that no three consecutive digits of n have a sum greater than 9?
! SOLUTION
! --------
<PRIVATE
: next-keys ( key -- keys )
[ peek ] [ 10 swap sum - ] bi [ 2array ] with map ;
: next-table ( assoc -- assoc )
H{ } clone swap
[ swap next-keys [ pick at+ ] with each ] assoc-each ;
: init-table ( -- assoc )
9 [1,b] [ 1array 1 ] H{ } map>assoc ;
PRIVATE>
: euler164 ( -- n )
init-table 19 [ next-table ] times values sum ;

View File

@ -0,0 +1 @@
Non-core sequence words

View File

@ -0,0 +1 @@
Iteration with access to next element

View File

@ -246,7 +246,7 @@ SYMBOL: deserialized
(deserialize) <wrapper> ; (deserialize) <wrapper> ;
:: (deserialize-seq) ( exemplar quot -- seq ) :: (deserialize-seq) ( exemplar quot -- seq )
deserialize-cell exemplar new deserialize-cell exemplar new-sequence
[ intern-object ] [ intern-object ]
[ dup [ drop quot call ] change-each ] bi ; inline [ dup [ drop quot call ] change-each ] bi ; inline

View File

@ -97,7 +97,7 @@ SYMBOL: prolog-data
#! advance spot to after the substring. #! advance spot to after the substring.
[ [ [ [
dup slip swap dup [ get-char , ] unless dup slip swap dup [ get-char , ] unless
] skip-until ] "" make nip ; ] skip-until ] "" make nip ; inline
: rest ( -- string ) : rest ( -- string )
[ f ] take-until ; [ f ] take-until ;

View File

@ -1 +1 @@
Splay Trees Splay trees

View File

@ -0,0 +1,62 @@
USING: kernel system sequences io.files io.launcher bootstrap.image
builder.util builder.release.branch ;
IN: update
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
: run-command ( cmd -- ) to-strings try-process ;
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
: git-pull-clean ( -- )
image parent-directory
[
{ "git" "pull" "git://factorcode.org/git/factor.git" branch-name }
run-command
]
with-directory ;
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
: remote-clean-image ( -- url )
"http://factorcode.org/images/clean/" my-boot-image-name append ;
: download-clean-image ( -- ) { "wget" remote-clean-image } run-command ;
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
: make-clean ( -- ) { gnu-make "clean" } run-command ;
: make ( -- ) { gnu-make } run-command ;
: boot ( -- ) { "./factor" { "-i=" my-boot-image-name } } run-command ;
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
: rebuild ( -- )
image parent-directory
[
download-clean-image
make-clean
make
boot
]
with-directory ;
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
: update ( -- )
image parent-directory
[
git-id
git-pull-clean
git-id
= not
[ rebuild ]
when
]
with-directory ;
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
MAIN: update

View File

@ -0,0 +1,26 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:title>Edit Item</t:title>
<t:form action="edit">
<t:edit component="id" />
<table>
<tr><th class="field-label">Summary: </th><td><t:edit component="summary" /></td></tr>
<tr><th class="field-label">Priority: </th><td><t:edit component="priority" /></td></tr>
<tr><th class="field-label big-field-label">Description:</th><td><t:edit component="description" /></td></tr>
</table>
<input type="SUBMIT" value="Done" />
</t:form>
<t:a href="view" query="id">View</t:a>
|
<t:form action="delete" class="inline">
<t:edit component="id" />
<button type="submit" class="link-button link">Delete</button>
</t:form>
</t:chloe>

View File

@ -0,0 +1,45 @@
<?xml version='1.0' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<head>
<t:write-title />
<t:style>
body, button {
font:9pt "Lucida Grande", "Lucida Sans Unicode", verdana, geneva, sans-serif;
color:#444;
}
a, .link {
color: #222;
border-bottom:1px dotted #666;
text-decoration:none;
}
a:hover, .link:hover {
border-bottom:1px solid #66a;
}
.error { color: #a00; }
.field-label {
text-align: right;
}
</t:style>
<t:write-style />
</head>
<body>
<t:call-next-template />
</body>
</t:chloe>
</html>

Some files were not shown because too many files have changed in this diff Show More