You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

257 lines
10 KiB

4 years ago
# vim: fileencoding=utf-8
#
# ToDo: написать счетчик скобок для финальной оценки корректности
# документа.
#
from .base_format import Format
4 years ago
from collections import OrderedDict
from pyparsing import originalTextFor, Literal, Word, printables, alphanums,\
ParseException, Regex, Group, Optional, alphas, lineEnd,\
lineStart, Keyword
4 years ago
class DovecotFormat(Format):
FORMAT = 'dovecot'
EXECUTABLE = False
_initialized = False
comment_symbol = '#'
def __new__(cls, *args, **kwargs):
if not cls._initialized:
cls._initialize_parser()
return super().__new__(cls)
def __init__(self, document_text: str,
template_path: str,
ignore_comments: bool = False,
join_before: bool = False,
add_header: bool = False,
already_changed: bool = False,
**kwargs):
4 years ago
processing_methods = [self._parse_comment_line,
self._parse_section_start_line,
self._parse_include_line,
self._parse_section_end_line,
self._parse_parameter_line,
self._parse_parameter_to_delete_line]
super().__init__(processing_methods)
self._ignore_comments: bool = ignore_comments
self._comments_processing: bool = True
self._need_finish: bool = True
self._join_before: bool = join_before
4 years ago
self._section_stack: OrderedDict = OrderedDict()
self._current_section_name: str = ''
4 years ago
self._last_comments_list: list = []
4 years ago
self.header: str
if add_header and not ignore_comments:
self.header, document_text = self._get_header_and_document_text(
document_text,
template_path,
already_changed=already_changed)
else:
self.header = ''
document_text = document_text.strip()
4 years ago
if document_text == '':
self._document_dictionary = OrderedDict()
else:
document_lines = self._get_list_of_logic_lines(document_text)
self._lines_to_dictionary(document_lines)
@classmethod
def _initialize_parser(cls):
4 years ago
# Знаки пунктуации и действий.
left_brace = Literal('{')
right_brace = Literal('}')
action_symbols = (Literal('!') | Literal('-'))
cls._comment_line_parser = originalTextFor(
Literal(cls.comment_symbol)
+ Regex(r'.*'))('comment')
4 years ago
# Для парсинга строк с началом секций.
section = Word(alphas, alphanums+'-_', excludeChars='{}')
section_name = Word(printables, excludeChars='{}')
cls._section_start_parser = Group(Optional(action_symbols,
default='')('action')
+ section
+ Optional(section_name)
+ left_brace.suppress())('name')
4 years ago
# Для парсинга строк, указывающих конец секций.
cls._section_end_parser = lineStart() + right_brace + lineEnd()
4 years ago
# Для парсинга строк, содержащих параметры.
parameter_name = Word(alphas, alphanums+'_-', excludeChars='{}=')
parameter_value = Regex(r'\S+(\s+\S+)*')
4 years ago
cls._parameter_line_parser = (Group(Optional(action_symbols,
default='')('action')
+ parameter_name)('name')
+ Literal('=').suppress()
+ originalTextFor(
parameter_value
)('value'))
4 years ago
# Для парсинга строк с параметрами, подлежащими удалению.
cls._parameter_to_delete_parser = (
action_symbols('action')
+ parameter_name
+ Optional(Literal('=')).suppress()
)
4 years ago
# Для парсинга строк, содержащих директиву !include.
include = Keyword('!include') | Keyword('!include_try')
include_line_plain = (Optional(~action_symbols, default='')('action')
+ include('keyword') + Regex(r'\S+')('value'))
4 years ago
include_line_to_delete = (action_symbols('action')
+ include('keyword')
+ Regex(r'\S+')('value'))
4 years ago
cls._include_line_parser = (include_line_plain |
include_line_to_delete)
cls._initialized = True
4 years ago
def _parse_section_start_line(self, line):
try:
parsing_result = self._section_start_parser.parseString(line)
self._match = True
section_name = tuple(parsing_result.name.asList())
if not self._last_comments_list == []:
section_content = OrderedDict({'#': self._last_comments_list})
self._last_comments_list = []
else:
section_content = OrderedDict()
new_section = OrderedDict({section_name: section_content})
if self._section_stack == OrderedDict():
if section_name in self._document_dictionary:
new_section = OrderedDict(
{section_name:
self._document_dictionary[section_name]}
)
else:
if section_name in \
self._section_stack[self._current_section_name]:
new_section = OrderedDict({
section_name:
self._section_stack[self._current_section_name]
[section_name]
})
else:
self._section_stack[self._current_section_name].update(
new_section
)
self._section_stack.update(new_section)
self._current_section_name = section_name
except ParseException:
return
def _parse_section_end_line(self, line):
try:
self._section_end_parser.parseString(line)
self._match = True
last_section_name, last_section = self._section_stack.popitem()
if self._section_stack == OrderedDict():
self._item_to_add = OrderedDict({last_section_name:
last_section})
self._ready_to_update = True
self._current_section_name = ''
else:
self._current_section_name = next(reversed(
self._section_stack
))
except ParseException:
return
def _parse_parameter_line(self, line):
try:
parsing_result = self._parameter_line_parser.parseString(line)
self._match = True
parameter_name = tuple(parsing_result.name.asList())
parameter_value = (self._last_comments_list
+ [parsing_result.value.strip()])
self._last_comments_list = []
parameter = OrderedDict({parameter_name: parameter_value})
if self._section_stack == OrderedDict():
self._item_to_add = parameter
self._ready_to_update = True
else:
self._section_stack[self._current_section_name].update(
parameter
)
except ParseException:
return
def _parse_parameter_to_delete_line(self, line):
try:
parsing_result = self._parameter_to_delete_parser.parseString(line)
self._match = True
parameter_name = tuple(parsing_result.asList())
parameter_value = self._last_comments_list + ['']
self._last_comments_list = []
parameter = OrderedDict({parameter_name: parameter_value})
if self._section_stack == OrderedDict():
self._item_to_add = parameter
self._ready_to_update = True
else:
self._section_stack[self._current_section_name].update(
parameter
)
except ParseException:
return
def _parse_include_line(self, line):
try:
parsing_result = self._include_line_parser.parseString(line)
self._match = True
parameter_name = tuple(parsing_result.asList())
if parsing_result.action == '-':
return
parameter_value = self._last_comments_list + ['']
self._last_comments_list = []
include_item = OrderedDict({parameter_name: parameter_value})
if self._section_stack == OrderedDict():
self._item_to_add = include_item
self._ready_to_update = True
else:
self._section_stack[self._current_section_name].update(
include_item
)
except ParseException:
return
def _parse_comment_line(self, line):
try:
parsing_result = self._comment_line_parser.parseString(line)
self._match = True
if not self._ignore_comments:
self._last_comments_list.append(parsing_result.comment)
except ParseException:
return