Merge git://factorcode.org/git/factor

db4
Joe Groff 2008-01-25 07:08:25 -08:00
commit c28f6ee6ff
124 changed files with 1732 additions and 697 deletions

View File

@ -1,5 +1,6 @@
USING: alien alien.c-types alien.structs alien.syntax
alien.syntax.private help.markup help.syntax ;
IN: alien.syntax
USING: alien alien.c-types alien.structs alien.syntax.private
help.markup help.syntax ;
HELP: DLL"
{ $syntax "DLL\" path\"" }

View File

@ -48,7 +48,11 @@ IN: bootstrap.stage2
"Compiling remaining words..." print flush
all-words [ compiled? not ] subset recompile-hook get call
"bootstrap.compiler" vocab [
vocabs [
words "compile" "compiler" lookup execute
] each
] when
] with-compiler-errors
f error set-global

View File

@ -7,21 +7,6 @@ optimizer definitions math compiler.errors threads graphs
generic ;
IN: compiler
SYMBOL: compiled-crossref
compiled-crossref global [ H{ } assoc-like ] change-at
: compiled-xref ( word dependencies -- )
2dup "compiled-uses" set-word-prop
compiled-crossref get add-vertex* ;
: compiled-unxref ( word -- )
dup "compiled-uses" word-prop
compiled-crossref get remove-vertex* ;
: compiled-usage ( word -- assoc )
compiled-crossref get at ;
: compiled-usages ( words -- seq )
[ [ dup ] H{ } map>assoc dup ] keep [
compiled-usage [ nip +inlined+ eq? ] assoc-subset update
@ -41,7 +26,7 @@ compiled-crossref global [ H{ } assoc-like ] change-at
>r dupd save-effect r>
f pick compiler-error
over compiled-unxref
compiled-xref ;
over word-vocabulary [ compiled-xref ] [ 2drop ] if ;
: compile-succeeded ( word -- effect dependencies )
[

View File

@ -1,6 +1,6 @@
USING: compiler definitions generic assocs inference math
namespaces parser tools.test words kernel sequences arrays io
effects tools.test.inference compiler.units ;
effects tools.test.inference compiler.units inference.state ;
IN: temporary
DEFER: x-1
@ -205,3 +205,36 @@ DEFER: generic-then-not-generic-test-2
[ ] [ "IN: temporary USE: math : generic-then-not-generic-test-1 1 + ;" eval ] unit-test
[ 4 ] [ generic-then-not-generic-test-2 ] unit-test
DEFER: foldable-test-1
DEFER: foldable-test-2
[ ] [ "IN: temporary : foldable-test-1 3 ; foldable" eval ] unit-test
[ ] [ "IN: temporary : foldable-test-2 foldable-test-1 ;" eval ] unit-test
[ +inlined+ ] [ \ foldable-test-2 \ foldable-test-1 compiled-usage at ] unit-test
[ 3 ] [ foldable-test-2 ] unit-test
[ ] [ "IN: temporary : foldable-test-1 4 ; foldable" eval ] unit-test
[ 4 ] [ foldable-test-2 ] unit-test
DEFER: flushable-test-2
[ ] [ "IN: temporary USE: kernel : flushable-test-1 drop 3 ; flushable" eval ] unit-test
[ ] [ "IN: temporary USE: kernel : flushable-test-2 V{ } dup flushable-test-1 drop ;" eval ] unit-test
[ V{ } ] [ flushable-test-2 ] unit-test
[ ] [ "IN: temporary USING: kernel sequences ; : flushable-test-1 3 over push ;" eval ] unit-test
[ V{ 3 } ] [ flushable-test-2 ] unit-test
: ax ;
: bx ax ;
[ \ bx forget ] with-compilation-unit
[ t ] [ \ ax compiled-usage [ drop interned? ] assoc-all? ] unit-test

View File

@ -51,14 +51,8 @@ HOOK: %save-dispatch-xt compiler-backend ( -- )
M: object %save-dispatch-xt %save-word-xt ;
! Call C primitive
HOOK: %call-primitive compiler-backend ( label -- )
! Call another label
HOOK: %call-label compiler-backend ( label -- )
! Far jump to C primitive
HOOK: %jump-primitive compiler-backend ( label -- )
! Call another word
HOOK: %call compiler-backend ( word -- )
! Local jump for branches
HOOK: %jump-label compiler-backend ( label -- )

View File

@ -97,26 +97,14 @@ M: ppc-backend %epilogue ( n -- )
1 1 rot ADDI
0 MTLR ;
: %prepare-primitive ( word -- )
#! Save stack pointer to stack_chain->callstack_top, load XT
4 1 MR
0 11 LOAD32
rc-absolute-ppc-2/2 rel-primitive ;
: (%call) 11 MTLR BLRL ;
M: ppc-backend %call-primitive ( word -- )
%prepare-primitive (%call) ;
: (%jump) 11 MTCTR BCTR ;
M: ppc-backend %jump-primitive ( word -- )
%prepare-primitive (%jump) ;
: %load-dlsym ( symbol dll register -- )
0 swap LOAD32 rc-absolute-ppc-2/2 rel-dlsym ;
M: ppc-backend %call-label ( label -- ) BL ;
M: ppc-backend %call ( label -- ) BL ;
M: ppc-backend %jump-label ( label -- ) B ;

View File

@ -70,15 +70,7 @@ M: x86-backend %prepare-alien-invoke
temp-reg v>operand 2 cells [+] ds-reg MOV
temp-reg v>operand 3 cells [+] rs-reg MOV ;
M: x86-backend %call-primitive ( word -- )
stack-save-reg stack-reg cell neg [+] LEA
address-operand CALL ;
M: x86-backend %jump-primitive ( word -- )
stack-save-reg stack-reg MOV
address-operand JMP ;
M: x86-backend %call-label ( label -- ) CALL ;
M: x86-backend %call ( label -- ) CALL ;
M: x86-backend %jump-label ( label -- ) JMP ;

View File

@ -78,7 +78,8 @@ PRIVATE>
: pop-front ( dlist -- obj )
dup dlist-front [
dlist-node-next
dup dlist-node-next
f rot set-dlist-node-next
f over set-prev-when
swap set-dlist-front
] 2keep dlist-node-obj
@ -87,13 +88,13 @@ PRIVATE>
: pop-front* ( dlist -- ) pop-front drop ;
: pop-back ( dlist -- obj )
[
dlist-back dup dlist-node-prev f over set-next-when
] keep
[ set-dlist-back ] keep
[ normalize-front ] keep
dec-length
dlist-node-obj ;
dup dlist-back [
dup dlist-node-prev
f rot set-dlist-node-prev
f over set-next-when
swap set-dlist-back
] 2keep dlist-node-obj
swap [ normalize-front ] keep dec-length ;
: pop-back* ( dlist -- ) pop-back drop ;

View File

@ -100,21 +100,10 @@ UNION: #terminal
! node
M: node generate-node drop iterate-next ;
: %call ( word -- )
dup primitive? [ %call-primitive ] [ %call-label ] if ;
: %jump ( word -- )
{
{ [ dup compiling-label get eq? ] [
drop current-label-start get %jump-label
] }
{ [ dup primitive? ] [
%epilogue-later %jump-primitive
] }
{ [ t ] [
%epilogue-later %jump-label
] }
} cond ;
dup compiling-label get eq?
[ drop current-label-start get ] [ %epilogue-later ] if
%jump-label ;
: generate-call ( label -- next )
dup maybe-compile

View File

@ -1,6 +1,6 @@
IN: temporary
USING: sequences inference.transforms tools.test math kernel
quotations ;
quotations tools.test.inference ;
: compose-n-quot <repetition> >quotation ;
: compose-n compose-n-quot call ;
@ -18,3 +18,5 @@ quotations ;
[ 268 ] [ 1 { 8 { 3 2 } } bitfield-quot call ] unit-test
[ 512 ] [ 1 { { 1+ 8 } } bitfield-quot call ] unit-test
\ construct-empty must-infer

View File

@ -1,8 +1,8 @@
! Copyright (C) 2007 Slava Pestov.
! Copyright (C) 2007, 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: arrays kernel words sequences generic math namespaces
quotations assocs combinators math.bitfields inference.backend
inference.dataflow inference.state tuples.private ;
inference.dataflow inference.state tuples.private effects ;
IN: inference.transforms
: pop-literals ( n -- rstate seq )
@ -61,11 +61,21 @@ M: pair (bitfield-quot) ( spec -- quot )
\ set-slots [ <reversed> [get-slots] ] 1 define-transform
: [construct] ( word quot -- newquot )
>r dup +inlined+ depends-on dup tuple-size r> 2curry ;
\ construct-boa [
dup +inlined+ depends-on
dup tuple-size [ <tuple-boa> ] 2curry
] 1 define-transform
\ construct-boa
[ [ <tuple-boa> ] [construct] ] 1 define-transform
\ construct-empty [
1 ensure-values
peek-d value? [
pop-literal
dup +inlined+ depends-on
dup tuple-size [ <tuple> ] 2curry
swap infer-quot
] [
\ construct-empty 1 1 <effect> make-call-node
] if
] "infer" set-word-prop
\ construct-empty
[ [ <tuple> ] [construct] ] 1 define-transform
\ construct-empty 1 1 <effect> "inferred-effect" set-word-prop

7
core/io/backend/backend.factor Normal file → Executable file
View File

@ -1,6 +1,6 @@
! Copyright (C) 2007 Slava Pestov.
! Copyright (C) 2007, 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: init kernel system ;
USING: init kernel system namespaces ;
IN: io.backend
SYMBOL: io-backend
@ -21,3 +21,6 @@ M: object normalize-pathname ;
[ init-io embedded? [ init-stdio ] unless ]
"io.backend" add-init-hook
: set-io-backend ( backend -- )
io-backend set-global init-io init-stdio ;

5
core/io/io-docs.factor Normal file → Executable file
View File

@ -29,7 +29,6 @@ ARTICLE: "stdio" "The default stream"
"Various words take an implicit stream parameter from a variable to reduce stack shuffling."
{ $subsection stdio }
"Unless rebound in a child namespace, this variable will be set to a console stream for interacting with the user."
{ $subsection close }
{ $subsection read1 }
{ $subsection read }
{ $subsection read-until }
@ -178,10 +177,6 @@ $io-error ;
HELP: stdio
{ $var-description "Holds a stream, used for various implicit stream operations. Rebound using " { $link with-stream } " and " { $link with-stream* } "." } ;
HELP: close
{ $contract "Closes the " { $link stdio } " stream." }
$io-error ;
HELP: readln
{ $values { "str/f" "a string or " { $link f } } }
{ $contract "Reads a line of input from the " { $link stdio } " stream. Outputs " { $link f } " on stream exhaustion." }

View File

@ -35,7 +35,8 @@ GENERIC: stream-write-table ( table-cells style stream -- )
! Default stream
SYMBOL: stdio
: close ( -- ) stdio get stream-close ;
! Default error stream
SYMBOL: stderr
: readln ( -- str/f ) stdio get stream-readln ;
: read1 ( -- ch/f ) stdio get stream-read1 ;
@ -53,7 +54,9 @@ SYMBOL: stdio
stdio swap with-variable ; inline
: with-stream ( stream quot -- )
swap [ [ close ] [ ] cleanup ] with-stream* ; inline
swap [
[ stdio get stream-close ] [ ] cleanup
] with-stream* ; inline
: tabular-output ( style quot -- )
swap >r { } make r> stdio get stream-write-table ; inline

View File

@ -14,9 +14,10 @@ ARTICLE: "io.streams.c" "ANSI C streams"
{ $subsection fclose }
{ $subsection fgetc }
{ $subsection fread }
"Two standard file handles:"
{ $subsection stdin }
{ $subsection stdout } ;
"The three standard file handles:"
{ $subsection stdin-handle }
{ $subsection stdout-handle }
{ $subsection stderr-handle } ;
ABOUT: "io.streams.c"
@ -64,10 +65,14 @@ HELP: fread ( n alien -- str/f )
{ $description "Reads a sequence of characters from a C FILE* handle, and outputs " { $link f } " on end of file." }
{ $errors "Throws an error if the input operation failed." } ;
HELP: stdin
HELP: stdin-handle
{ $values { "in" "a C FILE* handle" } }
{ $description "Outputs the console standard input file handle." } ;
HELP: stdout
HELP: stdout-handle
{ $values { "out" "a C FILE* handle" } }
{ $description "Outputs the console standard output file handle." } ;
HELP: stderr-handle
{ $values { "out" "a C FILE* handle" } }
{ $description "Outputs the console standard error file handle." } ;

View File

@ -56,12 +56,13 @@ M: c-reader stream-close
M: object init-io ;
: stdin 11 getenv ;
: stdout 12 getenv ;
: stdin-handle 11 getenv ;
: stdout-handle 12 getenv ;
: stderr-handle 38 getenv ;
M: object init-stdio
stdin stdout <duplex-c-stream> stdio set-global ;
stdin-handle stdout-handle <duplex-c-stream> stdio set-global
stderr-handle <c-writer> <plain-writer> stderr set-global ;
M: object io-multiplex (sleep) ;

View File

@ -49,7 +49,7 @@ ARTICLE: "basic-combinators" "Basic combinators"
{ $subsection execute }
"These words are used to implement " { $emphasis "combinators" } ", which are words that take code from the stack. Note that combinator definitions must be followed by the " { $link POSTPONE: inline } " declaration in order to compile in the optimizing compiler; for example:"
{ $code
": keep ( x quot -- x | quot: x -- )"
": keep ( x quot -- x )"
" over >r call r> ; inline"
}
"Word inlining is documented in " { $link "declarations" } "."
@ -557,7 +557,7 @@ HELP: dip
HELP: while
{ $values { "pred" "a quotation with stack effect " { $snippet "( -- ? )" } } { "body" "a quotation" } { "tail" "a quotation" } }
{ $description "Repeatedly calls " { $snippet "pred" } ". If it yields " { $link f } ", iteration stops, otherwise " { $snippet "quot" } " is called. After iteration stops, " { $snippet "tail" } " is called." }
{ $description "Repeatedly calls " { $snippet "pred" } ". If it yields " { $link f } ", iteration stops, otherwise " { $snippet "body" } " is called. After iteration stops, " { $snippet "tail" } " is called." }
{ $notes "In most cases, tail recursion should be used, because it is simpler both in terms of implementation and conceptually. However in some cases this combinator expresses intent better and should be used."
$nl
"Strictly speaking, the " { $snippet "tail" } " is not necessary, since the following are equivalent:"

View File

@ -209,7 +209,7 @@ HELP: bitxor
HELP: shift
{ $values { "x" integer } { "n" integer } { "y" integer } }
{ $description "Shifts " { $snippet "x" } " to the left by " { $snippet "y" } " bits if " { $snippet "y" } " is positive, or " { $snippet "-y" } " bits to the right if " { $snippet "y" } " is negative. A left shift of a fixnum may overflow, yielding a bignum. A right shift may result in bits ``falling off'' the right hand side and being discarded." }
{ $description "Shifts " { $snippet "x" } " to the left by " { $snippet "n" } " bits if " { $snippet "n" } " is positive, or " { $snippet "-n" } " bits to the right if " { $snippet "n" } " is negative. A left shift of a fixnum may overflow, yielding a bignum. A right shift may result in bits ``falling off'' the right hand side and being discarded." }
{ $examples { $example "BIN: 101 5 shift .b" "10100000" } { $example "BIN: 11111 -2 shift .b" "111" } } ;
HELP: bitnot

View File

@ -15,16 +15,16 @@ IN: namespaces
PRIVATE>
: namespace ( -- namespace ) namestack* peek ;
: namestack ( -- namestack ) namestack* clone ; inline
: set-namestack ( namestack -- ) >vector 0 setenv ; inline
: namestack ( -- namestack ) namestack* clone ;
: set-namestack ( namestack -- ) >vector 0 setenv ;
: global ( -- g ) 21 getenv { hashtable } declare ; inline
: init-namespaces ( -- ) global 1array set-namestack ;
: get ( variable -- value ) namestack* assoc-stack ; flushable
: set ( value variable -- ) namespace set-at ;
: on ( variable -- ) t swap set ; inline
: off ( variable -- ) f swap set ; inline
: get-global ( variable -- value ) global at ; inline
: set-global ( value variable -- ) global set-at ; inline
: get-global ( variable -- value ) global at ;
: set-global ( value variable -- ) global set-at ;
: change ( variable quot -- )
>r dup get r> rot slip set ; inline

View File

@ -17,17 +17,17 @@ SYMBOL: optimizer-changed
GENERIC: optimize-node* ( node -- node/t changed? )
: ?union ( hash/f hash -- hash )
: ?union ( assoc/f assoc -- hash )
over [ union ] [ nip ] if ;
: add-node-literals ( hash node -- )
: add-node-literals ( assoc node -- )
over assoc-empty? [
2drop
] [
[ node-literals ?union ] keep set-node-literals
] if ;
: add-node-classes ( hash node -- )
: add-node-classes ( assoc node -- )
over assoc-empty? [
2drop
] [
@ -324,6 +324,7 @@ M: #dispatch optimize-node*
] if ;
: flush-eval ( #call -- node )
dup node-param +inlined+ depends-on
dup node-out-d length f <repetition> inline-literals ;
: partial-eval? ( #call -- ? )
@ -337,9 +338,9 @@ M: #dispatch optimize-node*
dup node-in-d [ node-literal ] with map ;
: partial-eval ( #call -- node )
dup node-param +inlined+ depends-on
dup literal-in-d over node-param 1quotation
[ with-datastack ] catch
[ 3drop t ] [ inline-literals ] if ;
[ with-datastack inline-literals ] [ 2drop 2drop t ] recover ;
: define-identities ( words identities -- )
[ "identities" set-word-prop ] curry each ;

View File

@ -44,8 +44,7 @@ ARTICLE: "vocabulary-search-errors" "Word lookup errors"
"If the parser cannot not find a word in the current vocabulary search path, it attempts to look for the word in all loaded vocabularies. Then, one of three things happen:"
{ $list
{ "If there are no words having this name at all, an error is thrown and parsing stops." }
{ "If there is exactly one vocabulary having a word with this name, the vocabulary is automatically added to the search path. This behavior is intended for interactive use and exploratory programming only, and production code should contain full " { $link POSTPONE: USING: } " declarations." }
{ "If there is more than one vocabulary which contains a word with this name, a restartable error is thrown, with a restart for each vocabulary in question. The restarts add the vocabulary to the search path and continue parsing." }
{ "If there are vocabularies which contain words with this name, a restartable error is thrown, with a restart for each vocabulary in question. The restarts add the vocabulary to the search path and continue parsing." }
}
"When writing a new vocabulary, one approach is to ignore " { $link POSTPONE: USING: } " declarations altogether, then to load the vocabulary and observe any parser notes and restarts and use this information to write the correct " { $link POSTPONE: USING: } " declaration." ;

4
core/prettyprint/prettyprint-docs.factor Normal file → Executable file
View File

@ -1,6 +1,6 @@
USING: prettyprint.backend prettyprint.config
prettyprint.sections help.markup help.syntax io kernel words
definitions quotations strings ;
prettyprint.sections prettyprint.private help.markup help.syntax
io kernel words definitions quotations strings ;
IN: prettyprint
ARTICLE: "prettyprint-numbers" "Prettyprinting numbers"

View File

@ -86,14 +86,14 @@ combinators quotations ;
: .s ( -- ) datastack stack. ;
: .r ( -- ) retainstack stack. ;
<PRIVATE
SYMBOL: ->
\ ->
{ { foreground { 1 1 1 1 } } { background { 0 0 0 1 } } }
"word-style" set-word-prop
<PRIVATE
! This code is ugly and could probably be simplified
: remove-step-into
building get dup empty? [

View File

@ -175,3 +175,14 @@ SYMBOL: quot-uses-b
[ t ] [ "symbol-generic" "temporary" lookup symbol? ] unit-test
[ f ] [ "symbol-generic" "temporary" lookup generic? ] unit-test
! Regressions
[ ] [ "IN: temporary : decl-forget-test ; foldable" eval ] unit-test
[ t ] [ "decl-forget-test" "temporary" lookup "foldable" word-prop ] unit-test
[ ] [ "IN: temporary : decl-forget-test ;" eval ] unit-test
[ f ] [ "decl-forget-test" "temporary" lookup "foldable" word-prop ] unit-test
[ ] [ "IN: temporary : decl-forget-test ; flushable" eval ] unit-test
[ t ] [ "decl-forget-test" "temporary" lookup "flushable" word-prop ] unit-test
[ ] [ "IN: temporary : decl-forget-test ;" eval ] unit-test
[ f ] [ "decl-forget-test" "temporary" lookup "flushable" word-prop ] unit-test

View File

@ -87,6 +87,25 @@ M: wrapper (quot-uses) >r wrapped r> (quot-uses) ;
M: word uses ( word -- seq )
word-def quot-uses keys ;
SYMBOL: compiled-crossref
compiled-crossref global [ H{ } assoc-like ] change-at
: compiled-xref ( word dependencies -- )
2dup "compiled-uses" set-word-prop
compiled-crossref get add-vertex* ;
: compiled-unxref ( word -- )
dup "compiled-uses" word-prop
compiled-crossref get remove-vertex* ;
: delete-compiled-xref ( word -- )
dup compiled-unxref
compiled-crossref get delete-at ;
: compiled-usage ( word -- assoc )
compiled-crossref get at ;
M: word redefined* ( word -- )
{ "inferred-effect" "base-case" "no-effect" } reset-props ;
@ -127,7 +146,7 @@ SYMBOL: changed-words
: reset-word ( word -- )
{
"unannotated-def"
"parsing" "inline" "foldable"
"parsing" "inline" "foldable" "flushable"
"predicating"
"reading" "writing"
"constructing"
@ -187,6 +206,7 @@ M: word (forget-word)
: forget-word ( word -- )
dup delete-xref
dup delete-compiled-xref
(forget-word) ;
M: word forget* forget-word ;

View File

@ -11,14 +11,17 @@ IN: assocs.lib
! set-hash with alternative stack effects
: put-hash* ( table key value -- ) swap rot set-at ;
: put-hash* ( table key value -- ) spin set-at ;
: put-hash ( table key value -- table ) swap pick set-at ;
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
: set-hash-stack ( value key seq -- )
dupd [ key? ] when find-last nip set-at ;
dupd [ key? ] with find-last nip set-at ;
: at-default ( key assoc -- value/key )
dupd at [ nip ] when* ;
: at-peek ( key assoc -- value ? )
at* dup >r [ peek ] when r> ;

View File

@ -14,7 +14,7 @@ USING: kernel namespaces math quotations arrays hashtables sequences threads
ui.gadgets.packs
ui.gadgets.grids
ui.gadgets.theme
namespaces.lib hashtables.lib vars
namespaces.lib assocs.lib vars
rewrite-closures automata ;
IN: automata.ui
@ -85,4 +85,4 @@ over @center grid-add
: automata-window ( -- ) [ [ automata-window* ] with-scope ] with-ui ;
MAIN: automata-window
MAIN: automata-window

View File

@ -1,7 +1,7 @@
USING: crypto.sha1 io.files kernel ;
IN: benchmark.sha1
: sha1-primes-list ( -- seq )
"extra/math/primes/list/list.factor" resource-path file>sha1 ;
: sha1-primes-list ( -- )
"extra/math/primes/list/list.factor" resource-path file>sha1 drop ;
MAIN: sha1-primes-list

View File

@ -20,7 +20,7 @@ USING: kernel namespaces
ui.gadgets.grids
ui.gestures
combinators.cleave
hashtables.lib vars rewrite-closures boids ;
assocs.lib vars rewrite-closures boids ;
IN: boids.ui
@ -163,4 +163,4 @@ VARS: population-label cohesion-label alignment-label separation-label ;
: boids-window ( -- ) [ [ boids-window* ] with-scope ] with-ui ;
MAIN: boids-window
MAIN: boids-window

View File

@ -10,6 +10,3 @@ IN: bootstrap.io
{ [ wince? ] [ "windows.ce" ] }
} cond append require
] when
init-io
init-stdio

View File

@ -4,7 +4,8 @@
USING: arrays hashtables io io.streams.string kernel math
math.vectors math.functions math.parser namespaces sequences
strings tuples system debugger combinators vocabs.loader
calendar.backend structs alien.c-types math.vectors ;
calendar.backend structs alien.c-types math.vectors
math.ranges shuffle ;
IN: calendar
TUPLE: timestamp year month day hour minute second gmt-offset ;
@ -115,14 +116,18 @@ GENERIC: +second ( timestamp x -- timestamp )
: /rem ( f n -- q r )
#! q is positive or negative, r is positive from 0 <= r < n
[ /f floor >bignum ] 2keep rem ;
[ /f floor >integer ] 2keep rem ;
: float>whole-part ( float -- int float )
[ floor >bignum ] keep over - ;
[ floor >integer ] keep over - ;
: leap-year? ( year -- ? )
GENERIC: leap-year? ( obj -- ? )
M: integer leap-year? ( year -- ? )
dup 100 mod zero? 400 4 ? mod zero? ;
M: timestamp leap-year? ( timestamp -- ? )
timestamp-year leap-year? ;
: adjust-leap-year ( timestamp -- timestamp )
dup >date< 29 = swap 2 = and swap leap-year? not and [
dup >r timestamp-year 3 1 r> [ set-date ] keep
@ -161,7 +166,7 @@ M: real +minute ( timestamp n -- timestamp )
float>whole-part rot swap 60 * +second swap +minute ;
M: number +second ( timestamp n -- timestamp )
over timestamp-second + 60 /rem >r >bignum r>
over timestamp-second + 60 /rem >r >integer r>
pick set-timestamp-second +minute ;
: +dt ( timestamp dt -- timestamp )
@ -178,6 +183,9 @@ M: number +second ( timestamp n -- timestamp )
<timestamp> [ 0 seconds +dt ] keep
[ = [ "invalid timestamp" throw ] unless ] keep ;
: make-date ( year month day -- timestamp )
0 0 0 gmt-offset make-timestamp ;
: array>dt ( vec -- dt ) { dt f } swap append >tuple ;
: +dts ( dt dt -- dt ) [ tuple-slots ] 2apply v+ array>dt ;
@ -214,14 +222,14 @@ M: timestamp <=> ( ts1 ts2 -- n )
[ [ >date< julian-day-number ] 2apply - 86400 * ] 2keep
[ >time< >r >r 3600 * r> 60 * r> + + ] 2apply - + ;
: unix-1970
: unix-1970 ( -- timestamp )
1970 1 1 0 0 0 0 <timestamp> ;
: unix-time>timestamp ( n -- timestamp )
>r unix-1970 r> seconds +dt ;
: timestamp>unix-time ( timestamp -- n )
unix-1970 timestamp- >bignum ;
unix-1970 timestamp- >integer ;
: timestamp>timeval ( timestamp -- timeval )
timestamp>unix-time 1000 * make-timeval ;
@ -240,14 +248,7 @@ M: timestamp <=> ( ts1 ts2 -- n )
: from-now ( dt -- timestamp ) now swap +dt ;
: ago ( dt -- timestamp ) before from-now ;
: days-in-year ( year -- n ) leap-year? 366 365 ? ;
: day-counts { 0 31 28 31 30 31 30 31 31 30 31 30 31 } ;
: days-in-month ( year month -- n )
swap leap-year? [
[ day-counts nth ] keep 2 = [ 1+ ] when
] [
day-counts nth
] if ;
: zeller-congruence ( year month day -- n )
#! Zeller Congruence
@ -258,33 +259,79 @@ M: timestamp <=> ( ts1 ts2 -- n )
[ 1+ 3 * 5 /i + ] keep 2 * + r>
1+ + 7 mod ;
: day-of-week ( timestamp -- n )
GENERIC: days-in-year ( obj -- n )
M: integer days-in-year ( year -- n ) leap-year? 366 365 ? ;
M: timestamp days-in-year ( timestamp -- n ) timestamp-year days-in-year ;
GENERIC: days-in-month ( obj -- n )
M: array days-in-month ( obj -- n )
first2 dup 2 = [
drop leap-year? 29 28 ?
] [
nip day-counts nth
] if ;
M: timestamp days-in-month ( timestamp -- n )
{ timestamp-year timestamp-month } get-slots 2array days-in-month ;
GENERIC: day-of-week ( obj -- n )
M: timestamp day-of-week ( timestamp -- n )
>date< zeller-congruence ;
: day-of-year ( timestamp -- n )
[
[ timestamp-year leap-year? ] keep
[ >date< 3array ] keep timestamp-year 3 1 3array <=>
0 >= and 1 0 ?
] keep
[ timestamp-month day-counts swap head-slice sum + ] keep
timestamp-day + ;
M: array day-of-week ( array -- n )
first3 zeller-congruence ;
: print-day ( n -- )
GENERIC: day-of-year ( obj -- n )
M: array day-of-year ( array -- n )
first3
3dup day-counts rot head-slice sum +
swap leap-year? [
-roll
pick 3 1 make-date >r make-date r>
<=> 0 >= [ 1+ ] when
] [
3nip
] if ;
M: timestamp day-of-year ( timestamp -- n )
{ timestamp-year timestamp-month timestamp-day } get-slots
3array day-of-year ;
GENERIC: day. ( obj -- )
M: integer day. ( n -- )
number>string dup length 2 < [ bl ] when write ;
: print-month ( year month -- )
M: timestamp day. ( timestamp -- )
timestamp-day day. ;
GENERIC: month. ( obj -- )
M: array month. ( pair -- )
first2
[ month-names nth write bl number>string print ] 2keep
[ 1 zeller-congruence ] 2keep
days-in-month day-abbreviations2 " " join print
2array days-in-month day-abbreviations2 " " join print
over " " <repetition> concat write
[
[ 1+ print-day ] keep
[ 1+ day. ] keep
1+ + 7 mod zero? [ nl ] [ bl ] if
] with each nl ;
: print-year ( year -- )
12 [ 1+ print-month nl ] with each ;
M: timestamp month. ( timestamp -- )
{ timestamp-year timestamp-month } get-slots 2array month. ;
GENERIC: year. ( obj -- )
M: integer year. ( n -- )
12 [ 1+ 2array month. nl ] with each ;
M: timestamp year. ( timestamp -- )
timestamp-year year. ;
: pad-00 number>string 2 CHAR: 0 pad-left write ;
@ -298,9 +345,7 @@ M: timestamp <=> ( ts1 ts2 -- n )
timestamp-second >fixnum pad-00 ;
: timestamp>string ( timestamp -- str )
[
(timestamp>string)
] string-out ;
[ (timestamp>string) ] string-out ;
: timestamp>http-string ( timestamp -- str )
#! http timestamp format
@ -319,9 +364,7 @@ M: timestamp <=> ( ts1 ts2 -- n )
timestamp-second >fixnum pad-00 CHAR: Z write1 ;
: timestamp>rfc3339 ( timestamp -- str )
>gmt [
(timestamp>rfc3339)
] string-out ;
>gmt [ (timestamp>rfc3339) ] string-out ;
: expect read1 assert= ;
@ -340,9 +383,7 @@ M: timestamp <=> ( ts1 ts2 -- n )
0 <timestamp> ;
: rfc3339>timestamp ( str -- timestamp )
[
(rfc3339>timestamp)
] string-in ;
[ (rfc3339>timestamp) ] string-in ;
: file-time-string ( timestamp -- string )
[
@ -370,6 +411,23 @@ M: timestamp <=> ( ts1 ts2 -- n )
: friday ( timestamp -- timestamp ) 5 day-this-week ;
: saturday ( timestamp -- timestamp ) 6 day-this-week ;
: beginning-of-day ( timestamp -- new-timestamp )
clone dup >r 0 0 0 r>
{ set-timestamp-hour set-timestamp-minute set-timestamp-second }
set-slots ; inline
: beginning-of-month ( timestamp -- new-timestamp )
beginning-of-day 1 over set-timestamp-day ;
: beginning-of-week ( timestamp -- new-timestamp )
beginning-of-day sunday ;
: beginning-of-year ( timestamp -- new-timestamp )
beginning-of-month 1 over set-timestamp-month ;
: seconds-since-midnight ( timestamp -- x )
dup beginning-of-day timestamp- ;
{
{ [ unix? ] [ "calendar.unix" ] }
{ [ windows? ] [ "calendar.windows" ] }

38
extra/calendar/windows/windows.factor Normal file → Executable file
View File

@ -1,5 +1,5 @@
USING: alien alien.c-types kernel math
windows windows.kernel32 namespaces ;
USING: calendar.backend namespaces alien.c-types
windows windows.kernel32 kernel math ;
IN: calendar.windows
TUPLE: windows-calendar ;
@ -11,37 +11,3 @@ M: windows-calendar gmt-offset ( -- float )
[ GetTimeZoneInformation win32-error=0/f ] keep
[ TIME_ZONE_INFORMATION-Bias ] keep
TIME_ZONE_INFORMATION-DaylightBias + 60 /f neg ;
: >64bit ( lo hi -- n )
32 shift bitor ;
: windows-1601 ( -- timestamp )
1601 1 1 0 0 0 0 <timestamp> ;
: FILETIME>windows-time ( FILETIME -- n )
[ FILETIME-dwLowDateTime ] keep
FILETIME-dwHighDateTime >64bit ;
: windows-time>timestamp ( n -- timestamp )
10000000 /i seconds windows-1601 swap +dt ;
: windows-time ( -- n )
"FILETIME" <c-object> [ GetSystemTimeAsFileTime ] keep
FILETIME>windows-time ;
: timestamp>windows-time ( timestamp -- n )
#! 64bit number representing # of nanoseconds since Jan 1, 1601 (UTC)
>gmt windows-1601 timestamp- >bignum 10000000 * ;
: windows-time>FILETIME ( n -- FILETIME )
"FILETIME" <c-object>
[
[ >r HEX: ffffffff bitand r> set-FILETIME-dwLowDateTime ] 2keep
>r -32 shift r> set-FILETIME-dwHighDateTime
] keep ;
: timestamp>FILETIME ( timestamp -- FILETIME/f )
[ >gmt timestamp>windows-time windows-time>FILETIME ] [ f ] if* ;
: FILETIME>timestamp ( FILETIME -- timestamp/f )
FILETIME>windows-time windows-time>timestamp ;

View File

@ -79,11 +79,11 @@ MACRO: (send) ( selector super? -- quot )
super-message-senders message-senders ? get at
[ slip execute ] 2curry ;
: send ( args... receiver selector -- return... ) f (send) ; inline
: send ( receiver args... selector -- return... ) f (send) ; inline
\ send soft "break-after" set-word-prop
: super-send ( args... receiver selector -- return... ) t (send) ; inline
: super-send ( receiver args... selector -- return... ) t (send) ; inline
\ super-send soft "break-after" set-word-prop

View File

@ -201,3 +201,23 @@ MACRO: construct-slots ( assoc tuple-class -- tuple )
: either ( object first second -- ? )
>r keep swap [ r> drop ] [ r> call ] ?if ; inline
: 2quot-with ( obj seq quot1 quot2 -- seq quot1 quot2 )
>r pick >r with r> r> swapd with ;
: or? ( obj quot1 quot2 -- ? )
>r keep r> rot [ 2nip ] [ call ] if* ; inline
: and? ( obj quot1 quot2 -- ? )
>r keep r> rot [ call ] [ 2drop f ] if ; inline
: prepare-index ( seq quot -- seq n quot )
>r dup length r> ; inline
: each-index ( seq quot -- )
#! quot: ( elt index -- )
prepare-index 2each ; inline
: map-index ( seq quot -- )
#! quot: ( elt index -- obj )
prepare-index 2map ; inline

4
extra/editors/editpadpro/editpadpro.factor Normal file → Executable file
View File

@ -10,6 +10,8 @@ IN: editors.editpadpro
] unless* ;
: editpadpro ( file line -- )
[ editpadpro-path % " /l" % # " \"" % % "\"" % ] "" make run-detached ;
[
editpadpro-path , "/l" swap number>string append , ,
] { } make run-detached drop ;
[ editpadpro ] edit-hook set-global

View File

@ -9,7 +9,7 @@ IN: editors.editplus
: editplus ( file line -- )
[
editplus-path % " -cursor " % # " " % %
] "" make run-detached ;
editplus-path , "-cursor" , number>string , ,
] { } make run-detached drop ;
[ editplus ] edit-hook set-global

7
extra/editors/emacs/emacs.factor Normal file → Executable file
View File

@ -4,8 +4,11 @@ IN: editors.emacs
: emacsclient ( file line -- )
[
"emacsclient --no-wait +" % # " " % %
] "" make run-process ;
"emacsclient" ,
"--no-wait" ,
"+" swap number>string append ,
,
] { } make run-process drop ;
: emacs ( word -- )
where first2 emacsclient ;

View File

@ -9,8 +9,7 @@ IN: editors.emeditor
: emeditor ( file line -- )
[
emeditor-path % " /l " % #
" " % "\"" % % "\"" %
] "" make run-detached ;
emeditor-path , "/l" , number>string , ,
] { } make run-detached drop ;
[ emeditor ] edit-hook set-global

5
extra/editors/notepadpp/notepadpp.factor Normal file → Executable file
View File

@ -9,7 +9,8 @@ IN: editors.notepadpp
: notepadpp ( file line -- )
[
notepadpp-path % " -n" % # " " % %
] "" make run-detached ;
notepadpp-path ,
"-n" swap number>string append , ,
] "" make run-detached drop ;
[ notepadpp ] edit-hook set-global

13
extra/editors/scite/scite.factor Normal file → Executable file
View File

@ -18,14 +18,13 @@ SYMBOL: scite-path
: scite-command ( file line -- cmd )
swap
[ scite-path get %
" \"" %
%
"\" -goto:" %
#
] "" make ;
[
scite-path get ,
,
"-goto:" swap number>string append ,
] { } make ;
: scite-location ( file line -- )
scite-command run-detached ;
scite-command run-detached drop ;
[ scite-location ] edit-hook set-global

5
extra/editors/ted-notepad/ted-notepad.factor Normal file → Executable file
View File

@ -9,8 +9,7 @@ IN: editors.ted-notepad
: ted-notepad ( file line -- )
[
ted-notepad-path % " /l" % #
" " % %
] "" make run-detached ;
ted-notepad-path , "/l" swap number>string append , ,
] { } make run-detached drop ;
[ ted-notepad ] edit-hook set-global

3
extra/editors/textmate/textmate.factor Normal file → Executable file
View File

@ -4,6 +4,7 @@ namespaces prettyprint editors ;
IN: editors.textmate
: textmate-location ( file line -- )
[ "mate -a -l " % # " " % unparse % ] "" make run-process ;
[ "mate" , "-a" , "-l" , number>string , , ] { } make
run-process drop ;
[ textmate-location ] edit-hook set-global

4
extra/editors/ultraedit/ultraedit.factor Normal file → Executable file
View File

@ -10,8 +10,8 @@ IN: editors.ultraedit
: ultraedit ( file line -- )
[
ultraedit-path % " " % swap % "/" % # "/1" %
] "" make run-detached ;
ultraedit-path , [ % "/" % # "/1" % ] "" make ,
] { } make run-detached drop ;
[ ultraedit ] edit-hook set-global

8
extra/editors/vim/vim.factor Normal file → Executable file
View File

@ -10,13 +10,15 @@ HOOK: vim-command vim-editor
TUPLE: vim ;
M: vim vim-command ( file line -- string )
[ "\"" % vim-path get % "\" \"" % swap % "\" +" % # ] "" make ;
M: vim vim-command ( file line -- array )
[
vim-path get , swap , "+" swap number>string append ,
] { } make ;
: vim-location ( file line -- )
vim-command
vim-detach get-global
[ run-detached ] [ run-process ] if ;
[ run-detached ] [ run-process ] if drop ;
"vim" vim-path set-global
[ vim-location ] edit-hook set-global

4
extra/editors/wordpad/wordpad.factor Normal file → Executable file
View File

@ -8,8 +8,6 @@ IN: editors.wordpad
] unless* ;
: wordpad ( file line -- )
[
wordpad-path % drop " " % "\"" % % "\"" %
] "" make run-detached ;
drop wordpad-path swap 2array run-detached drop ;
[ wordpad ] edit-hook set-global

View File

@ -1,28 +1,25 @@
USING: kernel namespaces math math.constants math.functions
arrays sequences opengl opengl.gl opengl.glu ui ui.render
ui.gadgets ui.gadgets.theme ui.gadgets.slate colors ;
USING: kernel namespaces math math.constants math.functions arrays sequences
opengl opengl.gl opengl.glu ui ui.render ui.gadgets ui.gadgets.theme
ui.gadgets.slate colors ;
IN: golden-section
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! To run:
!
! "demos.golden-section" run
! "golden-section" run
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
: disk ( quadric radius center -- )
glPushMatrix
gl-translate
dup 0 glScalef
0 1 10 10 gluDisk
glPopMatrix ;
glPushMatrix
gl-translate
dup 0 glScalef
0 1 10 10 gluDisk
glPopMatrix ;
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
: phi ( -- phi ) 5 sqrt 1 + 2 / 1 - ;
: omega ( i -- omega ) phi * 2 * pi * ;
: omega ( i -- omega ) phi 1- * 2 * pi * ;
: x ( i -- x ) dup omega cos * 0.5 * ;
@ -35,10 +32,10 @@ glPopMatrix ;
: color ( i -- color ) 360.0 / dup 0.25 1 4array ;
: rim ( quadric i -- )
black gl-color dup radius 1.5 * swap center disk ;
black gl-color dup radius 1.5 * swap center disk ;
: inner ( quadric i -- )
dup color gl-color dup radius swap center disk ;
dup color gl-color dup radius swap center disk ;
: dot ( quadric i -- ) 2dup rim inner ;
@ -47,21 +44,21 @@ dup color gl-color dup radius swap center disk ;
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
: with-quadric ( quot -- )
gluNewQuadric [ swap call ] keep gluDeleteQuadric ; inline
gluNewQuadric [ swap call ] keep gluDeleteQuadric ; inline
: display ( -- )
GL_PROJECTION glMatrixMode
glLoadIdentity
-400 400 -400 400 -1 1 glOrtho
GL_MODELVIEW glMatrixMode
glLoadIdentity
[ golden-section ] with-quadric ;
GL_PROJECTION glMatrixMode
glLoadIdentity
-400 400 -400 400 -1 1 glOrtho
GL_MODELVIEW glMatrixMode
glLoadIdentity
[ golden-section ] with-quadric ;
: golden-section-window ( -- )
[
[ display ] <slate>
{ 600 600 } over set-slate-dim
"Golden Section" open-window
] with-ui ;
[
[ display ] <slate>
{ 600 600 } over set-slate-dim
"Golden Section" open-window
] with-ui ;
MAIN: golden-section-window
MAIN: golden-section-window

View File

@ -1,19 +0,0 @@
USING: kernel sequences assocs ;
IN: hashtables.lib
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
: ref-hash ( table key -- value ) swap at ;
! set-hash with alternative stack effects
: put-hash* ( table key value -- ) spin set-at ;
: put-hash ( table key value -- table ) swap pick set-at ;
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
: set-hash-stack ( value key seq -- )
dupd [ key? ] with find-last nip set-at ;

View File

@ -1,4 +1,6 @@
USING: arrays io io.streams.string kernel math math.parser namespaces prettyprint sequences splitting strings ;
USING: arrays combinators.lib io io.streams.string
kernel math math.parser namespaces prettyprint
sequences splitting strings ;
IN: hexdump
<PRIVATE
@ -6,12 +8,16 @@ IN: hexdump
: header. ( len -- )
"Length: " write dup unparse write ", " write >hex write "h" write nl ;
: offset. ( lineno -- ) 16 * >hex 8 CHAR: 0 pad-left write "h: " write ;
: h-pad. ( digit -- ) >hex 2 CHAR: 0 pad-left write ;
: offset. ( lineno -- )
16 * >hex 8 CHAR: 0 pad-left write "h: " write ;
: h-pad. ( digit -- )
>hex 2 CHAR: 0 pad-left write ;
: line. ( str n -- )
offset.
dup [ h-pad. " " write ] each
16 over length - " " <array> concat write
16 over length - 3 * CHAR: \s <string> write
[ dup printable? [ drop CHAR: . ] unless write1 ] each
nl ;
@ -19,9 +25,8 @@ PRIVATE>
: hexdump ( seq -- str )
[
dup length header.
16 <sliced-groups> dup length [ line. ] 2each
16 <sliced-groups> [ line. ] each-index
] string-out ;
: hexdump. ( seq -- )
hexdump write ;

View File

@ -9,11 +9,14 @@ IN: http.client
#! Extract the host name and port number from an HTTP URL.
":" split1 [ string>number ] [ 80 ] if* ;
SYMBOL: domain
: parse-url ( url -- host resource )
"http://" ?head [
"URL must begin with http://" throw
] unless
"/" split1 [ "/" swap append ] [ "/" ] if* ;
dup "https://" head? [
"ssl not yet supported: " swap append throw
] when "http://" ?head drop
"/" split1 [ "/" swap append ] [ "/" ] if*
>r dup empty? [ drop domain get ] [ dup domain set ] if r> ;
: parse-response ( line -- code )
"HTTP/" ?head [ " " split1 nip ] when
@ -52,7 +55,9 @@ DEFER: http-get-stream
: http-get ( url -- code headers string )
#! Opens a stream for reading from an HTTP URL.
http-get-stream [ stdio get contents ] with-stream ;
[
http-get-stream [ stdio get contents ] with-stream
] with-scope ;
: download ( url file -- )
#! Downloads the contents of a URL to a file.

View File

@ -1,6 +1,6 @@
! Copyright (C) 2007 Slava Pestov.
! Copyright (C) 2007, 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: help.markup help.syntax quotations kernel ;
USING: help.markup help.syntax quotations kernel io math ;
IN: io.launcher
HELP: +command+
@ -31,6 +31,36 @@ HELP: +environment-mode+
"Default value is " { $link append-environment } "."
} ;
HELP: +stdin+
{ $description "Launch descriptor key. Must equal one of the following:"
{ $list
{ { $link f } " - standard input is inherited" }
{ { $link +closed+ } " - standard input is closed" }
{ "a path name - standard input is read from the given file, which must exist" }
}
} ;
HELP: +stdout+
{ $description "Launch descriptor key. Must equal one of the following:"
{ $list
{ { $link f } " - standard output is inherited" }
{ { $link +closed+ } " - standard output is closed" }
{ "a path name - standard output is written to the given file, which is overwritten if it already exists" }
}
} ;
HELP: +stderr+
{ $description "Launch descriptor key. Must equal one of the following:"
{ $list
{ { $link f } " - standard error is inherited" }
{ { $link +closed+ } " - standard error is closed" }
{ "a path name - standard error is written to the given file, which is overwritten if it already exists" }
}
} ;
HELP: +closed+
{ $description "Possible value for " { $link +stdin+ } ", " { $link +stdout+ } ", and " { $link +stderr+ } " launch descriptors." } ;
HELP: prepend-environment
{ $description "Possible value of " { $link +environment-mode+ } " launch descriptor key. The child process environment consists of the value of the " { $link +environment+ } " key together with the current environment, with entries from the current environment taking precedence."
$nl
@ -58,7 +88,7 @@ HELP: get-environment
{ $description "Combines the current environment with the value of " { $link +environment+ } " using " { $link +environment-mode+ } "." } ;
HELP: run-process*
{ $values { "desc" "a launch descriptor" } }
{ $values { "desc" "a launch descriptor" } { "handle" "a process handle" } }
{ $contract "Launches a process using the launch descriptor." }
{ $notes "User code should call " { $link run-process } " instead." } ;
@ -73,22 +103,41 @@ HELP: >descriptor
} ;
HELP: run-process
{ $values { "obj" object } }
{ $contract "Launches a process. The object can either be a string, a sequence of strings or a launch descriptor. See " { $link >descriptor } " for details." } ;
{ $values { "obj" object } { "process" process } }
{ $description "Launches a process. The object can either be a string, a sequence of strings or a launch descriptor. See " { $link >descriptor } " for details." }
{ $notes "The output value can be passed to " { $link wait-for-process } " to get an exit code." } ;
HELP: run-detached
{ $values { "obj" object } }
{ $values { "obj" object } { "process" process } }
{ $contract "Launches a process without waiting for it to complete. The object can either be a string, a sequence of strings or a launch descriptor. See " { $link >descriptor } " for details." }
{ $notes
"This word is functionally identical to passing a launch descriptor to " { $link run-process } " having the " { $link +detached+ } " key set."
$nl
"The output value can be passed to " { $link wait-for-process } " to get an exit code."
} ;
HELP: process
{ $class-description "A class representing an active or finished process."
$nl
"Processes are output by " { $link run-process } " and " { $link run-detached } ", and are stored in the " { $link process-stream-process } " slot of " { $link process-stream } " instances."
$nl
"Processes can be passed to " { $link wait-for-process } "." } ;
HELP: process-stream
{ $class-description "A bidirectional stream for interacting with a running process. Instances are created by calling " { $link <process-stream> } ". The " { $link process-stream-process } " slot holds a " { $link process } " instance." } ;
HELP: <process-stream>
{ $values { "obj" object } { "stream" "a bidirectional stream" } }
{ $description "Launches a process and redirects its input and output via a paper of pipes which may be read and written as a stream." }
{ $description "Launches a process and redirects its input and output via a pair of pipes which may be read and written as a stream." }
{ $notes "Closing the stream will block until the process exits." } ;
{ run-process run-detached <process-stream> } related-words
HELP: with-process-stream
{ $values { "obj" object } { "quot" quotation } { "process" process } }
{ $description "Calls " { $snippet "quot" } " in a dynamic scope where " { $link stdio } " is rebound to a " { $link process-stream } ". When the quotation returns, the " { $link process } " instance is output." } ;
HELP: wait-for-process
{ $values { "process" process } { "status" integer } }
{ $description "If the process is still running, waits for it to exit, otherwise outputs the exit code immediately. Can be called multiple times on the same process." } ;
ARTICLE: "io.launcher" "Launching OS processes"
"The " { $vocab-link "io.launcher" } " vocabulary implements cross-platform process launching."
@ -105,9 +154,19 @@ $nl
{ $subsection +detached+ }
{ $subsection +environment+ }
{ $subsection +environment-mode+ }
"Redirecting standard input and output to files:"
{ $subsection +stdin+ }
{ $subsection +stdout+ }
{ $subsection +stderr+ }
"The following words are used to launch processes:"
{ $subsection run-process }
{ $subsection run-detached }
{ $subsection <process-stream> } ;
"Redirecting standard input and output to a pipe:"
{ $subsection <process-stream> }
{ $subsection with-process-stream }
"A class representing an active or finished process:"
{ $subsection process }
"Waiting for a process to end, or getting the exit code of a finished process:"
{ $subsection wait-for-process } ;
ABOUT: "io.launcher"

View File

@ -1,14 +1,39 @@
! Copyright (C) 2007 Slava Pestov.
! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: io.backend system kernel namespaces strings hashtables
sequences assocs combinators vocabs.loader ;
USING: io io.backend system kernel namespaces strings hashtables
sequences assocs combinators vocabs.loader init threads
continuations ;
IN: io.launcher
! Non-blocking process exit notification facility
SYMBOL: processes
[ H{ } clone processes set-global ] "io.launcher" add-init-hook
TUPLE: process handle status ;
HOOK: register-process io-backend ( process -- )
M: object register-process drop ;
: <process> ( handle -- process )
f process construct-boa
V{ } clone over processes get set-at
dup register-process ;
M: process equal? 2drop f ;
M: process hashcode* process-handle hashcode* ;
SYMBOL: +command+
SYMBOL: +arguments+
SYMBOL: +detached+
SYMBOL: +environment+
SYMBOL: +environment-mode+
SYMBOL: +stdin+
SYMBOL: +stdout+
SYMBOL: +stderr+
SYMBOL: +closed+
SYMBOL: prepend-environment
SYMBOL: replace-environment
@ -42,17 +67,38 @@ GENERIC: >descriptor ( obj -- desc )
M: string >descriptor +command+ associate ;
M: sequence >descriptor +arguments+ associate ;
M: assoc >descriptor ;
M: assoc >descriptor >hashtable ;
HOOK: run-process* io-backend ( desc -- )
HOOK: run-process* io-backend ( desc -- handle )
: run-process ( obj -- )
>descriptor run-process* ;
: wait-for-process ( process -- status )
dup process-handle [
dup [ processes get at push stop ] curry callcc0
] when process-status ;
: run-detached ( obj -- )
>descriptor H{ { +detached+ t } } union run-process* ;
: run-process ( obj -- process )
>descriptor
dup run-process*
+detached+ rot at [ dup wait-for-process drop ] unless ;
HOOK: process-stream* io-backend ( desc -- stream )
: run-detached ( obj -- process )
>descriptor H{ { +detached+ t } } union run-process ;
HOOK: process-stream* io-backend ( desc -- stream process )
TUPLE: process-stream process ;
: <process-stream> ( obj -- stream )
>descriptor process-stream* ;
>descriptor process-stream*
{ set-delegate set-process-stream-process }
process-stream construct ;
: with-process-stream ( obj quot -- process )
swap <process-stream>
[ swap with-stream ] keep
process-stream-process ; inline
: notify-exit ( status process -- )
[ set-process-status ] keep
[ processes get delete-at* drop [ schedule-thread ] each ] keep
f swap set-process-handle ;

View File

@ -1,5 +1,5 @@
USING: io io.buffers io.backend help.markup help.syntax kernel
strings sbufs ;
strings sbufs words ;
IN: io.nonblocking
ARTICLE: "io.nonblocking" "Non-blocking I/O implementation"
@ -40,7 +40,7 @@ $nl
{ { $link port-error } " - the most recent I/O error, if any. This error is thrown to the waiting thread when " { $link pending-error } " is called by stream operations" }
{ { $link port-timeout } " - a timeout, specifying the maximum length of time, in milliseconds, for which input operations can block before throwing an error. A value of 0 denotes no timeout is desired." }
{ { $link port-cutoff } " - the time when the current timeout expires; if no input data arrives before this time, an error is thrown" }
{ { $link port-type } " - a symbol identifying the port's intended purpose. Can be " { $link input } ", " { $link output } ", " { $link closed } ", or any other symbol" }
{ { $link port-type } " - a symbol identifying the port's intended purpose" }
{ { $link port-eof? } " - a flag indicating if the port has reached the end of file while reading" }
} } ;
@ -55,7 +55,7 @@ HELP: init-handle
{ $contract "Prepares a native handle for use by the port; called by " { $link <port> } "." } ;
HELP: <port>
{ $values { "handle" "a native handle identifying an I/O resource" } { "buffer" "a " { $link buffer } " or " { $link f } } { "port" "a new " { $link port } } }
{ $values { "handle" "a native handle identifying an I/O resource" } { "buffer" "a " { $link buffer } " or " { $link f } } { "type" symbol } { "port" "a new " { $link port } } }
{ $description "Creates a new " { $link port } " using the specified native handle and I/O buffer." }
$low-level-note ;

View File

@ -12,38 +12,36 @@ SYMBOL: default-buffer-size
! Common delegate of native stream readers and writers
TUPLE: port handle error timeout cutoff type eof? ;
SYMBOL: input
SYMBOL: output
SYMBOL: closed
PREDICATE: port input-port port-type input eq? ;
PREDICATE: port output-port port-type output eq? ;
PREDICATE: port input-port port-type input-port eq? ;
PREDICATE: port output-port port-type output-port eq? ;
GENERIC: init-handle ( handle -- )
GENERIC: close-handle ( handle -- )
: <port> ( handle buffer -- port )
over init-handle
: <port> ( handle buffer type -- port )
pick init-handle
0 0 {
set-port-handle
set-delegate
set-port-type
set-port-timeout
set-port-cutoff
} port construct ;
: <buffered-port> ( handle -- port )
default-buffer-size get <buffer> <port> ;
: <buffered-port> ( handle type -- port )
default-buffer-size get <buffer> swap <port> ;
: <reader> ( handle -- stream )
<buffered-port> input over set-port-type <line-reader> ;
input-port <buffered-port> <line-reader> ;
: <writer> ( handle -- stream )
<buffered-port> output over set-port-type <plain-writer> ;
output-port <buffered-port> <plain-writer> ;
: handle>duplex-stream ( in-handle out-handle -- stream )
<writer>
[ >r <reader> r> <duplex-stream> ]
[ ] [ stream-close ]
[ >r <reader> r> <duplex-stream> ] [ ] [ stream-close ]
cleanup ;
: touch-port ( port -- )
@ -162,7 +160,7 @@ M: output-port stream-flush ( port -- )
M: port stream-close
dup port-type closed eq? [
dup port-type >r closed over set-port-type r>
output eq? [ dup port-flush ] when
output-port eq? [ dup port-flush ] when
dup port-handle close-handle
dup delegate [ buffer-free ] when*
f over set-delegate
@ -170,8 +168,8 @@ M: port stream-close
TUPLE: server-port addr client ;
: <server-port> ( port addr -- server )
server-port pick set-port-type
: <server-port> ( handle addr -- server )
>r f server-port <port> r>
{ set-delegate set-server-port-addr }
server-port construct ;
@ -180,8 +178,8 @@ TUPLE: server-port addr client ;
TUPLE: datagram-port addr packet packet-addr ;
: <datagram-port> ( port addr -- datagram )
datagram-port pick set-port-type
: <datagram-port> ( handle addr -- datagram )
>r f datagram-port <port> r>
{ set-delegate set-datagram-port-addr }
datagram-port construct ;

View File

@ -83,7 +83,7 @@ M: unix-io <sniffer> ( obj -- sniffer )
] keep
dupd sniffer-spec-ifname ioctl-sniffer-fd
dup make-ioctl-buffer
<port> input over set-port-type <line-reader>
input-port <port> <line-reader>
\ sniffer construct-delegate
] with-destructors ;

View File

@ -51,10 +51,13 @@ M: inet4 make-sockaddr ( inet -- sockaddr )
"0.0.0.0" or
rot inet-pton *uint over set-sockaddr-in-addr ;
SYMBOL: port-override
: (port) port-override get [ ] [ ] ?if ;
M: inet4 parse-sockaddr
>r dup sockaddr-in-addr <uint> r> inet-ntop
swap sockaddr-in-port ntohs <inet4> ;
swap sockaddr-in-port ntohs (port) <inet4> ;
M: inet6 inet-ntop ( data addrspec -- str )
drop 16 memory>string 2 <groups> [ be> >hex ] map ":" join ;
@ -80,7 +83,7 @@ M: inet6 make-sockaddr ( inet -- sockaddr )
M: inet6 parse-sockaddr
>r dup sockaddr-in6-addr r> inet-ntop
swap sockaddr-in6-port ntohs <inet6> ;
swap sockaddr-in6-port ntohs (port) <inet6> ;
: addrspec-of-family ( af -- addrspec )
{
@ -102,15 +105,28 @@ M: f parse-sockaddr nip ;
[ dup addrinfo-next swap addrinfo>addrspec ]
[ ] unfold nip [ ] subset ;
: prepare-resolve-host ( host serv passive? -- host' serv' flags )
#! If the port is a number, we resolve for 'http' then
#! change it later. This is a workaround for a FreeBSD
#! getaddrinfo() limitation -- on Windows, Linux and Mac,
#! we can convert a number to a string and pass that as the
#! service name, but on FreeBSD this gives us an unknown
#! service error.
>r
dup integer? [ port-override set "http" ] when
r> AI_PASSIVE 0 ? ;
M: object resolve-host ( host serv passive? -- seq )
>r dup integer? [ number>string ] when
"addrinfo" <c-object>
r> [ AI_PASSIVE over set-addrinfo-flags ] when
PF_UNSPEC over set-addrinfo-family
IPPROTO_TCP over set-addrinfo-protocol
f <void*> [ getaddrinfo addrinfo-error ] keep *void*
[ parse-addrinfo-list ] keep
freeaddrinfo ;
[
prepare-resolve-host
"addrinfo" <c-object>
[ set-addrinfo-flags ] keep
PF_UNSPEC over set-addrinfo-family
IPPROTO_TCP over set-addrinfo-protocol
f <void*> [ getaddrinfo addrinfo-error ] keep *void*
[ parse-addrinfo-list ] keep
freeaddrinfo
] with-scope ;
M: object host-name ( -- name )
256 <byte-array> dup dup length gethostname

0
extra/io/sockets/sockets.factor Normal file → Executable file
View File

View File

@ -1,22 +1,66 @@
! Copyright (C) 2004, 2007 Slava Pestov.
! Copyright (C) 2004, 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: alien bit-arrays generic assocs io kernel
kernel.private math io.nonblocking sequences strings structs
sbufs threads unix vectors io.buffers io.backend
io.streams.duplex math.parser continuations system libc ;
USING: alien generic assocs kernel kernel.private math
io.nonblocking sequences strings structs sbufs threads unix
vectors io.buffers io.backend io.streams.duplex math.parser
continuations system libc qualified namespaces ;
QUALIFIED: io
IN: io.unix.backend
TUPLE: unix-io ;
MIXIN: unix-io
! We want namespaces::bind to shadow the bind system call from
! unix
USING: namespaces ;
! I/O tasks
TUPLE: io-task port callbacks ;
! Global variables
SYMBOL: read-fdset
SYMBOL: read-tasks
SYMBOL: write-fdset
SYMBOL: write-tasks
: io-task-fd io-task-port port-handle ;
: <io-task> ( port continuation class -- task )
>r 1vector io-task construct-boa r> construct-delegate ;
inline
TUPLE: input-task ;
: <input-task> ( port continuation class -- task )
>r input-task <io-task> r> construct-delegate ; inline
TUPLE: output-task ;
: <output-task> ( port continuation class -- task )
>r output-task <io-task> r> construct-delegate ; inline
GENERIC: do-io-task ( task -- ? )
GENERIC: io-task-container ( mx task -- hashtable )
! I/O multiplexers
TUPLE: mx fd reads writes ;
M: input-task io-task-container drop mx-reads ;
M: output-task io-task-container drop mx-writes ;
: <mx> ( -- mx ) f H{ } clone H{ } clone mx construct-boa ;
: construct-mx ( class -- obj ) <mx> swap construct-delegate ;
GENERIC: register-io-task ( task mx -- )
GENERIC: unregister-io-task ( task mx -- )
GENERIC: wait-for-events ( ms mx -- )
: fd/container ( task mx -- task fd container )
over io-task-container >r dup io-task-fd r> ; inline
: check-io-task ( task mx -- )
fd/container key? nip [
"Cannot perform multiple reads from the same port" throw
] when ;
M: mx register-io-task ( task mx -- )
2dup check-io-task fd/container set-at ;
: add-io-task ( task -- ) mx get-global register-io-task ;
M: mx unregister-io-task ( task mx -- )
fd/container delete-at drop ;
! Some general stuff
: file-mode OCT: 0666 ;
@ -49,72 +93,16 @@ M: integer close-handle ( fd -- )
err_no dup ignorable-error?
[ 2drop f ] [ strerror swap report-error t ] if ;
! Associates a port with a list of continuations waiting on the
! port to finish I/O
TUPLE: io-task port callbacks ;
: pop-callbacks ( mx task -- )
dup rot unregister-io-task
io-task-callbacks [ schedule-thread ] each ;
: <io-task> ( port class -- task )
>r V{ } clone io-task construct-boa
{ set-delegate } r> construct ; inline
! Multiplexer
GENERIC: do-io-task ( task -- ? )
GENERIC: task-container ( task -- vector )
: io-task-fd io-task-port port-handle ;
: add-io-task ( callback task -- )
[ io-task-callbacks push ] keep
dup io-task-fd over task-container 2dup at [
"Cannot perform multiple reads from the same port" throw
] when set-at ;
: remove-io-task ( task -- )
dup io-task-fd swap task-container delete-at ;
: pop-callbacks ( task -- )
dup io-task-callbacks swap remove-io-task
[ schedule-thread ] each ;
: handle-fd ( task -- )
: handle-io-task ( mx task -- )
dup io-task-port touch-port
dup do-io-task [ pop-callbacks ] [ drop ] if ;
dup do-io-task [ pop-callbacks ] [ 2drop ] if ;
: handle-fdset ( fdset tasks -- )
swap [
swap dup io-task-port timeout? [
dup io-task-port "Timeout" swap report-error
nip pop-callbacks
] [
tuck io-task-fd swap nth
[ handle-fd ] [ drop ] if
] if drop
] curry assoc-each ;
: init-fdset ( fdset tasks -- )
swap dup clear-bits
[ >r drop t swap r> set-nth ] curry assoc-each ;
: read-fdset/tasks
read-fdset get-global read-tasks get-global ;
: write-fdset/tasks
write-fdset get-global write-tasks get-global ;
: init-fdsets ( -- read write except )
read-fdset/tasks dupd init-fdset
write-fdset/tasks dupd init-fdset
f ;
: (io-multiplex) ( ms -- )
>r FD_SETSIZE init-fdsets r> make-timeval select 0 < [
err_no ignorable-error? [ (io-error) ] unless
] when ;
M: unix-io io-multiplex ( ms -- )
(io-multiplex)
read-fdset/tasks handle-fdset
write-fdset/tasks handle-fdset ;
: handle-timeout ( mx task -- )
"Timeout" over io-task-port report-error pop-callbacks ;
! Readers
: reader-eof ( reader -- )
@ -137,17 +125,15 @@ M: unix-io io-multiplex ( ms -- )
TUPLE: read-task ;
: <read-task> ( port -- task ) read-task <io-task> ;
: <read-task> ( port continuation -- task )
read-task <input-task> ;
M: read-task do-io-task
io-task-port dup refill
[ [ reader-eof ] [ drop ] if ] keep ;
M: read-task task-container drop read-tasks get-global ;
M: input-port (wait-to-read)
[ swap <read-task> add-io-task stop ] callcc0
pending-error ;
[ <read-task> add-io-task stop ] callcc0 pending-error ;
! Writers
: write-step ( port -- ? )
@ -156,35 +142,45 @@ M: input-port (wait-to-read)
TUPLE: write-task ;
: <write-task> ( port -- task ) write-task <io-task> ;
: <write-task> ( port continuation -- task )
write-task <output-task> ;
M: write-task do-io-task
io-task-port dup buffer-empty? over port-error or
[ 0 swap buffer-reset t ] [ write-step ] if ;
M: write-task task-container drop write-tasks get-global ;
: add-write-io-task ( callback task -- )
dup io-task-fd write-tasks get-global at
[ io-task-callbacks push ] [ add-io-task ] ?if ;
: add-write-io-task ( port continuation -- )
over port-handle mx get-global mx-writes at*
[ io-task-callbacks push drop ]
[ drop <write-task> add-io-task ] if ;
: (wait-to-write) ( port -- )
[ swap <write-task> add-write-io-task stop ] callcc0 drop ;
[ add-write-io-task stop ] callcc0 drop ;
M: port port-flush ( port -- )
dup buffer-empty? [ drop ] [ (wait-to-write) ] if ;
USE: io
M: unix-io init-io ( -- )
#! Should only be called on startup. Calling this at any
#! other time can have unintended consequences.
global [
H{ } clone read-tasks set
FD_SETSIZE 8 * <bit-array> read-fdset set
H{ } clone write-tasks set
FD_SETSIZE 8 * <bit-array> write-fdset set
] bind ;
M: unix-io io-multiplex ( ms -- )
mx get-global wait-for-events ;
M: unix-io init-stdio ( -- )
0 1 handle>duplex-stream stdio set-global ;
0 1 handle>duplex-stream io:stdio set-global
2 <writer> io:stderr set-global ;
! mx io-task for embedding an fd-based mx inside another mx
TUPLE: mx-port mx ;
: <mx-port> ( mx -- port )
dup mx-fd f mx-port <port>
{ set-mx-port-mx set-delegate } mx-port construct ;
TUPLE: mx-task ;
: <mx-task> ( port -- task )
f io-task construct-boa mx-task construct-delegate ;
M: mx-task do-io-task
io-task-port mx-port-mx 0 swap wait-for-events f ;
: multiplexer-error ( n -- )
0 < [ err_no ignorable-error? [ (io-error) ] unless ] when ;

29
extra/io/unix/bsd/bsd.factor Executable file
View File

@ -0,0 +1,29 @@
! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
IN: io.unix.bsd
USING: io.backend io.unix.backend io.unix.kqueue io.unix.select
io.launcher io.unix.launcher namespaces kernel assocs threads
continuations ;
! On *BSD and Mac OS X, we use select() for the top-level
! multiplexer, and we hang a kqueue off of it but file change
! notification and process exit notification.
! kqueue is buggy with files and ptys so we can't use it as the
! main multiplexer.
TUPLE: bsd-io ;
INSTANCE: bsd-io unix-io
M: bsd-io init-io ( -- )
<select-mx> mx set-global
<kqueue-mx> kqueue-mx set-global
kqueue-mx get-global <mx-port> <mx-task> dup io-task-fd
2dup mx get-global mx-reads set-at
mx get-global mx-writes set-at ;
M: bsd-io register-process ( process -- )
process-handle kqueue-mx get-global add-pid-task ;
T{ bsd-io } set-io-backend

View File

@ -0,0 +1,62 @@
! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: alien.c-types kernel io.nonblocking io.unix.backend
bit-arrays sequences assocs unix unix.linux.epoll math
namespaces structs ;
IN: io.unix.epoll
TUPLE: epoll-mx events ;
: max-events ( -- n )
#! We read up to 256 events at a time. This is an arbitrary
#! constant...
256 ; inline
: <epoll-mx> ( -- mx )
epoll-mx construct-mx
max-events epoll_create dup io-error over set-mx-fd
max-events "epoll-event" <c-array> over set-epoll-mx-events ;
GENERIC: io-task-events ( task -- n )
M: input-task io-task-events drop EPOLLIN ;
M: output-task io-task-events drop EPOLLOUT ;
: make-event ( task -- event )
"epoll-event" <c-object>
over io-task-events over set-epoll-event-events
swap io-task-fd over set-epoll-event-fd ;
: do-epoll-ctl ( task mx what -- )
>r mx-fd r> rot dup io-task-fd swap make-event
epoll_ctl io-error ;
M: epoll-mx register-io-task ( task mx -- )
2dup EPOLL_CTL_ADD do-epoll-ctl
delegate register-io-task ;
M: epoll-mx unregister-io-task ( task mx -- )
2dup delegate unregister-io-task
EPOLL_CTL_DEL do-epoll-ctl ;
: wait-event ( mx timeout -- n )
>r { mx-fd epoll-mx-events } get-slots max-events
r> epoll_wait dup multiplexer-error ;
: epoll-read-task ( mx fd -- )
over mx-reads at* [ handle-io-task ] [ 2drop ] if ;
: epoll-write-task ( mx fd -- )
over mx-writes at* [ handle-io-task ] [ 2drop ] if ;
: handle-event ( mx kevent -- )
epoll-event-fd 2dup epoll-read-task epoll-write-task ;
: handle-events ( mx n -- )
[
over epoll-mx-events epoll-event-nth handle-event
] with each ;
M: epoll-mx wait-for-events ( ms mx -- )
dup rot wait-event handle-events ;

View File

@ -4,13 +4,15 @@ USING: io.backend io.nonblocking io.unix.backend io.files io
unix kernel math continuations ;
IN: io.unix.files
: read-flags O_RDONLY ; inline
: open-read ( path -- fd )
O_RDONLY file-mode open dup io-error ;
M: unix-io <file-reader> ( path -- stream )
open-read <reader> ;
: write-flags O_WRONLY O_CREAT O_TRUNC bitor bitor ;
: write-flags O_WRONLY O_CREAT O_TRUNC bitor bitor ; inline
: open-write ( path -- fd )
write-flags file-mode open dup io-error ;
@ -18,7 +20,7 @@ M: unix-io <file-reader> ( path -- stream )
M: unix-io <file-writer> ( path -- stream )
open-write <writer> ;
: append-flags O_WRONLY O_APPEND O_CREAT bitor bitor ;
: append-flags O_WRONLY O_APPEND O_CREAT bitor bitor ; inline
: open-append ( path -- fd )
append-flags file-mode open dup io-error

View File

@ -0,0 +1,78 @@
! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: alien.c-types kernel io.nonblocking io.unix.backend
sequences assocs unix unix.kqueue unix.process math namespaces
combinators threads vectors io.launcher io.unix.launcher ;
IN: io.unix.kqueue
TUPLE: kqueue-mx events ;
: max-events ( -- n )
#! We read up to 256 events at a time. This is an arbitrary
#! constant...
256 ; inline
: <kqueue-mx> ( -- mx )
kqueue-mx construct-mx
kqueue dup io-error over set-mx-fd
max-events "kevent" <c-array> over set-kqueue-mx-events ;
GENERIC: io-task-filter ( task -- n )
M: input-task io-task-filter drop EVFILT_READ ;
M: output-task io-task-filter drop EVFILT_WRITE ;
: make-kevent ( task flags -- event )
"kevent" <c-object>
tuck set-kevent-flags
over io-task-fd over set-kevent-ident
swap io-task-filter over set-kevent-filter ;
: register-kevent ( kevent mx -- )
mx-fd swap 1 f 0 f kevent io-error ;
M: kqueue-mx register-io-task ( task mx -- )
over EV_ADD make-kevent over register-kevent
delegate register-io-task ;
M: kqueue-mx unregister-io-task ( task mx -- )
2dup delegate unregister-io-task
swap EV_DELETE make-kevent swap register-kevent ;
: wait-kevent ( mx timespec -- n )
>r dup mx-fd f 0 roll kqueue-mx-events max-events r> kevent
dup multiplexer-error ;
: kevent-read-task ( mx fd -- )
over mx-reads at handle-io-task ;
: kevent-write-task ( mx fd -- )
over mx-reads at handle-io-task ;
: kevent-proc-task ( pid -- )
dup wait-for-pid swap find-process
dup [ notify-exit ] [ 2drop ] if ;
: handle-kevent ( mx kevent -- )
dup kevent-ident swap kevent-filter {
{ [ dup EVFILT_READ = ] [ drop kevent-read-task ] }
{ [ dup EVFILT_WRITE = ] [ drop kevent-write-task ] }
{ [ dup EVFILT_PROC = ] [ drop kevent-proc-task drop ] }
} cond ;
: handle-kevents ( mx n -- )
[ over kqueue-mx-events kevent-nth handle-kevent ] with each ;
M: kqueue-mx wait-for-events ( ms mx -- )
swap make-timespec dupd wait-kevent handle-kevents ;
: make-proc-kevent ( pid -- kevent )
"kevent" <c-object>
tuck set-kevent-ident
EV_ADD over set-kevent-flags
EVFILT_PROC over set-kevent-filter
NOTE_EXIT over set-kevent-fflags ;
: add-pid-task ( pid mx -- )
swap make-proc-kevent swap register-kevent ;

View File

@ -1,9 +1,10 @@
! Copyright (C) 2007 Slava Pestov.
! Copyright (C) 2007, 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: io io.launcher io.unix.backend io.nonblocking
sequences kernel namespaces math system alien.c-types
debugger continuations arrays assocs combinators unix.process
parser-combinators memoize promises strings ;
USING: io io.backend io.launcher io.unix.backend io.unix.files
io.nonblocking sequences kernel namespaces math system
alien.c-types debugger continuations arrays assocs
combinators unix.process parser-combinators memoize
promises strings threads ;
IN: io.unix.launcher
! Search unix first
@ -42,31 +43,35 @@ MEMO: 'arguments' ( -- parser )
: assoc>env ( assoc -- env )
[ "=" swap 3append ] { } assoc>map ;
: (spawn-process) ( -- )
: (redirect) ( path mode fd -- )
>r file-mode open dup io-error dup
r> dup2 io-error close ;
: redirect ( obj mode fd -- )
{
{ [ pick not ] [ 3drop ] }
{ [ pick +closed+ eq? ] [ close 2drop ] }
{ [ pick string? ] [ (redirect) ] }
} cond ;
: setup-redirection ( -- )
+stdin+ get read-flags 0 redirect
+stdout+ get write-flags 1 redirect
+stderr+ get write-flags 2 redirect ;
: spawn-process ( -- )
[
pass-environment? [
get-arguments get-environment assoc>env exec-args-with-env
] [
get-arguments exec-args-with-path
] if io-error
setup-redirection
get-arguments
pass-environment?
[ get-environment assoc>env exec-args-with-env ]
[ exec-args-with-path ] if
io-error
] [ error. :c flush ] recover 1 exit ;
: wait-for-process ( pid -- )
0 <int> 0 waitpid drop ;
: spawn-process ( -- pid )
[ (spawn-process) ] [ ] with-fork ;
: spawn-detached ( -- )
[ spawn-process 0 exit ] [ ] with-fork wait-for-process ;
M: unix-io run-process* ( desc -- )
M: unix-io run-process* ( desc -- pid )
[
+detached+ get [
spawn-detached
] [
spawn-process wait-for-process
] if
[ spawn-process ] [ ] with-fork <process>
] with-descriptor ;
: open-pipe ( -- pair )
@ -80,20 +85,36 @@ M: unix-io run-process* ( desc -- )
: spawn-process-stream ( -- in out pid )
open-pipe open-pipe [
setup-stdio-pipe
(spawn-process)
spawn-process
] [
-rot 2dup second close first close
] with-fork first swap second rot ;
TUPLE: pipe-stream pid ;
: <pipe-stream> ( in out pid -- stream )
pipe-stream construct-boa
-rot handle>duplex-stream over set-delegate ;
M: pipe-stream stream-close
dup delegate stream-close
pipe-stream-pid wait-for-process ;
] with-fork first swap second rot <process> ;
M: unix-io process-stream*
[ spawn-process-stream <pipe-stream> ] with-descriptor ;
[
spawn-process-stream >r handle>duplex-stream r>
] with-descriptor ;
: find-process ( handle -- process )
processes get swap [ nip swap process-handle = ] curry
assoc-find 2drop ;
! Inefficient process wait polling, used on Linux and Solaris.
! On BSD and Mac OS X, we use kqueue() which scales better.
: wait-for-processes ( -- ? )
-1 0 <int> tuck WNOHANG waitpid
dup 0 <= [
2drop t
] [
find-process dup [
>r *uint r> notify-exit f
] [
2drop f
] if
] if ;
: wait-loop ( -- )
wait-for-processes [ 250 sleep ] when wait-loop ;
: start-wait-thread ( -- )
[ wait-loop ] in-thread ;

View File

@ -0,0 +1,15 @@
! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
IN: io.unix.linux
USING: io.backend io.unix.backend io.unix.launcher io.unix.select
namespaces kernel assocs unix.process ;
TUPLE: linux-io ;
INSTANCE: linux-io unix-io
M: linux-io init-io ( -- )
<select-mx> mx set-global
start-wait-thread ;
T{ linux-io } set-io-backend

View File

@ -0,0 +1,47 @@
! Copyright (C) 2004, 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: alien.c-types kernel io.nonblocking io.unix.backend
bit-arrays sequences assocs unix math namespaces structs ;
IN: io.unix.select
TUPLE: select-mx read-fdset write-fdset ;
! Factor's bit-arrays are an array of bytes, OS X expects
! FD_SET to be an array of cells, so we have to account for
! byte order differences on big endian platforms
: little-endian? 1 <int> *char 1 = ; foldable
: munge ( i -- i' )
little-endian? [ BIN: 11000 bitxor ] unless ; inline
: <select-mx> ( -- mx )
select-mx construct-mx
FD_SETSIZE 8 * <bit-array> over set-select-mx-read-fdset
FD_SETSIZE 8 * <bit-array> over set-select-mx-write-fdset ;
: handle-fd ( fd task fdset mx -- )
roll munge rot nth [ swap handle-io-task ] [ 2drop ] if ;
: handle-fdset ( tasks fdset mx -- )
[ handle-fd ] 2curry assoc-each ;
: init-fdset ( tasks fdset -- )
dup clear-bits
[ >r drop t swap munge r> set-nth ] curry assoc-each ;
: read-fdset/tasks
{ mx-reads select-mx-read-fdset } get-slots ;
: write-fdset/tasks
{ mx-writes select-mx-write-fdset } get-slots ;
: init-fdsets ( mx -- read write except )
[ read-fdset/tasks tuck init-fdset ] keep
write-fdset/tasks tuck init-fdset
f ;
M: select-mx wait-for-events ( ms mx -- )
swap >r FD_SETSIZE over init-fdsets r> make-timeval
select multiplexer-error
dup read-fdset/tasks pick handle-fdset
dup write-fdset/tasks rot handle-fdset ;

View File

@ -1,4 +1,4 @@
! Copyright (C) 2004, 2007 Slava Pestov, Ivan Tikhonov.
! Copyright (C) 2004, 2008 Slava Pestov, Ivan Tikhonov.
! See http://factorcode.org/license.txt for BSD license.
! We need to fiddle with the exact search order here, since
@ -33,16 +33,15 @@ M: unix-io addrinfo-error ( n -- )
TUPLE: connect-task ;
: <connect-task> ( port -- task ) connect-task <io-task> ;
: <connect-task> ( port continuation -- task )
connect-task <output-task> ;
M: connect-task do-io-task
io-task-port dup port-handle f 0 write
0 < [ defer-error ] [ drop t ] if ;
M: connect-task task-container drop write-tasks get-global ;
: wait-to-connect ( port -- )
[ swap <connect-task> add-io-task stop ] callcc0 drop ;
[ <connect-task> add-io-task stop ] callcc0 drop ;
M: unix-io (client) ( addrspec -- stream )
dup make-sockaddr/size >r >r
@ -66,9 +65,8 @@ USE: unix
TUPLE: accept-task ;
: <accept-task> ( port -- task ) accept-task <io-task> ;
M: accept-task task-container drop read-tasks get ;
: <accept-task> ( port continuation -- task )
accept-task <input-task> ;
: accept-sockaddr ( port -- fd sockaddr )
dup port-handle swap server-port-addr sockaddr-type
@ -85,7 +83,7 @@ M: accept-task do-io-task
over 0 >= [ do-accept t ] [ 2drop defer-error ] if ;
: wait-to-accept ( server -- )
[ swap <accept-task> add-io-task stop ] callcc0 drop ;
[ <accept-task> add-io-task stop ] callcc0 drop ;
USE: io.sockets
@ -99,7 +97,6 @@ M: unix-io <server> ( addrspec -- stream )
[
SOCK_STREAM server-fd
dup 10 listen zero? [ dup close (io-error) ] unless
f <port>
] keep <server-port> ;
M: unix-io accept ( server -- client )
@ -111,7 +108,7 @@ M: unix-io accept ( server -- client )
! Datagram sockets - UDP and Unix domain
M: unix-io <datagram>
[ SOCK_DGRAM server-fd f <port> ] keep <datagram-port> ;
[ SOCK_DGRAM server-fd ] keep <datagram-port> ;
SYMBOL: receive-buffer
@ -136,7 +133,8 @@ packet-size <byte-array> receive-buffer set-global
TUPLE: receive-task ;
: <receive-task> ( stream -- task ) receive-task <io-task> ;
: <receive-task> ( stream continuation -- task )
receive-task <input-task> ;
M: receive-task do-io-task
io-task-port
@ -149,10 +147,8 @@ M: receive-task do-io-task
2drop defer-error
] if ;
M: receive-task task-container drop read-tasks get ;
: wait-receive ( stream -- )
[ swap <receive-task> add-io-task stop ] callcc0 drop ;
[ <receive-task> add-io-task stop ] callcc0 drop ;
M: unix-io receive ( datagram -- packet addrspec )
dup check-datagram-port
@ -166,8 +162,8 @@ M: unix-io receive ( datagram -- packet addrspec )
TUPLE: send-task packet sockaddr len ;
: <send-task> ( packet sockaddr len port -- task )
send-task <io-task> [
: <send-task> ( packet sockaddr len stream continuation -- task )
send-task <output-task> [
{
set-send-task-packet
set-send-task-sockaddr
@ -182,11 +178,8 @@ M: send-task do-io-task
[ send-task-len do-send ] keep
swap 0 < [ io-task-port defer-error ] [ drop t ] if ;
M: send-task task-container drop write-tasks get ;
: wait-send ( packet sockaddr len stream -- )
[ >r <send-task> r> swap add-io-task stop ] callcc0
2drop 2drop ;
[ <send-task> add-io-task stop ] callcc0 2drop 2drop ;
M: unix-io send ( packet addrspec datagram -- )
3dup check-datagram-send

View File

@ -1,9 +1,10 @@
USE: io.unix.backend
USE: io.unix.files
USE: io.unix.sockets
USE: io.unix.launcher
USE: io.unix.mmap
USE: io.backend
USE: namespaces
USING: io.unix.backend io.unix.files io.unix.sockets
io.unix.launcher io.unix.mmap io.backend combinators namespaces
system vocabs.loader ;
T{ unix-io } io-backend set-global
{
{ [ bsd? ] [ "io.unix.bsd" ] }
{ [ macosx? ] [ "io.unix.bsd" ] }
{ [ linux? ] [ "io.unix.linux" ] }
{ [ solaris? ] [ "io.unix.solaris" ] }
} cond require

View File

@ -3,4 +3,4 @@ io.windows.ce.files io.windows.ce.sockets io.windows.ce.launcher
namespaces io.windows.mmap ;
IN: io.windows.ce
T{ windows-ce-io } io-backend set-global
T{ windows-ce-io } set-io-backend

View File

@ -38,7 +38,7 @@ M: windows-ce-io <server> ( addrspec -- duplex-stream )
[
windows.winsock:SOCK_STREAM server-fd
dup listen-on-socket
<win32-socket> f <port>
<win32-socket>
] keep <server-port> ;
M: windows-ce-io accept ( server -- client )
@ -58,7 +58,7 @@ M: windows-ce-io accept ( server -- client )
M: windows-ce-io <datagram> ( addrspec -- datagram )
[
windows.winsock:SOCK_DGRAM server-fd <win32-socket> f <port>
windows.winsock:SOCK_DGRAM server-fd <win32-socket>
] keep <datagram-port> ;
: packet-size 65536 ; inline

View File

@ -1,9 +1,10 @@
! Copyright (C) 2007 Doug Coleman, Slava Pestov.
! Copyright (C) 2007, 2008 Doug Coleman, Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: alien alien.c-types arrays continuations destructors io
io.windows libc io.nonblocking io.streams.duplex windows.types
math windows.kernel32 windows namespaces io.launcher kernel
sequences windows.errors assocs splitting system ;
io.windows io.windows.pipes libc io.nonblocking
io.streams.duplex windows.types math windows.kernel32 windows
namespaces io.launcher kernel sequences windows.errors assocs
splitting system threads init strings combinators io.backend ;
IN: io.windows.launcher
TUPLE: CreateProcess-args
@ -19,24 +20,17 @@ TUPLE: CreateProcess-args
lpProcessInformation
stdout-pipe stdin-pipe ;
: dispose-CreateProcess-args ( args -- )
#! From MSDN: "Handles in PROCESS_INFORMATION must be closed
#! with CloseHandle when they are no longer needed."
CreateProcess-args-lpProcessInformation dup
PROCESS_INFORMATION-hProcess [ CloseHandle drop ] when*
PROCESS_INFORMATION-hThread [ CloseHandle drop ] when* ;
: default-CreateProcess-args ( -- obj )
0
0
"STARTUPINFO" <c-object>
"STARTUPINFO" heap-size over set-STARTUPINFO-cb
"PROCESS_INFORMATION" <c-object>
TRUE
{
set-CreateProcess-args-bInheritHandles
set-CreateProcess-args-dwCreateFlags
set-CreateProcess-args-lpStartupInfo
set-CreateProcess-args-lpProcessInformation
set-CreateProcess-args-bInheritHandles
} \ CreateProcess-args construct ;
: call-CreateProcess ( CreateProcess-args -- )
@ -93,10 +87,58 @@ TUPLE: CreateProcess-args
over set-CreateProcess-args-lpEnvironment
] when ;
: wait-for-process ( args -- )
CreateProcess-args-lpProcessInformation
PROCESS_INFORMATION-hProcess INFINITE
WaitForSingleObject drop ;
: (redirect) ( path access-mode create-mode -- handle )
>r >r
normalize-pathname
r> ! access-mode
share-mode
security-attributes-inherit
r> ! create-mode
FILE_ATTRIBUTE_NORMAL ! flags and attributes
f ! template file
CreateFile dup invalid-handle? dup close-later ;
: redirect ( obj access-mode create-mode -- handle )
{
{ [ pick not ] [ 3drop f ] }
{ [ pick +closed+ eq? ] [ 3drop t ] }
{ [ pick string? ] [ (redirect) ] }
} cond ;
: ?closed or dup t eq? [ drop f ] when ;
: inherited-stdout ( args -- handle )
CreateProcess-args-stdout-pipe
[ pipe-out ] [ STD_OUTPUT_HANDLE GetStdHandle ] if* ;
: redirect-stdout ( args -- handle )
+stdout+ get GENERIC_WRITE CREATE_ALWAYS redirect
swap inherited-stdout ?closed ;
: inherited-stderr ( args -- handle )
drop STD_ERROR_HANDLE GetStdHandle ;
: redirect-stderr ( args -- handle )
+stderr+ get GENERIC_WRITE CREATE_ALWAYS redirect
swap inherited-stderr ?closed ;
: inherited-stdin ( args -- handle )
CreateProcess-args-stdin-pipe
[ pipe-in ] [ STD_INPUT_HANDLE GetStdHandle ] if* ;
: redirect-stdin ( args -- handle )
+stdin+ get GENERIC_READ OPEN_EXISTING redirect
swap inherited-stdin ?closed ;
: fill-startup-info
dup CreateProcess-args-lpStartupInfo
STARTF_USESTDHANDLES over set-STARTUPINFO-dwFlags
over redirect-stdout over set-STARTUPINFO-hStdOutput
over redirect-stderr over set-STARTUPINFO-hStdError
over redirect-stdin over set-STARTUPINFO-hStdInput
drop ;
: make-CreateProcess-args ( -- args )
default-CreateProcess-args
@ -104,10 +146,46 @@ TUPLE: CreateProcess-args
fill-dwCreateFlags
fill-lpEnvironment ;
M: windows-io run-process* ( desc -- )
M: windows-io run-process* ( desc -- handle )
[
make-CreateProcess-args
dup call-CreateProcess
+detached+ get [ dup wait-for-process ] unless
dispose-CreateProcess-args
] with-descriptor ;
[
make-CreateProcess-args fill-startup-info
dup call-CreateProcess
CreateProcess-args-lpProcessInformation <process>
] with-descriptor
] with-destructors ;
: dispose-process ( process-information -- )
#! From MSDN: "Handles in PROCESS_INFORMATION must be closed
#! with CloseHandle when they are no longer needed."
dup PROCESS_INFORMATION-hProcess [ CloseHandle drop ] when*
PROCESS_INFORMATION-hThread [ CloseHandle drop ] when* ;
: exit-code ( process -- n )
PROCESS_INFORMATION-hProcess
0 <ulong> [ GetExitCodeProcess ] keep *ulong
swap win32-error=0/f ;
: process-exited ( process -- )
dup process-handle exit-code
over process-handle dispose-process
swap notify-exit ;
: wait-for-processes ( processes -- ? )
keys dup
[ process-handle PROCESS_INFORMATION-hProcess ] map
dup length swap >c-void*-array 0 0
WaitForMultipleObjects
dup HEX: ffffffff = [ win32-error ] when
dup WAIT_TIMEOUT = [ 2drop t ] [ swap nth process-exited f ] if ;
: wait-loop ( -- )
processes get dup assoc-empty?
[ drop t ] [ wait-for-processes ] if
[ 250 sleep ] when
wait-loop ;
: start-wait-thread ( -- )
[ wait-loop ] in-thread ;
[ start-wait-thread ] "io.windows.launcher" add-init-hook

View File

@ -116,25 +116,27 @@ M: windows-nt-io add-completion ( handle -- )
: lookup-callback ( GetQueuedCompletion-args -- callback )
io-hash get-global delete-at* drop ;
: wait-for-io ( timeout -- continuation/f )
: handle-overlapped ( timeout -- ? )
wait-for-overlapped [
GetLastError dup expected-io-error? [
2drop f
2drop t
] [
dup eof? [
drop lookup-callback
dup io-callback-port t swap set-port-eof?
io-callback-continuation
] [
(win32-error-string) swap lookup-callback
[ io-callback-port set-port-error ] keep
io-callback-continuation
] if
] if io-callback-continuation schedule-thread f
] if
] [
lookup-callback io-callback-continuation
lookup-callback
io-callback-continuation schedule-thread f
] if ;
: drain-overlapped ( timeout -- )
handle-overlapped [ 0 drain-overlapped ] unless ;
: maybe-expire ( io-callbck -- )
io-callback-port
dup timeout? [
@ -144,10 +146,10 @@ M: windows-nt-io add-completion ( handle -- )
] if ;
: cancel-timeout ( -- )
io-hash get-global values [ maybe-expire ] each ;
io-hash get-global [ nip maybe-expire ] assoc-each ;
M: windows-nt-io io-multiplex ( ms -- )
cancel-timeout wait-for-io [ schedule-thread ] when* ;
cancel-timeout drain-overlapped ;
M: windows-nt-io init-io ( -- )
<master-completion-port> master-completion-port set-global

View File

@ -4,7 +4,7 @@ USING: alien alien.c-types arrays continuations destructors io
io.windows libc io.nonblocking io.streams.duplex windows.types
math windows.kernel32 windows namespaces io.launcher kernel
sequences windows.errors assocs splitting system
io.windows.launcher io.windows.nt.pipes ;
io.windows.launcher io.windows.pipes ;
IN: io.windows.nt.launcher
! The below code is based on the example given in
@ -30,22 +30,10 @@ IN: io.windows.nt.launcher
dup pipe-out f set-inherit
over set-CreateProcess-args-stdin-pipe ;
: fill-startup-info
dup CreateProcess-args-lpStartupInfo
STARTF_USESTDHANDLES over set-STARTUPINFO-dwFlags
over CreateProcess-args-stdout-pipe
pipe-out over set-STARTUPINFO-hStdOutput
over CreateProcess-args-stdout-pipe
pipe-out over set-STARTUPINFO-hStdError
over CreateProcess-args-stdin-pipe
pipe-in swap set-STARTUPINFO-hStdInput ;
M: windows-io process-stream*
[
[
make-CreateProcess-args
TRUE over set-CreateProcess-args-bInheritHandles
fill-stdout-pipe
fill-stdin-pipe
@ -59,6 +47,6 @@ M: windows-io process-stream*
dup CreateProcess-args-stdout-pipe pipe-in
over CreateProcess-args-stdin-pipe pipe-out <win32-duplex-stream>
swap dispose-CreateProcess-args
swap CreateProcess-args-lpProcessInformation <process>
] with-destructors
] with-descriptor ;

View File

@ -9,4 +9,4 @@ USE: io.windows.mmap
USE: io.backend
USE: namespaces
T{ windows-nt-io } io-backend set-global
T{ windows-nt-io } set-io-backend

View File

@ -149,7 +149,7 @@ M: windows-nt-io <server> ( addrspec -- server )
[
SOCK_STREAM server-fd dup listen-on-socket
dup add-completion
<win32-socket> f <port>
<win32-socket>
] keep <server-port>
] with-destructors ;
@ -158,7 +158,7 @@ M: windows-nt-io <datagram> ( addrspec -- datagram )
[
SOCK_DGRAM server-fd
dup add-completion
<win32-socket> f <port>
<win32-socket>
] keep <datagram-port>
] with-destructors ;

