Interpreter written in Python not working properly - python

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.

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

Related

Accessible variables at the root of a python script

I've declared a number of variables at the start of my script, as I'm using them in a number of different methods ("Functions" in python?). When I try to access them, I can't seem to get their value = or set them to another value for that matter. For example:
baseFile = open('C:/Users/<redacted>/Documents/python dev/ATM/Data.ICSF', 'a+')
secFile = open('C:/Users/<redacted>/Documents/python dev/ATM/security.ICSF', 'a+')
def usrInput(raw_input):
if raw_input == "99999":
self.close(True)
else:
identity = raw_input
def splitValues(source, string):
if source == "ident":
usrTitle = string.split('>')[1]
usrFN = string.split('>')[2]
usrLN = string.split('>')[3]
x = string.split('>')[4]
usrBal = Decimal(x)
usrBalDisplay = str(locale.currency(usrBal))
elif source == "sec":
usrPIN = string.split('>')[1]
pinAttempts = string.split('>')[2]
def openAccount(identity):
#read all the file first. it's f***ing heavy but it'll do here.
plString = baseFile.read()
xList = plString.split('|')
parm = str(identity)
for i in xList:
substr = i[0:4]
if parm == substr:
print "success"
usrString = str(i)
else:
lNumFunds = lNumFunds + 1
splitValues("ident", usrString)
When I place baseFile and secFile in the openAccount method, I can access the respective files as normal. However, when I place them at the root of the script, as in the example above, I can no longer access the file - although I can still "see" the variable.
Is there a reason to this? For reference, I am using Python 2.7.
methods ("Functions" in python?)
"function" when they "stand free"; "methods" when they are members of a class. So, functions in your case.
What you describe does definitely work in python. Hence, my diagnosis is that you already read something from the file elsewhere before you call openAccount, so that the read pointer is not at the beginning of the file.

I have Python code for detecting input parameter - How to do similar in Powershell

I have snippit of Python code that AUTOMATICALLY detects one of many parameters that an external program is sending.
In this case, the parameter name is date-sent
__name__="__main__"
import sys, os, traceback
import commands
# Switch this to 0 when in production mode.
debugMode = 1
def main(args):
try:
attributeMap = parseInput(args)
dateSent = attributeMap["date-sent"]
print "Script-attribute=script value"
return
except:
error()
print "something went wrong!"
return "something went wrong!"
def parseInput(args):
attributeMap = {}
delimiter = "="
for item in args:
if delimiter in item:
tuple = item.split(delimiter)
attributeMap[tuple[0]] = tuple[1]
return attributeMap
def error():
# "SCRIPT PROCESSING ERROR"
if(debugMode):
traceback.print_exc(file=sys.stdout)
return ""
#-----------------------------------------------------------------
# DOS-style shells (for DOS, NT, OS/2):
#-----------------------------------------------------------------
def getstatusoutput(cmd):
""" Return (status, output) of executing cmd in a
shell."""
pipe = os.popen(cmd + ' 2>&1', 'r')
text = pipe.read()
sts = pipe.close()
if sts is None: sts = 0
if text[-1:] == '\n': text = text[:-1]
return sts, text
#-----------------------------------------------------------------
# Entry Point
#-----------------------------------------------------------------
if __name__ == "__main__":
if(len(sys.argv) == 0):
error()
else:
main(sys.argv)
How to do this in powershell, i.e. an external program that I have no control over send in many variables, i.e. sent-data, sender-name, sender-up, etc. How do I make my program detect only sent-data, just like this Python code did.
You use $args in Powershell, similar to sys.argv used in the Python example.
Read more here - http://technet.microsoft.com/en-us/library/ff730958.aspx

