feat: MVP проекта закончено
This commit is contained in:
@ -4,13 +4,68 @@ this class applies markov algorightm to given string
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from collections import OrderedDict
|
from typing import ClassVar
|
||||||
|
from rule import Rule, EMPTY_SYMBOL
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Rulebook:
|
class Rulebook:
|
||||||
rules: OrderedDict[str, str]
|
MAX_DEPTH: ClassVar[int] = 100
|
||||||
|
rules: list[Rule]
|
||||||
|
|
||||||
|
def transform(self, string: str) -> str:
|
||||||
|
"""
|
||||||
|
Applies the next appropriate rule to given string. The
|
||||||
|
priority of rules is determined by their indexes in list
|
||||||
|
of rules. Lesser the index - higher the priority.
|
||||||
|
|
||||||
|
Return transformed string
|
||||||
|
"""
|
||||||
|
|
||||||
|
for _ in range(self.MAX_DEPTH):
|
||||||
|
rule = self.find_appropriate_rule(string)
|
||||||
|
if rule is None:
|
||||||
|
return string
|
||||||
|
string = self._apply_rule(rule, string)
|
||||||
|
if rule.is_blocking:
|
||||||
|
return string
|
||||||
|
raise ValueError("The amount of transformations exceeded "
|
||||||
|
f"{self.MAX_DEPTH}. You can change maximum "
|
||||||
|
"amount by setting class variable MAX_DEPTH,"
|
||||||
|
" but may be something wrong with your input")
|
||||||
|
|
||||||
|
|
||||||
|
def find_appropriate_rule(self, string) -> Rule | None:
|
||||||
|
"""
|
||||||
|
Searches for appropriate rule in rules list and if
|
||||||
|
found one returns it, otherwise returns None
|
||||||
|
"""
|
||||||
|
for rule in self.rules:
|
||||||
|
if self._is_rule_appropriate(rule, string):
|
||||||
|
return rule
|
||||||
|
|
||||||
|
def _apply_rule(self, rule: Rule, string: str) -> str:
|
||||||
|
"""
|
||||||
|
Tries to apply given rule to string. Doesn't check
|
||||||
|
wether the rule is appropriate or not. Inapplicability
|
||||||
|
leads to undefined behaviour
|
||||||
|
"""
|
||||||
|
if rule.operand == EMPTY_SYMBOL:
|
||||||
|
string = EMPTY_SYMBOL + string
|
||||||
|
|
||||||
|
string = (
|
||||||
|
string
|
||||||
|
.replace(rule.operand, rule.target, 1)
|
||||||
|
.replace(EMPTY_SYMBOL, '')
|
||||||
|
)
|
||||||
|
return string
|
||||||
|
|
||||||
|
def _is_rule_appropriate(self, rule: Rule, string: str) -> bool:
|
||||||
|
"""
|
||||||
|
Concrete realization of rule applicability check. Moved to separate
|
||||||
|
function mostly for extensibility purposes
|
||||||
|
"""
|
||||||
|
# Hardest part is always about empty set
|
||||||
|
if rule.operand == EMPTY_SYMBOL:
|
||||||
|
return True
|
||||||
|
return rule.operand in string
|
||||||
|
|
||||||
def __call__(self, string: str):
|
|
||||||
"""aplies rule to the given string"""
|
|
||||||
raise NotImplementedError("Sorry, we still don't know how to apply"
|
|
||||||
"algorithm to your string")
|
|
||||||
|
|||||||
@ -38,12 +38,31 @@ class RulesParser:
|
|||||||
raise ValueError(f"Can't recognize transform symbol. "
|
raise ValueError(f"Can't recognize transform symbol. "
|
||||||
f"\"{self.TRANSFORM}\" or \"{self.B_TRANSFORM}\""
|
f"\"{self.TRANSFORM}\" or \"{self.B_TRANSFORM}\""
|
||||||
f" expected, but \"{arrow}\" encountered")
|
f" expected, but \"{arrow}\" encountered")
|
||||||
|
|
||||||
|
#optimising empty symbol
|
||||||
return Rule(
|
return Rule(
|
||||||
operand=tokens[0],
|
operand=self._optimise_empty(tokens[0]),
|
||||||
target=tokens[2],
|
target=self._optimise_empty(tokens[2]),
|
||||||
is_blocking=is_blocking
|
is_blocking=is_blocking
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _optimise_empty(self, string: str) -> str:
|
||||||
|
"""
|
||||||
|
Empty symbol has meaning only while it's the only
|
||||||
|
symbol in the string (I hope i'm not wrong right now),
|
||||||
|
so all empty symbols can be optimised
|
||||||
|
|
||||||
|
Returns sting without EMPTY symbols if deleting them
|
||||||
|
is semantically possible, returns unchanges string if
|
||||||
|
nothing can be optimised
|
||||||
|
|
||||||
|
NOTE: right now contains naive implementation
|
||||||
|
"""
|
||||||
|
string = re.sub(self.EMPTY+'+', EMPTY_SYMBOL, string)
|
||||||
|
if re.fullmatch(self.EMPTY, string):
|
||||||
|
return string
|
||||||
|
return re.sub(self.EMPTY, '', string)
|
||||||
|
|
||||||
def _get_lines(self, src: str) -> list[str]:
|
def _get_lines(self, src: str) -> list[str]:
|
||||||
"""
|
"""
|
||||||
Get cleaned lines only with rules to parse
|
Get cleaned lines only with rules to parse
|
||||||
@ -65,5 +84,4 @@ class RulesParser:
|
|||||||
Strips whitespaces at the end of lines
|
Strips whitespaces at the end of lines
|
||||||
"""
|
"""
|
||||||
result = re.sub(r' +$', '', src, flags=re.M)
|
result = re.sub(r' +$', '', src, flags=re.M)
|
||||||
# result = re.sub(r"\n+", r'\n', result)
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
Reference in New Issue
Block a user