View File

@ -3,19 +3,11 @@
USING: alien alien.c-types arrays destructors io io.windows libc
windows.types math windows.kernel32 windows namespaces kernel
sequences windows.errors assocs math.parser system random ;
IN: io.windows.nt.pipes
IN: io.windows.pipes
! This code is based on
! http://twistedmatrix.com/trac/browser/trunk/twisted/internet/iocpreactor/process.py
: default-security-attributes ( -- obj )
"SECURITY_ATTRIBUTES" <c-object>
"SECURITY_ATTRIBUTES" heap-size over set-SECURITY_ATTRIBUTES-nLength ;
: security-attributes-inherit ( -- obj )
default-security-attributes
TRUE over set-SECURITY_ATTRIBUTES-bInheritHandle ; foldable
: create-named-pipe ( name mode -- handle )
FILE_FLAG_OVERLAPPED bitor
PIPE_TYPE_BYTE

View File

@ -4,7 +4,7 @@ USING: alien alien.c-types arrays destructors io io.backend
io.buffers io.files io.nonblocking io.sockets io.binary
io.sockets.impl windows.errors strings io.streams.duplex kernel
math namespaces sequences windows windows.kernel32
windows.shell32 windows.winsock splitting ;
windows.shell32 windows.types windows.winsock splitting ;
IN: io.windows
TUPLE: windows-nt-io ;
@ -34,6 +34,14 @@ M: windows-io normalize-directory ( string -- string )
FILE_SHARE_READ FILE_SHARE_WRITE bitor
FILE_SHARE_DELETE bitor ; foldable
: default-security-attributes ( -- obj )
"SECURITY_ATTRIBUTES" <c-object>
"SECURITY_ATTRIBUTES" heap-size over set-SECURITY_ATTRIBUTES-nLength ;
: security-attributes-inherit ( -- obj )
default-security-attributes
TRUE over set-SECURITY_ATTRIBUTES-bInheritHandle ; foldable
M: win32-file init-handle ( handle -- )
drop ;

