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