Esempio n. 1
0
class Interpreter():
    """
    A read eval print loop interactive interpreter. Takes input from the user and displays the resultant output on the next line
    """

    def __init__(self):
        
        # Windows does not have GNU readline, and has different environment variables
        if osname == 'nt':
            username = environ["USERNAME"]
        else:
            import readline
            import atexit
            username = environ["USER"]
            
            # Uses the readline library to gain tab completion, matching
            # parenthesis, and automatic history
            readline.set_completer(self.complete)
            readline.parse_and_bind('tab: complete')
            readline.parse_and_bind('set blink-matching-paren on')
    
            self.histfile = join(expanduser("~"), ".pylisp_history")
            try:
                readline.read_history_file(self.histfile)
            except FileNotFoundError:
                open(self.histfile, 'a').close()
    
            self.completion_candidates = []
    
            atexit.register(readline.write_history_file, self.histfile)
            
        # Initialises using the user's username as the prompt
        self.prompt = username + "> "

        self.intro = "Welcome to the PyLisp interpreter"

        self.vm = VirtualMachine({})
        self.load_std()
        self.registers = None
        
    def load_std(self):
        """
        Adds the core libraries to the environment
        """
        for i in env.standard_env:
            libs = env.include_lib(i)
            for lib in libs:
                if lib[0] == "py":
                    self.vm.env.update(lib[1])
                elif lib[0] == "pyl":
                    file_parse = FileParser(lib[1], self.vm)
                    file_parse.run()

    def complete(self, text, state):
        """
        The completer function. Works by finding all the symbols in the environment and in the core keywords and checking if they start with the provided text
        """
        if state == 0:
            self.completion_candidates = []

            tmp = []
            tmp.extend(self.repl_env.keys())
            tmp.extend(self.vm.core_keywords.keys())

            for i in tmp:
                if i.startswith(text):
                    self.completion_candidates.append(i)

        if state < len(self.completion_candidates):
            result = self.completion_candidates[state]
        else:
            result = None

        return result

    def precmd(self, line):
        """
        Executed on the input before the line is evaluated. Used to ensure that if the opening brackets do not match the closing brackets, the prompt is just extended
        """
        ret = line
        prompt_indent = len(self.prompt) - len("...")
        while ret.count("(") > ret.count(")"):
            indent = (ret.count("(") - ret.count(")")) * 2
            ret += " " + \
                input("\r" + " " * prompt_indent + "..." + indent * " ")

        return ret

    def cmdloop(self):
        """
        The main loop. Gets the input, sends it to precmd, then runs it. If it encounters a pylisp error, prints the error and cleans up.
        If it enconters an EOF i.e. Ctrl-D it closes gracefully
        """
        print(self.intro)
        while True:
            self.registers = self.vm.get_registers()
            try:
                inp = input(self.prompt)
                inp = self.precmd(inp)

                parser = Parser()
                print(self.vm.evaluate(parser.parse_buffer(inp)))

            except PylispError as e:
                print(e)
                self.vm.set_registers(self.registers)
            except (KeyboardInterrupt, EOFError):
                return