def XML: def String : ((consume("\"") | parse("[^\"]*") | consume("\"")) // (consume("'") | parse("[^']*") | consume("'"))); def CDataSec : box("@CDATA"; q("") | q("]]>") ) | ws; def PROLOG : box("@PROLOG"; q("") | q("?>")); def DTD : box("@DTD"; q("]") | q(">")); # The XML spec specifically disallows double-hyphen within comments def COMMENT : box("@COMMENT"; q("")); def CharData : parse("[^<]+"); # only `<` is disallowed # This is more permissive than required: def Name : parse("[A-Za-z:_][^/=<>\n\r\t ]*"); def Attribute : keyvalue(Name | ws | q("=") | ws | String | ws); def Attributes: box( plus(Attribute) ) | .result[-1] |= {"@attributes": add} ; # must be matched with def Element : def Content : star(Element // CDataSec // CharData // COMMENT); objectify( q("<") | Name | .result[-1] as $name | ws | (Attributes // ws) | ( (q("/>") // (q(">") | Content | q(""))) | ws) ) ; {remainder: . } | ws | optional(PROLOG) | ws | optional(DTD) | ws | star(COMMENT | ws) | Element | ws # for HTML, one would use star(Element) here | star(COMMENT | ws) | .result;