]> gitweb.factorcode.org Git - factor.git/blob - basis/xml/elements/elements.factor
Merge branch 'master' of git://factorcode.org/git/factor
[factor.git] / basis / xml / elements / elements.factor
1 ! Copyright (C) 2005, 2009 Daniel Ehrenberg
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: kernel namespaces xml.tokenize xml.state xml.name
4 xml.data accessors arrays make xml.char-classes fry assocs sequences
5 math xml.errors sets combinators io.encodings io.encodings.iana
6 unicode.case xml.dtd strings xml.entities ;
7 IN: xml.elements
8
9 : take-interpolated ( quot -- interpolated )
10     interpolating? get [
11         drop get-char CHAR: > =
12         [ next f ] [
13             pass-blank " \t\r\n-" take-to
14             pass-blank "->" expect
15         ] if <interpolated>
16     ] [ call ] if ; inline
17
18 : interpolate-quote ( -- interpolated )
19     [ quoteless-attr ] take-interpolated ;
20
21 : parse-attr ( -- )
22     parse-name pass-blank "=" expect pass-blank
23     get-char CHAR: < =
24     [ "<-" expect interpolate-quote ]
25     [ t parse-quote* ] if 2array , ;
26
27 : start-tag ( -- name ? )
28     #! Outputs the name and whether this is a closing tag
29     get-char CHAR: / = dup [ next ] when
30     parse-name swap ;
31
32 : (middle-tag) ( -- )
33     pass-blank version=1.0? get-char name-start?
34     [ parse-attr (middle-tag) ] when ;
35
36 : assure-no-duplicates ( attrs-alist -- attrs-alist )
37     H{ } clone 2dup '[ swap _ push-at ] assoc-each
38     [ nip length 2 >= ] assoc-filter >alist
39     [ first first2 duplicate-attr ] unless-empty ;
40
41 : middle-tag ( -- attrs-alist )
42     ! f make will make a vector if it has any elements
43     [ (middle-tag) ] f make pass-blank
44     assure-no-duplicates ;
45
46 : end-tag ( name attrs-alist -- tag )
47     tag-ns pass-blank get-char CHAR: / =
48     [ pop-ns <contained> next ">" expect ]
49     [ depth inc <opener> close ] if ;
50
51 : take-comment ( -- comment )
52     "--" expect
53     "--" take-string
54     <comment>
55     ">" expect ;
56
57 : assure-no-extra ( seq -- )
58     [ first ] map {
59         T{ name f "" "version" f }
60         T{ name f "" "encoding" f }
61         T{ name f "" "standalone" f }
62     } diff
63     [ extra-attrs ] unless-empty ; 
64
65 : good-version ( version -- version )
66     dup { "1.0" "1.1" } member? [ bad-version ] unless ;
67
68 : prolog-version ( alist -- version )
69     T{ name f "" "version" f } swap at
70     [ good-version ] [ versionless-prolog ] if* ;
71
72 : prolog-encoding ( alist -- encoding )
73     T{ name f "" "encoding" f } swap at "UTF-8" or ;
74
75 : yes/no>bool ( string -- t/f )
76     {
77         { "yes" [ t ] }
78         { "no" [ f ] }
79         [ not-yes/no ]
80     } case ;
81
82 : prolog-standalone ( alist -- version )
83     T{ name f "" "standalone" f } swap at
84     [ yes/no>bool ] [ f ] if* ;
85
86 : prolog-attrs ( alist -- prolog )
87     [ prolog-version ]
88     [ prolog-encoding ]
89     [ prolog-standalone ]
90     tri <prolog> ;
91
92 SYMBOL: string-input?
93 : decode-input-if ( encoding -- )
94     string-input? get [ drop ] [ decode-input ] if ;
95
96 : parse-prolog ( -- prolog )
97     pass-blank middle-tag "?>" expect
98     dup assure-no-extra prolog-attrs
99     dup encoding>> dup "UTF-16" =
100     [ drop ] [ name>encoding [ decode-input-if ] when* ] if
101     dup prolog-data set ;
102
103 : instruct ( -- instruction )
104     take-name {
105         { [ dup "xml" = ] [ drop parse-prolog ] }
106         { [ dup >lower "xml" = ] [ capitalized-prolog ] }
107         { [ dup valid-name? not ] [ bad-name ] }
108         [ "?>" take-string append <instruction> ]
109     } cond ;
110
111 : take-cdata ( -- string )
112     depth get zero? [ bad-cdata ] when
113     "[CDATA[" expect "]]>" take-string ;
114
115 DEFER: make-tag ! Is this unavoidable?
116
117 : expand-pe ( -- ) ; ! Make this run the contents of the pe within a DOCTYPE
118
119 : dtd-loop ( -- )
120     pass-blank get-char {
121         { CHAR: ] [ next ] }
122         { CHAR: % [ expand-pe ] }
123         { CHAR: < [
124             next make-tag dup dtd-acceptable?
125             [ bad-doctype ] unless , dtd-loop
126         ] }
127         { f [ ] }
128         [ 1string bad-doctype ]
129     } case ;
130
131 : take-internal-subset ( -- dtd )
132     [
133         H{ } clone pe-table set
134         t in-dtd? set
135         dtd-loop
136         pe-table get
137     ] { } make swap extra-entities get swap <dtd> ;
138
139 : take-optional-id ( -- id/f )
140     get-char "SP" member?
141     [ take-external-id ] [ f ] if ;
142
143 : take-internal ( -- dtd/f )
144     get-char CHAR: [ =
145     [ next take-internal-subset ] [ f ] if ;
146
147 : take-doctype-decl ( -- doctype-decl )
148     pass-blank take-name
149     pass-blank take-optional-id
150     pass-blank take-internal
151     <doctype-decl> close ;
152
153 : take-directive ( -- doctype )
154     take-name dup "DOCTYPE" =
155     [ drop take-doctype-decl ] [
156         in-dtd? get
157         [ take-inner-directive ]
158         [ misplaced-directive ] if
159     ] if ;
160
161 : direct ( -- object )
162     get-char {
163         { CHAR: - [ take-comment ] }
164         { CHAR: [ [ take-cdata ] }
165         [ drop take-directive ]
166     } case ;
167
168 : normal-tag ( -- tag )
169     start-tag
170     [ dup add-ns pop-ns <closer> depth dec close ]
171     [ middle-tag end-tag ] if ;
172
173 : interpolate-tag ( -- interpolated )
174     [ "-" bad-name ] take-interpolated ;
175
176 : make-tag ( -- tag )
177     {
178         { [ get-char dup CHAR: ! = ] [ drop next direct ] }
179         { [ dup CHAR: ? = ] [ drop next instruct ] }
180         { [ dup CHAR: - = ] [ drop next interpolate-tag ] }
181         [ drop normal-tag ]
182     } cond ;