Merge branch 'master' of git://factorcode.org/git/factor
commit
8644816f2a
|
@ -20,3 +20,4 @@ temp
|
||||||
logs
|
logs
|
||||||
work
|
work
|
||||||
build-support/wordsize
|
build-support/wordsize
|
||||||
|
*.bak
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -161,7 +161,7 @@ factor: $(DLL_OBJS) $(EXE_OBJS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f vm/*.o
|
rm -f vm/*.o
|
||||||
rm -f factor*.dll libfactor*.*
|
rm -f factor*.dll libfactor.{a,so,dylib}
|
||||||
|
|
||||||
vm/resources.o:
|
vm/resources.o:
|
||||||
$(WINDRES) vm/factor.rs vm/resources.o
|
$(WINDRES) vm/factor.rs vm/resources.o
|
||||||
|
|
|
@ -5,7 +5,7 @@ HELP: alarm
|
||||||
{ $class-description "An alarm. Can be passed to " { $link cancel-alarm } "." } ;
|
{ $class-description "An alarm. Can be passed to " { $link cancel-alarm } "." } ;
|
||||||
|
|
||||||
HELP: add-alarm
|
HELP: add-alarm
|
||||||
{ $values { "quot" quotation } { "time" timestamp } { "frequency" "a " { $link duration } " or " { $link f } } { "alarm" alarm } }
|
{ $values { "quot" quotation } { "time" timestamp } { "frequency" { $maybe duration } } { "alarm" alarm } }
|
||||||
{ $description "Creates and registers an alarm. If " { $snippet "frequency" } " is " { $link f } ", this will be a one-time alarm, otherwise it will fire with the given frequency. The quotation will be called from the alarm thread." } ;
|
{ $description "Creates and registers an alarm. If " { $snippet "frequency" } " is " { $link f } ", this will be a one-time alarm, otherwise it will fire with the given frequency. The quotation will be called from the alarm thread." } ;
|
||||||
|
|
||||||
HELP: later
|
HELP: later
|
||||||
|
|
|
@ -39,12 +39,12 @@ HELP: byte-length
|
||||||
{ $contract "Outputs the size of the byte array or float array data in bytes as presented to the C library interface." } ;
|
{ $contract "Outputs the size of the byte array or float array data in bytes as presented to the C library interface." } ;
|
||||||
|
|
||||||
HELP: c-getter
|
HELP: c-getter
|
||||||
{ $values { "name" string } { "quot" "a quotation with stack effect " { $snippet "( c-ptr n -- obj )" } } }
|
{ $values { "name" string } { "quot" { $quotation "( c-ptr n -- obj )" } } }
|
||||||
{ $description "Outputs a quotation which reads values of this C type from a C structure." }
|
{ $description "Outputs a quotation which reads values of this C type from a C structure." }
|
||||||
{ $errors "Throws a " { $link no-c-type } " error if the type does not exist." } ;
|
{ $errors "Throws a " { $link no-c-type } " error if the type does not exist." } ;
|
||||||
|
|
||||||
HELP: c-setter
|
HELP: c-setter
|
||||||
{ $values { "name" string } { "quot" "a quotation with stack effect " { $snippet "( obj c-ptr n -- )" } } }
|
{ $values { "name" string } { "quot" { $quotation "( obj c-ptr n -- )" } } }
|
||||||
{ $description "Outputs a quotation which writes values of this C type to a C structure." }
|
{ $description "Outputs a quotation which writes values of this C type to a C structure." }
|
||||||
{ $errors "Throws an error if the type does not exist." } ;
|
{ $errors "Throws an error if the type does not exist." } ;
|
||||||
|
|
||||||
|
|
|
@ -164,7 +164,7 @@ GENERIC: stack-size ( type -- size ) foldable
|
||||||
|
|
||||||
M: string stack-size c-type stack-size ;
|
M: string stack-size c-type stack-size ;
|
||||||
|
|
||||||
M: c-type stack-size size>> ;
|
M: c-type stack-size size>> cell align ;
|
||||||
|
|
||||||
GENERIC: byte-length ( seq -- n ) flushable
|
GENERIC: byte-length ( seq -- n ) flushable
|
||||||
|
|
||||||
|
@ -436,6 +436,6 @@ M: long-long-type box-return ( type -- )
|
||||||
"double" define-primitive-type
|
"double" define-primitive-type
|
||||||
|
|
||||||
"long" "ptrdiff_t" typedef
|
"long" "ptrdiff_t" typedef
|
||||||
|
"long" "intptr_t" typedef
|
||||||
"ulong" "size_t" typedef
|
"ulong" "size_t" typedef
|
||||||
] with-compilation-unit
|
] with-compilation-unit
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
! Copyright (C) 2004, 2008 Slava Pestov.
|
! Copyright (C) 2004, 2008 Slava Pestov.
|
||||||
! See http://factorcode.org/license.txt for BSD license.
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
USING: accessors arrays generic hashtables kernel kernel.private
|
USING: accessors arrays generic hashtables kernel kernel.private
|
||||||
math namespaces parser sequences strings words libc
|
math namespaces parser sequences strings words libc fry
|
||||||
alien.c-types alien.structs.fields cpu.architecture ;
|
alien.c-types alien.structs.fields cpu.architecture ;
|
||||||
IN: alien.structs
|
IN: alien.structs
|
||||||
|
|
||||||
: if-value-structs? ( ctype true false -- )
|
|
||||||
value-structs?
|
|
||||||
[ drop call ] [ >r 2drop "void*" r> call ] if ; inline
|
|
||||||
|
|
||||||
TUPLE: struct-type size align fields ;
|
TUPLE: struct-type size align fields ;
|
||||||
|
|
||||||
M: struct-type heap-size size>> ;
|
M: struct-type heap-size size>> ;
|
||||||
|
@ -17,20 +13,26 @@ M: struct-type c-type-align align>> ;
|
||||||
|
|
||||||
M: struct-type c-type-stack-align? drop f ;
|
M: struct-type c-type-stack-align? drop f ;
|
||||||
|
|
||||||
M: struct-type unbox-parameter
|
: if-value-struct ( ctype true false -- )
|
||||||
[ %unbox-struct ] [ unbox-parameter ] if-value-structs? ;
|
[ dup value-struct? ] 2dip '[ drop "void*" @ ] if ; inline
|
||||||
|
|
||||||
M: struct-type unbox-return
|
M: struct-type unbox-parameter
|
||||||
f swap %unbox-struct ;
|
[ %unbox-large-struct ] [ unbox-parameter ] if-value-struct ;
|
||||||
|
|
||||||
M: struct-type box-parameter
|
M: struct-type box-parameter
|
||||||
[ %box-struct ] [ box-parameter ] if-value-structs? ;
|
[ %box-large-struct ] [ box-parameter ] if-value-struct ;
|
||||||
|
|
||||||
|
: if-small-struct ( c-type true false -- ? )
|
||||||
|
[ dup struct-small-enough? ] 2dip '[ f swap @ ] if ; inline
|
||||||
|
|
||||||
|
M: struct-type unbox-return
|
||||||
|
[ %unbox-small-struct ] [ %unbox-large-struct ] if-small-struct ;
|
||||||
|
|
||||||
M: struct-type box-return
|
M: struct-type box-return
|
||||||
f swap %box-struct ;
|
[ %box-small-struct ] [ %box-large-struct ] if-small-struct ;
|
||||||
|
|
||||||
M: struct-type stack-size
|
M: struct-type stack-size
|
||||||
[ heap-size ] [ stack-size ] if-value-structs? ;
|
[ heap-size ] [ stack-size ] if-value-struct ;
|
||||||
|
|
||||||
: c-struct? ( type -- ? ) (c-type) struct-type? ;
|
: c-struct? ( type -- ? ) (c-type) struct-type? ;
|
||||||
|
|
||||||
|
@ -40,7 +42,7 @@ M: struct-type stack-size
|
||||||
-rot define-c-type ;
|
-rot define-c-type ;
|
||||||
|
|
||||||
: define-struct-early ( name vocab fields -- fields )
|
: define-struct-early ( name vocab fields -- fields )
|
||||||
-rot [ rot first2 <field-spec> ] 2curry map ;
|
[ first2 <field-spec> ] with with map ;
|
||||||
|
|
||||||
: compute-struct-align ( types -- n )
|
: compute-struct-align ( types -- n )
|
||||||
[ c-type-align ] map supremum ;
|
[ c-type-align ] map supremum ;
|
||||||
|
|
|
@ -2,7 +2,7 @@ IN: binary-search
|
||||||
USING: help.markup help.syntax sequences kernel math.order ;
|
USING: help.markup help.syntax sequences kernel math.order ;
|
||||||
|
|
||||||
HELP: search
|
HELP: search
|
||||||
{ $values { "seq" "a sorted sequence" } { "quot" "a quotation with stack effect " { $snippet "( elt -- <=> )" } } { "i" "an index, or " { $link f } } { "elt" "an element, or " { $link f } } }
|
{ $values { "seq" "a sorted sequence" } { "quot" { $quotation "( elt -- <=> )" } } { "i" "an index, or " { $link f } } { "elt" "an element, or " { $link f } } }
|
||||||
{ $description "Performs a binary search on a sequence, calling the quotation to decide whether to end the search (" { $link +eq+ } "), search lower (" { $link +lt+ } ") or search higher (" { $link +gt+ } ")."
|
{ $description "Performs a binary search on a sequence, calling the quotation to decide whether to end the search (" { $link +eq+ } "), search lower (" { $link +lt+ } ") or search higher (" { $link +gt+ } ")."
|
||||||
$nl
|
$nl
|
||||||
"If the sequence is non-empty, outputs the index and value of the closest match, which is either an element for which the quotation output " { $link +eq+ } ", or failing that, least element for which the quotation output " { $link +lt+ } "."
|
"If the sequence is non-empty, outputs the index and value of the closest match, which is either an element for which the quotation output " { $link +eq+ } ", or failing that, least element for which the quotation output " { $link +lt+ } "."
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
! 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: http.client checksums checksums.openssl splitting assocs
|
USING: http.client checksums checksums.md5 splitting assocs
|
||||||
kernel io.files bootstrap.image sequences io urls ;
|
kernel io.files bootstrap.image sequences io urls ;
|
||||||
IN: bootstrap.image.download
|
IN: bootstrap.image.download
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ IN: bootstrap.image.download
|
||||||
: need-new-image? ( image -- ? )
|
: need-new-image? ( image -- ? )
|
||||||
dup exists?
|
dup exists?
|
||||||
[
|
[
|
||||||
[ openssl-md5 checksum-file hex-string ]
|
[ md5 checksum-file hex-string ]
|
||||||
[ download-checksums at ]
|
[ download-checksums at ]
|
||||||
bi = not
|
bi = not
|
||||||
] [ drop t ] if ;
|
] [ drop t ] if ;
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
unportable
|
unportable
|
||||||
windows
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ HELP: alien>objc-types
|
||||||
{ objc>alien-types alien>objc-types } related-words
|
{ objc>alien-types alien>objc-types } related-words
|
||||||
|
|
||||||
HELP: import-objc-class
|
HELP: import-objc-class
|
||||||
{ $values { "name" string } { "quot" "a quotation with stack effect " { $snippet "( -- )" } } }
|
{ $values { "name" string } { "quot" { $quotation "( -- )" } } }
|
||||||
{ $description "If a class named " { $snippet "name" } " is already known to the Objective C interface, does nothing. Otherwise, first calls the quotation. The quotation should make the class available to the Objective C runtime if necessary, either by loading a framework or defining it directly. After the quotation returns, this word makes the class available to Factor programs by importing methods and creating a class word the class object in the " { $vocab-link "cocoa.classes" } " vocabulary." } ;
|
{ $description "If a class named " { $snippet "name" } " is already known to the Objective C interface, does nothing. Otherwise, first calls the quotation. The quotation should make the class available to the Objective C runtime if necessary, either by loading a framework or defining it directly. After the quotation returns, this word makes the class available to Factor programs by importing methods and creating a class word the class object in the " { $vocab-link "cocoa.classes" } " vocabulary." } ;
|
||||||
|
|
||||||
HELP: root-class
|
HELP: root-class
|
||||||
|
|
|
@ -2,10 +2,10 @@ USING: help.markup help.syntax parser vocabs.loader strings ;
|
||||||
IN: command-line
|
IN: command-line
|
||||||
|
|
||||||
HELP: run-bootstrap-init
|
HELP: run-bootstrap-init
|
||||||
{ $description "Runs the " { $snippet ".factor-boot-rc" } " file in the user's home directory unless the " { $snippet "-no-user-init" } " command line switch was given." } ;
|
{ $description "Runs the bootstrap initialization file in the user's home directory, unless the " { $snippet "-no-user-init" } " command line switch was given. This file is named " { $snippet ".factor-boot-rc" } " on Unix and " { $snippet "factor-boot-rc" } " on Windows." } ;
|
||||||
|
|
||||||
HELP: run-user-init
|
HELP: run-user-init
|
||||||
{ $description "Runs the " { $snippet ".factor-rc" } " file in the user's home directory unless the " { $snippet "-no-user-init" } " command line switch was given." } ;
|
{ $description "Runs the startup initialization file in the user's home directory, unless the " { $snippet "-no-user-init" } " command line switch was given. This file is named " { $snippet ".factor-rc" } " on Unix and " { $snippet "factor-rc" } " on Windows." } ;
|
||||||
|
|
||||||
HELP: cli-param
|
HELP: cli-param
|
||||||
{ $values { "param" string } }
|
{ $values { "param" string } }
|
||||||
|
@ -57,7 +57,7 @@ ARTICLE: "bootstrap-cli-args" "Command line switches for bootstrap"
|
||||||
"A number of command line switches can be passed to a bootstrap image to modify the behavior of the resulting image:"
|
"A number of command line switches can be passed to a bootstrap image to modify the behavior of the resulting image:"
|
||||||
{ $table
|
{ $table
|
||||||
{ { $snippet "-output-image=" { $emphasis "image" } } { "Save the result to " { $snippet "image" } ". The default is " { $snippet "factor.image" } "." } }
|
{ { $snippet "-output-image=" { $emphasis "image" } } { "Save the result to " { $snippet "image" } ". The default is " { $snippet "factor.image" } "." } }
|
||||||
{ { $snippet "-no-user-init" } { "Inhibits the running of the " { $snippet ".factor-boot-rc" } " file in the user's home directory." } }
|
{ { $snippet "-no-user-init" } { "Inhibits the running of user initialization files on startup. See " { $link "rc-files" } "." } }
|
||||||
{ { $snippet "-include=" { $emphasis "components..." } } "A list of components to include (see below)." }
|
{ { $snippet "-include=" { $emphasis "components..." } } "A list of components to include (see below)." }
|
||||||
{ { $snippet "-exclude=" { $emphasis "components..." } } "A list of components to exclude." }
|
{ { $snippet "-exclude=" { $emphasis "components..." } } "A list of components to exclude." }
|
||||||
{ { $snippet "-ui-backend=" { $emphasis "backend" } } { "One of " { $snippet "x11" } ", " { $snippet "windows" } ", or " { $snippet "cocoa" } ". The default is platform-specific." } }
|
{ { $snippet "-ui-backend=" { $emphasis "backend" } } { "One of " { $snippet "x11" } ", " { $snippet "windows" } ", or " { $snippet "cocoa" } ". The default is platform-specific." } }
|
||||||
|
@ -74,9 +74,9 @@ ARTICLE: "bootstrap-cli-args" "Command line switches for bootstrap"
|
||||||
"By default, all optional components are loaded. To load all optional components except for a given list, use the " { $snippet "-exclude=" } " switch; to only load specified optional components, use the " { $snippet "-include=" } "."
|
"By default, all optional components are loaded. To load all optional components except for a given list, use the " { $snippet "-exclude=" } " switch; to only load specified optional components, use the " { $snippet "-include=" } "."
|
||||||
$nl
|
$nl
|
||||||
"For example, to build an image with the compiler but no other components, you could do:"
|
"For example, to build an image with the compiler but no other components, you could do:"
|
||||||
{ $code "./factor -i=boot.ppc.image -include=compiler" }
|
{ $code "./factor -i=boot.macosx-ppc.image -include=compiler" }
|
||||||
"To build an image with everything except for the user interface and graphical tools,"
|
"To build an image with everything except for the user interface and graphical tools,"
|
||||||
{ $code "./factor -i=boot.ppc.image -exclude=\"ui ui.tools\"" }
|
{ $code "./factor -i=boot.macosx-ppc.image -exclude=\"ui ui.tools\"" }
|
||||||
"To generate a bootstrap image in the first place, see " { $link "bootstrap.image" } "." ;
|
"To generate a bootstrap image in the first place, see " { $link "bootstrap.image" } "." ;
|
||||||
|
|
||||||
ARTICLE: "standard-cli-args" "Command line switches for general usage"
|
ARTICLE: "standard-cli-args" "Command line switches for general usage"
|
||||||
|
@ -84,17 +84,43 @@ ARTICLE: "standard-cli-args" "Command line switches for general usage"
|
||||||
{ $table
|
{ $table
|
||||||
{ { $snippet "-e=" { $emphasis "code" } } { "This specifies a code snippet to evaluate. If you want Factor to exit immediately after, also specify " { $snippet "-run=none" } "." } }
|
{ { $snippet "-e=" { $emphasis "code" } } { "This specifies a code snippet to evaluate. If you want Factor to exit immediately after, also specify " { $snippet "-run=none" } "." } }
|
||||||
{ { $snippet "-run=" { $emphasis "vocab" } } { { $snippet { $emphasis "vocab" } } " is the name of a vocabulary with a " { $link POSTPONE: MAIN: } " hook to run on startup, for example " { $vocab-link "listener" } ", " { $vocab-link "ui" } " or " { $vocab-link "none" } "." } }
|
{ { $snippet "-run=" { $emphasis "vocab" } } { { $snippet { $emphasis "vocab" } } " is the name of a vocabulary with a " { $link POSTPONE: MAIN: } " hook to run on startup, for example " { $vocab-link "listener" } ", " { $vocab-link "ui" } " or " { $vocab-link "none" } "." } }
|
||||||
{ { $snippet "-no-user-init" } { "Inhibits the running of the " { $snippet ".factor-rc" } " file in the user's home directory on startup." } }
|
{ { $snippet "-no-user-init" } { "Inhibits the running of user initialization files on startup. See " { $link "rc-files" } "." } }
|
||||||
{ { $snippet "-quiet" } { "If set, " { $link run-file } " and " { $link require } " will not print load messages." } }
|
{ { $snippet "-quiet" } { "If set, " { $link run-file } " and " { $link require } " will not print load messages." } }
|
||||||
{ { $snippet "-script" } { "Equivalent to " { $snippet "-quiet -run=none" } "." $nl "On Unix systems, Factor can be used for scripting - just create an executable text file whose first line is:" { $code "#! /usr/local/bin/factor -script" } "The space after " { $snippet "#!" } " is necessary because of Factor syntax." } }
|
{ { $snippet "-script" } { "Equivalent to " { $snippet "-quiet -run=none" } "." $nl "On Unix systems, Factor can be used for scripting - just create an executable text file whose first line is:" { $code "#! /usr/local/bin/factor -script" } "The space after " { $snippet "#!" } " is necessary because of Factor syntax." } }
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
ARTICLE: "rc-files" "Running code on startup"
|
ARTICLE: "factor-boot-rc" "Bootstrap initialization file"
|
||||||
"Unless the " { $snippet "-no-user-init" } " command line switch is specified, The startup routine runs the " { $snippet ".factor-rc" } " file in the user's home directory, if it exists. This file can contain initialization and customization for your development environment."
|
"The botstrap initialization file is named " { $snippet "factor-boot-rc" } " on Windows and " { $snippet ".factor-boot-rc" } " on Unix. This file can contain " { $link require } " calls for vocabularies you use frequently, and other such long-running tasks that you do not want to perform every time Factor starts."
|
||||||
$nl
|
$nl
|
||||||
"The " { $snippet ".factor-rc" } " and " { $snippet ".factor-boot-rc" } " files can be run explicitly:"
|
"A word to run this file from an existing Factor session:"
|
||||||
{ $subsection run-user-init }
|
{ $subsection run-bootstrap-init }
|
||||||
{ $subsection run-bootstrap-init } ;
|
"For example, if you changed " { $snippet ".factor-boot-rc" } " and do not want to bootstrap again, you can just invoke " { $link run-bootstrap-init } " in the listener." ;
|
||||||
|
|
||||||
|
ARTICLE: "factor-rc" "Startup initialization file"
|
||||||
|
"The startup initialization file is named " { $snippet "factor-rc" } " on Windows and " { $snippet ".factor-rc" } " on Unix. If it exists, it is run every time Factor starts."
|
||||||
|
$nl
|
||||||
|
"A word to run this file from an existing Factor session:"
|
||||||
|
{ $subsection run-user-init } ;
|
||||||
|
|
||||||
|
ARTICLE: "rc-files" "Running code on startup"
|
||||||
|
"Factor looks for two files in your home directory."
|
||||||
|
{ $subsection "factor-boot-rc" }
|
||||||
|
{ $subsection "factor-rc" }
|
||||||
|
"The " { $snippet "-no-user-init" } " command line switch will inhibit the running of these files."
|
||||||
|
$nl
|
||||||
|
"If you are unsure where the files should be located, evaluate the following code:"
|
||||||
|
{ $code
|
||||||
|
"USE: command-line"
|
||||||
|
"\"factor-rc\" rc-path print"
|
||||||
|
"\"factor-boot-rc\" rc-path print"
|
||||||
|
}
|
||||||
|
"Here is an example " { $snippet ".factor-boot-rc" } " which sets up GVIM editor integration, adds an additional vocabulary root (see " { $link "vocabs.roots" } "), and increases the font size in the UI by setting the DPI (dots-per-inch) variable:"
|
||||||
|
{ $code
|
||||||
|
"USING: editors.gvim vocabs.loader ui.freetype namespaces sequences ;"
|
||||||
|
"\"/opt/local/bin\" \\ gvim-path set-global"
|
||||||
|
"\"/home/jane/src/\" vocab-roots get push"
|
||||||
|
"100 dpi set-global"
|
||||||
|
} ;
|
||||||
|
|
||||||
ARTICLE: "cli" "Command line usage"
|
ARTICLE: "cli" "Command line usage"
|
||||||
"Zero or more command line arguments may be passed to the Factor runtime. Command line arguments starting with a dash (" { $snippet "-" } ") is interpreted as switches. All other arguments are taken to be file names to be run by " { $link run-file } "."
|
"Zero or more command line arguments may be passed to the Factor runtime. Command line arguments starting with a dash (" { $snippet "-" } ") is interpreted as switches. All other arguments are taken to be file names to be run by " { $link run-file } "."
|
||||||
|
|
|
@ -5,14 +5,18 @@ kernel.private namespaces parser sequences strings system
|
||||||
splitting io.files eval ;
|
splitting io.files eval ;
|
||||||
IN: command-line
|
IN: command-line
|
||||||
|
|
||||||
|
: rc-path ( name -- path )
|
||||||
|
os windows? [ "." prepend ] unless
|
||||||
|
home prepend-path ;
|
||||||
|
|
||||||
: run-bootstrap-init ( -- )
|
: run-bootstrap-init ( -- )
|
||||||
"user-init" get [
|
"user-init" get [
|
||||||
home ".factor-boot-rc" append-path ?run-file
|
"factor-boot-rc" rc-path ?run-file
|
||||||
] when ;
|
] when ;
|
||||||
|
|
||||||
: run-user-init ( -- )
|
: run-user-init ( -- )
|
||||||
"user-init" get [
|
"user-init" get [
|
||||||
home ".factor-rc" append-path ?run-file
|
"factor-rc" rc-path ?run-file
|
||||||
] when ;
|
] when ;
|
||||||
|
|
||||||
: cli-var-param ( name value -- ) swap set-global ;
|
: cli-var-param ( name value -- ) swap set-global ;
|
||||||
|
|
|
@ -235,7 +235,7 @@ M: float-regs reg-class-variable drop float-regs ;
|
||||||
GENERIC: inc-reg-class ( register-class -- )
|
GENERIC: inc-reg-class ( register-class -- )
|
||||||
|
|
||||||
: ?dummy-stack-params ( reg-class -- )
|
: ?dummy-stack-params ( reg-class -- )
|
||||||
dummy-stack-params? [ reg-size stack-params +@ ] [ drop ] if ;
|
dummy-stack-params? [ reg-size cell align stack-params +@ ] [ drop ] if ;
|
||||||
|
|
||||||
: ?dummy-int-params ( reg-class -- )
|
: ?dummy-int-params ( reg-class -- )
|
||||||
dummy-int-params? [ reg-size cell /i 1 max int-regs +@ ] [ drop ] if ;
|
dummy-int-params? [ reg-size cell /i 1 max int-regs +@ ] [ drop ] if ;
|
||||||
|
@ -264,7 +264,7 @@ M: object reg-class-full?
|
||||||
|
|
||||||
: spill-param ( reg-class -- n reg-class )
|
: spill-param ( reg-class -- n reg-class )
|
||||||
stack-params get
|
stack-params get
|
||||||
>r reg-size stack-params +@ r>
|
>r reg-size cell align stack-params +@ r>
|
||||||
stack-params ;
|
stack-params ;
|
||||||
|
|
||||||
: fastcall-param ( reg-class -- n reg-class )
|
: fastcall-param ( reg-class -- n reg-class )
|
||||||
|
|
|
@ -6,7 +6,7 @@ HELP: enable-compiler
|
||||||
{ $description "Enables the optimizing compiler." } ;
|
{ $description "Enables the optimizing compiler." } ;
|
||||||
|
|
||||||
HELP: disable-compiler
|
HELP: disable-compiler
|
||||||
{ $description "Enables the optimizing compiler." } ;
|
{ $description "Disable the optimizing compiler." } ;
|
||||||
|
|
||||||
ARTICLE: "compiler-usage" "Calling the optimizing compiler"
|
ARTICLE: "compiler-usage" "Calling the optimizing compiler"
|
||||||
"Normally, new word definitions are recompiled automatically. This can be changed:"
|
"Normally, new word definitions are recompiled automatically. This can be changed:"
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
! Copyright (C) 2004, 2008 Slava Pestov.
|
! Copyright (C) 2004, 2008 Slava Pestov.
|
||||||
! See http://factorcode.org/license.txt for BSD license.
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
USING: accessors kernel namespaces arrays sequences io debugger
|
USING: accessors kernel namespaces arrays sequences io debugger
|
||||||
words fry continuations vocabs assocs dlists definitions math
|
words fry continuations vocabs assocs dlists definitions
|
||||||
threads graphs generic combinators deques search-deques
|
math threads graphs generic combinators deques search-deques
|
||||||
prettyprint io stack-checker stack-checker.state
|
prettyprint io stack-checker stack-checker.state
|
||||||
stack-checker.inlining compiler.errors compiler.units
|
stack-checker.inlining compiler.errors compiler.units
|
||||||
compiler.tree.builder compiler.tree.optimizer
|
compiler.tree.builder compiler.tree.optimizer
|
||||||
|
|
|
@ -146,13 +146,21 @@ FUNCTION: void ffi_test_20 double x1, double x2, double x3,
|
||||||
|
|
||||||
! Make sure XT doesn't get clobbered in stack frame
|
! Make sure XT doesn't get clobbered in stack frame
|
||||||
|
|
||||||
: ffi_test_31 ( a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a ptr -- result y )
|
: ffi_test_31 ( a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a -- result y )
|
||||||
"void"
|
"int"
|
||||||
f "ffi_test_31"
|
f "ffi_test_31"
|
||||||
{ "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" }
|
{ "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" "int" }
|
||||||
alien-invoke gc 3 ;
|
alien-invoke gc 3 ;
|
||||||
|
|
||||||
[ 3 ] [ 42 [ ] each ffi_test_31 ] unit-test
|
[ 861 3 ] [ 42 [ ] each ffi_test_31 ] unit-test
|
||||||
|
|
||||||
|
: ffi_test_31_point_5 ( a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a -- result )
|
||||||
|
"float"
|
||||||
|
f "ffi_test_31_point_5"
|
||||||
|
{ "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" "float" }
|
||||||
|
alien-invoke ;
|
||||||
|
|
||||||
|
[ 861.0 ] [ 42 [ >float ] each ffi_test_31_point_5 ] unit-test
|
||||||
|
|
||||||
FUNCTION: longlong ffi_test_21 long x long y ;
|
FUNCTION: longlong ffi_test_21 long x long y ;
|
||||||
|
|
||||||
|
|
|
@ -34,14 +34,10 @@ IN: compiler.tree.builder
|
||||||
if ;
|
if ;
|
||||||
|
|
||||||
: (build-tree-from-word) ( word -- )
|
: (build-tree-from-word) ( word -- )
|
||||||
dup
|
dup initial-recursive-state recursive-state set
|
||||||
[ "inline" word-prop ]
|
dup [ "inline" word-prop ] [ "recursive" word-prop ] bi and
|
||||||
[ "recursive" word-prop ] bi and [
|
[ 1quotation ] [ specialized-def ] if
|
||||||
1quotation f initial-recursive-state infer-quot
|
infer-quot-here ;
|
||||||
] [
|
|
||||||
[ specialized-def ] [ initial-recursive-state ] bi
|
|
||||||
infer-quot
|
|
||||||
] if ;
|
|
||||||
|
|
||||||
: check-cannot-infer ( word -- )
|
: check-cannot-infer ( word -- )
|
||||||
dup "cannot-infer" word-prop [ cannot-infer-effect ] [ drop ] if ;
|
dup "cannot-infer" word-prop [ cannot-infer-effect ] [ drop ] if ;
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
! 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: fry accessors namespaces assocs deques search-deques
|
USING: fry accessors namespaces assocs deques search-deques
|
||||||
kernel sequences sequences.deep words sets stack-checker.branches
|
dlists kernel sequences sequences.deep words sets
|
||||||
compiler.tree compiler.tree.def-use compiler.tree.combinators ;
|
stack-checker.branches compiler.tree compiler.tree.def-use
|
||||||
|
compiler.tree.combinators ;
|
||||||
IN: compiler.tree.dead-code.liveness
|
IN: compiler.tree.dead-code.liveness
|
||||||
|
|
||||||
SYMBOL: work-list
|
SYMBOL: work-list
|
||||||
|
|
|
@ -18,12 +18,16 @@ TUPLE: definition value node uses ;
|
||||||
swap >>node
|
swap >>node
|
||||||
V{ } clone >>uses ;
|
V{ } clone >>uses ;
|
||||||
|
|
||||||
|
ERROR: no-def-error value ;
|
||||||
|
|
||||||
: def-of ( value -- definition )
|
: def-of ( value -- definition )
|
||||||
def-use get at* [ "No def" throw ] unless ;
|
dup def-use get at* [ nip ] [ no-def-error ] if ;
|
||||||
|
|
||||||
|
ERROR: multiple-defs-error ;
|
||||||
|
|
||||||
: def-value ( node value -- )
|
: def-value ( node value -- )
|
||||||
def-use get 2dup key? [
|
def-use get 2dup key? [
|
||||||
"Multiple defs" throw
|
multiple-defs-error
|
||||||
] [
|
] [
|
||||||
[ [ <definition> ] keep ] dip set-at
|
[ [ <definition> ] keep ] dip set-at
|
||||||
] if ;
|
] if ;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
! 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 assocs arrays namespaces accessors sequences deques
|
USING: kernel assocs arrays namespaces accessors sequences deques
|
||||||
search-deques compiler.tree compiler.tree.combinators ;
|
search-deques dlists compiler.tree compiler.tree.combinators ;
|
||||||
IN: compiler.tree.recursive
|
IN: compiler.tree.recursive
|
||||||
|
|
||||||
! Collect label info
|
! Collect label info
|
||||||
|
|
|
@ -2,36 +2,42 @@ USING: help.markup help.syntax sequences ;
|
||||||
IN: concurrency.combinators
|
IN: concurrency.combinators
|
||||||
|
|
||||||
HELP: parallel-map
|
HELP: parallel-map
|
||||||
{ $values { "seq" sequence } { "quot" "a quotation with stack effect " { $snippet "( elt -- newelt )" } } { "newseq" sequence } }
|
{ $values { "seq" sequence } { "quot" { $quotation "( elt -- newelt )" } } { "newseq" sequence } }
|
||||||
{ $description "Spawns a new thread for applying " { $snippet "quot" } " to every element of " { $snippet "seq" } ", collecting the results at the end." }
|
{ $description "Spawns a new thread for applying " { $snippet "quot" } " to every element of " { $snippet "seq" } ", collecting the results at the end." }
|
||||||
{ $errors "Throws an error if one of the iterations throws an error." } ;
|
{ $errors "Throws an error if one of the iterations throws an error." } ;
|
||||||
|
|
||||||
HELP: 2parallel-map
|
HELP: 2parallel-map
|
||||||
{ $values { "seq1" sequence } { "seq2" sequence } { "quot" "a quotation with stack effect " { $snippet "( elt -- newelt )" } } { "newseq" sequence } }
|
{ $values { "seq1" sequence } { "seq2" sequence } { "quot" { $quotation "( elt -- newelt )" } } { "newseq" sequence } }
|
||||||
{ $description "Spawns a new thread for applying " { $snippet "quot" } " to pairwise elements of " { $snippet "seq1" } " and " { $snippet "seq2" } ", collecting the results at the end." }
|
{ $description "Spawns a new thread for applying " { $snippet "quot" } " to pairwise elements of " { $snippet "seq1" } " and " { $snippet "seq2" } ", collecting the results at the end." }
|
||||||
{ $errors "Throws an error if one of the iterations throws an error." } ;
|
{ $errors "Throws an error if one of the iterations throws an error." } ;
|
||||||
|
|
||||||
HELP: parallel-each
|
HELP: parallel-each
|
||||||
{ $values { "seq" sequence } { "quot" "a quotation with stack effect " { $snippet "( elt -- )" } } }
|
{ $values { "seq" sequence } { "quot" { $quotation "( elt -- )" } } }
|
||||||
{ $description "Spawns a new thread for applying " { $snippet "quot" } " to every element of " { $snippet "seq" } ", blocking until all quotations complete." }
|
{ $description "Spawns a new thread for applying " { $snippet "quot" } " to every element of " { $snippet "seq" } ", blocking until all quotations complete." }
|
||||||
{ $errors "Throws an error if one of the iterations throws an error." } ;
|
{ $errors "Throws an error if one of the iterations throws an error." } ;
|
||||||
|
|
||||||
HELP: 2parallel-each
|
HELP: 2parallel-each
|
||||||
{ $values { "seq1" sequence } { "seq2" sequence } { "quot" "a quotation with stack effect " { $snippet "( elt -- )" } } }
|
{ $values { "seq1" sequence } { "seq2" sequence } { "quot" { $quotation "( elt -- )" } } }
|
||||||
{ $description "Spawns a new thread for applying " { $snippet "quot" } " to pairwise elements of " { $snippet "seq1" } " and " { $snippet "seq2" } ", blocking until all quotations complete." }
|
{ $description "Spawns a new thread for applying " { $snippet "quot" } " to pairwise elements of " { $snippet "seq1" } " and " { $snippet "seq2" } ", blocking until all quotations complete." }
|
||||||
{ $errors "Throws an error if one of the iterations throws an error." } ;
|
{ $errors "Throws an error if one of the iterations throws an error." } ;
|
||||||
|
|
||||||
HELP: parallel-filter
|
HELP: parallel-filter
|
||||||
{ $values { "seq" sequence } { "quot" "a quotation with stack effect " { $snippet "( elt -- ? )" } } { "newseq" sequence } }
|
{ $values { "seq" sequence } { "quot" { $quotation "( elt -- ? )" } } { "newseq" sequence } }
|
||||||
{ $description "Spawns a new thread for applying " { $snippet "quot" } " to every element of " { $snippet "seq" } ", collecting the elements for which the quotation yielded a true value." }
|
{ $description "Spawns a new thread for applying " { $snippet "quot" } " to every element of " { $snippet "seq" } ", collecting the elements for which the quotation yielded a true value." }
|
||||||
{ $errors "Throws an error if one of the iterations throws an error." } ;
|
{ $errors "Throws an error if one of the iterations throws an error." } ;
|
||||||
|
|
||||||
ARTICLE: "concurrency.combinators" "Concurrent combinators"
|
ARTICLE: "concurrency.combinators" "Concurrent combinators"
|
||||||
"The " { $vocab-link "concurrency.combinators" } " vocabulary provides concurrent variants of " { $link each } ", " { $link map } " and " { $link filter } ":"
|
"The " { $vocab-link "concurrency.combinators" } " vocabulary provides concurrent variants of various combinators."
|
||||||
|
$nl
|
||||||
|
"Concurrent sequence combinators:"
|
||||||
{ $subsection parallel-each }
|
{ $subsection parallel-each }
|
||||||
{ $subsection 2parallel-each }
|
{ $subsection 2parallel-each }
|
||||||
{ $subsection parallel-map }
|
{ $subsection parallel-map }
|
||||||
{ $subsection 2parallel-map }
|
{ $subsection 2parallel-map }
|
||||||
{ $subsection parallel-filter } ;
|
{ $subsection parallel-filter }
|
||||||
|
"Concurrent cleave combinators:"
|
||||||
|
{ $subsection parallel-cleave }
|
||||||
|
{ $subsection parallel-spread }
|
||||||
|
{ $subsection parallel-napply } ;
|
||||||
|
|
||||||
ABOUT: "concurrency.combinators"
|
ABOUT: "concurrency.combinators"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
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 accessors arrays ;
|
concurrency.mailboxes threads sequences accessors arrays
|
||||||
|
math.parser ;
|
||||||
|
|
||||||
[ [ drop ] parallel-each ] must-infer
|
[ [ drop ] parallel-each ] must-infer
|
||||||
{ 2 0 } [ [ 2drop ] 2parallel-each ] must-infer-as
|
{ 2 0 } [ [ 2drop ] 2parallel-each ] must-infer-as
|
||||||
|
@ -45,3 +46,10 @@ concurrency.mailboxes threads sequences accessors arrays ;
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
[ { f } [ "OOPS" throw ] parallel-each ] must-fail
|
[ { f } [ "OOPS" throw ] parallel-each ] must-fail
|
||||||
|
|
||||||
|
[ "1a" "4b" "3c" ] [
|
||||||
|
2
|
||||||
|
{ [ 1- ] [ sq ] [ 1+ ] } parallel-cleave
|
||||||
|
[ number>string ] 3 parallel-napply
|
||||||
|
{ [ "a" append ] [ "b" append ] [ "c" append ] } parallel-spread
|
||||||
|
] unit-test
|
||||||
|
|
|
@ -1,34 +1,58 @@
|
||||||
! 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: concurrency.futures concurrency.count-downs sequences
|
USING: concurrency.futures concurrency.count-downs sequences
|
||||||
kernel ;
|
kernel macros fry combinators generalizations ;
|
||||||
IN: concurrency.combinators
|
IN: concurrency.combinators
|
||||||
|
|
||||||
<PRIVATE
|
<PRIVATE
|
||||||
|
|
||||||
: (parallel-each) ( n quot -- )
|
: (parallel-each) ( n quot -- )
|
||||||
>r <count-down> r> keep await ; inline
|
[ <count-down> ] dip keep await ; inline
|
||||||
|
|
||||||
PRIVATE>
|
PRIVATE>
|
||||||
|
|
||||||
: parallel-each ( seq quot -- )
|
: parallel-each ( seq quot -- )
|
||||||
over length [
|
over length [
|
||||||
[ >r curry r> spawn-stage ] 2curry each
|
'[ _ curry _ spawn-stage ] each
|
||||||
] (parallel-each) ; inline
|
] (parallel-each) ; inline
|
||||||
|
|
||||||
: 2parallel-each ( seq1 seq2 quot -- )
|
: 2parallel-each ( seq1 seq2 quot -- )
|
||||||
2over min-length [
|
2over min-length [
|
||||||
[ >r 2curry r> spawn-stage ] 2curry 2each
|
'[ _ 2curry _ spawn-stage ] 2each
|
||||||
] (parallel-each) ; inline
|
] (parallel-each) ; inline
|
||||||
|
|
||||||
: parallel-filter ( seq quot -- newseq )
|
: parallel-filter ( seq quot -- newseq )
|
||||||
over >r pusher >r each r> r> like ; inline
|
over [ pusher [ each ] dip ] dip like ; inline
|
||||||
|
|
||||||
<PRIVATE
|
<PRIVATE
|
||||||
|
|
||||||
|
: [future] ( quot -- quot' ) '[ _ curry future ] ; inline
|
||||||
|
|
||||||
: future-values dup [ ?future ] change-each ; inline
|
: future-values dup [ ?future ] change-each ; inline
|
||||||
|
|
||||||
PRIVATE>
|
PRIVATE>
|
||||||
|
|
||||||
: parallel-map ( seq quot -- newseq )
|
: parallel-map ( seq quot -- newseq )
|
||||||
[ curry future ] curry map future-values ;
|
[future] map future-values ; inline
|
||||||
inline
|
|
||||||
|
|
||||||
: 2parallel-map ( seq1 seq2 quot -- newseq )
|
: 2parallel-map ( seq1 seq2 quot -- newseq )
|
||||||
[ 2curry future ] curry 2map future-values ;
|
'[ _ 2curry future ] 2map future-values ;
|
||||||
|
|
||||||
|
<PRIVATE
|
||||||
|
|
||||||
|
: (parallel-spread) ( n -- spread-array )
|
||||||
|
[ ?future ] <repetition> ; inline
|
||||||
|
|
||||||
|
: (parallel-cleave) ( quots -- quot-array spread-array )
|
||||||
|
[ [future] ] map dup length (parallel-spread) ; inline
|
||||||
|
|
||||||
|
PRIVATE>
|
||||||
|
|
||||||
|
MACRO: parallel-cleave ( quots -- )
|
||||||
|
(parallel-cleave) '[ _ cleave _ spread ] ;
|
||||||
|
|
||||||
|
MACRO: parallel-spread ( quots -- )
|
||||||
|
(parallel-cleave) '[ _ spread _ spread ] ;
|
||||||
|
|
||||||
|
MACRO: parallel-napply ( quot n -- )
|
||||||
|
[ [future] ] dip dup (parallel-spread) '[ _ _ napply _ spread ] ;
|
||||||
|
|
|
@ -5,7 +5,7 @@ continuations help.markup help.syntax quotations ;
|
||||||
IN: concurrency.futures
|
IN: concurrency.futures
|
||||||
|
|
||||||
HELP: future
|
HELP: future
|
||||||
{ $values { "quot" "a quotation with stack effect " { $snippet "( -- value )" } } { "future" future } }
|
{ $values { "quot" { $quotation "( -- value )" } } { "future" future } }
|
||||||
{ $description "Creates a deferred computation."
|
{ $description "Creates a deferred computation."
|
||||||
$nl
|
$nl
|
||||||
"The quotation begins with an empty data stack, an empty catch stack, and a name stack containing the global namespace only. This means that the only way to pass data to the quotation is to partially apply the data, for example using " { $link curry } " or " { $link compose } "." } ;
|
"The quotation begins with an empty data stack, an empty catch stack, and a name stack containing the global namespace only. This means that the only way to pass data to the quotation is to partially apply the data, for example using " { $link curry } " or " { $link compose } "." } ;
|
||||||
|
|
|
@ -14,7 +14,7 @@ HELP: <reentrant-lock>
|
||||||
{ $description "Creates a reentrant lock." } ;
|
{ $description "Creates a reentrant lock." } ;
|
||||||
|
|
||||||
HELP: with-lock-timeout
|
HELP: with-lock-timeout
|
||||||
{ $values { "lock" lock } { "timeout" "a " { $link duration } " or " { $link f } } { "quot" quotation } }
|
{ $values { "lock" lock } { "timeout" { $maybe duration } } { "quot" quotation } }
|
||||||
{ $description "Calls the quotation, ensuring that only one thread executes with the lock held at a time. If another thread is holding the lock, blocks until the thread releases the lock." }
|
{ $description "Calls the quotation, ensuring that only one thread executes with the lock held at a time. If another thread is holding the lock, blocks until the thread releases the lock." }
|
||||||
{ $errors "Throws an error if the lock could not be acquired before the timeout expires. A timeout value of " { $link f } " means the thread is willing to wait indefinitely." } ;
|
{ $errors "Throws an error if the lock could not be acquired before the timeout expires. A timeout value of " { $link f } " means the thread is willing to wait indefinitely." } ;
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ HELP: rw-lock
|
||||||
{ $class-description "The class of reader/writer locks." } ;
|
{ $class-description "The class of reader/writer locks." } ;
|
||||||
|
|
||||||
HELP: with-read-lock-timeout
|
HELP: with-read-lock-timeout
|
||||||
{ $values { "lock" lock } { "timeout" "a " { $link duration } " or " { $link f } } { "quot" quotation } }
|
{ $values { "lock" lock } { "timeout" { $maybe duration } } { "quot" quotation } }
|
||||||
{ $description "Calls the quotation, ensuring that no other thread is holding a write lock at the same time. If another thread is holding a write lock, blocks until the thread releases the lock." }
|
{ $description "Calls the quotation, ensuring that no other thread is holding a write lock at the same time. If another thread is holding a write lock, blocks until the thread releases the lock." }
|
||||||
{ $errors "Throws an error if the lock could not be acquired before the timeout expires. A timeout value of " { $link f } " means the thread is willing to wait indefinitely." } ;
|
{ $errors "Throws an error if the lock could not be acquired before the timeout expires. A timeout value of " { $link f } " means the thread is willing to wait indefinitely." } ;
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ HELP: with-read-lock
|
||||||
{ $description "Calls the quotation, ensuring that no other thread is holding a write lock at the same time. If another thread is holding a write lock, blocks until the thread releases the lock." } ;
|
{ $description "Calls the quotation, ensuring that no other thread is holding a write lock at the same time. If another thread is holding a write lock, blocks until the thread releases the lock." } ;
|
||||||
|
|
||||||
HELP: with-write-lock-timeout
|
HELP: with-write-lock-timeout
|
||||||
{ $values { "lock" lock } { "timeout" "a " { $link duration } " or " { $link f } } { "quot" quotation } }
|
{ $values { "lock" lock } { "timeout" { $maybe duration } } { "quot" quotation } }
|
||||||
{ $description "Calls the quotation, ensuring that no other thread is holding a read or write lock at the same time. If another thread is holding a read or write lock, blocks until the thread releases the lock." }
|
{ $description "Calls the quotation, ensuring that no other thread is holding a read or write lock at the same time. If another thread is holding a read or write lock, blocks until the thread releases the lock." }
|
||||||
{ $errors "Throws an error if the lock could not be acquired before the timeout expires. A timeout value of " { $link f } " means the thread is willing to wait indefinitely." } ;
|
{ $errors "Throws an error if the lock could not be acquired before the timeout expires. A timeout value of " { $link f } " means the thread is willing to wait indefinitely." } ;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
USING: help.markup help.syntax kernel arrays ;
|
USING: help.markup help.syntax kernel arrays calendar ;
|
||||||
IN: concurrency.mailboxes
|
IN: concurrency.mailboxes
|
||||||
|
|
||||||
HELP: <mailbox>
|
HELP: <mailbox>
|
||||||
|
@ -18,46 +18,41 @@ HELP: mailbox-put
|
||||||
{ $description "Put the object into the mailbox. Any threads that have a blocking get on the mailbox are resumed. Only one of those threads will successfully get the object, the rest will immediately block waiting for the next item in the mailbox." } ;
|
{ $description "Put the object into the mailbox. Any threads that have a blocking get on the mailbox are resumed. Only one of those threads will successfully get the object, the rest will immediately block waiting for the next item in the mailbox." } ;
|
||||||
|
|
||||||
HELP: block-unless-pred
|
HELP: block-unless-pred
|
||||||
{ $values { "pred" "a quotation with stack effect " { $snippet "( X -- bool )" } }
|
{ $values { "pred" { $quotation "( obj -- ? )" } }
|
||||||
{ "mailbox" mailbox }
|
{ "mailbox" mailbox }
|
||||||
{ "timeout" "a timeout in milliseconds, or " { $link f } }
|
{ "timeout" "a " { $link duration } " or " { $link f } }
|
||||||
}
|
}
|
||||||
{ $description "Block the thread if there are no items in the mailbox that return true when the predicate is called with the item on the stack." } ;
|
{ $description "Block the thread if there are no items in the mailbox that return true when the predicate is called with the item on the stack." } ;
|
||||||
|
|
||||||
HELP: block-if-empty
|
HELP: block-if-empty
|
||||||
{ $values { "mailbox" mailbox }
|
{ $values { "mailbox" mailbox }
|
||||||
{ "timeout" "a timeout in milliseconds, or " { $link f } }
|
{ "timeout" "a " { $link duration } " or " { $link f } }
|
||||||
}
|
}
|
||||||
{ $description "Block the thread if the mailbox is empty." } ;
|
{ $description "Block the thread if the mailbox is empty." } ;
|
||||||
|
|
||||||
HELP: mailbox-get
|
HELP: mailbox-get
|
||||||
{ $values { "mailbox" mailbox }
|
{ $values { "mailbox" mailbox } { "obj" object } }
|
||||||
{ "obj" object }
|
|
||||||
}
|
|
||||||
{ $description "Get the first item put into the mailbox. If it is empty the thread blocks until an item is put into it. The thread then resumes, leaving the item on the stack." } ;
|
{ $description "Get the first item put into the mailbox. If it is empty the thread blocks until an item is put into it. The thread then resumes, leaving the item on the stack." } ;
|
||||||
|
|
||||||
HELP: mailbox-get-all
|
HELP: mailbox-get-all
|
||||||
{ $values { "mailbox" mailbox }
|
{ $values { "mailbox" mailbox } { "array" array } }
|
||||||
{ "array" array }
|
|
||||||
}
|
|
||||||
{ $description "Blocks the thread if the mailbox is empty, otherwise removes all objects in the mailbox and returns an array containing the objects." } ;
|
{ $description "Blocks the thread if the mailbox is empty, otherwise removes all objects in the mailbox and returns an array containing the objects." } ;
|
||||||
|
|
||||||
HELP: while-mailbox-empty
|
HELP: while-mailbox-empty
|
||||||
{ $values { "mailbox" mailbox }
|
{ $values { "mailbox" mailbox }
|
||||||
{ "quot" "a quotation with stack effect " { $snippet "( -- )" } }
|
{ "quot" { $quotation "( -- )" } }
|
||||||
}
|
}
|
||||||
{ $description "Repeatedly call the quotation while there are no items in the mailbox." } ;
|
{ $description "Repeatedly call the quotation while there are no items in the mailbox." } ;
|
||||||
|
|
||||||
HELP: mailbox-get?
|
HELP: mailbox-get?
|
||||||
{ $values { "mailbox" mailbox }
|
{ $values { "mailbox" mailbox }
|
||||||
{ "pred" "a quotation with stack effect " { $snippet "( X -- bool )" } }
|
{ "pred" { $quotation "( obj -- ? )" } }
|
||||||
{ "obj" object }
|
{ "obj" object }
|
||||||
}
|
}
|
||||||
{ $description "Get the first item in the mailbox which satisfies the predicate. 'pred' will be called repeatedly for each item in the mailbox. When 'pred' returns true that item will be returned. If nothing in the mailbox satisfies the predicate then the thread will block until something does." } ;
|
{ $description "Get the first item in the mailbox which satisfies the predicate. When the predicate returns true that item will be returned. If nothing in the mailbox satisfies the predicate then the thread will block until something does." } ;
|
||||||
|
|
||||||
|
|
||||||
ARTICLE: "concurrency.mailboxes" "Mailboxes"
|
ARTICLE: "concurrency.mailboxes" "Mailboxes"
|
||||||
"A " { $emphasis "mailbox" } " is a first-in-first-out queue where the operation of removing an element blocks if the queue is empty, instead of throwing an error. Mailboxes are implemented in the " { $vocab-link "concurrency.mailboxes" } " vocabulary."
|
"A " { $emphasis "mailbox" } " is a first-in-first-out queue where the operation of removing an element blocks if the queue is empty. Mailboxes are implemented in the " { $vocab-link "concurrency.mailboxes" } " vocabulary."
|
||||||
{ $subsection mailbox }
|
{ $subsection mailbox }
|
||||||
{ $subsection <mailbox> }
|
{ $subsection <mailbox> }
|
||||||
"Removing the first element:"
|
"Removing the first element:"
|
||||||
|
|
|
@ -12,7 +12,7 @@ HELP: promise-fulfilled?
|
||||||
{ $description "Tests if " { $link fulfill } " has previously been called on the promise, in which case " { $link ?promise } " will return immediately without blocking." } ;
|
{ $description "Tests if " { $link fulfill } " has previously been called on the promise, in which case " { $link ?promise } " will return immediately without blocking." } ;
|
||||||
|
|
||||||
HELP: ?promise-timeout
|
HELP: ?promise-timeout
|
||||||
{ $values { "promise" promise } { "timeout" "a " { $link duration } " or " { $link f } } { "result" object } }
|
{ $values { "promise" promise } { "timeout" { $maybe duration } } { "result" object } }
|
||||||
{ $description "Waits for another thread to fulfill a promise, returning immediately if the promise has already been fulfilled. A timeout of " { $link f } " indicates that the thread may block indefinitely, otherwise it will wait up to " { $snippet "timeout" } " milliseconds." }
|
{ $description "Waits for another thread to fulfill a promise, returning immediately if the promise has already been fulfilled. A timeout of " { $link f } " indicates that the thread may block indefinitely, otherwise it will wait up to " { $snippet "timeout" } " milliseconds." }
|
||||||
{ $errors "Throws an error if the timeout expires before the promise has been fulfilled." } ;
|
{ $errors "Throws an error if the timeout expires before the promise has been fulfilled." } ;
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ HELP: <semaphore>
|
||||||
{ $description "Creates a counting semaphore with the specified initial count." } ;
|
{ $description "Creates a counting semaphore with the specified initial count." } ;
|
||||||
|
|
||||||
HELP: acquire-timeout
|
HELP: acquire-timeout
|
||||||
{ $values { "semaphore" semaphore } { "timeout" "a " { $link duration } " or " { $link f } } }
|
{ $values { "semaphore" semaphore } { "timeout" { $maybe duration } } }
|
||||||
{ $description "If the semaphore has a non-zero count, decrements it and returns immediately. Otherwise, if the timeout is " { $link f } ", waits indefinitely for the semaphore to be released. If the timeout is not " { $link f } ", waits a certain period of time, and if the semaphore still has not been released, throws an error." }
|
{ $description "If the semaphore has a non-zero count, decrements it and returns immediately. Otherwise, if the timeout is " { $link f } ", waits indefinitely for the semaphore to be released. If the timeout is not " { $link f } ", waits a certain period of time, and if the semaphore still has not been released, throws an error." }
|
||||||
{ $errors "Throws an error if the timeout expires before the semaphore is released." } ;
|
{ $errors "Throws an error if the timeout expires before the semaphore is released." } ;
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ HELP: release
|
||||||
{ $description "Increments a semaphore's count. If the count was previously zero, any threads waiting on the semaphore are woken up." } ;
|
{ $description "Increments a semaphore's count. If the count was previously zero, any threads waiting on the semaphore are woken up." } ;
|
||||||
|
|
||||||
HELP: with-semaphore-timeout
|
HELP: with-semaphore-timeout
|
||||||
{ $values { "semaphore" semaphore } { "timeout" "a " { $link duration } " or " { $link f } } { "quot" quotation } }
|
{ $values { "semaphore" semaphore } { "timeout" { $maybe duration } } { "quot" quotation } }
|
||||||
{ $description "Calls the quotation with the semaphore held." } ;
|
{ $description "Calls the quotation with the semaphore held." } ;
|
||||||
|
|
||||||
HELP: with-semaphore
|
HELP: with-semaphore
|
||||||
|
|
|
@ -141,10 +141,10 @@ HOOK: %loop-entry cpu ( -- )
|
||||||
HOOK: small-enough? cpu ( n -- ? )
|
HOOK: small-enough? cpu ( n -- ? )
|
||||||
|
|
||||||
! Is this structure small enough to be returned in registers?
|
! Is this structure small enough to be returned in registers?
|
||||||
HOOK: struct-small-enough? cpu ( heap-size -- ? )
|
HOOK: struct-small-enough? cpu ( c-type -- ? )
|
||||||
|
|
||||||
! Do we pass value structs by value or hidden reference?
|
! Do we pass this struct by value or hidden reference?
|
||||||
HOOK: value-structs? cpu ( -- ? )
|
HOOK: value-struct? cpu ( c-type -- ? )
|
||||||
|
|
||||||
! If t, all parameters are shadowed by dummy stack parameters
|
! If t, all parameters are shadowed by dummy stack parameters
|
||||||
HOOK: dummy-stack-params? cpu ( -- ? )
|
HOOK: dummy-stack-params? cpu ( -- ? )
|
||||||
|
@ -207,14 +207,3 @@ M: object %callback-return drop %return ;
|
||||||
M: stack-params param-reg drop ;
|
M: stack-params param-reg drop ;
|
||||||
|
|
||||||
M: stack-params param-regs drop f ;
|
M: stack-params param-regs drop f ;
|
||||||
|
|
||||||
: if-small-struct ( n size true false -- ? )
|
|
||||||
[ 2dup [ not ] [ struct-small-enough? ] bi* and ] 2dip
|
|
||||||
[ '[ nip @ ] ] dip if ;
|
|
||||||
inline
|
|
||||||
|
|
||||||
: %unbox-struct ( n c-type -- )
|
|
||||||
[ %unbox-small-struct ] [ %unbox-large-struct ] if-small-struct ;
|
|
||||||
|
|
||||||
: %box-struct ( n c-type -- )
|
|
||||||
[ %box-small-struct ] [ %box-large-struct ] if-small-struct ;
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ M: linux lr-save 1 cells ;
|
||||||
|
|
||||||
M: float-regs param-regs drop { 1 2 3 4 5 6 7 8 } ;
|
M: float-regs param-regs drop { 1 2 3 4 5 6 7 8 } ;
|
||||||
|
|
||||||
M: ppc value-structs? f ;
|
M: ppc value-struct? drop f ;
|
||||||
|
|
||||||
M: ppc dummy-stack-params? f ;
|
M: ppc dummy-stack-params? f ;
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ M: macosx lr-save 2 cells ;
|
||||||
|
|
||||||
M: float-regs param-regs drop { 1 2 3 4 5 6 7 8 9 10 11 12 13 } ;
|
M: float-regs param-regs drop { 1 2 3 4 5 6 7 8 9 10 11 12 13 } ;
|
||||||
|
|
||||||
M: ppc value-structs? t ;
|
M: ppc value-struct? drop t ;
|
||||||
|
|
||||||
M: ppc dummy-stack-params? t ;
|
M: ppc dummy-stack-params? t ;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
! 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 layouts system math alien.c-types
|
USING: kernel layouts system math alien.c-types sequences
|
||||||
compiler.cfg.registers cpu.architecture cpu.x86.assembler cpu.x86 ;
|
compiler.cfg.registers cpu.architecture cpu.x86.assembler cpu.x86 ;
|
||||||
IN: cpu.x86.64.winnt
|
IN: cpu.x86.64.winnt
|
||||||
|
|
||||||
|
@ -10,8 +10,9 @@ M: float-regs param-regs drop { XMM0 XMM1 XMM2 XMM3 } ;
|
||||||
|
|
||||||
M: x86.64 reserved-area-size 4 cells ;
|
M: x86.64 reserved-area-size 4 cells ;
|
||||||
|
|
||||||
M: x86.64 struct-small-enough? ( size -- ? )
|
M: x86.64 struct-small-enough? heap-size { 1 2 4 8 } member? ;
|
||||||
heap-size cell <= ;
|
|
||||||
|
M: x86.64 value-struct? heap-size { 1 2 4 8 } member? ;
|
||||||
|
|
||||||
M: x86.64 dummy-stack-params? f ;
|
M: x86.64 dummy-stack-params? f ;
|
||||||
|
|
||||||
|
@ -21,6 +22,7 @@ M: x86.64 dummy-fp-params? t ;
|
||||||
|
|
||||||
<<
|
<<
|
||||||
"longlong" "ptrdiff_t" typedef
|
"longlong" "ptrdiff_t" typedef
|
||||||
|
"longlong" "intptr_t" typedef
|
||||||
"int" "long" typedef
|
"int" "long" typedef
|
||||||
"uint" "ulong" typedef
|
"uint" "ulong" typedef
|
||||||
>>
|
>>
|
||||||
|
|
|
@ -507,7 +507,7 @@ M: x86 %prepare-alien-invoke
|
||||||
temp-reg-1 2 cells [+] ds-reg MOV
|
temp-reg-1 2 cells [+] ds-reg MOV
|
||||||
temp-reg-1 3 cells [+] rs-reg MOV ;
|
temp-reg-1 3 cells [+] rs-reg MOV ;
|
||||||
|
|
||||||
M: x86 value-structs? t ;
|
M: x86 value-struct? drop t ;
|
||||||
|
|
||||||
M: x86 small-enough? ( n -- ? )
|
M: x86 small-enough? ( n -- ? )
|
||||||
HEX: -80000000 HEX: 7fffffff between? ;
|
HEX: -80000000 HEX: 7fffffff between? ;
|
||||||
|
|
|
@ -4,7 +4,7 @@ IN: deques
|
||||||
|
|
||||||
HELP: deque-empty?
|
HELP: deque-empty?
|
||||||
{ $values { "deque" deque } { "?" "a boolean" } }
|
{ $values { "deque" deque } { "?" "a boolean" } }
|
||||||
{ $description "Returns true if a deque is empty." }
|
{ $contract "Returns true if a deque is empty." }
|
||||||
{ $notes "This operation is O(1)." } ;
|
{ $notes "This operation is O(1)." } ;
|
||||||
|
|
||||||
HELP: clear-deque
|
HELP: clear-deque
|
||||||
|
@ -12,12 +12,6 @@ HELP: clear-deque
|
||||||
{ "deque" deque } }
|
{ "deque" deque } }
|
||||||
{ $description "Removes all elements from a deque." } ;
|
{ $description "Removes all elements from a deque." } ;
|
||||||
|
|
||||||
HELP: deque-length
|
|
||||||
{ $values
|
|
||||||
{ "deque" deque }
|
|
||||||
{ "n" integer } }
|
|
||||||
{ $description "Returns the number of elements in a deque." } ;
|
|
||||||
|
|
||||||
HELP: deque-member?
|
HELP: deque-member?
|
||||||
{ $values
|
{ $values
|
||||||
{ "value" object } { "deque" deque }
|
{ "value" object } { "deque" deque }
|
||||||
|
@ -31,7 +25,7 @@ HELP: push-front
|
||||||
|
|
||||||
HELP: push-front*
|
HELP: push-front*
|
||||||
{ $values { "obj" object } { "deque" deque } { "node" "a node" } }
|
{ $values { "obj" object } { "deque" deque } { "node" "a node" } }
|
||||||
{ $description "Push the object onto the front of the deque and return the newly created node." }
|
{ $contract "Push the object onto the front of the deque and return the newly created node." }
|
||||||
{ $notes "This operation is O(1)." } ;
|
{ $notes "This operation is O(1)." } ;
|
||||||
|
|
||||||
HELP: push-back
|
HELP: push-back
|
||||||
|
@ -41,7 +35,7 @@ HELP: push-back
|
||||||
|
|
||||||
HELP: push-back*
|
HELP: push-back*
|
||||||
{ $values { "obj" object } { "deque" deque } { "node" "a node" } }
|
{ $values { "obj" object } { "deque" deque } { "node" "a node" } }
|
||||||
{ $description "Push the object onto the back of the deque and return the newly created node." }
|
{ $contract "Push the object onto the back of the deque and return the newly created node." }
|
||||||
{ $notes "This operation is O(1)." } ;
|
{ $notes "This operation is O(1)." } ;
|
||||||
|
|
||||||
HELP: push-all-back
|
HELP: push-all-back
|
||||||
|
@ -56,7 +50,7 @@ HELP: push-all-front
|
||||||
|
|
||||||
HELP: peek-front
|
HELP: peek-front
|
||||||
{ $values { "deque" deque } { "obj" object } }
|
{ $values { "deque" deque } { "obj" object } }
|
||||||
{ $description "Returns the object at the front of the deque." } ;
|
{ $contract "Returns the object at the front of the deque." } ;
|
||||||
|
|
||||||
HELP: pop-front
|
HELP: pop-front
|
||||||
{ $values { "deque" deque } { "obj" object } }
|
{ $values { "deque" deque } { "obj" object } }
|
||||||
|
@ -65,12 +59,12 @@ HELP: pop-front
|
||||||
|
|
||||||
HELP: pop-front*
|
HELP: pop-front*
|
||||||
{ $values { "deque" deque } }
|
{ $values { "deque" deque } }
|
||||||
{ $description "Pop the object off the front of the deque." }
|
{ $contract "Pop the object off the front of the deque." }
|
||||||
{ $notes "This operation is O(1)." } ;
|
{ $notes "This operation is O(1)." } ;
|
||||||
|
|
||||||
HELP: peek-back
|
HELP: peek-back
|
||||||
{ $values { "deque" deque } { "obj" object } }
|
{ $values { "deque" deque } { "obj" object } }
|
||||||
{ $description "Returns the object at the back of the deque." } ;
|
{ $contract "Returns the object at the back of the deque." } ;
|
||||||
|
|
||||||
HELP: pop-back
|
HELP: pop-back
|
||||||
{ $values { "deque" deque } { "obj" object } }
|
{ $values { "deque" deque } { "obj" object } }
|
||||||
|
@ -79,13 +73,13 @@ HELP: pop-back
|
||||||
|
|
||||||
HELP: pop-back*
|
HELP: pop-back*
|
||||||
{ $values { "deque" deque } }
|
{ $values { "deque" deque } }
|
||||||
{ $description "Pop the object off the back of the deque." }
|
{ $contract "Pop the object off the back of the deque." }
|
||||||
{ $notes "This operation is O(1)." } ;
|
{ $notes "This operation is O(1)." } ;
|
||||||
|
|
||||||
HELP: delete-node
|
HELP: delete-node
|
||||||
{ $values
|
{ $values
|
||||||
{ "node" object } { "deque" deque } }
|
{ "node" object } { "deque" deque } }
|
||||||
{ $description "Deletes the node from the deque." } ;
|
{ $contract "Deletes the node from the deque." } ;
|
||||||
|
|
||||||
HELP: deque
|
HELP: deque
|
||||||
{ $description "A data structure that has constant-time insertion and removal of elements at both ends." } ;
|
{ $description "A data structure that has constant-time insertion and removal of elements at both ends." } ;
|
||||||
|
@ -111,7 +105,7 @@ $nl
|
||||||
"Querying the deque:"
|
"Querying the deque:"
|
||||||
{ $subsection peek-front }
|
{ $subsection peek-front }
|
||||||
{ $subsection peek-back }
|
{ $subsection peek-back }
|
||||||
{ $subsection deque-length }
|
{ $subsection deque-empty? }
|
||||||
{ $subsection deque-member? }
|
{ $subsection deque-member? }
|
||||||
"Adding and removing elements:"
|
"Adding and removing elements:"
|
||||||
{ $subsection push-front* }
|
{ $subsection push-front* }
|
||||||
|
@ -123,7 +117,6 @@ $nl
|
||||||
{ $subsection delete-node }
|
{ $subsection delete-node }
|
||||||
{ $subsection node-value }
|
{ $subsection node-value }
|
||||||
"Utility operations built in terms of the above:"
|
"Utility operations built in terms of the above:"
|
||||||
{ $subsection deque-empty? }
|
|
||||||
{ $subsection push-front }
|
{ $subsection push-front }
|
||||||
{ $subsection push-all-front }
|
{ $subsection push-all-front }
|
||||||
{ $subsection push-back }
|
{ $subsection push-back }
|
||||||
|
|
|
@ -10,13 +10,10 @@ GENERIC: peek-back ( deque -- obj )
|
||||||
GENERIC: pop-front* ( deque -- )
|
GENERIC: pop-front* ( deque -- )
|
||||||
GENERIC: pop-back* ( deque -- )
|
GENERIC: pop-back* ( deque -- )
|
||||||
GENERIC: delete-node ( node deque -- )
|
GENERIC: delete-node ( node deque -- )
|
||||||
GENERIC: deque-length ( deque -- n )
|
|
||||||
GENERIC: deque-member? ( value deque -- ? )
|
GENERIC: deque-member? ( value deque -- ? )
|
||||||
GENERIC: clear-deque ( deque -- )
|
GENERIC: clear-deque ( deque -- )
|
||||||
GENERIC: node-value ( node -- value )
|
GENERIC: node-value ( node -- value )
|
||||||
|
GENERIC: deque-empty? ( deque -- ? )
|
||||||
: deque-empty? ( deque -- ? )
|
|
||||||
deque-length zero? ;
|
|
||||||
|
|
||||||
: push-front ( obj deque -- )
|
: push-front ( obj deque -- )
|
||||||
push-front* drop ;
|
push-front* drop ;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
USING: help.markup help.syntax kernel quotations
|
USING: help.markup help.syntax kernel quotations
|
||||||
deques ;
|
deques search-deques hashtables ;
|
||||||
IN: dlists
|
IN: dlists
|
||||||
|
|
||||||
ARTICLE: "dlists" "Double-linked lists"
|
ARTICLE: "dlists" "Double-linked lists"
|
||||||
|
@ -18,10 +18,20 @@ $nl
|
||||||
{ $subsection dlist-contains? }
|
{ $subsection dlist-contains? }
|
||||||
"Deleting a node matching a predicate:"
|
"Deleting a node matching a predicate:"
|
||||||
{ $subsection delete-node-if* }
|
{ $subsection delete-node-if* }
|
||||||
{ $subsection delete-node-if } ;
|
{ $subsection delete-node-if }
|
||||||
|
"Search deque implementation:"
|
||||||
|
{ $subsection <hashed-dlist> } ;
|
||||||
|
|
||||||
ABOUT: "dlists"
|
ABOUT: "dlists"
|
||||||
|
|
||||||
|
HELP: <dlist>
|
||||||
|
{ $values { "list" dlist } }
|
||||||
|
{ $description "Creates a new double-linked list." } ;
|
||||||
|
|
||||||
|
HELP: <hashed-dlist>
|
||||||
|
{ $values { "search-deque" search-deque } }
|
||||||
|
{ $description "Creates a new " { $link search-deque } " backed by a " { $link dlist } ", with a " { $link hashtable } " for fast membership tests." } ;
|
||||||
|
|
||||||
HELP: dlist-find
|
HELP: dlist-find
|
||||||
{ $values { "dlist" { $link dlist } } { "quot" quotation } { "obj/f" "an object or " { $link f } } { "?" "a boolean" } }
|
{ $values { "dlist" { $link dlist } } { "quot" quotation } { "obj/f" "an object or " { $link f } } { "?" "a boolean" } }
|
||||||
{ $description "Applies the quotation to each element of the " { $link dlist } " in turn, until it outputs a true value or the end of the " { $link dlist } " is reached. Outputs either the object it found or " { $link f } ", and a boolean which is true if an object is found." }
|
{ $description "Applies the quotation to each element of the " { $link dlist } " in turn, until it outputs a true value or the end of the " { $link dlist } " is reached. Outputs either the object it found or " { $link f } ", and a boolean which is true if an object is found." }
|
||||||
|
|
|
@ -5,7 +5,7 @@ IN: dlists.tests
|
||||||
|
|
||||||
[ t ] [ <dlist> deque-empty? ] unit-test
|
[ t ] [ <dlist> deque-empty? ] unit-test
|
||||||
|
|
||||||
[ T{ dlist f T{ dlist-node f 1 f f } T{ dlist-node f 1 f f } 1 } ]
|
[ T{ dlist f T{ dlist-node f 1 f f } T{ dlist-node f 1 f f } } ]
|
||||||
[ <dlist> 1 over push-front ] unit-test
|
[ <dlist> 1 over push-front ] unit-test
|
||||||
|
|
||||||
! Make sure empty lists are empty
|
! Make sure empty lists are empty
|
||||||
|
@ -17,10 +17,10 @@ IN: dlists.tests
|
||||||
[ 1 ] [ <dlist> 1 over push-front pop-back ] unit-test
|
[ 1 ] [ <dlist> 1 over push-front pop-back ] unit-test
|
||||||
[ 1 ] [ <dlist> 1 over push-back pop-front ] unit-test
|
[ 1 ] [ <dlist> 1 over push-back pop-front ] unit-test
|
||||||
[ 1 ] [ <dlist> 1 over push-back pop-back ] unit-test
|
[ 1 ] [ <dlist> 1 over push-back pop-back ] unit-test
|
||||||
[ T{ dlist f f f 0 } ] [ <dlist> 1 over push-front dup pop-front* ] unit-test
|
[ T{ dlist f f f } ] [ <dlist> 1 over push-front dup pop-front* ] unit-test
|
||||||
[ T{ dlist f f f 0 } ] [ <dlist> 1 over push-front dup pop-back* ] unit-test
|
[ T{ dlist f f f } ] [ <dlist> 1 over push-front dup pop-back* ] unit-test
|
||||||
[ T{ dlist f f f 0 } ] [ <dlist> 1 over push-back dup pop-front* ] unit-test
|
[ T{ dlist f f f } ] [ <dlist> 1 over push-back dup pop-front* ] unit-test
|
||||||
[ T{ dlist f f f 0 } ] [ <dlist> 1 over push-back dup pop-back* ] unit-test
|
[ T{ dlist f f f } ] [ <dlist> 1 over push-back dup pop-back* ] unit-test
|
||||||
|
|
||||||
! Test the prev,next links for two nodes
|
! Test the prev,next links for two nodes
|
||||||
[ f ] [
|
[ f ] [
|
||||||
|
@ -52,15 +52,6 @@ IN: dlists.tests
|
||||||
[ 1 ] [ <dlist> 1 over push-back [ 1 = ] delete-node-if ] unit-test
|
[ 1 ] [ <dlist> 1 over push-back [ 1 = ] delete-node-if ] unit-test
|
||||||
[ t ] [ <dlist> 1 over push-back dup [ 1 = ] delete-node-if drop deque-empty? ] unit-test
|
[ t ] [ <dlist> 1 over push-back dup [ 1 = ] delete-node-if drop deque-empty? ] unit-test
|
||||||
[ t ] [ <dlist> 1 over push-back dup [ 1 = ] delete-node-if drop deque-empty? ] unit-test
|
[ t ] [ <dlist> 1 over push-back dup [ 1 = ] delete-node-if drop deque-empty? ] unit-test
|
||||||
[ 0 ] [ <dlist> 1 over push-back dup [ 1 = ] delete-node-if drop deque-length ] unit-test
|
|
||||||
[ 1 ] [ <dlist> 1 over push-back 2 over push-back dup [ 1 = ] delete-node-if drop deque-length ] unit-test
|
|
||||||
[ 2 ] [ <dlist> 1 over push-back 2 over push-back 3 over push-back dup [ 1 = ] delete-node-if drop deque-length ] unit-test
|
|
||||||
[ 2 ] [ <dlist> 1 over push-back 2 over push-back 3 over push-back dup [ 2 = ] delete-node-if drop deque-length ] unit-test
|
|
||||||
[ 2 ] [ <dlist> 1 over push-back 2 over push-back 3 over push-back dup [ 3 = ] delete-node-if drop deque-length ] unit-test
|
|
||||||
|
|
||||||
[ 0 ] [ <dlist> deque-length ] unit-test
|
|
||||||
[ 1 ] [ <dlist> 1 over push-front deque-length ] unit-test
|
|
||||||
[ 0 ] [ <dlist> 1 over push-front dup pop-front* deque-length ] unit-test
|
|
||||||
|
|
||||||
[ t ] [ <dlist> 4 over push-back 5 over push-back [ obj>> 4 = ] dlist-find-node drop class dlist-node = ] unit-test
|
[ t ] [ <dlist> 4 over push-back 5 over push-back [ obj>> 4 = ] dlist-find-node drop class dlist-node = ] unit-test
|
||||||
[ t ] [ <dlist> 4 over push-back 5 over push-back [ obj>> 5 = ] dlist-find-node drop class dlist-node = ] unit-test
|
[ t ] [ <dlist> 4 over push-back 5 over push-back [ obj>> 5 = ] dlist-find-node drop class dlist-node = ] unit-test
|
||||||
|
|
|
@ -2,51 +2,57 @@
|
||||||
! Slava Pestov.
|
! Slava Pestov.
|
||||||
! See http://factorcode.org/license.txt for BSD license.
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
USING: combinators kernel math sequences accessors deques
|
USING: combinators kernel math sequences accessors deques
|
||||||
summary ;
|
search-deques summary hashtables ;
|
||||||
IN: dlists
|
IN: dlists
|
||||||
|
|
||||||
TUPLE: dlist front back length ;
|
|
||||||
|
|
||||||
: <dlist> ( -- obj )
|
|
||||||
dlist new
|
|
||||||
0 >>length ;
|
|
||||||
|
|
||||||
M: dlist deque-length length>> ;
|
|
||||||
|
|
||||||
<PRIVATE
|
<PRIVATE
|
||||||
|
|
||||||
TUPLE: dlist-node obj prev next ;
|
MIXIN: ?dlist-node
|
||||||
|
|
||||||
|
INSTANCE: f ?dlist-node
|
||||||
|
|
||||||
|
TUPLE: dlist-node obj { prev ?dlist-node } { next ?dlist-node } ;
|
||||||
|
|
||||||
|
INSTANCE: dlist-node ?dlist-node
|
||||||
|
|
||||||
C: <dlist-node> dlist-node
|
C: <dlist-node> dlist-node
|
||||||
|
|
||||||
|
PRIVATE>
|
||||||
|
|
||||||
|
TUPLE: dlist
|
||||||
|
{ front ?dlist-node }
|
||||||
|
{ back ?dlist-node } ;
|
||||||
|
|
||||||
|
: <dlist> ( -- list )
|
||||||
|
dlist new ; inline
|
||||||
|
|
||||||
|
: <hashed-dlist> ( -- search-deque )
|
||||||
|
20 <hashtable> <dlist> <search-deque> ;
|
||||||
|
|
||||||
|
M: dlist deque-empty? front>> not ;
|
||||||
|
|
||||||
M: dlist-node node-value obj>> ;
|
M: dlist-node node-value obj>> ;
|
||||||
|
|
||||||
: inc-length ( dlist -- )
|
|
||||||
[ 1+ ] change-length drop ; inline
|
|
||||||
|
|
||||||
: dec-length ( dlist -- )
|
|
||||||
[ 1- ] change-length drop ; inline
|
|
||||||
|
|
||||||
: set-prev-when ( dlist-node dlist-node/f -- )
|
: set-prev-when ( dlist-node dlist-node/f -- )
|
||||||
[ (>>prev) ] [ drop ] if* ;
|
[ (>>prev) ] [ drop ] if* ; inline
|
||||||
|
|
||||||
: set-next-when ( dlist-node dlist-node/f -- )
|
: set-next-when ( dlist-node dlist-node/f -- )
|
||||||
[ (>>next) ] [ drop ] if* ;
|
[ (>>next) ] [ drop ] if* ; inline
|
||||||
|
|
||||||
: set-next-prev ( dlist-node -- )
|
: set-next-prev ( dlist-node -- )
|
||||||
dup next>> set-prev-when ;
|
dup next>> set-prev-when ; inline
|
||||||
|
|
||||||
: normalize-front ( dlist -- )
|
: normalize-front ( dlist -- )
|
||||||
dup back>> [ f >>front ] unless drop ;
|
dup back>> [ f >>front ] unless drop ; inline
|
||||||
|
|
||||||
: normalize-back ( dlist -- )
|
: normalize-back ( dlist -- )
|
||||||
dup front>> [ f >>back ] unless drop ;
|
dup front>> [ f >>back ] unless drop ; inline
|
||||||
|
|
||||||
: set-back-to-front ( dlist -- )
|
: set-back-to-front ( dlist -- )
|
||||||
dup back>> [ dup front>> >>back ] unless drop ;
|
dup back>> [ dup front>> >>back ] unless drop ; inline
|
||||||
|
|
||||||
: set-front-to-back ( dlist -- )
|
: set-front-to-back ( dlist -- )
|
||||||
dup front>> [ dup back>> >>front ] unless drop ;
|
dup front>> [ dup back>> >>front ] unless drop ; inline
|
||||||
|
|
||||||
: (dlist-find-node) ( dlist-node quot: ( node -- ? ) -- node/f ? )
|
: (dlist-find-node) ( dlist-node quot: ( node -- ? ) -- node/f ? )
|
||||||
over [
|
over [
|
||||||
|
@ -62,22 +68,20 @@ M: dlist-node node-value obj>> ;
|
||||||
|
|
||||||
: unlink-node ( dlist-node -- )
|
: unlink-node ( dlist-node -- )
|
||||||
dup prev>> over next>> set-prev-when
|
dup prev>> over next>> set-prev-when
|
||||||
dup next>> swap prev>> set-next-when ;
|
dup next>> swap prev>> set-next-when ; inline
|
||||||
|
|
||||||
PRIVATE>
|
PRIVATE>
|
||||||
|
|
||||||
M: dlist push-front* ( obj dlist -- dlist-node )
|
M: dlist push-front* ( obj dlist -- dlist-node )
|
||||||
[ front>> f swap <dlist-node> dup dup set-next-prev ] keep
|
[ front>> f swap <dlist-node> dup dup set-next-prev ] keep
|
||||||
[ (>>front) ] keep
|
[ (>>front) ] keep
|
||||||
[ set-back-to-front ] keep
|
set-back-to-front ;
|
||||||
inc-length ;
|
|
||||||
|
|
||||||
M: dlist push-back* ( obj dlist -- dlist-node )
|
M: dlist push-back* ( obj dlist -- dlist-node )
|
||||||
[ back>> f <dlist-node> ] keep
|
[ back>> f <dlist-node> ] keep
|
||||||
[ back>> set-next-when ] 2keep
|
[ back>> set-next-when ] 2keep
|
||||||
[ (>>back) ] 2keep
|
[ (>>back) ] 2keep
|
||||||
[ set-front-to-back ] keep
|
set-front-to-back ;
|
||||||
inc-length ;
|
|
||||||
|
|
||||||
ERROR: empty-dlist ;
|
ERROR: empty-dlist ;
|
||||||
|
|
||||||
|
@ -88,31 +92,27 @@ M: dlist peek-front ( dlist -- obj )
|
||||||
front>> [ obj>> ] [ empty-dlist ] if* ;
|
front>> [ obj>> ] [ empty-dlist ] if* ;
|
||||||
|
|
||||||
M: dlist pop-front* ( dlist -- )
|
M: dlist pop-front* ( dlist -- )
|
||||||
dup front>> [ empty-dlist ] unless
|
|
||||||
[
|
[
|
||||||
dup front>>
|
dup front>> [ empty-dlist ] unless*
|
||||||
dup next>>
|
dup next>>
|
||||||
f rot (>>next)
|
f rot (>>next)
|
||||||
f over set-prev-when
|
f over set-prev-when
|
||||||
swap (>>front)
|
swap (>>front)
|
||||||
] keep
|
] keep
|
||||||
[ normalize-back ] keep
|
normalize-back ;
|
||||||
dec-length ;
|
|
||||||
|
|
||||||
M: dlist peek-back ( dlist -- obj )
|
M: dlist peek-back ( dlist -- obj )
|
||||||
back>> [ obj>> ] [ empty-dlist ] if* ;
|
back>> [ obj>> ] [ empty-dlist ] if* ;
|
||||||
|
|
||||||
M: dlist pop-back* ( dlist -- )
|
M: dlist pop-back* ( dlist -- )
|
||||||
dup back>> [ empty-dlist ] unless
|
|
||||||
[
|
[
|
||||||
dup back>>
|
dup back>> [ empty-dlist ] unless*
|
||||||
dup prev>>
|
dup prev>>
|
||||||
f rot (>>prev)
|
f rot (>>prev)
|
||||||
f over set-next-when
|
f over set-next-when
|
||||||
swap (>>back)
|
swap (>>back)
|
||||||
] keep
|
] keep
|
||||||
[ normalize-front ] keep
|
normalize-front ;
|
||||||
dec-length ;
|
|
||||||
|
|
||||||
: dlist-find ( dlist quot -- obj/f ? )
|
: dlist-find ( dlist quot -- obj/f ? )
|
||||||
[ obj>> ] prepose
|
[ obj>> ] prepose
|
||||||
|
@ -128,7 +128,7 @@ M: dlist delete-node ( dlist-node dlist -- )
|
||||||
{
|
{
|
||||||
{ [ 2dup front>> eq? ] [ nip pop-front* ] }
|
{ [ 2dup front>> eq? ] [ nip pop-front* ] }
|
||||||
{ [ 2dup back>> eq? ] [ nip pop-back* ] }
|
{ [ 2dup back>> eq? ] [ nip pop-back* ] }
|
||||||
[ dec-length unlink-node ]
|
[ drop unlink-node ]
|
||||||
} cond ;
|
} cond ;
|
||||||
|
|
||||||
: delete-node-if* ( dlist quot -- obj/f ? )
|
: delete-node-if* ( dlist quot -- obj/f ? )
|
||||||
|
@ -148,7 +148,6 @@ M: dlist delete-node ( dlist-node dlist -- )
|
||||||
M: dlist clear-deque ( dlist -- )
|
M: dlist clear-deque ( dlist -- )
|
||||||
f >>front
|
f >>front
|
||||||
f >>back
|
f >>back
|
||||||
0 >>length
|
|
||||||
drop ;
|
drop ;
|
||||||
|
|
||||||
: dlist-each ( dlist quot -- )
|
: dlist-each ( dlist quot -- )
|
||||||
|
|
|
@ -42,7 +42,7 @@ HELP: doc-lines
|
||||||
{ $errors "Throws an error if " { $snippet "from" } " or " { $snippet "to" } " is out of bounds." } ;
|
{ $errors "Throws an error if " { $snippet "from" } " or " { $snippet "to" } " is out of bounds." } ;
|
||||||
|
|
||||||
HELP: each-line
|
HELP: each-line
|
||||||
{ $values { "from" "a non-negative integer" } { "to" "a non-negative integer" } { "quot" "a quotation with stack effect " { $snippet "( string -- )" } } }
|
{ $values { "from" "a non-negative integer" } { "to" "a non-negative integer" } { "quot" { $quotation "( string -- )" } } }
|
||||||
{ $description "Applies the quotation to each line in the range." }
|
{ $description "Applies the quotation to each line in the range." }
|
||||||
{ $notes "The range is created by calling " { $link <slice> } "." }
|
{ $notes "The range is created by calling " { $link <slice> } "." }
|
||||||
{ $errors "Throws an error if " { $snippet "from" } " or " { $snippet "to" } " is out of bounds." } ;
|
{ $errors "Throws an error if " { $snippet "from" } " or " { $snippet "to" } " is out of bounds." } ;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
USING: definitions io.launcher kernel parser words sequences math
|
USING: definitions io.launcher kernel parser words sequences math
|
||||||
math.parser namespaces editors make ;
|
math.parser namespaces editors make system ;
|
||||||
IN: editors.emacs
|
IN: editors.emacs
|
||||||
|
|
||||||
: emacsclient ( file line -- )
|
: emacsclient ( file line -- )
|
||||||
[
|
[
|
||||||
\ emacsclient get "emacsclient" or ,
|
\ emacsclient get "emacsclient" or ,
|
||||||
"--no-wait" ,
|
os windows? [ "--no-wait" , ] unless
|
||||||
"+" swap number>string append ,
|
"+" swap number>string append ,
|
||||||
,
|
,
|
||||||
] { } make try-process ;
|
] { } make try-process ;
|
||||||
|
|
|
@ -64,7 +64,7 @@ C-STRUCT: glyph
|
||||||
{ "FT_Pos" "advance-x" }
|
{ "FT_Pos" "advance-x" }
|
||||||
{ "FT_Pos" "advance-y" }
|
{ "FT_Pos" "advance-y" }
|
||||||
|
|
||||||
{ "long" "format" }
|
{ "intptr_t" "format" }
|
||||||
|
|
||||||
{ "int" "bitmap-rows" }
|
{ "int" "bitmap-rows" }
|
||||||
{ "int" "bitmap-width" }
|
{ "int" "bitmap-width" }
|
||||||
|
|
|
@ -15,7 +15,7 @@ HELP: fry
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
HELP: '[
|
HELP: '[
|
||||||
{ $syntax "code... ]" }
|
{ $syntax "'[ code... ]" }
|
||||||
{ $description "Literal fried quotation. Expands into code which takes values from the stack and substitutes them in place of the fry specifiers " { $link _ } " and " { $link @ } "." }
|
{ $description "Literal fried quotation. Expands into code which takes values from the stack and substitutes them in place of the fry specifiers " { $link _ } " and " { $link @ } "." }
|
||||||
{ $examples "See " { $link "fry.examples" } "." } ;
|
{ $examples "See " { $link "fry.examples" } "." } ;
|
||||||
|
|
||||||
|
@ -49,6 +49,8 @@ $nl
|
||||||
"{ 8 13 14 27 } [ even? ] 5 [ dup ] swap [ ? ] curry 3compose map"
|
"{ 8 13 14 27 } [ even? ] 5 [ dup ] swap [ ? ] curry 3compose map"
|
||||||
"{ 8 13 14 27 } [ even? dup 5 ? ] map"
|
"{ 8 13 14 27 } [ even? dup 5 ? ] map"
|
||||||
}
|
}
|
||||||
|
"The following is a no-op:"
|
||||||
|
{ $code "'[ @ ]" }
|
||||||
"Here are some built-in combinators rewritten in terms of fried quotations:"
|
"Here are some built-in combinators rewritten in terms of fried quotations:"
|
||||||
{ $table
|
{ $table
|
||||||
{ { $link literalize } { $snippet ": literalize '[ _ ] ;" } }
|
{ { $link literalize } { $snippet ": literalize '[ _ ] ;" } }
|
||||||
|
@ -74,18 +76,21 @@ ARTICLE: "fry.limitations" "Fried quotation limitations"
|
||||||
"As with " { $vocab-link "locals" } ", fried quotations cannot contain " { $link >r } " and " { $link r> } ". This is not a real limitation in practice, since " { $link dip } " can be used instead." ;
|
"As with " { $vocab-link "locals" } ", fried quotations cannot contain " { $link >r } " and " { $link r> } ". This is not a real limitation in practice, since " { $link dip } " can be used instead." ;
|
||||||
|
|
||||||
ARTICLE: "fry" "Fried quotations"
|
ARTICLE: "fry" "Fried quotations"
|
||||||
"A " { $emphasis "fried quotation" } " differs from a literal quotation in that when it is evaluated, instead of just pushing itself on the stack, it consumes zero or more stack values and inserts them into the quotation."
|
"The " { $vocab-link "fry" } " vocabulary implements " { $emphasis "fried quotation" } ". Conceptually, fried quotations are quotations with ``holes'' (more formally, " { $emphasis "fry specifiers" } "), and the holes are filled in when the fried quotation is pushed on the stack."
|
||||||
$nl
|
$nl
|
||||||
"Fried quotations are denoted with a special parsing word:"
|
"Fried quotations are started by a special parsing word:"
|
||||||
{ $subsection POSTPONE: '[ }
|
{ $subsection POSTPONE: '[ }
|
||||||
"Fried quotations contain zero or more " { $emphasis "fry specifiers" } ":"
|
"There are two types of fry specifiers; the first can hold a value, and the second ``splices'' a quotation, as if it were inserted without surrounding brackets:"
|
||||||
{ $subsection _ }
|
{ $subsection _ }
|
||||||
{ $subsection @ }
|
{ $subsection @ }
|
||||||
"When a fried quotation is being evaluated, values are consumed from the stack and spliced into the quotation from right to left."
|
"The holes are filled in with the top of stack going in the rightmost hole, the second item on the stack going in the second hole from the right, and so on."
|
||||||
{ $subsection "fry.examples" }
|
{ $subsection "fry.examples" }
|
||||||
{ $subsection "fry.philosophy" }
|
{ $subsection "fry.philosophy" }
|
||||||
{ $subsection "fry.limitations" }
|
{ $subsection "fry.limitations" }
|
||||||
"Quotations can also be fried without using a parsing word:"
|
"Fry is implemented as a parsing word which reads a quotation and scans for occurrences of " { $link _ } " and " { $link @ } "; these words are not actually executed, and doing so raises an error (this can happen if they're accidentally used outside of a fry)."
|
||||||
{ $subsection fry } ;
|
$nl
|
||||||
|
"Fried quotations can also be constructed without using a parsing word; this is useful when meta-programming:"
|
||||||
|
{ $subsection fry }
|
||||||
|
"Fried quotations are an abstraction on top of the " { $link "compositional-combinators" } "; their use is encouraged over the combinators, because often the fry form is shorter and clearer than the combinator form." ;
|
||||||
|
|
||||||
ABOUT: "fry"
|
ABOUT: "fry"
|
||||||
|
|
|
@ -4,7 +4,6 @@ USING: kernel sequences db.tuples alarms calendar db fry
|
||||||
furnace.db
|
furnace.db
|
||||||
furnace.cache
|
furnace.cache
|
||||||
furnace.asides
|
furnace.asides
|
||||||
furnace.referrer
|
|
||||||
furnace.sessions
|
furnace.sessions
|
||||||
furnace.conversations
|
furnace.conversations
|
||||||
furnace.auth.providers
|
furnace.auth.providers
|
||||||
|
@ -24,8 +23,7 @@ IN: furnace.alloy
|
||||||
<conversations>
|
<conversations>
|
||||||
<sessions>
|
<sessions>
|
||||||
] dip
|
] dip
|
||||||
<db-persistence>
|
<db-persistence> ;
|
||||||
<check-form-submissions> ;
|
|
||||||
|
|
||||||
: start-expiring ( db -- )
|
: start-expiring ( db -- )
|
||||||
'[
|
'[
|
||||||
|
|
|
@ -0,0 +1,193 @@
|
||||||
|
USING: assocs classes help.markup help.syntax kernel
|
||||||
|
quotations strings words furnace.auth.providers.db
|
||||||
|
checksums.sha2 furnace.auth.providers math byte-arrays
|
||||||
|
http multiline ;
|
||||||
|
IN: furnace.auth
|
||||||
|
|
||||||
|
HELP: <protected>
|
||||||
|
{ $values
|
||||||
|
{ "responder" "a responder" }
|
||||||
|
{ "protected" "a new responder" }
|
||||||
|
}
|
||||||
|
{ $description "Wraps a responder in a protected responder. Access to the wrapped responder will be conditional upon the client authenticating with the current authentication realm." } ;
|
||||||
|
|
||||||
|
HELP: >>encoded-password
|
||||||
|
{ $values { "user" user } { "string" string } }
|
||||||
|
{ $description "Sets the user's password by combining it with a random salt and encoding it with the current authentication realm's checksum." } ;
|
||||||
|
|
||||||
|
HELP: capabilities
|
||||||
|
{ $var-description "Global variable holding all defined capabilities. New capabilities may be defined with " { $link define-capability } "." } ;
|
||||||
|
|
||||||
|
HELP: check-login
|
||||||
|
{ $values { "password" string } { "username" string } { "user/f" { $maybe user } } }
|
||||||
|
{ $description "Checks a username/password pair with the current authentication realm. Outputs a user if authentication succeeded, otherwise outputs " { $link f } "." } ;
|
||||||
|
|
||||||
|
HELP: define-capability
|
||||||
|
{ $values { "word" symbol } }
|
||||||
|
{ $description "Defines a new capability by adding it to the " { $link capabilities } " global variable." } ;
|
||||||
|
|
||||||
|
HELP: encode-password
|
||||||
|
{ $values
|
||||||
|
{ "string" string } { "salt" integer }
|
||||||
|
{ "bytes" byte-array }
|
||||||
|
}
|
||||||
|
{ $description "Encodes a password with the current authentication realm's checksum." } ;
|
||||||
|
|
||||||
|
HELP: have-capabilities?
|
||||||
|
{ $values
|
||||||
|
{ "capabilities" "a sequence of capabilities" }
|
||||||
|
{ "?" "a boolean" }
|
||||||
|
}
|
||||||
|
{ $description "Tests if the currently logged-in user possesses the given capabilities." } ;
|
||||||
|
|
||||||
|
HELP: logged-in-user
|
||||||
|
{ $var-description "Holds the currently logged-in user." } ;
|
||||||
|
|
||||||
|
HELP: login-required
|
||||||
|
{ $values
|
||||||
|
{ "description" string } { "capabilities" "a sequence of capabilities" }
|
||||||
|
}
|
||||||
|
{ $description "Redirects the client to a login page." } ;
|
||||||
|
|
||||||
|
HELP: login-required*
|
||||||
|
{ $values
|
||||||
|
{ "description" string } { "capabilities" "a sequence of capabilities" } { "realm" "an authenticaiton realm" }
|
||||||
|
{ "response" response }
|
||||||
|
}
|
||||||
|
{ $contract "Constructs an HTTP response for redirecting the client to a login page." } ;
|
||||||
|
|
||||||
|
HELP: protected
|
||||||
|
{ $class-description "The class of protected responders. See " { $link "furnace.auth.protected" } " for a description of usage and slots." } ;
|
||||||
|
|
||||||
|
HELP: realm
|
||||||
|
{ $class-description "The class of authentication realms. See " { $link "furnace.auth.realms" } " for details." } ;
|
||||||
|
|
||||||
|
HELP: uchange
|
||||||
|
{ $values { "key" symbol } { "quot" { $quotation "( old -- new )" } } }
|
||||||
|
{ $description "Applies the quotation to the old value of the user profile variable, and assigns the resulting value back to the variable." } ;
|
||||||
|
|
||||||
|
HELP: uget
|
||||||
|
{ $values { "key" symbol } { "value" object } }
|
||||||
|
{ $description "Outputs the value of a user profile variable." } ;
|
||||||
|
|
||||||
|
HELP: uset
|
||||||
|
{ $values { "value" object } { "key" symbol } }
|
||||||
|
{ $description "Sets the value of a user profile variable." } ;
|
||||||
|
|
||||||
|
HELP: username
|
||||||
|
{ $values { "string/f" { $maybe string } }
|
||||||
|
}
|
||||||
|
{ $description "Outputs the currently logged-in username, or " { $link f } " if no user is logged in." } ;
|
||||||
|
HELP: users
|
||||||
|
{ $values { "provider" "an authentication provider" } }
|
||||||
|
{ $description "Outputs the current authentication provider." } ;
|
||||||
|
|
||||||
|
ARTICLE: "furnace.auth.capabilities" "Authentication capabilities"
|
||||||
|
"Every user in the authentication framework has a set of associated capabilities."
|
||||||
|
$nl
|
||||||
|
"Defining new capabilities:"
|
||||||
|
{ $subsection define-capability }
|
||||||
|
"Capabilities are stored in a global variable:"
|
||||||
|
{ $subsection capabilities }
|
||||||
|
"Protected resources can be restricted to users possessing certain capabilities only by storing a sequence of capabilities in the " { $slot "capabilities" } " slot of a " { $link protected } " instance." ;
|
||||||
|
|
||||||
|
ARTICLE: "furnace.auth.protected" "Protected resources"
|
||||||
|
"To restrict access to authenticated clients only, wrap a responder in a protected responder."
|
||||||
|
{ $subsection protected }
|
||||||
|
{ $subsection <protected> }
|
||||||
|
"Protected responders have the following two slots which may be set:"
|
||||||
|
{ $table
|
||||||
|
{ { $slot "description" } "A string identifying the protected resource for user interface purposes" }
|
||||||
|
{ { $slot "capabilities" } { "A sequence of capabilities; see " { $link "furnace.auth.capabilities" } } }
|
||||||
|
} ;
|
||||||
|
|
||||||
|
ARTICLE: "furnace.auth.realm-config" "Authentication realm configuration"
|
||||||
|
"Instances of subclasses of " { $link realm } " have the following slots which may be set:"
|
||||||
|
{ $table
|
||||||
|
{ { $slot "name" } "A string identifying the realm for user interface purposes" }
|
||||||
|
{ { $slot "users" } { "An authentication provider (see " { $link "furnace.auth.providers" } ". By default, the " { $link users-in-db } " provider is used." } }
|
||||||
|
{ { $slot "checksum" } { "An implementation of the checksum protocol used for verifying passwords (see " { $link "checksums" } "). The " { $link sha-256 } " checksum is used by default." } }
|
||||||
|
{ { $slot "users" } { "An authentication provider (see " { $link "furnace.auth.providers" } } }
|
||||||
|
{ { $slot "secure" } { "A boolean, that when set to a true value, forces the client to access the authentication realm via HTTPS. An attempt to access the realm via HTTP results in a redirect to the corresponding HTTPS URL. On by default." } }
|
||||||
|
} ;
|
||||||
|
|
||||||
|
ARTICLE: "furnace.auth.providers" "Authentication providers"
|
||||||
|
"The " { $vocab-link "furnace.auth" } " framework looks up users using an authentication provider. Different authentication providers can be swapped in to implement various authentication strategies."
|
||||||
|
$nl
|
||||||
|
"Each authentication realm has a provider stored in the " { $slot "users" } " slot. The default provider is " { $link users-in-db } "."
|
||||||
|
{ $subsection "furnace.auth.providers.protocol" }
|
||||||
|
{ $subsection "furnace.auth.providers.null" }
|
||||||
|
{ $subsection "furnace.auth.providers.assoc" }
|
||||||
|
{ $subsection "furnace.auth.providers.db" } ;
|
||||||
|
|
||||||
|
ARTICLE: "furnace.auth.features" "Optional authentication features"
|
||||||
|
"Vocabularies having names prefixed by " { $code "furnace.auth.features" } " implement optional features which can be enabled by calling special words. These words define new actions on an authentication realm."
|
||||||
|
{ $subsection "furnace.auth.features.deactivate-user" }
|
||||||
|
{ $subsection "furnace.auth.features.edit-profile" }
|
||||||
|
{ $subsection "furnace.auth.features.recover-password" }
|
||||||
|
{ $subsection "furnace.auth.features.registration" } ;
|
||||||
|
|
||||||
|
ARTICLE: "furnace.auth.realms" "Authentication realms"
|
||||||
|
"The superclass of authentication realms:"
|
||||||
|
{ $subsection realm }
|
||||||
|
"There are two concrete implementations:"
|
||||||
|
{ $subsection "furnace.auth.basic" }
|
||||||
|
{ $subsection "furnace.auth.login" }
|
||||||
|
"Authentication realms need to be configured after construction."
|
||||||
|
{ $subsection "furnace.auth.realm-config" } ;
|
||||||
|
|
||||||
|
ARTICLE: "furnace.auth.users" "User profiles"
|
||||||
|
"A responder wrapped in an authentication realm may access the currently logged-in user,"
|
||||||
|
{ $subsection logged-in-user }
|
||||||
|
"as well as the logged-in username:"
|
||||||
|
{ $subsection username }
|
||||||
|
"Values can also be stored in user profile variables:"
|
||||||
|
{ $subsection uget }
|
||||||
|
{ $subsection uset }
|
||||||
|
{ $subsection uchange }
|
||||||
|
"User profile variables have the same restrictions on their values as session variables; see " { $link "furnace.sessions.serialize" } " for a discussion." ;
|
||||||
|
|
||||||
|
ARTICLE: "furnace.auth.example" "Furnace authentication example"
|
||||||
|
"The " { $vocab-link "webapps.todo" } " vocabulary wraps all of its responders in a protected responder. The " { $slot "description" } " slot is set so that the login page contains the message ``You must log in to view your todo list'':"
|
||||||
|
{ $code
|
||||||
|
<" <protected>
|
||||||
|
"view your todo list" >>description">
|
||||||
|
}
|
||||||
|
"The " { $vocab-link "webapps.wiki" } " vocabulary defines a mix of protected and unprotected actions. One example of a protected action is that for deleting wiki pages, an action normally reserved for administrators. This action is protected with the following code:"
|
||||||
|
{ $code
|
||||||
|
<" <protected>
|
||||||
|
"delete wiki articles" >>description
|
||||||
|
{ can-delete-wiki-articles? } >>capabilities">
|
||||||
|
}
|
||||||
|
"The " { $vocab-link "websites.concatenative" } " vocabulary wraps all of its responders, including the wiki, in a login authentication realm:"
|
||||||
|
{ $code
|
||||||
|
<" : <login-config> ( responder -- responder' )
|
||||||
|
"Factor website" <login-realm>
|
||||||
|
"Factor website" >>name
|
||||||
|
allow-registration
|
||||||
|
allow-password-recovery
|
||||||
|
allow-edit-profile
|
||||||
|
allow-deactivation ;">
|
||||||
|
} ;
|
||||||
|
|
||||||
|
ARTICLE: "furnace.auth" "Furnace authentication"
|
||||||
|
"The " { $vocab-link "furnace.auth" } " vocabulary implements a pluggable authentication framework."
|
||||||
|
$nl
|
||||||
|
"Usernames and passwords are verified using an " { $emphasis "authentication provider" } "."
|
||||||
|
{ $subsection "furnace.auth.providers" }
|
||||||
|
"Users have capabilities assigned to them."
|
||||||
|
{ $subsection "furnace.auth.capabilities" }
|
||||||
|
"An " { $emphasis "authentication realm" } " is a responder which manages access to protected resources."
|
||||||
|
{ $subsection "furnace.auth.realms" }
|
||||||
|
"Actions contained inside an authentication realm can be protected by wrapping them with a responder."
|
||||||
|
{ $subsection "furnace.auth.protected" }
|
||||||
|
"Actions contained inside an authentication realm can access the currently logged-in user profile."
|
||||||
|
{ $subsection "furnace.auth.users" }
|
||||||
|
"Authentication realms can be adorned with additional functionality."
|
||||||
|
{ $subsection "furnace.auth.features" }
|
||||||
|
"An administration tool."
|
||||||
|
{ $subsection "furnace.auth.user-admin" }
|
||||||
|
"A concrete example."
|
||||||
|
{ $subsection "furnace.auth.example" } ;
|
||||||
|
|
||||||
|
ABOUT: "furnace.auth"
|
|
@ -0,0 +1,16 @@
|
||||||
|
USING: help.markup help.syntax ;
|
||||||
|
IN: furnace.auth.basic
|
||||||
|
|
||||||
|
HELP: <basic-auth-realm>
|
||||||
|
{ $values { "responder" "a responder" } { "name" "an authentication realm name" } { "realm" basic-auth-realm } }
|
||||||
|
{ $description "Wraps a responder in a basic authentication realm. The realm must be configured before use; see " { $link "furnace.auth.realm-config" } "." } ;
|
||||||
|
|
||||||
|
HELP: basic-auth-realm
|
||||||
|
{ $class-description "The basic authentication realm class. Slots are described in " { $link "furnace.auth.realm-config" } "." } ;
|
||||||
|
|
||||||
|
ARTICLE: "furnace.auth.basic" "Basic authentication"
|
||||||
|
"The " { $vocab-link "furnace.auth.basic" } " vocabulary implements HTTP basic authentication."
|
||||||
|
{ $subsection basic-auth-realm }
|
||||||
|
{ $subsection <basic-auth-realm> } ;
|
||||||
|
|
||||||
|
ABOUT: "furnace.auth.basic"
|
|
@ -0,0 +1,26 @@
|
||||||
|
USING: help.markup help.syntax kernel ;
|
||||||
|
IN: furnace.auth.features.deactivate-user
|
||||||
|
|
||||||
|
HELP: allow-deactivation
|
||||||
|
{ $values { "realm" "an authentication realm" } }
|
||||||
|
{ $description "Adds a " { $snippet "deactivate-user" } " action to an authentication realm." } ;
|
||||||
|
|
||||||
|
HELP: allow-deactivation?
|
||||||
|
{ $values { "?" "a boolean" } }
|
||||||
|
{ $description "Outputs true if the current authentication realm allows user profile deactivation." } ;
|
||||||
|
|
||||||
|
ARTICLE: "furnace.auth.features.deactivate-user" "User profile deactivation"
|
||||||
|
"The " { $vocab-link "furnace.auth.features.deactivate-user" } " vocabulary implements an authentication feature for user profile deactivation, allowing users to voluntarily deactivate their account."
|
||||||
|
$nl
|
||||||
|
"To enable this feature, call the following word on an authentication realm:"
|
||||||
|
{ $subsection allow-deactivation }
|
||||||
|
"To check if deactivation is enabled:"
|
||||||
|
{ $subsection allow-deactivation? }
|
||||||
|
"This feature adds a " { $snippet "deactivate-user" } " action to the realm, and a link to this action can be inserted in Chloe templates using the following XML snippet:"
|
||||||
|
{ $code
|
||||||
|
"<t:if t:code=\"furnace.auth.features.deactivate-user:allow-deactivation?\">"
|
||||||
|
" <t:button t:action=\"$realm/deactivate-user\">Deactivate user</t:button>"
|
||||||
|
"</t:if>"
|
||||||
|
} ;
|
||||||
|
|
||||||
|
ABOUT: "furnace.auth.features.deactivate-user"
|
|
@ -0,0 +1,24 @@
|
||||||
|
USING: help.markup help.syntax kernel ;
|
||||||
|
IN: furnace.auth.features.edit-profile
|
||||||
|
|
||||||
|
HELP: allow-edit-profile
|
||||||
|
{ $values { "realm" "an authentication realm" } }
|
||||||
|
{ $description "Adds an " { $snippet "edit-profile" } " action to an authentication realm." } ;
|
||||||
|
|
||||||
|
HELP: allow-edit-profile?
|
||||||
|
{ $values { "?" "a boolean" } }
|
||||||
|
{ $description "Outputs true if the current authentication realm allows user profile editing." } ;
|
||||||
|
|
||||||
|
ARTICLE: "furnace.auth.features.edit-profile" "User profile editing"
|
||||||
|
"The " { $vocab-link "furnace.auth.features.edit-profile" } " vocabulary implements an authentication feature for user profile editing, allowing users to change some details of their account."
|
||||||
|
$nl
|
||||||
|
"To enable this feature, call the following word on an authentication realm:"
|
||||||
|
{ $subsection allow-edit-profile }
|
||||||
|
"To check if profile editing is enabled:"
|
||||||
|
{ $subsection allow-edit-profile? }
|
||||||
|
"This feature adds an " { $snippet "edit-profile" } " action to the realm, and a link to this action can be inserted in Chloe templates using the following XML snippet:"
|
||||||
|
{ $code
|
||||||
|
"<t:if t:code=\"furnace.auth.features.edit-profile:allow-edit-profile?\">"
|
||||||
|
" <t:button t:action=\"$realm/edit-profile\">Edit profile</t:button>"
|
||||||
|
"</t:if>"
|
||||||
|
} ;
|
|
@ -58,7 +58,7 @@ IN: furnace.auth.features.edit-profile
|
||||||
<protected>
|
<protected>
|
||||||
"edit your profile" >>description ;
|
"edit your profile" >>description ;
|
||||||
|
|
||||||
: allow-edit-profile ( login -- login )
|
: allow-edit-profile ( realm -- realm )
|
||||||
<edit-profile-action> <auth-boilerplate> "edit-profile" add-responder ;
|
<edit-profile-action> <auth-boilerplate> "edit-profile" add-responder ;
|
||||||
|
|
||||||
: allow-edit-profile? ( -- ? )
|
: allow-edit-profile? ( -- ? )
|
||||||
|
|
|
@ -61,7 +61,7 @@
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<button>Update</button>
|
<button type="submit">Update</button>
|
||||||
<t:validation-errors />
|
<t:validation-errors />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<button>Recover password</button>
|
<button type="submit">Recover password</button>
|
||||||
|
|
||||||
</t:form>
|
</t:form>
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<button>Set password</button>
|
<button type="submit">Set password</button>
|
||||||
<t:validation-errors />
|
<t:validation-errors />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
USING: help.markup help.syntax kernel strings urls ;
|
||||||
|
IN: furnace.auth.features.recover-password
|
||||||
|
|
||||||
|
HELP: allow-password-recovery
|
||||||
|
{ $values { "realm" "an authentication realm" } }
|
||||||
|
{ $description "Adds a " { $snippet "recover-password" } " action to an authentication realm." } ;
|
||||||
|
|
||||||
|
HELP: allow-password-recovery?
|
||||||
|
{ $values { "?" "a boolean" } }
|
||||||
|
{ $description "Outputs true if the current authentication realm allows user password recovery." } ;
|
||||||
|
|
||||||
|
HELP: lost-password-from
|
||||||
|
{ $var-description "A variable with the source e-mail address of password recovery e-mails." } ;
|
||||||
|
|
||||||
|
ARTICLE: "furnace.auth.features.recover-password" "User password recovery"
|
||||||
|
"The " { $vocab-link "furnace.auth.features.recover-password" }
|
||||||
|
" vocabulary implements an authentication feature for user password recovery, allowing users to get a new password e-mailed to them in the event they forget their current one."
|
||||||
|
$nl
|
||||||
|
"To enable this feature, first call the following word on an authentication realm,"
|
||||||
|
{ $subsection allow-password-recovery }
|
||||||
|
"Then set a global configuration variable:"
|
||||||
|
{ $subsection lost-password-from }
|
||||||
|
"In addition, the " { $link "smtp" } " may need to be configured as well."
|
||||||
|
$nl
|
||||||
|
"To check if password recovery is enabled:"
|
||||||
|
{ $subsection allow-password-recovery? }
|
||||||
|
"This feature adds a " { $snippet "recover-password" } " action to the realm, and a link to this action can be inserted in Chloe templates using the following XML snippet:"
|
||||||
|
{ $code
|
||||||
|
"<t:if t:code=\"furnace.auth.features.recover-password:allow-password-recovery?\">"
|
||||||
|
" <t:button t:action=\"$realm/recover-password\">Recover password</t:button>"
|
||||||
|
"</t:if>"
|
||||||
|
} ;
|
||||||
|
|
||||||
|
ABOUT: "furnace.auth.features.recover-password"
|
|
@ -110,7 +110,7 @@ SYMBOL: lost-password-from
|
||||||
<page-action>
|
<page-action>
|
||||||
{ realm "features/recover-password/recover-4" } >>template ;
|
{ realm "features/recover-password/recover-4" } >>template ;
|
||||||
|
|
||||||
: allow-password-recovery ( login -- login )
|
: allow-password-recovery ( realm -- realm )
|
||||||
<recover-action-1> <auth-boilerplate>
|
<recover-action-1> <auth-boilerplate>
|
||||||
"recover-password" add-responder
|
"recover-password" add-responder
|
||||||
<recover-action-2> <auth-boilerplate>
|
<recover-action-2> <auth-boilerplate>
|
||||||
|
|
|
@ -62,7 +62,7 @@
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
<button>Register</button>
|
<button type="submit">Register</button>
|
||||||
<t:validation-errors />
|
<t:validation-errors />
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
USING: help.markup help.syntax kernel ;
|
||||||
|
IN: furnace.auth.features.registration
|
||||||
|
|
||||||
|
HELP: allow-registration
|
||||||
|
{ $values { "realm" "an authentication realm" } }
|
||||||
|
{ $description "Adds a " { $snippet "registration" } " action to an authentication realm." } ;
|
||||||
|
|
||||||
|
HELP: allow-registration?
|
||||||
|
{ $values { "?" "a boolean" } }
|
||||||
|
{ $description "Outputs true if the current authentication realm allows user registration." } ;
|
||||||
|
|
||||||
|
ARTICLE: "furnace.auth.features.registration" "User registration"
|
||||||
|
"The " { $vocab-link "furnace.auth.features.registration" } " vocabulary implements an authentication feature for user registration, allowing new users to create accounts."
|
||||||
|
$nl
|
||||||
|
"To enable this feature, call the following word on an authentication realm:"
|
||||||
|
{ $subsection allow-registration }
|
||||||
|
"To check if user registration is enabled:"
|
||||||
|
{ $subsection allow-registration? }
|
||||||
|
"This feature adds a " { $snippet "register" } " action to the realm. A link to this action is inserted on the login page if the " { $vocab-link "furnace.auth.login" } " authentication realm is used. Links to this action can be inserted from other pages using the following Chloe XML snippet:"
|
||||||
|
{ $code
|
||||||
|
"<t:if t:code=\"furnace.auth.features.registration:allow-registration?\">"
|
||||||
|
" <t:button t:action=\"$realm/register\">Register</t:button>"
|
||||||
|
"</t:if>"
|
||||||
|
} ;
|
|
@ -38,7 +38,7 @@ IN: furnace.auth.features.registration
|
||||||
<auth-boilerplate>
|
<auth-boilerplate>
|
||||||
<secure-realm-only> ;
|
<secure-realm-only> ;
|
||||||
|
|
||||||
: allow-registration ( login -- login )
|
: allow-registration ( realm -- realm )
|
||||||
<register-action> "register" add-responder ;
|
<register-action> "register" add-responder ;
|
||||||
|
|
||||||
: allow-registration? ( -- ? )
|
: allow-registration? ( -- ? )
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
USING: help.markup help.syntax kernel strings ;
|
||||||
|
IN: furnace.auth.login
|
||||||
|
|
||||||
|
HELP: <login-realm>
|
||||||
|
{ $values
|
||||||
|
{ "responder" "a responder" } { "name" string }
|
||||||
|
{ "realm" "a new responder" }
|
||||||
|
}
|
||||||
|
{ $description "Wraps a responder in a new login realm with the given name. The realm must be configured before use; see " { $link "furnace.auth.realm-config" } "." } ;
|
||||||
|
|
||||||
|
HELP: login-realm
|
||||||
|
{ $class-description "The login realm class. Slots are described in " { $link "furnace.auth.realm-config" } "." } ;
|
||||||
|
|
||||||
|
ARTICLE: "furnace.auth.login" "Login authentication"
|
||||||
|
"The " { $vocab-link "furnace.auth.login" } " vocabulary implements an authentication realm which displays a login page with a username and password field."
|
||||||
|
{ $subsection login-realm }
|
||||||
|
{ $subsection <login-realm> }
|
||||||
|
"The " { $snippet "logout" } " action logs the user out of the realm, and a link to this action can be inserted in Chloe templates using the following XML snippet:"
|
||||||
|
{ $code
|
||||||
|
"<t:button t:action=\"$login-realm/logout\">Logout</t:button>"
|
||||||
|
} ;
|
||||||
|
|
||||||
|
ABOUT: "furnace.auth.login"
|
|
@ -58,9 +58,13 @@ M: login-realm modify-form ( responder -- )
|
||||||
permit-id get [ delete-permit ] when*
|
permit-id get [ delete-permit ] when*
|
||||||
URL" $realm" end-aside ;
|
URL" $realm" end-aside ;
|
||||||
|
|
||||||
|
<PRIVATE
|
||||||
|
|
||||||
SYMBOL: description
|
SYMBOL: description
|
||||||
SYMBOL: capabilities
|
SYMBOL: capabilities
|
||||||
|
|
||||||
|
PRIVATE>
|
||||||
|
|
||||||
: flashed-variables { description capabilities } ;
|
: flashed-variables { description capabilities } ;
|
||||||
|
|
||||||
: login-failed ( -- * )
|
: login-failed ( -- * )
|
||||||
|
@ -107,7 +111,7 @@ M: login-realm login-required* ( description capabilities login -- response )
|
||||||
M: login-realm user-registered ( user realm -- )
|
M: login-realm user-registered ( user realm -- )
|
||||||
drop successful-login ;
|
drop successful-login ;
|
||||||
|
|
||||||
: <login-realm> ( responder name -- auth )
|
: <login-realm> ( responder name -- realm )
|
||||||
login-realm new-realm
|
login-realm new-realm
|
||||||
<login-action> "login" add-responder
|
<login-action> "login" add-responder
|
||||||
<logout-action> "logout" add-responder
|
<logout-action> "logout" add-responder
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
<button>Log in</button>
|
<button type="submit">Log in</button>
|
||||||
<t:validation-errors />
|
<t:validation-errors />
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
USING: help.markup help.syntax io.streams.string ;
|
||||||
|
IN: furnace.auth.providers.assoc
|
||||||
|
|
||||||
|
HELP: <users-in-memory>
|
||||||
|
{ $values { "provider" users-in-memory } }
|
||||||
|
{ $description "Creates a new authentication provider which stores the usernames and passwords in an associative mapping." } ;
|
||||||
|
|
||||||
|
ARTICLE: "furnace.auth.providers.assoc" "In-memory authentication provider"
|
||||||
|
"The " { $vocab-link "furnace.auth.providers.assoc" } " vocabulary implements an authentication provider which looks up usernames and passwords in an associative mapping."
|
||||||
|
{ $subsection users-in-memory }
|
||||||
|
{ $subsection <users-in-memory> }
|
||||||
|
"The " { $slot "assoc" } " slot of the " { $link users-in-memory } " tuple maps usernames to checksums of passwords." ;
|
||||||
|
|
||||||
|
ABOUT: "furnace.auth.providers.assoc"
|
|
@ -0,0 +1,13 @@
|
||||||
|
USING: help.markup help.syntax ;
|
||||||
|
IN: furnace.auth.providers.db
|
||||||
|
|
||||||
|
HELP: users-in-db
|
||||||
|
{ $class-description "Singleton class implementing the database authentication provider." } ;
|
||||||
|
|
||||||
|
ARTICLE: "furnace.auth.providers.db" "Database authentication provider"
|
||||||
|
"The " { $vocab-link "furnace.auth.providers.db" } " vocabulary implements an authentication provider which looks up authentication requests in the " { $snippet "USERS" } " table of the current database. The database schema is Factor-specific, and the table should be initialized by calling"
|
||||||
|
{ $code "users create-table" }
|
||||||
|
"The authentication provider class:"
|
||||||
|
{ $subsection users-in-db } ;
|
||||||
|
|
||||||
|
ABOUT: "furnace.auth.providers.db"
|
|
@ -0,0 +1,10 @@
|
||||||
|
USING: help.markup help.syntax ;
|
||||||
|
IN: furnace.auth.providers.null
|
||||||
|
|
||||||
|
HELP: no-users
|
||||||
|
{ $class-description "Singleton class implementing the dummy authentication provider." } ;
|
||||||
|
|
||||||
|
ARTICLE: "furnace.auth.providers.null" "Dummy authentication provider"
|
||||||
|
"The " { $vocab-link "furnace.auth.providers.null" } " vocabulary implements an authentication provider which refuses all authentication requests. It is only useful for testing purposes." ;
|
||||||
|
|
||||||
|
ABOUT: "furnace.auth.providers.null"
|
|
@ -0,0 +1,45 @@
|
||||||
|
USING: help.markup help.syntax strings ;
|
||||||
|
IN: furnace.auth.providers
|
||||||
|
|
||||||
|
HELP: user
|
||||||
|
{ $class-description "The class of users. Instances have the following slots:"
|
||||||
|
{ $table
|
||||||
|
{ { $slot "username" } { "The username, used to identify the user for login purposes" } }
|
||||||
|
{ { $slot "realname" } { "The user's real name, optional" } }
|
||||||
|
{ { $slot "password" } { "The user's password, encoded with a checksum" } }
|
||||||
|
{ { $slot "salt" } { "A random salt prepended to the password to ensure that two users with the same plain-text password still have different checksum output" } }
|
||||||
|
{ { $slot "email" } { "The user's e-mail address, optional" } }
|
||||||
|
{ { $slot "ticket" } { "Used for password recovery" } }
|
||||||
|
{ { $slot "capabilities" } { "A sequence of capabilities; see " { $link "furnace.auth.capabilities" } } }
|
||||||
|
{ { $slot "profile" } { "A hashtable with webapp-specific configuration" } }
|
||||||
|
{ { $slot "deleted" } { "A boolean indicating whether the user is active or not. This allows a user account to be deactivated without removing the user from the database" } }
|
||||||
|
{ { $slot "changed?" } { "A boolean indicating whether the user has changed since being retrieved from the database" } }
|
||||||
|
} } ;
|
||||||
|
|
||||||
|
HELP: add-user
|
||||||
|
{ $values { "provider" "an authentication provider" } { "user" user } }
|
||||||
|
{ $description "A utility word which calls " { $link new-user } " and throws an error if the user already exists." } ;
|
||||||
|
|
||||||
|
HELP: get-user
|
||||||
|
{ $values { "username" string } { "provider" "an authentication provider" } { "user/f" { $maybe user } } }
|
||||||
|
{ $contract "Looks up a username in the authentication provider." } ;
|
||||||
|
|
||||||
|
HELP: new-user
|
||||||
|
{ $values { "user" user } { "provider" "an authentication provider" } { "user/f" { $maybe user } } }
|
||||||
|
{ $contract "Adds a new user to the authentication provider. Outputs " { $link f } " if a user with this username already exists." } ;
|
||||||
|
|
||||||
|
HELP: update-user
|
||||||
|
{ $values { "user" user } { "provider" "an authentication provider" } }
|
||||||
|
{ $contract "Stores a user back to an authentication provider after being changed. This is a no-op with in-memory providers; providers which use an external store will save the user in this word. " } ;
|
||||||
|
|
||||||
|
ARTICLE: "furnace.auth.providers.protocol" "Authentication provider protocol"
|
||||||
|
"The " { $vocab-link "furnace.auth.providers" } " vocabulary implements a protocol for persistence and authentication of users."
|
||||||
|
$nl
|
||||||
|
"The class of users:"
|
||||||
|
{ $subsection user }
|
||||||
|
"Generic protocol:"
|
||||||
|
{ $subsection get-user }
|
||||||
|
{ $subsection new-user }
|
||||||
|
{ $subsection update-user } ;
|
||||||
|
|
||||||
|
ABOUT: "furnace.auth.providers.protocol"
|
|
@ -28,7 +28,7 @@ HELP: cset
|
||||||
{ $description "Sets the value of a conversation variable." } ;
|
{ $description "Sets the value of a conversation variable." } ;
|
||||||
|
|
||||||
HELP: cchange
|
HELP: cchange
|
||||||
{ $values { "key" symbol } { "quot" "a quotation with stack effect " { $snippet "( old -- new )" } } }
|
{ $values { "key" symbol } { "quot" { $quotation "( old -- new )" } } }
|
||||||
{ $description "Applies the quotation to the old value of the conversation variable, and assigns the resulting value back to the variable." } ;
|
{ $description "Applies the quotation to the old value of the conversation variable, and assigns the resulting value back to the variable." } ;
|
||||||
|
|
||||||
ARTICLE: "furnace.conversations" "Furnace conversation scope"
|
ARTICLE: "furnace.conversations" "Furnace conversation scope"
|
||||||
|
|
|
@ -1,159 +1,129 @@
|
||||||
USING: assocs help.markup help.syntax io.streams.string quotations sequences strings urls ;
|
USING: assocs help.markup help.syntax kernel
|
||||||
|
quotations sequences strings urls xml.data http ;
|
||||||
IN: furnace
|
IN: furnace
|
||||||
|
|
||||||
HELP: adjust-redirect-url
|
HELP: adjust-redirect-url
|
||||||
{ $values
|
{ $values { "url" url } { "url'" url } }
|
||||||
{ "url" url }
|
{ $description "Adjusts a redirection URL by filtering the URL's query parameters through the " { $link modify-redirect-query } " generic word on every responder involved in handling the current request." } ;
|
||||||
{ "url'" url }
|
|
||||||
}
|
|
||||||
{ $description "" } ;
|
|
||||||
|
|
||||||
HELP: adjust-url
|
HELP: adjust-url
|
||||||
{ $values
|
{ $values { "url" url } { "url'" url } }
|
||||||
{ "url" url }
|
{ $description "Adjusts a link URL by filtering the URL's query parameters through the " { $link modify-query } " generic word on every responder involved in handling the current request." } ;
|
||||||
{ "url'" url }
|
|
||||||
}
|
|
||||||
{ $description "" } ;
|
|
||||||
|
|
||||||
HELP: base-path
|
|
||||||
{ $values
|
|
||||||
{ "string" string }
|
|
||||||
{ "pair" null }
|
|
||||||
}
|
|
||||||
{ $description "" } ;
|
|
||||||
|
|
||||||
HELP: client-state
|
HELP: client-state
|
||||||
{ $values
|
{ $values { "key" string } { "value/f" { $maybe string } } }
|
||||||
{ "key" null }
|
{ $description "Looks up a cookie (if the current request is a GET or HEAD request) or a POST parameter (if the current request is a POST request)." }
|
||||||
{ "value/f" null }
|
{ $notes "This word is used by session management, conversation scope and asides." } ;
|
||||||
}
|
|
||||||
{ $description "" } ;
|
|
||||||
|
|
||||||
HELP: cookie-client-state
|
|
||||||
{ $values
|
|
||||||
{ "key" null } { "request" null }
|
|
||||||
{ "value/f" null }
|
|
||||||
}
|
|
||||||
{ $description "" } ;
|
|
||||||
|
|
||||||
HELP: each-responder
|
HELP: each-responder
|
||||||
{ $values
|
{ $values { "quot" { $quotation "( responder -- )" } } }
|
||||||
{ "quot" quotation }
|
{ $description "Applies the quotation to each responder involved in processing the current request." } ;
|
||||||
}
|
|
||||||
{ $description "" } ;
|
|
||||||
|
|
||||||
HELP: exit-continuation
|
|
||||||
{ $description "" } ;
|
|
||||||
|
|
||||||
HELP: exit-with
|
|
||||||
{ $values
|
|
||||||
{ "value" null }
|
|
||||||
}
|
|
||||||
{ $description "" } ;
|
|
||||||
|
|
||||||
HELP: hidden-form-field
|
HELP: hidden-form-field
|
||||||
{ $values
|
{ $values { "value" string } { "name" string } }
|
||||||
{ "value" null } { "name" null }
|
{ $description "Renders an HTML hidden form field tag." }
|
||||||
|
{ $notes "This word is used by session management, conversation scope and asides." }
|
||||||
|
{ $examples
|
||||||
|
{ $example
|
||||||
|
"USING: furnace io ;"
|
||||||
|
"\"bar\" \"foo\" hidden-form-field nl"
|
||||||
|
"<input type='hidden' name='foo' value='bar'/>"
|
||||||
}
|
}
|
||||||
{ $description "" } ;
|
} ;
|
||||||
|
|
||||||
HELP: link-attr
|
HELP: link-attr
|
||||||
{ $values
|
{ $values { "tag" tag } { "responder" "a responder" } }
|
||||||
{ "tag" null } { "responder" null }
|
{ $contract "Modifies an XHTML " { $snippet "a" } " tag." }
|
||||||
}
|
{ $notes "This word is called by " { $link "html.templates.chloe.tags.form" } "." }
|
||||||
{ $description "" } ;
|
{ $examples "Conversation scope adds attributes to link tags." } ;
|
||||||
|
|
||||||
HELP: modify-form
|
HELP: modify-form
|
||||||
{ $values
|
{ $values { "responder" "a responder" } }
|
||||||
{ "responder" null }
|
{ $contract "Emits hidden form fields using " { $link hidden-form-field } "." }
|
||||||
}
|
{ $notes "This word is called by " { $link "html.templates.chloe.tags.form" } "." }
|
||||||
{ $description "" } ;
|
{ $examples "Session management, conversation scope and asides use hidden form fields to pass state." } ;
|
||||||
|
|
||||||
HELP: modify-query
|
HELP: modify-query
|
||||||
{ $values
|
{ $values { "query" assoc } { "responder" "a responder" } { "query'" assoc } }
|
||||||
{ "query" null } { "responder" null }
|
{ $contract "Modifies the query parameters of a URL destined to be displayed as a link." }
|
||||||
{ "query'" null }
|
{ $notes "This word is called by " { $link "html.templates.chloe.tags.form" } "." }
|
||||||
}
|
{ $examples "Asides add query parameters to URLs." } ;
|
||||||
{ $description "" } ;
|
|
||||||
|
|
||||||
HELP: modify-redirect-query
|
HELP: modify-redirect-query
|
||||||
{ $values
|
{ $values { "query" assoc } { "responder" "a responder" } { "query'" assoc } }
|
||||||
{ "query" null } { "responder" null }
|
{ $contract "Modifies the query parameters of a URL destined to be used with a redirect." }
|
||||||
{ "query'" null }
|
{ $notes "This word is called by " { $link "furnace.redirection" } "." }
|
||||||
}
|
{ $examples "Conversation scope and asides add query parameters to redirect URLs." } ;
|
||||||
{ $description "" } ;
|
|
||||||
|
|
||||||
HELP: nested-forms-key
|
|
||||||
{ $description "" } ;
|
|
||||||
|
|
||||||
HELP: nested-responders
|
HELP: nested-responders
|
||||||
{ $values
|
{ $values { "seq" "a sequence of responders" } }
|
||||||
|
|
||||||
{ "seq" sequence }
|
|
||||||
}
|
|
||||||
{ $description "" } ;
|
|
||||||
|
|
||||||
HELP: post-client-state
|
|
||||||
{ $values
|
|
||||||
{ "key" null } { "request" null }
|
|
||||||
{ "value/f" null }
|
|
||||||
}
|
|
||||||
{ $description "" } ;
|
{ $description "" } ;
|
||||||
|
|
||||||
HELP: referrer
|
HELP: referrer
|
||||||
{ $values
|
{ $values { "referrer/f" { $maybe string } } }
|
||||||
|
{ $description "Outputs the current request's referrer URL." } ;
|
||||||
{ "referrer/f" null }
|
|
||||||
}
|
|
||||||
{ $description "" } ;
|
|
||||||
|
|
||||||
HELP: request-params
|
HELP: request-params
|
||||||
{ $values
|
{ $values { "request" request } { "assoc" assoc } }
|
||||||
{ "request" null }
|
{ $description "Outputs the query parameters (if the current request is a GET or HEAD request) or the POST parameters (if the current request is a POST request)." } ;
|
||||||
{ "assoc" assoc }
|
|
||||||
}
|
|
||||||
{ $description "" } ;
|
|
||||||
|
|
||||||
HELP: resolve-base-path
|
HELP: resolve-base-path
|
||||||
{ $values
|
{ $values { "string" string } { "string'" string } }
|
||||||
{ "string" string }
|
|
||||||
{ "string'" string }
|
|
||||||
}
|
|
||||||
{ $description "" } ;
|
{ $description "" } ;
|
||||||
|
|
||||||
HELP: resolve-template-path
|
HELP: resolve-template-path
|
||||||
{ $values
|
{ $values { "pair" "a pair with shape " { $snippet "{ class string }" } } { "path" "a pathname string" } }
|
||||||
{ "pair" null }
|
|
||||||
{ "path" "a pathname string" }
|
|
||||||
}
|
|
||||||
{ $description "" } ;
|
{ $description "" } ;
|
||||||
|
|
||||||
HELP: same-host?
|
HELP: same-host?
|
||||||
{ $values
|
{ $values { "url" url } { "?" "a boolean" } }
|
||||||
{ "url" url }
|
{ $description "Tests if the given URL is located on the same host as the URL of the current request." } ;
|
||||||
{ "?" "a boolean" }
|
|
||||||
}
|
|
||||||
{ $description "" } ;
|
|
||||||
|
|
||||||
HELP: user-agent
|
HELP: user-agent
|
||||||
{ $values
|
{ $values { "user-agent" { $maybe string } } }
|
||||||
|
{ $description "Outputs the user agent reported by the client for the current request." } ;
|
||||||
{ "user-agent" null }
|
|
||||||
}
|
|
||||||
{ $description "" } ;
|
|
||||||
|
|
||||||
HELP: vocab-path
|
HELP: vocab-path
|
||||||
{ $values
|
{ $values { "vocab" "a vocabulary specifier" } { "path" "a pathname string" } }
|
||||||
{ "vocab" "a vocabulary specifier" }
|
|
||||||
{ "path" "a pathname string" }
|
|
||||||
}
|
|
||||||
{ $description "" } ;
|
{ $description "" } ;
|
||||||
|
|
||||||
|
HELP: exit-with
|
||||||
|
{ $values { "value" object } }
|
||||||
|
{ $description "Exits from an outer " { $link with-exit-continuation } "." } ;
|
||||||
|
|
||||||
HELP: with-exit-continuation
|
HELP: with-exit-continuation
|
||||||
{ $values
|
{ $values { "quot" { $quotation { "( -- value )" } } } { "value" "a value returned by the quotation or an " { $link exit-with } " invocation" } }
|
||||||
{ "quot" quotation }
|
{ $description "Runs a quotation with the " { $link exit-continuation } " variable bound. Calling " { $link exit-with } " in the quotation will immediately return." }
|
||||||
}
|
{ $notes "Furnace actions and authentication realms wrap their execution in this combinator, allowing form validation failures and login requests, respectively, to immediately return an HTTP response to the client without running any more responder code." } ;
|
||||||
{ $description "" } ;
|
|
||||||
|
ARTICLE: "furnace.extension-points" "Furnace extension points"
|
||||||
|
"Furnace features such as session management, conversation scope and asides need to modify URLs in links and redirects, and insert hidden form fields, to implement state on top of the stateless HTTP protocol. In order to decouple the server-side state management code from the HTML templating code, a series of hooks are used."
|
||||||
|
$nl
|
||||||
|
"Responders can implement methods on the following generic words:"
|
||||||
|
{ $subsection modify-query }
|
||||||
|
{ $subsection modify-redirect-query }
|
||||||
|
{ $subsection link-attr }
|
||||||
|
{ $subsection modify-form }
|
||||||
|
"Presentation-level code can call the following words:"
|
||||||
|
{ $subsection adjust-url }
|
||||||
|
{ $subsection adjust-redirect-url } ;
|
||||||
|
|
||||||
|
ARTICLE: "furnace.misc" "Miscellaneous Furnace features"
|
||||||
|
"Inspecting the chain of responders handling the current request:"
|
||||||
|
{ $subsection nested-responders }
|
||||||
|
{ $subsection each-responder }
|
||||||
|
{ $subsection resolve-base-path }
|
||||||
|
"Vocabulary root-relative resources:"
|
||||||
|
{ $subsection vocab-path }
|
||||||
|
{ $subsection resolve-template-path }
|
||||||
|
"Early return from a responder:"
|
||||||
|
{ $subsection with-exit-continuation }
|
||||||
|
{ $subsection exit-with }
|
||||||
|
"Other useful words:"
|
||||||
|
{ $subsection hidden-form-field }
|
||||||
|
{ $subsection request-params }
|
||||||
|
{ $subsection client-state }
|
||||||
|
{ $subsection user-agent } ;
|
||||||
|
|
||||||
ARTICLE: "furnace.persistence" "Furnace persistence layer"
|
ARTICLE: "furnace.persistence" "Furnace persistence layer"
|
||||||
{ $subsection "furnace.db" }
|
{ $subsection "furnace.db" }
|
||||||
|
@ -193,10 +163,13 @@ ARTICLE: "furnace" "Furnace framework"
|
||||||
{ $subsection "furnace.alloy" }
|
{ $subsection "furnace.alloy" }
|
||||||
{ $subsection "furnace.persistence" }
|
{ $subsection "furnace.persistence" }
|
||||||
{ $subsection "furnace.presentation" }
|
{ $subsection "furnace.presentation" }
|
||||||
|
{ $subsection "furnace.auth" }
|
||||||
{ $subsection "furnace.load-balancing" }
|
{ $subsection "furnace.load-balancing" }
|
||||||
"Utilities:"
|
"Utilities:"
|
||||||
{ $subsection "furnace.referrer" }
|
{ $subsection "furnace.referrer" }
|
||||||
{ $subsection "furnace.redirection" }
|
{ $subsection "furnace.redirection" }
|
||||||
|
{ $subsection "furnace.extension-points" }
|
||||||
|
{ $subsection "furnace.misc" }
|
||||||
"Related frameworks:"
|
"Related frameworks:"
|
||||||
{ $subsection "db" }
|
{ $subsection "db" }
|
||||||
{ $subsection "xml" }
|
{ $subsection "xml" }
|
||||||
|
|
|
@ -90,7 +90,7 @@ M: object modify-form drop ;
|
||||||
} case ;
|
} case ;
|
||||||
|
|
||||||
: referrer ( -- referrer/f )
|
: referrer ( -- referrer/f )
|
||||||
#! Typo is intentional, its in the HTTP spec!
|
#! Typo is intentional, it's in the HTTP spec!
|
||||||
"referer" request get header>> at
|
"referer" request get header>> at
|
||||||
dup [ >url ensure-port [ remap-port ] change-port ] when ;
|
dup [ >url ensure-port [ remap-port ] change-port ] when ;
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ SYMBOL: exit-continuation
|
||||||
: exit-with ( value -- )
|
: exit-with ( value -- )
|
||||||
exit-continuation get continue-with ;
|
exit-continuation get continue-with ;
|
||||||
|
|
||||||
: with-exit-continuation ( quot -- )
|
: with-exit-continuation ( quot -- value )
|
||||||
'[ exit-continuation set @ ] callcc1 exit-continuation off ;
|
'[ exit-continuation set @ ] callcc1 exit-continuation off ;
|
||||||
|
|
||||||
USE: vocabs.loader
|
USE: vocabs.loader
|
||||||
|
@ -152,3 +152,4 @@ USE: vocabs.loader
|
||||||
"furnace.scopes" require
|
"furnace.scopes" require
|
||||||
"furnace.sessions" require
|
"furnace.sessions" require
|
||||||
"furnace.syndication" require
|
"furnace.syndication" require
|
||||||
|
"webapps.user-admin" require
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
USING: help.markup help.syntax io.streams.string ;
|
USING: help.markup help.syntax io.streams.string
|
||||||
|
furnace ;
|
||||||
IN: furnace.referrer
|
IN: furnace.referrer
|
||||||
|
|
||||||
HELP: <check-form-submissions>
|
HELP: <check-form-submissions>
|
||||||
|
@ -10,6 +11,9 @@ HELP: <check-form-submissions>
|
||||||
|
|
||||||
ARTICLE: "furnace.referrer" "Form submission referrer checking"
|
ARTICLE: "furnace.referrer" "Form submission referrer checking"
|
||||||
"The " { $vocab-link "furnace.referrer" } " implements a simple security measure which can be used to thwart cross-site scripting attacks."
|
"The " { $vocab-link "furnace.referrer" } " implements a simple security measure which can be used to thwart cross-site scripting attacks."
|
||||||
{ $subsection <check-form-submissions> } ;
|
{ $subsection <check-form-submissions> }
|
||||||
|
"Explicit referrer checking:"
|
||||||
|
{ $subsection referrer }
|
||||||
|
{ $subsection same-host? } ;
|
||||||
|
|
||||||
ABOUT: "furnace.referrer"
|
ABOUT: "furnace.referrer"
|
||||||
|
|
|
@ -9,7 +9,7 @@ HELP: <sessions>
|
||||||
{ $description "Wraps a responder in a session manager responder." } ;
|
{ $description "Wraps a responder in a session manager responder." } ;
|
||||||
|
|
||||||
HELP: schange
|
HELP: schange
|
||||||
{ $values { "key" symbol } { "quot" "a quotation with stack effect " { $snippet "( old -- new )" } } }
|
{ $values { "key" symbol } { "quot" { $quotation "( old -- new )" } } }
|
||||||
{ $description "Applies the quotation to the old value of the session variable, and assigns the resulting value back to the variable." } ;
|
{ $description "Applies the quotation to the old value of the session variable, and assigns the resulting value back to the variable." } ;
|
||||||
|
|
||||||
HELP: sget
|
HELP: sget
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Furnace web framework
|
|
@ -1,5 +1,6 @@
|
||||||
USING: help.markup help.syntax io kernel math namespaces parser
|
USING: help.markup help.syntax io kernel math namespaces parser
|
||||||
prettyprint sequences vocabs.loader namespaces stack-checker ;
|
prettyprint sequences vocabs.loader namespaces stack-checker
|
||||||
|
help ;
|
||||||
IN: help.cookbook
|
IN: help.cookbook
|
||||||
|
|
||||||
ARTICLE: "cookbook-syntax" "Basic syntax cookbook"
|
ARTICLE: "cookbook-syntax" "Basic syntax cookbook"
|
||||||
|
@ -324,6 +325,19 @@ ARTICLE: "cookbook-pitfalls" "Pitfalls to avoid"
|
||||||
{ "If " { $link run-file } " throws a stack depth assertion, it means that the top-level form in the file left behind values on the stack. The stack depth is compared before and after loading a source file, since this type of situation is almost always an error. If you have a legitimate need to load a source file which returns data in some manner, define a word in the source file which produces this data on the stack and call the word after loading the file." }
|
{ "If " { $link run-file } " throws a stack depth assertion, it means that the top-level form in the file left behind values on the stack. The stack depth is compared before and after loading a source file, since this type of situation is almost always an error. If you have a legitimate need to load a source file which returns data in some manner, define a word in the source file which produces this data on the stack and call the word after loading the file." }
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
|
ARTICLE: "cookbook-next" "Next steps"
|
||||||
|
"Once you have read through " { $link "first-program" } " and " { $link "cookbook" } ", the best way to keep learning Factor is to start looking at some simple example programs. Here are a few particularly nice vocabularies which should keep you busy for a little while:"
|
||||||
|
{ $list
|
||||||
|
{ $vocab-link "base64" }
|
||||||
|
{ $vocab-link "roman" }
|
||||||
|
{ $vocab-link "rot13" }
|
||||||
|
{ $vocab-link "smtp" }
|
||||||
|
{ $vocab-link "time-server" }
|
||||||
|
{ $vocab-link "tools.hexdump" }
|
||||||
|
{ $vocab-link "webapps.counter" }
|
||||||
|
}
|
||||||
|
"If you see code in there that you do not understand, use " { $link see } " and " { $link help } " to explore." ;
|
||||||
|
|
||||||
ARTICLE: "cookbook" "Factor cookbook"
|
ARTICLE: "cookbook" "Factor cookbook"
|
||||||
"The Factor cookbook is a high-level overview of the most important concepts required to program in Factor."
|
"The Factor cookbook is a high-level overview of the most important concepts required to program in Factor."
|
||||||
{ $subsection "cookbook-syntax" }
|
{ $subsection "cookbook-syntax" }
|
||||||
|
@ -336,6 +350,7 @@ ARTICLE: "cookbook" "Factor cookbook"
|
||||||
{ $subsection "cookbook-scripts" }
|
{ $subsection "cookbook-scripts" }
|
||||||
{ $subsection "cookbook-compiler" }
|
{ $subsection "cookbook-compiler" }
|
||||||
{ $subsection "cookbook-philosophy" }
|
{ $subsection "cookbook-philosophy" }
|
||||||
{ $subsection "cookbook-pitfalls" } ;
|
{ $subsection "cookbook-pitfalls" }
|
||||||
|
{ $subsection "cookbook-next" } ;
|
||||||
|
|
||||||
ABOUT: "cookbook"
|
ABOUT: "cookbook"
|
||||||
|
|
|
@ -65,6 +65,11 @@ $nl
|
||||||
{ "word" { "the basic unit of code, analogous to a function or procedure in other programming languages. See " { $link "words" } } }
|
{ "word" { "the basic unit of code, analogous to a function or procedure in other programming languages. See " { $link "words" } } }
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
|
ARTICLE: "tail-call-opt" "Tail-call optimization"
|
||||||
|
"If the last action performed is the execution of a word, the current quotation is not saved on the call stack; this is known as " { $emphasis "tail-call optimization" } " and the Factor implementation guarantees that it will be performed."
|
||||||
|
$nl
|
||||||
|
"Tail-call optimization allows iterative algorithms to be implemented in an efficient manner using recursion, without the need for any kind of primitive looping construct in the language. However, in practice, most iteration is performed via combinators such as " { $link while } ", " { $link each } ", " { $link map } ", " { $link assoc-each } ", and so on. The definitions of these combinators do bottom-out in recursive words, however." ;
|
||||||
|
|
||||||
ARTICLE: "evaluator" "Evaluation semantics"
|
ARTICLE: "evaluator" "Evaluation semantics"
|
||||||
{ $link "quotations" } " are evaluated sequentially from beginning to end. When the end is reached, the quotation returns to its caller. As each object in the quotation is evaluated in turn, an action is taken based on its type:"
|
{ $link "quotations" } " are evaluated sequentially from beginning to end. When the end is reached, the quotation returns to its caller. As each object in the quotation is evaluated in turn, an action is taken based on its type:"
|
||||||
{ $list
|
{ $list
|
||||||
|
@ -72,7 +77,7 @@ ARTICLE: "evaluator" "Evaluation semantics"
|
||||||
{ "a " { $link wrapper } " - the wrapped object is pushed on the data stack. Wrappers are used to push word objects directly on the stack when they would otherwise execute. See the " { $link POSTPONE: \ } " parsing word." }
|
{ "a " { $link wrapper } " - the wrapped object is pushed on the data stack. Wrappers are used to push word objects directly on the stack when they would otherwise execute. See the " { $link POSTPONE: \ } " parsing word." }
|
||||||
{ "All other types of objects are pushed on the data stack." }
|
{ "All other types of objects are pushed on the data stack." }
|
||||||
}
|
}
|
||||||
"If the last action performed is the execution of a word, the current quotation is not saved on the call stack; this is known as " { $snippet "tail-recursion" } " and allows iterative algorithms to execute without incurring unbounded call stack usage."
|
{ $subsection "tail-call-opt" }
|
||||||
{ $see-also "compiler" } ;
|
{ $see-also "compiler" } ;
|
||||||
|
|
||||||
ARTICLE: "objects" "Objects"
|
ARTICLE: "objects" "Objects"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
USING: help.markup help.crossref help.stylesheet help.topics
|
USING: help.markup help.crossref help.stylesheet help.topics
|
||||||
help.syntax definitions io prettyprint summary arrays math
|
help.syntax definitions io prettyprint summary arrays math
|
||||||
sequences vocabs ;
|
sequences vocabs strings ;
|
||||||
IN: help
|
IN: help
|
||||||
|
|
||||||
ARTICLE: "printing-elements" "Printing markup elements"
|
ARTICLE: "printing-elements" "Printing markup elements"
|
||||||
|
@ -33,6 +33,10 @@ ARTICLE: "block-elements" "Block elements"
|
||||||
{ $subsection $side-effects }
|
{ $subsection $side-effects }
|
||||||
{ $subsection $errors }
|
{ $subsection $errors }
|
||||||
{ $subsection $see-also }
|
{ $subsection $see-also }
|
||||||
|
"Elements used in " { $link $values } " forms:"
|
||||||
|
{ $subsection $instance }
|
||||||
|
{ $subsection $maybe }
|
||||||
|
{ $subsection $quotation }
|
||||||
"Boilerplate paragraphs:"
|
"Boilerplate paragraphs:"
|
||||||
{ $subsection $low-level-note }
|
{ $subsection $low-level-note }
|
||||||
{ $subsection $io-error }
|
{ $subsection $io-error }
|
||||||
|
@ -281,7 +285,7 @@ HELP: $link
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
HELP: textual-list
|
HELP: textual-list
|
||||||
{ $values { "seq" "a sequence" } { "quot" "a quotation with stack effect " { $snippet "( elt -- )" } } }
|
{ $values { "seq" "a sequence" } { "quot" { $quotation "( elt -- )" } } }
|
||||||
{ $description "Applies the quotation to each element of the sequence, printing a comma between each pair of elements." }
|
{ $description "Applies the quotation to each element of the sequence, printing a comma between each pair of elements." }
|
||||||
{ $examples
|
{ $examples
|
||||||
{ $example "USING: help.markup io ;" "{ \"fish\" \"chips\" \"salt\" } [ write ] textual-list" "fish, chips, salt" }
|
{ $example "USING: help.markup io ;" "{ \"fish\" \"chips\" \"salt\" } [ write ] textual-list" "fish, chips, salt" }
|
||||||
|
@ -318,7 +322,37 @@ HELP: $table
|
||||||
|
|
||||||
HELP: $values
|
HELP: $values
|
||||||
{ $values { "element" "an array of pairs of markup elements" } }
|
{ $values { "element" "an array of pairs of markup elements" } }
|
||||||
{ $description "Prints the description of arguments and values found on every word help page. The first element of a pair is the argument name and is output with " { $link $snippet } ". The remainder can be an element of any form." } ;
|
{ $description "Prints the description of arguments and values found on every word help page. The first element of a pair is the argument name and is output with " { $link $snippet } ". The remainder is either a single class word, or an element. If it is a class word " { $snippet "class" } ", it is intereted as if it were shorthand for " { $snippet "{ $instance class }" } "." }
|
||||||
|
{ $see-also $maybe $instance $quotation } ;
|
||||||
|
|
||||||
|
HELP: $instance
|
||||||
|
{ $values { "element" "an array with shape " { $snippet "{ class }" } } }
|
||||||
|
{ $description
|
||||||
|
"Produces the text ``a " { $emphasis "class" } "'' or ``an " { $emphasis "class" } "'', depending on the first letter of " { $emphasis "class" } "."
|
||||||
|
}
|
||||||
|
{ $examples
|
||||||
|
{ $markup-example { $instance string } }
|
||||||
|
{ $markup-example { $instance integer } }
|
||||||
|
{ $markup-example { $instance f } }
|
||||||
|
} ;
|
||||||
|
|
||||||
|
HELP: $maybe
|
||||||
|
{ $values { "element" "an array with shape " { $snippet "{ class }" } } }
|
||||||
|
{ $description
|
||||||
|
"Produces the text ``a " { $emphasis "class" } " or f'' or ``an " { $emphasis "class" } " or f'', depending on the first letter of " { $emphasis "class" } "."
|
||||||
|
}
|
||||||
|
{ $examples
|
||||||
|
{ $markup-example { $maybe string } }
|
||||||
|
} ;
|
||||||
|
|
||||||
|
HELP: $quotation
|
||||||
|
{ $values { "element" "an array with shape " { $snippet "{ effect }" } } }
|
||||||
|
{ $description
|
||||||
|
"Produces the text ``a quotation with stack effect " { $emphasis "effect" } "''."
|
||||||
|
}
|
||||||
|
{ $examples
|
||||||
|
{ $markup-example { $quotation "( obj -- )" } }
|
||||||
|
} ;
|
||||||
|
|
||||||
HELP: $list
|
HELP: $list
|
||||||
{ $values { "element" "an array of markup elements" } }
|
{ $values { "element" "an array of markup elements" } }
|
||||||
|
|
|
@ -5,23 +5,22 @@ io.files html.streams html.elements html.components help kernel
|
||||||
assocs sequences make words accessors arrays help.topics vocabs
|
assocs sequences make words accessors arrays help.topics vocabs
|
||||||
tools.vocabs tools.vocabs.browser namespaces prettyprint io
|
tools.vocabs tools.vocabs.browser namespaces prettyprint io
|
||||||
vocabs.loader serialize fry memoize unicode.case math.order
|
vocabs.loader serialize fry memoize unicode.case math.order
|
||||||
sorting ;
|
sorting debugger ;
|
||||||
IN: help.html
|
IN: help.html
|
||||||
|
|
||||||
: escape-char ( ch -- )
|
: escape-char ( ch -- )
|
||||||
dup H{
|
dup H{
|
||||||
{ CHAR: " "__quote__" }
|
{ CHAR: " "__quo__" }
|
||||||
{ CHAR: * "__star__" }
|
{ CHAR: * "__star__" }
|
||||||
{ CHAR: : "__colon__" }
|
{ CHAR: : "__colon__" }
|
||||||
{ CHAR: < "__lt__" }
|
{ CHAR: < "__lt__" }
|
||||||
{ CHAR: > "__gt__" }
|
{ CHAR: > "__gt__" }
|
||||||
{ CHAR: ? "__question__" }
|
{ CHAR: ? "__que__" }
|
||||||
{ CHAR: \\ "__backslash__" }
|
{ CHAR: \\ "__back__" }
|
||||||
{ CHAR: | "__pipe__" }
|
{ CHAR: | "__pipe__" }
|
||||||
{ CHAR: _ "__underscore__" }
|
|
||||||
{ CHAR: / "__slash__" }
|
{ CHAR: / "__slash__" }
|
||||||
{ CHAR: \\ "__backslash__" }
|
|
||||||
{ CHAR: , "__comma__" }
|
{ CHAR: , "__comma__" }
|
||||||
|
{ CHAR: @ "__at__" }
|
||||||
} at [ % ] [ , ] ?if ;
|
} at [ % ] [ , ] ?if ;
|
||||||
|
|
||||||
: escape-filename ( string -- filename )
|
: escape-filename ( string -- filename )
|
||||||
|
@ -88,19 +87,17 @@ M: topic browser-link-href topic>filename ;
|
||||||
all-vocabs-really [ dup vocab-name ] { } map>assoc "vocabs.idx" serialize-index ;
|
all-vocabs-really [ dup vocab-name ] { } map>assoc "vocabs.idx" serialize-index ;
|
||||||
|
|
||||||
: generate-help-files ( -- )
|
: generate-help-files ( -- )
|
||||||
all-topics [ help>html ] each ;
|
all-topics [ '[ _ help>html ] try ] each ;
|
||||||
|
|
||||||
: generate-help ( -- )
|
: generate-help ( -- )
|
||||||
{ "resource:core" "resource:basis" "resource:extra" } vocab-roots [
|
"docs" temp-file
|
||||||
load-everything
|
[ make-directories ]
|
||||||
|
[
|
||||||
"/tmp/docs/" make-directory
|
[
|
||||||
|
|
||||||
"/tmp/docs/" [
|
|
||||||
generate-indices
|
generate-indices
|
||||||
generate-help-files
|
generate-help-files
|
||||||
] with-directory
|
] with-directory
|
||||||
] with-variable ;
|
] bi ;
|
||||||
|
|
||||||
MEMO: load-index ( name -- index )
|
MEMO: load-index ( name -- index )
|
||||||
binary file-contents bytes>object ;
|
binary file-contents bytes>object ;
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
USING: accessors arrays definitions generic io kernel assocs
|
USING: accessors arrays definitions generic io kernel assocs
|
||||||
hashtables namespaces make parser prettyprint sequences strings
|
hashtables namespaces make parser prettyprint sequences strings
|
||||||
io.styles vectors words math sorting splitting classes slots
|
io.styles vectors words math sorting splitting classes slots
|
||||||
vocabs help.stylesheet help.topics vocabs.loader alias ;
|
vocabs help.stylesheet help.topics vocabs.loader alias
|
||||||
|
quotations ;
|
||||||
IN: help.markup
|
IN: help.markup
|
||||||
|
|
||||||
! Simple markup language.
|
! Simple markup language.
|
||||||
|
@ -234,7 +235,8 @@ ALIAS: $slot $snippet
|
||||||
] ($grid) ;
|
] ($grid) ;
|
||||||
|
|
||||||
: a/an ( str -- str )
|
: a/an ( str -- str )
|
||||||
first "aeiou" member? "an" "a" ? ;
|
[ first ] [ length ] bi 1 =
|
||||||
|
"afhilmnorsx" "aeiou" ? member? "an" "a" ? ;
|
||||||
|
|
||||||
GENERIC: ($instance) ( element -- )
|
GENERIC: ($instance) ( element -- )
|
||||||
|
|
||||||
|
@ -244,7 +246,17 @@ M: word ($instance)
|
||||||
M: string ($instance)
|
M: string ($instance)
|
||||||
dup a/an write bl $snippet ;
|
dup a/an write bl $snippet ;
|
||||||
|
|
||||||
: $instance ( children -- ) first ($instance) ;
|
M: f ($instance)
|
||||||
|
drop { f } $link ;
|
||||||
|
|
||||||
|
: $instance ( element -- ) first ($instance) ;
|
||||||
|
|
||||||
|
: $maybe ( element -- )
|
||||||
|
$instance " or " print-element { f } $instance ;
|
||||||
|
|
||||||
|
: $quotation ( element -- )
|
||||||
|
{ "a " { $link quotation } " with stack effect " } print-element
|
||||||
|
$snippet ;
|
||||||
|
|
||||||
: values-row ( seq -- seq )
|
: values-row ( seq -- seq )
|
||||||
unclip \ $snippet swap ?word-name 2array
|
unclip \ $snippet swap ?word-name 2array
|
||||||
|
|
|
@ -14,7 +14,7 @@ HELP: required-attr
|
||||||
{ $errors "Throws an error if the attribute is not specified." } ;
|
{ $errors "Throws an error if the attribute is not specified." } ;
|
||||||
|
|
||||||
HELP: optional-attr
|
HELP: optional-attr
|
||||||
{ $values { "tag" tag } { "name" string } { "value" "a " { $link string } " or " { $link f } } }
|
{ $values { "tag" tag } { "name" string } { "value" { $maybe string } } }
|
||||||
{ $description "Extracts an attribute from a tag." }
|
{ $description "Extracts an attribute from a tag." }
|
||||||
{ $notes "Outputs " { $link f } " if the attribute is not specified." } ;
|
{ $notes "Outputs " { $link f } " if the attribute is not specified." } ;
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ HELP: compile-attr
|
||||||
|
|
||||||
HELP: CHLOE:
|
HELP: CHLOE:
|
||||||
{ $syntax "name definition... ;" }
|
{ $syntax "name definition... ;" }
|
||||||
{ $values { "name" "the tag name" } { "definition" "a quotation with stack effect " { $snippet "( tag -- )" } } }
|
{ $values { "name" "the tag name" } { "definition" { $quotation "( tag -- )" } } }
|
||||||
{ $description "Defines compilation semantics for the Chloe tag named " { $snippet "tag" } ". The definition body receives a " { $link tag } " on the stack." } ;
|
{ $description "Defines compilation semantics for the Chloe tag named " { $snippet "tag" } ". The definition body receives a " { $link tag } " on the stack." } ;
|
||||||
|
|
||||||
HELP: COMPONENT:
|
HELP: COMPONENT:
|
||||||
|
@ -46,7 +46,7 @@ HELP: [code]
|
||||||
{ $description "Compiles the quotation. It will be called when the template is called." } ;
|
{ $description "Compiles the quotation. It will be called when the template is called." } ;
|
||||||
|
|
||||||
HELP: process-children
|
HELP: process-children
|
||||||
{ $values { "tag" tag } { "quot" "a quotation with stack effect " { $snippet "( compiled-tag -- )" } } }
|
{ $values { "tag" tag } { "quot" { $quotation "( compiled-tag -- )" } } }
|
||||||
{ $description "Compiles the tag. The quotation will be applied to the resulting quotation when the template is called." }
|
{ $description "Compiles the tag. The quotation will be applied to the resulting quotation when the template is called." }
|
||||||
{ $examples "See " { $link "html.templates.chloe.extend.tags.example" } " for an example which uses this word to implement a custom control flow tag." } ;
|
{ $examples "See " { $link "html.templates.chloe.extend.tags.example" } " for an example which uses this word to implement a custom control flow tag." } ;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
USING: io io.files io.streams.string io.encodings.utf8
|
USING: io io.files io.streams.string io.encodings.utf8
|
||||||
html.templates html.templates.fhtml kernel
|
html.templates html.templates.fhtml kernel
|
||||||
tools.test sequences parser ;
|
tools.test sequences parser splitting prettyprint ;
|
||||||
IN: html.templates.fhtml.tests
|
IN: html.templates.fhtml.tests
|
||||||
|
|
||||||
: test-template ( path -- ? )
|
: test-template ( path -- ? )
|
||||||
|
@ -8,8 +8,10 @@ IN: html.templates.fhtml.tests
|
||||||
prepend
|
prepend
|
||||||
[
|
[
|
||||||
".fhtml" append <fhtml> [ call-template ] with-string-writer
|
".fhtml" append <fhtml> [ call-template ] with-string-writer
|
||||||
|
<string-reader> lines
|
||||||
] keep
|
] keep
|
||||||
".html" append utf8 file-contents = ;
|
".html" append utf8 file-lines
|
||||||
|
[ . . ] [ = ] 2bi ;
|
||||||
|
|
||||||
[ t ] [ "example" test-template ] unit-test
|
[ t ] [ "example" test-template ] unit-test
|
||||||
[ t ] [ "bug" test-template ] unit-test
|
[ t ] [ "bug" test-template ] unit-test
|
||||||
|
|
|
@ -40,7 +40,7 @@ HELP: http-post
|
||||||
{ $errors "Throws an error if the HTTP request fails." } ;
|
{ $errors "Throws an error if the HTTP request fails." } ;
|
||||||
|
|
||||||
HELP: with-http-get
|
HELP: with-http-get
|
||||||
{ $values { "url" "a " { $link url } " or " { $link string } } { "quot" "a quotation with stack effect " { $snippet "( chunk -- )" } } { "response" response } }
|
{ $values { "url" "a " { $link url } " or " { $link string } } { "quot" { $quotation "( chunk -- )" } } { "response" response } }
|
||||||
{ $description "Downloads the contents of a URL. Chunks of data are passed to the quotation as they are read." }
|
{ $description "Downloads the contents of a URL. Chunks of data are passed to the quotation as they are read." }
|
||||||
{ $errors "Throws an error if the HTTP request fails." } ;
|
{ $errors "Throws an error if the HTTP request fails." } ;
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ HELP: http-request
|
||||||
{ $errors "Throws an error if the HTTP request fails." } ;
|
{ $errors "Throws an error if the HTTP request fails." } ;
|
||||||
|
|
||||||
HELP: with-http-request
|
HELP: with-http-request
|
||||||
{ $values { "request" request } { "quot" "a quotation with stack effect " { $snippet "( chunk -- )" } } { "response" response } }
|
{ $values { "request" request } { "quot" { $quotation "( chunk -- )" } } { "response" response } }
|
||||||
{ $description "Sends an HTTP request to an HTTP server, and reads the response incrementally. Chunks of data are passed to the quotation as they are read." }
|
{ $description "Sends an HTTP request to an HTTP server, and reads the response incrementally. Chunks of data are passed to the quotation as they are read." }
|
||||||
{ $errors "Throws an error if the HTTP request fails." } ;
|
{ $errors "Throws an error if the HTTP request fails." } ;
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ HELP: delete-cookie
|
||||||
{ $side-effects "request/response" } ;
|
{ $side-effects "request/response" } ;
|
||||||
|
|
||||||
HELP: get-cookie
|
HELP: get-cookie
|
||||||
{ $values { "request/response" "a " { $link request } " or a " { $link response } } { "name" string } { "cookie/f" "a " { $link cookie } " or " { $link f } } }
|
{ $values { "request/response" "a " { $link request } " or a " { $link response } } { "name" string } { "cookie/f" { $maybe cookie } } }
|
||||||
{ $description "Gets a named cookie from a request or response." } ;
|
{ $description "Gets a named cookie from a request or response." } ;
|
||||||
|
|
||||||
HELP: put-cookie
|
HELP: put-cookie
|
||||||
|
|
|
@ -4,7 +4,7 @@ USING: help.markup help.syntax io.streams.string ;
|
||||||
IN: http.server.static
|
IN: http.server.static
|
||||||
|
|
||||||
HELP: <file-responder>
|
HELP: <file-responder>
|
||||||
{ $values { "root" "a pathname string" } { "hook" "a quotation with stack effect " { $snippet "( path mime-type -- response )" } } { "responder" file-responder } }
|
{ $values { "root" "a pathname string" } { "hook" { $quotation "( path mime-type -- response )" } } { "responder" file-responder } }
|
||||||
{ $description "Creates a file responder which serves content from " { $snippet "path" } " by using the hook to generate a response." } ;
|
{ $description "Creates a file responder which serves content from " { $snippet "path" } " by using the hook to generate a response." } ;
|
||||||
|
|
||||||
HELP: <static>
|
HELP: <static>
|
||||||
|
|
Binary file not shown.
|
@ -3,4 +3,6 @@
|
||||||
USING: tools.test io.files.listing strings kernel ;
|
USING: tools.test io.files.listing strings kernel ;
|
||||||
IN: io.files.listing.tests
|
IN: io.files.listing.tests
|
||||||
|
|
||||||
|
\ directory. must-infer
|
||||||
|
|
||||||
[ ] [ "" directory. ] unit-test
|
[ ] [ "" directory. ] unit-test
|
||||||
|
|
|
@ -17,7 +17,7 @@ HELP: <mapped-file>
|
||||||
{ $errors "Throws an error if a memory mapping could not be established." } ;
|
{ $errors "Throws an error if a memory mapping could not be established." } ;
|
||||||
|
|
||||||
HELP: with-mapped-file
|
HELP: with-mapped-file
|
||||||
{ $values { "path" "a pathname string" } { "length" integer } { "quot" "a quotation with stack effect " { $snippet "( mmap -- )" } } }
|
{ $values { "path" "a pathname string" } { "length" integer } { "quot" { $quotation "( mmap -- )" } } }
|
||||||
{ $contract "Opens a file and maps its contents into memory, passing the " { $link mapped-file } " instance to the quotation. The mapped file is disposed of when the quotation returns, or if an error is thrown." }
|
{ $contract "Opens a file and maps its contents into memory, passing the " { $link mapped-file } " instance to the quotation. The mapped file is disposed of when the quotation returns, or if an error is thrown." }
|
||||||
{ $errors "Throws an error if a memory mapping could not be established." } ;
|
{ $errors "Throws an error if a memory mapping could not be established." } ;
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ HELP: next-change
|
||||||
{ $errors "Throws an error if the monitor is closed from another thread." } ;
|
{ $errors "Throws an error if the monitor is closed from another thread." } ;
|
||||||
|
|
||||||
HELP: with-monitor
|
HELP: with-monitor
|
||||||
{ $values { "path" "a pathname string" } { "recursive?" "a boolean" } { "quot" "a quotation with stack effect " { $snippet "( monitor -- )" } } }
|
{ $values { "path" "a pathname string" } { "recursive?" "a boolean" } { "quot" { $quotation "( monitor -- )" } } }
|
||||||
{ $description "Opens a file system change monitor and passes it to the quotation. Closes the monitor after the quotation returns or throws an error." }
|
{ $description "Opens a file system change monitor and passes it to the quotation. Closes the monitor after the quotation returns or throws an error." }
|
||||||
{ $errors "Throws an error if the pathname does not exist, if a monitor could not be created or if the platform does not support monitors." } ;
|
{ $errors "Throws an error if the pathname does not exist, if a monitor could not be created or if the platform does not support monitors." } ;
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ HELP: return-connection
|
||||||
{ $description "Returns a connection to the pool." } ;
|
{ $description "Returns a connection to the pool." } ;
|
||||||
|
|
||||||
HELP: with-pooled-connection
|
HELP: with-pooled-connection
|
||||||
{ $values { "pool" pool } { "quot" "a quotation with stack effect " { $snippet "( conn -- )" } } }
|
{ $values { "pool" pool } { "quot" { $quotation "( conn -- )" } } }
|
||||||
{ $description "Calls a quotation with a pooled connection on the stack. If the quotation returns successfully, the connection is returned to the pool; if the quotation throws an error, the connection is disposed of with " { $link dispose } "." } ;
|
{ $description "Calls a quotation with a pooled connection on the stack. If the quotation returns successfully, the connection is returned to the pool; if the quotation throws an error, the connection is disposed of with " { $link dispose } "." } ;
|
||||||
|
|
||||||
HELP: make-connection
|
HELP: make-connection
|
||||||
|
|
|
@ -114,11 +114,11 @@ HELP: stop-this-server
|
||||||
{ $description "Stops the current threaded server, preventing it from accepting any more connections and returning to the caller of " { $link start-server } ". All client connections which have already been opened continue to be serviced." } ;
|
{ $description "Stops the current threaded server, preventing it from accepting any more connections and returning to the caller of " { $link start-server } ". All client connections which have already been opened continue to be serviced." } ;
|
||||||
|
|
||||||
HELP: secure-port
|
HELP: secure-port
|
||||||
{ $values { "n" "an " { $link integer } " or " { $link f } } }
|
{ $values { "n" { $maybe integer } } }
|
||||||
{ $description "Outputs the port number on which the current threaded server accepts secure socket connections. Outputs " { $link f } " if the current threaded server does not accept secure socket connections." }
|
{ $description "Outputs the port number on which the current threaded server accepts secure socket connections. Outputs " { $link f } " if the current threaded server does not accept secure socket connections." }
|
||||||
{ $notes "Can only be used from the dynamic scope of a " { $link handle-client* } " call." } ;
|
{ $notes "Can only be used from the dynamic scope of a " { $link handle-client* } " call." } ;
|
||||||
|
|
||||||
HELP: insecure-port
|
HELP: insecure-port
|
||||||
{ $values { "n" "an " { $link integer } " or " { $link f } } }
|
{ $values { "n" { $maybe integer } } }
|
||||||
{ $description "Outputs the port number on which the current threaded server accepts ordinary socket connections. Outputs " { $link f } " if the current threaded server does not accept ordinary socket connections." }
|
{ $description "Outputs the port number on which the current threaded server accepts ordinary socket connections. Outputs " { $link f } " if the current threaded server does not accept ordinary socket connections." }
|
||||||
{ $notes "Can only be used from the dynamic scope of a " { $link handle-client* } " call." } ;
|
{ $notes "Can only be used from the dynamic scope of a " { $link handle-client* } " call." } ;
|
||||||
|
|
|
@ -114,19 +114,29 @@ M: threaded-server handle-client* handler>> call ;
|
||||||
] when*
|
] when*
|
||||||
] unless ;
|
] unless ;
|
||||||
|
|
||||||
PRIVATE>
|
: (start-server) ( threaded-server -- )
|
||||||
|
|
||||||
: start-server ( threaded-server -- )
|
|
||||||
init-server
|
init-server
|
||||||
dup secure-config>> [
|
|
||||||
dup threaded-server [
|
dup threaded-server [
|
||||||
dup name>> [
|
dup name>> [
|
||||||
[ listen-on [ start-accept-loop ] parallel-each ]
|
[ listen-on [ start-accept-loop ] parallel-each ]
|
||||||
[ ready>> raise-flag ]
|
[ ready>> raise-flag ]
|
||||||
bi
|
bi
|
||||||
] with-logging
|
] with-logging
|
||||||
] with-variable
|
] with-variable ;
|
||||||
] with-secure-context ;
|
|
||||||
|
PRIVATE>
|
||||||
|
|
||||||
|
: start-server ( threaded-server -- )
|
||||||
|
#! Only create a secure-context if we want to listen on
|
||||||
|
#! a secure port, otherwise start-server won't work at
|
||||||
|
#! all if SSL is not available.
|
||||||
|
dup secure>> [
|
||||||
|
dup secure-config>> [
|
||||||
|
(start-server)
|
||||||
|
] with-secure-context
|
||||||
|
] [
|
||||||
|
(start-server)
|
||||||
|
] if ;
|
||||||
|
|
||||||
: wait-for-server ( threaded-server -- )
|
: wait-for-server ( threaded-server -- )
|
||||||
ready>> wait-for-flag ;
|
ready>> wait-for-flag ;
|
||||||
|
|
|
@ -56,7 +56,7 @@ ARTICLE: "network-streams" "Networking"
|
||||||
{ $subsection "network-addressing" }
|
{ $subsection "network-addressing" }
|
||||||
{ $subsection "network-connection" }
|
{ $subsection "network-connection" }
|
||||||
{ $subsection "network-packet" }
|
{ $subsection "network-packet" }
|
||||||
{ $subsection "io.sockets.secure" }
|
{ $vocab-subsection "Secure sockets (SSL, TLS)" "io.sockets.secure" }
|
||||||
{ $see-also "io.pipes" } ;
|
{ $see-also "io.pipes" } ;
|
||||||
|
|
||||||
ABOUT: "network-streams"
|
ABOUT: "network-streams"
|
||||||
|
|
|
@ -2,11 +2,11 @@ IN: io.timeouts
|
||||||
USING: help.markup help.syntax math kernel calendar ;
|
USING: help.markup help.syntax math kernel calendar ;
|
||||||
|
|
||||||
HELP: timeout
|
HELP: timeout
|
||||||
{ $values { "obj" object } { "dt/f" "a " { $link duration } " or " { $link f } } }
|
{ $values { "obj" object } { "dt/f" { $maybe duration } } }
|
||||||
{ $contract "Outputs an object's timeout." } ;
|
{ $contract "Outputs an object's timeout." } ;
|
||||||
|
|
||||||
HELP: set-timeout
|
HELP: set-timeout
|
||||||
{ $values { "dt/f" "a " { $link duration } " or " { $link f } } { "obj" object } }
|
{ $values { "dt/f" { $maybe duration } } { "obj" object } }
|
||||||
{ $contract "Sets an object's timeout." } ;
|
{ $contract "Sets an object's timeout." } ;
|
||||||
|
|
||||||
HELP: cancel-operation
|
HELP: cancel-operation
|
||||||
|
@ -14,7 +14,7 @@ HELP: cancel-operation
|
||||||
{ $contract "Handles a timeout, usually by waking up all threads waiting on the object." } ;
|
{ $contract "Handles a timeout, usually by waking up all threads waiting on the object." } ;
|
||||||
|
|
||||||
HELP: with-timeout
|
HELP: with-timeout
|
||||||
{ $values { "obj" object } { "quot" "a quotation with stack effect " { $snippet "( obj -- )" } } }
|
{ $values { "obj" object } { "quot" { $quotation "( obj -- )" } } }
|
||||||
{ $description "Applies the quotation to the object. If the object's timeout expires before the quotation returns, " { $link cancel-operation } " is called on the object." } ;
|
{ $description "Applies the quotation to the object. If the object's timeout expires before the quotation returns, " { $link cancel-operation } " is called on the object." } ;
|
||||||
|
|
||||||
ARTICLE: "io.timeouts" "I/O timeout protocol"
|
ARTICLE: "io.timeouts" "I/O timeout protocol"
|
||||||
|
|
|
@ -117,8 +117,8 @@ M: unix stat>file-info ( stat -- file-info )
|
||||||
[ stat-st_blksize >>blocksize ]
|
[ stat-st_blksize >>blocksize ]
|
||||||
} cleave ;
|
} cleave ;
|
||||||
|
|
||||||
M: unix stat>type ( stat -- type )
|
: n>file-type ( n -- type )
|
||||||
stat-st_mode S_IFMT bitand {
|
S_IFMT bitand {
|
||||||
{ S_IFREG [ +regular-file+ ] }
|
{ S_IFREG [ +regular-file+ ] }
|
||||||
{ S_IFDIR [ +directory+ ] }
|
{ S_IFDIR [ +directory+ ] }
|
||||||
{ S_IFCHR [ +character-device+ ] }
|
{ S_IFCHR [ +character-device+ ] }
|
||||||
|
@ -129,6 +129,9 @@ M: unix stat>type ( stat -- type )
|
||||||
[ drop +unknown+ ]
|
[ drop +unknown+ ]
|
||||||
} case ;
|
} case ;
|
||||||
|
|
||||||
|
M: unix stat>type ( stat -- type )
|
||||||
|
stat-st_mode n>file-type ;
|
||||||
|
|
||||||
! Linux has no extra fields in its stat struct
|
! Linux has no extra fields in its stat struct
|
||||||
os {
|
os {
|
||||||
{ macosx [ "io.unix.files.bsd" require ] }
|
{ macosx [ "io.unix.files.bsd" require ] }
|
||||||
|
@ -150,7 +153,7 @@ os {
|
||||||
|
|
||||||
M: unix >directory-entry ( byte-array -- directory-entry )
|
M: unix >directory-entry ( byte-array -- directory-entry )
|
||||||
[ dirent-d_name utf8 alien>string ]
|
[ dirent-d_name utf8 alien>string ]
|
||||||
[ dirent-d_type ] bi directory-entry boa ;
|
[ dirent-d_type dirent-type>file-type ] bi directory-entry boa ;
|
||||||
|
|
||||||
M: unix (directory-entries) ( path -- seq )
|
M: unix (directory-entries) ( path -- seq )
|
||||||
[
|
[
|
||||||
|
|
|
@ -114,11 +114,6 @@ M: windows delete-directory ( path -- )
|
||||||
normalize-path
|
normalize-path
|
||||||
RemoveDirectory win32-error=0/f ;
|
RemoveDirectory win32-error=0/f ;
|
||||||
|
|
||||||
M: windows >directory-entry ( byte-array -- directory-entry )
|
|
||||||
[ WIN32_FIND_DATA-cFileName utf16n alien>string ]
|
|
||||||
[ WIN32_FIND_DATA-dwFileAttributes ]
|
|
||||||
bi directory-entry boa ;
|
|
||||||
|
|
||||||
: find-first-file ( path -- WIN32_FIND_DATA handle )
|
: find-first-file ( path -- WIN32_FIND_DATA handle )
|
||||||
"WIN32_FIND_DATA" <c-object> tuck
|
"WIN32_FIND_DATA" <c-object> tuck
|
||||||
FindFirstFile
|
FindFirstFile
|
||||||
|
@ -177,6 +172,15 @@ TUPLE: windows-file-info < file-info attributes ;
|
||||||
: win32-file-type ( n -- symbol )
|
: win32-file-type ( n -- symbol )
|
||||||
FILE_ATTRIBUTE_DIRECTORY mask? +directory+ +regular-file+ ? ;
|
FILE_ATTRIBUTE_DIRECTORY mask? +directory+ +regular-file+ ? ;
|
||||||
|
|
||||||
|
TUPLE: windows-directory-entry < directory-entry attributes ;
|
||||||
|
|
||||||
|
M: windows >directory-entry ( byte-array -- directory-entry )
|
||||||
|
[ WIN32_FIND_DATA-cFileName utf16n alien>string ]
|
||||||
|
[ WIN32_FIND_DATA-dwFileAttributes win32-file-type ]
|
||||||
|
[ WIN32_FIND_DATA-dwFileAttributes win32-file-attributes ]
|
||||||
|
tri
|
||||||
|
dupd remove windows-directory-entry boa ;
|
||||||
|
|
||||||
: WIN32_FIND_DATA>file-info ( WIN32_FIND_DATA -- file-info )
|
: WIN32_FIND_DATA>file-info ( WIN32_FIND_DATA -- file-info )
|
||||||
[ \ windows-file-info new ] dip
|
[ \ windows-file-info new ] dip
|
||||||
{
|
{
|
||||||
|
|
|
@ -122,12 +122,12 @@ IN: io.windows.launcher.nt.tests
|
||||||
"resource:basis/io/windows/nt/launcher/test" [
|
"resource:basis/io/windows/nt/launcher/test" [
|
||||||
<process>
|
<process>
|
||||||
vm "-script" "env.factor" 3array >>command
|
vm "-script" "env.factor" 3array >>command
|
||||||
{ { "HOME" "XXX" } } >>environment
|
{ { "USERPROFILE" "XXX" } } >>environment
|
||||||
+prepend-environment+ >>environment-mode
|
+prepend-environment+ >>environment-mode
|
||||||
ascii <process-reader> contents
|
ascii <process-reader> contents
|
||||||
] with-directory eval
|
] with-directory eval
|
||||||
|
|
||||||
"HOME" swap at "XXX" =
|
"USERPROFILE" swap at "XXX" =
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
2 [
|
2 [
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
unportable
|
unportable
|
||||||
windows
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ HELP: free
|
||||||
{ $description "Deallocates a block of memory allocated by " { $link malloc } ", " { $link calloc } " or " { $link realloc } "." } ;
|
{ $description "Deallocates a block of memory allocated by " { $link malloc } ", " { $link calloc } " or " { $link realloc } "." } ;
|
||||||
|
|
||||||
HELP: with-malloc
|
HELP: with-malloc
|
||||||
{ $values { "size" "a positive integer" } { "quot" "a quotation with stack effect " { $snippet "( c-ptr -- )" } } }
|
{ $values { "size" "a positive integer" } { "quot" { $quotation "( c-ptr -- )" } } }
|
||||||
{ $description "Allocates a zeroed block of " { $snippet "n" } " bytes and passes it to the quotation. When the quotation returns, the block is freed." } ;
|
{ $description "Allocates a zeroed block of " { $snippet "n" } " bytes and passes it to the quotation. When the quotation returns, the block is freed." } ;
|
||||||
|
|
||||||
HELP: &free
|
HELP: &free
|
||||||
|
|
|
@ -346,7 +346,6 @@ M:: sequence method-with-locals ( a -- y ) a reverse ;
|
||||||
|
|
||||||
{ 3 1 } [| from to seq | T{ slice f from to seq } ] must-infer-as
|
{ 3 1 } [| from to seq | T{ slice f from to seq } ] must-infer-as
|
||||||
|
|
||||||
|
|
||||||
:: literal-identity-test ( -- a b )
|
:: literal-identity-test ( -- a b )
|
||||||
{ } V{ } ;
|
{ } V{ } ;
|
||||||
|
|
||||||
|
@ -356,6 +355,10 @@ M:: sequence method-with-locals ( a -- y ) a reverse ;
|
||||||
swapd [ eq? ] [ eq? ] 2bi*
|
swapd [ eq? ] [ eq? ] 2bi*
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
|
:: mutable-local-in-literal-test ( a! -- b ) a 1 + a! { a } ;
|
||||||
|
|
||||||
|
[ { 4 } ] [ 3 mutable-local-in-literal-test ] unit-test
|
||||||
|
|
||||||
:: compare-case ( obj1 obj2 lt-quot eq-quot gt-quot -- )
|
:: compare-case ( obj1 obj2 lt-quot eq-quot gt-quot -- )
|
||||||
obj1 obj2 <=> {
|
obj1 obj2 <=> {
|
||||||
{ +lt+ [ lt-quot call ] }
|
{ +lt+ [ lt-quot call ] }
|
||||||
|
@ -385,6 +388,8 @@ M:: integer lambda-method-forget-test ( a -- b ) ;
|
||||||
|
|
||||||
[ ] [ [ { integer lambda-method-forget-test } forget ] with-compilation-unit ] unit-test
|
[ ] [ [ { integer lambda-method-forget-test } forget ] with-compilation-unit ] unit-test
|
||||||
|
|
||||||
|
[ { [ 10 ] } ] [ 10 [| A | { [ A ] } ] call ] unit-test
|
||||||
|
|
||||||
! :: wlet-&&-test ( a -- ? )
|
! :: wlet-&&-test ( a -- ? )
|
||||||
! [wlet | is-integer? [ a integer? ]
|
! [wlet | is-integer? [ a integer? ]
|
||||||
! is-even? [ a even? ]
|
! is-even? [ a even? ]
|
||||||
|
|
|
@ -229,6 +229,8 @@ M: tuple rewrite-element
|
||||||
|
|
||||||
M: local rewrite-element , ;
|
M: local rewrite-element , ;
|
||||||
|
|
||||||
|
M: local-reader rewrite-element , ;
|
||||||
|
|
||||||
M: word rewrite-element literalize , ;
|
M: word rewrite-element literalize , ;
|
||||||
|
|
||||||
M: object rewrite-element , ;
|
M: object rewrite-element , ;
|
||||||
|
|
|
@ -310,8 +310,9 @@ ARTICLE: "math-bitfields" "Constructing bit fields"
|
||||||
"Some applications, such as binary communication protocols and assemblers, need to construct integers from elaborate bit field specifications. Hand-coding this using " { $link shift } " and " { $link bitor } " results in repetitive code. A higher-level facility exists to factor out this repetition:"
|
"Some applications, such as binary communication protocols and assemblers, need to construct integers from elaborate bit field specifications. Hand-coding this using " { $link shift } " and " { $link bitor } " results in repetitive code. A higher-level facility exists to factor out this repetition:"
|
||||||
{ $subsection bitfield } ;
|
{ $subsection bitfield } ;
|
||||||
|
|
||||||
ARTICLE: "math.bitwise" "Bitwise arithmetic"
|
ARTICLE: "math.bitwise" "Additional bitwise arithmetic"
|
||||||
"The " { $vocab-link "math.bitwise" } " vocabulary can implements bitwise arithmetic words that are useful for efficiency, low-level programming, and interfacing with C libraries." $nl
|
"The " { $vocab-link "math.bitwise" } " vocabulary provides bitwise arithmetic words extending " { $link "bitwise-arithmetic" } ". They are useful for efficiency, low-level programming, and interfacing with C libraries."
|
||||||
|
$nl
|
||||||
"Setting and clearing bits:"
|
"Setting and clearing bits:"
|
||||||
{ $subsection set-bit }
|
{ $subsection set-bit }
|
||||||
{ $subsection clear-bit }
|
{ $subsection clear-bit }
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
! See http://factorcode.org/license.txt for BSD license.
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
USING: arrays kernel math math.functions sequences
|
USING: arrays kernel math math.functions sequences
|
||||||
sequences.private words namespaces macros hints
|
sequences.private words namespaces macros hints
|
||||||
combinators fry ;
|
combinators fry io.binary ;
|
||||||
IN: math.bitwise
|
IN: math.bitwise
|
||||||
|
|
||||||
! utilities
|
! utilities
|
||||||
|
@ -93,3 +93,11 @@ PRIVATE>
|
||||||
|
|
||||||
: bit-count ( x -- n )
|
: bit-count ( x -- n )
|
||||||
dup 0 < [ bitnot ] when (bit-count) ; inline
|
dup 0 < [ bitnot ] when (bit-count) ; inline
|
||||||
|
|
||||||
|
! Signed byte array to integer conversion
|
||||||
|
: signed-le> ( bytes -- x )
|
||||||
|
[ le> ] [ length 8 * 1- on-bits ] bi
|
||||||
|
2dup > [ bitnot bitor ] [ drop ] if ;
|
||||||
|
|
||||||
|
: signed-be> ( bytes -- x )
|
||||||
|
<reversed> signed-le> ;
|
||||||
|
|
|
@ -279,7 +279,7 @@ HELP: mod-inv
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
HELP: each-bit
|
HELP: each-bit
|
||||||
{ $values { "n" integer } { "quot" "a quotation with stack effect " { $snippet "( ? -- )" } } }
|
{ $values { "n" integer } { "quot" { $quotation "( ? -- )" } } }
|
||||||
{ $description "Applies the quotation to each bit of the integer, starting from the least significant bit, and stopping at the last bit from which point on all bits are either clear (if the integer is positive) or all bits are set (if the integer is negataive)." }
|
{ $description "Applies the quotation to each bit of the integer, starting from the least significant bit, and stopping at the last bit from which point on all bits are either clear (if the integer is positive) or all bits are set (if the integer is negataive)." }
|
||||||
{ $examples
|
{ $examples
|
||||||
{ $example "USING: math.functions make prettyprint ;" "[ BIN: 1101 [ , ] each-bit ] { } make ." "{ t f t t }" }
|
{ $example "USING: math.functions make prettyprint ;" "[ BIN: 1101 [ , ] each-bit ] { } make ." "{ t f t t }" }
|
||||||
|
|
|
@ -15,7 +15,7 @@ IN: math.functions
|
||||||
PRIVATE>
|
PRIVATE>
|
||||||
|
|
||||||
: rect> ( x y -- z )
|
: rect> ( x y -- z )
|
||||||
over real? over real? and [
|
2dup [ real? ] both? [
|
||||||
(rect>)
|
(rect>)
|
||||||
] [
|
] [
|
||||||
"Complex number must have real components" throw
|
"Complex number must have real components" throw
|
||||||
|
@ -27,10 +27,10 @@ M: real sqrt
|
||||||
>float dup 0.0 < [ neg fsqrt 0.0 swap rect> ] [ fsqrt ] if ;
|
>float dup 0.0 < [ neg fsqrt 0.0 swap rect> ] [ fsqrt ] if ;
|
||||||
|
|
||||||
: each-bit ( n quot: ( ? -- ) -- )
|
: each-bit ( n quot: ( ? -- ) -- )
|
||||||
over 0 = pick -1 = or [
|
over [ 0 = ] [ -1 = ] bi or [
|
||||||
2drop
|
2drop
|
||||||
] [
|
] [
|
||||||
2dup >r >r >r odd? r> call r> 2/ r> each-bit
|
2dup { [ odd? ] [ call ] [ 2/ ] [ each-bit ] } spread
|
||||||
] if ; inline recursive
|
] if ; inline recursive
|
||||||
|
|
||||||
: map-bits ( n quot: ( ? -- obj ) -- seq )
|
: map-bits ( n quot: ( ? -- obj ) -- seq )
|
||||||
|
@ -69,8 +69,7 @@ PRIVATE>
|
||||||
>rect [ >float ] bi@ ; inline
|
>rect [ >float ] bi@ ; inline
|
||||||
|
|
||||||
: >polar ( z -- abs arg )
|
: >polar ( z -- abs arg )
|
||||||
>float-rect [ [ sq ] bi@ + fsqrt ] [ swap fatan2 ] 2bi ;
|
>float-rect [ [ sq ] bi@ + fsqrt ] [ swap fatan2 ] 2bi ; inline
|
||||||
inline
|
|
||||||
|
|
||||||
: cis ( arg -- z ) dup fcos swap fsin rect> ; inline
|
: cis ( arg -- z ) dup fcos swap fsin rect> ; inline
|
||||||
|
|
||||||
|
@ -79,11 +78,10 @@ PRIVATE>
|
||||||
<PRIVATE
|
<PRIVATE
|
||||||
|
|
||||||
: ^mag ( w abs arg -- magnitude )
|
: ^mag ( w abs arg -- magnitude )
|
||||||
>r >r >float-rect swap r> swap fpow r> rot * fexp /f ;
|
[ >float-rect swap ] [ swap fpow ] [ rot * fexp /f ] tri* ; inline
|
||||||
inline
|
|
||||||
|
|
||||||
: ^theta ( w abs arg -- theta )
|
: ^theta ( w abs arg -- theta )
|
||||||
>r >r >float-rect r> flog * swap r> * + ; inline
|
[ >float-rect ] [ flog * swap ] [ * + ] tri* ; inline
|
||||||
|
|
||||||
: ^complex ( x y -- z )
|
: ^complex ( x y -- z )
|
||||||
swap >polar [ ^mag ] [ ^theta ] 3bi polar> ; inline
|
swap >polar [ ^mag ] [ ^theta ] 3bi polar> ; inline
|
||||||
|
@ -106,18 +104,18 @@ PRIVATE>
|
||||||
|
|
||||||
: (^mod) ( n x y -- z )
|
: (^mod) ( n x y -- z )
|
||||||
1 swap [
|
1 swap [
|
||||||
[ dupd * pick mod ] when >r sq over mod r>
|
[ dupd * pick mod ] when [ sq over mod ] dip
|
||||||
] each-bit 2nip ; inline
|
] each-bit 2nip ; inline
|
||||||
|
|
||||||
: (gcd) ( b a x y -- a d )
|
: (gcd) ( b a x y -- a d )
|
||||||
over zero? [
|
over zero? [
|
||||||
2nip
|
2nip
|
||||||
] [
|
] [
|
||||||
swap [ /mod >r over * swapd - r> ] keep (gcd)
|
swap [ /mod [ over * swapd - ] dip ] keep (gcd)
|
||||||
] if ;
|
] if ;
|
||||||
|
|
||||||
: gcd ( x y -- a d )
|
: gcd ( x y -- a d )
|
||||||
0 -rot 1 -rot (gcd) dup 0 < [ neg ] when ; foldable
|
[ 0 1 ] 2dip (gcd) dup 0 < [ neg ] when ; foldable
|
||||||
|
|
||||||
: lcm ( a b -- c )
|
: lcm ( a b -- c )
|
||||||
[ * ] 2keep gcd nip /i ; foldable
|
[ * ] 2keep gcd nip /i ; foldable
|
||||||
|
@ -131,7 +129,7 @@ PRIVATE>
|
||||||
|
|
||||||
: ^mod ( x y n -- z )
|
: ^mod ( x y n -- z )
|
||||||
over 0 < [
|
over 0 < [
|
||||||
[ >r neg r> ^mod ] keep mod-inv
|
[ [ neg ] dip ^mod ] keep mod-inv
|
||||||
] [
|
] [
|
||||||
-rot (^mod)
|
-rot (^mod)
|
||||||
] if ; foldable
|
] if ; foldable
|
||||||
|
@ -141,14 +139,14 @@ GENERIC: absq ( x -- y ) foldable
|
||||||
M: real absq sq ;
|
M: real absq sq ;
|
||||||
|
|
||||||
: ~abs ( x y epsilon -- ? )
|
: ~abs ( x y epsilon -- ? )
|
||||||
>r - abs r> < ;
|
[ - abs ] dip < ;
|
||||||
|
|
||||||
: ~rel ( x y epsilon -- ? )
|
: ~rel ( x y epsilon -- ? )
|
||||||
>r [ - abs ] 2keep [ abs ] bi@ + r> * < ;
|
[ [ - abs ] 2keep [ abs ] bi@ + ] dip * < ;
|
||||||
|
|
||||||
: ~ ( x y epsilon -- ? )
|
: ~ ( x y epsilon -- ? )
|
||||||
{
|
{
|
||||||
{ [ pick fp-nan? pick fp-nan? or ] [ 3drop f ] }
|
{ [ 2over [ fp-nan? ] either? ] [ 3drop f ] }
|
||||||
{ [ dup zero? ] [ drop number= ] }
|
{ [ dup zero? ] [ drop number= ] }
|
||||||
{ [ dup 0 < ] [ ~rel ] }
|
{ [ dup 0 < ] [ ~rel ] }
|
||||||
[ ~abs ]
|
[ ~abs ]
|
||||||
|
|
|
@ -47,3 +47,21 @@ HELP: <zero-rect>
|
||||||
{ $values { "rect" "a new " { $link rect } } }
|
{ $values { "rect" "a new " { $link rect } } }
|
||||||
{ $description "Creates a rectangle located at the origin with zero dimensions." } ;
|
{ $description "Creates a rectangle located at the origin with zero dimensions." } ;
|
||||||
|
|
||||||
|
ARTICLE: "math.geometry.rect" "Rectangles"
|
||||||
|
"The " { $vocab-link "math.geometry.rect" } " vocabulary defines a rectangle data type and operations on them."
|
||||||
|
{ $subsection rect }
|
||||||
|
"Rectangles can be taken apart:"
|
||||||
|
{ $subsection rect-loc }
|
||||||
|
{ $subsection rect-dim }
|
||||||
|
{ $subsection rect-bounds }
|
||||||
|
{ $subsection rect-extent }
|
||||||
|
"New rectangles can be created:"
|
||||||
|
{ $subsection <zero-rect> }
|
||||||
|
{ $subsection <rect> }
|
||||||
|
{ $subsection <extent-rect> }
|
||||||
|
"More utility words for working with rectangles:"
|
||||||
|
{ $subsection offset-rect }
|
||||||
|
{ $subsection rect-intersect }
|
||||||
|
{ $subsection intersects? } ;
|
||||||
|
|
||||||
|
ABOUT: "math.geometry.rect"
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue