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

Simple code-golf programming language written in Python

Submitted by: @import:stackexchange-codereview··
0
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

Solution

Seeing as it's a long post, I'll just see where I can get, starting now.

First, prompt:

def prompt():
    cmd = input("> ")
    return cmd


You 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 with

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])


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 with

elif 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 use

elif 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 -= 100


You 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 cmd
def 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.