CSC305 ASSIGNMENT Final
CSC305 ASSIGNMENT Final
CSC305 ASSIGNMENT
GROUP 5
Page | 1
GROUP MEMBERS
S/N NAME MATRIC NUMBER
1 OVABOR OSEDEBAMEH OBED SCI19CSC087
2 PETER ENOJO MOSES SCI19CSC088
3 SALAMI BABATUNDE SAMUEL SCI19CSC089
4 SALAMI VICTOR HARUNA SCI19CSC090
5 SALAWUDEEN ABDULMUHAYMIN SCI19CSC091
OGIRIMA
6 SAMUEL TITUS OLUWATIMILEYIN SCI19CSC092
7 SULEIMAN SADIK ABUBAKAR SCI19CSC093
8 SULEIMAN NANAHAWAW IZE SCI19CSC094
9 SUNDAY JEREMIAH SCI19CSC095
10 THOMAS OJONIMI SHEDRACK SCI19CSC096
11 TUWASE TOSIN SCI19CSC097
12 UKWUEZE CHUKWUEMEKA GIFT SCI19CSC098
13 UMOREN BARNABAS JOHN SCI19CSC099
14 USMAN ABDULMALEEK ANDA SCI19CSC100
15 USMAN OMOWUNMI GRACE SCI19CSC101
16 USMANKING ABDULLAHI SCI19CSC102
17 YAKUBU FRANCIS ELEOJO SCI19CSC103
18 YAU-YABA UMAR SCI19CSC104
19 YUSUF KHADIJAT OZOHU SCI19CSC105
20 ABDULRAHMAN AHMAD BABA SCI19CSC106
Page | 2
QUESTION 1
Explain in detail the symbol table and its implementation in C/C++/Java or Python.
ANSWER
Behind every remarkable compiler lies a very important tool known as the symbol
table. This unassuming data structure plays a very vital role in deciphering the
language of code by bridging the gap between human readable instructions and
machine-friendly executions. With the symbol table, the compiler resolves the
complexity of scoping, resolves naming problems and ensures semantic accuracy.
Page | 3
SYMBOL TABLE OPERATION
Common operations that occur in the symbol table during the compilation process
are:
1. Insert: This operation inserts a name into the symbol table and returns a
pointer.
2. Lookup: This operation searches a name and returns the pointer.
3. Create: This operation allocates a new empty symbol table.
4. Delete: This operation helps in removing symbols from the symbol table.
Page | 4
#include <iostream>
#include <unordered_map>
#include <string>
class Student {
public:
std::string name;
int age;
std::string major;
class StudentDatabase {
private:
std::unordered_map<int, Student> database;
public:
void insert(int studentID, const std::string& name, int
age, const std::string& major) {
Student student(name, age, major);
database[studentID] = student;
}
Page | 5
void remove(int studentID) {
auto it = database.find(studentID);
if (it != database.end()) {
database.erase(it);
} else {
throw std::runtime_error("Student not found in
the database.");
}
}
};
// Example Usage:
int main() {
StudentDatabase studentDB;
Page | 6
}
return 0;
}
Page | 7
QUESTION 2
Discuss in details with their implementations in any programming language of your
choice, the following
a) Java CC (Java Compiler Compiler), and
b) YACC (Yet another Compiler Compiler)
ANSWER
a. Java CC (Java Compiler Compiler)
JavaCC, short for Java Compiler Compiler, is a parser generator and lexical
analyzer generator for the Java programming language. It is used to generate Java
code for building parsers, interpreters, and compilers for various programming
languages or domain-specific languages (DSLs).
It uses a BNF-like (Backus-Naur Form) syntax to define the grammar rules of
the language being parsed. Based on this input grammar specification, JavaCC
generates the necessary Java code that can be integrated into your Java application
for lexing and parsing the input source code.
Parser Generation
Parser and lexical analyzers are the two software components that deal with the
input of character sequences. The compiler and interpreters integrate lexical
analyzers and parsers. The parsers are used to deciphers the files that contain
Page | 8
programs. In other words, the parser reads grammar specifications and converts them
into Java programs that recognize the matches to the grammar.
FEATURES OF JAVACC
1. Parser Generation: JavaCC generates LL(k) parsers, which are predictive
parsers based on a fixed number (k) of look-ahead tokens. This allows efficient
parsing of languages with deterministic grammars.
2. Lexical Analysis: JavaCC provides support for generating lexical analyzers
(scanners) that can tokenize the input source code into a sequence of tokens (tokens
are the smallest units of the language being parsed).
3. Error Reporting: JavaCC generates parsers with built-in error handling and
reporting capabilities, making it easier to identify syntax errors in the input source
code.
4. Grammar Extensions: JavaCC allows you to extend the BNF-like grammar
notation with additional features, such as lookahead, syntactic predicates, semantic
actions, and more.
5. Lexer States: JavaCC supports lexer states, which allow the lexer to switch
between different sets of lexical rules based on the parser's context.
Page | 9
6. Tree Building: JavaCC can be configured to generate Abstract Syntax Trees
(ASTs) or parse trees, making it easier to perform subsequent semantic analysis or
code generation.
// ArithmeticParser.jj
options {
STATIC = false;
}
PARSER_BEGIN(ArithmeticParser)
public class ArithmeticParser {
public static void main(String[] args) throws
ParseException {
ArithmeticParser parser = new
ArithmeticParser(System.in);
parser.parse();
}
}
PARSER_END(ArithmeticParser)
TOKEN : {
< INTEGER: (["0"-"9"])+ >
| < ADD: "+" >
| < SUBTRACT: "-" >
| < MULTIPLY: "*" >
| < DIVIDE: "/" >
| < LPAREN: "(" >
| < RPAREN: ")" >
Page | 10
}
int parse() :
{
int value;
}
{
value = additiveExpression() <EOF>
{
System.out.println("Result: " + value);
return value;
}
}
int additiveExpression() :
{
int value;
Token op;
}
{
value = multiplicativeExpression()
(
op = <ADD> { value += multiplicativeExpression(); }
| op = <SUBTRACT> { value -= multiplicativeExpression();
}
)*
{ return value; }
}
int multiplicativeExpression() :
{
int value;
Token op;
Page | 11
}
{
value = primaryExpression()
(
op = <MULTIPLY> { value *= primaryExpression(); }
| op = <DIVIDE> { value /= primaryExpression(); }
)*
{ return value; }
}
int primaryExpression() :
{
int value;
}
{
value = <INTEGER>
| <LPAREN> value = additiveExpression() <RPAREN>
{ return value; }
}
Page | 12
b. YACC (Yet another compiler compiler)
Yacc (Yet Another Compiler Compiler) is a syntax analyser generator or parser
generator, hence Yacc takes in a context-free grammar as input and generates a
parser in C programming language as output. It is often used in combination with
Lex (Lexical analyzer generator) which generates the lexical analyzer. Although
Flex is commonly used recently, it is an open source and faster analyzer for the
language being compiled.
The parser generated by Yacc is typically a LALR (Look-Ahead LR) parser, which
is an efficient and widely used type of bottom-up parser. The LALR parsing
technique allows the parser to recognize and analyze the structure of the input
program and build an Abstract Syntax Tree (AST) for further processing.
FEATURES OF YACC
1. Grammar Specification: Yacc uses a context-free grammar (CFG) specification
to define the syntax of the language being parsed. The grammar consists of rules that
describe the valid sequences of tokens in the language.
2. Parser Generation: Based on the input grammar, Yacc generates the necessary
C code to implement the LALR parser. The generated parser reads the sequence of
tokens produced by the lexical analyzer (Lex) and matches them against the
grammar rules to recognize the syntax of the input program.
Page | 13
3. Symbol Table and Semantic Actions: Yacc allows the inclusion of semantic
actions in the grammar rules. These semantic actions are snippets of code that get
executed when specific grammar rules are matched, allowing the parser to perform
tasks like building the AST, handling symbol table entries, and performing semantic
analysis.
4. Error Recovery: Yacc provides mechanisms for error recovery during parsing.
It can be configured to produce informative error messages and continue parsing
after encountering syntax errors.
5. Shift-Reduce Conflicts Resolution: Yacc automatically resolves shift-reduce
and reduce-reduce conflicts that may arise in the grammar. This ensures that the
generated parser behaves predictably and can handle ambiguous grammars.
Page | 14
Lexical Analyser source code:
%option noyywrap
%{
#include "parser.tab.hpp"
%}
DIGIT [0-9]
WS [ \t\r\n]
%%
%%
%{
#include <stdio.h>
%}
%token NUMBER
%left '+' '-'
%left '*' '/'
%%
input: expression { printf("Result: %d\n", $1); }
;
Page | 15
| expression '-' expression { $$ = $1 - $3; }
| expression '*' expression { $$ = $1 * $3; }
| expression '/' expression { $$ = $1 / $3; }
| NUMBER { $$ = $1; }
;
%%
int yylex();
void yyerror(const char *msg);
int main() {
yyparse();
return 0;
}
MAIN PROGRAM
#include <iostream>
int main() {
yyparse();
return 0;
}
Page | 16
References
https://www.i2tutorials.com/compiler-design-tutorial/compiler-design-
https://www.tutorialspoint.com/compiler_design/compiler_design_symbol_t
Page | 17