![]() |
|||
| HSG |
|
Der Compiler benötigt das Modul PLY. Will man den Namen der Quelltextdatei nicht direkt in den Quelltext schreiben, so ist der normale Aufruf:
python3 py2bon.py add0.py
Aus der Datei 'add0.py'
from goto import goto, label
a = 12
b = 33
label .SB
if b > 0:
goto .SK
else:
goto .SE
label .SK
b = b - 1
a = a + 1
goto .SB # Achtung, wird leicht vergessen
label .SE
print(a,b)
wird die Datei 'add0.bon'.
tst2 jmp4 jmp7 dec2 inc1 jmp1 hlt # 12 # 33 ; a = 12 ; b = 33
Beispieldateien: add0.py, divmod.py, sumn.py
Verwendet wird die Syntax der Webseite Generation of Syntax Diagrams.
bon = (befehl bon)? befehl = inc|dec|jmp|tst|zuw|lab inc = 'ID' '=' 'ID' '+' 'NUMBER' dec = 'ID' '=' 'ID' '-' 'NUMBER' jmp = 'goto' 'LID' tst = 'if' 'ID' '>' 'NUMBER' ':' 'goto' 'LID' 'else' ':' 'goto' 'LID' zuw = 'ID' '=' 'NUMBER' lab = 'label' 'LID'
erzeugt folgende Syntaxdiagramme:

