Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python3
- import sys, getopt
- def showHelp():
- print("""Usage: """+sys.argv[0]+""" [OPTION]... [FILES]...
- Compiles a .ffm into a .ffb
- -b, bytewidth=BITS takes a number 1-255 to use as the program's byte width
- -h, --help shows this help page
- """)
- class State:
- def __init__(self, tag, cmd, bar, jmp, lineNumber):
- self.tag=tag
- self.cmd=cmd
- self.bar=bar
- self.jmp=jmp
- #for error reports
- self.lineNumber=lineNumber
- def comp(program):
- global BITWIDTH
- global p
- #the compiled program
- c=[int(BITWIDTH/8)]
- #registered tag names
- regNames=[]
- #program states
- states=[]
- program=program.split("\n")
- n=0
- for l in program:
- n+=1
- l=l.replace(" ","")
- l=l.replace("\t","")
- #check for comment
- if len(l)!=0:
- if l[0]!="#":
- l=l.split(";")
- #make sure the line is complete
- if(len(l)!=4):
- print("ERROR: Line "+str(n)+" of "+str(p)+": incorrect argument count")
- return False
- #no duplicate tag names
- if l[0] in regNames:
- print("ERROR: Line "+str(n)+" of "+str(p)+": duplicate tag")
- return False
- regNames.append(l[0])
- #make sure the command is valid
- l[1]=l[1].upper()
- if not(l[1] in ("LFT","RGT","INC","DEC","INP","OUT","NOP","HLT")):
- print("ERROR: Line "+str(n)+" of "+str(p)+": unrecognized command \""+str(l[1])+"\"")
- return False
- #make sure bar is valid
- if l[2].isdigit():
- l[2]=int(l[2])
- else:
- print("ERROR: Line "+str(n)+" of "+str(p)+": the bar provided is not an integer")
- return False
- if not(0<=l[2] and l[2] <= 255):
- print("ERROR: Line "+str(n)+" of "+str(p)+": the bar provided is out of range")
- return False
- #first part of making sure jumps are valid
- l[3]=l[3].split(":")
- if len(l[3])!=2:
- print("ERROR: Line "+str(n)+" of "+str(p)+": incorrectly formated pass:fail")
- return False
- #line is valid, add the state to the list
- states.append(State(l[0], l[1], l[2], l[3], n))
- #check if program length is okay
- if len(states)>2**BITWIDTH:
- print("ERROR: program "+str(p)+" is too long to be compiled as a "+str(BITWIDTH)+" bit program")
- return False
- for s in states:
- #other half of making sure jumps are valid
- if not(s.jmp[0] in regNames) or not(s.jmp[1] in regNames):
- print("ERROR: Line "+str(s.lineNumber)+" of "+str(p)+": duplicate tag")
- return False
- #it is safe to compile the line!
- cmd=s.cmd
- pos=("LFT","RGT","INC","DEC","INP","OUT","NOP","HLT").index(cmd)
- #add command as a byte
- c.append(pos)
- #add bar as byte
- c.append(s.bar)
- #add fail jump
- pos=regNames.index(s.jmp[0])
- #buffer it to bit width
- num="{0:b}".format(pos)
- while len(num)<BITWIDTH:
- num="0"+num
- #split into 8 bit chunks
- #thanks stackoverflow
- num=[num[i:i+8] for i in range(0, len(num), 8)]
- #convert back to ints and actually add to program
- for i in range(len(num)):
- c.append(int(num[i],2))
- #add pass jump
- pos=regNames.index(s.jmp[1])
- #buffer it to bit width
- num="{0:b}".format(pos)
- while len(num)<BITWIDTH:
- num="0"+num
- #split into 8 bit chunks
- #thanks stackoverflow
- num=[num[i:i+8] for i in range(0, len(num), 8)]
- #convert back to ints and actually add to program
- for i in range(len(num)):
- c.append(int(num[i],2))
- return bytes(c)
- #get arguments
- args=sys.argv[1:]
- try:
- opts,args=getopt.getopt(args,"hb:",["help","bytewidth="])
- except getopt.GetoptError:
- showHelp()
- sys.exit(2)
- #default bit width
- BITWIDTH=32
- #read options
- for opt, arg in opts:
- if opt in ('-h','--help'):
- showHelp()
- sys.exit(0)
- elif opt in ('-b','--bitwidth'):
- if arg.isdigit():
- if 1<=int(arg) and int(arg)<=255:
- if int(arg)>4:
- print("It is not recomended to use a byte width higher than 4 (32 bit).")
- print("Are you sure you want to contine?")
- cont=input("[type 'YES' in full, all caps, to continue]: ")
- if cont!="YES":
- print("Aborting compilation.")
- sys.exit(1)
- BITWIDTH=int(arg)*8
- else:
- print("ERROR: illegal bit width \""+str(arg)+"\"")
- sys.exit(2)
- #make sure we were given a file
- if len(args)==0:
- print("ERROR: no files given!")
- sys.exit(2)
- #compile each file we recieved
- for p in args:
- #turns to True if an error is enountered in compiling
- ERROR=False
- #find the name of the file without the extension
- name=p.split('.')
- if len(name)>1:
- del name[-1]
- name=".".join(name)
- #default file
- r="#file could not be read"
- #open and read the file
- try:
- f=open(p,'r')
- r=f.read()
- f.close()
- #read failed
- except IOError:
- print("ERROR: could not read file \""+str(p)+"\"")
- ERROR=True
- #compile the code
- c=comp(r)
- #compile failed
- if c==False:
- ERROR=True
- #if we have our code
- if not(ERROR):
- #write to file
- try:
- f=open(name+".ffb",'wb')
- f.truncate()
- f.write(c)
- f.close()
- #write failed
- except IOError:
- print("ERROR: could not write to file \""+name+".ffb\"")
- ERROR=True
- #yay!
- print("Successfully compiled "+p)
Advertisement
Add Comment
Please, Sign In to add comment