Created
March 12, 2020 20:41
-
-
Save evlanov-alex/13c75d2223c4c04885dc7438448ba9c6 to your computer and use it in GitHub Desktop.
Task with processors and registers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import sys | |
| from abc import ABC, abstractmethod | |
| from dataclasses import dataclass | |
| from typing import List, Any, Optional | |
| class BaseProcessorException(Exception): | |
| """Base exception.""" | |
| class UnknownRegisterException(BaseProcessorException): | |
| """When we are trying to get unknown register.""" | |
| class UnknownCommandException(BaseProcessorException): | |
| """When we are trying to get unknown command.""" | |
| class InvalidDataTypesException(BaseProcessorException): | |
| """When trying to operate with different type objects.""" | |
| class ArgsValidationException(BaseProcessorException): | |
| """When we have invalid arguments.""" | |
| @dataclass | |
| class Register: | |
| name: str | |
| value: Any = None | |
| def __str__(self): | |
| return f'{self.name}: {self.value}' | |
| class RegistersContainer: | |
| def __init__(self, registers: List[Register]): | |
| self._registers = registers | |
| self._name_mapping = { | |
| register.name: register | |
| for register in self._registers | |
| } | |
| @property | |
| def register_names(self) -> List[str]: | |
| return [register.name for register in self._registers] | |
| def get_register_value(self, name: str) -> Any: | |
| register = self._get_register_by_name(name) | |
| return register.value | |
| def set_register_value(self, name: str, value: Any): | |
| register = self._get_register_by_name(name) | |
| register.value = value | |
| def _get_register_by_name(self, name: str) -> Register: | |
| register = self._name_mapping.get(name) | |
| if not register: | |
| raise UnknownRegisterException(f'No register with name {name}') | |
| return register | |
| def __str__(self): | |
| results = [] | |
| for register in sorted(self._registers, key=lambda x: x.name): | |
| results.append(f'{register.name} - {register.value}') | |
| return ' | '.join(results) | |
| class Command(ABC): | |
| name: str | |
| def __init__(self, processor, command_args: list): | |
| self._processor = processor | |
| self._container = processor.registers_container | |
| self._history = processor.history | |
| self._args = command_args | |
| self._backup = None | |
| @abstractmethod | |
| def validate_arguments(self): | |
| ... | |
| @abstractmethod | |
| def execute(self): | |
| ... | |
| def save_backup(self): | |
| if not self._backup: | |
| self._backup = {} | |
| for name in self._container.register_names: | |
| self._backup[name] = self._container.get_register_value(name) | |
| def apply_backup(self): | |
| if self._backup: | |
| for name in self._container.register_names: | |
| self._container.set_register_value(name, self._backup[name]) | |
| def __str__(self): | |
| return self.name | |
| def __repr__(self): | |
| return f'Command "{self.name}"' | |
| class PutCommand(Command): | |
| name = 'put' | |
| def validate_arguments(self): | |
| if len(self._args) != 2: | |
| raise ArgsValidationException('Must have 2 args.') | |
| if not isinstance(self._args[0], str): | |
| raise ArgsValidationException('First arg must be a string.') | |
| try: | |
| self._args[1] = int(self._args[1]) | |
| except ValueError: | |
| raise ArgsValidationException('Second arg must be a number.') | |
| return self._args | |
| def execute(self): | |
| self.save_backup() | |
| register_name, value = self._args | |
| self._container.set_register_value(register_name, value) | |
| self._history.push(self) | |
| class SwapCommand(Command): | |
| name = 'swap' | |
| def validate_arguments(self): | |
| if len(self._args) != 2: | |
| raise ArgsValidationException('Must have 2 args.') | |
| for arg in self._args: | |
| if not isinstance(arg, str): | |
| raise ArgsValidationException('Args must be a strings.') | |
| return self._args | |
| def execute(self): | |
| self.save_backup() | |
| register1_name, register2_name = self._args | |
| register1_value = self._container.get_register_value(register1_name) | |
| register2_value = self._container.get_register_value(register2_name) | |
| self._container.set_register_value(register1_name, register2_value) | |
| self._container.set_register_value(register2_name, register1_value) | |
| self._history.push(self) | |
| class AddCommand(Command): | |
| name = 'add' | |
| def validate_arguments(self): | |
| if len(self._args) != 3: | |
| raise ArgsValidationException('Must have 3 args.') | |
| for arg in self._args: | |
| if not isinstance(arg, str): | |
| raise ArgsValidationException('Args must be a strings.') | |
| return self._args | |
| def execute(self): | |
| self.save_backup() | |
| register1_name, register2_name, register3_name = self._args | |
| register1_value = self._container.get_register_value(register1_name) | |
| register2_value = self._container.get_register_value(register2_name) | |
| try: | |
| result = register1_value + register2_value | |
| except TypeError: | |
| raise InvalidDataTypesException(f'Try to sum {type(register1_value)} with {type(register2_value)}') | |
| self._container.set_register_value(register3_name, result) | |
| self._history.push(self) | |
| class UndoCommand(Command): | |
| name = 'undo' | |
| def validate_arguments(self): | |
| if len(self._args) != 0: | |
| raise ArgsValidationException('Must have no arguments.') | |
| return self._args | |
| def execute(self): | |
| last_command = self._history.pop() | |
| if last_command: | |
| last_command.apply_backup() | |
| class RememberCommand(Command): | |
| name = 'remember' | |
| def validate_arguments(self): | |
| if len(self._args) < 1: | |
| raise ArgsValidationException('Must have arguments.') | |
| return self._args | |
| def execute(self): | |
| self._processor.set_buffer(self._args) | |
| class RecallCommand(Command): | |
| name = 'recall' | |
| def validate_arguments(self): | |
| if len(self._args) != 0: | |
| raise ArgsValidationException('Must have no arguments.') | |
| return self._args | |
| def execute(self): | |
| if self._processor.buffer: | |
| self._processor.execute_command(*self._processor.buffer) | |
| class CommandHistory: | |
| max_length = 1000 | |
| def __init__(self): | |
| self._history = [] | |
| def push(self, command: Command): | |
| if len(self._history) >= self.max_length: | |
| self._history.pop(0) | |
| self._history.append(command) | |
| def pop(self) -> Optional[Command]: | |
| if self._history: | |
| return self._history.pop() | |
| return None | |
| class Processor: | |
| commands: List[Command] = [ | |
| PutCommand, | |
| SwapCommand, | |
| AddCommand, | |
| UndoCommand, | |
| RememberCommand, | |
| RecallCommand, | |
| ] | |
| registers = { | |
| 'A', | |
| 'B', | |
| 'C', | |
| 'D', | |
| } | |
| def __init__(self): | |
| self._command_name_mapping = {command.name: command for command in self.commands} | |
| self._command_history = CommandHistory() | |
| self._registers_container = RegistersContainer( | |
| registers=[Register(name=name) for name in self.registers], | |
| ) | |
| self._buffer = None | |
| @property | |
| def history(self) -> CommandHistory: | |
| return self._command_history | |
| @property | |
| def registers_container(self) -> RegistersContainer: | |
| return self._registers_container | |
| @property | |
| def buffer(self) -> Any: | |
| return self._buffer | |
| def set_buffer(self, value: Any): | |
| self._buffer = value | |
| def execute_command(self, name, *args): | |
| command_class = self._get_command(name) | |
| command = command_class(self, list(args)) | |
| command.validate_arguments() | |
| command.execute() | |
| def _get_command(self, name: str) -> type: | |
| if name not in self._command_name_mapping: | |
| raise UnknownCommandException(f'Unknown command with name {name}') | |
| return self._command_name_mapping[name] | |
| class Console: | |
| exit_command = 'exit' | |
| preview = '>>> ' | |
| def __init__(self, processor: Processor): | |
| self._processor = processor | |
| def run(self): | |
| while True: | |
| user_input = input(self.preview) | |
| separated = user_input.split() | |
| if not separated: | |
| continue | |
| command_name = separated[0] | |
| args = separated[1:] | |
| if command_name == self.exit_command: | |
| sys.exit(0) | |
| try: | |
| self._processor.execute_command(command_name, *args) | |
| except BaseProcessorException as e: | |
| print(e) | |
| else: | |
| print(self._processor.registers_container) | |
| if __name__ == '__main__': | |
| processor = Processor() | |
| console = Console(processor=processor) | |
| console.run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment