This repository has been archived by the owner on Sep 22, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmyASTExt.py
1698 lines (1526 loc) · 66.3 KB
/
myASTExt.py
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
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# -----------------------------------------------------------------------------
# myAST.py
#
# File used for the syntax and semantic analysis as well as code generation
# Made by Simon Bernard and Ivan Klapka for the Project 1 : lexical analysis
# University of Liège - Academic year 2019-2020 - INFO0085-1 Compilers course
# -----------------------------------------------------------------------------
from llvmlite import ir
##### Classes for AST
# General class node
class Node:
def __init__(self):
self.typeChecked = ""
self.line = -1
self.col = -1
def add_position(self, line, col):
self.line = line
self.col = col
def add_position_from_node(self, cl):
self.line = cl.line
self.col = cl.col
# Program
class Program(Node):
def __init__(self):
Node.__init__(self)
self.list_class = []
self.list_ext = []
# Print the list of class
def __str__(self):
if len(self.list_ext) != 0:
return get_list_string(self.list_ext) + get_list_string(self.list_class)
return get_list_string(self.list_class)
# Add a class to the program
def add_class(self, c):
self.list_class.append(c)
# Add an external method to the program
def add_external(self, ext):
self.list_ext.append(ext)
# Check the type of expression in the tree
def checkTypeTree(self, gst, file_name, error_buffer):
# For every class
for cl in self.list_class:
# Check type tree
cl.checkTypeTree(gst, file_name, error_buffer)
# Generate code for the program
def codeGen(self, lgen):
# For every class
for cl in self.list_class:
# Generate the code
cl.codeGen(lgen)
# Class
class Class(Node):
def __init__(self):
Node.__init__(self)
self.name = ""
self.nameLine = -1
self.nameCol = -1
self.parent = "Object"
self.parentLine = -1
self.parentCol = -1
self.fields = []
self.methods = []
# Print the class
def __str__(self):
str = "Class(" + self.name + ", " + self.parent + ", "
str += get_list_string(self.fields) + ", "
str += get_list_string(self.methods)
str += ")"
return str
# Name the class
def change_name(self, name):
self.name = name
# Parent of the class (default = Object)
def change_parent(self, parent):
self.parent = parent
# Add a field
def add_field(self, field):
self.fields.append(field)
# Add a method
def add_method(self, method):
self.methods.append(method)
# Add position for name
def add_position_name(self, line, col):
self.nameLine = line
self.nameCol = col
# Add position for parent
def add_position_parent(self, line, col):
self.parentLine = line
self.parentCol = col
# Check the type of expression in the tree
def checkTypeTree(self, gst, file_name, error_buffer):
# For every field
for fl in self.fields:
fl.checkTypeField(gst, file_name, error_buffer)
# Create the self Type
selfType = Type(self.name)
selfType.add_position(self.nameLine, self.nameCol)
# For every method
for mt in self.methods:
mt.checkTypeMethod(gst, selfType, file_name, error_buffer)
# Generate code for the class
def codeGen(self, lgen):
# Get the class init info
clInitDictInfo = lgen.initDict.get(self.name)
## Create new
# Get the function new info
infoMetNew = clInitDictInfo[4]
# Get the function new declaration and create the first block
blockNew = infoMetNew.append_basic_block()
# Create the new builder on that block
bldrNew = ir.IRBuilder(blockNew)
# Get the size of the struct
c = ir.Constant(clInitDictInfo[0], None)
size_as_ptr = bldrNew.gep(c, [lgen.int32(1)], inbounds=False, name="size_as_ptr")
size_as_i64 = bldrNew.ptrtoint(size_as_ptr, lgen.int64, name="size_as_i64")
# Get the info of malloc
malloc_f = lgen.initDict.get("_malloc")
# Call malloc
ans_malloc = bldrNew.call(malloc_f, [size_as_i64])
# Bitcast malloc
allptr = bldrNew.bitcast(ans_malloc, clInitDictInfo[0])
# Call init
ans_int = bldrNew.call(clInitDictInfo[5], [allptr])
# Return the object of type class
bldrNew.ret(ans_int)
## Create init
# Get the function init info
infoMetInit = clInitDictInfo[5]
# Get the function init declaration and create the first block
blockInit = infoMetInit.append_basic_block()
# Create the init builder on that block
bldrInit = ir.IRBuilder(blockInit)
# Get the ptr to object
ptr_obj = infoMetInit.args[0]
# Check that the pointer is not null
pred = bldrInit.icmp_unsigned("!=", ptr_obj, c)
# If not null
with bldrInit.if_then(pred) as then:
# Get the parent class init info
parInitDictInfo = lgen.initDict.get(self.parent)
# Cast the class into his parent
ptr_obj_par = bldrInit.bitcast(ptr_obj, parInitDictInfo[0])
# Call the init of the parent
bldrInit.call(parInitDictInfo[5], [ptr_obj_par])
# Get the pointer to where the ptr vtable will be stored
ptr_vtable = bldrInit.gep(ptr_obj, [lgen.int32(0), lgen.int32(0)], inbounds=True)
# Store the pointer to the const vtable inside the object
bldrInit.store(clInitDictInfo[6], ptr_vtable)
# Initialize the fields
for fl in self.fields:
# Generate the value of the field
value_f = fl.codeGen(lgen, self.name, bldrInit)
# Get the field position on the table
pos_f = clInitDictInfo[2][fl.name][0]
# If void skip
if clInitDictInfo[2][fl.name][1] == lgen.void:
continue
# Get the pointer to the field
ptr_f = bldrInit.gep(ptr_obj, [lgen.int32(0), lgen.int32(pos_f)])
# Cast if necessary
cast_f = bldrInit.bitcast(value_f, lgen.initDict.get(fl.type.type)[0])
# Store the value in the field
bldrInit.store(cast_f, ptr_f)
# Return the pointer to the obj
bldrInit.ret(ptr_obj)
# For every method, generate the code
for mt in self.methods:
mt.codeGen(lgen, self.name)
# Field
class Field(Node):
def __init__(self, name, type):
Node.__init__(self)
self.name = name
self.type = type
self.init_expr = ""
def __str__(self):
if(self.init_expr==""):
str = get_object_string("Field", [self.name, self.type])
else:
str = get_object_string("Field", [self.name, self.type, self.init_expr])
return str
# Add init expr
def add_init_expr(self, init_expr):
self.init_expr = init_expr
# Check the type of initializer expression
def checkTypeField(self, gst, file_name, error_buffer):
if self.init_expr != "":
# Create a symbol table
st = symbolTable()
# Check the expression
typeExpr = self.init_expr.checkExpr(gst, st, file_name, error_buffer)
# Check that the initializer type corresponds to the field type
if not gst.areConform(typeExpr, self.type.type):
error_message_ast(self.type.line, self.type.col, "the type of the initializer (" + typeExpr + ") must conform to the declared type of the field (" + self.type.type + ")", file_name, error_buffer)
# Generate code for the field
def codeGen(self, lgen, className, bldr):
# Get the llvmlite type of the field
fieldType = lgen.initDict[className][2][self.name][1]
# If there is not initial expr, set to null
if self.init_expr == "":
# For string declare an empty string
if self.type.type == "string":
# Create a global constant
string1 = "\0"
c_string1 = ir.Constant(ir.ArrayType(ir.IntType(8), len(string1)), bytearray(string1.encode("utf-8")))
global_string1 = ir.GlobalVariable(lgen.module, c_string1.type, name=("str" + str(lgen.nbrStr)))
lgen.nbrStr = lgen.nbrStr + 1
global_string1.linkage = ''
global_string1.global_constant = True
global_string1.initializer = c_string1
# Return a pointer to the global constant
pt = bldr.gep(global_string1, [lgen.int32(0), lgen.int32(0)], inbounds=True)
return pt
# In the case of unit, return void
elif self.type.type == "unit":
return lgen.void
else:
# Return null
return ir.Constant(fieldType, None)
else:
# Create empty symbol table
st = symbolTable()
value = self.init_expr.codeGenExpr(lgen, className, bldr, st)
return value
class Method(Node):
def __init__(self, name, formals, type, block):
Node.__init__(self)
self.name = name
self.formals = formals
self.type = type
self.block = block
def __str__(self):
str = get_object_string("Method", [self.name, self.formals, self.type, self.block])
return str
# Check the type of the block
def checkTypeMethod(self, gst, classNameType, file_name, error_buffer):
# Check that the declared type is valid
if not isPrimitive(self.type.type):
# Check that class type is valid
classInfo = gst.lookupForClass(self.type.type)
if classInfo is None:
error_message_ast(self.type.line, self.type.col, "unknown type " + self.type.type, file_name, error_buffer)
self.type.type = "Object" # Error recovery
# Create a symbol table
st = symbolTable()
# Add self
st.bind("self", classNameType)
# Get class info
classInfo = gst.lookupForClass(classNameType.type)
## Add the fields (also ancestor fields)
# For every ancestor (including itself)
for clName in classInfo[3]:
# Get the ancestor info
anceClassInfo = gst.lookupForClass(clName)
# Add the fields
for fl in anceClassInfo[0].fields:
st.bind(fl.name, fl.type)
## Add the formals
methodInfo = classInfo[2].get(self.name)
st.update(methodInfo[1])
# Check the type of the block
typeBlock = self.block.checkExpr(gst, st, file_name, error_buffer)
# Check that it conform the declared type
if not gst.areConform(typeBlock, self.type.type):
error_message_ast(self.type.line, self.type.col, "the type of the method's body (" + typeBlock + ") must conform to the declared type of the method (" + self.type.type + ")", file_name, error_buffer)
# Generate code for the method
def codeGen(self, lgen, className):
# Get the class init info
clInitDictInfo = lgen.initDict.get(className)
# Get the method info
metInitDictInfo = clInitDictInfo[3][self.name]
# Get the method declaration and create the first block
block = metInitDictInfo[2].append_basic_block()
# Create the builder on that block
bldr = ir.IRBuilder(block)
# Main exception
if className == "Main" and self.name == "main":
# Create a symbol table for self
st = symbolTable()
# Add the arguments
args = metInitDictInfo[2].args
# Allocate space for self
ptr = bldr.alloca(args[0].type)
# Get the info of the class
funcNew = lgen.initDict["Main"][4]
# Call the new method of the object an return the value
mainObj = bldr.call(funcNew, ())
# Store the value of self
bldr.store(mainObj, ptr)
# Bind the pointer to self to the symbol table
st.bind("self", ptr)
else :
# Create a symbol table for the arguments (and self)
st = symbolTable()
# Add the arguments
args = metInitDictInfo[2].args
# Allocate space for self
ptr = bldr.alloca(args[0].type)
# Store the value of self
bldr.store(args[0], ptr)
# Bind the pointer to self to the symbol table
st.bind("self", ptr)
i = 1 # Start at 1 to skip self
for fm in self.formals.list_formals:
# Special case for unit
if fm.type.type == "unit":
continue
# Allocate space for the arg
ptr = bldr.alloca(args[i].type)
# Store the value of the arg
bldr.store(args[i], ptr)
# Bind the pointer to the symbol table
st.bind(fm.name, ptr)
i = i + 1
# Launch the builder on the block
value = self.block.codeGenExpr(lgen, className, bldr, st)
# If a void comes, return void
if value == lgen.void:
bldr.ret_void()
else:
# Return the value
bldr.ret(value)
class Type(Node):
def __init__(self, type):
Node.__init__(self)
self.type = type
def __str__(self):
return self.type.__str__()
class Formals(Node):
def __init__(self):
Node.__init__(self)
self.list_formals = []
def __str__(self):
str = get_list_string(self.list_formals)
return str
def add_formal(self, formal):
self.list_formals.append(formal)
class Formal(Node):
def __init__(self, name, type):
Node.__init__(self)
self.name = name
self.type = type
def __str__(self):
str = self.name + " : " + self.type.__str__()
return str
## Expressions
class Expr(Node):
pass
class Block(Expr):
def __init__(self):
Node.__init__(self)
self.list_expr = []
def __str__(self):
if(len(self.list_expr)==1):
str = self.list_expr[0].__str__()
else:
str = get_list_string(self.list_expr)
# In case the type was checked print it
if self.typeChecked != "":
str += " : " + self.typeChecked
return str
def add_expr(self, expr):
self.list_expr.append(expr)
# Check the expressions of the block
def checkExpr(self, gst, st, file_name, error_buffer):
# Open a new context
st.enter_ctx()
# Check all expression
for exp in self.list_expr:
typeExpr = exp.checkExpr(gst, st, file_name, error_buffer)
# The type of the last expression is the expression of the block
self.typeChecked = typeExpr
# Exit the context
st.exit_ctx()
return self.typeChecked
# Generate code for a block
def codeGenExpr(self, lgen, className, bldr, st):
# Open a new context
st.enter_ctx()
# Generate the code for all the expr
for exp in self.list_expr:
value = exp.codeGenExpr(lgen, className, bldr, st)
# Exit the context
st.exit_ctx()
# Return the value of the last expr
return value
class Expr_if(Expr):
def __init__(self, cond_expr, then_expr):
Node.__init__(self)
self.cond_expr = cond_expr
self.then_expr = then_expr
self.else_expr = ""
def __str__(self):
if(self.else_expr==""):
str = get_object_string("If", [self.cond_expr, self.then_expr])
else:
str = get_object_string("If", [self.cond_expr, self.then_expr, self.else_expr])
# In case the type was checked print it
if self.typeChecked != "":
str += " : " + self.typeChecked
return str
def add_else_expr(self, expr):
self.else_expr = expr
# Check the expressions of the conditional
def checkExpr(self, gst, st, file_name, error_buffer):
# Check expr of if and then
typeCondExpr = self.cond_expr.checkExpr(gst, st, file_name, error_buffer)
typeThenExpr = self.then_expr.checkExpr(gst, st, file_name, error_buffer)
# Cond expr must be of type bool
if typeCondExpr != "bool":
error_message_ast(self.cond_expr.line, self.cond_expr.col, "IF conditional expression must be of type bool", file_name, error_buffer)
# If then
if self.else_expr == "" :
self.typeChecked = "unit"
return self.typeChecked
# If then else
else:
# Check type for then expr
typeElseExpr = self.else_expr.checkExpr(gst, st, file_name, error_buffer)
# If one expr has unit type, if has unit type
if (typeThenExpr == "unit") or (typeElseExpr == "unit"):
self.typeChecked = "unit"
return self.typeChecked
# Types are both primitives
elif isPrimitive(typeThenExpr) and isPrimitive(typeElseExpr):
# Check that both types are equal
if typeThenExpr == typeElseExpr:
self.typeChecked = typeThenExpr
return self.typeChecked
# They are both of class type
elif not (isPrimitive(typeThenExpr) or isPrimitive(typeElseExpr)):
# Return the first common ancestor
self.typeChecked = gst.commonAcenstor(typeThenExpr, typeElseExpr)
return self.typeChecked
# Error recovery
error_message_ast(self.line, self.col, "IF conditional expression, the type of the expression inside THEN (" + typeThenExpr + ") does not agree with the type of the expression inside ELSE (" + typeElseExpr + ")", file_name, error_buffer)
return "unit"
# Generate code for a if
def codeGenExpr(self, lgen, className, bldr, st):
# Get the value of predicate
pred = self.cond_expr.codeGenExpr(lgen, className, bldr, st)
# If then
if(self.else_expr==""):
with bldr.if_then(pred) as then:
value = self.then_expr.codeGenExpr(lgen, className, bldr, st)
return lgen.void
# In the case of a unit
elif self.typeChecked == "unit":
# Launch if else
with bldr.if_else(pred) as (then, otherwise):
with then:
value_then = self.then_expr.codeGenExpr(lgen, className, bldr, st)
with otherwise:
value_otherwise = self.else_expr.codeGenExpr(lgen, className, bldr, st)
return lgen.void
# if then else
else:
# Get the type
typeIf = lgen.initDict[self.typeChecked][0]
# Allocate memory for return type
ptrIf = bldr.alloca(typeIf)
# Launch if else
with bldr.if_else(pred) as (then, otherwise):
with then:
value_then = self.then_expr.codeGenExpr(lgen, className, bldr, st)
# Cast the value to the good type
vcast_then = bldr.bitcast(value_then, typeIf)
# Store the value
bldr.store(vcast_then, ptrIf)
with otherwise:
value_otherwise = self.else_expr.codeGenExpr(lgen, className, bldr, st)
# Cast the value to the good type
vcast_otherwise = bldr.bitcast(value_otherwise, typeIf)
# Store the value
bldr.store(vcast_otherwise, ptrIf)
return bldr.load(ptrIf)
class Expr_while(Expr):
def __init__(self, cond_expr, body_expr):
Node.__init__(self)
self.cond_expr = cond_expr
self.body_expr = body_expr
self.isdowhile = False
def __str__(self):
str = get_object_string("While", [self.cond_expr, self.body_expr])
# In case the type was checked print it
if self.typeChecked != "":
str += " : " + self.typeChecked
return str
def set_isdowhile(self):
self.isdowhile = True
# Check the expressions of while
def checkExpr(self, gst, st, file_name, error_buffer):
# Check type of expr
typeCondExpr = self.cond_expr.checkExpr(gst, st, file_name, error_buffer)
typeBodyExpr = self.body_expr.checkExpr(gst, st, file_name, error_buffer)
# Check that cond expr is type bool
if typeCondExpr != "bool":
error_message_ast(self.cond_expr.line, self.cond_expr.col, "while conditional expression must be of type bool", file_name, error_buffer)
self.typeChecked = "unit"
return self.typeChecked
# Generate code for a while
def codeGenExpr(self, lgen, className, bldr, st):
# Create the basic blocks
bb_cond = bldr.append_basic_block("cond")
bb_loop = bldr.append_basic_block("loop")
bb_after_loop = bldr.append_basic_block("after_loop")
# Check for do while
if self.isdowhile:
# Enter the loop
bldr.branch(bb_loop)
else:
# Enter the cnd
bldr.branch(bb_cond)
# Build the cond
bldr.position_at_end(bb_cond)
cond = self.cond_expr.codeGenExpr(lgen, className, bldr, st)
bldr.cbranch(cond, bb_loop, bb_after_loop)
# Build the loop
bldr.position_at_end(bb_loop)
loop = self.body_expr.codeGenExpr(lgen, className, bldr, st)
bldr.branch(bb_cond)
# Return to the after loop
bldr.position_at_end(bb_after_loop)
return lgen.void
class Expr_let(Expr):
def __init__(self, name, type, scope_expr):
Node.__init__(self)
self.name = name
self.type = type
self.init_expr = ""
self.scope_expr = scope_expr
def __str__(self):
if(self.init_expr==""):
str = get_object_string("Let", [self.name, self.type, self.scope_expr])
else:
str = get_object_string("Let", [self.name, self.type, self.init_expr, self.scope_expr])
# In case the type was checked print it
if self.typeChecked != "":
str += " : " + self.typeChecked
return str
# Add init expr
def add_init_expr(self, init_expr):
self.init_expr = init_expr
# Check the expressions of let
def checkExpr(self, gst, st, file_name, error_buffer):
# Check that the type is valid
if not isPrimitive(self.type.type):
# Check that class type is valid
classInfo = gst.lookupForClass(self.type.type)
if classInfo is None:
error_message_ast(self.type.line, self.type.col, "unknown type " + self.type.type, file_name, error_buffer)
self.type.type = "Object" # Error recovery
# if let assign in
if self.init_expr != "":
# Look at the type of the initializer
typeInitExpr = self.init_expr.checkExpr(gst, st, file_name, error_buffer)
# Check that it conforms the type
if not gst.areConform(typeInitExpr, self.type.type):
error_message_ast(self.init_expr.line, self.init_expr.col, "the type of the initializer (" + typeInitExpr + ") must conform to the declared type of the let (" + self.type.type + ")", file_name, error_buffer)
# Check that self is not the name of the identifier
if self.name == "self":
error_message_ast(self.line, self.col, "bound identifier cannot be named self", file_name, error_buffer)
# Error recovery
# Create a new context
st.enter_ctx()
# Bind the identifier and type
st.bind(self.name, self.type)
# Check the type of the scope
self.typeChecked = self.scope_expr.checkExpr(gst, st, file_name, error_buffer)
# Exit the context
st.exit_ctx()
return self.typeChecked
# Generate code for let
def codeGenExpr(self, lgen, className, bldr, st):
# Get the type of the id
typeId = lgen.initDict[self.type.type][0]
# For unit return a void
if self.type.type == "unit":
# If initialised execute the code
if self.init_expr != "":
# Get the value
self.init_expr.codeGenExpr(lgen, className, bldr, st)
# Generate the body of the let
return self.scope_expr.codeGenExpr(lgen, className, bldr, st)
# Allocate space for the arg
ptr = bldr.alloca(typeId)
# If it is initialized
if self.init_expr != "":
# Get the value
v = self.init_expr.codeGenExpr(lgen, className, bldr, st)
# Cast the value in the allocate pointer type
vcast = bldr.bitcast(v, typeId)
# Store the value of the arg
bldr.store(vcast, ptr)
else:
# For string declare an empty string
if self.type.type == "string":
# Create a global constant
string1 = "\0"
c_string1 = ir.Constant(ir.ArrayType(ir.IntType(8), len(string1)), bytearray(string1.encode("utf-8")))
global_string1 = ir.GlobalVariable(lgen.module, c_string1.type, name=("str" + str(lgen.nbrStr)))
lgen.nbrStr = lgen.nbrStr + 1
global_string1.linkage = ''
global_string1.global_constant = True
global_string1.initializer = c_string1
# Return a pointer to the global constant
pt = bldr.gep(global_string1, [lgen.int32(0), lgen.int32(0)], inbounds=True)
bldr.store(pt, ptr)
else:
# Create a null constant
v = ir.Constant(typeId, None)
# Store it
bldr.store(v, ptr)
# Enter new context
st.enter_ctx()
# Bind the pointer to the symbol table
st.bind(self.name, ptr)
# Get the value of the block
valueBlock = self.scope_expr.codeGenExpr(lgen, className, bldr, st)
# Exit the context
st.exit_ctx()
# Return the value of the block
return valueBlock
class Expr_assign(Expr):
def __init__(self, name, expr):
Node.__init__(self)
self.name = name
self.expr = expr
def __str__(self):
str = get_object_string("Assign", [self.name, self.expr])
# In case the type was checked print it
if self.typeChecked != "":
str += " : " + self.typeChecked
return str
# Check expression of assign and that it does not assign to self
def checkExpr(self, gst, st, file_name, error_buffer):
# Check it does not assign to self
if self.name == "self":
error_message_ast(self.line, self.col, "cannot assign to self", file_name, error_buffer)
# Get the type of identifier
infoId = st.lookup(self.name)
# Check it exist
if infoId is None:
error_message_ast(self.line, self.col, "unknown variable " + self.name, file_name, error_buffer)
return "Object" # Error recovery
# Check the type of the expr
typeExpr = self.expr.checkExpr(gst, st, file_name, error_buffer)
# Check that types are conform
if not gst.areConform(typeExpr, infoId.type):
error_message_ast(self.line, self.col, "cannot assign identifier " + self.name + " with declared type (" + infoId.type + ") as assigned expression type (" + typeExpr + ") does not conform", file_name, error_buffer)
return infoId.type # Error recovery
self.typeChecked = infoId.type
return self.typeChecked
# Generate code for an assign
def codeGenExpr(self, lgen, className, bldr, st):
# Get the type of the id
typeId = lgen.initDict[self.typeChecked][0]
# Get the value of the expr
value = self.expr.codeGenExpr(lgen, className, bldr, st)
# Get the ptr to the identifier
ptrId = st.lookup(self.name)
# Check it exist
if ptrId is not None:
# Cast the value in the allocate pointer type
vcast = bldr.bitcast(value, typeId)
# Store the new value in the identifier
bldr.store(vcast, ptrId)
# If it does not exist, it is a field
else:
# If unit, skip it
if self.typeChecked == "unit":
return lgen.void
# Get the ptr to ptr to self
ptrptrSelf = st.lookup("self")
# Load it
ptrSelf = bldr.load(ptrptrSelf)
# Get the field info
llvmInfoField = lgen.initDict[className][2][self.name]
nbrField = llvmInfoField[0]
# Get the pointer to the field
ptrField = bldr.gep(ptrSelf, [lgen.int32(0), lgen.int32(nbrField)], inbounds=True)
# Cast the value in the allocate pointer type
vcast = bldr.bitcast(value, llvmInfoField[1])
# Store the value in the field
bldr.store(vcast, ptrField)
return value
class Expr_UnOp(Expr):
def __init__(self, unop, expr):
Node.__init__(self)
self.unop = unop
self.expr = expr
self.expr_is_left = False
def __str__(self):
str = get_object_string("UnOp", [self.unop, self.expr])
# In case the type was checked print it
if self.typeChecked != "":
str += " : " + self.typeChecked
return str
def set_expr_is_left(self):
self.expr_is_left = True
# Check expression of unary operator
def checkExpr(self, gst, st, file_name, error_buffer):
# Check type of expr
typeExpr = self.expr.checkExpr(gst, st, file_name, error_buffer)
# if unop is "not" check for bool
if self.unop == "not":
if typeExpr != "bool":
error_message_ast(self.line, self.col, "unary operator NOT, expression must be of type bool", file_name, error_buffer)
return "bool" # Error recovery : bool
self.typeChecked = "bool"
# if unop is minus check for int32
elif self.unop == "-":
if typeExpr != "int32":
error_message_ast(self.line, self.col, "unary MINUS operator, expression must be of type int32", file_name, error_buffer)
return "int32" # Error recovery : int32
self.typeChecked = "int32"
# if unop is minusminus check for int32
elif self.unop == "--":
if typeExpr != "int32":
error_message_ast(self.line, self.col, "unary -- operator, expression must be of type int32", file_name, error_buffer)
return "int32" # Error recovery : int32
# Check that the expression is an object identifier (variable)
if not isinstance(self.expr, Expr_Object_identifier):
error_message_ast(self.line, self.col, "unary -- operator, expression must be a variable", file_name, error_buffer)
return "int32" # Error recovery : int32
self.typeChecked = "int32"
# if unop is plusplus check for int32
elif self.unop == "++":
if typeExpr != "int32":
error_message_ast(self.line, self.col, "unary ++ operator, expression must be of type int32", file_name, error_buffer)
return "int32" # Error recovery : int32
# Check that the expression is an object identifier (variable)
if not isinstance(self.expr, Expr_Object_identifier):
error_message_ast(self.line, self.col, "unary ++ operator, expression must be a variable", file_name, error_buffer)
return "int32" # Error recovery : int32
self.typeChecked = "int32"
# if unop is isnull check for class type
else:
if isPrimitive(typeExpr):
error_message_ast(self.line, self.col, "unary ISNULL operator, expression must be of type Object", file_name, error_buffer)
return "bool" # Error recovery : bool
self.typeChecked = "bool"
return self.typeChecked
# Generate code for unary operator
def codeGenExpr(self, lgen, className, bldr, st):
# Get the value of the expr
value = self.expr.codeGenExpr(lgen, className, bldr, st)
# if unop is "not"
if self.unop == "not":
return bldr.not_(value)
# if unop is minus
elif self.unop == "-":
return bldr.neg(value)
# if unop is minusminus
elif self.unop == "--":
# Get the ptr to the expr
ptr = st.lookup(self.expr.name)
# Do the minus 1
newVal = bldr.sub(value, lgen.int32(1))
# Store the value
bldr.store(newVal, ptr)
# Check if the value is to the left aka i--
if self.expr_is_left:
# Return the value before modification
return value
# The value is on the right aka --i
else:
# Return the value after modification
return newVal
# if unop is plusplus
elif self.unop == "++":
# Get the ptr to the expr
ptr = st.lookup(self.expr.name)
# Do the plus 1
newVal = bldr.add(value, lgen.int32(1))
# Store the value
bldr.store(newVal, ptr)
# Check if the value is to the left aka i++
if self.expr_is_left:
# Return the value before modification
return value
# The value is on the right aka ++i
else:
# Return the value after modification
return newVal
# if unop is isnull
else:
# Get the type
typeInfo = lgen.initDict[self.expr.typeChecked]
# Generate null
null = ir.Constant(typeInfo[0], None)
# Check that the pointer is null
return bldr.icmp_unsigned("==", value, null)
class Expr_BinOp(Expr):
def __init__(self, op, left_expr, right_expr):
Node.__init__(self)
self.op = op
self.left_expr = left_expr
self.right_expr = right_expr
def __str__(self):
str = get_object_string("BinOp", [self.op, self.left_expr, self.right_expr])
# In case the type was checked print it
if self.typeChecked != "":
str += " : " + self.typeChecked
return str
# Check expression of binary operator
def checkExpr(self, gst, st, file_name, error_buffer):
# Check type of both expr
typeLeftExpr = self.left_expr.checkExpr(gst, st, file_name, error_buffer)
typeRightExpr = self.right_expr.checkExpr(gst, st, file_name, error_buffer)
# And
if self.op == "and":
# Check that both type are bool
if (typeLeftExpr != "bool") or (typeRightExpr != "bool"):
error_message_ast(self.line, self.col, "binary operator AND, expressions must be of type bool", file_name, error_buffer)
return "bool"
else:
self.typeChecked = "bool"
return self.typeChecked
# Or
elif self.op == "or":
# Check that both type are bool
if (typeLeftExpr != "bool") or (typeRightExpr != "bool"):
error_message_ast(self.line, self.col, "binary operator OR, expressions must be of type bool", file_name, error_buffer)
return "bool"
else:
self.typeChecked = "bool"
return self.typeChecked
# Xor
elif self.op == "xor":
# Check that both type are bool
if (typeLeftExpr != "bool") or (typeRightExpr != "bool"):
error_message_ast(self.line, self.col, "binary operator XOR, expressions must be of type bool", file_name, error_buffer)
return "bool"
else:
self.typeChecked = "bool"
return self.typeChecked
# Modulo
elif self.op == "%":
# Check that both type are int32
if (typeLeftExpr != "int32") or (typeRightExpr != "int32"):
error_message_ast(self.line, self.col, "binary operator modulo, expressions must be of type int32", file_name, error_buffer)
return "int32"
else:
self.typeChecked = "int32"
return self.typeChecked
# Equal
elif self.op == "=":
# Types are both primitives
if isPrimitive(typeLeftExpr) and isPrimitive(typeRightExpr):
# Check that both types are equal
if typeLeftExpr != typeRightExpr:
error_message_ast(self.line, self.col, "binary operator =, for primitive types, types of expression must be identical", file_name, error_buffer)
return "bool"
else:
self.typeChecked = "bool"
return self.typeChecked
# They are both of class type
elif not (isPrimitive(typeLeftExpr) or isPrimitive(typeRightExpr)):
self.typeChecked = "bool"
return self.typeChecked
else:
error_message_ast(self.line, self.col, "binary operator =, types of expression must be identical", file_name, error_buffer)
return "bool"
# Lower
elif self.op == "<":
# Check that both type are int32
if (typeLeftExpr != "int32") or (typeRightExpr != "int32"):
error_message_ast(self.line, self.col, "binary operator <, expressions must be of type int32", file_name, error_buffer)
return "bool"
else:
self.typeChecked = "bool"
return self.typeChecked
# Larger
elif self.op == ">":
# Check that both type are int32
if (typeLeftExpr != "int32") or (typeRightExpr != "int32"):
error_message_ast(self.line, self.col, "binary operator >, expressions must be of type int32", file_name, error_buffer)
return "bool"
else:
self.typeChecked = "bool"
return self.typeChecked
# Lower equal
elif self.op == "<=":
# Check that both type are int32
if (typeLeftExpr != "int32") or (typeRightExpr != "int32"):
error_message_ast(self.line, self.col, "binary operator <=, expressions must be of type int32", file_name, error_buffer)
return "bool"
else:
self.typeChecked = "bool"
return self.typeChecked
# Larger equal
elif self.op == ">=":
# Check that both type are int32
if (typeLeftExpr != "int32") or (typeRightExpr != "int32"):
error_message_ast(self.line, self.col, "binary operator >=, expressions must be of type int32", file_name, error_buffer)