| 
									
										
										
										
											2008-04-17 16:29:04 -04:00
										 |  |  | ! Copyright (C) 2007, 2008 Phil Dawes | 
					
						
							|  |  |  | ! See http://factorcode.org/license.txt for BSD license. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ! Simple CSV Parser | 
					
						
							|  |  |  | ! Phil Dawes phil@phildawes.net | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-30 12:50:40 -04:00
										 |  |  | USING: kernel sequences io namespaces combinators unicode.categories vars ;
 | 
					
						
							| 
									
										
										
										
											2008-04-17 16:29:04 -04:00
										 |  |  | IN: csv | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | DEFER: quoted-field | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-30 12:50:40 -04:00
										 |  |  | VAR: delimiter | 
					
						
							| 
									
										
										
										
											2008-07-25 17:02:07 -04:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2008-04-30 07:28:39 -04:00
										 |  |  | ! trims whitespace from either end of string | 
					
						
							|  |  |  | : trim-whitespace ( str -- str )
 | 
					
						
							|  |  |  |   [ blank? ] trim ; inline
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | : skip-to-field-end ( -- endchar )
 | 
					
						
							| 
									
										
										
										
											2008-04-30 12:50:40 -04:00
										 |  |  |   "\n" delimiter> suffix read-until nip ; inline
 | 
					
						
							| 
									
										
										
										
											2008-04-30 07:28:39 -04:00
										 |  |  |    | 
					
						
							| 
									
										
										
										
											2008-04-17 16:29:04 -04:00
										 |  |  | : not-quoted-field ( -- endchar )
 | 
					
						
							| 
									
										
										
										
											2008-04-30 12:50:40 -04:00
										 |  |  |   "\"\n" delimiter> suffix read-until   ! " | 
					
						
							| 
									
										
										
										
											2008-04-17 16:29:04 -04:00
										 |  |  |   dup
 | 
					
						
							| 
									
										
										
										
											2008-04-30 12:50:40 -04:00
										 |  |  |   { { CHAR: "     [ drop drop quoted-field ] }  ! "  | 
					
						
							|  |  |  |     { delimiter> [ swap trim-whitespace % ] }  | 
					
						
							|  |  |  |     { CHAR: \n    [ swap trim-whitespace % ] }     | 
					
						
							|  |  |  |     { f           [ swap trim-whitespace % ] }       ! eof | 
					
						
							| 
									
										
										
										
											2008-04-17 16:29:04 -04:00
										 |  |  |   } case ;
 | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | : maybe-escaped-quote ( -- endchar )
 | 
					
						
							| 
									
										
										
										
											2008-04-30 07:28:39 -04:00
										 |  |  |   read1 dup  | 
					
						
							| 
									
										
										
										
											2008-04-30 12:50:40 -04:00
										 |  |  |   { { CHAR: "    [ , quoted-field ] }  ! " is an escaped quote | 
					
						
							|  |  |  |     { delimiter> [ ] }                 ! end of quoted field  | 
					
						
							| 
									
										
										
										
											2008-05-01 06:54:09 -04:00
										 |  |  |     { CHAR: \n   [ ] } | 
					
						
							| 
									
										
										
										
											2008-04-30 07:28:39 -04:00
										 |  |  |     [ 2drop skip-to-field-end ]       ! end of quoted field + padding | 
					
						
							| 
									
										
										
										
											2008-04-17 16:29:04 -04:00
										 |  |  |   } case ;
 | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | : quoted-field ( -- endchar )
 | 
					
						
							|  |  |  |   "\"" read-until                                 ! " | 
					
						
							|  |  |  |   drop % maybe-escaped-quote ;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | : field ( -- sep string )
 | 
					
						
							| 
									
										
										
										
											2008-04-30 07:28:39 -04:00
										 |  |  |   [ not-quoted-field ] "" make  ; ! trim-whitespace | 
					
						
							| 
									
										
										
										
											2008-04-17 16:29:04 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | : (row) ( -- sep )
 | 
					
						
							|  |  |  |   field ,  | 
					
						
							| 
									
										
										
										
											2008-04-30 12:50:40 -04:00
										 |  |  |   dup delimiter> = [ drop (row) ] when ;
 | 
					
						
							| 
									
										
										
										
											2008-04-17 16:29:04 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | : row ( -- eof? array[string] )
 | 
					
						
							|  |  |  |   [ (row) ] { } make ;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | : append-if-row-not-empty ( row -- )
 | 
					
						
							|  |  |  |   dup { "" } = [ drop ] [ , ] if ;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | : (csv) ( -- )
 | 
					
						
							|  |  |  |   row append-if-row-not-empty | 
					
						
							|  |  |  |   [ (csv) ] when ;
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-30 12:50:40 -04:00
										 |  |  | : init-vars ( -- )
 | 
					
						
							| 
									
										
										
										
											2008-07-25 17:02:07 -04:00
										 |  |  |   delimiter> [ CHAR: , >delimiter ] unless ; inline  | 
					
						
							| 
									
										
										
										
											2008-04-30 12:50:40 -04:00
										 |  |  |    | 
					
						
							| 
									
										
										
										
											2008-04-17 16:29:04 -04:00
										 |  |  | : csv-row ( stream -- row )
 | 
					
						
							| 
									
										
										
										
											2008-04-30 12:50:40 -04:00
										 |  |  |   init-vars | 
					
						
							| 
									
										
										
										
											2008-05-05 03:19:25 -04:00
										 |  |  |   [ row nip ] with-input-stream ;
 | 
					
						
							| 
									
										
										
										
											2008-04-17 16:29:04 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | : csv ( stream -- rows )
 | 
					
						
							| 
									
										
										
										
											2008-04-30 12:50:40 -04:00
										 |  |  |   init-vars | 
					
						
							| 
									
										
										
										
											2008-05-05 03:19:25 -04:00
										 |  |  |   [ [ (csv) ] { } make ] with-input-stream ;
 | 
					
						
							| 
									
										
										
										
											2008-04-30 12:50:40 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | : with-delimiter ( char quot -- )
 | 
					
						
							|  |  |  |   delimiter swap with-variable ; inline
 | 
					
						
							| 
									
										
										
										
											2008-07-25 17:02:07 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  | : needs-escaping? ( cell -- ? )
 | 
					
						
							|  |  |  |   [ "\n\"" delimiter> suffix member? ] contains? ; inline ! " | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | : escape-quotes ( cell -- cell' )
 | 
					
						
							|  |  |  |   [ [ dup , CHAR: " = [ CHAR: " , ] when ] each ] "" make ; inline
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | : enclose-in-quotes ( cell -- cell' )
 | 
					
						
							|  |  |  |   CHAR: " [ prefix ] [ suffix ] bi ; inline ! " | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  | : escape-if-required ( cell -- cell' )
 | 
					
						
							|  |  |  |   dup needs-escaping? [ escape-quotes enclose-in-quotes ] when ; inline
 | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  | : write-row ( row -- )
 | 
					
						
							|  |  |  |   [ delimiter> write1 ] [ escape-if-required write ] interleave nl ; inline
 | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  | : write-csv ( rows outstream -- )
 | 
					
						
							|  |  |  |   init-vars | 
					
						
							|  |  |  |   [ [ write-row ] each ] with-output-stream ;
 |