diff --git a/core/compiler/units/units.factor b/core/compiler/units/units.factor index 5fcf7b3047..9f1976bec4 100755 --- a/core/compiler/units/units.factor +++ b/core/compiler/units/units.factor @@ -42,7 +42,7 @@ definition-observers global [ V{ } like ] change-at GENERIC: definitions-changed ( assoc obj -- ) : add-definition-observer ( obj -- ) - definition-observers get push ; + definition-observers get push-new ; : remove-definition-observer ( obj -- ) definition-observers get delete ; diff --git a/core/threads/threads-docs.factor b/core/threads/threads-docs.factor index fa79906cdf..d157907cc2 100755 --- a/core/threads/threads-docs.factor +++ b/core/threads/threads-docs.factor @@ -19,8 +19,7 @@ ARTICLE: "threads-yield" "Yielding and suspending threads" { $subsection yield } "Sleeping for a period of time:" { $subsection sleep } -"Interruptible sleep:" -{ $subsection nap } +"Interrupting sleep:" { $subsection interrupt } "Threads can be suspended and woken up at some point in the future when a condition is satisfied:" { $subsection suspend } @@ -106,14 +105,17 @@ HELP: stop HELP: yield { $description "Adds the current thread to the end of the run queue, and switches to the next runnable thread." } ; +HELP: sleep-until +{ $values { "time/f" "a non-negative integer or " { $link f } } } +{ $description "Suspends the current thread until the given time, or indefinitely if a value of " { $link f } " is passed in." +$nl +"Other threads may interrupt the sleep by calling " { $link interrupt } "." } ; + HELP: sleep { $values { "ms" "a non-negative integer" } } -{ $description "Suspends the current thread for " { $snippet "ms" } " milliseconds." } -{ $errors "Throws an error if another thread interrupted the sleep with " { $link interrupt } "." } ; - -HELP: nap -{ $values { "ms/f" "a non-negative integer or " { $link f } } { "?" "a boolean indicating whether the thread was interrupted" } } -{ $description "Suspends the current thread until another thread interrupts it with " { $link interrupt } ". If the input parameter is not " { $link f } ", then the thread will also wake up if the timeout expires before an interrupt is received." } ; +{ $description "Suspends the current thread for " { $snippet "ms" } " milliseconds." +$nl +"Other threads may interrupt the sleep by calling " { $link interrupt } "." } ; HELP: interrupt { $values { "thread" thread } } diff --git a/core/threads/threads.factor b/core/threads/threads.factor index 70ed44e539..490c8dc740 100755 --- a/core/threads/threads.factor +++ b/core/threads/threads.factor @@ -75,12 +75,15 @@ PRIVATE> : sleep-queue 43 getenv ; : resume ( thread -- ) + f over set-thread-state check-registered run-queue push-front ; : resume-now ( thread -- ) + f over set-thread-state check-registered run-queue push-back ; : resume-with ( obj thread -- ) + f over set-thread-state check-registered 2array run-queue push-front ; self swap call next ] callcc1 2nip ; inline -: yield ( -- ) [ resume ] "yield" suspend drop ; +: yield ( -- ) [ resume ] f suspend drop ; -GENERIC: nap-until ( time -- ? ) +GENERIC: sleep-until ( time/f -- ) -M: integer nap-until [ schedule-sleep ] curry "sleep" suspend ; +M: integer sleep-until + [ schedule-sleep ] curry "sleep" suspend drop ; -M: f nap-until drop [ drop ] "interrupt" suspend ; +M: f sleep-until + drop [ drop ] "interrupt" suspend drop ; -GENERIC: nap ( time -- ? ) +GENERIC: sleep ( ms -- ) -M: real nap millis + >integer nap-until ; - -M: f nap nap-until ; - -: sleep-until ( time -- ) - nap-until [ "Sleep interrupted" throw ] when ; - -: sleep ( time -- ) - nap [ "Sleep interrupted" throw ] when ; +M: real sleep + millis + >integer sleep-until ; : interrupt ( thread -- ) - dup self eq? [ - drop - ] [ + dup thread-state [ dup thread-sleep-entry [ sleep-queue heap-delete ] when* f over set-thread-sleep-entry - t swap resume-with - ] if ; + dup resume + ] when drop ; : (spawn) ( thread -- ) [ @@ -204,6 +200,7 @@ M: f nap nap-until ; initial-thread global [ drop f "Initial" [ die ] ] cache over set-thread-continuation + f over set-thread-state dup register-thread set-self ; diff --git a/extra/alarms/alarms.factor b/extra/alarms/alarms.factor index 92a7c488ef..7f43dbd612 100755 --- a/extra/alarms/alarms.factor +++ b/extra/alarms/alarms.factor @@ -62,7 +62,7 @@ SYMBOL: alarm-thread : alarm-thread-loop ( -- ) alarms get-global - dup next-alarm nap-until drop + dup next-alarm sleep-until dup trigger-alarms alarm-thread-loop ; diff --git a/extra/calendar/calendar.factor b/extra/calendar/calendar.factor index d1d7246a58..d834698d08 100755 --- a/extra/calendar/calendar.factor +++ b/extra/calendar/calendar.factor @@ -473,9 +473,9 @@ M: timestamp year. ( timestamp -- ) : seconds-since-midnight ( timestamp -- x ) dup beginning-of-day timestamp- ; -M: timestamp nap-until timestamp>millis nap-until ; +M: timestamp sleep-until timestamp>millis sleep-until ; -M: dt nap from-now nap-until ; +M: dt sleep from-now sleep-until ; { { [ unix? ] [ "calendar.unix" ] } diff --git a/extra/db/db.factor b/extra/db/db.factor index d88bbaee03..d5242659ae 100755 --- a/extra/db/db.factor +++ b/extra/db/db.factor @@ -1,14 +1,18 @@ ! Copyright (C) 2008 Doug Coleman. ! See http://factorcode.org/license.txt for BSD license. USING: arrays assocs classes continuations kernel math -namespaces sequences sequences.lib tuples words strings ; +namespaces sequences sequences.lib tuples words strings +tools.walker ; IN: db -TUPLE: db handle insert-statements update-statements delete-statements ; +TUPLE: db handle ; +! TUPLE: db handle insert-statements update-statements delete-statements ; : ( handle -- obj ) - H{ } clone H{ } clone H{ } clone + ! H{ } clone H{ } clone H{ } clone db construct-boa ; +GENERIC: make-db* ( seq class -- db ) +: make-db ( seq class -- db ) construct-empty make-db* ; GENERIC: db-open ( db -- ) HOOK: db-close db ( handle -- ) @@ -17,22 +21,29 @@ HOOK: db-close db ( handle -- ) : dispose-db ( db -- ) dup db [ - dup db-insert-statements dispose-statements - dup db-update-statements dispose-statements - dup db-delete-statements dispose-statements + ! dup db-insert-statements dispose-statements + ! dup db-update-statements dispose-statements + ! dup db-delete-statements dispose-statements db-handle db-close ] with-variable ; -TUPLE: statement sql params handle bound? slot-names ; +TUPLE: statement handle sql in-params out-params bind-params bound? ; +: ( sql in out -- statement ) + { + set-statement-sql + set-statement-in-params + set-statement-out-params + } statement construct ; + TUPLE: simple-statement ; TUPLE: prepared-statement ; -HOOK: db ( str -- statement ) -HOOK: db ( str -- statement ) +HOOK: db ( str in out -- statement ) +HOOK: db ( str in out -- statement ) GENERIC: prepare-statement ( statement -- ) GENERIC: bind-statement* ( obj statement -- ) GENERIC: reset-statement ( statement -- ) -GENERIC: insert-statement ( statement -- id ) +GENERIC: bind-tuple ( tuple statement -- ) TUPLE: result-set sql params handle n max ; GENERIC: query-results ( query -- result-set ) @@ -42,12 +53,17 @@ GENERIC# row-column 1 ( result-set n -- obj ) GENERIC: advance-row ( result-set -- ) GENERIC: more-rows? ( result-set -- ? ) -: execute-statement ( statement -- ) query-results dispose ; +: execute-statement ( statement -- ) + dup sequence? [ + [ execute-statement ] each + ] [ + query-results dispose + ] if ; : bind-statement ( obj statement -- ) dup statement-bound? [ dup reset-statement ] when [ bind-statement* ] 2keep - [ set-statement-params ] keep + [ set-statement-bind-params ] keep t swap set-statement-bound? ; : init-result-set ( result-set -- ) @@ -55,7 +71,7 @@ GENERIC: more-rows? ( result-set -- ? ) 0 swap set-result-set-n ; : ( query handle tuple -- result-set ) - >r >r { statement-sql statement-params } get-slots r> + >r >r { statement-sql statement-in-params } get-slots r> { set-result-set-sql set-result-set-params @@ -75,17 +91,15 @@ GENERIC: more-rows? ( result-set -- ? ) : query-map ( statement quot -- seq ) accumulator >r query-each r> { } like ; inline -: with-db ( db quot -- ) - [ - over db-open - [ db swap with-variable ] curry with-disposal - ] with-scope ; +: with-db ( db seq quot -- ) + >r make-db dup db-open db r> + [ db get swap [ drop ] swap compose with-disposal ] curry with-variable ; -: do-query ( query -- result-set ) +: default-query ( query -- result-set ) query-results [ [ sql-row ] query-map ] with-disposal ; : do-bound-query ( obj query -- rows ) - [ bind-statement ] keep do-query ; + [ bind-statement ] keep default-query ; : do-bound-command ( obj query -- ) [ bind-statement ] keep execute-statement ; @@ -105,11 +119,11 @@ HOOK: rollback-transaction db ( -- ) ] with-variable ; : sql-query ( sql -- rows ) - [ do-query ] with-disposal ; + f f [ default-query ] with-disposal ; : sql-command ( sql -- ) dup string? [ - [ execute-statement ] with-disposal + f f [ execute-statement ] with-disposal ] [ ! [ [ sql-command ] each diff --git a/extra/db/postgresql/lib/lib.factor b/extra/db/postgresql/lib/lib.factor old mode 100644 new mode 100755 index c48eff964a..25b3a6d2cf --- a/extra/db/postgresql/lib/lib.factor +++ b/extra/db/postgresql/lib/lib.factor @@ -2,21 +2,25 @@ ! See http://factorcode.org/license.txt for BSD license. USING: arrays continuations db io kernel math namespaces quotations sequences db.postgresql.ffi alien alien.c-types -db.types ; +db.types tools.walker ascii splitting ; IN: db.postgresql.lib : postgresql-result-error-message ( res -- str/f ) dup zero? [ drop f ] [ - PQresultErrorMessage [ CHAR: \n = ] right-trim + PQresultErrorMessage [ blank? ] trim ] if ; : postgres-result-error ( res -- ) postgresql-result-error-message [ throw ] when* ; +: (postgresql-error-message) ( handle -- str ) + PQerrorMessage + "\n" split [ [ blank? ] trim ] map "\n" join ; + : postgresql-error-message ( -- str ) - db get db-handle PQerrorMessage [ CHAR: \n = ] right-trim ; + db get db-handle (postgresql-error-message) ; : postgresql-error ( res -- res ) dup [ postgresql-error-message throw ] unless ; @@ -27,7 +31,7 @@ IN: db.postgresql.lib : connect-postgres ( host port pgopts pgtty db user pass -- conn ) PQsetdbLogin - dup PQstatus zero? [ postgresql-error-message throw ] unless ; + dup PQstatus zero? [ (postgresql-error-message) throw ] unless ; : do-postgresql-statement ( statement -- res ) db get db-handle swap statement-sql PQexec dup postgresql-result-ok? [ @@ -37,9 +41,9 @@ IN: db.postgresql.lib : do-postgresql-bound-statement ( statement -- res ) >r db get db-handle r> [ statement-sql ] keep - [ statement-params length f ] keep - statement-params - [ first number>string* malloc-char-string ] map >c-void*-array + [ statement-bind-params length f ] keep + statement-bind-params + [ number>string* malloc-char-string ] map >c-void*-array f f 0 PQexecParams dup postgresql-result-ok? [ dup postgresql-result-error-message swap PQclear throw diff --git a/extra/db/postgresql/postgresql-tests.factor b/extra/db/postgresql/postgresql-tests.factor old mode 100644 new mode 100755 index 36b6fc829b..7ea2bb629a --- a/extra/db/postgresql/postgresql-tests.factor +++ b/extra/db/postgresql/postgresql-tests.factor @@ -1,13 +1,14 @@ ! You will need to run 'createdb factor-test' to create the database. ! Set username and password in the 'connect' word. -USING: kernel db.postgresql alien continuations io prettyprint -sequences namespaces tools.test db db.types ; +USING: kernel db.postgresql alien continuations io classes +prettyprint sequences namespaces tools.test db +db.tuples db.types unicode.case ; IN: temporary IN: scratchpad : test-db ( -- postgresql-db ) - "localhost" "postgres" "" "factor-test" ; + { "localhost" "postgres" "" "factor-test" } postgresql-db ; IN: temporary [ ] [ test-db [ ] with-db ] unit-test @@ -39,7 +40,7 @@ IN: temporary ] [ test-db [ "select * from person where name = $1 and country = $2" - [ + f f [ { { "Jane" TEXT } { "New Zealand" TEXT } } over do-bound-query @@ -108,3 +109,248 @@ IN: temporary "select * from person" sql-query length ] with-db ] unit-test + + +: with-dummy-db ( quot -- ) + >r T{ postgresql-db } db r> with-variable ; + +! TEST TUPLE DB + +TUPLE: puppy id name age ; +: ( name age -- puppy ) + { set-puppy-name set-puppy-age } puppy construct ; + +puppy "PUPPY" { + { "id" "ID" +native-id+ +not-null+ } + { "name" "NAME" { VARCHAR 256 } } + { "age" "AGE" INTEGER } +} define-persistent + +TUPLE: kitty id name age ; +: ( name age -- kitty ) + { set-kitty-name set-kitty-age } kitty construct ; + +kitty "KITTY" { + { "id" "ID" INTEGER +assigned-id+ } + { "name" "NAME" TEXT } + { "age" "AGE" INTEGER } +} define-persistent + +TUPLE: basket id puppies kitties ; +basket "BASKET" +{ + { "id" "ID" +native-id+ +not-null+ } + { "location" "LOCATION" TEXT } + { "puppies" { +has-many+ puppy } } + { "kitties" { +has-many+ kitty } } +} define-persistent + +! Create table +[ + "create table puppy(id serial primary key not null, name varchar 256, age integer);" +] [ + T{ postgresql-db } db [ + puppy dup db-columns swap db-table create-table-sql >lower + ] with-variable +] unit-test + +[ + "create table kitty(id integer primary key, name text, age integer);" +] [ + T{ postgresql-db } db [ + kitty dup db-columns swap db-table create-table-sql >lower + ] with-variable +] unit-test + +[ + "create table basket(id serial primary key not null, location text);" +] [ + T{ postgresql-db } db [ + basket dup db-columns swap db-table create-table-sql >lower + ] with-variable +] unit-test + +! Create function +[ + "create function add_puppy(varchar,integer) returns bigint as 'insert into puppy(name, age) values($1, $2); select currval(''puppy_id_seq'');' language sql;" +] [ + T{ postgresql-db } db [ + puppy dup db-columns swap db-table create-function-sql >lower + ] with-variable +] unit-test + +! Drop table + +[ + "drop table puppy;" +] [ + T{ postgresql-db } db [ + puppy db-table drop-table-sql >lower + ] with-variable +] unit-test + +[ + "drop table kitty;" +] [ + T{ postgresql-db } db [ + kitty db-table drop-table-sql >lower + ] with-variable +] unit-test + +[ + "drop table basket;" +] [ + T{ postgresql-db } db [ + basket db-table drop-table-sql >lower + ] with-variable +] unit-test + + +! Drop function +[ + "drop function add_puppy(varchar, integer);" +] [ + T{ postgresql-db } db [ + puppy dup db-columns swap db-table drop-function-sql >lower + ] with-variable +] unit-test + +! Insert +[ +] [ + T{ postgresql-db } db [ + puppy + ] with-variable +] unit-test + +[ + "insert into kitty(id, name, age) values($1, $2, $3);" + { + T{ + sql-spec + f + "id" + "ID" + INTEGER + { +assigned-id+ } + +assigned-id+ + } + T{ sql-spec f "name" "NAME" TEXT { } f } + T{ sql-spec f "age" "AGE" INTEGER { } f } + } + { } +] [ + T{ postgresql-db } db [ + kitty + ] with-variable +] unit-test + +! Update +[ + "update puppy set name = $1, age = $2 where id = $3" + { + T{ sql-spec f "name" "NAME" { VARCHAR 256 } { } f } + T{ sql-spec f "age" "AGE" INTEGER { } f } + T{ + sql-spec + f + "id" + "ID" + +native-id+ + { +not-null+ } + +native-id+ + } + } + { } +] [ + T{ postgresql-db } db [ + puppy dup db-columns swap db-table >r >r >lower r> r> + ] with-variable +] unit-test + +[ + "update kitty set name = $1, age = $2 where id = $3" + { + T{ sql-spec f "name" "NAME" TEXT { } f } + T{ sql-spec f "age" "AGE" INTEGER { } f } + T{ + sql-spec + f + "id" + "ID" + INTEGER + { +assigned-id+ } + +assigned-id+ + } + } + { } +] [ + T{ postgresql-db } db [ + kitty dup db-columns swap db-table >r >r >lower r> r> + ] with-variable +] unit-test + +! Delete +[ + "delete from puppy where id = $1" + { + T{ + sql-spec + f + "id" + "ID" + +native-id+ + { +not-null+ } + +native-id+ + } + } + { } +] [ + T{ postgresql-db } db [ + puppy dup db-columns swap db-table >r >r >lower r> r> + ] with-variable +] unit-test + +[ + "delete from KITTY where ID = $1" + { + T{ + sql-spec + f + "id" + "ID" + INTEGER + { +assigned-id+ } + +assigned-id+ + } + } + { } +] [ + T{ postgresql-db } db [ + kitty dup db-columns swap db-table + ] with-variable +] unit-test + +! Select +[ + "select from PUPPY ID, NAME, AGE where NAME = $1;" + { T{ sql-spec f "name" "NAME" { VARCHAR 256 } { } f } } + { + T{ + sql-spec + f + "id" + "ID" + +native-id+ + { +not-null+ } + +native-id+ + } + T{ sql-spec f "name" "NAME" { VARCHAR 256 } { } f } + T{ sql-spec f "age" "AGE" INTEGER { } f } + } +] [ + T{ postgresql-db } db [ + T{ puppy f f "Mr. Clunkers" } + + ] with-variable +] unit-test diff --git a/extra/db/postgresql/postgresql.factor b/extra/db/postgresql/postgresql.factor index 03746bcaa0..154a330913 100755 --- a/extra/db/postgresql/postgresql.factor +++ b/extra/db/postgresql/postgresql.factor @@ -4,25 +4,28 @@ USING: arrays assocs alien alien.syntax continuations io kernel math math.parser namespaces prettyprint quotations sequences debugger db db.postgresql.lib db.postgresql.ffi db.tuples db.types tools.annotations math.ranges -combinators ; +combinators sequences.lib classes locals words tools.walker ; IN: db.postgresql TUPLE: postgresql-db host port pgopts pgtty db user pass ; TUPLE: postgresql-statement ; TUPLE: postgresql-result-set ; -: ( statement -- postgresql-statement ) +: ( statement in out -- postgresql-statement ) + postgresql-statement construct-delegate ; -: ( host user pass db -- obj ) - { - set-postgresql-db-host - set-postgresql-db-user - set-postgresql-db-pass - set-postgresql-db-db - } postgresql-db construct ; +M: postgresql-db make-db* ( seq tuple -- db ) + >r first4 r> [ + { + set-postgresql-db-host + set-postgresql-db-user + set-postgresql-db-pass + set-postgresql-db-db + } set-slots + ] keep ; M: postgresql-db db-open ( db -- ) - dup { + dup { postgresql-db-host postgresql-db-port postgresql-db-pgopts @@ -35,15 +38,18 @@ M: postgresql-db db-open ( db -- ) M: postgresql-db dispose ( db -- ) db-handle PQfinish ; -: with-postgresql ( host ust pass db quot -- ) - >r r> with-disposal ; - M: postgresql-statement bind-statement* ( seq statement -- ) - set-statement-params ; + set-statement-bind-params ; M: postgresql-statement reset-statement ( statement -- ) drop ; +M: postgresql-statement bind-tuple ( tuple statement -- ) + [ + statement-in-params + [ sql-spec-slot-name swap get-slot-named ] with map + ] keep set-statement-bind-params ; + M: postgresql-result-set #rows ( result-set -- n ) result-set-handle PQntuples ; @@ -56,19 +62,8 @@ M: postgresql-result-set row-column ( result-set n -- obj ) M: postgresql-result-set row-column-typed ( result-set n type -- obj ) >r row-column r> sql-type>factor-type ; -M: postgresql-result-set sql-type>factor-type ( obj type -- newobj ) - { - { INTEGER [ string>number ] } - { BIG_INTEGER [ string>number ] } - { DOUBLE [ string>number ] } - [ drop ] - } case ; - -M: postgresql-statement insert-statement ( statement -- id ) - query-results [ 0 row-column ] with-disposal string>number ; - M: postgresql-statement query-results ( query -- result-set ) - dup statement-params [ + dup statement-bind-params [ over [ bind-statement ] keep do-postgresql-bound-statement ] [ @@ -96,17 +91,15 @@ M: postgresql-result-set dispose ( result-set -- ) M: postgresql-statement prepare-statement ( statement -- ) [ >r db get db-handle "" r> - dup statement-sql swap statement-params + dup statement-sql swap statement-in-params length f PQprepare postgresql-error ] keep set-statement-handle ; -M: postgresql-db ( sql -- statement ) - { set-statement-sql } statement construct +M: postgresql-db ( sql in out -- statement ) ; -M: postgresql-db ( sql -- statement ) - { set-statement-sql } statement construct - ; +M: postgresql-db ( sql in out -- statement ) + dup prepare-statement ; M: postgresql-db begin-transaction ( -- ) "BEGIN" sql-command ; @@ -117,139 +110,176 @@ M: postgresql-db commit-transaction ( -- ) M: postgresql-db rollback-transaction ( -- ) "ROLLBACK" sql-command ; -: postgresql-type-hash* ( -- assoc ) - H{ - { SERIAL "serial" } - } ; +SYMBOL: postgresql-counter +: bind-name% ( -- ) + CHAR: $ 0, + postgresql-counter [ inc ] keep get 0# ; -: postgresql-type-hash ( -- assoc ) +M: postgresql-db bind% ( spec -- ) + 1, bind-name% ; + +: postgresql-make ( class quot -- ) + >r sql-props r> + [ postgresql-counter off ] swap compose + { "" { } { } } nmake ; + +: create-table-sql ( class -- statement ) + [ + "create table " 0% 0% + "(" 0% + [ ", " 0% ] [ + dup sql-spec-column-name 0% + " " 0% + dup sql-spec-type t lookup-type 0% + modifiers 0% + ] interleave ");" 0% + ] postgresql-make ; + +: create-function-sql ( class -- statement ) + [ + >r remove-id r> + "create function add_" 0% dup 0% + "(" 0% + over [ "," 0% ] + [ + sql-spec-type f lookup-type 0% + ] interleave + ")" 0% + " returns bigint as '" 0% + + "insert into " 0% + dup 0% + "(" 0% + over [ ", " 0% ] [ sql-spec-column-name 0% ] interleave + ") values(" 0% + swap [ ", " 0% ] [ drop bind-name% ] interleave + "); " 0% + "select currval(''" 0% 0% "_id_seq'');' language sql;" 0% + ] postgresql-make ; + +M: postgresql-db create-sql-statement ( class -- seq ) + [ + [ create-table-sql , ] keep + dup db-columns find-primary-key native-id? + [ create-function-sql , ] [ drop ] if + ] { } make ; + +: drop-function-sql ( class -- statement ) + [ + "drop function add_" 0% 0% + "(" 0% + remove-id + [ ", " 0% ] [ sql-spec-type f lookup-type 0% ] interleave + ");" 0% + ] postgresql-make ; + +: drop-table-sql ( table -- statement ) + [ + "drop table " 0% 0% ";" 0% drop + ] postgresql-make ; + +M: postgresql-db drop-sql-statement ( class -- seq ) + [ + [ drop-table-sql , ] keep + dup db-columns find-primary-key native-id? + [ drop-function-sql , ] [ drop ] if + ] { } make ; + +M: postgresql-db ( class -- statement ) + [ + "select add_" 0% 0% + "(" 0% + dup find-primary-key 2, + remove-id + [ ", " 0% ] [ bind% ] interleave + ");" 0% + ] postgresql-make ; + +M: postgresql-db ( class -- statement ) + [ + "insert into " 0% 0% + "(" 0% + dup [ ", " 0% ] [ sql-spec-column-name 0% ] interleave + ")" 0% + + " values(" 0% + [ ", " 0% ] [ bind% ] interleave + ");" 0% + ] postgresql-make ; + +M: postgresql-db insert-tuple* ( tuple statement -- ) + query-modify-tuple ; + +M: postgresql-db ( class -- statement ) + [ + "update " 0% 0% + " set " 0% + dup remove-id + [ ", " 0% ] + [ dup sql-spec-column-name 0% " = " 0% bind% ] interleave + " where " 0% + find-primary-key + dup sql-spec-column-name 0% " = " 0% bind% + ] postgresql-make ; + +M: postgresql-db ( class -- statement ) + [ + "delete from " 0% 0% + " where " 0% + find-primary-key + dup sql-spec-column-name 0% " = " 0% bind% + ] postgresql-make ; + +M: postgresql-db ( tuple class -- statement ) + [ + ! tuple columns table + "select " 0% + over [ ", " 0% ] + [ dup sql-spec-column-name 0% 2, ] interleave + + " from " 0% 0% + [ sql-spec-slot-name swap get-slot-named ] with subset + " where " 0% + [ ", " 0% ] + [ dup sql-spec-column-name 0% " = " 0% bind% ] interleave + ";" 0% + ] postgresql-make ; + +M: postgresql-db type-table ( -- hash ) H{ - { INTEGER "integer" } - { SERIAL "integer" } + { +native-id+ "integer" } { TEXT "text" } { VARCHAR "varchar" } + { INTEGER "integer" } { DOUBLE "real" } + { TIMESTAMP "timestamp" } } ; -: enquote ( str -- newstr ) "(" swap ")" 3append ; - -: postgresql-type ( str n/str -- newstr ) - " " swap number>string* enquote 3append ; - -: >sql-type* ( obj -- str ) - dup pair? [ - first2 >r >sql-type* r> postgresql-type - ] [ - dup postgresql-type-hash* at* [ - nip - ] [ - drop >sql-type - ] if - ] if ; - -M: postgresql-db >sql-type ( hash obj -- str ) - dup pair? [ - first2 >r >sql-type r> postgresql-type - ] [ - postgresql-type-hash at* [ - no-sql-type - ] unless - ] if ; - -: insert-function ( columns table -- sql ) - [ - >r remove-id r> - "create function add_" % dup % - "(" % - over [ "," % ] - [ third dup array? [ first ] when >sql-type % ] interleave - ")" % - " returns bigint as '" % - - 2dup "insert into " % - % - "(" % - dup [ ", " % ] [ second % ] interleave - ") " % - " values (" % - length [1,b] [ ", " % ] [ "$" % # ] interleave - "); " % - - "select currval(''" % % "_id_seq'');' language sql;" % - drop - ] "" make ; - -: drop-function ( columns table -- sql ) - [ - >r remove-id r> - "drop function add_" % % - "(" % - [ "," % ] [ third >sql-type % ] interleave - ")" % - ] "" make ; - -M: postgresql-db create-sql ( columns table -- seq ) - [ - [ - 2dup - "create table " % % - " (" % [ ", " % ] [ - dup second % " " % - dup third >sql-type* % " " % - sql-modifiers " " join % - ] interleave "); " % - ] "" make , - - over native-id? [ insert-function , ] [ 2drop ] if - ] { } make ; - -M: postgresql-db drop-sql ( columns table -- seq ) - [ - [ - dup "drop table " % % ";" % - ] "" make , - over native-id? [ drop-function , ] [ 2drop ] if - ] { } make ; - -M: postgresql-db insert-sql* ( columns table -- slot-names sql ) - [ - "select add_" % % - "(" % - length [1,b] [ ", " % ] [ "$" % # ] interleave - ")" % - ] "" make ; - -M: postgresql-db update-sql* ( columns table -- slot-names sql ) - [ - "update " % - % - " set " % - dup remove-id - dup length [1,b] swap 2array flip - [ ", " % ] [ first2 second % " = $" % # ] interleave - " where " % - [ primary-key? ] find nip second dup % " = $" % length 2 + # - ] "" make ; - -M: postgresql-db delete-sql* ( columns table -- slot-names sql ) - [ - "delete from " % - % - " where " % - first second % " = $1" % - ] "" make ; - -M: postgresql-db select-sql ( columns table -- slot-names sql ) - drop ; - -M: postgresql-db tuple>params ( columns tuple -- obj ) - [ >r dup third swap first r> get-slot-named swap ] - curry { } map>assoc ; - -: postgresql-db-modifiers ( -- hashtable ) +M: postgresql-db create-type-table ( -- hash ) H{ - { +native-id+ "not null primary key" } + { +native-id+ "serial primary key" } + } ; + +: postgresql-compound ( str n -- newstr ) + over { + { "default" [ first number>string join-space ] } + { "varchar" [ first number>string paren append ] } + { "references" [ + first2 >r [ unparse join-space ] keep db-columns r> + swap [ sql-spec-slot-name = ] with find nip + sql-spec-column-name paren append + ] } + [ "no compound found" 3array throw ] + } case ; + +M: postgresql-db compound-modifier ( str seq -- newstr ) + postgresql-compound ; + +M: postgresql-db modifier-table ( -- hashtable ) + H{ + { +native-id+ "primary key" } { +assigned-id+ "primary key" } + { +foreign-id+ "references" } { +autoincrement+ "autoincrement" } { +unique+ "unique" } { +default+ "default" } @@ -257,13 +287,5 @@ M: postgresql-db tuple>params ( columns tuple -- obj ) { +not-null+ "not null" } } ; -M: postgresql-db sql-modifiers* ( modifiers -- str ) - postgresql-db-modifiers swap [ - dup array? [ - first2 - >r swap at r> number>string* - " " swap 3append - ] [ - swap at - ] if - ] with map [ ] subset ; +M: postgresql-db compound-type ( str n -- newstr ) + postgresql-compound ; diff --git a/extra/db/sqlite/lib/lib.factor b/extra/db/sqlite/lib/lib.factor index 85aa671d4d..648d8493dc 100755 --- a/extra/db/sqlite/lib/lib.factor +++ b/extra/db/sqlite/lib/lib.factor @@ -78,7 +78,8 @@ IN: db.sqlite.lib { TEXT [ sqlite-bind-text-by-name ] } { VARCHAR [ sqlite-bind-text-by-name ] } { DOUBLE [ sqlite-bind-double-by-name ] } - { SERIAL [ sqlite-bind-int-by-name ] } + { TIMESTAMP [ sqlite-bind-double-by-name ] } + { +native-id+ [ sqlite-bind-int-by-name ] } ! { NULL [ sqlite-bind-null-by-name ] } [ no-sql-type ] } case ; @@ -102,6 +103,8 @@ IN: db.sqlite.lib { BIG_INTEGER [ sqlite3_column_int64 ] } { TEXT [ sqlite3_column_text ] } { DOUBLE [ sqlite3_column_double ] } + { TIMESTAMP [ sqlite3_column_double ] } + [ no-sql-type ] } case ; ! TODO diff --git a/extra/db/sqlite/sqlite-tests.factor b/extra/db/sqlite/sqlite-tests.factor old mode 100644 new mode 100755 index d3388b4648..6c4b65ff9f --- a/extra/db/sqlite/sqlite-tests.factor +++ b/extra/db/sqlite/sqlite-tests.factor @@ -1,6 +1,6 @@ USING: io io.files io.launcher kernel namespaces prettyprint tools.test db.sqlite db sequences -continuations db.types ; +continuations db.types db.tuples unicode.case ; IN: temporary : test.db "extra/db/sqlite/test.db" resource-path ; @@ -89,3 +89,158 @@ IN: temporary "select * from person" sql-query length ] with-sqlite ] unit-test + +! TEST TUPLE DB + +TUPLE: puppy id name age ; +: ( name age -- puppy ) + { set-puppy-name set-puppy-age } puppy construct ; + +puppy "PUPPY" { + { "id" "ID" +native-id+ +not-null+ } + { "name" "NAME" { VARCHAR 256 } } + { "age" "AGE" INTEGER } +} define-persistent + +TUPLE: kitty id name age ; +: ( name age -- kitty ) + { set-kitty-name set-kitty-age } kitty construct ; + +kitty "KITTY" { + { "id" "ID" INTEGER +assigned-id+ } + { "name" "NAME" TEXT } + { "age" "AGE" INTEGER } +} define-persistent + +TUPLE: basket id puppies kitties ; +basket "BASKET" +{ + { "id" "ID" +native-id+ +not-null+ } + { "location" "LOCATION" TEXT } + { "puppies" { +has-many+ puppy } } + { "kitties" { +has-many+ kitty } } +} define-persistent + +! Create table +[ + "create table puppy(id integer primary key not null, name varchar, age integer);" +] [ + T{ sqlite-db } db [ + puppy dup db-columns swap db-table create-sql >lower + ] with-variable +] unit-test + +[ + "create table kitty(id integer primary key, name text, age integer);" +] [ + T{ sqlite-db } db [ + kitty dup db-columns swap db-table create-sql >lower + ] with-variable +] unit-test + +[ + "create table basket(id integer primary key not null, location text);" +] [ + T{ sqlite-db } db [ + basket dup db-columns swap db-table create-sql >lower + ] with-variable +] unit-test + +! Drop table +[ + "drop table puppy;" +] [ + T{ sqlite-db } db [ + puppy db-table drop-sql >lower + ] with-variable +] unit-test + +[ + "drop table kitty;" +] [ + T{ sqlite-db } db [ + kitty db-table drop-sql >lower + ] with-variable +] unit-test + +[ + "drop table basket;" +] [ + T{ sqlite-db } db [ + basket db-table drop-sql >lower + ] with-variable +] unit-test + +! Insert +[ + "insert into puppy(name, age) values(:name, :age);" +] [ + T{ sqlite-db } db [ + puppy dup db-columns swap db-table insert-sql* >lower + ] with-variable +] unit-test + +[ + "insert into kitty(id, name, age) values(:id, :name, :age);" +] [ + T{ sqlite-db } db [ + kitty dup db-columns swap db-table insert-sql* >lower + ] with-variable +] unit-test + +! Update +[ + "update puppy set name = :name, age = :age where id = :id" +] [ + T{ sqlite-db } db [ + puppy dup db-columns swap db-table update-sql* >lower + ] with-variable +] unit-test + +[ + "update kitty set name = :name, age = :age where id = :id" +] [ + T{ sqlite-db } db [ + kitty dup db-columns swap db-table update-sql* >lower + ] with-variable +] unit-test + +! Delete +[ + "delete from puppy where id = :id" +] [ + T{ sqlite-db } db [ + puppy dup db-columns swap db-table delete-sql* >lower + ] with-variable +] unit-test + +[ + "delete from kitty where id = :id" +] [ + T{ sqlite-db } db [ + kitty dup db-columns swap db-table delete-sql* >lower + ] with-variable +] unit-test + +! Select +[ + "select from puppy id, name, age where name = :name;" + { + T{ + sql-spec + f + "id" + "ID" + +native-id+ + { +not-null+ } + +native-id+ + } + T{ sql-spec f "name" "NAME" { VARCHAR 256 } { } f } + T{ sql-spec f "age" "AGE" INTEGER { } f } + } +] [ + T{ sqlite-db } db [ + T{ puppy f f "Mr. Clunkers" } + select-sql >r >lower r> + ] with-variable +] unit-test diff --git a/extra/db/sqlite/sqlite.factor b/extra/db/sqlite/sqlite.factor index 4eabfc2ecd..b8e8bca300 100755 --- a/extra/db/sqlite/sqlite.factor +++ b/extra/db/sqlite/sqlite.factor @@ -4,7 +4,7 @@ USING: alien arrays assocs classes compiler db hashtables io.files kernel math math.parser namespaces prettyprint sequences strings tuples alien.c-types continuations db.sqlite.lib db.sqlite.ffi db.tuples -words combinators.lib db.types ; +words combinators.lib db.types combinators tools.walker ; IN: db.sqlite TUPLE: sqlite-db path ; @@ -23,7 +23,6 @@ M: sqlite-db dispose ( db -- ) dispose-db ; >r r> with-db ; inline TUPLE: sqlite-statement ; -C: sqlite-statement TUPLE: sqlite-result-set has-more? ; @@ -31,9 +30,15 @@ M: sqlite-db ( str -- obj ) ; M: sqlite-db ( str -- obj ) - db get db-handle over sqlite-prepare - { set-statement-sql set-statement-handle } statement construct - [ set-delegate ] keep ; + db get db-handle + { + set-statement-sql + set-statement-in-params + set-statement-out-params + set-statement-handle + } statement construct + dup statement-handle over statement-sql sqlite-prepare + sqlite-statement construct-delegate ; M: sqlite-statement dispose ( statement -- ) statement-handle sqlite-finalize ; @@ -41,10 +46,11 @@ M: sqlite-statement dispose ( statement -- ) M: sqlite-result-set dispose ( result-set -- ) f swap set-result-set-handle ; -: sqlite-bind ( triples handle -- ) - swap [ first3 sqlite-bind-type ] with each ; +: sqlite-bind ( specs handle -- ) +break + swap [ sqlite-bind-type ] with each ; -M: sqlite-statement bind-statement* ( triples statement -- ) +M: sqlite-statement bind-statement* ( obj statement -- ) statement-handle sqlite-bind ; M: sqlite-statement reset-statement ( statement -- ) @@ -54,8 +60,8 @@ M: sqlite-statement reset-statement ( statement -- ) db get db-handle sqlite3_last_insert_rowid dup zero? [ "last-id failed" throw ] when ; -M: sqlite-statement insert-statement ( statement -- id ) - execute-statement last-insert-id ; +M: sqlite-statement insert-tuple* ( tuple statement -- ) + execute-statement last-insert-id swap set-primary-key ; M: sqlite-result-set #columns ( result-set -- n ) result-set-handle sqlite-#columns ; @@ -74,6 +80,7 @@ M: sqlite-result-set more-rows? ( result-set -- ? ) sqlite-result-set-has-more? ; M: sqlite-statement query-results ( query -- result-set ) +break dup statement-handle sqlite-result-set dup advance-row ; @@ -86,78 +93,85 @@ M: sqlite-db commit-transaction ( -- ) M: sqlite-db rollback-transaction ( -- ) "ROLLBACK" sql-command ; -M: sqlite-db create-sql ( columns table -- sql ) - [ - "create table " % % - " (" % [ ", " % ] [ - dup second % " " % - dup third >sql-type % " " % - sql-modifiers " " join % - ] interleave ")" % - ] "" make ; +: sqlite-make ( class quot -- ) + >r sql-props r> + { "" { } { } } nmake ; -M: sqlite-db drop-sql ( columns table -- sql ) +M: sqlite-db create-sql-statement ( class -- statement ) [ - "drop table " % % - drop - ] "" make ; + "create table " 0% 0% + "(" 0% [ ", " 0% ] [ + dup sql-spec-column-name 0% + " " 0% + dup sql-spec-type t lookup-type 0% + modifiers 0% + ] interleave ");" 0% + ] sqlite-make ; -M: sqlite-db insert-sql* ( columns table -- sql ) +M: sqlite-db drop-sql-statement ( class -- statement ) [ - "insert into " % - % - "(" % - dup [ ", " % ] [ second % ] interleave - ") " % - " values (" % - [ ", " % ] [ ":" % second % ] interleave - ")" % - ] "" make ; + "drop table " 0% 0% ";" 0% drop + ] sqlite-make ; -: where-primary-key% ( columns -- ) - " where " % - [ primary-key? ] find nip second dup % " = :" % % ; - -M: sqlite-db update-sql* ( columns table -- sql ) +M: sqlite-db ( tuple -- statement ) [ - "update " % - % - " set " % + "insert into " 0% 0% + "(" 0% + maybe-remove-id + dup [ ", " 0% ] [ sql-spec-column-name 0% ] interleave + ") values(" 0% + [ ", " 0% ] [ bind% ] interleave + ");" 0% + ] sqlite-make ; + +M: sqlite-db ( tuple -- statement ) + ; + +: where-primary-key% ( specs -- ) + " where " 0% + find-primary-key sql-spec-column-name dup 0% " = " 0% bind% ; + +M: sqlite-db ( class -- statement ) + [ + "update " 0% + 0% + " set " 0% dup remove-id - [ ", " % ] [ second dup % " = :" % % ] interleave + [ ", " 0% ] [ sql-spec-column-name dup 0% " = " 0% bind% ] interleave where-primary-key% - ] "" make ; + ] sqlite-make ; -M: sqlite-db delete-sql* ( columns table -- sql ) +M: sqlite-db ( specs table -- sql ) [ - "delete from " % - % - " where " % - first second dup % " = :" % % - ] "" make ; + "delete from " 0% 0% + " where " 0% + find-primary-key + sql-spec-column-name dup 0% " = " 0% bind% + ] sqlite-make ; -: select-interval ( interval name -- ) - ; +! : select-interval ( interval name -- ) ; +! : select-sequence ( seq name -- ) ; -: select-sequence ( seq name -- ) - ; +M: sqlite-db bind% ( spec -- ) + dup 1, sql-spec-column-name ":" swap append 0% ; + ! dup 1, sql-spec-column-name + ! dup 0% " = " 0% ":" swap append 0% ; -M: sqlite-db select-sql ( columns table -- sql ) +M: sqlite-db ( tuple class -- statement ) [ - "select ROWID, " % - over [ ", " % ] [ second % ] interleave - " from " % % - " where " % - ] "" make ; + "select " 0% + over [ ", " 0% ] + [ dup sql-spec-column-name 0% 2, ] interleave -M: sqlite-db tuple>params ( columns tuple -- obj ) - [ - >r [ second ":" swap append ] keep r> - dupd >r first r> get-slot-named swap - third 3array - ] curry map ; + " from " 0% 0% + [ sql-spec-slot-name swap get-slot-named ] with subset + " where " 0% + [ ", " 0% ] + [ dup sql-spec-column-name 0% " = " 0% bind% ] interleave + ";" 0% + ] sqlite-make ; -: sqlite-db-modifiers ( -- hashtable ) +M: sqlite-db modifier-table ( -- hashtable ) H{ { +native-id+ "primary key" } { +assigned-id+ "primary key" } @@ -168,32 +182,27 @@ M: sqlite-db tuple>params ( columns tuple -- obj ) { +not-null+ "not null" } } ; -M: sqlite-db sql-modifiers* ( modifiers -- str ) - sqlite-db-modifiers swap [ - dup array? [ - first2 - >r swap at r> number>string* - " " swap 3append - ] [ - swap at - ] if - ] with map [ ] subset ; +M: sqlite-db compound-modifier ( str obj -- newstr ) + compound-type ; -: sqlite-type-hash ( -- assoc ) +M: sqlite-db compound-type ( str seq -- newstr ) + over { + { "default" [ first number>string join-space ] } + [ 2drop ] ! "no sqlite compound data type" 3array throw ] + } case ; + +M: sqlite-db type-table ( -- assoc ) H{ + { +native-id+ "integer primary key" } { INTEGER "integer" } - { SERIAL "integer" } { TEXT "text" } { VARCHAR "text" } + { TIMESTAMP "timestamp" } { DOUBLE "real" } } ; -M: sqlite-db >sql-type ( obj -- str ) - dup pair? [ - first >sql-type - ] [ - sqlite-type-hash at* [ T{ no-sql-type } throw ] unless - ] if ; +M: sqlite-db create-type-table + type-table ; ! HOOK: get-column-value ( n result-set type -- ) ! M: sqlite get-column-value { { "TEXT" get-text-column } { diff --git a/extra/db/tuples/tuples-tests.factor b/extra/db/tuples/tuples-tests.factor index ea57193750..6a0d0378b2 100755 --- a/extra/db/tuples/tuples-tests.factor +++ b/extra/db/tuples/tuples-tests.factor @@ -1,70 +1,118 @@ ! Copyright (C) 2008 Doug Coleman. ! See http://factorcode.org/license.txt for BSD license. -USING: io.files kernel tools.test db db.sqlite db.tuples -db.types continuations namespaces db.postgresql math ; -! tools.time ; +USING: io.files kernel tools.test db db.tuples +db.types continuations namespaces db.postgresql math +prettyprint tools.walker db.sqlite ; IN: temporary -TUPLE: person the-id the-name the-number real ; +TUPLE: person the-id the-name the-number the-real ; : ( name age real -- person ) { set-person-the-name set-person-the-number - set-person-real + set-person-the-real } person construct ; -: ( id name number real -- obj ) +: ( id name number the-real -- obj ) [ set-person-the-id ] keep ; -SYMBOL: the-person +SYMBOL: the-person1 +SYMBOL: the-person2 : test-tuples ( -- ) [ person drop-table ] [ drop ] recover [ ] [ person create-table ] unit-test - [ ] [ the-person get insert-tuple ] unit-test + [ ] [ the-person1 get insert-tuple ] unit-test - [ 1 ] [ the-person get person-the-id ] unit-test + [ 1 ] [ the-person1 get person-the-id ] unit-test - 200 the-person get set-person-the-number + 200 the-person1 get set-person-the-number - [ ] [ the-person get update-tuple ] unit-test + [ ] [ the-person1 get update-tuple ] unit-test - [ ] [ the-person get delete-tuple ] unit-test - ; ! 1 [ ] [ person drop-table ] unit-test ; + [ T{ person f 1 "billy" 200 3.14 } ] + [ T{ person f 1 } select-tuple ] unit-test + [ ] [ the-person2 get insert-tuple ] unit-test + [ + { + T{ person f 1 "billy" 200 3.14 } + T{ person f 2 "johnny" 10 3.14 } + } + ] [ T{ person f f f f 3.14 } select-tuples ] unit-test + + [ ] [ the-person1 get delete-tuple ] unit-test + [ f ] [ T{ person f 1 } select-tuple ] unit-test + [ ] [ person drop-table ] unit-test ; : test-sqlite ( -- ) - "tuples-test.db" resource-path [ + "tuples-test.db" resource-path sqlite-db [ test-tuples ] with-db ; : test-postgresql ( -- ) - "localhost" "postgres" "" "factor-test" [ + { "localhost" "postgres" "" "factor-test" } postgresql-db [ test-tuples ] with-db ; person "PERSON" { - { "the-id" "ID" SERIAL +native-id+ } + { "the-id" "ID" +native-id+ } { "the-name" "NAME" { VARCHAR 256 } +not-null+ } { "the-number" "AGE" INTEGER { +default+ 0 } } - { "real" "REAL" DOUBLE { +default+ 0.3 } } + { "the-real" "REAL" DOUBLE { +default+ 0.3 } } } define-persistent -"billy" 10 3.14 the-person set +"billy" 10 3.14 the-person1 set +"johnny" 10 3.14 the-person2 set ! test-sqlite - test-postgresql +test-postgresql -! person "PERSON" -! { - ! { "the-id" "ID" INTEGER +assigned-id+ } - ! { "the-name" "NAME" { VARCHAR 256 } +not-null+ } - ! { "the-number" "AGE" INTEGER { +default+ 0 } } - ! { "real" "REAL" DOUBLE { +default+ 0.3 } } -! } define-persistent +person "PERSON" +{ + { "the-id" "ID" INTEGER +assigned-id+ } + { "the-name" "NAME" { VARCHAR 256 } +not-null+ } + { "the-number" "AGE" INTEGER { +default+ 0 } } + { "the-real" "REAL" DOUBLE { +default+ 0.3 } } +} define-persistent -! 1 "billy" 20 6.28 the-person set +1 "billy" 10 3.14 the-person1 set +2 "johnny" 10 3.14 the-person2 set ! test-sqlite -! test-postgresql +test-postgresql + +TUPLE: paste n summary author channel mode contents timestamp annotations ; +TUPLE: annotation n paste-id summary author mode contents ; + +paste "PASTE" +{ + { "n" "ID" +native-id+ } + { "summary" "SUMMARY" TEXT } + { "author" "AUTHOR" TEXT } + { "channel" "CHANNEL" TEXT } + { "mode" "MODE" TEXT } + { "contents" "CONTENTS" TEXT } + { "date" "DATE" TIMESTAMP } + { "annotations" { +has-many+ annotation } } +} define-persistent + +annotation "ANNOTATION" +{ + { "n" "ID" +native-id+ } + { "paste-id" "PASTE_ID" INTEGER { +foreign-id+ paste "n" } } + { "summary" "SUMMARY" TEXT } + { "author" "AUTHOR" TEXT } + { "mode" "MODE" TEXT } + { "contents" "CONTENTS" TEXT } +} define-persistent + +{ "localhost" "postgres" "" "factor-test" } postgresql-db [ + [ paste drop-table ] [ drop ] recover + [ annotation drop-table ] [ drop ] recover + [ paste drop-table ] [ drop ] recover + [ annotation drop-table ] [ drop ] recover + [ ] [ paste create-table ] unit-test + [ ] [ annotation create-table ] unit-test +] with-db diff --git a/extra/db/tuples/tuples.factor b/extra/db/tuples/tuples.factor index 20cdd8a386..4e8b8ec9d0 100755 --- a/extra/db/tuples/tuples.factor +++ b/extra/db/tuples/tuples.factor @@ -1,115 +1,100 @@ ! Copyright (C) 2008 Doug Coleman. ! See http://factorcode.org/license.txt for BSD license. USING: arrays assocs classes db kernel namespaces -tuples words sequences slots slots.private math -math.parser io prettyprint db.types continuations ; +tuples words sequences slots math +math.parser io prettyprint db.types continuations +mirrors sequences.lib tools.walker combinators.lib ; IN: db.tuples -: db-columns ( class -- obj ) "db-columns" word-prop ; +: define-persistent ( class table columns -- ) + >r dupd "db-table" set-word-prop dup r> + [ relation? ] partition swapd + dupd [ spec>tuple ] with map + "db-columns" set-word-prop + "db-relations" set-word-prop ; + : db-table ( class -- obj ) "db-table" word-prop ; +: db-columns ( class -- obj ) "db-columns" word-prop ; +: db-relations ( class -- obj ) "db-relations" word-prop ; -TUPLE: no-slot-named ; -: no-slot-named ( -- * ) T{ no-slot-named } throw ; +: set-primary-key ( key tuple -- ) + [ + class db-columns find-primary-key sql-spec-slot-name + ] keep set-slot-named ; -: slot-spec-named ( str class -- slot-spec ) - "slots" word-prop [ slot-spec-name = ] with find nip - [ no-slot-named ] unless* ; +! returns a sequence of prepared-statements +HOOK: create-sql-statement db ( class -- obj ) +HOOK: drop-sql-statement db ( class -- obj ) -: offset-of-slot ( str obj -- n ) - class slot-spec-named slot-spec-offset ; +HOOK: db ( tuple -- obj ) +HOOK: db ( tuple -- obj ) -: get-slot-named ( str obj -- value ) - tuck offset-of-slot [ no-slot-named ] unless* slot ; +HOOK: db ( tuple -- obj ) +HOOK: db ( tuple -- obj ) -: set-slot-named ( value str obj -- ) - tuck offset-of-slot [ no-slot-named ] unless* set-slot ; +HOOK: db ( tuple -- obj ) +HOOK: db ( tuple -- obj ) -: primary-key-spec ( class -- spec ) - db-columns [ primary-key? ] find nip ; - -: primary-key ( tuple -- obj ) - dup class primary-key-spec get-slot-named ; - -: set-primary-key ( obj tuple -- ) - [ class primary-key-spec first ] keep - set-slot-named ; - -: cache-statement ( columns class assoc quot -- statement ) - [ db-table dupd ] swap - [ ] 3compose cache nip ; inline - -HOOK: create-sql db ( columns table -- seq ) -HOOK: drop-sql db ( columns table -- seq ) - -HOOK: insert-sql* db ( columns table -- slot-names sql ) -HOOK: update-sql* db ( columns table -- slot-names sql ) -HOOK: delete-sql* db ( columns table -- slot-names sql ) -HOOK: select-sql db ( tuple -- statement ) +HOOK: db ( tuple -- tuple ) HOOK: row-column-typed db ( result-set n type -- sql ) -HOOK: sql-type>factor-type db ( obj type -- obj ) -HOOK: tuple>params db ( columns tuple -- obj ) +HOOK: insert-tuple* db ( tuple statement -- ) +: resulting-tuple ( row out-params -- tuple ) + dup first sql-spec-class construct-empty [ + [ + >r [ sql-spec-type sql-type>factor-type ] keep + sql-spec-slot-name r> set-slot-named + ] curry 2each + ] keep ; -HOOK: make-slot-names* db ( quot -- seq ) -HOOK: column-slot-name% db ( spec -- ) -HOOK: column-bind-name% db ( spec -- ) +: query-tuples ( statement -- seq ) + [ statement-out-params ] keep query-results [ + [ sql-row swap resulting-tuple ] with query-map + ] with-disposal ; + +: query-modify-tuple ( tuple statement -- ) + [ query-results [ sql-row ] with-disposal ] keep + statement-out-params rot [ + >r [ sql-spec-type sql-type>factor-type ] keep + sql-spec-slot-name r> set-slot-named + ] curry 2each ; -: make-slots-names ( quot -- seq str ) - [ make-slot-names* ] "" make ; inline -: slot-name% ( seq -- ) first % ; -: column-name% ( seq -- ) second % ; -: column-type% ( seq -- ) third % ; +: sql-props ( class -- columns table ) + dup db-columns swap db-table ; -: insert-sql ( columns class -- statement ) - db get db-insert-statements [ insert-sql* ] cache-statement ; +: create-table ( class -- ) create-sql-statement execute-statement ; +: drop-table ( class -- ) drop-sql-statement execute-statement ; -: update-sql ( columns class -- statement ) - db get db-update-statements [ update-sql* ] cache-statement ; +: insert-native ( tuple -- ) + dup class + [ bind-tuple ] 2keep insert-tuple* ; -: delete-sql ( columns class -- statement ) - db get db-delete-statements [ delete-sql* ] cache-statement ; - - -: tuple-statement ( columns tuple quot -- statement ) - >r [ tuple>params ] 2keep class r> call - 2dup . . - [ bind-statement ] keep ; - -: make-tuple-statement ( tuple columns-quot statement-quot -- statement ) - >r [ class db-columns ] swap compose keep - r> tuple-statement ; - -: do-tuple-statement ( tuple columns-quot statement-quot -- ) - make-tuple-statement execute-statement ; - -: create-table ( class -- ) - dup db-columns swap db-table create-sql sql-command ; - -: drop-table ( class -- ) - dup db-columns swap db-table drop-sql sql-command ; +: insert-assigned ( tuple -- ) + dup class + [ bind-tuple ] keep execute-statement ; : insert-tuple ( tuple -- ) - [ - [ maybe-remove-id ] [ insert-sql ] - make-tuple-statement insert-statement - ] keep set-primary-key ; + dup class db-columns find-primary-key assigned-id? [ + insert-assigned + ] [ + insert-native + ] if ; : update-tuple ( tuple -- ) - [ ] [ update-sql ] do-tuple-statement ; + dup class + [ bind-tuple ] keep execute-statement ; + +: update-tuples ( seq -- ) + execute-statement ; : delete-tuple ( tuple -- ) - [ [ primary-key? ] subset ] [ delete-sql ] do-tuple-statement ; + dup class + [ bind-tuple ] keep execute-statement ; -: select-tuple ( tuple -- ) - [ select-sql ] keep do-query ; +: setup-select ( tuple -- statement ) + dup dup class + [ bind-tuple ] keep ; -: persist ( tuple -- ) - dup primary-key [ update-tuple ] [ insert-tuple ] if ; - -: define-persistent ( class table columns -- ) - >r dupd "db-table" set-word-prop r> - "db-columns" set-word-prop ; - -: define-relation ( spec -- ) - drop ; +: select-tuples ( tuple -- tuple ) setup-select query-tuples ; +: select-tuple ( tuple -- tuple/f ) select-tuples ?first ; diff --git a/extra/db/types/types.factor b/extra/db/types/types.factor index 7cacbcf861..c84b23c50f 100755 --- a/extra/db/types/types.factor +++ b/extra/db/types/types.factor @@ -1,21 +1,50 @@ ! Copyright (C) 2008 Doug Coleman. ! See http://factorcode.org/license.txt for BSD license. USING: arrays assocs db kernel math math.parser -sequences continuations ; +sequences continuations sequences.deep sequences.lib +words namespaces tools.walker slots slots.private classes +mirrors tuples combinators ; IN: db.types +HOOK: modifier-table db ( -- hash ) +HOOK: compound-modifier db ( str seq -- hash ) +HOOK: type-table db ( -- hash ) +HOOK: create-type-table db ( -- hash ) +HOOK: compound-type db ( str n -- hash ) + +TUPLE: sql-spec class slot-name column-name type modifiers primary-key ; ! ID is the Primary key +! +native-id+ can be a columns type or a modifier SYMBOL: +native-id+ +! +assigned-id+ can only be a modifier SYMBOL: +assigned-id+ -: primary-key? ( spec -- ? ) - [ { +native-id+ +assigned-id+ } member? ] contains? ; +: (primary-key?) ( obj -- ? ) + { +native-id+ +assigned-id+ } member? ; -: contains-id? ( columns id -- ? ) - swap [ member? ] with contains? ; - -: assigned-id? ( columns -- ? ) +assigned-id+ contains-id? ; -: native-id? ( columns -- ? ) +native-id+ contains-id? ; +: primary-key? ( spec -- ? ) + sql-spec-primary-key (primary-key?) ; + +: normalize-spec ( spec -- ) + dup sql-spec-type dup (primary-key?) [ + swap set-sql-spec-primary-key + ] [ + drop dup sql-spec-modifiers [ + (primary-key?) + ] deep-find + [ swap set-sql-spec-primary-key ] [ drop ] if* + ] if ; + +: find-primary-key ( specs -- obj ) + [ sql-spec-primary-key ] find nip ; + +: native-id? ( spec -- ? ) + sql-spec-primary-key +native-id+ = ; + +: assigned-id? ( spec -- ? ) + sql-spec-primary-key +assigned-id+ = ; + +SYMBOL: +foreign-id+ ! Same concept, SQLite has autoincrement, PostgreSQL has serial SYMBOL: +autoincrement+ @@ -28,40 +57,168 @@ SYMBOL: +not-null+ SYMBOL: +has-many+ -SYMBOL: SERIAL -SYMBOL: INTEGER -SYMBOL: DOUBLE -SYMBOL: BOOLEAN +: relation? ( spec -- ? ) [ +has-many+ = ] deep-find ; +SYMBOL: INTEGER +SYMBOL: BIG_INTEGER +SYMBOL: DOUBLE +SYMBOL: REAL +SYMBOL: BOOLEAN SYMBOL: TEXT SYMBOL: VARCHAR - SYMBOL: TIMESTAMP SYMBOL: DATE -SYMBOL: BIG_INTEGER +: spec>tuple ( class spec -- tuple ) + [ ?first3 ] keep 3 ?tail* + { + set-sql-spec-class + set-sql-spec-slot-name + set-sql-spec-column-name + set-sql-spec-type + set-sql-spec-modifiers + } sql-spec construct + dup normalize-spec ; + +: sql-type-hash ( -- assoc ) + H{ + { INTEGER "integer" } + { TEXT "text" } + { VARCHAR "varchar" } + { DOUBLE "real" } + { TIMESTAMP "timestamp" } + } ; TUPLE: no-sql-type ; : no-sql-type ( -- * ) T{ no-sql-type } throw ; -HOOK: sql-modifiers* db ( modifiers -- str ) -HOOK: >sql-type db ( obj -- str ) - -! HOOK: >factor-type db ( obj -- obj ) +TUPLE: no-sql-modifier ; +: no-sql-modifier ( -- * ) T{ no-sql-modifier } throw ; : number>string* ( n/str -- str ) dup number? [ number>string ] when ; -: maybe-remove-id ( columns -- obj ) - [ +native-id+ swap member? not ] subset ; +: maybe-remove-id ( specs -- obj ) + [ native-id? not ] subset ; -: remove-id ( columns -- obj ) - [ primary-key? not ] subset ; +: remove-relations ( specs -- newcolumns ) + [ relation? not ] subset ; -: sql-modifiers ( spec -- seq ) - 3 tail sql-modifiers* ; +: remove-id ( specs -- obj ) + [ sql-spec-primary-key not ] subset ; ! SQLite Types: http://www.sqlite.org/datatype3.html ! NULL INTEGER REAL TEXT BLOB ! PostgreSQL Types: ! http://developer.postgresql.org/pgdocs/postgres/datatype.html + +: lookup-modifier ( obj -- str ) + dup array? [ + unclip lookup-modifier swap compound-modifier + ] [ + modifier-table at* + [ "unknown modifier" throw ] unless + ] if ; + +: lookup-type* ( obj -- str ) + dup array? [ + first lookup-type* + ] [ + type-table at* + [ no-sql-type ] unless + ] if ; + +: lookup-create-type ( obj -- str ) + dup array? [ + unclip lookup-create-type swap compound-type + ] [ + dup create-type-table at* + [ nip ] [ drop lookup-type* ] if + ] if ; + +: lookup-type ( obj create? -- str ) + [ lookup-create-type ] [ lookup-type* ] if ; + +: single-quote ( str -- newstr ) + "'" swap "'" 3append ; + +: double-quote ( str -- newstr ) + "\"" swap "\"" 3append ; + +: paren ( str -- newstr ) + "(" swap ")" 3append ; + +: join-space ( str1 str2 -- newstr ) + " " swap 3append ; + +: modifiers ( spec -- str ) + sql-spec-modifiers + [ lookup-modifier ] map " " join + dup empty? [ " " swap append ] unless ; + +SYMBOL: building-seq +: get-building-seq ( n -- seq ) + building-seq get nth ; + +: n, get-building-seq push ; +: n% get-building-seq push-all ; +: n# >r number>string r> n% ; + +: 0, 0 n, ; +: 0% 0 n% ; +: 0# 0 n# ; +: 1, 1 n, ; +: 1% 1 n% ; +: 1# 1 n# ; +: 2, 2 n, ; +: 2% 2 n% ; +: 2# 2 n# ; + +: nmake ( quot exemplars -- seqs ) + dup length dup zero? [ 1+ ] when + [ + [ + [ drop 1024 swap new-resizable ] 2map + [ building-seq set call ] keep + ] 2keep >r [ like ] 2map r> firstn + ] with-scope ; + +HOOK: bind% db ( spec -- ) + +TUPLE: no-slot-named ; +: no-slot-named ( -- * ) T{ no-slot-named } throw ; + +: slot-spec-named ( str class -- slot-spec ) + "slots" word-prop [ slot-spec-name = ] with find nip + [ no-slot-named ] unless* ; + +: offset-of-slot ( str obj -- n ) + class slot-spec-named slot-spec-offset ; + +: get-slot-named ( str obj -- value ) + tuck offset-of-slot [ no-slot-named ] unless* slot ; + +: set-slot-named ( value str obj -- ) + tuck offset-of-slot [ no-slot-named ] unless* set-slot ; + +: tuple>filled-slots ( tuple -- alist ) + dup mirror-slots [ slot-spec-name ] map + swap tuple-slots 2array flip [ nip ] assoc-subset ; + +: tuple>params ( specs tuple -- obj ) + [ + >r dup sql-spec-type swap sql-spec-slot-name r> + get-slot-named swap + ] curry { } map>assoc ; + +: sql-type>factor-type ( obj type -- obj ) + dup array? [ first ] when + { + { +native-id+ [ string>number ] } + { INTEGER [ string>number ] } + { DOUBLE [ string>number ] } + { REAL [ string>number ] } + { TEXT [ ] } + { VARCHAR [ ] } + [ "no conversion from sql type to factor type" throw ] + } case ; diff --git a/extra/http/basic-authentication/basic-authentication.factor b/extra/http/basic-authentication/basic-authentication.factor index e15ba9db16..dfe04dc4b5 100644 --- a/extra/http/basic-authentication/basic-authentication.factor +++ b/extra/http/basic-authentication/basic-authentication.factor @@ -61,5 +61,5 @@ SYMBOL: realms #! Check if the user is authenticated in the given realm #! to run the specified quotation. If not, use Basic #! Authentication to ask for authorization details. - over "Authorization" header-param authorization-ok? + over "authorization" header-param authorization-ok? [ nip call ] [ drop authentication-error ] if ; diff --git a/extra/http/server/responders/responders.factor b/extra/http/server/responders/responders.factor index e4e0e257c4..5e0a5d57d9 100755 --- a/extra/http/server/responders/responders.factor +++ b/extra/http/server/responders/responders.factor @@ -77,7 +77,7 @@ SYMBOL: max-post-request 1024 256 * max-post-request set-global : content-length ( header -- n ) - "Content-Length" swap at string>number dup [ + "content-length" peek-at string>number dup [ dup max-post-request get > [ "Content-Length > max-post-request" throw ] when @@ -136,7 +136,7 @@ LOG: log-headers DEBUG : host ( -- string ) #! The host the current responder was called from. - "Host" header-param ":" split1 drop ; + "host" header-param ":" split1 drop ; : add-responder ( responder -- ) #! Add a responder object to the list. diff --git a/extra/io/windows/launcher/launcher.factor b/extra/io/windows/launcher/launcher.factor index 58e3c0ba69..708dc1dc38 100755 --- a/extra/io/windows/launcher/launcher.factor +++ b/extra/io/windows/launcher/launcher.factor @@ -146,8 +146,8 @@ M: windows-io kill-process* ( handle -- ) : wait-loop ( -- ) processes get dup assoc-empty? - [ drop f nap drop ] - [ wait-for-processes [ 100 nap drop ] when ] if ; + [ drop f sleep-until ] + [ wait-for-processes [ 100 sleep ] when ] if ; SYMBOL: wait-thread diff --git a/extra/ogg/player/player.factor b/extra/ogg/player/player.factor index dae96dc0ea..3307e921b8 100755 --- a/extra/ogg/player/player.factor +++ b/extra/ogg/player/player.factor @@ -14,7 +14,7 @@ USING: kernel alien ogg ogg.vorbis ogg.theora io byte-arrays sequences libc shuffle alien.c-types system openal math namespaces threads shuffle opengl arrays ui.gadgets.worlds combinators math.parser ui.gadgets ui.render opengl.gl ui - continuations io.files hints combinators.lib sequences.lib ; + continuations io.files hints combinators.lib sequences.lib debugger ; IN: ogg.player @@ -149,7 +149,7 @@ HINTS: yuv>rgb byte-array byte-array ; dup player-gadget [ dup { player-td player-yuv } get-slots theora_decode_YUVout drop dup player-rgb over player-yuv yuv>rgb - dup player-gadget find-world draw-world + dup player-gadget relayout yield ] when ; : num-audio-buffers-processed ( player -- player n ) @@ -177,7 +177,7 @@ HINTS: yuv>rgb byte-array byte-array ; : append-audio ( player -- player bool ) num-audio-buffers-processed { { [ over player-buffers length 1 = over zero? and ] [ drop append-new-audio-buffer t ] } - { [ over player-buffers length 2 = over zero? and ] [ 0 sleep drop f ] } + { [ over player-buffers length 2 = over zero? and ] [ yield drop f ] } { [ t ] [ fill-processed-audio-buffer t ] } } cond ; @@ -602,8 +602,7 @@ M: theora-gadget draw-gadget* ( gadget -- ) parse-remaining-headers initialize-decoder dup player-gadget [ initialize-gui ] when* - [ decode ] [ drop ] recover -! decode + [ decode ] try wait-for-sound cleanup drop ; diff --git a/extra/random-tester/random-tester.factor b/extra/random-tester/random-tester.factor index 8704687e34..7fb1714860 100755 --- a/extra/random-tester/random-tester.factor +++ b/extra/random-tester/random-tester.factor @@ -1,5 +1,6 @@ USING: compiler continuations io kernel math namespaces -prettyprint quotations random sequences vectors ; +prettyprint quotations random sequences vectors +compiler.units ; USING: random-tester.databank random-tester.safe-words ; IN: random-tester diff --git a/extra/sequences/lib/lib-tests.factor b/extra/sequences/lib/lib-tests.factor index 2f50ad1786..d4af66b72f 100755 --- a/extra/sequences/lib/lib-tests.factor +++ b/extra/sequences/lib/lib-tests.factor @@ -20,8 +20,6 @@ IN: temporary [ 10 ] [ { 1 2 3 4 } [ + ] reduce* ] unit-test [ 24 ] [ { 1 2 3 4 } [ * ] reduce* ] unit-test -[ 1 2 3 4 ] [ { 1 2 3 4 } 4 nfirst ] unit-test - [ -4 ] [ 1 -4 [ abs ] higher ] unit-test [ 1 ] [ 1 -4 [ abs ] lower ] unit-test @@ -80,4 +78,4 @@ IN: temporary { 13 } [ 1 2 { 3 4 } [ + + ] 2 each-withn + ] unit-test [ { 910 911 912 } ] [ 10 900 3 [ + + ] map-with2 ] unit-test -[ 1 2 3 4 ] [ { 1 2 3 4 } 4 nfirst ] unit-test +[ 1 2 3 4 ] [ { 1 2 3 4 } 4 firstn ] unit-test diff --git a/extra/sequences/lib/lib.factor b/extra/sequences/lib/lib.factor index 1beec90b75..c02932a020 100755 --- a/extra/sequences/lib/lib.factor +++ b/extra/sequences/lib/lib.factor @@ -18,8 +18,9 @@ IN: sequences.lib : map-with2 ( obj obj list quot -- newseq ) 2 map-withn ; inline -MACRO: nfirst ( n -- ) - [ [ swap nth ] curry [ keep ] curry ] map concat [ drop ] compose ; +MACRO: firstn ( n -- ) + [ [ swap nth ] curry + [ keep ] curry ] map concat [ drop ] compose ; : prepare-index ( seq quot -- seq n quot ) >r dup length r> ; inline @@ -182,6 +183,14 @@ PRIVATE> : ?first3 ( seq -- 1st/f 2nd/f 3rd/f ) dup ?first2 rot ?third ; inline : ?first4 ( seq -- 1st/f 2nd/f 3rd/f 4th/f ) dup ?first3 roll ?fourth ; inline +USE: continuations +: ?subseq ( from to seq -- subseq ) + >r >r 0 max r> r> + [ length tuck min >r min r> ] keep subseq ; + +: ?head* ( seq n -- seq/f ) (head) ?subseq ; +: ?tail* ( seq n -- seq/f ) (tail) ?subseq ; + : accumulator ( quot -- quot vec ) V{ } clone [ [ push ] curry compose ] keep ; diff --git a/extra/tools/threads/threads.factor b/extra/tools/threads/threads.factor index 3313a56964..552247e2c4 100755 --- a/extra/tools/threads/threads.factor +++ b/extra/tools/threads/threads.factor @@ -8,7 +8,10 @@ heaps.private system math math.parser ; : thread. ( thread -- ) dup thread-id pprint-cell dup thread-name over [ write-object ] with-cell - dup thread-state "running" or [ write ] with-cell + dup thread-state [ + [ dup self eq? "running" "yield" ? ] unless* + write + ] with-cell [ thread-sleep-entry [ entry-key millis [-] number>string write diff --git a/extra/ui/cocoa/cocoa.factor b/extra/ui/cocoa/cocoa.factor index 53ed62252d..b719556cba 100755 --- a/extra/ui/cocoa/cocoa.factor +++ b/extra/ui/cocoa/cocoa.factor @@ -19,7 +19,7 @@ SYMBOL: stop-after-last-window? : event-loop ( -- ) event-loop? [ [ - [ NSApp do-events ui-step 10 sleep ] ui-try + [ NSApp do-events ui-step ui-wait ] ui-try ] with-autorelease-pool event-loop ] when ; diff --git a/extra/ui/gadgets/gadgets.factor b/extra/ui/gadgets/gadgets.factor index 37c5684cc9..3db3b9c270 100755 --- a/extra/ui/gadgets/gadgets.factor +++ b/extra/ui/gadgets/gadgets.factor @@ -2,7 +2,7 @@ ! See http://factorcode.org/license.txt for BSD license. USING: arrays hashtables kernel models math namespaces sequences quotations math.vectors combinators sorting vectors dlists -models ; +models threads ; IN: ui.gadgets TUPLE: rect loc dim ; @@ -178,13 +178,17 @@ M: array gadget-text* : forget-pref-dim ( gadget -- ) f swap set-gadget-pref-dim ; +SYMBOL: ui-thread + +: notify-ui-thread ( -- ) ui-thread get interrupt ; + : layout-queue ( -- queue ) \ layout-queue get ; : layout-later ( gadget -- ) #! When unit testing gadgets without the UI running, the #! invalid queue is not initialized and we simply ignore #! invalidation requests. - layout-queue [ push-front ] [ drop ] if* ; + layout-queue [ push-front notify-ui-thread ] [ drop ] if* ; DEFER: relayout @@ -256,11 +260,11 @@ M: gadget layout* drop ; : queue-graft ( gadget -- ) { f t } over set-gadget-graft-state - graft-queue push-front ; + graft-queue push-front notify-ui-thread ; : queue-ungraft ( gadget -- ) { t f } over set-gadget-graft-state - graft-queue push-front ; + graft-queue push-front notify-ui-thread ; : graft-later ( gadget -- ) dup gadget-graft-state { diff --git a/extra/ui/ui.factor b/extra/ui/ui.factor index 1de0dac6f0..adff223bc7 100755 --- a/extra/ui/ui.factor +++ b/extra/ui/ui.factor @@ -133,6 +133,9 @@ SYMBOL: ui-hook : ui-step ( -- ) [ notify-queued layout-queued redraw-worlds ] assert-depth ; +: ui-wait ( -- ) + 10 sleep ; + : open-world-window ( world -- ) dup pref-dim over set-gadget-dim dup relayout graft ui-step ; @@ -155,6 +158,7 @@ M: object close-window find-world [ ungraft ] when* ; : start-ui ( -- ) + self ui-thread set-global restore-windows? [ restore-windows ] [ diff --git a/extra/ui/windows/windows.factor b/extra/ui/windows/windows.factor index 80c03a3f5d..b7040c875d 100755 --- a/extra/ui/windows/windows.factor +++ b/extra/ui/windows/windows.factor @@ -15,8 +15,11 @@ TUPLE: windows-ui-backend ; : lf>crlf [ [ dup CHAR: \n = [ CHAR: \r , ] when , ] each ] "" make ; : enum-clipboard ( -- seq ) - 0 [ EnumClipboardFormats win32-error dup dup 0 > ] [ ] - { } unfold nip ; + 0 + [ EnumClipboardFormats win32-error dup dup 0 > ] + [ ] + [ drop ] + unfold nip ; : with-clipboard ( quot -- ) f OpenClipboard win32-error=0/f @@ -40,13 +43,12 @@ TUPLE: windows-ui-backend ; : copy ( str -- ) lf>crlf [ string>u16-alien - f OpenClipboard win32-error=0/f EmptyClipboard win32-error=0/f GMEM_MOVEABLE over length 1+ GlobalAlloc dup win32-error=0/f dup GlobalLock dup win32-error=0/f - rot dup length memcpy + swapd byte-array>memory dup GlobalUnlock win32-error=0/f CF_UNICODETEXT swap SetClipboardData win32-error=0/f ] with-clipboard ; @@ -72,31 +74,29 @@ SYMBOL: mouse-captured : style ( -- n ) WS_OVERLAPPEDWINDOW ; inline : ex-style ( -- n ) WS_EX_APPWINDOW WS_EX_WINDOWEDGE bitor ; inline -: adjust-RECT ( RECT -- ) - style 0 ex-style AdjustWindowRectEx win32-error=0/f ; - -: make-RECT ( width height -- RECT ) - "RECT" [ set-RECT-bottom ] keep [ set-RECT-right ] keep ; - -: make-adjusted-RECT ( width height -- RECT ) - make-RECT dup adjust-RECT ; - -: get-RECT-dimensions ( RECT -- width height ) - [ RECT-right ] keep [ RECT-left - ] keep - [ RECT-bottom ] keep RECT-top - ; - : get-RECT-top-left ( RECT -- x y ) [ RECT-left ] keep RECT-top ; +: get-RECT-dimensions ( RECT -- x y width height ) + [ get-RECT-top-left ] keep + [ RECT-right ] keep [ RECT-left - ] keep + [ RECT-bottom ] keep RECT-top - ; + : handle-wm-paint ( hWnd uMsg wParam lParam -- ) #! wParam and lParam are unused #! only paint if width/height both > 0 3drop window draw-world ; : handle-wm-size ( hWnd uMsg wParam lParam -- ) - [ lo-word ] keep hi-word make-RECT get-RECT-dimensions 2array 2nip + 2nip + [ lo-word ] keep hi-word 2array dup { 0 0 } = [ 2drop ] [ swap window set-gadget-dim ui-step ] if ; +: handle-wm-move ( hWnd uMsg wParam lParam -- ) + 2nip + [ lo-word ] keep hi-word 2array + swap window set-world-loc ; + : wm-keydown-codes ( -- key ) H{ { 8 "BACKSPACE" } @@ -240,7 +240,7 @@ M: windows-ui-backend (close-window) : mouse-absolute>relative ( lparam handle -- array ) >r >lo-hi r> - 0 0 make-RECT [ GetWindowRect win32-error=0/f ] keep + "RECT" [ GetWindowRect win32-error=0/f ] keep get-RECT-top-left 2array v- ; : mouse-event>gesture ( uMsg -- button ) @@ -317,6 +317,7 @@ M: windows-ui-backend (close-window) { [ dup WM_PAINT = ] [ drop 4dup handle-wm-paint DefWindowProc ] } { [ dup WM_SIZE = ] [ drop handle-wm-size 0 ] } + { [ dup WM_MOVE = ] [ drop handle-wm-move 0 ] } ! Keyboard events { [ dup WM_KEYDOWN = over WM_SYSKEYDOWN = or ] @@ -353,7 +354,7 @@ M: windows-ui-backend (close-window) { { [ windows get empty? ] [ drop ] } { [ dup peek-message? ] [ - >r [ ui-step 10 sleep ] ui-try + >r [ ui-step ui-wait ] ui-try r> event-loop ] } { [ dup MSG-message WM_QUIT = ] [ drop ] } @@ -383,13 +384,26 @@ M: windows-ui-backend (close-window) RegisterClassEx dup win32-error=0/f ] when ; -: create-window ( width height -- hwnd ) +: adjust-RECT ( RECT -- ) + style 0 ex-style AdjustWindowRectEx win32-error=0/f ; + +: make-RECT ( world -- RECT ) + dup world-loc { 40 40 } vmax dup rot rect-dim v+ + "RECT" + over first over set-RECT-right + swap second over set-RECT-bottom + over first over set-RECT-left + swap second over set-RECT-top ; + +: make-adjusted-RECT ( rect -- RECT ) + make-RECT dup adjust-RECT ; + +: create-window ( rect -- hwnd ) make-adjusted-RECT >r class-name-ptr get-global f r> >r >r >r ex-style r> r> { WS_CLIPSIBLINGS WS_CLIPCHILDREN style } flags - CW_USEDEFAULT dup r> - get-RECT-dimensions + r> get-RECT-dimensions f f f GetModuleHandle f CreateWindowEx dup win32-error=0/f ; : show-window ( hWnd -- ) @@ -424,7 +438,7 @@ M: windows-ui-backend (close-window) get-dc dup setup-pixel-format dup get-rc ; M: windows-ui-backend (open-window) ( world -- ) - [ rect-dim first2 create-window dup setup-gl ] keep + [ create-window dup setup-gl ] keep [ f ] keep [ swap win-hWnd register-window ] 2keep dupd set-world-handle @@ -445,8 +459,8 @@ M: windows-ui-backend raise-window* ( world -- ) M: windows-ui-backend set-title ( string world -- ) world-handle [ nip win-hWnd WM_SETTEXT 0 ] 2keep dup win-title [ free ] when* - >r malloc-u16-string r> - dupd set-win-title alien-address + >r malloc-u16-string dup r> + set-win-title alien-address SendMessage drop ; M: windows-ui-backend ui diff --git a/extra/ui/x11/x11.factor b/extra/ui/x11/x11.factor index 9156089a2f..d56bd8e119 100755 --- a/extra/ui/x11/x11.factor +++ b/extra/ui/x11/x11.factor @@ -178,7 +178,7 @@ M: world client-event next-event dup None XFilterEvent zero? [ drop wait-event ] unless ] [ - ui-step 10 sleep wait-event + ui-step ui-wait wait-event ] if ; : do-events ( -- ) diff --git a/extra/webapps/cgi/cgi.factor b/extra/webapps/cgi/cgi.factor index 6be99088d0..e4fdeb7ac7 100755 --- a/extra/webapps/cgi/cgi.factor +++ b/extra/webapps/cgi/cgi.factor @@ -31,13 +31,13 @@ SYMBOL: cgi-root "method" get >upper "REQUEST_METHOD" set "raw-query" get "QUERY_STRING" set - "Cookie" header-param "HTTP_COOKIE" set + "cookie" header-param "HTTP_COOKIE" set - "User-Agent" header-param "HTTP_USER_AGENT" set - "Accept" header-param "HTTP_ACCEPT" set + "user-agent" header-param "HTTP_USER_AGENT" set + "accept" header-param "HTTP_ACCEPT" set post? [ - "Content-Type" header-param "CONTENT_TYPE" set + "content-type" header-param "CONTENT_TYPE" set "raw-response" get length number>string "CONTENT_LENGTH" set ] when ] H{ } make-assoc ; diff --git a/extra/webapps/file/file.factor b/extra/webapps/file/file.factor index c324561279..82bc5d1316 100755 --- a/extra/webapps/file/file.factor +++ b/extra/webapps/file/file.factor @@ -25,7 +25,7 @@ SYMBOL: doc-root : last-modified-matches? ( filename -- bool ) file-http-date dup [ - "If-Modified-Since" header-param = + "if-modified-since" header-param = ] when ; : not-modified-response ( -- ) diff --git a/extra/webapps/fjsc/fjsc.factor b/extra/webapps/fjsc/fjsc.factor index 56ecb3f546..cf01bf63db 100755 --- a/extra/webapps/fjsc/fjsc.factor +++ b/extra/webapps/fjsc/fjsc.factor @@ -25,7 +25,7 @@ IN: webapps.fjsc : compile-url ( url -- ) #! Compile the factor code at the given url, return the javascript. dup "http:" head? [ "Unable to access remote sites." throw ] when - "http://" "Host" header-param rot 3append http-get compile "();" write flush ; + "http://" "host" header-param rot 3append http-get compile "();" write flush ; \ compile-url { { "url" v-required }