This repository was archived by the owner on Jun 15, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgeneration_code.py
More file actions
399 lines (331 loc) · 14.7 KB
/
generation_code.py
File metadata and controls
399 lines (331 loc) · 14.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
import argparse
import sys
import textwrap
from analyse_lexicale import FloLexer
from analyse_syntaxique import FloParser
import arbre_abstrait
from table_des_symboles import TableSymboles
num_etiquette_courante = -1 # Permet de donner des noms différents à toutes les étiquettes (en les appelant e0, e1,e2,...)
afficher_table = False
print_builtins = False
output = None
tableSymboles = TableSymboles()
def erreur(s, line=None):
"""affiche une erreur sur la sortie stderr et quitte le programme"""
print(f"Error (l{line}):" if line else "Error:", s, file=sys.stderr)
exit(1)
def printifm(*args, **kwargs):
"""
Un print qui ne fonctionne que si la variable.flo afficher_table vaut Vrai.
(permet de choisir si on affiche le code assembleur ou la table des symboles)
"""
if output:
print(*args, **kwargs, file=output)
def printift(*args, **kwargs):
"""
Un print qui ne fonctionne que si la variable.flo afficher_table vaut Vrai.
(permet de choisir si on affiche le code assembleur ou la table des symboles)
"""
if afficher_table:
print(*args, **kwargs)
def typeStr(t):
"""Converti un type en une chaîne de caractères"""
if isinstance(t, type):
return t.__name__
elif isinstance(t, list):
return "|".join([typeStr(_t) for _t in t])
elif isinstance(t, str):
return t
else:
return str(t)
def arm_comment(comment):
"""Permet d'afficher un commentaire dans le code arm."""
if comment != "":
printifm(f"@ {comment}")
else:
printifm("")
maxOps = 3
def arm_instruction(opcode, *ops, comment=""):
"""
Affiche une instruction ARM sur une ligne
Par convention, les derniers opérandes sont nuls si l'opération a moins de 3 arguments.
"""
printifm(f"\t{opcode}\t" + ",\t".join(ops) + "\t\t" * (maxOps - len(ops)) + "\t", end="")
arm_comment(comment)
def arm_nouvelle_etiquette():
"""
Retourne le nom d'une nouvelle étiquette
"""
global num_etiquette_courante
num_etiquette_courante += 1
return ".e" + str(num_etiquette_courante)
def gen_programme(programme: arbre_abstrait.Program):
"""
Affiche le code arm correspondant à tout un programme
"""
header = textwrap.dedent(
"""\
.global __aeabi_idiv
.global __aeabi_idivmod
.boolean_or:
mov r0, #1
bx lr
.boolean_not:
mov r0, #0
bx lr
.LC0:
.ascii "%d\\000"
.align 2
.LC1:
.ascii "%d\\012\\000"
.text
.align 2
.global main
"""
)
printifm(header)
if programme.listeFunctions:
for function in programme.listeFunctions.functions:
tableSymboles.add(function)
printift(tableSymboles)
arm_comment("functions")
for function in programme.listeFunctions.functions:
gen_def_fonction(function)
printifm("main:")
arm_instruction("push", "{fp,lr}")
arm_instruction("add", "fp", "sp", "#4")
gen_listeInstructions(programme.listeInstructions)
arm_instruction("mov", "r0", "#0")
arm_instruction("pop", "{fp, pc}")
def gen_def_fonction(f: arbre_abstrait.DeclarationFunction):
tableSymboles.enterFunction(f)
printifm(f"_{f.name}:")
arm_instruction("push", "{fp,lr}")
arm_instruction("add", "fp", "sp", "#4")
gen_listeInstructions(f.instructions, False)
tableSymboles.quitFunction()
def gen_listeInstructions(listeInstructions: arbre_abstrait.Instructions, deallocate=True):
"""Affiche le code arm correspondant à une suite d'instructions"""
tableSymboles.enterBlock(deallocate)
for instruction in listeInstructions.instructions:
gen_instruction(instruction)
removed = tableSymboles.quitBlock(deallocate)
if deallocate and len(removed) > 0:
arm_instruction("add", "sp", f"#{len(removed)*4}")
def gen_instruction(instruction):
"""Affiche le code arm correspondant à une instruction"""
if type(instruction) == arbre_abstrait.Function:
return gen_function(instruction)
elif type(instruction) in [arbre_abstrait.While, arbre_abstrait.If]:
gen_block_operation(instruction)
elif type(instruction) == arbre_abstrait.Return:
gen_return(instruction)
elif type(instruction) == arbre_abstrait.Declaration:
gen_def_variable(instruction)
elif type(instruction) == arbre_abstrait.Assignment:
gen_assign_variable(instruction)
else:
erreur("génération type instruction non implémenté " + typeStr(type(instruction)))
return None
def gen_function(instruction):
inProgram, inBuiltins = tableSymboles.has(instruction.fct)
if not inProgram and not inBuiltins:
erreur(f"Unknown function {instruction.fct}")
else:
args = instruction.args.listArgs if instruction.args else []
argsType = [gen_expression(arg) for arg in args]
tableSymboles.checkArgsType(instruction.fct, argsType)
if inProgram:
arm_instruction("bl", f"_{instruction.fct}")
memory = tableSymboles.memory(instruction.fct)
if memory > 0:
arm_instruction("add", "sp", f"#{memory}")
else:
if instruction.fct == "lire":
gen_lire()
elif instruction.fct == "ecrire":
gen_ecrire(instruction)
else:
erreur(f"Builtin function not found {instruction.fct}")
return tableSymboles.returnType(instruction.fct)
def gen_return(instruction):
function = tableSymboles.getFunction()
if not function:
erreur("Return keyword is only valid inside a function")
returnType = gen_expression(instruction.exp)
expectedType = tableSymboles.returnType(function)
if returnType != expectedType:
erreur(f"Incorrect return type expected {typeStr(expectedType)} got {typeStr(returnType)}")
arm_instruction("pop", "{r2}", comment="Return value")
removed = tableSymboles.symbolsToFree(2)
if len(removed) > 0:
arm_instruction("add", "sp", f"#{len(removed)*4}")
arm_instruction("pop", "{fp, pc}")
def gen_ecrire(ecrire):
"""Affiche le code arm correspondant au fait d'envoyer la valeur entière d'une expression sur la sortie standard"""
arm_instruction("pop", "{r1}") # on dépile la valeur d'expression sur r1
arm_instruction("ldr", "r0", "=.LC1")
arm_instruction("bl", "printf") # on envoie la valeur de r1 sur la sortie standard
def gen_lire():
"""
Affiche le code arm correspondant au fait de mettre en pause le programme, et permet à l’utilisateur d’entrée au clavier une chaîne
de caractère qui est interprétée comme un entier.
"""
arm_instruction("ldr", "r0", "=.LC0", comment="Charge l’adresse de la chaîne de format pour scanf dans r0")
arm_instruction("sub", "sp", "sp", "#4", comment="Réserve de l’espace sur la pile pour stocker l’entier lu (on fait sp = sp -4)")
arm_instruction("movs", "r1", "sp", comment="Copie l’adresse de cet espace dans r1")
arm_instruction("bl", "scanf", comment="Lance scanf pour lire l’entier et le stocker à l’adresse spécifiée par r1")
arm_instruction("pop", "{r2}", comment="Dépiler l'input dans r2")
def gen_block_operation(instruction: arbre_abstrait.While | arbre_abstrait.If):
endTrue = arm_nouvelle_etiquette()
ifFalse = arm_nouvelle_etiquette()
if type(instruction) == arbre_abstrait.While:
arm_instruction(f"{endTrue}:", comment="true condition jump")
conditionType = gen_expression(instruction.cond)
if conditionType != arbre_abstrait.Boolean:
erreur(f"Wrong condition type, expected {typeStr(arbre_abstrait.Boolean)}, got {typeStr(conditionType)}")
arm_instruction("pop", "{r0}")
arm_instruction("cmp", "r0", "#1")
arm_instruction("bNE", ifFalse, comment="condition is false")
gen_listeInstructions(instruction.instructions)
arm_instruction("b", endTrue)
arm_instruction(f"{ifFalse}:", comment="false condition jump")
if type(instruction) == arbre_abstrait.If:
if instruction.elseInstruction:
gen_listeInstructions(instruction.elseInstruction.instructions)
arm_instruction(f"{endTrue}:", comment="true condition jump")
def gen_def_variable(instruction: arbre_abstrait.Declaration):
tableSymboles.add(instruction)
declaredType = tableSymboles.returnType(instruction.name)
if instruction.value:
valType = gen_expression(instruction.value)
if valType != declaredType:
erreur(f"Type mismatch, declared {typeStr(declaredType)}, assigned {typeStr(valType)}")
else:
arm_instruction("mov", "r2", f"#{declaredType.DEFAULT_VALUE}", comment="Default value")
arm_instruction("push", "{r2}", comment=f"Assign newly declared variable {instruction.name}")
def gen_assign_variable(instruction: arbre_abstrait.Assignment):
inProgram, _ = tableSymboles.has(instruction.variable)
if not inProgram:
erreur(f"Unknown variable {instruction.variable}")
valueType = gen_expression(instruction.value)
expectedType = tableSymboles.returnType(instruction.variable)
if valueType != expectedType:
erreur(f"Invalid assignment, expected type {typeStr(expectedType)}, got {typeStr(valueType)}")
offset = tableSymboles.address(instruction.variable)
arm_instruction("pop", "{r2}")
arm_instruction("str", "r2", f"[fp, #{offset}]", comment=f"Assign {instruction.variable}")
return tableSymboles.returnType(instruction.variable)
def gen_expression(expression):
"""Affiche le code arm pour calculer et empiler la valeur d'une expression"""
if type(expression) == arbre_abstrait.Function:
instType = gen_function(expression)
arm_instruction("push", "{r2}")
return instType
elif type(expression) == arbre_abstrait.Operation:
return gen_operation(expression)
elif type(expression) == arbre_abstrait.Variable:
return gen_variable(expression)
elif type(expression) == arbre_abstrait.Integer:
arm_instruction("mov", "r1", "#" + str(expression.valeur))
arm_instruction("push", "{r1}")
elif type(expression) == arbre_abstrait.Boolean:
arm_instruction("mov", "r1", "#" + str(1 if expression.valeur else 0))
arm_instruction("push", "{r1}")
else:
erreur("type d'expression inconnu " + typeStr(type(expression)))
return type(expression)
def gen_variable(variable):
inProgram, _ = tableSymboles.has(variable.valeur)
if not inProgram:
erreur(f"Unknown variable {variable.valeur}")
offset = tableSymboles.address(variable.valeur)
arm_instruction("ldr", "r2", f"[fp, #{offset}]", comment=f"Retrieve {variable.valeur}")
arm_instruction("push", "{r2}", comment=f"Stack {variable.valeur}")
return tableSymboles.returnType(variable.valeur)
def gen_operation(operation):
"""Affiche le code arm pour calculer l'opération et la mettre en haut de la pile"""
op = operation.op
type1 = gen_expression(operation.exp1) # on calcule et empile la valeur de exp1
if operation.exp2:
type2 = gen_expression(operation.exp2) # on calcule et empile la valeur de exp2
if type1 != type2:
erreur(f"Types incompatibles {typeStr(type1)} != {typeStr(type2)}")
arm_instruction("pop", "{r1}", comment="dépile exp2 dans r1")
arm_instruction("pop", "{r0}", comment="dépile exp1 dans r0")
comparisons = {"==": "EQ", "!=": "NE", "<=": "LE", ">=": "GE", "<": "LT", ">": "GT"}
if type1 == arbre_abstrait.Integer and gen_operation_integer(op):
True # do nothing
elif type1 == arbre_abstrait.Boolean and gen_operation_boolean(op):
True # do nothing
elif op in comparisons.keys():
labelTrue = arm_nouvelle_etiquette()
labelFalse = arm_nouvelle_etiquette()
arm_instruction("cmp", "r0", "r1")
arm_instruction(f"b{comparisons[op]}", labelTrue)
arm_instruction("mov", "r0", "#0")
arm_instruction("b", labelFalse)
arm_instruction(f"{labelTrue}:")
arm_instruction("mov", "r0", "#1")
arm_instruction(f"{labelFalse}:")
type1 = arbre_abstrait.Boolean
else:
erreur(f'operateur "{op}" non implémenté pour le type {typeStr(type1)}')
arm_instruction("push", "{r0}", comment="empile le résultat")
return type1
def gen_operation_integer(op):
code = {
"+": "add",
"*": "mul",
} # Un dictionnaire qui associe à chaque opérateur sa fonction arm
# Voir: https://developer.arm.com/documentation/dui0497/a/the-cortex-m0-instruction-set/instruction-set-summary?lang=en
if op in code.keys():
arm_instruction(code[op], "r0", "r1", "r0", comment=f"effectue l'opération r0 {op} r1 et met le résultat dans r0")
elif op == "-":
arm_instruction("sub", "r0", "r0", "r1", comment=f"effectue l'opération r0 {op} r1 et met le résultat dans r0")
elif op == "/":
arm_instruction("bl", "__aeabi_idiv")
elif op == "%":
arm_instruction("bl", "__aeabi_idivmod")
arm_instruction("mov", "r0", "r1")
else:
return False
return True
def gen_operation_boolean(op):
if op == "et":
arm_instruction("mul", "r0", "r1", "r0", comment=f"effectue l'opération r0 {op} r1 et met le résultat dans r0")
elif op == "ou":
arm_instruction("add", "r0", "r1", "r0")
arm_instruction("cmp", "r0", "#2")
arm_instruction("blEQ", ".boolean_or")
elif op == "non":
arm_instruction("add", "r0", "#1")
arm_instruction("cmp", "r0", "#2")
arm_instruction("blEQ", ".boolean_not")
else:
return False
return True
if __name__ == "__main__":
argParser = argparse.ArgumentParser(description="Generate assembly code from flo script", formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=50))
argParser.add_argument("filename")
argParser.add_argument("-o", "--output", action="store", help="Destination file")
argParser.add_argument("-arm", "--arm", action="store_true", help="Generate ARM assembly")
argParser.add_argument("-t", "-table", "--table", action="store_true", help="Display the symbol table")
argParser.add_argument("--builtins", action="store_true", help="Display builtins in the symbol tables")
args = argParser.parse_args()
afficher_table = args.table
print_builtins = args.builtins
lexer = FloLexer()
parser = FloParser()
with open(args.filename, "r") as f:
if args.arm:
output = open(args.output, "w") if args.output else sys.stdout
data = f.read()
try:
arbre = parser.parse(lexer.tokenize(data))
gen_programme(arbre)
except EOFError:
exit(1)
finally:
if output:
output.close()