Skip to content

Instantly share code, notes, and snippets.

@evlanov-alex
Created March 12, 2020 20:41
Show Gist options
  • Select an option

  • Save evlanov-alex/13c75d2223c4c04885dc7438448ba9c6 to your computer and use it in GitHub Desktop.

Select an option

Save evlanov-alex/13c75d2223c4c04885dc7438448ba9c6 to your computer and use it in GitHub Desktop.
Task with processors and registers
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