def __init__(self, colors, start=0, *, dep: ['@NUTSHELL', '@TABLE'] = (None, None)): _nutshell, _table = dep self._vars = _table.vars if _table else {} self._packed_dict = None self._src = [i.split('#')[0].strip() for i in colors] self.colors = list(enumerate((k.split(':', 1) for k in self._src if k), 1)) if _nutshell is not None: self.colors = [(i, (v[0], _nutshell.replace_line(v[1]))) for i, v in self.colors] d = {} self.non_override_colors = set() for lno, (color, states) in self.colors: states = self._sep_states(multisplit(states, (None, ','))) if '..' in color: states = list(states) crange = ColorRange(len(states)-1, *(self.unpack(c.strip(), lno) for c in color.split('..'))) for state, color in zip(states, crange): d[state] = self.unpack(color, lno) continue color = self.unpack(color.strip(), lno) for term in states: try: state = int(term.lstrip('*')) except ValueError: raise Error(lno, f'State {term} is not an integer') d[state] = color if term.startswith('*'): self.non_override_colors.add(state) self.states = d
def _sep_states(self, start) -> dict: states, comments, dims = {}, {}, {} cur_states, cur_comments = set(), [] last_comment, last_comment_lno = None, 0 replace_constants = ( lambda x: x ) if self._nutshell is None else self._nutshell.replace_line for lno, line in enumerate(map(str.strip, self._src[start - 1:]), start): if not line: continue if line.startswith('#'): cur_comments.append(line) last_comment_lno = lno continue if last_comment_lno: *cur_comments, last_comment = cur_comments cur_states = set() for word in multisplit(replace_constants(last_comment), (None, ',')): if word.isdigit(): state = int(word) if not 0 < state < 256: raise Error( last_comment_lno, f'Icon declared for invalid state {state}') if state in states: raise Error( last_comment_lno, f'State {state} has already been assigned an icon' ) cur_states.add(state) elif word in self._vars: cur_states.update(self._vars[word]) elif TableRange.check(word): cur_states.update(TableRange(word)) last_comment_lno = 0 if cur_states: line = line.translate(TWO_STATE) for state in cur_states: states.setdefault(state, []).append(line) comments.setdefault(state, []).extend(cur_comments) cur_comments = [] for state, rle in states.items(): states[state] = rle[1:] dims[state] = list( map(int, chain.from_iterable(self._rDIMS.findall(rle[0])))) return states, comments, dims
def infile(path: Path): """Path to a file that defines @ICONS. Hyphen for stdin.""" with StreamProxy(path, alternate=sys.stdin, use_alternate=(path.name == '-')) as f: file = f.readlines() it = iter(file) # seek to @ICONS for i in iter(lambda: next(it).strip(), '@ICONS'): pass # Return line of and line after @RULE header (to extract rulename from), # as well as the lines on which icon colors are defined return ( next(((a.split(), b) for a, b in zip(*[map(str.strip, file)]*2) if a.startswith(('@RULE', '@NUTSHELL'))), None), [ multisplit(i.split('#')[0].strip(), (' ', ':'), amounts=(1, 1)) for i in takewhile(lambda s: not s.startswith('@'), it) if i.startswith(STARTS) ] )