Why does my program add ('', ' to the name of my file?

Here is my code (sorry for the messy code):
def main():
pass
if __name__ == '__main__':
main()
from easygui import *
import time
import os
import random
import sys
##multenterbox(msg='Fill in values for the fields.', title=' ', fields=(), values=())
msg = "Enter your personal information"
title = "Credit Card Application"
fieldNames = ["First name",'Last name','email',"Street Address","City","State","ZipCode",'phone','phone 2)']
fieldValues = [] # we start with blanks for the values
fieldValues = multenterbox(msg,title, fieldNames)
# make sure that none of the fields was left blank
def make(x):
xys = x,".acc"
xyzasd = str(xys)
tf = open(xyzasd,'a+')
tf.writelines(lifes)
tf.writelines("\n")
tf.writelines("credits = 0")
tf.close
def add(x):
nl = "\n"
acc = ".acc"
xy = x + acc
exyz = xy
xyz = exyz
xxx = str(xyz)
tf = open('accounts.dat',"a+")
tf.writelines(nl)
tf.writelines(xxx)
tf.close
while 1:
if fieldValues == None: break
errmsg = ""
for i in range(len(fieldNames)-1):
if fieldValues[i].strip() == "":
errmsg += ('"%s" is a required field.\n\n' % fieldNames[i])
if errmsg == "":
break # no problems found
fieldValues = multenterbox(errmsg, title, fieldNames, fieldValues)
names = enterbox(msg= ('confirm FIRST name and the FIRST LETTER of the persons LAST name'))
##txt = "acc"
##na = str(name)
##name = (names)
life = ( str(fieldValues))
lifes = life,'\n'
herro = ("Reply was: %s" % str(fieldValues))
correct = buttonbox(msg=(herro,'\n is that correct'),choices = ('yes','no','cancel'))
if correct == "yes":
make(names)
add(names)
elif correct == "no":
os.system('openacc.py')
time.sleep(0.5)
sys.exit()
else:
os.system('cellocakes-main.py')
sys.exit()
os.system('cellocakes-main.py')
I don't know what the problem is also I am sorry about how sloppy it was programmed I have a white board to help me out still new to programming (I'm only 13) sorry. Personally I think the issue is in the def add area's syntax but because I am still new I don't see the issue personally I am hoping to have a more experienced programmer help me out.
This is an answer not directly answering your question.
Alas, comment fields are STILL not capable to hold formatted code, so I choose this way.
def main():
pass
if __name__ == '__main__':
main()
This is a nice coding pattern, but used by you in a useless way.
It is supposed to prevent executing of the stuff if it is imported as a module and not executed as a script.
Nevertheless, it is not bad to use it always, but then put your code inside the main() function instead of adding it below.
fieldNames = ["First name",'Last name','email',"Street Address","City","State","ZipCode",'phone','phone 2)']
There is a ) too much.
fieldValues = [] # we start with blanks for the values
fieldValues = multenterbox(msg,title, fieldNames)
The second line makes the first one useless, as you don't use fieldValues in-between.
It would be different if you expected multenterbox() to fail and would want [] as a default value.
def make(x):
xys = x,".acc"
xyzasd = str(xys)
tf = open(xyzasd,'a+')
tf.writelines(lifes)
tf.writelines("\n")
tf.writelines("credits = 0")
tf.close
You was already told about this: x, ".acc" creates a tuple, not a string. To create a string, use x + ".acc".
Besides, your close call is no call, because it is missing the (). This one just references the function and ignores the value.
A better way to write this would be (please name your variables appropriately)
with open(xyzs, 'a+') as tf:
tf.writelines(lifes)
tf.writelines("\n")
tf.writelines("credits = 0")
The with statement automatically closes the file, even if an error occurs.
Besides, you use writelines() wrong: it is supposed to take a sequence of strings and write each element to the file. As it doesn't add newlines in-between, the result looks the same,. but in your case, it writes each byte separately, making it a little bit more inefficient.
Additionally, you access the global variable lifes from within the function. You should only do such things if it is absolutely necessary.
def add(x):
Here the same remarks hold as above, plus
xy = x + acc
exyz = xy
xyz = exyz
xxx = str(xyz)
why that? Just use xy; the two assignments do nothing useful and the str() call is useless as well, as you already have a string.
for i in range(len(fieldNames)-1):
if fieldValues[i].strip() == "":
errmsg += ('"%s" is a required field.\n\n' % fieldNames[i])
Better:
for name, value in zip(fieldNames, fieldValues):
if not value.strip(): # means: empty
errmsg += '"%s" is a required field.\n\n' % name
Then:
life = ( str(fieldValues))
makes a string from a list.
lifes = life,'\n'
makes a tuple from these 2 strings.
os.system('openacc.py')
os.system('cellocakes-main.py')
Please don't use os.system(); it is deprecated. Better use the subprocess module.
The problem of the question is here:
# assign the tuple (x, ".acc") to xys
xys = x,".acc"
# now xyzasd is the tuple converted to a string, thus
# making the name of your file into '("content of x", ".acc")'
xyzasd = str(xys)
# and open file named thus
tf = open(xyzasd,'a+')
What you wanted to do is:
# use proper variable and function names!
def make_account(account):
filename = account + '.acc'
the_file = open(filename, 'a+')
....
On the other hand there are other problems with your code, for example the
def main():
pass
if __name__ == '__main__':
main()
is utterly useless.

Story Progression through if blocks and variables: Failure

