# tiny.py: compiler for tiny
import sys
import datetime # for date header
next = None
src = "" # Tiny source printed at end
hatused = False # check for '^', include pow
addr = {'a':10,'b':11,'c':12,'d':13,'e':14,
'f':15,'g':16,'h':17,'i':18,'j':19,
'k':20,'l':21,'m':22,'n':23,'o':24,
'p':25,'q':26,'r':27,'s':28,'t':29,
'u':30,'v':31,'w':32,'x':33,'y':34,
'z':35}
op = {'+':"add", '-':"sub", '*':"mul", '/':"div" }
def next_var(): # for temp variables
next_var.count += 1
return str(next_var.count)
next_var.count = -1
def next_while(): # for whiles
next_while.count += 1
return str(next_while.count)
next_while.count = -1
def next_if(): # for if-then-else's
next_if.count += 1
return str(next_if.count)
next_if.count = -1
def M():
global src
src = ""
d = datetime.datetime.today()
gen("### compiled by Neal Wagner on\n");
gen("# " + d.ctime() + "\n")
genf("pre.s") # 1st part of framework
scan()
S()
while next != '$':
S()
genf("post1.s") # 2nd part of framework
if hatused:
genf("pow.s") # only if '^' used
genf("post2.s") # final part of framewk
write_source()
def S(): # statement
if next == '[': # if-then-else
scan()
I()
elif next == '{': # while
scan()
W()
elif next.islower(): # assignment
# don't scan; A wants first char
A()
elif next == '<': # output
scan()
P()
elif next == '>': # input
scan()
G()
else:
error(1)
def I(): # if-then-else
ind = next_if() # next counter for if labels
gen("### IfThenElseStart" + ind + ":\n")
res = E()
gen(" l.d $f2, " + str(8*res) + "($s1)\n")
gen(" l.d $f4, 0($s1)\n")
gen(" c.eq.d $f2, $f4\n")
gen(" bc1t ThenEnd" + ind + "\n")
if next != '?':
error(2)
else: # then[-else]
scan()
gen("# ThenPart" + ind + "\n")
S()
while next != ']' and next != ':':
scan()
S()
if next == ':':
gen(" j EndIf" + ind + "\n")
gen("ThenEnd" + ind + ":\n")
if next == ':':
# else part
scan() # scan past ':'
S()
while next != ']':
S()
scan() # past ']'
gen("EndIf" + ind + ":\n")
else:
scan() # past ']'
gen("### EndIf" + ind + "\n")
def W(): # while
ind = next_while() # next counter for while
gen("WhileStart" + ind + ":\n")
res = E() # while test
gen(" l.d $f2, " + str(8*res) + "($s1)\n")
gen(" l.d $f4, 0($s1)\n")
gen(" c.eq.d $f2, $f4\n")
gen(" bc1t WhileEnd" + ind + "\n")
if next != '?':
error(4)
else:
scan()
gen("# WhileBody" + ind + "\n")
# while body
S()
while next != '}':
S()
scan()
gen(" j WhileStart" + ind + "\n")
gen("WhileEnd" + ind + ":\n")
def A(): # assignment
# called because starts with lower-case
if next.isalpha(): # no need to check
lhs = addr[next]
else:
error(13)
scan()
if next != '=':
error(5)
else:
scan()
res = E()
if next != ';':
error(6)
scan()
gen_assign(lhs, res);
def P(): # output special char or expr
if next.isupper():
if next == 'N': # newline
gen_white("NewL")
elif next == 'B':
gen_white("Blank")
elif next == 'T':
gen_white("Tab")
else:
error(14)
scan()
if next == ';':
scan()
else:
error(7)
else:
res = E()
if next == ';':
gen_output_expr(res)
scan()
else:
error(8)
| def G(): # get a double
# already scanned past ">"
if next.islower():
gen_input(addr[next])
scan()
if next != ';':
error(9)
scan()
else:
error(10)
def E(): # expression
res = T()
while next == '+' or next == '-':
save = next
arg1 = res
scan()
arg2 = T()
res = 36 + int(next_var())
gen_op(res, arg1, save, arg2)
return res
def T(): # term
res = U()
while next == '*' or next == '/' or \
next == '%' or next == '@':
save = next
arg1 = res
scan()
arg2 = U()
res = 36 + int(next_var())
gen_op(res, arg1, save, arg2)
return res
def U(): # ugly term
res = F()
if next == '^':
arg1 = res
scan()
arg2 = U()
res = 36 + int(next_var())
gen_op(res, arg1, '^', arg2)
return res
def F(): # factor
# possible initial unary +s or -s
negs = 0 # no unary minus
while next == '+' or next == '-':
if next == '-':
negs += 1
scan()
negs = negs % 2; # 1 = same as 1 minus
if next.isdigit(): # digit
res = int(next)
scan()
elif next.isalpha(): # lower-case
res = addr[next]
scan()
elif next == '(': # expr in parens
scan()
res = E()
if next == ')':
scan()
else:
error(11)
else:
error(12)
if negs == 1: # now negate
res0 = res
res = 36 + int(next_var())
gen_op(res, 0, '-', res0) # use 0.0 - res to negate
return res
def error(n):
sys.stdout.write("ERROR:" +
str(n))
sys.stdout.write(", SOURCE:" +
src + "\n")
sys.exit(1)
def getch():
global src
global hatused
c = sys.stdin.read(1)
if len(c) > 0:
if c == '^':
hatused = True
src += c
return c
else:
return None
def scan():
global next
next = getch()
if next == None:
sys.exit(1)
while next.isspace():
next = getch()
# nasty code to handle comments
while next == '#':
while next != "\n":
next = getch()
while next.isspace():
next = getch()
def genf(fname): # output file of MIPS
f = open(fname, 'r')
for line in f:
gen(line)
def gen_assign(left, right):
gen("# M[" + str(left) + "] = M[" + str(right) + "]\n")
gen(" l.d $f2, " + str(8*right) + "($s1)\n")
gen(" s.d $f2, " + str(8*left) + "($s1)\n")
def gen_output_expr(res):
gen("# Output M[" + str(res) + "]\n")
gen(" li $v0, 3\n")
gen(" l.d $f12, " + str(8*res) + "($s1)\n")
gen(" syscall\n")
def gen_input(res):
gen("# Read M[" + str(res) + "]\n")
gen(" li $v0, 7\n")
gen(" syscall\n")
gen(" s.d $f0, " + str(8*res) + "($s1)\n")
def gen_white(s): # 3 kinds of whitespace
gen("# Output " + s + "\n")
gen(" li $v0, 4\n")
gen(" la $a0, " + s + "\n")
gen(" syscall\n")
def gen_op(res, arg1, save, arg2): # all binary ops
gen("# M[" + str(res) + "] = M[" + str(arg1) + "] " +
save + " M[" + str(arg2) + "]\n")
gen(" l.d $f2, " + str(8*arg1) + "($s1)\n")
gen(" l.d $f4, " + str(8*arg2) + "($s1)\n")
if save in op:
gen(" " + op[save] + ".d $f6, $f2, $f4\n")
elif save == '^':
gen(" jal pow\n");
else:
gen(" div.d $f6, $f2, $f4\n")
gen(" trunc.w.d $f6, $f6\n")
gen(" cvt.d.w $f6, $f6\n")
if save == '%':
gen(" mul.d $f6, $f6, $f4\n")
gen(" sub.d $f6, $f2, $f6\n")
gen(" s.d $f6, " + str(8*res) + "($s1)\n")
def gen(line): # newline already present
sys.stdout.write(line)
def write_source(): # Tiny source as comments at end
global src
gen("### tiny source program:\n")
srcf = src.split("\n")
for line in srcf:
gen("# " + line + "\n")
M() |