Compiler Theory
listing.cc
listing.cc
// CMSC 430 Compiler Theory and Design
// Project 3 Skeleton
// UMGC CITE
// Summer 2023
// This file contains the bodies of the functions that produces the
// compilation listing
#include
<
cstdio
>
#include
<
string
>
#include
<
vector
>
using
namespace
std
;
#include
"listing.h"
static
int
lineNumber
;
static
string error
=
""
;
static
int
totalErrors
=
0
;
static
int
lexicalErrors
=
0
;
static
int
syntaxErrors
=
0
;
static
int
semanticErrors
=
0
;
static
vector
<
string
>
errors
;
static
void
displayErrors
();
void
firstLine
()
{
lineNumber
=
1
;
printf
(
"\n%4d "
,
lineNumber
);
}
void
nextLine
()
{
displayErrors
();
lineNumber
++
;
printf
(
"%4d "
,
lineNumber
);
}
int
lastLine
()
{
printf
(
"\r"
);
displayErrors
();
printf
(
" \n"
);
if
(
totalErrors
>
0
)
{
printf
(
"Lexical errors: %d\n"
,
lexicalErrors
);
printf
(
"Syntax errors: %d\n"
,
syntaxErrors
);
printf
(
"Semantic errors: %d\n"
,
semanticErrors
);
}
else
{
printf
(
"Compiled Successfully\n"
);
}
return
totalErrors
;
}
void
appendError
(
ErrorCategories
errorCategory
,
string message
)
{
string messages
[]
=
{
"Lexical Error, Invalid Character "
,
""
,
"Semantic Error, "
,
"Semantic Error, Duplicate "
,
"Semantic Error, Undeclared "
};
error
=
messages
[
errorCategory
]
+
message
;
totalErrors
++
;
if
(
errorCategory
==
LEXICAL
)
{
lexicalErrors
++
;
}
else
if
(
errorCategory
==
SYNTAX
)
{
syntaxErrors
++
;
}
else
if
(
errorCategory
==
GENERAL_SEMANTIC
||
errorCategory
==
DUPLICATE_IDENTIFIER
||
errorCategory
==
UNDECLARED
)
{
semanticErrors
++
;
}
errors
.
push_back
(
error
);
error
=
""
;
}
void
displayErrors
()
{
for
(
const
string
&
err
:
errors
)
{
printf
(
"%s\n"
,
err
.
c_str
());
}
errors
.
clear
();
}
listing.h
// CMSC 430 Compiler Theory and Design // Project 3 Skeleton // UMGC CITE // Summer 2023 // This file contains the function prototypes for the functions that produce // the compilation listing enum ErrorCategories {LEXICAL, SYNTAX, GENERAL_SEMANTIC, DUPLICATE_IDENTIFIER, UNDECLARED, ARITHMETIC, RELATIONAL}; void firstLine(); void nextLine(); int lastLine(); void appendError(ErrorCategories errorCategory, string message);
makefile
compile: scanner.o parser.o listing.o values.o g++ -o compile scanner.o parser.o listing.o values.o scanner.o: scanner.c values.h listing.h tokens.h g++ -c scanner.c scanner.c: scanner.l flex scanner.l mv lex.yy.c scanner.c parser.o: parser.c values.h listing.h symbols.h g++ -c parser.c parser.c tokens.h: parser.y bison -d -v parser.y mv parser.tab.c parser.c cp parser.tab.h tokens.h listing.o: listing.cc listing.h g++ -c listing.cc values.o: values.cc values.h g++ -c values.cc
parser.y
/* CMSC 430 Compiler Theory and Design Project 3 UMGC CITE Project 3 Parser with semantic actions for the interpreter */ %{ #include <iostream> #include <cmath> #include <string> #include <vector> #include <map> using namespace std; #include "values.h" #include "listing.h" #include "symbols.h" int yylex(); void yyerror(const char* message); double extract_element(CharPtr list_name, double subscript); Symbols<double> scalars; Symbols<vector<double>*> lists; double result; double* param_values; int param_index; %} %define parse.error verbose %union { CharPtr iden; Operators oper; double value; vector<double>* list; } %left ADDOP %left MULOP %right EXPOP %left ANDOP %left OROP %left RELOP %nonassoc NOTOP %token <iden> IDENTIFIER %token <value> INT_LITERAL CHAR_LITERAL REAL_LITERAL HEX_LITERAL %token <oper> ADDOP MULOP ANDOP RELOP ARROW OROP MODOP EXPOP NOTOP NEGOP SUBOP DIVOP %token BEGIN_ CASE CHARACTER IF ELSE END ENDSWITCH FUNCTION INTEGER IS LIST OF OTHERS RETURNS SWITCH WHEN ELSIF THEN ENDIF FOLD ENDFOLD LEFT RIGHT REAL %type <value> function function_header type optional_variable variables variable body statement_ statement cases case condition relation expression term factor unary primary optional_parameters parameters parameter elsif_statements else_statement direction operator others_case %type <list> list expressions list_choice %% function: function_header optional_variable body ';' {result = $3;}; function_header: FUNCTION IDENTIFIER optional_parameters RETURNS type ';' { param_index = 0; } | FUNCTION error ';' { $$ = 0; }; optional_parameters: %empty { $$ = 0; } | parameters; parameters: parameters ',' parameter { $$ = 0; } | parameter { $$ = 0; }; parameter: IDENTIFIER ':' type { scalars.insert($1, param_values[param_index++]); }; type: INTEGER { $$ = 0; } | REAL { $$ = 0; } | CHARACTER { $$ = 0; }; optional_variable: variables { $$ = 0; } | %empty { $$ = 0; }; variables: variables variable { $$ = 0; } | variable { $$ = 0; } | error ';' { $$ = 0; }; variable: IDENTIFIER ':' type IS statement ';' { scalars.insert($1, $5); $$ = 0; } | IDENTIFIER ':' LIST OF type IS list ';' { lists.insert($1, $7); $$ = 0; }; list: '(' expressions ')' { $$ = $2; }; expressions: expressions ',' expression { $1->push_back($3); $$ = $1; } | expression { $$ = new vector<double>(); $$->push_back($1); }; body: BEGIN_ statement_ END { $$ = $2; }; statement_: statement ';' { $$ = $1; } | error ';' { $$ = 0; }; statement: expression { $$ = $1; } | WHEN condition ',' expression ':' expression { $$ = $2 ? $4 : $6; } | SWITCH expression IS cases others_case ENDSWITCH { $$ = !isnan($4) ? $4 : $5; } | IF condition THEN statement_ elsif_statements else_statement ENDIF { $$ = $2 ? $4 : ($5 != 0 ? $5 : $6); } | FOLD direction operator list_choice ENDFOLD { $$ = evaluateFold($2, (Operators)$3, $4); }; elsif_statements: %empty { $$ = 0; } | elsif_statements ELSIF condition THEN statement_ { $$ = $3 ? $5 : $1; }; else_statement: ELSE statement_ { $$ = $2; } | %empty { $$ = 0; }; cases: cases case { $$ = !isnan($1) ? $1 : $2; } | %empty { $$ = NAN; }; others_case: OTHERS ARROW statement ';' { $$ = $3; } | OTHERS ARROW statement { $$ = $2; } case: CASE INT_LITERAL ARROW statement ';' { $$ = ($2 == result) ? $4 : NAN; } | CASE CHAR_LITERAL ARROW statement ';' { $$ = ($2 == result) ? $4 : NAN; } | error ';' { $$ = NAN; }; direction: LEFT { $$ = 'L'; } | RIGHT { $$ = 'R'; }; operator: ADDOP { $$ = ADD; } | MULOP { $$ = MULTIPLY; } | SUBOP { $$ = SUBTRACT; } | DIVOP { $$ = DIVIDE; }; list_choice: list { $$ = $1; } | IDENTIFIER { vector<double>* temp; if (lists.find($1, temp)) $$ = temp; else { $$ = nullptr; appendError(UNDECLARED, $1); } }; condition: condition OROP relation { $$ = $1 || $3; } | condition ANDOP relation { $$ = $1 && $3; } | relation; relation: '(' condition ')' { $$ = $2; } | expression RELOP expression { $$ = evaluateRelational($1, $2, $3); } | NOTOP relation { $$ = !$2; }; expression: expression ADDOP term { $$ = evaluateArithmetic($1, $2, $3); } | expression SUBOP term { $$ = evaluateArithmetic($1, SUBTRACT, $3); } | term; term: term MULOP factor { $$ = evaluateArithmetic($1, $2, $3); } | term DIVOP factor { $$ = evaluateArithmetic($1, DIVIDE, $3); } | term MODOP factor { $$ = fmod($1, $3); } | factor; factor: primary EXPOP factor { $$ = pow($1, $3); } | unary; unary: NEGOP unary { $$ = -$2; } | primary; primary: '(' expression ')' { $$ = $2; } | INT_LITERAL { $$ = $1; } | REAL_LITERAL { $$ = $1; } | CHAR_LITERAL { $$ = $1; } | HEX_LITERAL { $$ = $1; } | IDENTIFIER '(' expression ')' { $$ = extract_element($1, $3); } | IDENTIFIER { if (!scalars.find($1, $$)) appendError(UNDECLARED, $1); }; %% void yyerror(const char* message) { appendError(SYNTAX, message); } double extract_element(CharPtr list_name, double subscript) { vector<double>* list; if (lists.find(list_name, list)) return (*list)[subscript]; appendError(UNDECLARED, list_name); return NAN; } int main(int argc, char *argv[]) { firstLine(); param_values = new double[argc - 1]; for (int i = 1; i < argc; ++i) { param_values[i - 1] = atof(argv[i]); } yyparse(); if (lastLine() == 0) cout << "Result = " << result << endl; delete[] param_values; return 0; }
scanner.l
%{ #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <vector> using namespace std; #include "values.h" #include "listing.h" #include "tokens.h" %} %option noyywrap ws [ \t\r]+ comment "//".*\n newcomment "--".*\n line [\n] id [A-Za-z]([A-Za-z0-9]*(_{1,2}[A-Za-z0-9]+)*)* digit [0-9] hex [0-9A-Fa-f]+ dec {digit}+ real {digit}*"."{digit}+([eE][+-]?{digit}+)? hexint "#"{hex} char \'.\'|\'\\[btnfr]\' punc [\(\),:;] %% {ws} { ECHO; } {comment} { ECHO; nextLine(); } {newcomment} { ECHO; nextLine(); } {line} { ECHO; nextLine(); } "+" { ECHO; yylval.oper = ADD; return(ADDOP); } "-" { ECHO; yylval.oper = SUBTRACT; return(SUBOP); } "*" { ECHO; yylval.oper = MULTIPLY; return(MULOP); } "/" { ECHO; yylval.oper = DIVIDE; return(DIVOP); } "%" { ECHO; yylval.oper = MOD; return(MODOP); } "^" { ECHO; yylval.oper = EXPONENT; return(EXPOP); } "~" { ECHO; return(NEGOP); } "&" { ECHO; yylval.oper = AND; return(ANDOP); } "<" { ECHO; yylval.oper = LESS; return(RELOP); } "=>" { ECHO; return(ARROW); } "|" { ECHO; return(OROP); } "!" { ECHO; return(NOTOP); } "=" { ECHO; yylval.oper = EQUAL; return(RELOP); } "<>" { ECHO; yylval.oper = NOTEQUAL; return(RELOP); } ">" { ECHO; yylval.oper = GREATER; return(RELOP); } ">=" { ECHO; yylval.oper = GREATEREQUAL; return(RELOP); } "<=" { ECHO; yylval.oper = LESSEQUAL; return(RELOP); } begin { ECHO; return(BEGIN_); } case { ECHO; return(CASE); } character { ECHO; return(CHARACTER); } end { ECHO; return(END); } endswitch { ECHO; return(ENDSWITCH); } function { ECHO; return(FUNCTION); } integer { ECHO; return(INTEGER); } is { ECHO; return(IS); } list { ECHO; return(LIST); } of { ECHO; return(OF); } others { ECHO; return(OTHERS); } returns { ECHO; return(RETURNS); } switch { ECHO; return(SWITCH); } when { ECHO; return(WHEN); } else { ECHO; return(ELSE); } endif { ECHO; return(ENDIF); } endfold { ECHO; return(ENDFOLD); } elsif { ECHO; return(ELSIF); } fold { ECHO; return(FOLD); } if { ECHO; return(IF); } left { ECHO; return(LEFT); } real { ECHO; return(REAL); } right { ECHO; return(RIGHT); } then { ECHO; return(THEN); } {id} { ECHO; yylval.iden = strdup(yytext); return(IDENTIFIER); } {dec} { ECHO; yylval.value = atoi(yytext); return(INT_LITERAL); } {hexint} { ECHO; yylval.value = strtol(yytext + 1, nullptr, 16); return HEX_LITERAL; } {real} { ECHO; yylval.value = atof(yytext); return REAL_LITERAL; } {char} { if (strlen(yytext) == 3) { yylval.value = yytext[1]; } else { switch (yytext[2]) { case 'n': yylval.value = '\n'; break; case 't': yylval.value = '\t'; break; case 'r': yylval.value = '\r'; break; case 'f': yylval.value = '\f'; break; case 'b': yylval.value = '\b'; break; default: yylval.value = yytext[2]; break; } } return CHAR_LITERAL; } {punc} { ECHO; return(yytext[0]); } . { ECHO; appendError(LEXICAL, yytext); } %%
symbols.h
// CMSC 430 Compiler Theory and Design // Project 3 Complete // UMGC CITE // Summer 2023 // This file contains the template symbol table template <typename T> class Symbols { public: void insert(char* lexeme, T entry); bool find(char* lexeme, T& entry); private: map<string, T> symbols; }; template <typename T> void Symbols<T>::insert(char* lexeme, T entry) { string name(lexeme); symbols[name] = entry; } template <typename T> bool Symbols<T>::find(char* lexeme, T& entry) { string name(lexeme); typedef typename map<string, T>::iterator Iterator; Iterator iterator = symbols.find(name); bool found = iterator != symbols.end(); if (found) entry = iterator->second; return found; }
tokens.h
/* A Bison parser, made by GNU Bison 3.8.2. */ /* Bison interface for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, especially those whose name start with YY_ or yy_. They are private implementation details that can be changed or removed. */ #ifndef YY_YY_PARSER_TAB_H_INCLUDED # define YY_YY_PARSER_TAB_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif #if YYDEBUG extern int yydebug; #endif /* Token kinds. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE enum yytokentype { YYEMPTY = -2, YYEOF = 0, /* "end of file" */ YYerror = 256, /* error */ YYUNDEF = 257, /* "invalid token" */ IDENTIFIER = 258, /* IDENTIFIER */ INT_LITERAL = 259, /* INT_LITERAL */ CHAR_LITERAL = 260, /* CHAR_LITERAL */ REAL_LITERAL = 261, /* REAL_LITERAL */ HEX_LITERAL = 262, /* HEX_LITERAL */ ADDOP = 263, /* ADDOP */ MULOP = 264, /* MULOP */ ANDOP = 265, /* ANDOP */ RELOP = 266, /* RELOP */ ARROW = 267, /* ARROW */ OROP = 268, /* OROP */ MODOP = 269, /* MODOP */ EXPOP = 270, /* EXPOP */ NOTOP = 271, /* NOTOP */ NEGOP = 272, /* NEGOP */ SUBOP = 273, /* SUBOP */ DIVOP = 274, /* DIVOP */ BEGIN_ = 275, /* BEGIN_ */ CASE = 276, /* CASE */ CHARACTER = 277, /* CHARACTER */ IF = 278, /* IF */ ELSE = 279, /* ELSE */ END = 280, /* END */ ENDSWITCH = 281, /* ENDSWITCH */ FUNCTION = 282, /* FUNCTION */ INTEGER = 283, /* INTEGER */ IS = 284, /* IS */ LIST = 285, /* LIST */ OF = 286, /* OF */ OTHERS = 287, /* OTHERS */ RETURNS = 288, /* RETURNS */ SWITCH = 289, /* SWITCH */ WHEN = 290, /* WHEN */ ELSIF = 291, /* ELSIF */ THEN = 292, /* THEN */ ENDIF = 293, /* ENDIF */ FOLD = 294, /* FOLD */ ENDFOLD = 295, /* ENDFOLD */ LEFT = 296, /* LEFT */ RIGHT = 297, /* RIGHT */ REAL = 298 /* REAL */ }; typedef enum yytokentype yytoken_kind_t; #endif /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { #line 35 "parser.y" CharPtr iden; Operators oper; double value; vector<double>* list; #line 114 "parser.tab.h" }; typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif extern YYSTYPE yylval; int yyparse (void); #endif /* !YY_YY_PARSER_TAB_H_INCLUDED */
values.cc
#include <iostream> #include <cmath> #include <vector> using namespace std; #include "values.h" #include "listing.h" double evaluateArithmetic(double left, Operators operator_, double right) { double result; switch (operator_) { case ADD: result = left + right; break; case MULTIPLY: result = left * right; break; case SUBTRACT: result = left - right; break; case DIVIDE: result = left / right; break; case MOD: result = fmod(left, right); break; case EXPONENT: result = pow(left, right); break; default: appendError(ARITHMETIC, "Unknown operator"); result = NAN; } return result; } double evaluateRelational(double left, Operators operator_, double right) { double result; switch (operator_) { case LESS: result = left < right; break; case GREATER: result = left > right; break; case EQUAL: result = left == right; break; case NOTEQUAL: result = left != right; break; case LESSEQUAL: result = left <= right; break; case GREATEREQUAL: result = left >= right; break; default: appendError(RELATIONAL, "Unknown operator"); result = NAN; } return result; } double evaluateFold(char direction, Operators oper, vector<double>* list) { if (!list || list->empty()) { appendError(GENERAL_SEMANTIC, "Empty list in fold operation"); return NAN; } double result; if (direction == 'L') { result = list->front(); for (size_t i = 1; i < list->size(); ++i) { result = evaluateArithmetic(result, oper, (*list)[i]); } } else { result = list->back(); for (int i = list->size() - 2; i >= 0; --i) { result = evaluateArithmetic((*list)[i], oper, result); } } return result; }
values.h
// CMSC 430 Compiler Theory and Design // Project 3 Skeleton // UMGC CITE // Summer 2023 // This file contains type definitions and the function // definitions for the evaluation functions typedef char* CharPtr; enum Operators {ADD, MULTIPLY, SUBTRACT, DIVIDE, MOD, EXPONENT, LESS, GREATER, EQUAL, NOTEQUAL, LESSEQUAL, GREATEREQUAL, AND, OR, NOT }; double evaluateArithmetic(double left, Operators operator_, double right); double evaluateRelational(double left, Operators operator_, double right); double evaluateFold(char direction, Operators oper, std::vector<double>* list);