Merge branch 'master' of git://factorcode.org/git/factor
commit
c61c9eb625
|
@ -0,0 +1 @@
|
||||||
|
John Benediktsson
|
|
@ -0,0 +1,13 @@
|
||||||
|
USING: definitions io.launcher kernel math math.parser parser
|
||||||
|
namespaces prettyprint editors make ;
|
||||||
|
|
||||||
|
IN: editors.macvim
|
||||||
|
|
||||||
|
: macvim-location ( file line -- )
|
||||||
|
drop
|
||||||
|
[ "open" , "-a" , "MacVim", , ] { } make
|
||||||
|
try-process ;
|
||||||
|
|
||||||
|
[ macvim-location ] edit-hook set-global
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
MacVim editor integration
|
|
@ -0,0 +1 @@
|
||||||
|
unportable
|
|
@ -0,0 +1 @@
|
||||||
|
John Benediktsson
|
|
@ -0,0 +1 @@
|
||||||
|
TextEdit editor integration
|
|
@ -0,0 +1 @@
|
||||||
|
unportable
|
|
@ -0,0 +1,13 @@
|
||||||
|
USING: definitions io.launcher kernel math math.parser parser
|
||||||
|
namespaces prettyprint editors make ;
|
||||||
|
|
||||||
|
IN: editors.textedit
|
||||||
|
|
||||||
|
: textedit-location ( file line -- )
|
||||||
|
drop
|
||||||
|
[ "open" , "-a" , "TextEdit", , ] { } make
|
||||||
|
try-process ;
|
||||||
|
|
||||||
|
[ textedit-location ] edit-hook set-global
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ TUPLE: action rest authorize init display validate submit ;
|
||||||
begin-conversation
|
begin-conversation
|
||||||
nested-forms-key param " " split harvest nested-forms cset
|
nested-forms-key param " " split harvest nested-forms cset
|
||||||
form get form cset
|
form get form cset
|
||||||
<redirect>
|
<continue-conversation>
|
||||||
] [ <400> ] if*
|
] [ <400> ] if*
|
||||||
exit-with ;
|
exit-with ;
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
USING: kernel sequences db.tuples alarms calendar db fry
|
USING: kernel sequences db.tuples alarms calendar db fry
|
||||||
furnace.db
|
furnace.db
|
||||||
furnace.cache
|
furnace.cache
|
||||||
|
furnace.asides
|
||||||
furnace.referrer
|
furnace.referrer
|
||||||
furnace.sessions
|
furnace.sessions
|
||||||
furnace.conversations
|
furnace.conversations
|
||||||
|
@ -12,13 +13,14 @@ IN: furnace.alloy
|
||||||
|
|
||||||
: <alloy> ( responder db params -- responder' )
|
: <alloy> ( responder db params -- responder' )
|
||||||
'[
|
'[
|
||||||
|
<asides>
|
||||||
<conversations>
|
<conversations>
|
||||||
<sessions>
|
<sessions>
|
||||||
_ _ <db-persistence>
|
_ _ <db-persistence>
|
||||||
<check-form-submissions>
|
<check-form-submissions>
|
||||||
] call ;
|
] call ;
|
||||||
|
|
||||||
: state-classes { session conversation permit } ; inline
|
: state-classes { session aside conversation permit } ; inline
|
||||||
|
|
||||||
: init-furnace-tables ( -- )
|
: init-furnace-tables ( -- )
|
||||||
state-classes ensure-tables
|
state-classes ensure-tables
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
! Copyright (C) 2008 Slava Pestov.
|
||||||
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
|
USING: namespaces assocs kernel sequences accessors hashtables
|
||||||
|
urls db.types db.tuples math.parser fry logging combinators
|
||||||
|
html.templates.chloe.syntax
|
||||||
|
http http.server http.server.filters http.server.redirection
|
||||||
|
furnace
|
||||||
|
furnace.cache
|
||||||
|
furnace.sessions
|
||||||
|
furnace.redirection ;
|
||||||
|
IN: furnace.asides
|
||||||
|
|
||||||
|
TUPLE: aside < server-state
|
||||||
|
session method url post-data ;
|
||||||
|
|
||||||
|
: <aside> ( id -- aside )
|
||||||
|
aside new-server-state ;
|
||||||
|
|
||||||
|
aside "ASIDES" {
|
||||||
|
{ "session" "SESSION" BIG-INTEGER +not-null+ }
|
||||||
|
{ "method" "METHOD" { VARCHAR 10 } }
|
||||||
|
{ "url" "URL" URL }
|
||||||
|
{ "post-data" "POST_DATA" FACTOR-BLOB }
|
||||||
|
} define-persistent
|
||||||
|
|
||||||
|
: aside-id-key "__a" ;
|
||||||
|
|
||||||
|
TUPLE: asides < server-state-manager ;
|
||||||
|
|
||||||
|
: <asides> ( responder -- responder' )
|
||||||
|
asides new-server-state-manager ;
|
||||||
|
|
||||||
|
SYMBOL: aside-id
|
||||||
|
|
||||||
|
: get-aside ( id -- aside )
|
||||||
|
dup [ aside get-state ] when check-session ;
|
||||||
|
|
||||||
|
: request-aside-id ( request -- id )
|
||||||
|
aside-id-key swap request-params at string>number ;
|
||||||
|
|
||||||
|
: request-aside ( request -- aside )
|
||||||
|
request-aside-id get-aside ;
|
||||||
|
|
||||||
|
: set-aside ( aside -- )
|
||||||
|
[ id>> aside-id set ] when* ;
|
||||||
|
|
||||||
|
: init-asides ( asides -- )
|
||||||
|
asides set
|
||||||
|
request get request-aside-id
|
||||||
|
get-aside
|
||||||
|
set-aside ;
|
||||||
|
|
||||||
|
M: asides call-responder*
|
||||||
|
[ init-asides ] [ asides set ] [ call-next-method ] tri ;
|
||||||
|
|
||||||
|
: touch-aside ( aside -- )
|
||||||
|
asides get touch-state ;
|
||||||
|
|
||||||
|
: begin-aside ( url -- )
|
||||||
|
f <aside>
|
||||||
|
swap >>url
|
||||||
|
session get id>> >>session
|
||||||
|
request get method>> >>method
|
||||||
|
request get post-data>> >>post-data
|
||||||
|
[ touch-aside ] [ insert-tuple ] [ set-aside ] tri ;
|
||||||
|
|
||||||
|
: end-aside-post ( aside -- response )
|
||||||
|
[ url>> ] [ post-data>> ] bi
|
||||||
|
request [
|
||||||
|
clone
|
||||||
|
swap >>post-data
|
||||||
|
over >>url
|
||||||
|
] change
|
||||||
|
[ url set ] [ path>> split-path ] bi
|
||||||
|
asides get responder>> call-responder ;
|
||||||
|
|
||||||
|
\ end-aside-post DEBUG add-input-logging
|
||||||
|
|
||||||
|
ERROR: end-aside-in-get-error ;
|
||||||
|
|
||||||
|
: move-on ( id -- response )
|
||||||
|
post-request? [ end-aside-in-get-error ] unless
|
||||||
|
dup method>> {
|
||||||
|
{ "GET" [ url>> <redirect> ] }
|
||||||
|
{ "HEAD" [ url>> <redirect> ] }
|
||||||
|
{ "POST" [ end-aside-post ] }
|
||||||
|
} case ;
|
||||||
|
|
||||||
|
: end-aside ( default -- response )
|
||||||
|
aside-id get aside-id off get-aside [ move-on ] [ <redirect> ] ?if ;
|
||||||
|
|
||||||
|
M: asides link-attr ( tag -- )
|
||||||
|
drop
|
||||||
|
"aside" optional-attr {
|
||||||
|
{ "none" [ aside-id off ] }
|
||||||
|
{ "begin" [ url get begin-aside ] }
|
||||||
|
{ "current" [ ] }
|
||||||
|
{ f [ ] }
|
||||||
|
} case ;
|
||||||
|
|
||||||
|
M: asides modify-query ( query asides -- query' )
|
||||||
|
drop
|
||||||
|
aside-id get [
|
||||||
|
aside-id-key associate assoc-union
|
||||||
|
] when* ;
|
||||||
|
|
||||||
|
M: asides modify-form ( asides -- )
|
||||||
|
drop
|
||||||
|
aside-id get
|
||||||
|
aside-id-key
|
||||||
|
hidden-form-field ;
|
|
@ -2,7 +2,7 @@
|
||||||
! See http://factorcode.org/license.txt for BSD license.
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
USING: kernel assocs namespaces accessors db db.tuples urls
|
USING: kernel assocs namespaces accessors db db.tuples urls
|
||||||
http.server.dispatchers
|
http.server.dispatchers
|
||||||
furnace.conversations
|
furnace.asides
|
||||||
furnace.actions
|
furnace.actions
|
||||||
furnace.auth
|
furnace.auth
|
||||||
furnace.auth.providers ;
|
furnace.auth.providers ;
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
USING: kernel accessors namespaces sequences assocs
|
USING: kernel accessors namespaces sequences assocs
|
||||||
validators urls html.forms http.server.dispatchers
|
validators urls html.forms http.server.dispatchers
|
||||||
furnace.auth
|
furnace.auth
|
||||||
furnace.actions
|
furnace.asides
|
||||||
furnace.conversations ;
|
furnace.actions ;
|
||||||
IN: furnace.auth.features.edit-profile
|
IN: furnace.auth.features.edit-profile
|
||||||
|
|
||||||
: <edit-profile-action> ( -- action )
|
: <edit-profile-action> ( -- action )
|
||||||
|
|
|
@ -19,7 +19,7 @@ SYMBOL: lost-password-from
|
||||||
[ username>> "username" set-query-param ]
|
[ username>> "username" set-query-param ]
|
||||||
[ ticket>> "ticket" set-query-param ]
|
[ ticket>> "ticket" set-query-param ]
|
||||||
bi
|
bi
|
||||||
adjust-url relative-to-request ;
|
adjust-url ;
|
||||||
|
|
||||||
: password-email ( user -- email )
|
: password-email ( user -- email )
|
||||||
<email>
|
<email>
|
||||||
|
|
|
@ -5,6 +5,7 @@ calendar validators urls logging html.forms
|
||||||
http http.server http.server.dispatchers
|
http http.server http.server.dispatchers
|
||||||
furnace
|
furnace
|
||||||
furnace.auth
|
furnace.auth
|
||||||
|
furnace.asides
|
||||||
furnace.actions
|
furnace.actions
|
||||||
furnace.sessions
|
furnace.sessions
|
||||||
furnace.utilities
|
furnace.utilities
|
||||||
|
@ -93,9 +94,15 @@ SYMBOL: capabilities
|
||||||
[ logout ] >>submit ;
|
[ logout ] >>submit ;
|
||||||
|
|
||||||
M: login-realm login-required* ( description capabilities login -- response )
|
M: login-realm login-required* ( description capabilities login -- response )
|
||||||
begin-aside
|
begin-conversation
|
||||||
[ description cset ] [ capabilities cset ] [ drop ] tri*
|
[ description cset ] [ capabilities cset ] [ secure>> ] tri*
|
||||||
URL" $realm/login" >secure-url <redirect> ;
|
[
|
||||||
|
url get >secure-url begin-aside
|
||||||
|
URL" $realm/login" >secure-url <continue-conversation>
|
||||||
|
] [
|
||||||
|
url get begin-aside
|
||||||
|
URL" $realm/login" <continue-conversation>
|
||||||
|
] if ;
|
||||||
|
|
||||||
: <login-realm> ( responder name -- auth )
|
: <login-realm> ( responder name -- auth )
|
||||||
login-realm new-realm
|
login-realm new-realm
|
||||||
|
|
|
@ -37,7 +37,7 @@ IN: furnace.chloe-tags
|
||||||
<url>
|
<url>
|
||||||
swap parse-query-attr >>query
|
swap parse-query-attr >>query
|
||||||
-rot a-url-path >>path
|
-rot a-url-path >>path
|
||||||
adjust-url relative-to-request
|
adjust-url
|
||||||
] if ;
|
] if ;
|
||||||
|
|
||||||
: compile-a-url ( tag -- )
|
: compile-a-url ( tag -- )
|
||||||
|
|
|
@ -11,18 +11,13 @@ furnace.sessions
|
||||||
furnace.redirection ;
|
furnace.redirection ;
|
||||||
IN: furnace.conversations
|
IN: furnace.conversations
|
||||||
|
|
||||||
TUPLE: conversation < scope
|
TUPLE: conversation < scope session ;
|
||||||
session
|
|
||||||
method url post-data ;
|
|
||||||
|
|
||||||
: <conversation> ( id -- aside )
|
: <conversation> ( id -- conversation )
|
||||||
conversation new-server-state ;
|
conversation new-server-state ;
|
||||||
|
|
||||||
conversation "CONVERSATIONS" {
|
conversation "CONVERSATIONS" {
|
||||||
{ "session" "SESSION" BIG-INTEGER +not-null+ }
|
{ "session" "SESSION" BIG-INTEGER +not-null+ }
|
||||||
{ "method" "METHOD" { VARCHAR 10 } }
|
|
||||||
{ "url" "URL" URL }
|
|
||||||
{ "post-data" "POST_DATA" FACTOR-BLOB }
|
|
||||||
} define-persistent
|
} define-persistent
|
||||||
|
|
||||||
: conversation-id-key "__c" ;
|
: conversation-id-key "__c" ;
|
||||||
|
@ -46,8 +41,7 @@ SYMBOL: conversation-id
|
||||||
conversation get scope-change ; inline
|
conversation get scope-change ; inline
|
||||||
|
|
||||||
: get-conversation ( id -- conversation )
|
: get-conversation ( id -- conversation )
|
||||||
dup [ conversation get-state ] when
|
dup [ conversation get-state ] when check-session ;
|
||||||
dup [ dup session>> session get id>> = [ drop f ] unless ] when ;
|
|
||||||
|
|
||||||
: request-conversation-id ( request -- id )
|
: request-conversation-id ( request -- id )
|
||||||
conversation-id-key swap request-params at string>number ;
|
conversation-id-key swap request-params at string>number ;
|
||||||
|
@ -88,22 +82,21 @@ M: conversations call-responder*
|
||||||
: add-conversation ( conversation -- )
|
: add-conversation ( conversation -- )
|
||||||
[ touch-conversation ] [ insert-tuple ] bi ;
|
[ touch-conversation ] [ insert-tuple ] bi ;
|
||||||
|
|
||||||
: begin-conversation* ( -- conversation )
|
|
||||||
empty-conversastion dup add-conversation ;
|
|
||||||
|
|
||||||
: begin-conversation ( -- )
|
: begin-conversation ( -- )
|
||||||
conversation get [
|
conversation get [
|
||||||
begin-conversation*
|
empty-conversastion
|
||||||
set-conversation
|
[ add-conversation ]
|
||||||
|
[ set-conversation ] bi
|
||||||
] unless ;
|
] unless ;
|
||||||
|
|
||||||
: end-conversation ( -- )
|
: end-conversation ( -- )
|
||||||
conversation off
|
conversation off
|
||||||
conversation-id off ;
|
conversation-id off ;
|
||||||
|
|
||||||
: <conversation-redirect> ( url seq -- response )
|
: <continue-conversation> ( url -- response )
|
||||||
begin-conversation
|
conversation-id get
|
||||||
[ [ get ] keep cset ] each
|
conversation-id-key
|
||||||
|
set-query-param
|
||||||
<redirect> ;
|
<redirect> ;
|
||||||
|
|
||||||
: restore-conversation ( seq -- )
|
: restore-conversation ( seq -- )
|
||||||
|
@ -114,64 +107,6 @@ M: conversations call-responder*
|
||||||
bi
|
bi
|
||||||
] [ 2drop ] if ;
|
] [ 2drop ] if ;
|
||||||
|
|
||||||
: begin-aside ( -- )
|
|
||||||
begin-conversation
|
|
||||||
conversation get
|
|
||||||
request get
|
|
||||||
[ method>> >>method ]
|
|
||||||
[ url>> >>url ]
|
|
||||||
[ post-data>> >>post-data ]
|
|
||||||
tri
|
|
||||||
touch-conversation ;
|
|
||||||
|
|
||||||
: end-aside-post ( aside -- response )
|
|
||||||
request [
|
|
||||||
clone
|
|
||||||
over post-data>> >>post-data
|
|
||||||
over url>> >>url
|
|
||||||
] change
|
|
||||||
[ url>> url set ]
|
|
||||||
[ url>> path>> split-path ] bi
|
|
||||||
conversations get responder>> call-responder ;
|
|
||||||
|
|
||||||
\ end-aside-post DEBUG add-input-logging
|
|
||||||
|
|
||||||
ERROR: end-aside-in-get-error ;
|
|
||||||
|
|
||||||
: move-on ( id -- response )
|
|
||||||
post-request? [ end-aside-in-get-error ] unless
|
|
||||||
dup method>> {
|
|
||||||
{ "GET" [ url>> <redirect> ] }
|
|
||||||
{ "HEAD" [ url>> <redirect> ] }
|
|
||||||
{ "POST" [ end-aside-post ] }
|
|
||||||
} case ;
|
|
||||||
|
|
||||||
: get-aside ( id -- conversation )
|
|
||||||
get-conversation dup [ dup method>> [ drop f ] unless ] when ;
|
|
||||||
|
|
||||||
: end-aside* ( url id -- response )
|
|
||||||
get-aside [ move-on ] [ <redirect> ] ?if ;
|
|
||||||
|
|
||||||
: end-aside ( default -- response )
|
|
||||||
conversation-id get
|
|
||||||
end-conversation
|
|
||||||
end-aside* ;
|
|
||||||
|
|
||||||
M: conversations link-attr ( tag -- )
|
|
||||||
drop
|
|
||||||
"aside" optional-attr {
|
|
||||||
{ "none" [ conversation-id off ] }
|
|
||||||
{ "begin" [ begin-aside ] }
|
|
||||||
{ "current" [ ] }
|
|
||||||
{ f [ ] }
|
|
||||||
} case ;
|
|
||||||
|
|
||||||
M: conversations modify-query ( query conversations -- query' )
|
|
||||||
drop
|
|
||||||
conversation-id get [
|
|
||||||
conversation-id-key associate assoc-union
|
|
||||||
] when* ;
|
|
||||||
|
|
||||||
M: conversations modify-form ( conversations -- )
|
M: conversations modify-form ( conversations -- )
|
||||||
drop
|
drop
|
||||||
conversation-id get
|
conversation-id get
|
||||||
|
|
|
@ -37,6 +37,10 @@ GENERIC: modify-query ( query responder -- query' )
|
||||||
|
|
||||||
M: object modify-query drop ;
|
M: object modify-query drop ;
|
||||||
|
|
||||||
|
GENERIC: modify-redirect-query ( query responder -- query' )
|
||||||
|
|
||||||
|
M: object modify-redirect-query drop ;
|
||||||
|
|
||||||
GENERIC: adjust-url ( url -- url' )
|
GENERIC: adjust-url ( url -- url' )
|
||||||
|
|
||||||
M: url adjust-url
|
M: url adjust-url
|
||||||
|
@ -47,6 +51,14 @@ M: url adjust-url
|
||||||
|
|
||||||
M: string adjust-url ;
|
M: string adjust-url ;
|
||||||
|
|
||||||
|
GENERIC: adjust-redirect-url ( url -- url' )
|
||||||
|
|
||||||
|
M: url adjust-redirect-url
|
||||||
|
adjust-url
|
||||||
|
[ [ modify-redirect-query ] each-responder ] change-query ;
|
||||||
|
|
||||||
|
M: string adjust-redirect-url ;
|
||||||
|
|
||||||
GENERIC: link-attr ( tag responder -- )
|
GENERIC: link-attr ( tag responder -- )
|
||||||
|
|
||||||
M: object link-attr 2drop ;
|
M: object link-attr 2drop ;
|
||||||
|
|
|
@ -7,7 +7,7 @@ http.server.filters furnace ;
|
||||||
IN: furnace.redirection
|
IN: furnace.redirection
|
||||||
|
|
||||||
: <redirect> ( url -- response )
|
: <redirect> ( url -- response )
|
||||||
adjust-url request get method>> {
|
adjust-redirect-url request get method>> {
|
||||||
{ "GET" [ <temporary-redirect> ] }
|
{ "GET" [ <temporary-redirect> ] }
|
||||||
{ "HEAD" [ <temporary-redirect> ] }
|
{ "HEAD" [ <temporary-redirect> ] }
|
||||||
{ "POST" [ <permanent-redirect> ] }
|
{ "POST" [ <permanent-redirect> ] }
|
||||||
|
|
|
@ -107,3 +107,8 @@ M: sessions call-responder* ( path responder -- response )
|
||||||
sessions set
|
sessions set
|
||||||
request-session [ begin-session ] unless*
|
request-session [ begin-session ] unless*
|
||||||
existing-session put-session-cookie ;
|
existing-session put-session-cookie ;
|
||||||
|
|
||||||
|
SLOT: session
|
||||||
|
|
||||||
|
: check-session ( state/f -- state/f )
|
||||||
|
dup [ dup session>> session get id>> = [ drop f ] unless ] when ;
|
||||||
|
|
|
@ -32,7 +32,7 @@ M: object >entry
|
||||||
: process-entries ( seq -- seq' )
|
: process-entries ( seq -- seq' )
|
||||||
20 short head-slice [
|
20 short head-slice [
|
||||||
>entry clone
|
>entry clone
|
||||||
[ adjust-url relative-to-request ] change-url
|
[ adjust-url ] change-url
|
||||||
] map ;
|
] map ;
|
||||||
|
|
||||||
: <feed-content> ( body -- response )
|
: <feed-content> ( body -- response )
|
||||||
|
@ -46,7 +46,7 @@ TUPLE: feed-action < action title url entries ;
|
||||||
feed new
|
feed new
|
||||||
_
|
_
|
||||||
[ title>> call >>title ]
|
[ title>> call >>title ]
|
||||||
[ url>> call adjust-url relative-to-request >>url ]
|
[ url>> call adjust-url >>url ]
|
||||||
[ entries>> call process-entries >>entries ]
|
[ entries>> call process-entries >>entries ]
|
||||||
tri
|
tri
|
||||||
<feed-content>
|
<feed-content>
|
||||||
|
|
|
@ -144,19 +144,22 @@ M: code render*
|
||||||
[ string-lines ] [ drop ] [ mode>> value ] tri* htmlize-lines ;
|
[ string-lines ] [ drop ] [ mode>> value ] tri* htmlize-lines ;
|
||||||
|
|
||||||
! Farkup component
|
! Farkup component
|
||||||
TUPLE: farkup no-follow disable-images ;
|
TUPLE: farkup no-follow disable-images parsed ;
|
||||||
|
|
||||||
: string>boolean ( string -- boolean )
|
: string>boolean ( string -- boolean )
|
||||||
{
|
{
|
||||||
{ "true" [ t ] }
|
{ "true" [ t ] }
|
||||||
{ "false" [ f ] }
|
{ "false" [ f ] }
|
||||||
|
{ f [ f ] }
|
||||||
} case ;
|
} case ;
|
||||||
|
|
||||||
M: farkup render*
|
M: farkup render*
|
||||||
[
|
[
|
||||||
|
nip
|
||||||
[ no-follow>> [ string>boolean link-no-follow? set ] when* ]
|
[ no-follow>> [ string>boolean link-no-follow? set ] when* ]
|
||||||
[ disable-images>> [ string>boolean disable-images? set ] when* ] bi
|
[ disable-images>> [ string>boolean disable-images? set ] when* ]
|
||||||
drop string-lines "\n" join write-farkup
|
[ parsed>> string>boolean [ (write-farkup) ] [ write-farkup ] if ]
|
||||||
|
tri
|
||||||
] with-scope ;
|
] with-scope ;
|
||||||
|
|
||||||
! Inspector component
|
! Inspector component
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
! Copyright (C) 2008 Doug Coleman.
|
! Copyright (C) 2008 Doug Coleman.
|
||||||
! See http://factorcode.org/license.txt for BSD license.
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
USING: accessors combinators kernel math math.ranges
|
USING: accessors combinators kernel math math.ranges sequences
|
||||||
sequences regexp.backend regexp.utils memoize sets
|
sets assocs prettyprint.backend make lexer namespaces parser
|
||||||
regexp.parser regexp.nfa regexp.dfa regexp.traversal
|
arrays fry regexp.backend regexp.utils regexp.parser regexp.nfa
|
||||||
regexp.transition-tables assocs prettyprint.backend
|
regexp.dfa regexp.traversal regexp.transition-tables ;
|
||||||
make lexer namespaces parser ;
|
|
||||||
IN: regexp
|
IN: regexp
|
||||||
|
|
||||||
: default-regexp ( string -- regexp )
|
: default-regexp ( string -- regexp )
|
||||||
|
@ -47,6 +46,33 @@ IN: regexp
|
||||||
[ 3drop drop f f ] [ drop [ 1+ ] dip match-range ] if
|
[ 3drop drop f f ] [ drop [ 1+ ] dip match-range ] if
|
||||||
] if ;
|
] if ;
|
||||||
|
|
||||||
|
: first-match ( string regexp -- pair/f )
|
||||||
|
0 swap match-range dup [ 2array ] [ 2drop f ] if ;
|
||||||
|
|
||||||
|
: re-cut ( string regexp -- end/f start )
|
||||||
|
dupd first-match
|
||||||
|
[ [ second tail-slice ] [ first head ] 2bi ]
|
||||||
|
[ "" like f swap ]
|
||||||
|
if* ;
|
||||||
|
|
||||||
|
: re-split ( string regexp -- seq )
|
||||||
|
[ dup ] swap '[ _ re-cut ] [ ] produce nip ;
|
||||||
|
|
||||||
|
: re-replace ( string regexp replacement -- result )
|
||||||
|
[ re-split ] dip join ;
|
||||||
|
|
||||||
|
: next-match ( string regexp -- end/f match/f )
|
||||||
|
dupd first-match dup
|
||||||
|
[ [ second tail-slice ] keep ]
|
||||||
|
[ 2drop f f ]
|
||||||
|
if ;
|
||||||
|
|
||||||
|
: all-matches ( string regexp -- seq )
|
||||||
|
[ dup ] swap '[ _ next-match ] [ ] produce nip ;
|
||||||
|
|
||||||
|
: count-matches ( string regexp -- n )
|
||||||
|
all-matches length 1- ;
|
||||||
|
|
||||||
: initial-option ( regexp option -- regexp' )
|
: initial-option ( regexp option -- regexp' )
|
||||||
over options>> conjoin ;
|
over options>> conjoin ;
|
||||||
|
|
||||||
|
@ -102,8 +128,6 @@ IN: regexp
|
||||||
: option? ( option regexp -- ? )
|
: option? ( option regexp -- ? )
|
||||||
options>> key? ;
|
options>> key? ;
|
||||||
|
|
||||||
USE: multiline
|
|
||||||
/*
|
|
||||||
M: regexp pprint*
|
M: regexp pprint*
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
|
@ -112,4 +136,3 @@ M: regexp pprint*
|
||||||
case-insensitive swap option? [ "i" % ] when
|
case-insensitive swap option? [ "i" % ] when
|
||||||
] "" make
|
] "" make
|
||||||
] keep present-text ;
|
] keep present-text ;
|
||||||
*/
|
|
|
@ -72,8 +72,8 @@ TUPLE: effect-error word inferred declared ;
|
||||||
M: effect-error error.
|
M: effect-error error.
|
||||||
"Stack effects of the word " write
|
"Stack effects of the word " write
|
||||||
[ word>> pprint " do not match." print ]
|
[ word>> pprint " do not match." print ]
|
||||||
[ "Inferred: " write inferred>> effect>string . ]
|
[ "Inferred: " write inferred>> . ]
|
||||||
[ "Declared: " write declared>> effect>string . ] tri ;
|
[ "Declared: " write declared>> . ] tri ;
|
||||||
|
|
||||||
TUPLE: recursive-quotation-error quot ;
|
TUPLE: recursive-quotation-error quot ;
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,13 @@
|
||||||
|
agggtaaa|tttaccct 0
|
||||||
|
[cgt]gggtaaa|tttaccc[acg] 3
|
||||||
|
a[act]ggtaaa|tttacc[agt]t 9
|
||||||
|
ag[act]gtaaa|tttac[agt]ct 8
|
||||||
|
agg[act]taaa|ttta[agt]cct 10
|
||||||
|
aggg[acg]aaa|ttt[cgt]ccct 3
|
||||||
|
agggt[cgt]aa|tt[acg]accct 4
|
||||||
|
agggta[cgt]a|t[acg]taccct 3
|
||||||
|
agggtaa[cgt]|[acg]ttaccct 5
|
||||||
|
|
||||||
|
101745
|
||||||
|
100000
|
||||||
|
133640
|
|
@ -0,0 +1,10 @@
|
||||||
|
USING: benchmark.regex-dna io io.files io.encodings.ascii
|
||||||
|
io.streams.string kernel tools.test ;
|
||||||
|
IN: benchmark.regex-dna.tests
|
||||||
|
|
||||||
|
[ t ] [
|
||||||
|
"resource:extra/benchmark/regex-dna/regex-dna-test-in.txt"
|
||||||
|
[ regex-dna ] with-string-writer
|
||||||
|
"resource:extra/benchmark/regex-dna/regex-dna-test-out.txt"
|
||||||
|
ascii file-contents =
|
||||||
|
] unit-test
|
|
@ -0,0 +1,60 @@
|
||||||
|
! Copyright (C) 2008 Slava Pestov.
|
||||||
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
|
USING: accessors regexp prettyprint io io.encodings.ascii
|
||||||
|
io.files kernel sequences assocs namespaces ;
|
||||||
|
IN: benchmark.regex-dna
|
||||||
|
|
||||||
|
! Based on http://shootout.alioth.debian.org/gp4/benchmark.php?test=regexdna&lang=ruby&id=1
|
||||||
|
|
||||||
|
: strip-line-breaks ( string -- string' )
|
||||||
|
R/ >.*\n|\n/ "" re-replace ;
|
||||||
|
|
||||||
|
: count-patterns ( string -- )
|
||||||
|
{
|
||||||
|
R/ agggtaaa|tttaccct/i,
|
||||||
|
R/ [cgt]gggtaaa|tttaccc[acg]/i,
|
||||||
|
R/ a[act]ggtaaa|tttacc[agt]t/i,
|
||||||
|
R/ ag[act]gtaaa|tttac[agt]ct/i,
|
||||||
|
R/ agg[act]taaa|ttta[agt]cct/i,
|
||||||
|
R/ aggg[acg]aaa|ttt[cgt]ccct/i,
|
||||||
|
R/ agggt[cgt]aa|tt[acg]accct/i,
|
||||||
|
R/ agggta[cgt]a|t[acg]taccct/i,
|
||||||
|
R/ agggtaa[cgt]|[acg]ttaccct/i
|
||||||
|
} [
|
||||||
|
[ raw>> write bl ]
|
||||||
|
[ count-matches . ]
|
||||||
|
bi
|
||||||
|
] with each ;
|
||||||
|
|
||||||
|
: do-replacements ( string -- string' )
|
||||||
|
{
|
||||||
|
{ R/ B/ "(c|g|t)" }
|
||||||
|
{ R/ D/ "(a|g|t)" }
|
||||||
|
{ R/ H/ "(a|c|t)" }
|
||||||
|
{ R/ K/ "(g|t)" }
|
||||||
|
{ R/ M/ "(a|c)" }
|
||||||
|
{ R/ N/ "(a|c|g|t)" }
|
||||||
|
{ R/ R/ "(a|g)" }
|
||||||
|
{ R/ S/ "(c|t)" }
|
||||||
|
{ R/ V/ "(a|c|g)" }
|
||||||
|
{ R/ W/ "(a|t)" }
|
||||||
|
{ R/ Y/ "(c|t)" }
|
||||||
|
} [ re-replace ] assoc-each ;
|
||||||
|
|
||||||
|
SYMBOL: ilen
|
||||||
|
SYMBOL: clen
|
||||||
|
|
||||||
|
: regex-dna ( file -- )
|
||||||
|
ascii file-contents dup length ilen set
|
||||||
|
strip-line-breaks dup length clen set
|
||||||
|
dup count-patterns
|
||||||
|
do-replacements
|
||||||
|
nl
|
||||||
|
ilen get .
|
||||||
|
clen get .
|
||||||
|
length . ;
|
||||||
|
|
||||||
|
: regex-dna-main ( -- )
|
||||||
|
"resource:extra/benchmark/regex-dna/regex-dna-test-in.txt" regex-dna ;
|
||||||
|
|
||||||
|
MAIN: regex-dna-main
|
|
@ -20,7 +20,7 @@ M: mb-writer stream-nl ( mb-writer -- )
|
||||||
[ [ last-line>> concat ] [ lines>> ] bi push ] keep
|
[ [ last-line>> concat ] [ lines>> ] bi push ] keep
|
||||||
V{ } clone >>last-line drop ;
|
V{ } clone >>last-line drop ;
|
||||||
|
|
||||||
: spawn-client ( lines listeners -- irc-client )
|
: spawn-client ( -- irc-client )
|
||||||
"someserver" irc-port "factorbot" f <irc-profile>
|
"someserver" irc-port "factorbot" f <irc-profile>
|
||||||
<irc-client>
|
<irc-client>
|
||||||
t >>is-running
|
t >>is-running
|
||||||
|
|
|
@ -68,12 +68,17 @@ SINGLETON: irc-end ! sent when the client isn't running anymore
|
||||||
SINGLETON: irc-disconnected ! sent when connection is lost
|
SINGLETON: irc-disconnected ! sent when connection is lost
|
||||||
SINGLETON: irc-connected ! sent when connection is established
|
SINGLETON: irc-connected ! sent when connection is established
|
||||||
|
|
||||||
|
<PRIVATE
|
||||||
|
: end-loops ( irc-client -- )
|
||||||
|
[ listeners>> values [ out-messages>> ] map ]
|
||||||
|
[ in-messages>> ]
|
||||||
|
[ out-messages>> ] tri 2array prepend
|
||||||
|
[ irc-end swap mailbox-put ] each ;
|
||||||
|
PRIVATE>
|
||||||
|
|
||||||
: terminate-irc ( irc-client -- )
|
: terminate-irc ( irc-client -- )
|
||||||
[ is-running>> ] keep and [
|
[ is-running>> ] keep and [
|
||||||
[ [ irc-end ] dip in-messages>> mailbox-put ]
|
[ end-loops ] [ [ f ] dip (>>is-running) ] bi
|
||||||
[ [ f ] dip (>>is-running) ]
|
|
||||||
[ stream>> dispose ]
|
|
||||||
tri
|
|
||||||
] when* ;
|
] when* ;
|
||||||
|
|
||||||
<PRIVATE
|
<PRIVATE
|
||||||
|
@ -90,7 +95,8 @@ SYMBOL: current-irc-client
|
||||||
: irc-print ( s -- ) irc-stream> [ stream-print ] keep stream-flush ;
|
: irc-print ( s -- ) irc-stream> [ stream-print ] keep stream-flush ;
|
||||||
: irc-send ( irc-message -- ) irc> out-messages>> mailbox-put ;
|
: irc-send ( irc-message -- ) irc> out-messages>> mailbox-put ;
|
||||||
: listener> ( name -- listener/f ) irc> listeners>> at ;
|
: listener> ( name -- listener/f ) irc> listeners>> at ;
|
||||||
|
: channel-mode? ( mode -- ? ) name>> first "#&" member? ;
|
||||||
|
: me? ( string -- ? ) irc> profile>> nickname>> = ;
|
||||||
|
|
||||||
GENERIC: to-listener ( message obj -- )
|
GENERIC: to-listener ( message obj -- )
|
||||||
|
|
||||||
|
@ -137,10 +143,14 @@ M: irc-listener to-listener ( message irc-listener -- )
|
||||||
swap dup listeners-with-participant [ rename-participant ] with with each ;
|
swap dup listeners-with-participant [ rename-participant ] with with each ;
|
||||||
|
|
||||||
: add-participant ( mode nick channel -- )
|
: add-participant ( mode nick channel -- )
|
||||||
listener> [
|
listener>
|
||||||
[ participants>> set-at ]
|
[ participants>> set-at ]
|
||||||
[ [ +join+ f <participant-changed> ] dip to-listener ] 2bi
|
[ [ +join+ f <participant-changed> ] dip to-listener ] 2bi ;
|
||||||
] [ 2drop ] if* ;
|
|
||||||
|
: change-participant-mode ( channel mode nick -- )
|
||||||
|
rot listener>
|
||||||
|
[ participants>> set-at ]
|
||||||
|
[ [ [ +mode+ ] dip <participant-changed> ] dip to-listener ] 3bi ; ! FIXME
|
||||||
|
|
||||||
DEFER: me?
|
DEFER: me?
|
||||||
|
|
||||||
|
@ -174,14 +184,11 @@ DEFER: me?
|
||||||
! Server message handling
|
! Server message handling
|
||||||
! ======================================
|
! ======================================
|
||||||
|
|
||||||
: me? ( string -- ? )
|
|
||||||
irc> profile>> nickname>> = ;
|
|
||||||
|
|
||||||
GENERIC: forward-name ( irc-message -- name )
|
GENERIC: forward-name ( irc-message -- name )
|
||||||
M: join forward-name ( join -- name ) trailing>> ;
|
M: join forward-name ( join -- name ) trailing>> ;
|
||||||
M: part forward-name ( part -- name ) channel>> ;
|
M: part forward-name ( part -- name ) channel>> ;
|
||||||
M: kick forward-name ( kick -- name ) channel>> ;
|
M: kick forward-name ( kick -- name ) channel>> ;
|
||||||
M: mode forward-name ( mode -- name ) channel>> ;
|
M: mode forward-name ( mode -- name ) name>> ;
|
||||||
M: privmsg forward-name ( privmsg -- name )
|
M: privmsg forward-name ( privmsg -- name )
|
||||||
dup name>> me? [ irc-message-sender ] [ name>> ] if ;
|
dup name>> me? [ irc-message-sender ] [ name>> ] if ;
|
||||||
|
|
||||||
|
@ -220,7 +227,8 @@ M: nick-in-use process-message ( nick-in-use -- )
|
||||||
name>> "_" append /NICK ;
|
name>> "_" append /NICK ;
|
||||||
|
|
||||||
M: join process-message ( join -- )
|
M: join process-message ( join -- )
|
||||||
[ drop +normal+ ] [ irc-message-sender ] [ trailing>> ] tri add-participant ;
|
[ drop +normal+ ] [ irc-message-sender ] [ trailing>> ] tri
|
||||||
|
dup listener> [ add-participant ] [ 3drop ] if ;
|
||||||
|
|
||||||
M: part process-message ( part -- )
|
M: part process-message ( part -- )
|
||||||
[ irc-message-sender ] [ channel>> ] bi remove-participant ;
|
[ irc-message-sender ] [ channel>> ] bi remove-participant ;
|
||||||
|
@ -236,6 +244,12 @@ M: quit process-message ( quit -- )
|
||||||
M: nick process-message ( nick -- )
|
M: nick process-message ( nick -- )
|
||||||
[ irc-message-sender ] [ trailing>> ] bi rename-participant-in-all ;
|
[ irc-message-sender ] [ trailing>> ] bi rename-participant-in-all ;
|
||||||
|
|
||||||
|
! M: mode process-message ( mode -- )
|
||||||
|
! [ channel-mode? ] keep and [
|
||||||
|
! [ name>> ] [ mode>> ] [ parameter>> ] tri
|
||||||
|
! [ change-participant-mode ] [ 2drop ] if*
|
||||||
|
! ] when* ;
|
||||||
|
|
||||||
: >nick/mode ( string -- nick mode )
|
: >nick/mode ( string -- nick mode )
|
||||||
dup first "+@" member? [ unclip ] [ 0 ] if participant-mode ;
|
dup first "+@" member? [ unclip ] [ 0 ] if participant-mode ;
|
||||||
|
|
||||||
|
@ -249,15 +263,14 @@ M: names-reply process-message ( names-reply -- )
|
||||||
[ [ f f f <participant-changed> ] dip name>> to-listener ] bi
|
[ [ f f f <participant-changed> ] dip name>> to-listener ] bi
|
||||||
] [ drop ] if* ;
|
] [ drop ] if* ;
|
||||||
|
|
||||||
: handle-incoming-irc ( irc-message -- )
|
|
||||||
[ forward-message ] [ process-message ] bi ;
|
|
||||||
|
|
||||||
! ======================================
|
! ======================================
|
||||||
! Client message handling
|
! Client message handling
|
||||||
! ======================================
|
! ======================================
|
||||||
|
|
||||||
: handle-outgoing-irc ( irc-message -- )
|
GENERIC: handle-outgoing-irc ( irc-message -- ? )
|
||||||
irc-message>client-line irc-print ;
|
M: irc-end handle-outgoing-irc ( irc-end -- ? ) drop f ;
|
||||||
|
M: irc-message handle-outgoing-irc ( irc-message -- ? )
|
||||||
|
irc-message>client-line irc-print t ;
|
||||||
|
|
||||||
! ======================================
|
! ======================================
|
||||||
! Reader/Writer
|
! Reader/Writer
|
||||||
|
@ -279,27 +292,28 @@ DEFER: (connect-irc)
|
||||||
: handle-disconnect ( error -- )
|
: handle-disconnect ( error -- )
|
||||||
drop irc> is-running>> [ (handle-disconnect) ] when ;
|
drop irc> is-running>> [ (handle-disconnect) ] when ;
|
||||||
|
|
||||||
: (reader-loop) ( -- )
|
: (reader-loop) ( -- ? )
|
||||||
irc> stream>> [
|
irc> stream>> [
|
||||||
|dispose stream-readln [
|
|dispose stream-readln [
|
||||||
parse-irc-line handle-reader-message
|
parse-irc-line handle-reader-message t
|
||||||
] [
|
] [
|
||||||
irc> terminate-irc
|
irc> terminate-irc f
|
||||||
] if*
|
] if*
|
||||||
] with-destructors ;
|
] with-destructors ;
|
||||||
|
|
||||||
: reader-loop ( -- ? )
|
: reader-loop ( -- ? )
|
||||||
[ (reader-loop) ] [ handle-disconnect ] recover t ;
|
[ (reader-loop) ] [ handle-disconnect t ] recover ;
|
||||||
|
|
||||||
: writer-loop ( -- ? )
|
: writer-loop ( -- ? )
|
||||||
irc> out-messages>> mailbox-get handle-outgoing-irc t ;
|
irc> out-messages>> mailbox-get handle-outgoing-irc ;
|
||||||
|
|
||||||
! ======================================
|
! ======================================
|
||||||
! Processing loops
|
! Processing loops
|
||||||
! ======================================
|
! ======================================
|
||||||
|
|
||||||
: in-multiplexer-loop ( -- ? )
|
: in-multiplexer-loop ( -- ? )
|
||||||
irc> in-messages>> mailbox-get handle-incoming-irc t ;
|
irc> in-messages>> mailbox-get
|
||||||
|
[ forward-message ] [ process-message ] [ irc-end? not ] tri ;
|
||||||
|
|
||||||
: strings>privmsg ( name string -- privmsg )
|
: strings>privmsg ( name string -- privmsg )
|
||||||
privmsg new [ (>>trailing) ] keep [ (>>name) ] keep ;
|
privmsg new [ (>>trailing) ] keep [ (>>name) ] keep ;
|
||||||
|
@ -310,22 +324,22 @@ DEFER: (connect-irc)
|
||||||
[ nip ]
|
[ nip ]
|
||||||
} cond ;
|
} cond ;
|
||||||
|
|
||||||
|
GENERIC: handle-listener-out ( irc-message -- ? )
|
||||||
|
M: irc-end handle-listener-out ( irc-end -- ? ) drop f ;
|
||||||
|
M: irc-message handle-listener-out ( irc-message -- ? )
|
||||||
|
irc> out-messages>> mailbox-put t ;
|
||||||
|
|
||||||
: listener-loop ( name -- ? )
|
: listener-loop ( name -- ? )
|
||||||
dup listener> [
|
dup listener> [
|
||||||
out-messages>> mailbox-get
|
out-messages>> mailbox-get
|
||||||
maybe-annotate-with-name
|
maybe-annotate-with-name handle-listener-out
|
||||||
irc> out-messages>> mailbox-put
|
|
||||||
t
|
|
||||||
] [ drop f ] if* ;
|
] [ drop f ] if* ;
|
||||||
|
|
||||||
: spawn-irc-loop ( quot: ( -- ? ) name -- )
|
|
||||||
[ '[ irc> is-running>> [ @ ] [ f ] if ] ] dip
|
|
||||||
spawn-server drop ;
|
|
||||||
|
|
||||||
: spawn-irc ( -- )
|
: spawn-irc ( -- )
|
||||||
[ reader-loop ] "irc-reader-loop" spawn-irc-loop
|
[ reader-loop ] "irc-reader-loop" spawn-server
|
||||||
[ writer-loop ] "irc-writer-loop" spawn-irc-loop
|
[ writer-loop ] "irc-writer-loop" spawn-server
|
||||||
[ in-multiplexer-loop ] "in-multiplexer-loop" spawn-irc-loop ;
|
[ in-multiplexer-loop ] "in-multiplexer-loop" spawn-server
|
||||||
|
3drop ;
|
||||||
|
|
||||||
! ======================================
|
! ======================================
|
||||||
! Listener join request handling
|
! Listener join request handling
|
||||||
|
@ -333,7 +347,7 @@ DEFER: (connect-irc)
|
||||||
|
|
||||||
: set+run-listener ( name irc-listener -- )
|
: set+run-listener ( name irc-listener -- )
|
||||||
over irc> listeners>> set-at
|
over irc> listeners>> set-at
|
||||||
'[ _ listener-loop ] "listener" spawn-irc-loop ;
|
'[ _ listener-loop ] "irc-listener-loop" spawn-server drop ;
|
||||||
|
|
||||||
GENERIC: (add-listener) ( irc-listener -- )
|
GENERIC: (add-listener) ( irc-listener -- )
|
||||||
|
|
||||||
|
|
|
@ -6,54 +6,60 @@ IN: irc.messages.tests
|
||||||
|
|
||||||
{ "someuser" } [ "someuser!n=user@some.where" parse-name ] unit-test
|
{ "someuser" } [ "someuser!n=user@some.where" parse-name ] unit-test
|
||||||
|
|
||||||
irc-message new
|
{ T{ irc-message
|
||||||
":someuser!n=user@some.where PRIVMSG #factortest :hi" >>line
|
{ line ":someuser!n=user@some.where PRIVMSG #factortest :hi" }
|
||||||
"someuser!n=user@some.where" >>prefix
|
{ prefix "someuser!n=user@some.where" }
|
||||||
"PRIVMSG" >>command
|
{ command "PRIVMSG" }
|
||||||
{ "#factortest" } >>parameters
|
{ parameters { "#factortest" } }
|
||||||
"hi" >>trailing
|
{ trailing "hi" } } }
|
||||||
1array
|
|
||||||
[ ":someuser!n=user@some.where PRIVMSG #factortest :hi"
|
[ ":someuser!n=user@some.where PRIVMSG #factortest :hi"
|
||||||
string>irc-message f >>timestamp ] unit-test
|
string>irc-message f >>timestamp ] unit-test
|
||||||
|
|
||||||
privmsg new
|
{ T{ privmsg
|
||||||
":someuser!n=user@some.where PRIVMSG #factortest :hi" >>line
|
{ line ":someuser!n=user@some.where PRIVMSG #factortest :hi" }
|
||||||
"someuser!n=user@some.where" >>prefix
|
{ prefix "someuser!n=user@some.where" }
|
||||||
"PRIVMSG" >>command
|
{ command "PRIVMSG" }
|
||||||
{ "#factortest" } >>parameters
|
{ parameters { "#factortest" } }
|
||||||
"hi" >>trailing
|
{ trailing "hi" }
|
||||||
"#factortest" >>name
|
{ name "#factortest" } } }
|
||||||
1array
|
|
||||||
[ ":someuser!n=user@some.where PRIVMSG #factortest :hi"
|
[ ":someuser!n=user@some.where PRIVMSG #factortest :hi"
|
||||||
parse-irc-line f >>timestamp ] unit-test
|
parse-irc-line f >>timestamp ] unit-test
|
||||||
|
|
||||||
join new
|
{ T{ join
|
||||||
":someuser!n=user@some.where JOIN :#factortest" >>line
|
{ line ":someuser!n=user@some.where JOIN :#factortest" }
|
||||||
"someuser!n=user@some.where" >>prefix
|
{ prefix "someuser!n=user@some.where" }
|
||||||
"JOIN" >>command
|
{ command "JOIN" }
|
||||||
{ } >>parameters
|
{ parameters { } }
|
||||||
"#factortest" >>trailing
|
{ trailing "#factortest" } } }
|
||||||
1array
|
|
||||||
[ ":someuser!n=user@some.where JOIN :#factortest"
|
[ ":someuser!n=user@some.where JOIN :#factortest"
|
||||||
parse-irc-line f >>timestamp ] unit-test
|
parse-irc-line f >>timestamp ] unit-test
|
||||||
|
|
||||||
mode new
|
{ T{ mode
|
||||||
":ircserver.net MODE #factortest +ns" >>line
|
{ line ":ircserver.net MODE #factortest +ns" }
|
||||||
"ircserver.net" >>prefix
|
{ prefix "ircserver.net" }
|
||||||
"MODE" >>command
|
{ command "MODE" }
|
||||||
{ "#factortest" "+ns" } >>parameters
|
{ parameters { "#factortest" "+ns" } }
|
||||||
"#factortest" >>channel
|
{ name "#factortest" }
|
||||||
"+ns" >>mode
|
{ mode "+ns" } } }
|
||||||
1array
|
|
||||||
[ ":ircserver.net MODE #factortest +ns"
|
[ ":ircserver.net MODE #factortest +ns"
|
||||||
parse-irc-line f >>timestamp ] unit-test
|
parse-irc-line f >>timestamp ] unit-test
|
||||||
|
|
||||||
nick new
|
{ T{ mode
|
||||||
":someuser!n=user@some.where NICK :someuser2" >>line
|
{ line ":ircserver.net MODE #factortest +o someuser" }
|
||||||
"someuser!n=user@some.where" >>prefix
|
{ prefix "ircserver.net" }
|
||||||
"NICK" >>command
|
{ command "MODE" }
|
||||||
{ } >>parameters
|
{ parameters { "#factortest" "+o" "someuser" } }
|
||||||
"someuser2" >>trailing
|
{ name "#factortest" }
|
||||||
1array
|
{ mode "+o" }
|
||||||
|
{ parameter "someuser" } } }
|
||||||
|
[ ":ircserver.net MODE #factortest +o someuser"
|
||||||
|
parse-irc-line f >>timestamp ] unit-test
|
||||||
|
|
||||||
|
{ T{ nick
|
||||||
|
{ line ":someuser!n=user@some.where NICK :someuser2" }
|
||||||
|
{ prefix "someuser!n=user@some.where" }
|
||||||
|
{ command "NICK" }
|
||||||
|
{ parameters { } }
|
||||||
|
{ trailing "someuser2" } } }
|
||||||
[ ":someuser!n=user@some.where NICK :someuser2"
|
[ ":someuser!n=user@some.where NICK :someuser2"
|
||||||
parse-irc-line f >>timestamp ] unit-test
|
parse-irc-line f >>timestamp ] unit-test
|
|
@ -1,9 +1,10 @@
|
||||||
! Copyright (C) 2008 Bruno Deferrari
|
! Copyright (C) 2008 Bruno Deferrari
|
||||||
! See http://factorcode.org/license.txt for BSD license.
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
USING: kernel fry splitting ascii calendar accessors combinators qualified
|
USING: kernel fry splitting ascii calendar accessors combinators qualified
|
||||||
arrays classes.tuple math.order quotations ;
|
arrays classes.tuple math.order ;
|
||||||
RENAME: join sequences => sjoin
|
RENAME: join sequences => sjoin
|
||||||
EXCLUDE: sequences => join ;
|
EXCLUDE: sequences => join ;
|
||||||
|
EXCLUDE: inverse => _ ;
|
||||||
IN: irc.messages
|
IN: irc.messages
|
||||||
|
|
||||||
TUPLE: irc-message line prefix command parameters trailing timestamp ;
|
TUPLE: irc-message line prefix command parameters trailing timestamp ;
|
||||||
|
@ -18,8 +19,8 @@ TUPLE: kick < irc-message channel who ;
|
||||||
TUPLE: roomlist < irc-message channel names ;
|
TUPLE: roomlist < irc-message channel names ;
|
||||||
TUPLE: nick-in-use < irc-message asterisk name ;
|
TUPLE: nick-in-use < irc-message asterisk name ;
|
||||||
TUPLE: notice < irc-message type ;
|
TUPLE: notice < irc-message type ;
|
||||||
TUPLE: mode < irc-message channel mode ;
|
TUPLE: mode < irc-message name mode parameter ;
|
||||||
TUPLE: names-reply < irc-message who = channel ;
|
TUPLE: names-reply < irc-message who channel ;
|
||||||
TUPLE: unhandled < irc-message ;
|
TUPLE: unhandled < irc-message ;
|
||||||
|
|
||||||
: <irc-client-message> ( command parameters trailing -- irc-message )
|
: <irc-client-message> ( command parameters trailing -- irc-message )
|
||||||
|
@ -28,41 +29,58 @@ TUPLE: unhandled < irc-message ;
|
||||||
|
|
||||||
<PRIVATE
|
<PRIVATE
|
||||||
|
|
||||||
GENERIC: irc-command-string ( irc-message -- string )
|
GENERIC: command-string>> ( irc-message -- string )
|
||||||
|
|
||||||
M: irc-message irc-command-string ( irc-message -- string ) command>> ;
|
M: irc-message command-string>> ( irc-message -- string ) command>> ;
|
||||||
M: ping irc-command-string ( ping -- string ) drop "PING" ;
|
M: ping command-string>> ( ping -- string ) drop "PING" ;
|
||||||
M: join irc-command-string ( join -- string ) drop "JOIN" ;
|
M: join command-string>> ( join -- string ) drop "JOIN" ;
|
||||||
M: part irc-command-string ( part -- string ) drop "PART" ;
|
M: part command-string>> ( part -- string ) drop "PART" ;
|
||||||
M: quit irc-command-string ( quit -- string ) drop "QUIT" ;
|
M: quit command-string>> ( quit -- string ) drop "QUIT" ;
|
||||||
M: nick irc-command-string ( nick -- string ) drop "NICK" ;
|
M: nick command-string>> ( nick -- string ) drop "NICK" ;
|
||||||
M: privmsg irc-command-string ( privmsg -- string ) drop "PRIVMSG" ;
|
M: privmsg command-string>> ( privmsg -- string ) drop "PRIVMSG" ;
|
||||||
M: notice irc-command-string ( notice -- string ) drop "NOTICE" ;
|
M: notice command-string>> ( notice -- string ) drop "NOTICE" ;
|
||||||
M: mode irc-command-string ( mode -- string ) drop "MODE" ;
|
M: mode command-string>> ( mode -- string ) drop "MODE" ;
|
||||||
M: kick irc-command-string ( kick -- string ) drop "KICK" ;
|
M: kick command-string>> ( kick -- string ) drop "KICK" ;
|
||||||
|
|
||||||
GENERIC: irc-command-parameters ( irc-message -- seq )
|
GENERIC: command-parameters>> ( irc-message -- seq )
|
||||||
|
|
||||||
M: irc-message irc-command-parameters ( irc-message -- seq ) parameters>> ;
|
M: irc-message command-parameters>> ( irc-message -- seq ) parameters>> ;
|
||||||
M: ping irc-command-parameters ( ping -- seq ) drop { } ;
|
M: ping command-parameters>> ( ping -- seq ) drop { } ;
|
||||||
M: join irc-command-parameters ( join -- seq ) drop { } ;
|
M: join command-parameters>> ( join -- seq ) drop { } ;
|
||||||
M: part irc-command-parameters ( part -- seq ) channel>> 1array ;
|
M: part command-parameters>> ( part -- seq ) channel>> 1array ;
|
||||||
M: quit irc-command-parameters ( quit -- seq ) drop { } ;
|
M: quit command-parameters>> ( quit -- seq ) drop { } ;
|
||||||
M: nick irc-command-parameters ( nick -- seq ) drop { } ;
|
M: nick command-parameters>> ( nick -- seq ) drop { } ;
|
||||||
M: privmsg irc-command-parameters ( privmsg -- seq ) name>> 1array ;
|
M: privmsg command-parameters>> ( privmsg -- seq ) name>> 1array ;
|
||||||
M: notice irc-command-parameters ( norice -- seq ) type>> 1array ;
|
M: notice command-parameters>> ( norice -- seq ) type>> 1array ;
|
||||||
M: kick irc-command-parameters ( kick -- seq )
|
M: kick command-parameters>> ( kick -- seq )
|
||||||
[ channel>> ] [ who>> ] bi 2array ;
|
[ channel>> ] [ who>> ] bi 2array ;
|
||||||
M: mode irc-command-parameters ( mode -- seq )
|
M: mode command-parameters>> ( mode -- seq )
|
||||||
[ name>> ] [ channel>> ] [ mode>> ] tri 3array ;
|
[ name>> ] [ channel>> ] [ mode>> ] tri 3array ;
|
||||||
|
|
||||||
|
GENERIC: (>>command-parameters) ( params irc-message -- )
|
||||||
|
|
||||||
|
M: irc-message (>>command-parameters) ( params irc-message -- ) 2drop ;
|
||||||
|
M: logged-in (>>command-parameters) ( params part -- ) [ first ] dip (>>name) ;
|
||||||
|
M: privmsg (>>command-parameters) ( params privmsg -- ) [ first ] dip (>>name) ;
|
||||||
|
M: notice (>>command-parameters) ( params notice -- ) [ first ] dip (>>type) ;
|
||||||
|
M: part (>>command-parameters) ( params part -- )
|
||||||
|
[ first ] dip (>>channel) ;
|
||||||
|
M: kick (>>command-parameters) ( params kick -- )
|
||||||
|
[ first2 ] dip [ (>>who) ] [ (>>channel) ] bi ;
|
||||||
|
M: names-reply (>>command-parameters) ( params names-reply -- )
|
||||||
|
[ [ first ] dip (>>who) ] [ [ third ] dip (>>channel) ] 2bi ;
|
||||||
|
M: mode (>>command-parameters) ( params mode -- )
|
||||||
|
{ { [ >r 2array r> ] [ [ (>>mode) ] [ (>>name) ] bi ] }
|
||||||
|
{ [ >r 3array r> ] [ [ (>>parameter) ] [ (>>mode) ] [ (>>name) ] tri ] }
|
||||||
|
} switch ;
|
||||||
|
|
||||||
PRIVATE>
|
PRIVATE>
|
||||||
|
|
||||||
GENERIC: irc-message>client-line ( irc-message -- string )
|
GENERIC: irc-message>client-line ( irc-message -- string )
|
||||||
|
|
||||||
M: irc-message irc-message>client-line ( irc-message -- string )
|
M: irc-message irc-message>client-line ( irc-message -- string )
|
||||||
[ irc-command-string ]
|
[ command-string>> ]
|
||||||
[ irc-command-parameters " " sjoin ]
|
[ command-parameters>> " " sjoin ]
|
||||||
[ trailing>> [ CHAR: : prefix ] [ "" ] if* ]
|
[ trailing>> [ CHAR: : prefix ] [ "" ] if* ]
|
||||||
tri 3array " " sjoin ;
|
tri 3array " " sjoin ;
|
||||||
|
|
||||||
|
@ -77,10 +95,7 @@ M: irc-message irc-message>server-line ( irc-message -- string )
|
||||||
! ======================================
|
! ======================================
|
||||||
|
|
||||||
: split-at-first ( seq separators -- before after )
|
: split-at-first ( seq separators -- before after )
|
||||||
dupd '[ _ member? ] find
|
dupd '[ _ member? ] find [ cut 1 tail ] [ swap ] if ;
|
||||||
[ cut 1 tail ]
|
|
||||||
[ swap ]
|
|
||||||
if ;
|
|
||||||
|
|
||||||
: remove-heading-: ( seq -- seq ) dup ":" head? [ 1 tail ] when ;
|
: remove-heading-: ( seq -- seq ) dup ":" head? [ 1 tail ] when ;
|
||||||
|
|
||||||
|
@ -96,6 +111,15 @@ M: irc-message irc-message>server-line ( irc-message -- string )
|
||||||
: split-trailing ( string -- string string/f )
|
: split-trailing ( string -- string string/f )
|
||||||
":" split1 ;
|
":" split1 ;
|
||||||
|
|
||||||
|
: copy-message-in ( origin dest -- )
|
||||||
|
{ [ [ parameters>> ] dip [ (>>command-parameters) ] [ (>>parameters) ] 2bi ]
|
||||||
|
[ [ line>> ] dip (>>line) ]
|
||||||
|
[ [ prefix>> ] dip (>>prefix) ]
|
||||||
|
[ [ command>> ] dip (>>command) ]
|
||||||
|
[ [ trailing>> ] dip (>>trailing) ]
|
||||||
|
[ [ timestamp>> ] dip (>>timestamp) ]
|
||||||
|
} 2cleave ;
|
||||||
|
|
||||||
PRIVATE>
|
PRIVATE>
|
||||||
|
|
||||||
UNION: sender-in-prefix privmsg join part quit kick mode nick ;
|
UNION: sender-in-prefix privmsg join part quit kick mode nick ;
|
||||||
|
@ -124,7 +148,4 @@ M: sender-in-prefix irc-message-sender ( sender-in-prefix -- sender )
|
||||||
{ "MODE" [ mode ] }
|
{ "MODE" [ mode ] }
|
||||||
{ "KICK" [ kick ] }
|
{ "KICK" [ kick ] }
|
||||||
[ drop unhandled ]
|
[ drop unhandled ]
|
||||||
} case
|
} case new [ copy-message-in ] keep ;
|
||||||
[ [ tuple-slots ] [ parameters>> ] bi append ] dip
|
|
||||||
[ all-slots over [ length ] bi@ min head >quotation ] keep
|
|
||||||
'[ @ _ boa ] call ;
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
John Benediktsson
|
|
@ -0,0 +1,74 @@
|
||||||
|
|
||||||
|
USING: help.syntax help.markup kernel prettyprint sequences strings ;
|
||||||
|
|
||||||
|
IN: printf
|
||||||
|
|
||||||
|
HELP: printf
|
||||||
|
{ $values { "format-string" string } }
|
||||||
|
{ $description "Writes the arguments (specified on the stack) formatted according to the format string." }
|
||||||
|
{ $examples
|
||||||
|
{ $example
|
||||||
|
"USING: printf ;"
|
||||||
|
"123 \"%05d\" printf"
|
||||||
|
"00123" }
|
||||||
|
{ $example
|
||||||
|
"USING: printf ;"
|
||||||
|
"HEX: ff \"%04X\" printf"
|
||||||
|
"00FF" }
|
||||||
|
{ $example
|
||||||
|
"USING: printf ;"
|
||||||
|
"1.23456789 \"%.3f\" printf"
|
||||||
|
"1.234" }
|
||||||
|
{ $example
|
||||||
|
"USING: printf ;"
|
||||||
|
"1234567890 \"%.5e\" printf"
|
||||||
|
"1.23456e+09" }
|
||||||
|
{ $example
|
||||||
|
"USING: printf ;"
|
||||||
|
"12 \"%'#4d\" printf"
|
||||||
|
"##12" }
|
||||||
|
{ $example
|
||||||
|
"USING: printf ;"
|
||||||
|
"1234 \"%+d\" printf"
|
||||||
|
"+1234" }
|
||||||
|
} ;
|
||||||
|
|
||||||
|
HELP: sprintf
|
||||||
|
{ $values { "format-string" string } { "result" string } }
|
||||||
|
{ $description "Returns the arguments (specified on the stack) formatted according to the format string as a result string." }
|
||||||
|
{ $see-also printf } ;
|
||||||
|
|
||||||
|
ARTICLE: "printf" "Formatted printing"
|
||||||
|
"The " { $vocab-link "printf" } " and " { $vocab-link "sprintf" } " words are used for formatted printing.\n"
|
||||||
|
"\n"
|
||||||
|
"Several format specifications exist for handling arguments of different types, and specifying attributes for the result string, including such things as maximum width, padding, and decimals.\n"
|
||||||
|
{ $table
|
||||||
|
{ "%%" "Single %" "" }
|
||||||
|
{ "%P.Ds" "String format" "string" }
|
||||||
|
{ "%P.DS" "String format uppercase" "string" }
|
||||||
|
{ "%c" "Character format" "char" }
|
||||||
|
{ "%C" "Character format uppercase" "char" }
|
||||||
|
{ "%+Pd" "Integer format" "fixnum" }
|
||||||
|
{ "%+P.De" "Scientific notation" "fixnum, float" }
|
||||||
|
{ "%+P.DE" "Scientific notation" "fixnum, float" }
|
||||||
|
{ "%+P.Df" "Fixed format" "fixnum, float" }
|
||||||
|
{ "%+Px" "Hexadecimal" "hex" }
|
||||||
|
{ "%+PX" "Hexadecimal uppercase" "hex" }
|
||||||
|
}
|
||||||
|
"\n"
|
||||||
|
"A plus sign ('+') is used to optionally specify that the number should be formatted with a '+' preceeding it if positive.\n"
|
||||||
|
"\n"
|
||||||
|
"Padding ('P') is used to optionally specify the minimum width of the result string, the padding character, and the alignment. By default, the padding character defaults to a space and the alignment defaults to right-aligned. For example:\n"
|
||||||
|
{ $list
|
||||||
|
"\"%5s\" formats a string padding with spaces up to 5 characters wide."
|
||||||
|
"\"%08d\" formats an integer padding with zeros up to 3 characters wide."
|
||||||
|
"\"%'#5f\" formats a float padding with '#' up to 3 characters wide."
|
||||||
|
"\"%-10d\" formats an integer to 10 characters wide and left-aligns."
|
||||||
|
}
|
||||||
|
"\n"
|
||||||
|
"Digits ('D') is used to optionally specify the maximum digits in the result string. For example:\n"
|
||||||
|
{ $list
|
||||||
|
"\"%.3s\" formats a string to truncate at 3 characters (from the left)."
|
||||||
|
"\"%.10f\" formats a float to pad-right with zeros up to 10 digits beyond the decimal point."
|
||||||
|
"\"%.5E\" formats a float into scientific notation with zeros up to 5 digits beyond the decimal point, but before the exponent."
|
||||||
|
} ;
|
|
@ -0,0 +1,126 @@
|
||||||
|
! Copyright (C) 2008 John Benediktsson
|
||||||
|
! See http://factorcode.org/license.txt for BSD license
|
||||||
|
|
||||||
|
USING: kernel printf tools.test ;
|
||||||
|
|
||||||
|
[ "%s" printf ] must-infer
|
||||||
|
|
||||||
|
[ "%s" sprintf ] must-infer
|
||||||
|
|
||||||
|
[ t ] [ "10" 10 "%d" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "+10" 10 "%+d" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "-10" -10 "%d" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ " -10" -10 "%5d" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "-0010" -10 "%05d" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "+0010" 10 "%+05d" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "123.456" 123.456 "%f" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "123.10" 123.1 "%01.2f" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "1.2345" 1.23456789 "%.4f" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ " 1.23" 1.23456789 "%6.2f" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "1.234e+08" 123400000 "%e" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "-1.234e+08" -123400000 "%e" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "1.234567e+08" 123456700 "%e" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "3.625e+08" 362525200 "%.3e" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "2.5e-03" 0.0025 "%e" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "2.5E-03" 0.0025 "%E" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ " 1.0E+01" 10 "%10.1E" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ " -1.0E+01" -10 "%10.1E" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ " -1.0E+01" -10 "%+10.1E" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ " +1.0E+01" 10 "%+10.1E" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "-001.0E+01" -10 "%+010.1E" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "+001.0E+01" 10 "%+010.1E" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "ff" HEX: ff "%x" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "FF" HEX: ff "%X" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "0f" HEX: f "%02x" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "0F" HEX: f "%02X" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "2008-09-10"
|
||||||
|
2008 9 10 "%04d-%02d-%02d" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "Hello, World!"
|
||||||
|
"Hello, World!" "%s" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "printf test"
|
||||||
|
"printf test" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "char a = 'a'"
|
||||||
|
CHAR: a "char %c = 'a'" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "00" HEX: 0 "%02x" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "ff" HEX: ff "%02x" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "0 message(s)"
|
||||||
|
0 "message" "%d %s(s)" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "0 message(s) with %"
|
||||||
|
0 "message" "%d %s(s) with %%" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "justif: \"left \""
|
||||||
|
"left" "justif: \"%-10s\"" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "justif: \" right\""
|
||||||
|
"right" "justif: \"%10s\"" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ " 3: 0003 zero padded"
|
||||||
|
3 " 3: %04d zero padded" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ " 3: 3 left justif"
|
||||||
|
3 " 3: %-4d left justif" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ " 3: 3 right justif"
|
||||||
|
3 " 3: %4d right justif" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ " -3: -003 zero padded"
|
||||||
|
-3 " -3: %04d zero padded" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ " -3: -3 left justif"
|
||||||
|
-3 " -3: %-4d left justif" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ " -3: -3 right justif"
|
||||||
|
-3 " -3: %4d right justif" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "There are 10 monkeys in the kitchen"
|
||||||
|
10 "kitchen" "There are %d monkeys in the %s" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ f ] [ "%d" 10 "%d" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "[monkey]" "monkey" "[%s]" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "[ monkey]" "monkey" "[%10s]" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "[monkey ]" "monkey" "[%-10s]" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "[0000monkey]" "monkey" "[%010s]" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "[####monkey]" "monkey" "[%'#10s]" sprintf = ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ "[many monke]" "many monkeys" "[%10.10s]" sprintf = ] unit-test
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
! Copyright (C) 2008 John Benediktsson
|
||||||
|
! See http://factorcode.org/license.txt for BSD license
|
||||||
|
|
||||||
|
USING: io io.encodings.ascii io.files io.streams.string combinators
|
||||||
|
kernel sequences splitting strings math math.parser macros
|
||||||
|
fry peg.ebnf ascii unicode.case arrays quotations vectors ;
|
||||||
|
|
||||||
|
IN: printf
|
||||||
|
|
||||||
|
<PRIVATE
|
||||||
|
|
||||||
|
: compose-all ( seq -- quot )
|
||||||
|
[ ] [ compose ] reduce ;
|
||||||
|
|
||||||
|
: fix-sign ( string -- string )
|
||||||
|
dup CHAR: 0 swap index 0 =
|
||||||
|
[ dup 0 swap [ [ CHAR: 0 = not ] keep digit? and ] find-from
|
||||||
|
[ dup 1- rot dup [ nth ] dip swap
|
||||||
|
{
|
||||||
|
{ CHAR: - [ [ 1- ] dip remove-nth "-" prepend ] }
|
||||||
|
{ CHAR: + [ [ 1- ] dip remove-nth "+" prepend ] }
|
||||||
|
[ drop swap drop ]
|
||||||
|
} case
|
||||||
|
] [ drop ] if
|
||||||
|
] when ;
|
||||||
|
|
||||||
|
: >digits ( string -- digits )
|
||||||
|
[ 0 ] [ string>number ] if-empty ;
|
||||||
|
|
||||||
|
: max-digits ( string digits -- string )
|
||||||
|
[ "." split1 ] dip [ CHAR: 0 pad-right ] [ head-slice ] bi "." swap 3append ;
|
||||||
|
|
||||||
|
: max-width ( string length -- string )
|
||||||
|
short head ;
|
||||||
|
|
||||||
|
: >exponential ( n -- base exp )
|
||||||
|
[ 0 < ] keep abs 0
|
||||||
|
[ swap dup [ 10.0 >= ] keep 1.0 < or ]
|
||||||
|
[ dup 10.0 >=
|
||||||
|
[ 10.0 / [ 1+ ] dip swap ]
|
||||||
|
[ 10.0 * [ 1- ] dip swap ] if
|
||||||
|
] [ swap ] while
|
||||||
|
[ number>string ] dip
|
||||||
|
dup abs number>string 2 CHAR: 0 pad-left
|
||||||
|
[ 0 < "-" "+" ? ] dip append
|
||||||
|
"e" prepend
|
||||||
|
rot [ [ "-" prepend ] dip ] when ;
|
||||||
|
|
||||||
|
EBNF: parse-format-string
|
||||||
|
|
||||||
|
zero = "0" => [[ CHAR: 0 ]]
|
||||||
|
char = "'" (.) => [[ second ]]
|
||||||
|
|
||||||
|
pad-char = (zero|char)? => [[ CHAR: \s or 1quotation ]]
|
||||||
|
pad-align = ("-")? => [[ [ pad-right ] [ pad-left ] ? ]]
|
||||||
|
pad-width = ([0-9])* => [[ >digits 1quotation ]]
|
||||||
|
pad = pad-align pad-char pad-width => [[ reverse compose-all [ first ] keep swap 0 = [ drop [ ] ] when ]]
|
||||||
|
|
||||||
|
sign = ("+")? => [[ [ dup CHAR: - swap index not [ "+" prepend ] when ] [ ] ? ]]
|
||||||
|
|
||||||
|
width_ = "." ([0-9])* => [[ second >digits '[ _ max-width ] ]]
|
||||||
|
width = (width_)? => [[ [ ] or ]]
|
||||||
|
|
||||||
|
digits_ = "." ([0-9])* => [[ second >digits '[ _ max-digits ] ]]
|
||||||
|
digits = (digits_)? => [[ [ ] or ]]
|
||||||
|
|
||||||
|
fmt-% = "%" => [[ [ "%" ] ]]
|
||||||
|
fmt-c = "c" => [[ [ 1string ] ]]
|
||||||
|
fmt-C = "C" => [[ [ 1string >upper ] ]]
|
||||||
|
fmt-s = "s" => [[ [ ] ]]
|
||||||
|
fmt-S = "S" => [[ [ >upper ] ]]
|
||||||
|
fmt-d = "d" => [[ [ >fixnum number>string ] ]]
|
||||||
|
fmt-e = "e" => [[ [ >exponential ] ]]
|
||||||
|
fmt-E = "E" => [[ [ >exponential >upper ] ]]
|
||||||
|
fmt-f = "f" => [[ [ >float number>string ] ]]
|
||||||
|
fmt-x = "x" => [[ [ >hex ] ]]
|
||||||
|
fmt-X = "X" => [[ [ >hex >upper ] ]]
|
||||||
|
unknown = (.)* => [[ "Unknown directive" throw ]]
|
||||||
|
|
||||||
|
chars = fmt-c | fmt-C
|
||||||
|
strings = pad width (fmt-s|fmt-S) => [[ reverse compose-all ]]
|
||||||
|
decimals = fmt-d
|
||||||
|
exps = digits (fmt-e|fmt-E) => [[ reverse [ swap ] join [ swap append ] append ]]
|
||||||
|
floats = digits fmt-f => [[ reverse compose-all ]]
|
||||||
|
hex = fmt-x | fmt-X
|
||||||
|
numbers = sign pad (decimals|floats|hex|exps) => [[ reverse first3 swap 3append [ fix-sign ] append ]]
|
||||||
|
|
||||||
|
formats = "%" (chars|strings|numbers|fmt-%|unknown) => [[ second '[ _ dip ] ]]
|
||||||
|
|
||||||
|
plain-text = (!("%").)+ => [[ >string '[ _ swap ] ]]
|
||||||
|
|
||||||
|
text = (formats|plain-text)* => [[ reverse [ [ dup [ push ] dip ] append ] map ]]
|
||||||
|
|
||||||
|
;EBNF
|
||||||
|
|
||||||
|
PRIVATE>
|
||||||
|
|
||||||
|
MACRO: printf ( format-string -- )
|
||||||
|
parse-format-string [ length ] keep compose-all '[ _ <vector> @ reverse [ write ] each ] ;
|
||||||
|
|
||||||
|
: sprintf ( format-string -- result )
|
||||||
|
[ printf ] with-string-writer ; inline
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Format data according to a specified format string, and writes (or returns) the result string.
|
|
@ -1,5 +1,5 @@
|
||||||
This Wiki uses [[Farkup]] to mark up text.
|
This Wiki uses [[Farkup]] to mark up text.
|
||||||
|
|
||||||
Two special article names are recognized by the Wiki: [[Sidebar]] and [[Footer]]. They do not exist by default, but if you create them, they will be visible on every page.
|
Two special article names are recognized by the Wiki: [[Contents]] and [[Footer]]. They do not exist by default, but if you create them, they will be visible on every page.
|
||||||
|
|
||||||
The Wiki supports hierarchical article organization. You can separate components in article names with slashes, and Wiki links only display the last component. An example: [[Factor/Features]].
|
The Wiki supports hierarchical article organization. You can separate components in article names with slashes, and Wiki links only display the last component. An example: [[Factor/Features]].
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<t:title><t:label t:name="title" /></t:title>
|
<t:title><t:label t:name="title" /></t:title>
|
||||||
|
|
||||||
<div class="description">
|
<div class="description">
|
||||||
<t:html t:name="html" />
|
<t:farkup t:name="parsed" t:parsed="true" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -10,17 +10,17 @@
|
||||||
|
|
||||||
<table width="100%">
|
<table width="100%">
|
||||||
<tr>
|
<tr>
|
||||||
<t:if t:value="sidebar">
|
<t:if t:value="contents">
|
||||||
<td valign="top" style="width: 210px;">
|
<td valign="top" style="width: 210px;">
|
||||||
<div class="sidebar">
|
<div class="contents">
|
||||||
<t:bind t:name="sidebar">
|
<t:bind t:name="contents">
|
||||||
<h2>
|
<h2>
|
||||||
<t:a t:href="$wiki/view" t:query="title">
|
<t:a t:href="$wiki/view" t:query="title">
|
||||||
<t:label t:name="title" />
|
<t:label t:name="title" />
|
||||||
</t:a>
|
</t:a>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<t:html t:name="html" />
|
<t:farkup t:name="parsed" t:parsed="true" />
|
||||||
</t:bind>
|
</t:bind>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
@ -59,7 +59,7 @@
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<t:bind t:name="footer">
|
<t:bind t:name="footer">
|
||||||
<small>
|
<small>
|
||||||
<t:html t:name="html" />
|
<t:farkup t:name="parsed" t:parsed="true" />
|
||||||
</small>
|
</small>
|
||||||
</t:bind>
|
</t:bind>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
border-width: 1px 1px 0 0;
|
border-width: 1px 1px 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
.contents {
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
border: 1px dashed grey;
|
border: 1px dashed grey;
|
||||||
|
|
|
@ -47,7 +47,7 @@ article "ARTICLES" {
|
||||||
|
|
||||||
: <article> ( title -- article ) article new swap >>title ;
|
: <article> ( title -- article ) article new swap >>title ;
|
||||||
|
|
||||||
TUPLE: revision id title author date content html description ;
|
TUPLE: revision id title author date content parsed description ;
|
||||||
|
|
||||||
revision "REVISIONS" {
|
revision "REVISIONS" {
|
||||||
{ "id" "ID" INTEGER +db-assigned-id+ }
|
{ "id" "ID" INTEGER +db-assigned-id+ }
|
||||||
|
@ -55,7 +55,7 @@ revision "REVISIONS" {
|
||||||
{ "author" "AUTHOR" { VARCHAR 256 } +not-null+ } ! uid
|
{ "author" "AUTHOR" { VARCHAR 256 } +not-null+ } ! uid
|
||||||
{ "date" "DATE" TIMESTAMP +not-null+ }
|
{ "date" "DATE" TIMESTAMP +not-null+ }
|
||||||
{ "content" "CONTENT" TEXT +not-null+ }
|
{ "content" "CONTENT" TEXT +not-null+ }
|
||||||
{ "html" "HTML" TEXT +not-null+ } ! Farkup converted to HTML
|
{ "parsed" "PARSED" FACTOR-BLOB +not-null+ } ! Farkup AST
|
||||||
{ "description" "DESCRIPTION" TEXT }
|
{ "description" "DESCRIPTION" TEXT }
|
||||||
} define-persistent
|
} define-persistent
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ M: revision feed-entry-url id>> revision-url ;
|
||||||
revision new swap >>id ;
|
revision new swap >>id ;
|
||||||
|
|
||||||
: compute-html ( revision -- )
|
: compute-html ( revision -- )
|
||||||
dup content>> convert-farkup >>html drop ;
|
dup content>> parse-farkup >>parsed drop ;
|
||||||
|
|
||||||
: validate-title ( -- )
|
: validate-title ( -- )
|
||||||
{ { "title" [ v-one-line ] } } validate-params ;
|
{ { "title" [ v-one-line ] } } validate-params ;
|
||||||
|
@ -344,10 +344,13 @@ M: revision feed-entry-url id>> revision-url ;
|
||||||
[ "author" value user-edits-url ] >>url
|
[ "author" value user-edits-url ] >>url
|
||||||
[ list-user-edits ] >>entries ;
|
[ list-user-edits ] >>entries ;
|
||||||
|
|
||||||
: init-sidebar ( -- )
|
: init-sidebars ( -- )
|
||||||
"Sidebar" latest-revision [ "sidebar" [ from-object ] nest-form ] when*
|
"Contents" latest-revision [ "contents" [ from-object ] nest-form ] when*
|
||||||
"Footer" latest-revision [ "footer" [ from-object ] nest-form ] when* ;
|
"Footer" latest-revision [ "footer" [ from-object ] nest-form ] when* ;
|
||||||
|
|
||||||
|
: init-relative-link-prefix ( -- )
|
||||||
|
URL" $wiki/view/" adjust-url present relative-link-prefix set ;
|
||||||
|
|
||||||
: <wiki> ( -- dispatcher )
|
: <wiki> ( -- dispatcher )
|
||||||
wiki new-dispatcher
|
wiki new-dispatcher
|
||||||
<main-article-action> "" add-responder
|
<main-article-action> "" add-responder
|
||||||
|
@ -367,7 +370,7 @@ M: revision feed-entry-url id>> revision-url ;
|
||||||
<list-changes-feed-action> "changes.atom" add-responder
|
<list-changes-feed-action> "changes.atom" add-responder
|
||||||
<delete-action> "delete" add-responder
|
<delete-action> "delete" add-responder
|
||||||
<boilerplate>
|
<boilerplate>
|
||||||
[ init-sidebar ] >>init
|
[ init-sidebars init-relative-link-prefix ] >>init
|
||||||
{ wiki "wiki-common" } >>template ;
|
{ wiki "wiki-common" } >>template ;
|
||||||
|
|
||||||
: init-wiki ( -- )
|
: init-wiki ( -- )
|
||||||
|
|
|
@ -111,6 +111,7 @@
|
||||||
(use-local-map factor-mode-map)
|
(use-local-map factor-mode-map)
|
||||||
(setq major-mode 'factor-mode)
|
(setq major-mode 'factor-mode)
|
||||||
(setq mode-name "Factor")
|
(setq mode-name "Factor")
|
||||||
|
(set (make-local-variable 'indent-line-function) #'factor-indent-line)
|
||||||
(make-local-variable 'comment-start)
|
(make-local-variable 'comment-start)
|
||||||
(setq comment-start "! ")
|
(setq comment-start "! ")
|
||||||
(make-local-variable 'font-lock-defaults)
|
(make-local-variable 'font-lock-defaults)
|
||||||
|
@ -224,6 +225,73 @@
|
||||||
(define-key factor-mode-map "\C-c\C-h" 'factor-help)
|
(define-key factor-mode-map "\C-c\C-h" 'factor-help)
|
||||||
(define-key factor-mode-map "\C-cc" 'comment-region)
|
(define-key factor-mode-map "\C-cc" 'comment-region)
|
||||||
(define-key factor-mode-map [return] 'newline-and-indent)
|
(define-key factor-mode-map [return] 'newline-and-indent)
|
||||||
|
(define-key factor-mode-map [tab] 'indent-for-tab-command)
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; indentation
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(defconst factor-word-starting-keywords
|
||||||
|
'("" ":" "TUPLE" "MACRO" "MACRO:" "M"))
|
||||||
|
|
||||||
|
(defmacro factor-word-start-re (keywords)
|
||||||
|
`(format
|
||||||
|
"^\\(%s\\): "
|
||||||
|
(mapconcat 'identity ,keywords "\\|")))
|
||||||
|
|
||||||
|
(defun factor-calculate-indentation ()
|
||||||
|
"Calculate Factor indentation for line at point."
|
||||||
|
(let ((not-indented t)
|
||||||
|
(cur-indent 0))
|
||||||
|
(save-excursion
|
||||||
|
(beginning-of-line)
|
||||||
|
(if (bobp)
|
||||||
|
(setq cur-indent 0)
|
||||||
|
(save-excursion
|
||||||
|
(while not-indented
|
||||||
|
;; Check that we are inside open brackets
|
||||||
|
(save-excursion
|
||||||
|
(let ((cur-depth (factor-brackets-depth)))
|
||||||
|
(forward-line -1)
|
||||||
|
(setq cur-indent (+ (current-indentation)
|
||||||
|
(* default-tab-width
|
||||||
|
(- cur-depth (factor-brackets-depth)))))
|
||||||
|
(setq not-indented nil)))
|
||||||
|
(forward-line -1)
|
||||||
|
;; Check that we are after the end of previous word
|
||||||
|
(if (looking-at ".*;[ \t]*$")
|
||||||
|
(progn
|
||||||
|
(setq cur-indent (- (current-indentation) default-tab-width))
|
||||||
|
(setq not-indented nil))
|
||||||
|
;; Check that we are after the start of word
|
||||||
|
(if (looking-at (factor-word-start-re factor-word-starting-keywords))
|
||||||
|
; (if (looking-at "^[A-Z:]*: ")
|
||||||
|
(progn
|
||||||
|
(message "inword")
|
||||||
|
(setq cur-indent (+ (current-indentation) default-tab-width))
|
||||||
|
(setq not-indented nil))
|
||||||
|
(if (bobp)
|
||||||
|
(setq not-indented nil))))))))
|
||||||
|
cur-indent))
|
||||||
|
|
||||||
|
(defun factor-brackets-depth ()
|
||||||
|
"Returns number of brackets, not closed on previous lines."
|
||||||
|
(syntax-ppss-depth
|
||||||
|
(save-excursion
|
||||||
|
(syntax-ppss (line-beginning-position)))))
|
||||||
|
|
||||||
|
(defun factor-indent-line ()
|
||||||
|
"Indent current line as Factor code"
|
||||||
|
(let ((target (factor-calculate-indentation))
|
||||||
|
(pos (- (point-max) (point))))
|
||||||
|
(if (= target (current-indentation))
|
||||||
|
(if (< (current-column) (current-indentation))
|
||||||
|
(back-to-indentation))
|
||||||
|
(beginning-of-line)
|
||||||
|
(delete-horizontal-space)
|
||||||
|
(indent-to target)
|
||||||
|
(if (> (- (point-max) pos) (point))
|
||||||
|
(goto-char (- (point-max) pos))))))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; factor-listener-mode
|
;; factor-listener-mode
|
||||||
|
@ -244,5 +312,3 @@
|
||||||
(defun factor-refresh-all ()
|
(defun factor-refresh-all ()
|
||||||
(interactive)
|
(interactive)
|
||||||
(comint-send-string "*factor*" "refresh-all\n"))
|
(comint-send-string "*factor*" "refresh-all\n"))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -131,18 +131,18 @@ syn region factorStackEffect start=/\<(\>/ end=/\<)\>/ contained
|
||||||
|
|
||||||
"adapted from lisp.vim
|
"adapted from lisp.vim
|
||||||
if exists("g:factor_norainbow")
|
if exists("g:factor_norainbow")
|
||||||
syn region factorQuotation matchgroup=factorDelimiter start=/\<\[\>/ matchgroup=factorDelimiter end=/\<\]\>/ contains=ALL
|
syn region factorQuotation0 matchgroup=factorDelimiter start=/\<\'\?\[\>/ matchgroup=factorDelimiter end=/\<\]\>/ contains=ALL
|
||||||
else
|
else
|
||||||
syn region factorQuotation0 matchgroup=hlLevel0 start=/\<\[\>/ end=/\<\]\>/ contains=@factorCluster,factorQuotation1,factorArray1
|
syn region factorQuotation0 matchgroup=hlLevel0 start=/\<\'\?\[\>/ end=/\<\]\>/ contains=@factorCluster,factorQuotation1,factorArray1
|
||||||
syn region factorQuotation1 contained matchgroup=hlLevel1 start=/\<\[\>/ end=/\<\]\>/ contains=@factorCluster,factorQuotation2,factorArray2
|
syn region factorQuotation1 contained matchgroup=hlLevel1 start=/\<\'\?\[\>/ end=/\<\]\>/ contains=@factorCluster,factorQuotation2,factorArray2
|
||||||
syn region factorQuotation2 contained matchgroup=hlLevel2 start=/\<\[\>/ end=/\<\]\>/ contains=@factorCluster,factorQuotation3,factorArray3
|
syn region factorQuotation2 contained matchgroup=hlLevel2 start=/\<\'\?\[\>/ end=/\<\]\>/ contains=@factorCluster,factorQuotation3,factorArray3
|
||||||
syn region factorQuotation3 contained matchgroup=hlLevel3 start=/\<\[\>/ end=/\<\]\>/ contains=@factorCluster,factorQuotation4,factorArray4
|
syn region factorQuotation3 contained matchgroup=hlLevel3 start=/\<\'\?\[\>/ end=/\<\]\>/ contains=@factorCluster,factorQuotation4,factorArray4
|
||||||
syn region factorQuotation4 contained matchgroup=hlLevel4 start=/\<\[\>/ end=/\<\]\>/ contains=@factorCluster,factorQuotation5,factorArray5
|
syn region factorQuotation4 contained matchgroup=hlLevel4 start=/\<\'\?\[\>/ end=/\<\]\>/ contains=@factorCluster,factorQuotation5,factorArray5
|
||||||
syn region factorQuotation5 contained matchgroup=hlLevel5 start=/\<\[\>/ end=/\<\]\>/ contains=@factorCluster,factorQuotation6,factorArray6
|
syn region factorQuotation5 contained matchgroup=hlLevel5 start=/\<\'\?\[\>/ end=/\<\]\>/ contains=@factorCluster,factorQuotation6,factorArray6
|
||||||
syn region factorQuotation6 contained matchgroup=hlLevel6 start=/\<\[\>/ end=/\<\]\>/ contains=@factorCluster,factorQuotation7,factorArray7
|
syn region factorQuotation6 contained matchgroup=hlLevel6 start=/\<\'\?\[\>/ end=/\<\]\>/ contains=@factorCluster,factorQuotation7,factorArray7
|
||||||
syn region factorQuotation7 contained matchgroup=hlLevel7 start=/\<\[\>/ end=/\<\]\>/ contains=@factorCluster,factorQuotation8,factorArray8
|
syn region factorQuotation7 contained matchgroup=hlLevel7 start=/\<\'\?\[\>/ end=/\<\]\>/ contains=@factorCluster,factorQuotation8,factorArray8
|
||||||
syn region factorQuotation8 contained matchgroup=hlLevel8 start=/\<\[\>/ end=/\<\]\>/ contains=@factorCluster,factorQuotation9,factorArray9
|
syn region factorQuotation8 contained matchgroup=hlLevel8 start=/\<\'\?\[\>/ end=/\<\]\>/ contains=@factorCluster,factorQuotation9,factorArray9
|
||||||
syn region factorQuotation9 contained matchgroup=hlLevel9 start=/\<\[\>/ end=/\<\]\>/ contains=@factorCluster,factorQuotation0,factorArray0
|
syn region factorQuotation9 contained matchgroup=hlLevel9 start=/\<\'\?\[\>/ end=/\<\]\>/ contains=@factorCluster,factorQuotation0,factorArray0
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if exists("g:factor_norainbow")
|
if exists("g:factor_norainbow")
|
||||||
|
|
Loading…
Reference in New Issue