|
|
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]])}
|