patternpythonMinor
Simple interpreter for a golfing language
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)]
```
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
This "open/read/close" pattern is more idiomatic as:
This
You can test strings directly, if they are empty they are boolean false, otherwise true, so it can become:
These:
could all use the operator module, which has standard operators (+,-,*,/,%) as functions. And then you could remove all your lambdas, e.g.:
(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
Then you could shorten the elif/elif/elif block later when calling them.
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
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:
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?)
These could be one block, e.g.:
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
Similar to earlier, replace the spaces first. (Or maybe do it once at the top for the whole processing code).
You use
[Edit: Now I have traced this through, this spurious
These might break if they put more than one space in between ? and the number.
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 userInputYou can test strings directly, if they are empty they are boolean false, otherwise true, so it can become:
if userInput:
return userInputThese:
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] = ydef putOnStack(text, num):
stack[int(num)] = textI 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 userInputif userInput:
return userInputdef 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.