SEMANTIC ANALYSIS ~~~~~~~~~~~~~~~~~ Basic Structure ~~~~~~~~~~~~~~~ - Semantic analysis is done with the help of two visitors. The first one (SymbolTableVisitor) fills the SymbolTable structure and the seconds one performs type checking (TypeCheckerVisitor). Both of them are located in package 'myvisitors'. - class Scope represents the scope of a method. That is, a map of <IDENTIFIERS,TYPES>. - class ClassScope represents the scope of a class and extends Scope. It contains information about the method signatures and the offsets that will be printed if the input is correct. - The symbolTable class consists of three Maps and two Sets: -> One Map for mapping className to ClassScope. -> Another Map for mapping methods to Scope . That is <method_primary_key,Scope> where method_primary_key = <methodName,className>. -> A third Map for mapping Scopes to ClassScopes (Inheritance Map). All methods have a mapping to their class (i.e. <Scope,ClassScope>) and if one class 'extends' another one then we have a mapping of <DerivedClass,BaseClass>. -> The first Set (knownTypes) contains all types that have been declared (user-defined) plus the primitives and the array type. -> The seconds Set (undeclaredTypes) contains all types that the parser have seen but are not yet declared. When SymbolTableVisitor parses the whole source file, we check if the second Set is a subset of the first Set.If yes, we continue parsing.Otherwise, there is at least one undeclared type and we stop the parsing. This is all the information needed by the TypeCheckerVisitor to perform the typechecking. Extra Features ~~~~~~~~~~~~~~ 1) The parsers doesn't stop the parsing on the first error it sees. More specifically, if the Parser finds an error during symbol table creation then it tries to find as much as possible errors until the symbol table creation finishes (i.e until SymbolTableVisitor finishes traversing the source). If the parser finds an error at typechecking, it tries to find as much as possible errors until the end of parsing. 2) As a result of (1), my parser has some decisions to make when it recognizes some particular errors in order to resume parsing: i) When we have 'class A extends B { ... }' and B is not declared, then my parser ignores the 'extends B' part pretending that class A is not a subtype of class B. Of course, an error is generated by the parser. ii) When parser encounters method overloading and the two methods are declared in the same class, it ignores the method that was declared second. iii) When a method 'foo' in class Derived overloads another 'foo' from the Base class and the base class overloads another 'foo' from superBase class then we perform overloading checking with the 'foo' of closest parent of class Derived, that is 'foo' of Base. I will provide an example to be more clear.Consider this scenario: class A { int foo(int a) {} } class B extends A { int foo(boolean a) {} } class C extends B { int foo(boolean a) {} } Initialy, the parser will generate error because we have function overloading in class B (foo method). When the parser reaches foo in class C it should check whether the methods of this class are overloading other methods of the base class. Will the parser compare the 'foo' of C with the 'foo' of B or the 'foo' of A to check for overloading? My parser selects the 'foo' of the closest parent of class C, that is 'foo' of class B. iv) In typecking, consider this statement: counter = 0; If counter is not declared, then my parser will generate two errors. The first one will be "undeclared identifier 'counter'" and the second will be "not compatible types between assignemnt statement". Testing ~~~~~~~ - A script has been created for testing multiple inputs called test_script.sh. The script parses all files with extension '.java' under inputs directory.