Created
November 5, 2018 09:47
-
-
Save Pigu-A/a6b2997ab71504087fa5eb1cf953f28c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import argparse, string, sys | |
BCODES = { | |
"line": (10,), | |
"id": (11,), | |
"const": (12,), | |
"if": (13,0), | |
"goto": (14,), | |
"print": (15,0), | |
"stop": (16,0), | |
"+": (17,1), | |
"-": (17,2), | |
"<": (17,3), | |
"=": (17,4) | |
} | |
def scan(intx): | |
# Scans the Basic source and separate characters into a token list | |
toks = [] | |
buf = "" | |
st = 0 # start at whitespace state | |
pos = 0 | |
ttx = intx.lower() # make this case insensitive | |
while pos < len(ttx): | |
i = ttx[pos] | |
if st == 0: # whitespace | |
if i in string.digits: st = 1 | |
elif i in string.ascii_letters: st = 2 | |
elif i in string.punctuation: st = 3 | |
else: pos += 1 # skip the current character | |
continue # skip the below lines | |
elif st == 1: # number digits | |
if i in string.digits: | |
buf += i # add the character to the buffer | |
pos += 1 # advance to the next character | |
continue | |
elif st == 2: # letters | |
if i in string.ascii_letters: | |
buf += i | |
pos += 1 | |
continue | |
elif st == 3: # punctuations | |
if i in string.punctuation: | |
buf += i | |
pos += 1 | |
continue | |
# different type of character found | |
toks.append((st,buf)) # add the current state and sequence to the list | |
buf = "" | |
st = 0 # set the state back to 0 | |
if len(buf) > 0: toks.append((st,buf)) # empty the buffer | |
toks.append((-1,"EOF")) | |
return toks | |
# non-terminals are in integer index of parse table | |
# terminals are in string | |
PARSE_TABLE = [ | |
[[["slnum"],[1,0]],[["EOF"],["EOF"]]], # pgm | |
[[["slnum"],["slnum",2]]], # line | |
[[["id"],[3]],[["if"],[7]],[["print"],[10]],[["goto"],[11]],[["stop"],["stop"]]], # stmt | |
[[["id"],["id","=",4]]], # asgmt | |
[[["id","+","-","const"],[6,5]]], # exp | |
[[["+"],["+",6]],[["-"],["-",6]],[["EOF","lnum"],[]]], # exp' | |
[[["id"],["id"]],[["const"],["const"]]], # term | |
[[["if"],["if",8,"gtdlnum"]]], # if | |
[[["id","const"],[6,9]]], # cond | |
[[["<"],["<",6]],[["="],["=",6]]], # cond' | |
[[["print"],["print","id"]]], # print | |
[[["goto"],["goto","lnum"]]], # goto | |
] | |
def validate(tok,term): | |
# Checks if the token type matches the terminal | |
# returns the B-Code if match, None if not | |
outb = None | |
if tok[0] == -1 and term == "EOF": outb = () | |
if tok[0] == 1: # number | |
num = int(tok[1]) | |
if term in ["lnum","slnum","gtdlnum"] and 1 <= num <= 1000: | |
outb = (num,) | |
if term == "slnum": outb = BCODES["line"]+outb | |
if term == "gtdlnum": outb = BCODES["goto"]+outb | |
if term == "const" and 0 <= num <= 100: | |
outb = BCODES[term]+(num,) | |
else: # letters and punctuations | |
if term in ["if","goto","print","stop","+","-","<","="] and tok[1] == term: | |
outb = BCODES[term] | |
elif term == "id" and tok[0] == 2 and len(tok[1]) == 1: # id | |
outb = BCODES["id"]+(ord(tok[1])-ord("a")+1,) | |
return outb | |
def parse(toks): | |
# Parses the loken list into a B-Code table | |
pstk = ["EOF",0] # parse stack | |
outb = [] | |
tpos = 0 # token position | |
lpos = -1 # parsed line number | |
while tpos < len(toks): | |
ctok = toks[tpos] | |
if pstk[-1] == "EOF" and ctok == (-1,"EOF"): | |
break | |
elif type(pstk[-1]) is int: # non-terminal | |
found = False | |
for i in PARSE_TABLE[pstk[-1]]: | |
for j in i[0]: # valid terminals | |
found = validate(ctok,j) != None | |
if found: | |
pstk.pop() | |
break | |
if found: | |
pstk += i[1][::-1] # push the sequence to the stack backwards | |
break | |
if not found: raise Exception("Error in line {}".format(lpos)) | |
else: # terminal | |
bcod = validate(ctok,pstk[-1]) | |
if bcod == None: raise Exception("Error in line {}".format(lpos)) | |
if pstk[-1] == "slnum": lpos = bcod[1] | |
outb += bcod | |
pstk.pop() | |
tpos += 1 | |
return outb | |
if __name__ == "__main__": | |
ap = argparse.ArgumentParser() | |
ap.add_argument('fi', metavar='in', type=argparse.FileType('r'), help='Input file name') | |
ap.add_argument('fo', metavar='out', type=argparse.FileType('w'), help='Output file name') | |
args = ap.parse_args() | |
toks = scan(args.fi.read()) | |
bcodes = [] | |
try: | |
bcodes = parse(toks) | |
except Exception as e: | |
print(e) | |
sys.exit() | |
args.fo.write(" ".join([str(i) for i in bcodes])) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment