Interpreter written in Python not working properly
Question: No, I'm not done with my interpreter questions yet. Sorry.

So I've written another interpreter for a programming language I made up in
Python. The programming language has an interesting concept: you only get some
basic commands. To "get" more complex commands, you have to write functions
and combine the simple command given to you.

Anyways, I've written a simple interpreter for the language.

The problem is: the simple commands work, but defining functions doesn't.

Here's the interpreter (I've removed code that isn't useful for solving the
problem).

    
    
    class Interpreter:
        acc = 0
        defined = {}
    
        def read(self, cmd):
            tokens = cmd.replace("\n", ";").split(";")
            for token in tokens:
                self.parse(token)
    
        def parse(self, cmd):
            if cmd.startswith("def(") and cmd.endswith(")"):
                cmd = cmd[2:]
                cmd = cmd[:-1]
                cmd = cmd.split("|")
                self.defined[cmd[0]] = cmd[1]
    
            elif cmd in self.defined:
                self.read(self.defined[cmd])
    
            elif cmd == "1":
                self.acc += 1
    
            elif cmd == "2":
                print(self.acc)
    
            elif cmd == "i":
                self.acc = int(input(">> "))
    
    i = Interpreter()
    while 1:
        i.read(input("> "))
    

You can define functions using the syntax `def(name|code)`. For example,
`def(cat|i;2)`.

Now, onto the problem I'm having. It's impossible to define functions. They
just don't work. It doesn't throw an error or anything. It does nothing.

Here's the code I tried to use:

    
    
    def(c|i;2)
    c
    

It should get input and display it, but instead, it doesn't do anything.

This works, though:

    
    
    i;2
    

It seems to me like the problem is somewhere in the `if
cmd.startswith("def(")` if statement, because everything except the functions
work.


Answer: When solving these kinds of problems it is imperative to be able to see what
is going on while the program is running. You can e.g. use a debugger or you
can go with the age old debug print method (like I've done below).

I've extended the interpreter with a `p` command that prints the `acc`, and
made it accept any integer, otherwise it is the same.

The problem you're having is that you destroy the input before you can store
it in `defined`. I solve it by only using `\n` to split outer commands and `;`
to split commands inside a `def`.

    
    
    import textwrap
    
    class Interpreter:
        acc = 0
        defined = {}
    
        def read(self, cmd):
            cmd = textwrap.dedent(cmd).strip()
            lines = cmd.split("\n")
            for line in lines:
                print '==> parsing:', line
                self.parse(line)
    
        def parse(self, cmd):
            if cmd.startswith("def(") and cmd.endswith(")"):
                print '::found def',
                name, code = cmd[4:-1].split('|')  # def( is 4 characters..
                self.defined[name] = code.replace(';', '\n')  # read() expects commands divided by \n, so replace ; before storing in self.defined
                print self.defined
    
            elif cmd in self.defined:
                print '::found defined name', cmd, '=>', `self.defined[cmd]`
                self.read(self.defined[cmd])
    
            elif cmd == "i":
                self.acc = int(input(">> "))
    
            elif cmd == "p":
                print(self.acc)
    
            else:
                self.acc += int(cmd)
    
    
    intp = Interpreter()
    intp.read("""
        def(c|i;2)
        c
        p
    """)
    

the output from a run:

    
    
    (dev) go|c:\srv\tmp> python pars.py
    ==> parsing: def(c|i;2)
    ::found def {'c': 'i\n2'}
    ==> parsing: c
    ::found defined name c => 'i\n2'
    ==> parsing: i
    >> 5
    ==> parsing: 2
    ==> parsing: p
    7
    

writing an interpreter that recursively calls itself in this way has some
major limitations since every function call in the compiled language requires
a function call in the host language (Python). A better way is to transform
the program into a stack of commands, then pop a command from the stack and
execute it. You're done when the stack is empty. Function calls will then just
involve pushing the value of the defined symbol onto the stack. I've extended
your interpreter to do this below. I've added a command `x0` which will exit a
function call if `acc` is zero (and I push a `$marker` onto the stack before
calling a function so I know where the function call started):

    
    
    def debug(*args):
        pass
        # print '[dbg]', ' '.join(str(a) for a in args)
    
    class Interpreter:
        acc = 0
        defined = {}
        commands = []  # the stack
    
        def compile(self, program):
            program = textwrap.dedent(program).strip()
            lines = program.split("\n")
            lines.reverse()
            self.commands += lines
    
            while self.commands:
                command = self.commands.pop()
                debug('==> running:', command, 'stack:', self.commands)
                self.run_command(command)
    
        def run_command(self, cmd):
            if cmd.startswith("def(") and cmd.endswith(")"):
                name, code = cmd[4:-1].split('|')
                self.defined[name] = code.split(';')
                debug('::found def', self.defined)
    
            elif cmd in self.defined:
                debug('::found defined name', cmd, '=>', `self.defined[cmd]`)
                # mark command stack before executing function
                self.commands += ['$marker']  
                self.commands += list(reversed(self.defined[cmd]))
    
            elif cmd == '$marker':
                pass  # do nothing (we get here if a def doesn't have an x0 when the acc is zero)
    
            elif cmd == 'x0':
                # exit function call if acc is zero
                if self.acc == 0:
                    while self.commands:  # pop the stack until we get to the $marker
                        tmp = self.commands.pop()
                        if tmp == '$marker':
                            break
    
            elif cmd == "i":
                self.acc = int(input(">> "))
    
            elif cmd == "p":
                print(self.acc)
    
            else:
                self.acc += int(cmd)
    

we can now write recursive functions:

    
    
    intp = Interpreter()
    intp.compile("""
        4
        def(c|-1;x0;p;c)
        c
        p
    """)
    

which outputs:

    
    
    (dev) go|c:\srv\tmp> python pars.py  
    3                                    
    2                                    
    1                                    
    0                                    
    

instead of an accumulator (`acc`) it is probably more expressive to use the
stack for values too, so e.g. `5;p` would push `5` on the stack, then `p`
would print the top element on the stack. Then you could implement addition
like `5;2;+` meaning `push 5`, `push 2`, and let `+` mean `add top two items
on stack and push the result`... I'll leave that as an excercise ;-)

