"""Shared support for scanning document type declarations in HTML and XHTML.This module is used as a foundation for the html.parser module. It has nodocumented public API and should not be used directly."""import re_declname_match = re.compile(r'[a-zA-Z][-_.a-zA-Z0-9]*\s*').match_declstringlit_match = re.compile(r'(\'[^\']*\'|"[^"]*")\s*').match_commentclose = re.compile(r'--\s*>')_markedsectionclose = re.compile(r']\s*]\s*>')# An analysis of the MS-Word extensions is available at# http://www.planetpublish.com/xmlarena/xap/Thursday/WordtoXML.pdf_msmarkedsectionclose = re.compile(r']\s*>')del reclass ParserBase:"""Parser base class which provides some common support methods usedby the SGML/HTML and XHTML parsers."""def __init__(self):if self.__class__ is ParserBase:raise RuntimeError("_markupbase.ParserBase must be subclassed")def error(self, message):raise NotImplementedError("subclasses of ParserBase must override error()")def reset(self):self.lineno = 1self.offset = 0def getpos(self):"""Return current line number and offset."""return self.lineno, self.offset# Internal -- update line number and offset. This should be# called for each piece of data exactly once, in order -- in other# words the concatenation of all the input strings to this# function should be exactly the entire input.def updatepos(self, i, j):if i >= j:return jrawdata = self.rawdatanlines = rawdata.count("\n", i, j)if nlines:self.lineno = self.lineno + nlinespos = rawdata.rindex("\n", i, j) # Should not failself.offset = j-(pos+1)else:self.offset = self.offset + j-ireturn j_decl_otherchars = ''# Internal -- parse declaration (for use by subclasses).def parse_declaration(self, i):# This is some sort of declaration; in "HTML as# deployed," this should only be the document type# declaration ("<!DOCTYPE html...>").# ISO 8879:1986, however, has more complex# declaration syntax for elements in <!...>, including:# --comment--# [marked section]# name in the following list: ENTITY, DOCTYPE, ELEMENT,# ATTLIST, NOTATION, SHORTREF, USEMAP,# LINKTYPE, LINK, IDLINK, USELINK, SYSTEMrawdata = self.rawdataj = i + 2assert rawdata[i:j] == "<!", "unexpected call to parse_declaration"if rawdata[j:j+1] == ">":# the empty comment <!>return j + 1if rawdata[j:j+1] in ("-", ""):# Start of comment followed by buffer boundary,# or just a buffer boundary.return -1# A simple, practical version could look like: ((name|stringlit) S*) + '>'n = len(rawdata)if rawdata[j:j+2] == '--': #comment# Locate --.*-- as the body of the commentreturn self.parse_comment(i)elif rawdata[j] == '[': #marked section# Locate [statusWord [...arbitrary SGML...]] as the body of the marked section# Where statusWord is one of TEMP, CDATA, IGNORE, INCLUDE, RCDATA# Note that this is extended by Microsoft Office "Save as Web" function# to include [if...] and [endif].return self.parse_marked_section(i)else: #all other declaration elementsdecltype, j = self._scan_name(j, i)if j < 0:return jif decltype == "doctype":self._decl_otherchars = ''while j < n:c = rawdata[j]if c == ">":# end of declaration syntaxdata = rawdata[i+2:j]if decltype == "doctype":self.handle_decl(data)else:# According to the HTML5 specs sections "8.2.4.44 Bogus# comment state" and "8.2.4.45 Markup declaration open# state", a comment token should be emitted.# Calling unknown_decl provides more flexibility though.self.unknown_decl(data)return j + 1if c in "\"'":m = _declstringlit_match(rawdata, j)if not m:return -1 # incompletej = m.end()elif c in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ":name, j = self._scan_name(j, i)elif c in self._decl_otherchars:j = j + 1elif c == "[":# this could be handled in a separate doctype parserif decltype == "doctype":j = self._parse_doctype_subset(j + 1, i)elif decltype in {"attlist", "linktype", "link", "element"}:# must tolerate []'d groups in a content model in an element declaration# also in data attribute specifications of attlist declaration# also link type declaration subsets in linktype declarations# also link attribute specification lists in link declarationsself.error("unsupported '[' char in %s declaration" % decltype)else:self.error("unexpected '[' char in declaration")else:self.error("unexpected %r char in declaration" % rawdata[j])if j < 0:return jreturn -1 # incomplete# Internal -- parse a marked section# Override this to handle MS-word extension syntax <![if word]>content<![endif]>def parse_marked_section(self, i, report=1):rawdata= self.rawdataassert rawdata[i:i+3] == '<![', "unexpected call to parse_marked_section()"sectName, j = self._scan_name( i+3, i )if j < 0:return jif sectName in {"temp", "cdata", "ignore", "include", "rcdata"}:# look for standard ]]> endingmatch= _markedsectionclose.search(rawdata, i+3)elif sectName in {"if", "else", "endif"}:# look for MS Office ]> endingmatch= _msmarkedsectionclose.search(rawdata, i+3)else:self.error('unknown status keyword %r in marked section' % rawdata[i+3:j])if not match:return -1if report:j = match.start(0)self.unknown_decl(rawdata[i+3: j])return match.end(0)# Internal -- parse comment, return length or -1 if not terminateddef parse_comment(self, i, report=1):rawdata = self.rawdataif rawdata[i:i+4] != '<!--':self.error('unexpected call to parse_comment()')match = _commentclose.search(rawdata, i+4)if not match:return -1if report:j = match.start(0)self.handle_comment(rawdata[i+4: j])return match.end(0)# Internal -- scan past the internal subset in a <!DOCTYPE declaration,# returning the index just past any whitespace following the trailing ']'.def _parse_doctype_subset(self, i, declstartpos):rawdata = self.rawdatan = len(rawdata)j = iwhile j < n:c = rawdata[j]if c == "<":s = rawdata[j:j+2]if s == "<":# end of buffer; incompletereturn -1if s != "<!":self.updatepos(declstartpos, j + 1)self.error("unexpected char in internal subset (in %r)" % s)if (j + 2) == n:# end of buffer; incompletereturn -1if (j + 4) > n:# end of buffer; incompletereturn -1if rawdata[j:j+4] == "<!--":j = self.parse_comment(j, report=0)if j < 0:return jcontinuename, j = self._scan_name(j + 2, declstartpos)if j == -1:return -1if name not in {"attlist", "element", "entity", "notation"}:self.updatepos(declstartpos, j + 2)self.error("unknown declaration %r in internal subset" % name)# handle the individual namesmeth = getattr(self, "_parse_doctype_" + name)j = meth(j, declstartpos)if j < 0:return jelif c == "%":# parameter entity referenceif (j + 1) == n:# end of buffer; incompletereturn -1s, j = self._scan_name(j + 1, declstartpos)if j < 0:return jif rawdata[j] == ";":j = j + 1elif c == "]":j = j + 1while j < n and rawdata[j].isspace():j = j + 1if j < n:if rawdata[j] == ">":return jself.updatepos(declstartpos, j)self.error("unexpected char after internal subset")else:return -1elif c.isspace():j = j + 1else:self.updatepos(declstartpos, j)self.error("unexpected char %r in internal subset" % c)# end of buffer reachedreturn -1# Internal -- scan past <!ELEMENT declarationsdef _parse_doctype_element(self, i, declstartpos):name, j = self._scan_name(i, declstartpos)if j == -1:return -1# style content model; just skip until '>'rawdata = self.rawdataif '>' in rawdata[j:]:return rawdata.find(">", j) + 1return -1# Internal -- scan past <!ATTLIST declarationsdef _parse_doctype_attlist(self, i, declstartpos):rawdata = self.rawdataname, j = self._scan_name(i, declstartpos)c = rawdata[j:j+1]if c == "":return -1if c == ">":return j + 1while 1:# scan a series of attribute descriptions; simplified:# name type [value] [#constraint]name, j = self._scan_name(j, declstartpos)if j < 0:return jc = rawdata[j:j+1]if c == "":return -1if c == "(":# an enumerated type; look for ')'if ")" in rawdata[j:]:j = rawdata.find(")", j) + 1else:return -1while rawdata[j:j+1].isspace():j = j + 1if not rawdata[j:]:# end of buffer, incompletereturn -1else:name, j = self._scan_name(j, declstartpos)c = rawdata[j:j+1]if not c:return -1if c in "'\"":m = _declstringlit_match(rawdata, j)if m:j = m.end()else:return -1c = rawdata[j:j+1]if not c:return -1if c == "#":if rawdata[j:] == "#":# end of bufferreturn -1name, j = self._scan_name(j + 1, declstartpos)if j < 0:return jc = rawdata[j:j+1]if not c:return -1if c == '>':# all donereturn j + 1# Internal -- scan past <!NOTATION declarationsdef _parse_doctype_notation(self, i, declstartpos):name, j = self._scan_name(i, declstartpos)if j < 0:return jrawdata = self.rawdatawhile 1:c = rawdata[j:j+1]if not c:# end of buffer; incompletereturn -1if c == '>':return j + 1if c in "'\"":m = _declstringlit_match(rawdata, j)if not m:return -1j = m.end()else:name, j = self._scan_name(j, declstartpos)if j < 0:return j# Internal -- scan past <!ENTITY declarationsdef _parse_doctype_entity(self, i, declstartpos):rawdata = self.rawdataif rawdata[i:i+1] == "%":j = i + 1while 1:c = rawdata[j:j+1]if not c:return -1if c.isspace():j = j + 1else:breakelse:j = iname, j = self._scan_name(j, declstartpos)if j < 0:return jwhile 1:c = self.rawdata[j:j+1]if not c:return -1if c in "'\"":m = _declstringlit_match(rawdata, j)if m:j = m.end()else:return -1 # incompleteelif c == ">":return j + 1else:name, j = self._scan_name(j, declstartpos)if j < 0:return j# Internal -- scan a name token and the new position and the token, or# return -1 if we've reached the end of the buffer.def _scan_name(self, i, declstartpos):rawdata = self.rawdatan = len(rawdata)if i == n:return None, -1m = _declname_match(rawdata, i)if m:s = m.group()name = s.strip()if (i + len(s)) == n:return None, -1 # end of bufferreturn name.lower(), m.end()else:self.updatepos(declstartpos, i)self.error("expected name token at %r"% rawdata[declstartpos:declstartpos+20])# To be overridden -- handlers for unknown objectsdef unknown_decl(self, data):pass
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。