HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Simple interpreter for a golfing language

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
interpretersimplelanguageforgolfing

Problem

This is the seconds version of an interpreter yesterday. It's supposed to be used for code golf, but it currently can't do much.

```
import re, functools, time

stack = [""] * 9

def run(file):
f = open(file, 'r')
code = f.read()
f.close()
parse(code)

def prompt():
"""Lets the user input commands and feeds them to parse()"""
while True:
userInput = input("> ")
if len(userInput) > 0:
return userInput
print("Please enter a command.")

def parse_numbers(userInput, operator):
"""Parses math like 100+100"""
numbers = userInput.split(operator)
try:
return map(int, numbers)
except ValueError as error:
print(error.message)

def add(command):
"""Adds two or more numbers together"""
numbers = parse_numbers(command, '+')
return sum(numbers)

def subtract(command):
"""Subtracts two or more numbers"""
numbers = parse_numbers(command, '-')
return functools.reduce(lambda x, y: x - y, numbers)

def multiply(command):
"""Multiplies two or more numbers"""
numbers = parse_numbers(command, '*')
return functools.reduce(lambda x, y: x * y, numbers)

def divide(command):
"""Divides two or more numbers"""
numbers = parse_numbers(command, '/')
return functools.reduce(lambda x, y: x / y, numbers)

def modulus(command):
"""Prints the remainder of two numbers"""
numbers = parse_numbers(command, '%')
return functools.reduce(lambda x, y: x % y, numbers)

def quine(command):
"""Prints itself"""
print(command)

def print_command(command):
"""Prints whatever is after the command"""
print(command[2:])

def var(command):
command = command.split('=')
command[0].replace(" ", "")
command[1].replace(" ", "")
stack[command[0]] = command[1]

def loop(command, amount):
"""Loops (a) command(s) for multiple times"""
for i in range(0, int(amount)):
parse(command)

def putOnStack(text, num):
stack[int(num)]

Solution

Assuming Python 3: [Edit: Joe Wallis identifies it as Python2; I think my comments all stand, but I'd add recommending not using file as a variable name, because file() is a builtin function].

f = open(file, 'r')
    code = f.read()
    f.close()
    parse(code)


This "open/read/close" pattern is more idiomatic as:

with open(file, 'r') as f:
    code = f.read()


This

if len(userInput) > 0:
        return userInput


You can test strings directly, if they are empty they are boolean false, otherwise true, so it can become:

if userInput:
        return userInput


These:

def subtract(command):
    """Subtracts two or more numbers"""
    numbers = parse_numbers(command, '-')
    return functools.reduce(lambda x, y: x - y, numbers)

def multiply(command):

def divide(command):

def modulus(command):


could all use the operator module, which has standard operators (+,-,*,/,%) as functions. And then you could remove all your lambdas, e.g.:

import operator
def subtract(command):
    """Subtracts two or more numbers"""
    numbers = parse_numbers(command, '-')
    return functools.reduce(operator.sub, numbers)


(NB. noted by Joe Wallis, Python 3 has different division methods in its operator module, truediv() and floordiv() instead of just div(). Might be relevant because your use of functools and print() might mean you're going for Python 3-compatible code).

And then once you have four things which just do reduce(operator.___, numbers) you could merge those into one function, possibly with a hashtable like {'+':operator.add, '-':operator.sub,...} or similar.

Then you could shorten the elif/elif/elif block later when calling them.

def print_command(command):
    """Prints whatever is after the command"""
    print(command[2:])


This isn't terrible, but having 2 as a magic number in the code isn't very clear, and it hard-codes a possible instruction length. And this function naming and comment show that you're using 'command' to mean both instructions your interpreter has, and the text of instruction with arguments supplied by the user. That's needlessly mixed up.

It would be clearer to change the names of things so it wasn't mixed up, and have some way to .lstrip the instruction name off the parameters, or otherwise separate the instruction as a whole item, without relying on it being 2 characters, e.g. split on the first space character.

def var(command):
    command = command.split('=')
    command[0].replace(" ", "")
    command[1].replace(" ", "")
    stack[command[0]] = command[1]


Why not replace the spaces before splitting it? Then you don't need to do it twice. If it's always going to split into two parts, you could make it:

def var(command):
    x, y = command.replace(' ', '').split('=')
    stack[x] = y


def putOnStack(text, num):
    stack[int(num)] = text


I haven't followed where this is called from, but it looks like it doesn't put On the stack, it puts In the stack. (And if so, is it really a stack at all?)

def parse(command):
    """Parses the commands."""
    if ';' in command:
        commands = command.split(";")
        for i in commands:
            parse(i)
    if '\n' in command:
        commands = command.split('\n')
        for i in commands:
            parse(i)


These could be one block, e.g.:

for c in (';', '\n'):
        if c in command:
            commands = command.split(c)
            for i in commands:
                parse(i)


I'm not sure how well that will work, if a command has both in it, it will get processed twice. But then, it will in your code as well. It might be possible to use re.split('[;\n]', command) to split on either character - maybe.

elif '=' in command:
        lst = command.split('=')
        lst[0].replace(" ", "")
        lst[1].replace(" ", "")
        stackNum = ''.join(lst[1])
        putOnStack(stackNum, lst[0])


Similar to earlier, replace the spaces first. (Or maybe do it once at the top for the whole processing code).

You use ''.join(lst[1]) implying that lst[1] is a list, but it can't be a list, it comes from split(), it's always going to be a string. Should that be ''.join(lst[1:]) ?

[Edit: Now I have traced this through, this spurious join is why you have to call int(num) in putOnStack. It makes more sense to put the int() call here, rather than there.]

elif command.startswith('? '):
        stackNum = command[2]
    [..]
    elif command.startswith('@ '):
        stackNum = command[2]


These might break if they put more than one space in between ? and the number.

Code Snippets

f = open(file, 'r')
    code = f.read()
    f.close()
    parse(code)
with open(file, 'r') as f:
    code = f.read()
if len(userInput) > 0:
        return userInput
if userInput:
        return userInput
def subtract(command):
    """Subtracts two or more numbers"""
    numbers = parse_numbers(command, '-')
    return functools.reduce(lambda x, y: x - y, numbers)

def multiply(command):

def divide(command):

def modulus(command):

Context

StackExchange Code Review Q#106968, answer score: 4

Revisions (0)

No revisions yet.