enoua5

ffm-comp

Jun 19th, 2017
218
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.96 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. import sys, getopt
  3. def showHelp():
  4.     print("""Usage: """+sys.argv[0]+""" [OPTION]... [FILES]...
  5. Compiles a .ffm into a .ffb
  6. -b, bytewidth=BITS   takes a number 1-255 to use as the program's byte width
  7. -h, --help          shows this help page
  8. """)
  9. class State:
  10.     def __init__(self, tag, cmd, bar, jmp, lineNumber):
  11.         self.tag=tag
  12.         self.cmd=cmd
  13.         self.bar=bar
  14.         self.jmp=jmp
  15.         #for error reports
  16.         self.lineNumber=lineNumber
  17. def comp(program):
  18.     global BITWIDTH
  19.     global p
  20.     #the compiled program
  21.     c=[int(BITWIDTH/8)]
  22.     #registered tag names
  23.     regNames=[]
  24.     #program states
  25.     states=[]
  26.     program=program.split("\n")
  27.     n=0
  28.     for l in program:
  29.         n+=1
  30.         l=l.replace(" ","")
  31.         l=l.replace("\t","")
  32.         #check for comment
  33.         if len(l)!=0:
  34.             if l[0]!="#":
  35.                 l=l.split(";")
  36.                 #make sure the line is complete
  37.                 if(len(l)!=4):
  38.                     print("ERROR: Line "+str(n)+" of "+str(p)+": incorrect argument count")
  39.                     return False
  40.                 #no duplicate tag names
  41.                 if l[0] in regNames:
  42.                     print("ERROR: Line "+str(n)+" of "+str(p)+": duplicate tag")
  43.                     return False
  44.                 regNames.append(l[0])
  45.                 #make sure the command is valid
  46.                 l[1]=l[1].upper()
  47.                 if not(l[1] in ("LFT","RGT","INC","DEC","INP","OUT","NOP","HLT")):
  48.                     print("ERROR: Line "+str(n)+" of "+str(p)+": unrecognized command \""+str(l[1])+"\"")
  49.                     return False
  50.                 #make sure bar is valid
  51.                 if l[2].isdigit():
  52.                     l[2]=int(l[2])
  53.                 else:
  54.                     print("ERROR: Line "+str(n)+" of "+str(p)+": the bar provided is not an integer")
  55.                     return False
  56.                 if not(0<=l[2] and l[2] <= 255):
  57.                     print("ERROR: Line "+str(n)+" of "+str(p)+": the bar provided is out of range")
  58.                     return False
  59.                 #first part of making sure jumps are valid
  60.                 l[3]=l[3].split(":")
  61.                 if len(l[3])!=2:
  62.                     print("ERROR: Line "+str(n)+" of "+str(p)+": incorrectly formated pass:fail")
  63.                     return False
  64.                 #line is valid, add the state to the list
  65.                 states.append(State(l[0], l[1], l[2], l[3], n))
  66.     #check if program length is okay
  67.     if len(states)>2**BITWIDTH:
  68.         print("ERROR: program "+str(p)+" is too long to be compiled as a "+str(BITWIDTH)+" bit program")
  69.         return False
  70.     for s in states:
  71.         #other half of making sure jumps are valid
  72.         if not(s.jmp[0] in regNames) or not(s.jmp[1] in regNames):
  73.             print("ERROR: Line "+str(s.lineNumber)+" of "+str(p)+": duplicate tag")
  74.             return False
  75.         #it is safe to compile the line!
  76.         cmd=s.cmd
  77.         pos=("LFT","RGT","INC","DEC","INP","OUT","NOP","HLT").index(cmd)
  78.         #add command as a byte
  79.         c.append(pos)
  80.         #add bar as byte
  81.         c.append(s.bar)
  82.         #add fail jump
  83.         pos=regNames.index(s.jmp[0])
  84.         #buffer it to bit width
  85.         num="{0:b}".format(pos)
  86.         while len(num)<BITWIDTH:
  87.             num="0"+num
  88.         #split into 8 bit chunks
  89.         #thanks stackoverflow
  90.         num=[num[i:i+8] for i in range(0, len(num), 8)]
  91.         #convert back to ints and actually add to program
  92.         for i in range(len(num)):
  93.             c.append(int(num[i],2))
  94.         #add pass jump
  95.         pos=regNames.index(s.jmp[1])
  96.         #buffer it to bit width
  97.         num="{0:b}".format(pos)
  98.         while len(num)<BITWIDTH:
  99.             num="0"+num
  100.         #split into 8 bit chunks
  101.         #thanks stackoverflow
  102.         num=[num[i:i+8] for i in range(0, len(num), 8)]
  103.         #convert back to ints and actually add to program
  104.         for i in range(len(num)):
  105.             c.append(int(num[i],2))
  106.     return bytes(c)
  107. #get arguments
  108. args=sys.argv[1:]
  109. try:
  110.     opts,args=getopt.getopt(args,"hb:",["help","bytewidth="])
  111. except getopt.GetoptError:
  112.     showHelp()
  113.     sys.exit(2)
  114. #default bit width
  115. BITWIDTH=32
  116. #read options
  117. for opt, arg in opts:
  118.     if opt in ('-h','--help'):
  119.         showHelp()
  120.         sys.exit(0)
  121.     elif opt in ('-b','--bitwidth'):
  122.         if arg.isdigit():
  123.             if 1<=int(arg) and int(arg)<=255:
  124.                 if int(arg)>4:
  125.                     print("It is not recomended to use a byte width higher than 4 (32 bit).")
  126.                     print("Are you sure you want to contine?")
  127.                     cont=input("[type 'YES' in full, all caps, to continue]: ")
  128.                     if cont!="YES":
  129.                         print("Aborting compilation.")
  130.                         sys.exit(1)
  131.                 BITWIDTH=int(arg)*8
  132.         else:
  133.             print("ERROR: illegal bit width \""+str(arg)+"\"")
  134.             sys.exit(2)
  135. #make sure we were given a file
  136. if len(args)==0:
  137.     print("ERROR: no files given!")
  138.     sys.exit(2)
  139. #compile each file we recieved
  140. for p in args:
  141.     #turns to True if an error is enountered in compiling
  142.     ERROR=False
  143.     #find the name of the file without the extension
  144.     name=p.split('.')
  145.     if len(name)>1:
  146.         del name[-1]
  147.     name=".".join(name)
  148.     #default file
  149.     r="#file could not be read"
  150.     #open and read the file
  151.     try:
  152.         f=open(p,'r')
  153.         r=f.read()
  154.         f.close()
  155.     #read failed
  156.     except IOError:
  157.         print("ERROR: could not read file \""+str(p)+"\"")
  158.         ERROR=True
  159.     #compile the code
  160.     c=comp(r)
  161.     #compile failed
  162.     if c==False:
  163.         ERROR=True
  164.     #if we have our code
  165.     if not(ERROR):
  166.         #write to file
  167.         try:
  168.             f=open(name+".ffb",'wb')
  169.             f.truncate()
  170.             f.write(c)
  171.             f.close()
  172.         #write failed
  173.         except IOError:
  174.             print("ERROR: could not write to file \""+name+".ffb\"")
  175.             ERROR=True
  176.         #yay!
  177.         print("Successfully compiled "+p)
Advertisement
Add Comment
Please, Sign In to add comment