from pyparsing import Literal, Word, ZeroOrMore, Group, Dict, Optional,\ restOfLine, empty, printables, OneOrMore, oneOf, nums,\ lineno, line, col, Keyword, SkipTo, LineEnd, Combine from enum import Enum class CalculateIniParser: '''Класс парсера calculate.ini файлов.''' class Define(Enum): assign = 0 append = 1 remove = 2 def __init__(self): self.operations = {"=": self.Define.assign, "+=": self.Define.append, "-=": self.Define.remove} lbrack = Literal("[") rbrack = Literal("]") # comma = Literal(",").suppress() comment_symbol = Literal(';') | Literal('#') # Define = self.Define value_operation = (Literal("=") | Combine(Literal("+") + Literal("=")) | Combine(Literal("-") + Literal("="))) comment = comment_symbol + Optional(restOfLine) section_name = Word(printables+'\t', excludeChars='[]') value_name = Word(printables+'\t', excludeChars='=-+') # non_comma = Word(printables+'\t', excludeChars=',') clear_section = lbrack.suppress() + Group(empty) + rbrack.suppress() section_start = Group(OneOrMore(lbrack.suppress() + section_name + rbrack.suppress()) + (clear_section | ~lbrack()) + LineEnd().suppress()) # Если содержимое ini-файла не предваряется заголовком секции, # значит эта строка ошибочна. unexpected = Group(~section_start + SkipTo(LineEnd(), include=True))("error") unexpected.setParseAction(self._unexpected_token) key_value = (~lbrack + value_name + value_operation + empty + restOfLine + LineEnd().suppress()) def strip_key_value(tokens): tokens[0] = tokens[0].strip() tokens[1] = tokens[1].strip() key_value.setParseAction(strip_key_value) self.ini_section_parser = (section_start + Group(ZeroOrMore( Group(key_value | unexpected))) | unexpected) self.ini_section_parser.ignore(comment) def _unexpected_token(self, string, location, tokens): '''Метод вызываемый парсером, если обнаружена некорректная строка, предназначен для получения некорректной строки и ее дальнейшего разбора.''' error_line = line(location, string).strip() if error_line: return (error_line, lineno(location, string), col(location, string)) def parse(self, data): for tokens, start, end in self.ini_section_parser.scanString(data): if tokens.getName() == "error": print('error tokens: {}'.format(tokens)) yield {'error': tokens} section, defkeys = tokens section_list = section.asList() if section_list[-1] == []: self.clear_section(section_list[:-1]) yield {'clear_section': (section_list[:-1], )} self.start_section(section.asList()) for defkey in defkeys: yield {'define_key': (section.asList(), defkey[0], defkey[2], self.operations[defkey[1]])}