Ok. I'm designing a small text-based RPG, however, I need to have the player be able to save the game. I've succeeded in doing this through using the pickle module, but I'm trying to get the player to be able to get back to their previous point in the storyline through using this variable I call 'storypointe'. Basically it would work like this:
if storypointe == 0:
#Story, story, stuff happens here...
storypointe += 1
if storypointe == 1:
#More story, more story, more stuff happens here....
I would then pickle the variable storypointe, and when the game is loaded (meaning using pickle.load to get player stats and storypointe from whatever file I pickled it into), and IDEALLY it would just start from whichever code block storypointe corresponds to. The actual code is way too much work for the writer and (perhaps) the readers, so I've written the following code that simulates the same environment and replicates the same problem.
storypointe = 0
jump = 0
spin = 0
dive = 0
roar = 0
savefile = "C:\Users\Sammu\The Folder\databin.txt"
import pickle, sys
def save ():
with open(savefile, 'w') as savebin:
actions = [jump, spin, dive, roar, storypointe]
pickle.dump (actions, savebin)
def load ():
with open(savefile, 'r') as loadbin:
actions2 = pickle.load (loadbin)
print actions2
jump = actions2[0]
spin = actions2[1]
dive = actions2[2]
roar = actions2[3]
storypointe = actions2[4]
#Begin the code#
gameIO = raw_input ('Would you like to load previous game?\n>>> ')
if gameIO in ['yes', 'load', 'load game', 'Yes', 'Load', 'Load game']:
load ()
if storypointe == 0:
action = raw_input ('Would you like to jump, spin, dive or roar?\n>>> ')
if action in ['jump', 'Jump']:
jump += 1
print jump
if action in ['spin', 'Spin']:
spin += 1
print spin
if action in ['dive', 'Dive']:
dive += 1
print dive
if action in ['roar', 'Roar']:
roar += 1
print roar
storypointe += 1
if storypointe == 1:
print "\n\nYou have progressed to the next stage"
save ()
So if storypointe is equal to actions2[4], then that must mean it should be equal to 1. But the problem here is that it's always following the first code block, starting from
action = raw_input ('#yadayadayada')
instead of:
print "You have progressed to the next stage"
I think you're confused about Python scoping.
Here you make a new variable at module level:
storypointe = 0
[...]
But here:
def load ():
with open(savefile, 'r') as loadbin:
actions2 = pickle.load (loadbin)
[...]
storypointe = actions2[4]
you simply make a new local name "storypointe" in the function load. It doesn't affect what storypointe is at the module level. I would store your variables in a class or a dict instead, which would also prevent having to do the actions2[i] stuff.
Rather than express your narrative as a bunch of if statements consider it as a state machine, if you express your story-line as a tree then you can easily store routes through the game as references to the next node in the tree, you can also store references (unique) to each node, allowing for easy saving and loading of positions.
See for example
class Node(object):
def __init__(self, parent, children=None):
self.parent = parent
self.children = children or {}
story = {}
story['a'] = Node(None)
story['b'] = Node(a)
story['a'].children['b'] = story['b']

Zelle Graphics interface issue

I'm using Zelle Graphics library and I'm having trouble replacing graphics objects (which, in this case, happens to be text objects).
Here's the code:
from Graphics import *
winName = "Window"
win = Window(winName,600,500)
win.setBackground(Color('silver'))
title = Text((300,20),"Zack's Flash Card Maker")
title.draw(win)
p1 = Rectangle((50, 100),(550,400))
p1.setFill(Color("black"))
p1.draw(win)
class FlashCard:
def __init__(self):
self.commands = {'addQuestion':self.addQuestion,'startGame':self.startGame}
self.stack = []
self.questions = {}
self.questionAnswered = False
self.questionsCorrect = 0
self.questionsIncorrect = 0
def addQuestion(self):
question = ' '.join(self.stack)
self.stack = []
answer = input(question)
self.questions[question] = answer
def startGame(self):
for question in self.questions:
if(self.questionAnswered == False):
answer=input(question)
questionText = Text((300,150),question)
questionText.setFill(Color("white"))
questionText.draw(win)
if(answer == self.questions[question]):
questionAnswer = Text((300,200),answer + " is correct!")
questionAnswer.setFill(Color("green"))
questionAnswer.draw(win)
self.questionsCorrect = self.questionsCorrect + 1
continue
else:
questionAnswer = Text((300,200),answer + " is incorrect. Study this one.")
questionAnswer.setFill(Color("red"))
questionAnswer.draw(win)
self.questionsIncorrect = self.questionsIncorrect + 1
continue
def interpret(self,expression):
for token in expression.split():
if token in self.commands:
operator = self.commands[token]
operator()
else:
self.stack.append(token)
i = FlashCard()
i.interpret('What is your dog\'s name? addQuestion')
i.interpret('What is your favorite thing to do? addQuestion')
i.interpret('startGame')
This is essentially a mini flash card program I'm making. It takes the interpret commands at the bottom and executes them based on the dictionary in the FlashCard class. It basically works: it does the correct text objects. However, text begins to overlap other text objects because it re-draws. I've tried moving the .draw function all over, but it either doesn't appear at all or it overlaps.
Anyone have any suggestions? I want the text to replace for each new flashcard question.
Thanks!
there's an undraw() command that you need to use if you want to make something invisible. I'd recommend placing it right before your continue statements. It's used like
questionText.undraw()
questionAnswer.undraw()
Alternatively, you can use the del command to get rid of each questionText/questionAnswer instance when you're done with it. That's probably a better option since you're actually freeing up the memory that way instead of storing data and not doing anything with it.
You can use setText method to change the text.
example:
string = Text(Point(1, 1), 'original string')
sting.setText('new string')

Categories

Resources