1 ! Copyright (C) 2005, 2009 Daniel Ehrenberg
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: words assocs kernel accessors parser vocabs.parser effects.parser
4 sequences summary lexer splitting combinators locals
5 memoize sequences.deep xml.data xml.state xml namespaces present
6 arrays generalizations strings make math macros multiline
7 combinators.short-circuit sorting fry unicode.categories
13 TUPLE: no-tag name word ;
15 drop "The tag-dispatching word has no method for the given tag name" ;
17 : compile-tags ( word xtable -- quot )
18 >alist swap '[ _ no-tag boa throw ] suffix
19 '[ dup main>> _ case ] ;
21 : define-tags ( word effect -- )
22 [ dup dup "xtable" word-prop compile-tags ] dip define-declared ;
24 :: define-tag ( string word quot -- )
25 quot string word "xtable" word-prop set-at
26 word word stack-effect define-tags ;
31 CREATE-WORD complete-effect
32 [ drop H{ } clone "xtable" set-word-prop ]
37 scan scan-word parse-definition define-tag ;
40 CREATE-WORD scan '[ f swap _ <name> ] (( string -- name )) define-memoized ;
44 : each-attrs ( attrs quot -- )
45 [ values [ interpolated? ] filter ] dip each ; inline
47 : (each-interpolated) ( ... item quot: ( ... interpolated -- ... ) -- ... )
49 { [ over interpolated? ] [ call ] }
50 { [ over tag? ] [ [ attrs>> ] dip each-attrs ] }
51 { [ over attrs? ] [ each-attrs ] }
52 { [ over xml? ] [ [ body>> ] dip (each-interpolated) ] }
54 } cond ; inline recursive
56 : each-interpolated ( xml quot -- )
57 '[ _ (each-interpolated) ] deep-each ; inline
59 : has-interpolated? ( xml -- ? )
60 ! If this becomes a performance problem, it can be improved
61 f swap [ 2drop t ] each-interpolated ;
63 : when-interpolated ( xml quot -- genquot )
64 [ dup has-interpolated? ] dip [ '[ _ swap ] ] if ; inline
66 : string>chunk ( string -- chunk )
67 t interpolating? [ string>xml-chunk ] with-variable ;
69 : string>doc ( string -- xml )
70 t interpolating? [ string>xml ] with-variable ;
72 DEFER: interpolate-sequence
74 : get-interpolated ( interpolated -- quot )
75 var>> '[ [ _ swap at ] keep ] ;
77 : ?present ( object -- string )
78 dup [ present ] when ;
80 : interpolate-attr ( key value -- quot )
82 [ get-interpolated '[ _ swap @ [ ?present 2array ] dip ] ]
83 [ 2array '[ _ swap ] ] if ;
85 : filter-nulls ( assoc -- newassoc )
86 [ nip ] assoc-filter ;
88 : interpolate-attrs ( attrs -- quot )
90 [ [ interpolate-attr ] { } assoc>map [ ] join ]
92 '[ @ _ swap [ narray filter-nulls <attrs> ] dip ]
95 : interpolate-tag ( tag -- quot )
98 [ attrs>> interpolate-attrs ]
99 [ children>> interpolate-sequence ] tri
100 '[ _ swap @ @ [ <tag> ] dip ]
101 ] when-interpolated ;
103 GENERIC: push-item ( item -- )
104 M: string push-item , ;
105 M: xml-data push-item , ;
106 M: object push-item present , ;
107 M: sequence push-item
108 dup xml-data? [ , ] [ [ push-item ] each ] if ;
109 M: number push-item present , ;
110 M: xml-chunk push-item % ;
112 : concat-interpolate ( array -- newarray )
113 [ [ push-item ] each ] { } make ;
115 GENERIC: interpolate-item ( item -- quot )
116 M: object interpolate-item [ swap ] curry ;
117 M: tag interpolate-item interpolate-tag ;
118 M: interpolated interpolate-item get-interpolated ;
120 : interpolate-sequence ( seq -- quot )
122 [ [ interpolate-item ] map concat ]
124 '[ @ _ swap [ narray concat-interpolate ] dip ]
125 ] when-interpolated ;
127 GENERIC: [interpolate-xml] ( xml -- quot )
129 M: xml [interpolate-xml]
130 dup body>> interpolate-tag
131 '[ _ (clone) swap @ drop >>body ] ;
133 M: xml-chunk [interpolate-xml]
135 '[ @ drop <xml-chunk> ] ;
137 MACRO: interpolate-xml ( xml -- quot )
140 : number<-> ( doc -- dup )
143 over >>var [ 1 + ] dip
145 ] each-interpolated drop ;
147 : >search-hash ( seq -- hash )
148 [ dup search ] H{ } map>assoc ;
150 : extract-variables ( xml -- seq )
151 [ [ var>> , ] each-interpolated ] { } make ;
153 : nenum ( ... n -- assoc )
154 narray <enum> ; inline
156 : collect ( accum variables -- accum ? )
158 { [ dup empty? ] [ drop f ] } ! Just a literal
159 { [ dup [ ] all? ] [ >search-hash suffix! t ] } ! locals
160 { [ dup [ not ] all? ] [ length suffix! \ nenum suffix! t ] } ! fry
161 [ drop "XML interpolation contains both fry and locals" throw ] ! mixed
164 : parse-def ( accum delimiter quot -- accum )
165 [ parse-multiline-string [ blank? ] trim ] dip call
166 [ extract-variables collect ] keep swap
167 [ number<-> suffix! ] dip
168 [ \ interpolate-xml suffix! ] when ; inline
173 "XML>" [ string>doc ] parse-def ;
176 "XML]" [ string>chunk ] parse-def ;
180 "inverse" "xml.syntax.inverse" require-when