115 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Factor
		
	
	
			
		
		
	
	
			115 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Factor
		
	
	
! Copyright (C) 2007, 2008 Phil Dawes
 | 
						|
! See http://factorcode.org/license.txt for BSD license.
 | 
						|
USING: kernel sequences io namespaces make combinators
 | 
						|
unicode.categories io.files combinators.short-circuit
 | 
						|
io.streams.string ;
 | 
						|
IN: csv
 | 
						|
 | 
						|
SYMBOL: delimiter
 | 
						|
 | 
						|
CHAR: , delimiter set-global
 | 
						|
 | 
						|
<PRIVATE
 | 
						|
 | 
						|
: delimiter> ( -- delimiter ) delimiter get ; inline
 | 
						|
 | 
						|
DEFER: quoted-field
 | 
						|
 | 
						|
: skip-to-field-end ( -- endchar )
 | 
						|
  "\n" delimiter> suffix read-until nip ; inline
 | 
						|
 | 
						|
: not-quoted-field ( -- endchar )
 | 
						|
    "\"\n" delimiter> suffix read-until
 | 
						|
    dup {
 | 
						|
        { CHAR: "    [ 2drop quoted-field ] }
 | 
						|
        { delimiter> [ swap [ blank? ] trim % ] }
 | 
						|
        { CHAR: \n   [ swap [ blank? ] trim % ] }
 | 
						|
        { f          [ swap [ blank? ] trim % ] }
 | 
						|
    } case ;
 | 
						|
 | 
						|
: maybe-escaped-quote ( -- endchar )
 | 
						|
    read1 dup {
 | 
						|
        { CHAR: "    [ , quoted-field ] }
 | 
						|
        { delimiter> [ ] }
 | 
						|
        { CHAR: \n   [ ] }
 | 
						|
        [ 2drop skip-to-field-end ]
 | 
						|
    } case ;
 | 
						|
 | 
						|
: quoted-field ( -- endchar )
 | 
						|
    "\"" read-until
 | 
						|
    drop % maybe-escaped-quote ;
 | 
						|
 | 
						|
: field ( -- sep string )
 | 
						|
    [ not-quoted-field ] "" make  ;
 | 
						|
 | 
						|
: (row) ( -- sep )
 | 
						|
    field ,
 | 
						|
    dup delimiter> = [ drop (row) ] when ;
 | 
						|
 | 
						|
: row ( -- eof? array[string] )
 | 
						|
    [ (row) ] { } make ;
 | 
						|
 | 
						|
: (csv) ( -- )
 | 
						|
    row
 | 
						|
    dup [ empty? ] all? [ drop ] [ , ] if
 | 
						|
    [ (csv) ] when ;
 | 
						|
 | 
						|
PRIVATE>
 | 
						|
 | 
						|
: csv-row ( stream -- row )
 | 
						|
    [ row nip ] with-input-stream ;
 | 
						|
 | 
						|
: csv ( stream -- rows )
 | 
						|
    [ [ (csv) ] { } make ] with-input-stream
 | 
						|
    dup last { "" } = [ but-last ] when ;
 | 
						|
 | 
						|
: string>csv ( string -- csv )
 | 
						|
    <string-reader> csv ;
 | 
						|
 | 
						|
: file>csv ( path encoding -- csv )
 | 
						|
    <file-reader> csv ;
 | 
						|
 | 
						|
: with-delimiter ( ch quot -- )
 | 
						|
    [ delimiter ] dip with-variable ; inline
 | 
						|
 | 
						|
<PRIVATE
 | 
						|
 | 
						|
: needs-escaping? ( cell -- ? )
 | 
						|
    [ { [ "\n\"" member? ] [ delimiter> = ] } 1|| ] any? ; inline
 | 
						|
 | 
						|
: escape-quotes ( cell -- cell' )
 | 
						|
    [
 | 
						|
        [
 | 
						|
            [ , ]
 | 
						|
            [ dup CHAR: " = [ , ] [ drop ] if ] bi
 | 
						|
        ] each
 | 
						|
    ] "" make ; inline
 | 
						|
 | 
						|
: enclose-in-quotes ( cell -- cell' )
 | 
						|
    "\"" dup surround ; inline
 | 
						|
 | 
						|
: escape-if-required ( cell -- cell' )
 | 
						|
    dup needs-escaping?
 | 
						|
    [ escape-quotes enclose-in-quotes ] when ; inline
 | 
						|
 | 
						|
PRIVATE>
 | 
						|
 | 
						|
: write-row ( row -- )
 | 
						|
    [ delimiter> write1 ]
 | 
						|
    [ escape-if-required write ] interleave nl ; inline
 | 
						|
 | 
						|
<PRIVATE
 | 
						|
 | 
						|
: (write-csv) ( rows -- )
 | 
						|
    [ write-row ] each ;
 | 
						|
 | 
						|
PRIVATE>
 | 
						|
 | 
						|
: write-csv ( rows stream -- )
 | 
						|
    [ (write-csv) ] with-output-stream ;
 | 
						|
 | 
						|
: csv>string ( csv -- string )
 | 
						|
    [ (write-csv) ] with-string-writer ;
 | 
						|
 | 
						|
: csv>file ( rows path encoding -- ) <file-writer> write-csv ;
 |