from typing import Union, List, Optional, Dict, Tuple, Set # from collections import ... # use if necessary import enum # don't import any more modules unless explicitly allowed in assigment text # Do not change this class! class Operation(enum.Enum): Epsilon = enum.auto() # reprezentuje typ listu, který je prázdné slovo Emptyset = enum.auto() # list, který je prázdná množina Literal = enum.auto() # list, který je znak Union = enum.auto() # sjednocení (+) Concat = enum.auto() # zřetězení (.) Iteration = enum.auto() # iterace (*) # Do not change this class! class Regex: def __init__(self, op_or_value: Union[str, Operation], left: Optional["Regex"] = None, right: Optional["Regex"] = None) -> None: if isinstance(op_or_value, str): # if the first arg is a string we have a literal self.op: Operation = Operation.Literal self.value: Optional[str] = op_or_value assert len(self.value) == 1 assert left is None assert right is None else: # otherwise we have an operator self.op = op_or_value self.value = None assert ((op_or_value is Operation.Iteration and left is not None and right is None) or (op_or_value in [Operation.Epsilon, Operation.Emptyset] and left is None and right is None) or (left is not None and right is not None)) self.left = left self.right = right def is_lit(self) -> bool: return self.op is Operation.Literal # for pretty-printing results in interpreter and for the sake of # print-debugging def __repr__(self) -> str: op_or_value = repr(self.value) if self.op is Operation.Literal \ else self.op base = f"Regex({op_or_value}" assert self.left is not None or self.right is None for arg in [self.left, self.right]: if arg is not None: base += f", {arg}" return base + ")" # equality of regexes def __eq__(self, other: object) -> bool: if isinstance(other, Regex): return self.op == other.op and self.value == other.value \ and self.left == other.left and self.right == other.right return False # hashing to make set & dict work properly def __hash__(self) -> int: return hash((self.op, self.value, self.left, self.right)) # This is what you should implement def parse(str_regex: str) -> Union[Regex, int]: """Parse the given string into a Regex object or provide location of the first error.""" ... # TODO return 0 def main() -> None: def _check_parse_error(str_regex: str, pos_expected: int) -> bool: res = parse(str_regex) if not isinstance(res, int): return False if res != pos_expected: print(f"W: wrong error location for {repr(str_regex)}: {res} " f"(expected {pos_expected}") return True assert _check_parse_error("", 0) assert parse("\u03B5") == Regex(Operation.Epsilon) assert parse("ε") == Regex(Operation.Epsilon) assert parse("\u2205") == Regex(Operation.Emptyset) assert parse("∅") == Regex(Operation.Emptyset) assert parse("a") == Regex("a") assert parse("(a + b)") == Regex(Operation.Union, Regex("a"), Regex("b")) assert _check_parse_error("(a + x)", 5) assert parse("( ([a] + [b]) + [c])") == Regex( Operation.Union, Regex(Operation.Union, Regex(Operation.Iteration, Regex("a")), Regex(Operation.Iteration, Regex("b"))), Regex(Operation.Iteration, Regex("c"))) assert _check_parse_error("[a)", 2) assert _check_parse_error("a + b", 2) assert _check_parse_error("(a)", 2) assert _check_parse_error("[a + b]", 3) if __name__ == '__main__': main()