View File

@ -4,6 +4,8 @@ IN: math.constants
ARTICLE: "math-constants" "Constants"
"Standard mathematical constants:"
{ $subsection e }
{ $subsection gamma }
{ $subsection phi }
{ $subsection pi }
"Various limits:"
{ $subsection most-positive-fixnum }
@ -15,6 +17,13 @@ ABOUT: "math-constants"
HELP: e
{ $values { "e" "base of natural logarithm" } } ;
HELP: gamma
{ $values { "gamma" "Euler-Mascheroni constant" } }
{ $description "The Euler-Mascheroni constant, also called \"Euler's constant\" or \"the Euler constant\"." } ;
HELP: phi
{ $values { "phi" "golden ratio" } } ;
HELP: pi
{ $values { "pi" "circumference of circle with diameter 1" } } ;

View File

@ -3,5 +3,7 @@
IN: math.constants
: e ( -- e ) 2.7182818284590452354 ; inline
: gamma ( -- gamma ) 0.57721566490153286060 ; inline
: pi ( -- pi ) 3.14159265358979323846 ; inline
: phi ( -- phi ) 1.61803398874989484820 ; inline
: epsilon ( -- epsilon ) 2.2204460492503131e-16 ; inline

View File

@ -1,4 +1,5 @@
USING: math.miller-rabin kernel math namespaces tools.test ;
USING: math.miller-rabin tools.test ;
IN: temporary
[ f ] [ 473155932665450549999756893736999469773678960651272093993257221235459777950185377130233556540099119926369437865330559863 miller-rabin ] unit-test
[ t ] [ 2 miller-rabin ] unit-test
@ -7,4 +8,3 @@ USING: math.miller-rabin kernel math namespaces tools.test ;
[ t ] [ 37 miller-rabin ] unit-test
[ 101 ] [ 100 next-prime ] unit-test
[ 100000000000031 ] [ 100000000000000 next-prime ] unit-test

