#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
#include "gccprefs.hh"
#include "io.hh"
Writer& operator << (Writer& w,bool b)
{
if (b) {
w << "#t";
}
else {
w << "#f";
}
return w;
}
File_Writer cout = File_Writer(stdout);
File_Writer cerr = File_Writer(stderr);
File_Writer debug = File_Writer(stdout);
File_Writer::File_Writer(string file_name)
{
char array[FILENAME_MAX];
f = fopen(file_name.char_star(array,FILENAME_MAX),"wb");
ours_to_close = true;
}
File_Writer::File_Writer(const char* file_name)
{
f = fopen(file_name,"wb");
ours_to_close = true;
}
File_Writer::File_Writer(FILE* f)
{
this->f = f;
ours_to_close = false;
}
File_Writer::~File_Writer()
{
if (ours_to_close) fclose(f);
}
Writer& File_Writer::operator << (const char* string)
{
fprintf(f,"%s",string);
return *this;
}
Writer& File_Writer::operator << (char ch)
{
fprintf(f,"%c",ch);
return *this;
}
Writer& File_Writer::operator << (int i)
{
fprintf(f,"%d",i);
return *this;
}
Writer& File_Writer::operator << (double d)
{
fprintf(f,"%g",d);
return *this;
}
int string::number_of_errors;
int string::my_strncpy(char* to, const char* from, int mem_size)
{
if (from == null) { *to = '\0';
return 0;
}
int i = 0;
int max = mem_size-1;
while ((*to++ = *from++)) {
if (++i >= max) {
*to++ = '\0';
return max;
}
}
return i;
}
void string::set_char_at(int index, char ch)
{
worker_mutator_clone();
ASSERT(0 <= index);
if (index >= ptr->length) {
cout << "Array index out of bounds for set_char_at, index="
<< index << endl;
ASSERT(false);
}
ptr->array[index] = ch;
}
char string::get_char_at(int index) const
{
ASSERT(0 <= index);
ASSERT(index < ptr->length);
return ptr->array[index];
}
int string::get_length() const
{
return ptr->length;
}
char* string::char_star(char* s, int mem_size) const
{
my_strncpy(s,ptr->array,min(mem_size,String_Internal::ARRAY_SIZE));
return s;
}
Writer& string::operator << (char ch)
{
worker_mutator_clone();
if (ptr->length < String_Internal::ARRAY_SIZE) {
ptr->array[ptr->length++] = ch;
ptr->array[ptr->length] = '\0';
}
else {
cout << "*** Warning: string too long for char\n";
ASSERT(++number_of_errors < 10);
}
return *this;
}
Writer& string::operator << (const char* s)
{
worker_mutator_clone();
while (*s != '\0') {
if (ptr->length < String_Internal::MAX_LENGTH) {
ptr->array[ptr->length++] = *s++;
}
else {
cout << "*** Warning: string too long for char*\n";
ASSERT(++number_of_errors < 10);
ptr->array[ptr->length] = '\0';
return *this;
}
}
ptr->array[ptr->length] = '\0';
return *this;
}
Writer& string::operator << (int i)
{
worker_mutator_clone();
char tmp[100];
sprintf(tmp,"%d",i);
*this << tmp;
return *this;
}
Writer& string::operator << (double d)
{
worker_mutator_clone();
char tmp[100];
sprintf(tmp,"%g",d);
*this << tmp;
return *this;
}
Writer& operator << (Writer& w, const string& s)
{
w << s.ptr->array;
return w;
}
string string::quoted() const
{
int len = get_length();
string s;
s << '"';
for (int i=0; i<len; i++)
{
char ch = get_char_at(i);
if (ch == '"') {
s << '\\' << '"';
}
else if (ch == '\\') {
s << '\\' << '\\';
}
else if (ch == '\t') {
s << '\\' << 't';
}
else if (ch == '\n') {
s << '\\' << 'n';
}
else if (ch == '\r') {
s << '\\' << 'r';
}
else {
s << ch;
}
}
s << '"';
return s;
}
string string::substring(int start, int stop) const
{
if (start < 0) start += get_length();
if (stop < 0) stop += get_length();
string temp = "";
for (int i=start; i<stop; i++) {
temp << get_char_at(i);
}
return temp;
}
string string::reversed() const
{
string temp = "";
for (int i=get_length()-1; i>=0; i--) {
temp << get_char_at(i);
}
return temp;
}
string string::capitalised() const
{
bool last_was_space = true;
string temp = "";
for (int i=0; i<get_length(); i++) {
char ch = get_char_at(i);
if (last_was_space) {
ch = toupper(ch);
}
if (ch == ' ') {
last_was_space = true;
}
else {
last_was_space = false;
}
temp << ch;
}
return temp;
}
void io_assert(const char* test, const char* file, int line)
{
cout << "*** Assertion "
<< "(" << file << ":" << line << ") "
<< "failed: " << test << endl;
exit(EXIT_FAILURE);
}
FileGulper::FileGulper(string file_name)
{
char array[FILENAME_MAX];
f = fopen(file_name.char_star(array,FILENAME_MAX),"rb");
if (f == null) {
cout << "Unable to open file '" << file_name << "' for reading\n";
exit(EXIT_FAILURE);
}
ours_to_close = true;
}
FileGulper::FileGulper(const char* file_name)
{
f = fopen(file_name,"rb");
if (f == null) {
cout << "Unable to open file '" << file_name << "' for reading\n";
exit(EXIT_FAILURE);
}
ours_to_close = true;
}
FileGulper::FileGulper(FILE* f)
{
this->f = f;
ASSERT(f != null);
ours_to_close = false;
}
FileGulper::~FileGulper()
{
if (ours_to_close) fclose(f);
}
int
FileGulper::gulp_char()
{
return getc(f);
}
Reader_core::Reader_core(string file_name)
{
reader = new FileGulper(file_name);
this->file_name = file_name;
last_char = 0;
last_int = 0;
last_double = 0;
last_identifier = "";
last_string = "";
current_file = file_name;
current_line = 1; gulp();
next_token();
}
Reader_core::~Reader_core()
{
delete reader;
}
void
Reader_core::gulp()
{
current = reader->gulp_char();
if (current == '\n') current_line++;
}
string
Reader_core::scan_identifier()
{
ASSERT(isalpha(current));
string s;
s << (char)current;
gulp();
for (;;) {
if (current == EOF ||
isspace(current) ||
current == ')' ||
current == '(') {
return s;
}
s << (char)current;
gulp();
}
return s;
}
string
Reader_core::scan_quoted_string()
{
ASSERT(current == '"'); gulp();
string s;
while (current != '"') {
char duplicate = current;
if (current == '\\') {
gulp();
switch (current) {
case 'n':
duplicate = '\n';
break;
case 't':
duplicate = '\n';
break;
case '\\':
duplicate = '\\';
break;
case '\"':
duplicate = '\"';
break;
case EOF:
break;
default: {
string msg = "Unrecognised escape sequence: \\";
msg << (char)current;
error(msg);
}
}
}
if (current == EOF) {
string msg = "End of file before end of string: ";
msg << s.quoted();
error(msg);
break;
}
s << duplicate;
gulp();
}
ASSERT(current == '"'); gulp();
return s;
}
void
Reader_core::skip_whitespace()
{
bool found_whites = true;
while (found_whites) {
found_whites = false;
if (isspace(current)) {
found_whites = true;
while (isspace(current)) {
gulp();
}
}
if (current == ';') {
found_whites = true;
int count=0;
for (;;) {
if (current == '\n' || current == EOF) break;
gulp();
ASSERT(++count < 1000);
}
skip_whitespace();
}
}
}
int
Reader_core::scan_int()
{
bool neg = false;
if (current == '-') {
gulp();
}
int answer = 0;
while (isdigit(current)) {
answer = answer * 10 + (current - '0');
gulp();
}
if (neg) answer = - answer;
return answer;
}
double
Reader_core::scan_fraction()
{
double answer = 0;
for (int k=10; isdigit(current); k*=10) {
answer = answer + (current - '0') / ((double)k);
gulp();
}
return answer;
}
void
Reader_core::next_token()
{
skip_whitespace();
bool minus_flag;
if (current == '-') {
gulp();
minus_flag = true;
}
else {
minus_flag = false;
}
if (isdigit(current)) {
last_int = scan_int();
if (minus_flag) {
last_int = (-last_int);
}
if (current == '.') {
gulp();
last_double = last_int + (minus_flag? -1:1) * scan_fraction();
if (current == 'e') {
gulp();
ASSERT(current == '+' || current == '-');
bool exponent_negative;
if (current == '-') {
exponent_negative = true;
}
else {
exponent_negative = false;
}
gulp();
int exponent = scan_int();
if (exponent_negative) {
exponent = (-exponent);
}
const double ln10 = log(10);
last_double = last_double * exp(exponent * ln10);
}
current_type = TT_double;
}
else {
current_type = TT_int;
}
skip_whitespace();
}
else if (isalpha(current)) {
current_type = TT_identifier;
last_identifier = scan_identifier();
}
else if (current == '"') {
current_type = TT_string;
last_string = scan_quoted_string();
}
else if (current == '#') {
gulp();
if (current == 't') {
current_type = TT_bool;
last_bool = true;
gulp();
}
else if (current == 'f') {
current_type = TT_bool;
last_bool = false;
gulp();
}
else {
current_type = TT_char;
last_char = '#';
}
}
else if (current == EOF) {
current_type = TT_eof;
}
else {
current_type = TT_char;
last_char = current;
gulp();
}
}
void
Reader_core::error(string s)
{
cout << "Reader_core reports the following error:\n\n";
cout << file_name << ':' << current_line << ": " << s << "\n\n";
cout << "Reader_core errors are serious!\n";
exit(EXIT_FAILURE);
}
string
Reader_core::debug_say_symbol()
{
string s;
switch (current_type) {
case TT_bool:
s << "(bool, " << (last_bool?"#t" : "#f") << ")";
break;
case TT_char:
s << "(char, '" << last_char << "' (" << (int)last_char << "))";
break;
case TT_int:
s << "(int, " << last_int << ")";
break;
case TT_double:
s << "(double, " << last_double << ")";
break;
case TT_identifier:
s << "(identifier, " << last_identifier << ")";
break;
case TT_string:
s << "(string, " << last_string.quoted() << ")";
break;
default:
s << "(UNKNOWN! current = " << current
<< ", type = " << current_type << ")";
}
return s;
}
void
Reader_core::wrong_type(string tname)
{
cout << "Coding error in Reader_core call: tried to get_" << tname
<< ", but current token = " << debug_say_symbol() << "\n";
exit(EXIT_FAILURE);
}
bool
Reader_core::peek_bool()
{
if (current_type != TT_bool) wrong_type("bool");
return last_bool;
}
char
Reader_core::peek_char()
{
if (current_type != TT_char) wrong_type("char");
return last_char;
}
int
Reader_core::peek_int()
{
if (current_type != TT_int) wrong_type("int");
return last_int;
}
double
Reader_core::peek_double()
{
if (current_type != TT_double) wrong_type("double");
return last_double;
}
string
Reader_core::peek_identifier()
{
if (current_type != TT_identifier) wrong_type("identifier");
return last_identifier;
}
string
Reader_core::peek_string()
{
if (current_type != TT_string) wrong_type("string");
return last_string;
}
int
Reader_core::get_file_line()
{
return current_line;
}
string
Reader_core::get_file_name()
{
return current_file;
}
bool Reader::looking_at(char ch)
{
return (currently_char() && peek_char() == ch);
}
bool Reader::looking_at(string s)
{
return (currently_identifier() && strings_equal(peek_identifier(),s));
}
Reader& operator >> (Reader& r, const char& ch)
{
ASSERT(r.currently_char() &&
r.peek_char() == ch);
r.next_token();
return r;
}
Reader& operator >> (Reader& r, const char* s)
{
ASSERT(r.currently_identifier() &&
strings_equal(r.peek_identifier(),s));
r.next_token();
return r;
}
Reader& operator >> (Reader& r, bool& b)
{
ASSERT(r.currently_bool());
b = r.peek_bool();
r.next_token();
return r;
}
Reader& operator >> (Reader& r, char& ch)
{
ASSERT(r.currently_char());
ch = r.peek_char();
r.next_token();
return r;
}
Reader& operator >> (Reader& r, int& i)
{
ASSERT(r.currently_int());
i = r.peek_int();
r.next_token();
return r;
}
Reader& operator >> (Reader& r, double& d)
{
ASSERT(r.currently_double());
d = r.peek_double();
r.next_token();
return r;
}
Reader& operator >> (Reader& r, string& s)
{
ASSERT(r.currently_identifier());
s = r.peek_identifier();
r.next_token();
return r;
}
string Reader::gulp_quoted_string()
{
ASSERT(currently_string());
string s = peek_string();
next_token();
return s;
}