Adding 'ini-file' vocab to parse INI config files.
parent
b1c1ca0e74
commit
e592e3ede6
|
@ -0,0 +1 @@
|
|||
John Benediktsson
|
|
@ -0,0 +1,36 @@
|
|||
! Copyright (C) 2010 John Benediktsson
|
||||
! See http://factorcode.org/license.txt for BSD license
|
||||
|
||||
USING: assocs hashtables help.syntax help.markup io strings ;
|
||||
|
||||
IN: ini-file
|
||||
|
||||
HELP: read-ini
|
||||
{ $values { "assoc" assoc } }
|
||||
{ $description
|
||||
"Reads and parses an INI configuration from the " { $link input-stream }
|
||||
" and returns the result as a nested " { $link hashtable }
|
||||
"."
|
||||
} ;
|
||||
|
||||
HELP: write-ini
|
||||
{ $values { "assoc" assoc } }
|
||||
{ $description
|
||||
"Writes a configuration to the " { $link output-stream }
|
||||
" in the INI format."
|
||||
} ;
|
||||
|
||||
HELP: string>ini
|
||||
{ $values { "str" string } { "assoc" assoc } }
|
||||
{ $description
|
||||
"Parses the specified " { $link string } " as an INI configuration"
|
||||
" and returns the result as a nested " { $link hashtable }
|
||||
"."
|
||||
} ;
|
||||
|
||||
HELP: ini>string
|
||||
{ $values { "assoc" assoc } { "str" string } }
|
||||
{ $description
|
||||
"Encodes the specified " { $link hashtable } " as an INI configuration."
|
||||
} ;
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
! Copyright (C) 2010 John Benediktsson
|
||||
! See http://factorcode.org/license.txt for BSD license
|
||||
|
||||
USING: ini-file tools.test ;
|
||||
|
||||
IN: ini-file.tests
|
||||
|
||||
[ H{ } ] [ "" string>ini ] unit-test
|
||||
|
||||
[ H{ { "section" H{ } } } ] [ "[section]" string>ini ] unit-test
|
||||
|
||||
[ H{ { "section" H{ } } } ] [ "[\"section\" ]" string>ini ] unit-test
|
||||
|
||||
[ H{ { " some name with spaces " H{ } } } ]
|
||||
[ "[ \" some name with spaces \"]" string>ini ] unit-test
|
||||
|
||||
[ H{ { "[]" H{ } } } ] [ "[\\[\\]]" string>ini ] unit-test
|
||||
|
||||
[ H{ { "foo" "bar" } } ] [ "foo=bar" string>ini ] unit-test
|
||||
|
||||
[ H{ { "foo" "bar" } { "baz" "quz" } } ]
|
||||
[ "foo=bar\nbaz= quz" string>ini ] unit-test
|
||||
|
||||
[ H{ { "section" H{ { "foo" "abc def" } } } } ]
|
||||
[
|
||||
"""
|
||||
[section]
|
||||
foo = abc def
|
||||
""" string>ini
|
||||
] unit-test
|
||||
|
||||
[ H{ { "section" H{ { "foo" "abc def" } } } } ]
|
||||
[
|
||||
"""
|
||||
[section]
|
||||
foo = abc \\
|
||||
"def"
|
||||
""" string>ini
|
||||
] unit-test
|
||||
|
||||
[ H{ { "section" H{ { "foo" "abc def" } } } } ]
|
||||
[
|
||||
"""
|
||||
[section]
|
||||
foo = "abc " \\
|
||||
def
|
||||
""" string>ini
|
||||
] unit-test
|
||||
|
||||
[ H{ { "section" H{ { "foo" "abc def" } } } } ]
|
||||
[
|
||||
"""
|
||||
[section] foo = "abc def"
|
||||
""" string>ini
|
||||
] unit-test
|
||||
|
||||
[ H{ { "section" H{ { "foo" "abc def" } } } } ]
|
||||
[
|
||||
"""
|
||||
[section] foo = abc \\
|
||||
"def"
|
||||
""" string>ini
|
||||
] unit-test
|
||||
|
||||
[ H{ { "section" H{ { "foo" "" } } } } ]
|
||||
[
|
||||
"""
|
||||
[section]
|
||||
foo=
|
||||
""" string>ini
|
||||
] unit-test
|
||||
|
||||
[ H{ { "section" H{ { "foo" "" } } } } ]
|
||||
[
|
||||
"""
|
||||
[section]
|
||||
foo
|
||||
""" string>ini
|
||||
] unit-test
|
||||
|
||||
[ H{ { "" H{ { "" "" } } } } ]
|
||||
[
|
||||
"""
|
||||
[]
|
||||
=
|
||||
""" string>ini
|
||||
] unit-test
|
||||
|
||||
[ H{ { "owner" H{ { "name" "John Doe" }
|
||||
{ "organization" "Acme Widgets Inc." } } }
|
||||
{ "database" H{ { "server" "192.0.2.62" }
|
||||
{ "port" "143" }
|
||||
{ "file" "payroll.dat" } } } } ]
|
||||
[
|
||||
"""
|
||||
; last modified 1 April 2001 by John Doe
|
||||
[owner]
|
||||
name=John Doe
|
||||
organization=Acme Widgets Inc.
|
||||
|
||||
[database]
|
||||
server=192.0.2.62 ; use IP address in case network name resolution is not working
|
||||
port=143
|
||||
file = "payroll.dat"
|
||||
""" string>ini
|
||||
] unit-test
|
||||
|
||||
[ H{ { "a long section name"
|
||||
H{ { "a long key name" "a long value name" } } } } ]
|
||||
[
|
||||
"""
|
||||
[a long section name ]
|
||||
a long key name= a long value name
|
||||
""" string>ini
|
||||
] unit-test
|
||||
|
||||
[ H{ { "key with \n esc\ape \r codes \""
|
||||
"value with \t esc\ape codes" } } ]
|
||||
[
|
||||
"""
|
||||
key with \\n esc\\ape \\r codes \\\" = value with \\t esc\\ape codes
|
||||
""" string>ini
|
||||
] unit-test
|
||||
|
||||
|
||||
[ """key with \\n esc\\ape \\r codes \\\"=value with \\t esc\\ape codes\n""" ]
|
||||
[
|
||||
H{ { "key with \n esc\ape \r codes \""
|
||||
"value with \t esc\ape codes" } } ini>string
|
||||
] unit-test
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
! Copyright (C) 2010 John Benediktsson
|
||||
! See http://factorcode.org/license.txt for BSD license
|
||||
|
||||
USING: arrays assocs combinators combinators.short-circuit
|
||||
formatting hashtables io io.streams.string kernel make math
|
||||
namespaces quoting sequences splitting strings strings.parser ;
|
||||
|
||||
IN: ini-file
|
||||
|
||||
<PRIVATE
|
||||
|
||||
: escape ( ch -- ch' )
|
||||
H{
|
||||
{ CHAR: a CHAR: \a }
|
||||
{ CHAR: b HEX: 08 } ! \b
|
||||
{ CHAR: f HEX: 0c } ! \f
|
||||
{ CHAR: n CHAR: \n }
|
||||
{ CHAR: r CHAR: \r }
|
||||
{ CHAR: t CHAR: \t }
|
||||
{ CHAR: v HEX: 0b } ! \v
|
||||
{ CHAR: ' CHAR: ' }
|
||||
{ CHAR: " CHAR: " }
|
||||
{ CHAR: \\ CHAR: \\ }
|
||||
{ CHAR: ? CHAR: ? }
|
||||
{ CHAR: ; CHAR: ; }
|
||||
{ CHAR: [ CHAR: [ }
|
||||
{ CHAR: ] CHAR: ] }
|
||||
{ CHAR: = CHAR: = }
|
||||
} ?at [ bad-escape ] unless ;
|
||||
|
||||
: (unescape-string) ( str -- )
|
||||
CHAR: \\ over index [
|
||||
cut-slice [ % ] dip rest-slice
|
||||
dup empty? [ "Missing escape code" throw ] when
|
||||
unclip-slice escape , (unescape-string)
|
||||
] [ % ] if* ;
|
||||
|
||||
: unescape-string ( str -- str' )
|
||||
[ (unescape-string) ] "" make ;
|
||||
|
||||
USE: xml.entities
|
||||
|
||||
: escape-string ( str -- str' )
|
||||
H{
|
||||
{ CHAR: \a "\\a" }
|
||||
{ HEX: 08 "\\b" }
|
||||
{ HEX: 0c "\\f" }
|
||||
{ CHAR: \n "\\n" }
|
||||
{ CHAR: \r "\\r" }
|
||||
{ CHAR: \t "\\t" }
|
||||
{ HEX: 0b "\\v" }
|
||||
{ CHAR: ' "\\'" }
|
||||
{ CHAR: " "\\\"" }
|
||||
{ CHAR: \\ "\\\\" }
|
||||
{ CHAR: ? "\\?" }
|
||||
{ CHAR: ; "\\;" }
|
||||
{ CHAR: [ "\\[" }
|
||||
{ CHAR: ] "\\]" }
|
||||
{ CHAR: = "\\=" }
|
||||
} escape-string-by ;
|
||||
|
||||
: space? ( ch -- ? )
|
||||
{
|
||||
[ CHAR: \s = ]
|
||||
[ CHAR: \t = ]
|
||||
[ CHAR: \n = ]
|
||||
[ CHAR: \r = ]
|
||||
[ HEX: 0c = ] ! \f
|
||||
[ HEX: 0b = ] ! \v
|
||||
} 1|| ;
|
||||
|
||||
: unspace ( str -- str' )
|
||||
[ space? ] trim ;
|
||||
|
||||
: unwrap ( str -- str' )
|
||||
1 swap [ length 1 - ] keep subseq ;
|
||||
|
||||
: uncomment ( str -- str' )
|
||||
";#" [ over index [ head ] when* ] each ;
|
||||
|
||||
: cleanup-string ( str -- str' )
|
||||
unspace unquote unescape-string ;
|
||||
|
||||
SYMBOL: section
|
||||
SYMBOL: option
|
||||
|
||||
: section? ( line -- index/f )
|
||||
{
|
||||
[ length 1 > ]
|
||||
[ first CHAR: [ = ]
|
||||
[ CHAR: ] swap last-index ]
|
||||
} 1&& ;
|
||||
|
||||
: line-continues? ( line -- ? )
|
||||
{ [ empty? not ] [ last CHAR: \ = ] } 1&& ;
|
||||
|
||||
: section, ( -- )
|
||||
section get [ , ] when* ;
|
||||
|
||||
: option, ( name value -- )
|
||||
section get [ second swapd set-at ] [ 2array , ] if* ;
|
||||
|
||||
: [section] ( line -- )
|
||||
unwrap cleanup-string H{ } clone 2array section set ;
|
||||
|
||||
: name=value ( line -- )
|
||||
option [
|
||||
[ swap [ first2 ] dip ] [
|
||||
"=" split1 [ cleanup-string "" ] [ "" or ] bi*
|
||||
] if*
|
||||
dup line-continues? [
|
||||
dup length 1 - head cleanup-string
|
||||
dup last space? [ " " append ] unless append 2array
|
||||
] [
|
||||
cleanup-string append option, f
|
||||
] if
|
||||
] change ;
|
||||
|
||||
: parse-line ( line -- )
|
||||
uncomment unspace dup section? [
|
||||
section, 1 + cut [ [section] ] [ unspace ] bi*
|
||||
] when* [ name=value ] unless-empty ;
|
||||
|
||||
PRIVATE>
|
||||
|
||||
: read-ini ( -- assoc )
|
||||
section off option off
|
||||
[ [ parse-line ] each-line section, ] { } make
|
||||
>hashtable ;
|
||||
|
||||
: write-ini ( assoc -- )
|
||||
[
|
||||
dup string?
|
||||
[ [ escape-string ] bi@ "%s=%s\n" printf ]
|
||||
[
|
||||
[ escape-string "[%s]\n" printf ] dip
|
||||
[ [ escape-string ] bi@ "%s=%s\n" printf ]
|
||||
assoc-each nl
|
||||
] if
|
||||
] assoc-each ;
|
||||
|
||||
! FIXME: escaped comments "\;" don't work
|
||||
|
||||
: string>ini ( str -- assoc )
|
||||
[ read-ini ] with-string-reader ;
|
||||
|
||||
: ini>string ( str -- assoc )
|
||||
[ write-ini ] with-string-writer ;
|
||||
|
|
@ -0,0 +1 @@
|
|||
Parses INI configuration files.
|
Loading…
Reference in New Issue