patternpythonMinor
Simple code-golf programming language written in Python
Viewed 0 times
simplewrittengolfprogramminglanguagepythoncode
Problem
I've written a "simple" interpreter for a programming language in Python, called Tellurium (named after the element of the same name). So far, it's working pretty well, but there are some things I'd like to fix/remove.
Tellurium is a tape-based language, so it uses a tape to store data.
```
import string, sys, random, time, codecs
tape = [0] * 25500
funcs = {}
variables = {}
readingStr = False
readingLoopAmount = False
readingLoopCode = False
readingRand = False
readingRand2 = False
readingFName = False
readingFCode = False
readingName = False
readingVName = False
readingFileName = False
readingVText = False
readingVName2 = False
appendToFront = False
appendToBack = False
loopInf = False
loopRand = False
string = False
isChar = False
fileName = []
vName = []
vText = []
vName2 = []
tempText = []
tempName = []
fName = []
fCode = []
text = []
rand = []
rand2 = []
loopCode = []
loopAmount = []
selected = 0
def prompt():
cmd = input("> ")
return cmd
def read(cmd):
if "!K" in cmd:
cmd = cmd.replace("!K", "1000")
if "!H" in cmd:
cmd = cmd.replace("!H", "100")
commands = len(cmd)
tokens = list(cmd)
for i in range(0, commands):
parse(tokens[i])
def parse(cmd):
# Sorry for all these globals...
global tape
global funcs
global variables
global readingStr
global readingFileName
global readingLoopAmount
global readingLoopCode
global readingRand
global readingRand2
global readingFName
global readingFCode
global readingName
global readingVName
global readingVText
global readingVName2
global appendToFront
global appendToBack
global loopInf
global loopRand
global vName, vText, vName2
global fileName
global string
global isChar
global tempName
global tempText
global fName
global fCode
global text
global rand
global rand2
global loopCode
global loopAmount
global selected
if readingFileNam
Tellurium is a tape-based language, so it uses a tape to store data.
```
import string, sys, random, time, codecs
tape = [0] * 25500
funcs = {}
variables = {}
readingStr = False
readingLoopAmount = False
readingLoopCode = False
readingRand = False
readingRand2 = False
readingFName = False
readingFCode = False
readingName = False
readingVName = False
readingFileName = False
readingVText = False
readingVName2 = False
appendToFront = False
appendToBack = False
loopInf = False
loopRand = False
string = False
isChar = False
fileName = []
vName = []
vText = []
vName2 = []
tempText = []
tempName = []
fName = []
fCode = []
text = []
rand = []
rand2 = []
loopCode = []
loopAmount = []
selected = 0
def prompt():
cmd = input("> ")
return cmd
def read(cmd):
if "!K" in cmd:
cmd = cmd.replace("!K", "1000")
if "!H" in cmd:
cmd = cmd.replace("!H", "100")
commands = len(cmd)
tokens = list(cmd)
for i in range(0, commands):
parse(tokens[i])
def parse(cmd):
# Sorry for all these globals...
global tape
global funcs
global variables
global readingStr
global readingFileName
global readingLoopAmount
global readingLoopCode
global readingRand
global readingRand2
global readingFName
global readingFCode
global readingName
global readingVName
global readingVText
global readingVName2
global appendToFront
global appendToBack
global loopInf
global loopRand
global vName, vText, vName2
global fileName
global string
global isChar
global tempName
global tempText
global fName
global fCode
global text
global rand
global rand2
global loopCode
global loopAmount
global selected
if readingFileNam
Solution
Seeing as it's a long post, I'll just see where I can get, starting now.
First,
You don't need the intermediate variable. Just write this:
Also, you only use it once, so why not use
Next,
The
Also, looping over a string gives us a list of characters. Using that, we get
Which reads a bit saner. The
Binary int operations.
Browsing down further, I see the following interesting piece of code:
See the repetition? This is easily fixable, add the following to the top of your module:
and replace the
in-place unary operations
Next, I moved the cases for
Reading carefully, all have the form
for proper functions
and then in the
(funnily enough, you actually have "n" twice, I just removed one of them).
Furthermore, I also added
Assignment from nowhere
There are also
And in the
Uniform position logic
Again, make things simple. Move similar things together:
You can probably expect the drill:
And the handler:
Make sure it is later than the value-actions, because you re-used "-" (bug?).
Complex logic
Now, the above changes were fairly trivial (but beneficial!). You can probably handle some extr
First,
prompt:def prompt():
cmd = input("> ")
return cmdYou don't need the intermediate variable. Just write this:
def prompt():
return input("> ")Also, you only use it once, so why not use
input("> ") instead of prompt()?.Next,
read:def read(cmd):
if "!K" in cmd:
cmd = cmd.replace("!K", "1000")
if "!H" in cmd:
cmd = cmd.replace("!H", "100")
commands = len(cmd)
tokens = list(cmd)
for i in range(0, commands):
parse(tokens[i])The
if checks can be gone, because otherwise .replace is just a no-op. (It might matter for efficiency, perhaps!). That leaves us withdef read(cmd):
cmd = cmd.replace("!K", "1000")
cmd = cmd.replace("!H", "100")
commands = len(cmd)
tokens = list(cmd)
for i in range(0, commands):
parse(tokens[i])Also, looping over a string gives us a list of characters. Using that, we get
def read(cmd):
if isinstance(cmd, str):
cmd = cmd.replace("!K", "1000")
cmd = cmd.replace("!H", "100")
for token in cmd:
parse(token)Which reads a bit saner. The
isinstance is needed, because cmd is sometimes a list, and that does not have a replace method. (Alternatively, make sure read is always called with a str).Binary int operations.
Browsing down further, I see the following interesting piece of code:
elif cmd == "a":
tape[selected] = int(tape[selected]) + int(tape[selected+1])
elif cmd == "s":
tape[selected] = int(tape[selected]) - int(tape[selected+1])
elif cmd == "m":
tape[selected] = int(tape[selected]) * int(tape[selected+1])
elif cmd == "d":
tape[selected] = int(tape[selected]) / int(tape[selected+1])See the repetition? This is easily fixable, add the following to the top of your module:
import operator
INT_BINOPS = {
"a": operator.add,
"s": operator.sub,
"m": operator.mul,
"d": operator.truediv, # Maybe you meant floordiv?
}and replace the
elif change withelif cmd in INT_BINOPS:
op = INT_BINOPS[cmd]
tape[selected] = op(int(tape[selected]), int(tape[selected + 1]))in-place unary operations
Next, I moved the cases for
/, \, ", ', n, % closer together to bring out the similarity:elif cmd == "/":
tape[selected] += 10
elif cmd == "\\":
tape[selected] -= 10
elif cmd == '"':
tape[selected] += 100
elif cmd == "'":
tape[selected] -= 100
elif cmd == "n":
tape[selected] = int(tape[selected])
elif cmd == "%":
tape[selected] = ord(tape[selected])Reading carefully, all have the form
tape[selected] = f(tape[selected])for proper functions
f. Let's apply the same trick. Using lambda because import functools
INPLACE_UNARYOPS = {
"+": functools.partial(operator.add, 1),
"-": functools.partial(operator.add, -1), # can't use .sub here
"/": functools.partial(operator.add, 10),
"\\": functools.partial(operator.add, -10), # can't use .sub here
'"': functools.partial(operator.add, 100),
"'": functools.partial(operator.add, -100), # can't use .sub here
"n": int,
"%": ord,
}and then in the
elif chain useelif cmd in INPLACE_UNARYOPS:
op = INPLACE_UNARYOPS[cmd]
tape[selected] = op(tape[selected])(funnily enough, you actually have "n" twice, I just removed one of them).
Furthermore, I also added
"r" and "f" to the dictionary as follows (because I missed those):"r": lambda v: codecs.encode(str(v), 'rot_13'),
"f": float,Assignment from nowhere
There are also
"t", "i" and "#" which look alike. They get the same treatment as before. At the top:FUNCS = {
"t": lambda: str(time.ctime()),
"#": lambda: 0,
"i": lambda: input(">> "),
}And in the
if-chain:elif cmd in FUNCS:
tape[selected] = FUNCS[cmd]()Uniform position logic
Again, make things simple. Move similar things together:
elif cmd == "$":
selected = 0
elif cmd == ">":
selected += 1
elif cmd == "<":
selected -= 1
elif cmd == "{":
selected += 10
elif cmd == "}":
selected -= 10
elif cmd == "-":
selected += 100
elif cmd == "_":
selected -= 100You can probably expect the drill:
POSITION_ACTIONS = {
"$": lambda _: 0,
">": functools.partial(operator.add, 1),
"<": functools.partial(operator.add, -1),
"{": functools.partial(operator.add, 10),
"}": functools.partial(operator.add, -10),
"-": functools.partial(operator.add, 100),
"_": functools.partial(operator.add, -100),
}And the handler:
elif cmd in POSITION_ACTIONS:
selected = POSITION_ACTIONS[cmd](selected)Make sure it is later than the value-actions, because you re-used "-" (bug?).
Complex logic
Now, the above changes were fairly trivial (but beneficial!). You can probably handle some extr
Code Snippets
def prompt():
cmd = input("> ")
return cmddef prompt():
return input("> ")def read(cmd):
if "!K" in cmd:
cmd = cmd.replace("!K", "1000")
if "!H" in cmd:
cmd = cmd.replace("!H", "100")
commands = len(cmd)
tokens = list(cmd)
for i in range(0, commands):
parse(tokens[i])def read(cmd):
cmd = cmd.replace("!K", "1000")
cmd = cmd.replace("!H", "100")
commands = len(cmd)
tokens = list(cmd)
for i in range(0, commands):
parse(tokens[i])def read(cmd):
if isinstance(cmd, str):
cmd = cmd.replace("!K", "1000")
cmd = cmd.replace("!H", "100")
for token in cmd:
parse(token)Context
StackExchange Code Review Q#128847, answer score: 7
Revisions (0)
No revisions yet.