#! python 2.7 """ V: 0.0.1 Test Build Easy As Py easyPyCompiler.py Last edit was: Sunday, December 11, 2016 Jacob DiDiodato A python-based interpreter for very novice programmers Probably pretty slow and error-prone Should write to a temporary python file and run it with syntax error checking A very space-minded language My apologies about not much commenting. I think a lot of this is fairly self-explainatory. If people want it then I'll add it later My attempt at writing a compiler without an AST (just text verification) """ #Includes pygame and random natively! Thats fun! import pygame from random import * import os,sys dQuote = '"' sQuote = "'" #Words and symbols you can't use in var names keyWords = ["print", "set", "to", "random", "add", "False", "None", "True", "and", "as", "assert", "break", "class", "continue", "def", "del", "elif", "else", "except", "finally", "for", "from", "global", "if", "import", "in", "is", "lambda", "nonlocal", "not", "or", "pass", "raise", "return", "try", "while", "with", "yield"] keyChars = ['~', '`', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '+', '=', '{', '[', '}', ']', ':', ';', dQuote, sQuote, '<', ',', '>', '.', '?', '/', '|', '\\'] def generateRandom(commands): exclusive = False try: commands[3] except IndexError: exclusive = True else: if (commands[3] == "e"): exclusive = True return "randint(" + str(commands[0]) + ", " + str(commands[2]) + " - " + str(int(exclusive)) + ")" def checkRandomNum(commands, lineNo, globalVars): if (commands[1] == "to"): if (globalVars.get(commands[0]) != None): commands[0] = globalVars.get(commands[0]) if (globalVars.get(commands[2]) != None): commands[2] = globalVars.get(commands[2]) try: int(commands[0]) int(commands[2]) except ValueError: print "Line " + str(lineNo + 1) + "\nValueError: [easyPyError #4] One of the numbers you have entered cannot be converted to an integer." return -1 else: if (int(commands[2]) < int(commands[0])): print "Line " + str(lineNo + 1) + "\nRandomError: [easyPyError #5] Your second number in range is larger than your first number in range.\nConsider switching them." return -1 else: return generateRandom(commands) else: print "Line " + str(lineNo + 1) + "\nFormattingError: [easyPyError #3] Missing word 'to'.\nFormat random number like: 'random num1 to num2 (i/e)'." return -1 def addition(commands): return str(commands[0]) + "+" + str(commands[2]) def checkAddNum(commands, lineNo, globalVars): if (commands[1] == "to"): if (globalVars.get(commands[0]) != None): commands[0] = globalVars.get(commands[0]) if (globalVars.get(commands[2]) != None): commands[2] = globalVars.get(commands[2]) try: int(commands[0]) int(commands[2]) except ValueError: print "Line " + str(lineNo + 1) + "\nValueError: [easyPyError #4] One of the numbers you have entered cannot be converted to an integer." return -1 else: return addition(commands) else: print "Line " + str(lineNo + 1) + "\nFormattingError: [easyPyError #3] Missing word 'to'.\nFormat addition like: 'add num1 to num2'." return -1 def subtract(commands): return str(commands[0]) + "-" + str(commands[2]) def checkSubNum(commands, lineNo, globalVars): if (commands[1] == "from"): if (globalVars.get(commands[0]) != None): commands[0] = globalVars.get(commands[0]) if (globalVars.get(commands[2]) != None): commands[2] = globalVars.get(commands[2]) try: int(commands[0]) int(commands[2]) except ValueError: print "Line " + str(lineNo + 1) + "\nValueError: [easyPyError #4] One of the numbers you have entered cannot be converted to an integer." return -1 else: return subtract(commands) else: print "Line " + str(lineNo + 1) + "\nFormattingError: [easyPyError #3] Missing word 'from'.\nFormat subtraction like: 'subtract num1 from num2'." return -1 def interpret(srcText): #the container for all global variables in the code globalVars = {} runningTab = "" open("pySource.py", "w").close() pySource = open("pySource.py", "a") pySource.write("#! python2.7\n#Compiled with the Easy As Py Compiler\n\nimport pygame\nfrom random import *\n\n") line = len(srcText) for i in range(line): commands = srcText[i].split(" ") #To print a string/variable if (commands[0] == "print"): pySource.write("print ") #If i am printing a variable check if that variable exists #If it does then i print it but if it doesnt then i just print the word instead if (globalVars.get(commands[1]) != None): pySource.write(runningTab + commands[1] + "\n") elif (commands[1] == "random"): if (checkRandomNum(commands[2:], i, globalVars) != -1): pySource.write(runningTab + checkRandomNum(commands[2:], i, globalVars) + "\n") else: break elif (commands[1] == "add"): if (checkAddNum(commands[2:], i, globalVars) != -1): pySource.write(runningTab + checkAddNum(commands[2:], i, globalVars) + "\n") else: break elif (commands[1] == "subtract"): if (checkSubNum(commands[2:], i, globalVars) != -1): pySource.write(runningTab + checkSubNum(commands[2:], i, globalVars) + "\n") else: break #If i am printing a string else: #Print the contents of the string pySource.write(runningTab + dQuote + " ".join(commands[1:]) + dQuote + "\n") #To set a variable elif (commands[0] == "set"): #Checking syntax if (commands[2] == "to"): badVar = False for j in range(len(keyChars)): for k in range(len(commands[1])): if (commands[1][k] == keyChars[j]): print(commands[1][k]) print "Line " + str(i + 1) + "\nVariableNameError: [easyPyError #6] Invalid variable name.\nThe character '" + keyChars[j] + "' can't be used in a variable name." badVar = True for j in range(len(keyWords)): if (commands[1] == keyWords[j]): print "Line " + str(i + 1) + "\nVariableNameError: [easyPyError #6] Invalid variable name.\nThe word '" + keyWords[j] + "' is special and can't be used in a variable name." badVar = True if (badVar == True): break #Set our variable internally in a sort of RAM #I use a dictionary to specify an arbitrarily large amount of RAM #Write it into python code if (commands[3] == "random"): if (checkRandomNum(commands[4:], i, globalVars) != -1): exclusive = False try: commands[7] except IndexError: exclusive = True else: if (commands[7] == "e"): exclusive = True if (globalVars.get(commands[4]) != None): commands[4] = globalVars.get(commands[4]) if (globalVars.get(commands[6]) != None): commands[6] = globalVars.get(commands[6]) randVar = randint(int(commands[4]), int(commands[6]) - int(exclusive)) globalVars[commands[1]] = randVar pySource.write(runningTab + commands[1] + "=" + str(globalVars[commands[1]]) + "\n") else: break elif (commands[3] == "add"): if (checkAddNum(commands[4:], i, globalVars) != -1): globalVars[commands[1]] = int(commands[4]) + int(commands[6]) pySource.write(runningTab + commands[1] + "=" + str(checkAddNum(commands[4:], i, globalVars)) + "\n") else: break elif (commands[3] == "subtract"): if (checkSubNum(commands[4:], i, globalVars) != -1): globalVars[commands[1]] = int(commands[4]) - int(commands[6]) pySource.write(runningTab + commands[1] + "=" + str(checkSubNum(commands[4:], i, globalVars)) + "\n") else: break else: try: globalVars[commands[1]] = int(commands[3]); pySource.write(runningTab + commands[1] + "=" + str(globalVars[commands[1]]) + "\n") except ValueError: globalVars[commands[1]] = commands[3]; pySource.write(runningTab + commands[1] + "=" + dQuote + globalVars[commands[1]] + dQuote + "\n") else: #If we are missing some syntax print "Line " + str(i + 1) + "\nFormattingError: [easyPyError #3] Missing word 'to'.\nFormat varible setting like: 'set varName to value'." break elif (commands[0] == "random"): pySource.write(runningTab + checkRandomNum(commands[1:], i, globalVars)) elif (commands[0] == "add"): pySource.write(runningTab + checkAddNum(commands[1:], i, globalVars)) elif (commands[0] == "subtract"): pySource.write(runningTab + checkSubNum(commands[1:], i, globalVars)) elif (commands[0] == "!"): pySource.write("#" + " ".join(commands[1:]) + "\n") elif (commands[0] == ""): pass #If the first token is not recognized by the compiler else: print "Line " + str(i + 1) + "\nSyntaxError: [easyPyError #2] Token or function '" + str(commands[0] + "' does not exist.\nTake a look at the first word of line " + str(i + 1) + " to see if thats what you meant to type.") pySource.close() os.remove("pySource.py") return -1 pySource.close() os.system("python pySource.py") #Keep the following line in the release build #os.remove("pySource.py") return 0 def openFile(): #Get the filename from the command line arguments filename = str(sys.argv[1]) #Check to see if the file exists try: #Open the file src = open(filename, "r") except IOError: #Give a very English defintion of the error print "Line 0\nFileNameError: [easyPyError #1] That file does not exist.\nTry naming the file something like: '" + str(sys.argv[1].split(".")[0]) + ".easyPy'" else: srcText = src.read().split("\n") interpret(srcText) def main(): openFile() main()