[ "/_-.:" member? ]
} 1|| ; foldable
+! see http://tools.ietf.org/html/rfc3986#section-2.2
+: gen-delim? ( ch -- ? )
+ ":/?#[]@" member? ; foldable
+
+: sub-delim? ( ch -- ? )
+ "!$&'()*+,;=" member? ; foldable
+
+: reserved? ( ch -- ? )
+ [ gen-delim? ] [ sub-delim? ] bi or ; foldable
+
+! see http://tools.ietf.org/html/rfc3986#section-2.3
+: unreserved? ( ch -- ? )
+ {
+ [ letter? ]
+ [ LETTER? ]
+ [ digit? ]
+ [ "-._~" member? ]
+ } 1|| ; foldable
+
<PRIVATE
: push-utf8 ( ch -- )
1string utf8 encode
- [ CHAR: % , >hex 2 CHAR: 0 pad-left % ] each ;
+ [ CHAR: % , >hex >upper 2 CHAR: 0 pad-head % ] each ;
PRIVATE>
[ dup url-quotable? [ , ] [ push-utf8 ] if ] each
] "" make ;
+: url-encode-full ( str -- encoded )
+ [
+ [ dup unreserved? [ , ] [ push-utf8 ] if ] each
+ ] "" make ;
+
<PRIVATE
: url-decode-hex ( index str -- )
2dup length 2 - >= [
2drop
] [
- [ 1+ dup 2 + ] dip subseq hex> [ , ] when*
+ [ 1 + dup 2 + ] dip subseq hex> [ , ] when*
] if ;
: url-decode-% ( index str -- index str )
2dup nth dup CHAR: % = [
drop url-decode-% [ 3 + ] dip
] [
- , [ 1+ ] dip
+ , [ 1 + ] dip
] if url-decode-iter
] if ;
] when*
] 2keep set-at ;
+: assoc-strings ( assoc -- assoc' )
+ [
+ {
+ { [ dup not ] [ ] }
+ { [ dup array? ] [ [ present ] map ] }
+ [ present 1array ]
+ } cond
+ ] assoc-map ;
+
PRIVATE>
: query>assoc ( query -- assoc )
: assoc>query ( assoc -- str )
[
- dup array? [ [ present ] map ] [ present 1array ] if
- ] assoc-map
- [
- [
+ assoc-strings [
[ url-encode ] dip
- [ url-encode "=" swap 3append , ] with each
+ [ [ url-encode "=" glue , ] with each ] [ , ] if*
] assoc-each
] { } make "&" join ;