def _setCssText(self, cssText): self._checkReadonly() prods = Sequence( # PreDef.unary(), Choice(PreDef.dimension(stop=True), PreDef.number(stop=True), PreDef.percentage(stop=True) ) ) ok, seq, store, unused = ProdParser().parse(cssText, 'DimensionValue', prods) self.wellformed = ok if ok: item = seq[0] sign, v, d = self.__reUnNumDim.findall( normalize(item.value))[0] if '.' in v: val = float(sign + v) else: val = int(sign + v) dim = None if d: dim = d self._sign = sign self._value = val self._dimension = dim self._type = item.type self._setSeq(seq)
def _setCssText(self, cssText): self._checkReadonly() types = self._prods # rename! _operator = Choice(Prod(name='Operator */', match=lambda t, v: v in '*/', toSeq=lambda t, tokens: (t[0], t[1]) ), Sequence( PreDef.S(), Choice( Sequence( Prod(name='Operator */', match=lambda t, v: v in '*/', toSeq=lambda t, tokens: (t[0], t[1]) ), PreDef.S(optional=True) ), Sequence( Prod(name='Operator +-', match=lambda t, v: v in '+-', toSeq=lambda t, tokens: (t[0], t[1]) ), PreDef.S() ), PreDef.funcEnd(stop=True, mayEnd=True) ) ) ) def _operant(): return Choice(_DimensionProd(self), _CSSVariableProd(self)) prods = Sequence(Prod(name='CALC', match=lambda t, v: t == types.FUNCTION and normalize(v) == 'calc(' ), PreDef.S(optional=True), _operant(), Sequence(_operator, _operant(), minmax=lambda: (0, None) ), PreDef.funcEnd(stop=True) ) # store: name of variable ok, seq, store, unused = ProdParser().parse(cssText, 'CSSCalc', prods, checkS=True) self.wellformed = ok if ok: self._setSeq(seq)
def expression(): return Sequence(PreDef.char(name='expression', char='('), Prod(name='media_feature', match=lambda t, v: t == PreDef.types.IDENT ), Sequence(PreDef.char(name='colon', char=':'), css_parser.css.value.MediaQueryValueProd(self), minmax=lambda: (0, 1) # optional ), PreDef.char(name='expression END', char=')', stopIfNoMoreMatch=self._partof ) ) prods = Choice(Sequence(Prod(name='ONLY|NOT', # media_query
def _setCssText(self, cssText): self._checkReadonly() prods = Choice(PreDef.hexcolor(stop=True), PreDef.ident(stop=True), PreDef.string(stop=True), PreDef.unicode_range(stop=True), ) ok, seq, store, unused = ProdParser().parse(cssText, 'Value', prods) self.wellformed = ok if ok: # only 1 value anyway! self._type = seq[0].type self._value = seq[0].value self._setSeq(seq)
def _CSSVariableProd(parent, nextSor=False, toStore=None): return PreDef.variable(nextSor=nextSor, toStore=toStore, toSeq=lambda t, tokens: (CSSVariable._functionName, CSSVariable( pushtoken(t, tokens), parent=parent) ) )
def setVariable(self, variableName, value): """Used to set a variable value within this variable declaration block. :param variableName: The name of the CSS variable. :param value: The new value of the variable, may also be a PropertyValue object. :exceptions: - :exc:`~xml.dom.SyntaxErr`: Raised if the specified value has a syntax error and is unparsable. - :exc:`~xml.dom.NoModificationAllowedErr`: Raised if this declaration is readonly or the property is readonly. """ self._checkReadonly() # check name wellformed, seq, store, unused = \ ProdParser().parse(normalize(variableName), 'variableName', Sequence(PreDef.ident())) if not wellformed: self._log.error('Invalid variableName: %r: %r' % (variableName, value)) else: # check value if isinstance(value, PropertyValue): v = value else: v = PropertyValue(cssText=value, parent=self) if not v.wellformed: self._log.error('Invalid variable value: %r: %r' % (variableName, value)) else: # update seq self.seq._readonly = False variableName = normalize(variableName) if variableName in self._vars: for i, x in enumerate(self.seq): if x.value[0] == variableName: self.seq.replace(i, [variableName, v], x.type, x.line, x.col) break else: self.seq.append([variableName, v], 'var') self.seq._readonly = True self._vars[variableName] = v
def _setCssText(self, cssText): self._checkReadonly() prods = Sequence(PreDef.uri(stop=True)) ok, seq, store, unused = ProdParser().parse(cssText, 'URIValue', prods) self.wellformed = ok if ok: # only 1 value only anyway self._type = seq[0].type self._value = seq[0].value self._setSeq(seq)
def _setCssText(self, cssText): self._checkReadonly() types = self._prods # rename! prods = Sequence(Prod(name='var', match=lambda t, v: t == types.FUNCTION and normalize(v) == 'var(' ), PreDef.ident(toStore='ident'), Sequence(PreDef.comma(), Choice(_ColorProd(self, toStore='fallback'), _DimensionProd(self, toStore='fallback'), _URIProd(self, toStore='fallback'), _ValueProd(self, toStore='fallback'), _CalcValueProd(self, toStore='fallback'), _CSSVariableProd(self, toStore='fallback'), _CSSFunctionProd(self, toStore='fallback') ), minmax=lambda: (0, 1) ), PreDef.funcEnd(stop=True)) # store: name of variable store = {'ident': None, 'fallback': None} ok, seq, store, unused = ProdParser().parse(cssText, 'CSSVariable', prods) self.wellformed = ok if ok: self._name = store['ident'].value try: self._fallback = store['fallback'].value except KeyError: self._fallback = None self._setSeq(seq)
def _productions(self): """Return definition used for parsing.""" types = self._prods # rename! itemProd = Choice(_ColorProd(self), _DimensionProd(self), _URIProd(self), _ValueProd(self), _CalcValueProd(self), _CSSVariableProd(self), _CSSFunctionProd(self) ) funcProds = Sequence(Prod(name='FUNCTION', match=lambda t, v: t == types.FUNCTION, toSeq=lambda t, tokens: (t[0], normalize(t[1]))), Choice(Sequence(itemProd, Sequence(PreDef.comma(optional=True), itemProd, minmax=lambda: (0, None)), PreDef.funcEnd(stop=True)), PreDef.funcEnd(stop=True)) ) return funcProds
def _productions(self): """Return definition used for parsing.""" types = self._prods # rename! func = Prod(name='MSValue-Sub', match=lambda t, v: t == self._prods.FUNCTION, toSeq=lambda t, tokens: (MSValue._functionName, MSValue(pushtoken(t, tokens ), parent=self ) ) ) funcProds = Sequence(Prod(name='FUNCTION', match=lambda t, v: t == types.FUNCTION, toSeq=lambda t, tokens: (t[0], t[1]) ), Sequence(Choice(_ColorProd(self), _DimensionProd(self), _URIProd(self), _ValueProd(self), _MSValueProd(self), # _CalcValueProd(self), _CSSVariableProd(self), func, # _CSSFunctionProd(self), Prod(name='MSValuePart', match=lambda t, v: v != ')', toSeq=lambda t, tokens: (t[0], t[1]) ) ), minmax=lambda: (0, None) ), PreDef.funcEnd(stop=True) ) return funcProds
def _setCssText(self, cssText): if isinstance(cssText, (int, float)): cssText = text_type(cssText) # if it is a number """ Format:: unary_operator : '-' | '+' ; operator : '/' S* | ',' S* | /* empty */ ; expr : term [ operator term ]* ; term : unary_operator? [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* | TIME S* | FREQ S* ] | STRING S* | IDENT S* | URI S* | hexcolor | function | UNICODE-RANGE S* ; function : FUNCTION S* expr ')' S* ; /* * There is a constraint on the color that it must * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F]) * after the "#"; e.g., "#000" is OK, but "#abcd" is not. */ hexcolor : HASH S* ; :exceptions: - :exc:`~xml.dom.SyntaxErr`: Raised if the specified CSS string value has a syntax error (according to the attached property) or is unparsable. - :exc:`~xml.dom.InvalidModificationErr`: TODO: Raised if the specified CSS string value represents a different type of values than the values allowed by the CSS property. - :exc:`~xml.dom.NoModificationAllowedErr`: Raised if this value is readonly. """ self._checkReadonly() # used as operator is , / or S nextSor = ',/' term = Choice(_ColorProd(self, nextSor), _DimensionProd(self, nextSor), _URIProd(self, nextSor), _ValueProd(self, nextSor), # _Rect(self, nextSor), # all other functions _CSSVariableProd(self, nextSor), _MSValueProd(self, nextSor), _CalcValueProd(self, nextSor), _CSSFunctionProd(self, nextSor) ) operator = Choice(PreDef.S(toSeq=False), PreDef.char('comma', ',', toSeq=lambda t, tokens: ('operator', t[1]), optional=True ), PreDef.char('slash', '/', toSeq=lambda t, tokens: ('operator', t[1]), optional=True), optional=True) prods = Sequence(term, Sequence( # mayEnd this Sequence if whitespace operator, # TODO: only when setting via other class # used by variabledeclaration currently PreDef.char('END', ';', stopAndKeep=True, optional=True), # TODO: } and !important ends too! term, minmax=lambda: (0, None))) # parse ok, seq, store, unused = ProdParser().parse(cssText, 'PropertyValue', prods) # must be at least one value! ok = ok and len(as_list(self.__items(seq))) > 0 for item in seq: if hasattr(item.value, 'wellformed') and not item.value.wellformed: ok = False break self.wellformed = ok if ok: self._setSeq(seq) else: self._log.error('PropertyValue: Unknown syntax or no value: %s' % self._valuestr(cssText))
def _setCssText(self, cssText): self._checkReadonly() types = self._prods # rename! component = Choice( PreDef.unary(toSeq=lambda t, tokens: (t[ 0], DimensionValue(pushtoken(t, tokens), parent=self))), PreDef.number(toSeq=lambda t, tokens: (t[ 0], DimensionValue(pushtoken(t, tokens), parent=self))), PreDef.percentage(toSeq=lambda t, tokens: (t[ 0], DimensionValue(pushtoken(t, tokens), parent=self)))) noalp = Sequence( Prod(name='FUNCTION', match=lambda t, v: t == types.FUNCTION and v.lower() in ('rgb(', 'hsl('), toSeq=lambda t, tokens: (t[0], normalize(t[1]))), component, Sequence(PreDef.comma(optional=True), component, minmax=lambda: (2, 2)), PreDef.funcEnd(stop=True)) witha = Sequence( Prod(name='FUNCTION', match=lambda t, v: t == types.FUNCTION and v.lower() in ('rgba(', 'hsla('), toSeq=lambda t, tokens: (t[0], normalize(t[1]))), component, Sequence(PreDef.comma(optional=True), component, minmax=lambda: (3, 3)), PreDef.funcEnd(stop=True)) namedcolor = Prod(name='Named Color', match=lambda t, v: t == 'IDENT' and (normalize(v) in as_list(self.COLORS.keys())), stop=True) prods = Choice(PreDef.hexcolor(stop=True), namedcolor, noalp, witha) ok, seq, store, unused = ProdParser().parse(cssText, self.type, prods) self.wellformed = ok if ok: t, v = seq[0].type, seq[0].value if 'IDENT' == t: rgba = self.COLORS[normalize(v)] if 'HASH' == t: if len(v) == 4: # HASH #rgb rgba = (int(2 * v[1], 16), int(2 * v[2], 16), int(2 * v[3], 16), 1.0) else: # HASH #rrggbb rgba = (int(v[1:3], 16), int(v[3:5], 16), int(v[5:7], 16), 1.0) elif 'FUNCTION' == t: functiontype, raw, check = None, [], '' HSL = False for item in seq: try: type_ = item.value.type except AttributeError: # type of function, e.g. rgb( if item.type == 'FUNCTION': functiontype = item.value HSL = functiontype in ('hsl(', 'hsla(') continue # save components if type_ == Value.NUMBER: raw.append(item.value.value) check += 'N' elif type_ == Value.PERCENTAGE: if HSL: # save as percentage fraction raw.append(item.value.value / 100.0) else: # save as real value of percentage of 255 raw.append(int(255 * item.value.value / 100)) check += 'P' if HSL: # convert to rgb # h is 360 based (circle) h, s, l_ = raw[0] / 360.0, raw[1], raw[2] # ORDER h l_ s !!! r, g, b = colorsys.hls_to_rgb(h, l_, s) # back to 255 based rgba = [ int(round(r * 255)), int(round(g * 255)), int(round(b * 255)) ] if len(raw) > 3: rgba.append(raw[3]) else: # rgb, rgba rgba = raw if len(rgba) < 4: rgba.append(1.0) # validate checks = { 'rgb(': ('NNN', 'PPP'), 'rgba(': ('NNNN', 'PPPN'), 'hsl(': ('NPP', ), 'hsla(': ('NPPN', ) } if check not in checks[functiontype]: self._log.error('ColorValue has invalid %s) parameters: ' '%s (N=Number, P=Percentage)' % (functiontype, check)) self._colorType = t self._red, self._green, self._blue, self._alpha = tuple(rgba) self._setSeq(seq)
def _CSSVariableProd(parent, nextSor=False, toStore=None): return PreDef.variable(nextSor=nextSor, toStore=toStore, toSeq=lambda t, tokens: (CSSVariable._functionName, CSSVariable(pushtoken(t, tokens), parent=parent)))
def _setCssText(self, cssText): self._checkReadonly() types = self._prods # rename! component = Choice(PreDef.unary(toSeq=lambda t, tokens: (t[0], DimensionValue(pushtoken(t, tokens), parent=self) )), PreDef.number(toSeq=lambda t, tokens: (t[0], DimensionValue(pushtoken(t, tokens), parent=self) )), PreDef.percentage(toSeq=lambda t, tokens: (t[0], DimensionValue(pushtoken(t, tokens), parent=self) )) ) noalp = Sequence(Prod(name='FUNCTION', match=lambda t, v: t == types.FUNCTION and v.lower() in ('rgb(', 'hsl('), toSeq=lambda t, tokens: (t[0], normalize(t[1]))), component, Sequence(PreDef.comma(optional=True), component, minmax=lambda: (2, 2) ), PreDef.funcEnd(stop=True) ) witha = Sequence(Prod(name='FUNCTION', match=lambda t, v: t == types.FUNCTION and v.lower() in ('rgba(', 'hsla('), toSeq=lambda t, tokens: (t[0], normalize(t[1])) ), component, Sequence(PreDef.comma(optional=True), component, minmax=lambda: (3, 3) ), PreDef.funcEnd(stop=True) ) namedcolor = Prod(name='Named Color', match=lambda t, v: t == 'IDENT' and ( normalize(v) in as_list(self.COLORS.keys()) ), stop=True) prods = Choice(PreDef.hexcolor(stop=True), namedcolor, noalp, witha) ok, seq, store, unused = ProdParser().parse(cssText, self.type, prods) self.wellformed = ok if ok: t, v = seq[0].type, seq[0].value if 'IDENT' == t: rgba = self.COLORS[normalize(v)] if 'HASH' == t: if len(v) == 4: # HASH #rgb rgba = (int(2*v[1], 16), int(2*v[2], 16), int(2*v[3], 16), 1.0) else: # HASH #rrggbb rgba = (int(v[1:3], 16), int(v[3:5], 16), int(v[5:7], 16), 1.0) elif 'FUNCTION' == t: functiontype, raw, check = None, [], '' HSL = False for item in seq: try: type_ = item.value.type except AttributeError: # type of function, e.g. rgb( if item.type == 'FUNCTION': functiontype = item.value HSL = functiontype in ('hsl(', 'hsla(') continue # save components if type_ == Value.NUMBER: raw.append(item.value.value) check += 'N' elif type_ == Value.PERCENTAGE: if HSL: # save as percentage fraction raw.append(item.value.value / 100.0) else: # save as real value of percentage of 255 raw.append(int(255 * item.value.value / 100)) check += 'P' if HSL: # convert to rgb # h is 360 based (circle) h, s, l_ = raw[0] / 360.0, raw[1], raw[2] # ORDER h l_ s !!! r, g, b = colorsys.hls_to_rgb(h, l_, s) # back to 255 based rgba = [int(round(r*255)), int(round(g*255)), int(round(b*255))] if len(raw) > 3: rgba.append(raw[3]) else: # rgb, rgba rgba = raw if len(rgba) < 4: rgba.append(1.0) # validate checks = {'rgb(': ('NNN', 'PPP'), 'rgba(': ('NNNN', 'PPPN'), 'hsl(': ('NPP',), 'hsla(': ('NPPN',) } if check not in checks[functiontype]: self._log.error('ColorValue has invalid %s) parameters: ' '%s (N=Number, P=Percentage)' % (functiontype, check)) self._colorType = t self._red, self._green, self._blue, self._alpha = tuple(rgba) self._setSeq(seq)
def MediaQueryValueProd(parent): return Choice(_ColorProd(parent), _DimensionProd(parent), _ValueProd(parent), PreDef.ratio(), )
def _setCssText(self, cssText): if isinstance(cssText, (int, float)): cssText = text_type(cssText) # if it is a number """ Format:: unary_operator : '-' | '+' ; operator : '/' S* | ',' S* | /* empty */ ; expr : term [ operator term ]* ; term : unary_operator? [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* | TIME S* | FREQ S* ] | STRING S* | IDENT S* | URI S* | hexcolor | function | UNICODE-RANGE S* ; function : FUNCTION S* expr ')' S* ; /* * There is a constraint on the color that it must * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F]) * after the "#"; e.g., "#000" is OK, but "#abcd" is not. */ hexcolor : HASH S* ; :exceptions: - :exc:`~xml.dom.SyntaxErr`: Raised if the specified CSS string value has a syntax error (according to the attached property) or is unparsable. - :exc:`~xml.dom.InvalidModificationErr`: TODO: Raised if the specified CSS string value represents a different type of values than the values allowed by the CSS property. - :exc:`~xml.dom.NoModificationAllowedErr`: Raised if this value is readonly. """ self._checkReadonly() # used as operator is , / or S nextSor = ',/' term = Choice( _ColorProd(self, nextSor), _DimensionProd(self, nextSor), _URIProd(self, nextSor), _ValueProd(self, nextSor), # _Rect(self, nextSor), # all other functions _CSSVariableProd(self, nextSor), _MSValueProd(self, nextSor), _CalcValueProd(self, nextSor), _CSSFunctionProd(self, nextSor)) operator = Choice(PreDef.S(toSeq=False), PreDef.char('comma', ',', toSeq=lambda t, tokens: ('operator', t[1]), optional=True), PreDef.char('slash', '/', toSeq=lambda t, tokens: ('operator', t[1]), optional=True), optional=True) prods = Sequence( term, Sequence( # mayEnd this Sequence if whitespace operator, # TODO: only when setting via other class # used by variabledeclaration currently PreDef.char('END', ';', stopAndKeep=True, optional=True), # TODO: } and !important ends too! term, minmax=lambda: (0, None))) # parse ok, seq, store, unused = ProdParser().parse(cssText, 'PropertyValue', prods) # must be at least one value! ok = ok and len(as_list(self.__items(seq))) > 0 for item in seq: if hasattr(item.value, 'wellformed') and not item.value.wellformed: ok = False break self.wellformed = ok if ok: self._setSeq(seq) else: self._log.error('PropertyValue: Unknown syntax or no value: %s' % self._valuestr(cssText))
def _setCssText(self, cssText): """Setting this attribute will result in the parsing of the new value and resetting of all the properties in the declaration block including the removal or addition of properties. :exceptions: - :exc:`~xml.dom.NoModificationAllowedErr`: Raised if this declaration is readonly or a property is readonly. - :exc:`~xml.dom.SyntaxErr`: Raised if the specified CSS string value has a syntax error and is unparsable. Format:: variableset : vardeclaration [ ';' S* vardeclaration ]* ; vardeclaration : varname ':' S* term ; varname : IDENT S* ; expr : [ VARCALL | term ] [ operator [ VARCALL | term ] ]* ; """ self._checkReadonly() vardeclaration = Sequence( PreDef.ident(), PreDef.char(':', ':', toSeq=False, optional=True), # PreDef.S(toSeq=False, optional=True), Prod(name='term', match=lambda t, v: True, toSeq=lambda t, tokens: ('value', PropertyValue(itertools.chain([t], tokens), parent=self) ) ) ) prods = Sequence(vardeclaration, Sequence(PreDef.S(optional=True), PreDef.char(';', ';', toSeq=False, optional=True), PreDef.S(optional=True), vardeclaration, minmax=lambda: (0, None)), PreDef.S(optional=True), PreDef.char(';', ';', toSeq=False, optional=True) ) # parse wellformed, seq, store, notused = \ ProdParser().parse(cssText, 'CSSVariableDeclaration', prods, emptyOk=True) if wellformed: newseq = self._tempSeq() newvars = {} # seq contains only name: value pairs plus comments etc nameitem = None for item in seq: if 'IDENT' == item.type: nameitem = item elif 'value' == item.type: nname = normalize(nameitem.value) if nname in newvars: # replace var with same name for i, it in enumerate(newseq): if normalize(it.value[0]) == nname: newseq.replace(i, (nameitem.value, item.value), 'var', nameitem.line, nameitem.col) else: # saved non normalized name for reserialization newseq.append((nameitem.value, item.value), 'var', nameitem.line, nameitem.col) # newseq.append((nameitem.value, item.value), # 'var', # nameitem.line, nameitem.col) newvars[nname] = item.value else: newseq.appendItem(item) self._setSeq(newseq) self._vars = newvars self.wellformed = True
def prods(): return Sequence(PreDef.char(';', ';'), PreDef.char(':', ':'))
def _setMediaText(self, mediaText): """ :param mediaText: simple value or comma-separated list of media :exceptions: - - :exc:`~xml.dom.SyntaxErr`: Raised if the specified string value has a syntax error and is unparsable. - - :exc:`~xml.dom.NoModificationAllowedErr`: Raised if this media list is readonly. """ self._checkReadonly() def mediaquery(): return Prod(name='MediaQueryStart', match=lambda t, v: t == 'IDENT' or v == '(', toSeq=lambda t, tokens: ('MediaQuery', MediaQuery(pushtoken(t, tokens), _partof = True)) ) prods = Sequence(Sequence(PreDef.comment(parent=self), minmax=lambda: (0, None) ), mediaquery(), Sequence(PreDef.comma(toSeq=False), mediaquery(), minmax=lambda: (0, None)) ) # parse ok, seq, store, unused = ProdParser().parse(mediaText, 'MediaList', prods, debug="ml") # each mq must be valid atleastone = False for item in seq: v = item.value if isinstance(v, MediaQuery): if not v.wellformed: ok = False break else: atleastone = True # must be at least one value! if not atleastone: ok = False self._wellformed = ok self._log.error('MediaQuery: No content.', error=xml.dom.SyntaxErr) self._wellformed = ok if ok: mediaTypes = [] finalseq = css_parser.util.Seq(readonly=False) commentseqonly = css_parser.util.Seq(readonly=False) for item in seq: # filter for doubles? if item.type == 'MediaQuery': mediaType = item.value.mediaType if mediaType: if mediaType == 'all': # remove anthing else and keep all+comments(!) only finalseq = commentseqonly finalseq.append(item) break elif mediaType in mediaTypes: continue else: mediaTypes.append(mediaType) elif isinstance(item.value, css_parser.css.csscomment.CSSComment): commentseqonly.append(item) finalseq.append(item) self._setSeq(finalseq)
def _setCssText(self, cssText): """Setting this attribute will result in the parsing of the new value and resetting of all the properties in the declaration block including the removal or addition of properties. :exceptions: - :exc:`~xml.dom.NoModificationAllowedErr`: Raised if this declaration is readonly or a property is readonly. - :exc:`~xml.dom.SyntaxErr`: Raised if the specified CSS string value has a syntax error and is unparsable. Format:: variableset : vardeclaration [ ';' S* vardeclaration ]* ; vardeclaration : varname ':' S* term ; varname : IDENT S* ; expr : [ VARCALL | term ] [ operator [ VARCALL | term ] ]* ; """ self._checkReadonly() vardeclaration = Sequence( PreDef.ident(), PreDef.char(':', ':', toSeq=False, optional=True), # PreDef.S(toSeq=False, optional=True), Prod(name='term', match=lambda t, v: True, toSeq=lambda t, tokens: ('value', PropertyValue(itertools.chain([t], tokens), parent=self)))) prods = Sequence( vardeclaration, Sequence(PreDef.S(optional=True), PreDef.char(';', ';', toSeq=False, optional=True), PreDef.S(optional=True), vardeclaration, minmax=lambda: (0, None)), PreDef.S(optional=True), PreDef.char(';', ';', toSeq=False, optional=True)) # parse wellformed, seq, store, notused = \ ProdParser().parse(cssText, 'CSSVariableDeclaration', prods, emptyOk=True) if wellformed: newseq = self._tempSeq() newvars = {} # seq contains only name: value pairs plus comments etc nameitem = None for item in seq: if 'IDENT' == item.type: nameitem = item elif 'value' == item.type: nname = normalize(nameitem.value) if nname in newvars: # replace var with same name for i, it in enumerate(newseq): if normalize(it.value[0]) == nname: newseq.replace(i, (nameitem.value, item.value), 'var', nameitem.line, nameitem.col) else: # saved non normalized name for reserialization newseq.append((nameitem.value, item.value), 'var', nameitem.line, nameitem.col) # newseq.append((nameitem.value, item.value), # 'var', # nameitem.line, nameitem.col) newvars[nname] = item.value else: newseq.appendItem(item) self._setSeq(newseq) self._vars = newvars self.wellformed = True