1 ! Copyright (C) 2019 John Benediktsson
2 ! See http://factorcode.org/license.txt for BSD license
4 USING: accessors arrays assocs hashtables kernel locals make
5 math.parser peg peg.parsers regexp sequences splitting
10 ERROR: duplicate-key key ;
12 ERROR: unknown-value value ;
16 ! FIXME: key = 1234abcd # should error!
18 TUPLE: table name array? entries ;
20 TUPLE: entry key value ;
22 : boolean-parser ( -- parser )
23 "true" token [ drop t ] action
24 "false" token [ drop f ] action
27 : digits ( parser -- parser )
28 "_" token [ drop f ] action 2choice repeat1 [ sift ] action ;
31 "+" token "-" token 2choice ;
33 : hexdigit ( -- parser )
35 CHAR: 0 CHAR: 9 range ,
36 CHAR: a CHAR: f range ,
37 CHAR: A CHAR: F range ,
41 "0x" token hide hexdigit digits 2seq [ first hex> ] action ;
43 : decdigit ( -- parser )
44 CHAR: 0 CHAR: 9 range ;
47 decdigit digits [ dec> ] action ;
49 : octdigit ( -- parser )
50 CHAR: 0 CHAR: 7 range ;
53 "0o" token hide octdigit digits 2seq [ first oct> ] action ;
55 : bindigit ( -- parser )
56 CHAR: 0 CHAR: 1 range ;
59 "0b" token hide bindigit digits 2seq [ first bin> ] action ;
61 : integer-parser ( -- parser )
62 hex oct bin dec 4choice ;
67 decdigit digits optional ,
69 decdigit digits optional ,
70 "e" token "E" token 2choice
72 decdigit digits optional 3seq optional ,
73 ] seq* [ unclip-last append "" concat-as string>number ] action ;
76 "+" token optional "inf" token 2seq [ drop 1/0. ] action ;
79 "-inf" token [ drop -1/0. ] action ;
82 sign optional "nan" token 2seq
83 [ drop NAN: 8000000000000 ] action ;
85 : float-parser ( -- parser )
86 float +inf -inf nan 4choice ;
88 : escaped ( -- parser )
89 "\\" token hide [ "btnfr\"\\" member-eq? ] satisfy 2seq
90 [ first escape ] action ;
92 : unicode ( -- parser )
93 "\\u" token hide hexdigit 4 exactly-n 2seq
94 "\\U" token hide hexdigit 8 exactly-n 2seq
95 2choice [ first hex> ] action ;
97 : basic-string ( -- parser )
98 escaped unicode [ "\"\n" member? not ] satisfy 3choice repeat0
99 "\"" dup surrounded-by ;
101 : literal-string ( -- parser )
102 [ "'\n" member? not ] satisfy repeat0
103 "'" dup surrounded-by ;
105 : single-string ( -- parser )
106 basic-string literal-string 2choice [ "" like ] action ;
108 : multi-basic-string ( -- parser )
109 escaped unicode [ CHAR: \" = not ] satisfy 3choice repeat0
110 "\"\"\"" dup surrounded-by ;
112 : multi-literal-string ( -- parser )
113 [ CHAR: ' = not ] satisfy repeat0
114 "'''" dup surrounded-by ;
116 : multi-string ( -- parser )
117 multi-basic-string multi-literal-string 2choice [
118 "" like "\n" ?head drop
119 R/ \\[ \t\r\n]*\n[ \t\r\n]*/m "" re-replace
122 : string-parser ( -- parser )
123 multi-string single-string 2choice ;
125 : date-parser ( -- parser )
127 decdigit 4 exactly-n ,
129 decdigit 2 exactly-n ,
131 decdigit 2 exactly-n ,
132 ] seq* [ "" concat-as ] action ;
134 : time-parser ( -- parser )
136 decdigit 2 exactly-n ,
138 decdigit 2 exactly-n ,
140 decdigit 2 exactly-n ,
141 "." token decdigit repeat1 2seq optional ,
142 ] seq* [ "" concat-as ] action ;
144 : timezone-parser ( -- parser )
147 decdigit 2 exactly-n ":" token
148 decdigit 2 exactly-n 4seq [ "" concat-as ] action
151 : datetime-parser ( -- parser )
154 "T" token " " token 2choice ,
156 timezone-parser optional ,
157 ] seq* [ "" concat-as ] action ;
159 : space ( -- parser )
160 [ " \t" member? ] satisfy repeat0 ;
162 : whitespace ( -- parser )
163 [ " \t\r\n" member? ] satisfy repeat0 ;
167 : array-parser ( -- parser )
172 whitespace "," token whitespace pack list-of ,
175 ] seq* [ first { } like ] action ;
177 DEFER: key-value-parser
181 : inline-table-parser ( -- parser )
186 whitespace "," token whitespace pack list-of ,
190 first [ length <hashtable> ] keep [ update-toml ] each
193 : value-parser ( -- parser )
204 inline-table-parser ,
208 : name-parser ( -- parser )
210 CHAR: A CHAR: Z range ,
211 CHAR: a CHAR: z range ,
212 CHAR: 0 CHAR: 9 range ,
213 "_" token [ first ] action ,
214 "-" token [ first ] action ,
215 ] choice* repeat1 [ "" like ] action single-string 2choice ;
217 : comment-parser ( -- parser )
221 [ CHAR: \n = not ] satisfy repeat0 ,
222 ] seq* [ drop f ] action ;
224 : key-parser ( -- parser )
225 name-parser "." token list-of [ { } like ] action ;
227 : key-value-parser ( -- parser )
235 comment-parser optional hide ,
236 ] seq* [ first2 entry boa ] action ;
238 : line-parser ( -- parser )
239 "\n" token "\r\n" token 2choice ;
241 :: table-name-parser ( begin end -- parser )
246 space "." token space pack list-of
247 [ { } like ] action ,
250 comment-parser optional hide ,
253 : table-parser ( -- parser )
256 "[[" "]]" table-name-parser [ t suffix! ] action
257 "[" "]" table-name-parser [ f suffix! ] action
260 key-value-parser line-parser list-of optional ,
261 ] seq* [ first2 [ first2 ] dip table boa ] action ;
263 : toml-parser ( -- parser )
270 ] choice* whitespace list-of ,
272 ] seq* [ first sift { } like ] action ;
274 : check-no-key ( key assoc -- key assoc )
275 2dup at* nip [ over duplicate-key ] when ;
277 : deep-at ( keys assoc -- value )
280 H{ } clone [ swap rot check-no-key set-at ] keep
284 GENERIC: update-toml ( assoc entry -- assoc )
287 [ key>> unclip-last [ over deep-at ] dip ] [ value>> ] bi
288 swap rot check-no-key set-at ;
291 [ name>> unclip-last [ over deep-at ] dip ]
292 [ entries>> [ H{ } clone ] dip [ update-toml ] each swap rot ]
293 [ array?>> [ push-at ] [ check-no-key set-at ] if ] tri ;
297 : toml> ( string -- assoc )
298 [ H{ } clone ] dip toml-parser parse [ update-toml ] each ;