86 lines
3.2 KiB
Python
86 lines
3.2 KiB
Python
# ===----------------- directive.py - Directive Base Class ----------------===//
|
|
#
|
|
# Copyright 2019-2020 The IBM Research Authors.
|
|
#
|
|
# =============================================================================
|
|
#
|
|
# ===----------------------------------------------------------------------===//
|
|
|
|
import re
|
|
import ast
|
|
from typing import List, Dict, Callable, Any, Pattern, Tuple
|
|
|
|
from doc_parser import failure, success, succeeded
|
|
from utils import DocCheckerCtx
|
|
|
|
DirectiveConfigList = List[Dict[str, Any]]
|
|
ConfigParseResult = Tuple[str, Dict[str, Any]]
|
|
|
|
|
|
class Directive(object):
|
|
""""""
|
|
def __init__(self, ext_to_regexes: Dict[str, str],
|
|
config_parsers: List[Callable[[str, DirectiveConfigList],
|
|
ConfigParseResult]],
|
|
handler: Callable[[Dict[str, Any], DocCheckerCtx], None]):
|
|
"""
|
|
:param ext_to_regexes: specify a regex expression to match the directive (for each file extension type).
|
|
:param config_parsers: specify a list of parsers to parse configuration. They will be invoked in order until one indicates parsing is successful.
|
|
:param handler: a function to perform the invariance check specified by the directive.
|
|
"""
|
|
self.ext_to_patterns: Dict[str, Pattern] = {}
|
|
for ext, pattern in ext_to_regexes.items():
|
|
self.ext_to_patterns[ext] = re.compile(pattern)
|
|
|
|
self.config_parsers: List[Callable[[str, DirectiveConfigList],
|
|
ConfigParseResult]] = config_parsers
|
|
self.handler = handler
|
|
|
|
def try_parse_directive(
|
|
self, line: str, doc_file_ext: str,
|
|
directive_config: DirectiveConfigList) -> Tuple[str, Any]:
|
|
"""
|
|
:param line: next line to try parse a directive from.
|
|
:param doc_file_ext: file extention.
|
|
:param directive_config: a list used to output parsed directive configuration.
|
|
:return: parse result.
|
|
"""
|
|
|
|
if doc_file_ext not in self.ext_to_patterns:
|
|
return failure()
|
|
|
|
matches = self.ext_to_patterns[doc_file_ext].findall(line)
|
|
if len(matches) > 1:
|
|
raise ValueError("more than one directives in a line")
|
|
|
|
match = matches[0] if len(matches) else None
|
|
if match:
|
|
for parser in self.config_parsers:
|
|
if succeeded(parser(match, directive_config)):
|
|
return success()
|
|
|
|
raise ValueError("Failed to parse configuration.")
|
|
else:
|
|
return failure()
|
|
|
|
def handle(self, config, ctx):
|
|
self.handler(config, ctx)
|
|
|
|
|
|
def generic_config_parser(
|
|
match: str, directive_config: DirectiveConfigList) -> Tuple[str, Any]:
|
|
"""
|
|
Generic configuration parser.
|
|
Will return success if and only if configuration is specified as a python dictionary literal.
|
|
|
|
@param match: the content from which to parse the directive configuration.
|
|
@param directive_config: a list to output the parsed directive_config.
|
|
@return: parsing result.
|
|
"""
|
|
try:
|
|
directive_config.append(ast.literal_eval(match))
|
|
return success()
|
|
except (SyntaxError, ValueError):
|
|
# If literal_eval failed, return parsing failure.
|
|
return failure()
|