239 lines
7.0 KiB
C++
239 lines
7.0 KiB
C++
//
|
|
// See copyright.h for copyright notice and limitation of liability
|
|
// and disclaimer of warranty provisions.
|
|
//
|
|
#include "copyright.h"
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// utilities.c
|
|
//
|
|
// General support code for lexer and parser.
|
|
//
|
|
// This file contains:
|
|
// fatal_error print an error message and exit
|
|
// print_escaped_string print a string showing escape characters
|
|
// print_cool_token print a cool token and its semantic value
|
|
// dump_cool_token dump a readable token representation
|
|
// strdup duplicate a string (missing from some libraries)
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "cool-io.h" // for cerr, <<, manipulators
|
|
#include <ctype.h> // for isprint
|
|
#include "cool-parse.h" // defines tokens
|
|
#include "stringtab.h" // Symbol <-> String conversions
|
|
#include "utilities.h"
|
|
|
|
// #define CHECK_TABLES
|
|
|
|
// sm: fixed an off-by-one error here; code assumed there were 80 spaces, but
|
|
// in fact only 79 spaces were there; I've made it 80 now
|
|
// 1 2 3 4 5 6 7
|
|
// 01234567890123456789012345678901234567890123456789012345678901234567890123456789
|
|
static char *padding = " "; // 80 spaces for padding
|
|
|
|
void fatal_error(char *msg)
|
|
{
|
|
cerr << msg;
|
|
exit(1);
|
|
}
|
|
|
|
|
|
void print_escaped_string(ostream& str, const char *s)
|
|
{
|
|
while (*s) {
|
|
switch (*s) {
|
|
case '\\' : str << "\\\\"; break;
|
|
case '\"' : str << "\\\""; break;
|
|
case '\n' : str << "\\n"; break;
|
|
case '\t' : str << "\\t"; break;
|
|
case '\b' : str << "\\b"; break;
|
|
case '\f' : str << "\\f"; break;
|
|
|
|
default:
|
|
if (isprint(*s))
|
|
str << *s;
|
|
else
|
|
//
|
|
// Unprintable characters are printed using octal equivalents.
|
|
// To get the sign of the octal number correct, the character
|
|
// must be cast to an unsigned char before coverting it to an
|
|
// integer.
|
|
//
|
|
str << '\\' << oct << setfill('0') << setw(3)
|
|
<< (int) ((unsigned char) (*s))
|
|
<< dec << setfill(' ');
|
|
break;
|
|
}
|
|
s++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The following two functions are used for debugging the parser.
|
|
//
|
|
char *cool_token_to_string(int tok)
|
|
{
|
|
switch (tok) {
|
|
case 0: return("EOF"); break;
|
|
case (CLASS): return("CLASS"); break;
|
|
case (ELSE): return("ELSE"); break;
|
|
case (FI): return("FI"); break;
|
|
case (IF): return("IF"); break;
|
|
case (IN): return("IN"); break;
|
|
case (INHERITS): return("INHERITS"); break;
|
|
case (LET): return("LET"); break;
|
|
case (LOOP): return("LOOP"); break;
|
|
case (POOL): return("POOL"); break;
|
|
case (THEN): return("THEN"); break;
|
|
case (WHILE): return("WHILE"); break;
|
|
case (ASSIGN): return("ASSIGN"); break;
|
|
case (CASE): return("CASE"); break;
|
|
case (ESAC): return("ESAC"); break;
|
|
case (OF): return("OF"); break;
|
|
case (DARROW): return("DARROW"); break;
|
|
case (NEW): return("NEW"); break;
|
|
case (STR_CONST): return("STR_CONST"); break;
|
|
case (INT_CONST): return("INT_CONST"); break;
|
|
case (BOOL_CONST): return("BOOL_CONST"); break;
|
|
case (TYPEID): return("TYPEID"); break;
|
|
case (OBJECTID): return("OBJECTID"); break;
|
|
case (ERROR): return("ERROR"); break;
|
|
case (LE): return("LE"); break;
|
|
case (NOT): return("NOT"); break;
|
|
case (ISVOID): return("ISVOID"); break;
|
|
case '+': return("'+'"); break;
|
|
case '/': return("'/'"); break;
|
|
case '-': return("'-'"); break;
|
|
case '*': return("'*'"); break;
|
|
case '=': return("'='"); break;
|
|
case '<': return("'<'"); break;
|
|
case '.': return("'.'"); break;
|
|
case '~': return("'~'"); break;
|
|
case ',': return("','"); break;
|
|
case ';': return("';'"); break;
|
|
case ':': return("':'"); break;
|
|
case '(': return("'('"); break;
|
|
case ')': return("')'"); break;
|
|
case '@': return("'@'"); break;
|
|
case '{': return("'{'"); break;
|
|
case '}': return("'}'"); break;
|
|
default: return("<Invalid Token>");
|
|
}
|
|
}
|
|
|
|
void print_cool_token(int tok)
|
|
{
|
|
|
|
cerr << cool_token_to_string(tok);
|
|
|
|
switch (tok) {
|
|
case (STR_CONST):
|
|
cerr << " = ";
|
|
cerr << " \"";
|
|
print_escaped_string(cerr, cool_yylval.symbol->get_string());
|
|
cerr << "\"";
|
|
#ifdef CHECK_TABLES
|
|
stringtable.lookup_string(cool_yylval.symbol->get_string());
|
|
#endif
|
|
break;
|
|
case (INT_CONST):
|
|
cerr << " = " << cool_yylval.symbol;
|
|
#ifdef CHECK_TABLES
|
|
inttable.lookup_string(cool_yylval.symbol->get_string());
|
|
#endif
|
|
break;
|
|
case (BOOL_CONST):
|
|
cerr << (cool_yylval.boolean ? " = true" : " = false");
|
|
break;
|
|
case (TYPEID):
|
|
case (OBJECTID):
|
|
cerr << " = " << cool_yylval.symbol;
|
|
#ifdef CHECK_TABLES
|
|
idtable.lookup_string(cool_yylval.symbol->get_string());
|
|
#endif
|
|
break;
|
|
case (ERROR):
|
|
cerr << " = ";
|
|
print_escaped_string(cerr, cool_yylval.error_msg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// dump the token in format readable by the sceond phase token lexer
|
|
void dump_cool_token(ostream& out, int lineno, int token, YYSTYPE yylval)
|
|
{
|
|
out << "#" << lineno << " " << cool_token_to_string(token);
|
|
|
|
switch (token) {
|
|
case (STR_CONST):
|
|
out << " \"";
|
|
print_escaped_string(out, cool_yylval.symbol->get_string());
|
|
out << "\"";
|
|
#ifdef CHECK_TABLES
|
|
stringtable.lookup_string(cool_yylval.symbol->get_string());
|
|
#endif
|
|
break;
|
|
case (INT_CONST):
|
|
out << " " << cool_yylval.symbol;
|
|
#ifdef CHECK_TABLES
|
|
inttable.lookup_string(cool_yylval.symbol->get_string());
|
|
#endif
|
|
break;
|
|
case (BOOL_CONST):
|
|
out << (cool_yylval.boolean ? " true" : " false");
|
|
break;
|
|
case (TYPEID):
|
|
case (OBJECTID):
|
|
out << " " << cool_yylval.symbol;
|
|
#ifdef CHECK_TABLES
|
|
idtable.lookup_string(cool_yylval.symbol->get_string());
|
|
#endif
|
|
break;
|
|
case (ERROR):
|
|
// sm: I've changed assignment 2 so students are supposed to
|
|
// *not* coalesce error characters into one string; therefore,
|
|
// if we see an "empty" string here, we can safely assume the
|
|
// lexer is reporting an occurrance of an illegal NUL in the
|
|
// input stream
|
|
if (cool_yylval.error_msg[0] == 0) {
|
|
out << " \"\\000\"";
|
|
}
|
|
else {
|
|
out << " \"";
|
|
print_escaped_string(out, cool_yylval.error_msg);
|
|
out << "\"";
|
|
break;
|
|
}
|
|
}
|
|
out << endl;
|
|
}
|
|
|
|
//
|
|
// Decstations don't have strdup in the library.
|
|
//
|
|
char *strdup(const char *s)
|
|
{
|
|
char *news;
|
|
|
|
if (s == NULL) return(NULL);
|
|
|
|
news = (char *)malloc(strlen(s)+1);
|
|
strcpy(news, s);
|
|
return(news);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// pad
|
|
//
|
|
// function to add pad
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
char *pad(int n) {
|
|
if (n > 80) return padding;
|
|
if (n <= 0) return "";
|
|
return padding+(80-n);
|
|
}
|