View File

@ -0,0 +1 @@
Miller-Rabin probabilistic primality test

View File

@ -12,10 +12,10 @@ IN: math.text.english
"Seventeen" "Eighteen" "Nineteen" } nth ;
: tens ( n -- str )
{ "" "" "Twenty" "Thirty" "Forty" "Fifty" "Sixty" "Seventy" "Eighty" "Ninety" } nth ;
{ f f "Twenty" "Thirty" "Forty" "Fifty" "Sixty" "Seventy" "Eighty" "Ninety" } nth ;
: scale-numbers ( n -- str ) ! up to 10^99
{ "" "Thousand" "Million" "Billion" "Trillion" "Quadrillion" "Quintillion"
{ f "Thousand" "Million" "Billion" "Trillion" "Quadrillion" "Quintillion"
"Sextillion" "Septillion" "Octillion" "Nonillion" "Decillion" "Undecillion"
"Duodecillion" "Tredecillion" "Quattuordecillion" "Quindecillion"
"Sexdecillion" "Septendecillion" "Octodecillion" "Novemdecillion"
@ -45,7 +45,7 @@ SYMBOL: and-needed?
: tens-place ( n -- str )
100 mod dup 20 >= [
10 /mod >r tens r>
10 /mod [ tens ] dip
dup zero? [ drop ] [ "-" swap small-numbers 3append ] if
] [
dup zero? [ drop "" ] [ small-numbers ] if
@ -97,3 +97,4 @@ PRIVATE>
] [
[ (number>text) ] with-scope
] if ;

View File

@ -0,0 +1 @@
Convert integers to text in multiple languages

View File

@ -1,8 +1,8 @@
! USING: kernel quotations namespaces sequences hashtables.lib ;
! USING: kernel quotations namespaces sequences assocs.lib ;
USING: kernel namespaces namespaces.private quotations sequences
hashtables.lib ;
assocs.lib ;
IN: namespaces.lib
@ -16,4 +16,4 @@ IN: namespaces.lib
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
: set* ( val var -- ) namestack* set-hash-stack ;
: set* ( val var -- ) namestack* set-hash-stack ;

View File

@ -1,6 +1,6 @@
! Copyright (c) 2007 Aaron Schaefer.
! Copyright (c) 2007 Aaron Schaefer, Alexander Solovyov.
! See http://factorcode.org/license.txt for BSD license.
USING: kernel math sequences ;
USING: kernel math sequences shuffle ;
IN: project-euler.002
! http://projecteuler.net/index.php?section=problems&id=2
@ -22,12 +22,12 @@ IN: project-euler.002
<PRIVATE
: (fib-upto) ( seq n limit -- seq )
2dup <= [ >r add dup 2 tail* sum r> (fib-upto) ] [ 2drop ] if ;
2dup <= [ [ over push dup 2 tail* sum ] dip (fib-upto) ] [ 2drop ] if ;
PRIVATE>
: fib-upto ( n -- seq )
{ 0 } 1 rot (fib-upto) ;
V{ 0 } clone 1 rot (fib-upto) ;
: euler002 ( -- answer )
1000000 fib-upto [ even? ] subset sum ;
@ -35,4 +35,18 @@ PRIVATE>
! [ euler002 ] 100 ave-time
! 0 ms run / 0 ms GC ave time - 100 trials
MAIN: euler002
! ALTERNATE SOLUTIONS
! -------------------
: fib-upto* ( n -- seq )
0 1 [ pick over >= ] [ tuck + dup ] [ ] unfold 3nip
1 head-slice* { 0 1 } swap append ;
: euler002a ( -- answer )
1000000 fib-upto* [ even? ] subset sum ;
! [ euler002a ] 100 ave-time
! 0 ms run / 0 ms GC ave time - 100 trials
MAIN: euler002a

View File

@ -1,6 +1,6 @@
! Copyright (c) 2007 Aaron Schaefer, Daniel Ehrenberg.
! See http://factorcode.org/license.txt for BSD license.
USING: arrays combinators.lib hashtables kernel math math.parser math.ranges
USING: hashtables kernel math math.parser math.ranges project-euler.common
sequences sorting ;
IN: project-euler.004
@ -21,9 +21,6 @@ IN: project-euler.004
: palindrome? ( n -- ? )
number>string dup reverse = ;
: cartesian-product ( seq1 seq2 -- seq1xseq2 )
swap [ swap [ 2array ] map-with ] map-with concat ;
<PRIVATE
: source-004 ( -- seq )

View File

@ -1,4 +1,4 @@
! Copyright (c) 2007 Aaron Schaefer.
! Copyright (c) 2008 Aaron Schaefer.
! See http://factorcode.org/license.txt for BSD license.
USING: hashtables kernel math math.ranges project-euler.common sequences
sorting ;

View File

@ -1,4 +1,4 @@
! Copyright (c) 2007 Aaron Schaefer.
! Copyright (c) 2008 Aaron Schaefer.
! See http://factorcode.org/license.txt for BSD license.
USING: kernel math math.parser math.ranges namespaces sequences ;
IN: project-euler.024

View File

@ -1,7 +1,7 @@
! Copyright (c) 2007 Aaron Schaefer.
! Copyright (c) 2008 Aaron Schaefer.
! See http://factorcode.org/license.txt for BSD license.
USING: alien.syntax kernel math math.functions math.parser math.ranges memoize
project-euler.common sequences ;
USING: alien.syntax kernel math math.constants math.functions math.parser
math.ranges memoize project-euler.common sequences ;
IN: project-euler.025
! http://projecteuler.net/index.php?section=problems&id=25
@ -67,9 +67,6 @@ PRIVATE>
<PRIVATE
: phi ( -- phi )
5 sqrt 1+ 2 / ;
: digit-fib* ( n -- term )
1- 5 log10 2 / + phi log10 / ceiling >integer ;

View File

@ -1,4 +1,4 @@
! Copyright (c) 2007 Aaron Schaefer.
! Copyright (c) 2008 Aaron Schaefer.
! See http://factorcode.org/license.txt for BSD license.
USING: kernel math math.functions math.primes math.ranges sequences ;
IN: project-euler.026

View File

@ -0,0 +1,75 @@
! Copyright (c) 2008 Aaron Schaefer.
! See http://factorcode.org/license.txt for BSD license.
USING: kernel math math.primes project-euler.common sequences ;
IN: project-euler.027
! http://projecteuler.net/index.php?section=problems&id=27
! DESCRIPTION
! -----------
! Euler published the remarkable quadratic formula:
! n² + n + 41
! It turns out that the formula will produce 40 primes for the consecutive
! values n = 0 to 39. However, when n = 40, 402 + 40 + 41 = 40(40 + 1) + 41 is
! divisible by 41, and certainly when n = 41, 41² + 41 + 41 is clearly
! divisible by 41.
! Using computers, the incredible formula n² - 79n + 1601 was discovered, which
! produces 80 primes for the consecutive values n = 0 to 79. The product of the
! coefficients, -79 and 1601, is -126479.
! Considering quadratics of the form:
! n² + an + b, where |a| < 1000 and |b| < 1000
! where |n| is the modulus/absolute value of n
! e.g. |11| = 11 and |-4| = 4
! Find the product of the coefficients, a and b, for the quadratic expression
! that produces the maximum number of primes for consecutive values of n,
! starting with n = 0.
! SOLUTION
! --------
! b must be prime since n = 0 must return a prime
! a + b + 1 must be prime since n = 1 must return a prime
! 1 - a + b must be prime as well, hence >= 2. Therefore:
! 1 - a + b >= 2
! b - a >= 1
! a < b
<PRIVATE
: source-027 ( -- seq )
1000 [ prime? ] subset [ dup [ neg ] map append ] keep
cartesian-product [ first2 < ] subset ;
: quadratic ( b a n -- m )
dup sq -rot * + + ;
: (consecutive-primes) ( b a n -- m )
3dup quadratic prime? [ 1+ (consecutive-primes) ] [ 2nip ] if ;
: consecutive-primes ( a b -- m )
swap 0 (consecutive-primes) ;
: max-consecutive ( seq -- elt n )
dup [ first2 consecutive-primes ] map dup supremum
over index [ swap nth ] curry 2apply ;
PRIVATE>
: euler027 ( -- answer )
source-027 max-consecutive drop product ;
! [ euler027 ] 100 ave-time
! 687 ms run / 23 ms GC ave time - 100 trials
! TODO: generalize max-consecutive/max-product (from #26) into a new word
MAIN: euler027

View File

@ -0,0 +1,46 @@
! Copyright (c) 2008 Aaron Schaefer.
! See http://factorcode.org/license.txt for BSD license.
USING: combinators.lib kernel math math.ranges ;
IN: project-euler.028
! http://projecteuler.net/index.php?section=problems&id=28
! DESCRIPTION
! -----------
! Starting with the number 1 and moving to the right in a clockwise direction a
! 5 by 5 spiral is formed as follows:
! 21 22 23 24 25
! 20 7 8 9 10
! 19 6 1 2 11
! 18 5 4 3 12
! 17 16 15 14 13
! It can be verified that the sum of both diagonals is 101.
! What is the sum of both diagonals in a 1001 by 1001 spiral formed in the same way?
! SOLUTION
! --------
! For a square sized n by n, the sum of corners is 4n² - 6n + 6
<PRIVATE
: sum-corners ( n -- sum )
dup 1 = [ [ sq 4 * ] keep 6 * - 6 + ] unless ;
: sum-diags ( n -- sum )
1 swap 2 <range> [ sum-corners ] sigma ;
PRIVATE>
: euler028 ( -- answer )
1001 sum-diags ;
! [ euler028 ] 100 ave-time
! 0 ms run / 0 ms GC ave time - 100 trials
MAIN: euler028

View File

@ -0,0 +1,37 @@
! Copyright (c) 2008 Aaron Schaefer.
! See http://factorcode.org/license.txt for BSD license.
USING: hashtables kernel math.functions math.ranges project-euler.common
sequences ;
IN: project-euler.029
! http://projecteuler.net/index.php?section=problems&id=29
! DESCRIPTION
! -----------
! Consider all integer combinations of a^b for 2 ≤ a ≤ 5 and 2 ≤ b ≤ 5:
! 2^2 = 4, 2^3 = 8, 2^4 = 16, 2^5 = 32
! 3^2 = 9, 3^3 = 27, 3^4 = 81, 3^5 = 243
! 4^2 = 16, 4^3 = 64, 4^4 = 256, 4^5 = 1024
! 5^2 = 25, 5^3 = 125, 5^4 = 625, 5^5 = 3125
! If they are then placed in numerical order, with any repeats removed, we get
! the following sequence of 15 distinct terms:
! 4, 8, 9, 16, 25, 27, 32, 64, 81, 125, 243, 256, 625, 1024, 3125
! How many distinct terms are in the sequence generated by a^b for 2 ≤ a ≤ 100
! and 2 ≤ b ≤ 100?
! SOLUTION
! --------
: euler029 ( -- answer )
2 100 [a,b] dup cartesian-product [ first2 ^ ] map prune length ;
! [ euler029 ] 100 ave-time
! 951 ms run / 12 ms GC ave time - 100 trials
MAIN: euler029

View File

@ -0,0 +1,46 @@
! Copyright (c) 2008 Aaron Schaefer.
! See http://factorcode.org/license.txt for BSD license.
USING: combinators.lib kernel math math.functions project-euler.common sequences ;
IN: project-euler.030
! http://projecteuler.net/index.php?section=problems&id=30
! DESCRIPTION
! -----------
! Surprisingly there are only three numbers that can be written as the sum of
! fourth powers of their digits:
! 1634 = 1^4 + 6^4 + 3^4 + 4^4
! 8208 = 8^4 + 2^4 + 0^4 + 8^4
! 9474 = 9^4 + 4^4 + 7^4 + 4^4
! As 1 = 1^4 is not a sum it is not included.
! The sum of these numbers is 1634 + 8208 + 9474 = 19316.
! Find the sum of all the numbers that can be written as the sum of fifth
! powers of their digits.
! SOLUTION
! --------
! if n is the number of digits
! n * 9^5 = 10^n when n ≈ 5.513
! 10^5.513 ≈ 325537
<PRIVATE
: sum-fifth-powers ( n -- sum )
number>digits [ 5 ^ ] sigma ;
PRIVATE>
: euler030 ( -- answer )
325537 [ dup sum-fifth-powers = ] subset sum 1- ;
! [ euler030 ] 100 ave-time
! 2537 ms run / 125 ms GC ave time - 100 trials
MAIN: euler030

View File

@ -0,0 +1,63 @@
! Copyright (c) 2008 Aaron Schaefer.
! See http://factorcode.org/license.txt for BSD license.
USING: kernel math ;
IN: project-euler.031
! http://projecteuler.net/index.php?section=problems&id=31
! DESCRIPTION
! -----------
! In England the currency is made up of pound, £, and pence, p, and there are
! eight coins in general circulation:
! 1p, 2p, 5p, 10p, 20p, 50p, £1 (100p) and £2 (200p).
! It is possible to make £2 in the following way:
! 1×£1 + 1×50p + 2×20p + 1×5p + 1×2p + 3×1p
! How many different ways can £2 be made using any number of coins?
! SOLUTION
! --------
<PRIVATE
: 1p ( m -- n )
drop 1 ;
: 2p ( m -- n )
dup 0 >= [ [ 2 - 2p ] keep 1p + ] [ drop 0 ] if ;
: 5p ( m -- n )
dup 0 >= [ [ 5 - 5p ] keep 2p + ] [ drop 0 ] if ;
: 10p ( m -- n )
dup 0 >= [ [ 10 - 10p ] keep 5p + ] [ drop 0 ] if ;
: 20p ( m -- n )
dup 0 >= [ [ 20 - 20p ] keep 10p + ] [ drop 0 ] if ;
: 50p ( m -- n )
dup 0 >= [ [ 50 - 50p ] keep 20p + ] [ drop 0 ] if ;
: 100p ( m -- n )
dup 0 >= [ [ 100 - 100p ] keep 50p + ] [ drop 0 ] if ;
: 200p ( m -- n )
dup 0 >= [ [ 200 - 200p ] keep 100p + ] [ drop 0 ] if ;
PRIVATE>
: euler031 ( -- answer )
200 200p ;
! [ euler031 ] 100 ave-time
! 4 ms run / 0 ms GC ave time - 100 trials
! TODO: generalize to eliminate duplication; use a sequence to specify denominations?
MAIN: euler031

View File

@ -0,0 +1,81 @@
! Copyright (c) 2008 Aaron Schaefer.
! See http://factorcode.org/license.txt for BSD license.
USING: combinators.lib hashtables kernel math math.combinatorics math.parser
math.ranges project-euler.common project-euler.024 sequences sorting ;
IN: project-euler.032
! http://projecteuler.net/index.php?section=problems&id=32
! DESCRIPTION
! -----------
! The product 7254 is unusual, as the identity, 39 × 186 = 7254, containing
! multiplicand, multiplier, and product is 1 through 9 pandigital.
! Find the sum of all products whose multiplicand/multiplier/product identity
! can be written as a 1 through 9 pandigital.
! HINT: Some products can be obtained in more than one way so be sure to only
! include it once in your sum.
! SOLUTION
! --------
! Generate all pandigital numbers and then check if they fit the identity
<PRIVATE
: source-032 ( -- seq )
9 factorial [ 9 permutation [ 1+ ] map 10 swap digits>integer ] map ;
: 1and4 ( n -- ? )
number>string 1 cut-slice 4 cut-slice
[ 10 string>integer ] 3apply [ * ] dip = ;
: 2and3 ( n -- ? )
number>string 2 cut-slice 3 cut-slice
[ 10 string>integer ] 3apply [ * ] dip = ;
: valid? ( n -- ? )
dup 1and4 swap 2and3 or ;
: products ( seq -- m )
[ number>string 4 tail* 10 string>integer ] map ;
PRIVATE>
: euler032 ( -- answer )
source-032 [ valid? ] subset products prune sum ;
! [ euler032 ] 10 ave-time
! 27609 ms run / 2484 ms GC ave time - 10 trials
! ALTERNATE SOLUTIONS
! -------------------
! Generate all reasonable multiplicand/multiplier pairs, then multiply and see
! if the equation is pandigital
<PRIVATE
: source-032a ( -- seq )
50 [1,b] 2000 [1,b] cartesian-product ;
: pandigital? ( n -- ? )
number>string natural-sort "123456789" = ;
! multiplicand/multiplier/product
: mmp ( pair -- n )
first2 2dup * [ number>string ] 3apply 3append 10 string>integer ;
PRIVATE>
: euler032a ( -- answer )
source-032a [ mmp ] map [ pandigital? ] subset products prune sum ;
! [ euler032a ] 100 ave-time
! 5978 ms run / 327 ms GC ave time - 100 trials
MAIN: euler032a

View File

@ -1,5 +1,5 @@
USING: kernel math math.functions math.miller-rabin math.parser
math.primes.factors math.ranges namespaces sequences ;
USING: arrays combinators.lib kernel math math.functions math.miller-rabin
math.parser math.primes.factors math.ranges namespaces sequences ;
IN: project-euler.common
! A collection of words used by more than one Project Euler solution
@ -7,10 +7,11 @@ IN: project-euler.common
! Problems using each public word
! -------------------------------
! cartesian-product - #4, #27
! collect-consecutive - #8, #11
! log10 - #25, #134
! max-path - #18, #67
! number>digits - #16, #20
! number>digits - #16, #20, #30
! propagate-all - #18, #67
! sum-proper-divisors - #21
! tau* - #12
@ -45,6 +46,9 @@ IN: project-euler.common
PRIVATE>
: cartesian-product ( seq1 seq2 -- seq1xseq2 )
swap [ swap [ 2array ] map-with ] map-with concat ;
: collect-consecutive ( seq width -- seq )
[
2dup count-shifts [ 2dup head shift-3rd , ] times

View File

@ -8,7 +8,8 @@ USING: definitions io io.files kernel math.parser sequences vocabs
project-euler.013 project-euler.014 project-euler.015 project-euler.016
project-euler.017 project-euler.018 project-euler.019 project-euler.020
project-euler.021 project-euler.022 project-euler.023 project-euler.024
project-euler.025 project-euler.026 project-euler.067 project-euler.134
project-euler.025 project-euler.026 project-euler.027 project-euler.028
project-euler.029 project-euler.030 project-euler.067 project-euler.134
project-euler.169 project-euler.173 project-euler.175 ;
IN: project-euler

View File

@ -1,4 +1,5 @@
USING: shufflers help.syntax help.markup ;
USING: help.syntax help.markup ;
IN: shufflers
HELP: SHUFFLE:
{ $syntax "SHUFFLE: alphabet #" }

View File

@ -29,7 +29,7 @@ IN: shufflers
: define-shuffles ( names max-out -- )
in-shuffle over length make-shuffles [
[ shuffle>string create-in ] keep
shuffle>quot dupd define-compound put-effect
shuffle>quot dupd define put-effect
] with each out-shuffle ;
: SHUFFLE:

View File

@ -7,7 +7,7 @@ IN: state-machine
";" parse-tokens
[ length ] keep
unclip add
[ create-in swap 1quotation define-compound ] 2each ; parsing
[ create-in swap 1quotation define ] 2each ; parsing
TUPLE: state place data ;
@ -27,7 +27,7 @@ M: missing-state error.
: define-machine ( word state-class -- )
execute make-machine
>r over r> define-compound
>r over r> define
"state-table" set-word-prop ;
: MACHINE:

View File

@ -1,8 +1,14 @@
USING: math arrays sequences ;
USING: math arrays sequences kernel splitting strings ;
IN: strings.lib
: char>digit ( c -- i ) 48 - ;
: string>digits ( s -- seq ) [ char>digit ] { } map-as ;
: >Upper ( str -- str )
dup empty? [
unclip ch>upper 1string swap append
] unless ;
: >Upper-dashes ( str -- str )
"-" split [ >Upper ] map "-" join ;

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