4
\$\begingroup\$

Here is a very basic object orientated implementation of a read evaluate print loop AKA REPL. I encluded REShell to demonstrate how one could use Reple

#-*-coding:utf8;-*-
#qpy:3
#qpy:console
import re
import sre_constants
PS1 = '>> '
DEFAULT_EVALUATOR = print
class Repl(object):
 def __init__(self, evaluator=DEFAULT_EVALUATOR):
 self.ps1 = PS1
 self.evaluator = evaluator
 def read(self):
 return input(self.ps1)
 def evaluate(self):
 return self.evaluator(self.read())
 def run(self):
 while 1:
 try:
 self.evaluate()
 except KeyboardInterrupt:
 sys.exit(0)
class REShell(Repl):
 def __init__(self, data):
 self.data = data
 self.ps1 = PS1
 def evaluate(self):
 try:
 expression = re.compile(self.read())
 print(*expression.findall(self.data), sep='\n')
 except sre_constants.error as error:
 print(error)
def source_code():
 with open(__file__, 'r') as source:
 return source.read()
if __name__ == '__main__':
 data = source_code()
 print(data)
 shell = REShell(data)
 shell.run()
asked Dec 23, 2017 at 20:21
\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

Looks quite good to me! There's some things I think you could improve:

  • When a user hits Ctrl-C, the program flow is suddenly interrupted. The call to sys.exit should use 1 as a status code to indicate that something went wrong.

  • The REPL prompt is currently hardcoded, but you could easily change Reple's signature to allow a custom prompt:

    class Reple(object):
     def __init__(self, evaluator=DEFAULT_EVALUATOR, prompt=PS1):
     self.prompt = prompt
    

    On that note, PS1 is a bit ambiguous, maybe rename it to DEFAULT_PROMPT.

  • This is more of a design argument, but in my opinion it would make more sense to have run call evaluate, making evaluator superfluous; in pseudocode:

    while true
     read from stdin
     evaluate(expression)
    

    And in Python:

    class Reple(object):
     ...
     def run(self):
     while 1:
     try:
     expression = input(self.prompt)
     except KeyboardInterrupt:
     sys.exit(1)
     else:
     self.evaluate(expression=expression)
     def evaluate(self, expression):
     raise NotImplementedError("This method must be implemented in a derived class")
    
  • You can use abc.ABCMeta together with abc.abstractmethod to provide 'truly' abstract methods:

    import abc
    class Reple(object, metaclass=abc.ABCMeta):
     ...
     @abc.abstractmethod
     def evaluate(self, expression):
     return
    

    And here's a Python 2 version:

    import abc
    class Reple(object):
     __metaclass__ = abc.ABCMeta
     ...
     @abc.abstractmethod
     def evaluate(self, expression):
     return
    
  • You don't have any docstrings, so it's hard to tell what certain methods do or how they should be implemented by subclasses of Reple.

answered Dec 23, 2017 at 21:41
\$\endgroup\$
1
  • 2
    \$\begingroup\$ PS1 is a nod to us Linux hackers. Bash was my introduction to programming. I \$\endgroup\$ Commented Dec 24, 2017 at 0:55

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.