Zusätzlich zur Grammatik ist im Parser festgelegt, dass im inc- und dec-Befehl der erste und zweite Bezeichner ID gleich sein muss und dass die Zahl NUMBER gleich 1 sein muss. Ebenso überwacht der Parser, dass im tst-Befehl die Zahl NUMBER gleich 0 ist.
Die Token ID, LID, NUMBER, FROMZEILE, PRINTZEILE und KOMMENTAR sind folgendermaßen festgelegt:
ID = ⸢[a-zA-Z_][a-zA-Z_0-9]*⸥ LID = ⸢\.[a-zA-Z_][a-zA-Z_0-9]*⸥ NUMBER = ⸢\d+⸥ FROMZEILE = ⸢from.*⸥ PRINTZEILE = ⸢print.*⸥ KOMMENTAR = ⸢\#.*⸥
Die Token FROMZEILE, PRINTZEILE und KOMMENTAR werden ignoriert.
import ply.lex as lex
reserved = {'label' : 'LABEL',
'if' : 'IF',
'goto' : 'GOTO',
'else' : 'ELSE' }
tokens = ['ID','LID','NUMBER','FROMZEILE',
'PRINTZEILE','KOMMENTAR'] + list(reserved.values())
literals = '=:>-+'
t_LID = r'\.[a-zA-Z_][a-zA-Z_0-9]*'
def t_FROMZEILE(t):
r'from.*'
def t_PRINTZEILE(t):
r'print.*'
def t_KOMMENTAR(t):
r'\#.*'
def t_ID(t):
r'[a-zA-Z_][a-zA-Z_0-9]*'
t.type = reserved.get(t.value,'ID') # get gibt Wert aus Dictionary zurück,
return t # wenn nicht gefunden 'ID'
def t_NUMBER(t):
r'\d+'
t.value = int(t.value)
return t
def t_newline(t):
r'\n+'
t.lexer.lineno = t.lexer.lineno + len(t.value)
t_ignore = ' \t'
def t_error(t):
print('Unerwartetes Zeichen:',t.value[0],',Zeile:',s.lineno)
t.lexer.skip(1)
s = lex.lex()
import ply.yacc as yacc
n = 1 # Zeilennummer der Ausgabe
nv = 1 # Variablennummer
sym = {} # Symboltabelle, Dictionary
var = [] # Liste der Anfangs-Variablen mit Werten
lab = [] # Liste der Labels
lauf1 = '' # String für 1.Lauf
def p_0(p):
'bon : befehl bon'
def p_1(p):
'bon :'
def p_2(p):
'befehl : inc'
def p_3(p):
'befehl : dec'
def p_4(p):
'befehl : jmp'
def p_5(p):
'befehl : tst'
def p_6(p):
'befehl : zuw'
def p_7(p):
'befehl : lab'
def p_8(p):
"inc : ID '=' ID '+' NUMBER"
if (p[1] != p[3]) or (p[5] != 1):
print('Syntaxfehler in inc, Zeile:',s.lineno)
raise SyntaxError
global lauf1,n
lauf1 = lauf1+'inc '+p[1]+'\n'
n = n+1
def p_9(p):
"dec : ID '=' ID '-' NUMBER"
if (p[1] != p[3]) or (p[5] != 1):
print('Syntaxfehler in dec, Zeile:',s.lineno)
raise SyntaxError
global lauf1,n
lauf1 = lauf1+'dec '+p[1]+'\n'
n = n+1
def p_10(p):
"jmp : GOTO LID"
global lauf1,n
lauf1 = lauf1+'jmp '+p[2]+'\n'
n = n+1
def p_11(p):
"tst : IF ID '>' NUMBER ':' GOTO LID ELSE ':' GOTO LID"
if (p[4] != 0):
print('Syntaxfehler in tst, Zeile:',s.lineno)
raise SyntaxError
global lauf1,n
lauf1 = lauf1+'tst '+p[2]+'\n'
lauf1 = lauf1+'jmp '+p[7]+'\n'
lauf1 = lauf1+'jmp '+p[11]+'\n'
n = n+3
def p_12(p):
"zuw : ID '=' NUMBER"
global var,sym,nv
sym.update({p[1]:nv})
var.append((p[1],p[3]))
nv = nv + 1
def p_13(p):
'lab : LABEL LID'
global lab,sym
lab.append((p[2],n))
sym.update({p[2]:n})
def p_error(p):
print('Syntaxfehler in Zeile:',s.lineno)
pr = yacc.yacc()
import sys
try:
dateiname = sys.argv[1]
except:
dateiname = 'sumn.py'
datei = open(dateiname,'r')
inhalt = datei.read()
datei.close()
pr.parse(inhalt)
# Symboltabelle einarbeiten und Ausgabedatei erzeugen
zeilen = lauf1.split('\n')
zeilen.remove('')
ausgabename = dateiname.split('.')[0]+'.bon'
datei = open(ausgabename,'w')
for z in zeilen:
zalt = z.split(' ')
zneu = zalt[0]+str(sym.get(zalt[1]))+'\r\n'
datei.write(zneu)
datei.write('hlt\r\n')
for v in var:
zneu = '# '+str(v[1])+'\r\n'
datei.write(zneu)
for v in var:
zneu = '; '+str(v[0])+' = '+str(v[1])+'\r\n'
datei.write(zneu)
datei.close()
datei = open(ausgabename,'r') # DEBUG
inhalt = datei.read() # DEBUG
datei.close() # DEBUG
print(inhalt) # DEBUG
Wer den Compiler in Aktion sehen will, kann die mit ausführlichen DEBUG-Informationen versehene Version py2bonDEBUG.py benutzen.
Folgende Ausgabe wird bei der Verarbeitung obiger Datei 'add0.py' erzeugt.
>>>
WARNING: Token 'PRINTZEILE' defined, but not used
WARNING: Token 'FROMZEILE' defined, but not used
WARNING: Token 'KOMMENTAR' defined, but not used
WARNING: There are 3 unused tokens
Generating LALR tables
LexToken(FROMZEILE,'from goto import goto, label',1,0)
LexToken(ID,'a',3,30)
LexToken(ISTGLEICH,'=',3,32)
LexToken(NUMBER,'12',3,34)
LexToken(ID,'b',4,37)
zuw: a = 12
Symboltabelle: {'a': 1}
Variablentabelle: [('a', 12)]
befehl : zuw
LexToken(ISTGLEICH,'=',4,39)
LexToken(NUMBER,'33',4,41)
LexToken(LABEL,'label',6,45)
zuw: b = 33
Symboltabelle: {'a': 1, 'b': 2}
Variablentabelle: [('a', 12), ('b', 33)]
befehl : zuw
LexToken(LID,'.SB',6,51)
LexToken(IF,'if',7,55)
lab: label .SB
Labeltabelle: [('.SB', 1)]
Symboltabelle: {'a': 1, '.SB': 1, 'b': 2}
befehl : lab
LexToken(ID,'b',7,58)
LexToken(GROESSER,'>',7,60)
LexToken(NUMBER,'0',7,62)
LexToken(DOPPELPUNKT,':',7,63)
LexToken(GOTO,'goto',8,69)
LexToken(LID,'.SK',8,74)
LexToken(ELSE,'else',9,78)
LexToken(DOPPELPUNKT,':',9,82)
LexToken(GOTO,'goto',10,88)
LexToken(LID,'.SE',10,93)
LexToken(LABEL,'label',12,98)
tst : if b > 0 : goto .SK else : goto .SE
lauf1:
tst b
jmp .SK
jmp .SE
befehl : tst
LexToken(LID,'.SK',12,104)
LexToken(ID,'b',13,108)
lab: label .SK
Labeltabelle: [('.SB', 1), ('.SK', 4)]
Symboltabelle: {'a': 1, '.SB': 1, 'b': 2, '.SK': 4}
befehl : lab
LexToken(ISTGLEICH,'=',13,110)
LexToken(ID,'b',13,112)
LexToken(MINUS,'-',13,114)
LexToken(NUMBER,'1',13,116)
LexToken(ID,'a',14,118)
dec : b = b - 1
lauf1:
tst b
jmp .SK
jmp .SE
dec b
befehl : dec
LexToken(ISTGLEICH,'=',14,120)
LexToken(ID,'a',14,122)
LexToken(PLUS,'+',14,124)
LexToken(NUMBER,'1',14,126)
LexToken(GOTO,'goto',15,128)
inc : a = a + 1
lauf1:
tst b
jmp .SK
jmp .SE
dec b
inc a
befehl : inc
LexToken(LID,'.SB',15,133)
LexToken(KOMMENTAR,'# Achtung, wird leicht vergessen',15,156)
LexToken(LABEL,'label',17,190)
jmp : goto .SB
lauf1:
tst b
jmp .SK
jmp .SE
dec b
inc a
jmp .SB
befehl : jmp
LexToken(LID,'.SE',17,196)
LexToken(PRINTZEILE,'print(a,b)',19,201)
lab: label .SE
Labeltabelle: [('.SB', 1), ('.SK', 4), ('.SE', 7)]
Symboltabelle: {'a': 1, '.SB': 1, 'b': 2, '.SK': 4, '.SE': 7}
befehl : lab
bon :
bon : befehl bon
bon : befehl bon
bon : befehl bon
bon : befehl bon
bon : befehl bon
bon : befehl bon
bon : befehl bon
bon : befehl bon
bon : befehl bon
erzeugte Datei:
tst2
jmp4
jmp7
dec2
inc1
jmp1
hlt
# 12
# 33
; a = 12
; b = 33
>>>