// Copyright 2007 Georgia Institute of Technology. All rights reserved. // ABSOLUTELY NOT FOR DISTRIBUTION /** * @file textfile.h * * Small wrappers for text files. * The most useful thing here is the ReadLine function. */ #ifndef FILE_TEXTFILE_H #define FILE_TEXTFILE_H #include "fastlib/base/base.h" #include "fastlib/col/string.h" #include #include #include /** * Helper for reading text files. * * Files are closed automatically when they fall out of scope, though * you may choose to close it yourself at no harm. */ class TextLineReader { FORBID_ACCIDENTAL_COPIES(TextLineReader); private: FILE *f_; String line_; int line_num_; bool has_line_; public: /** Creates an unitialized object. */ TextLineReader() { f_ = NULL; } /** * Automatically closes the file. */ ~TextLineReader() { if (f_) { ::fclose(f_); } } /** * Opens a file. * * @return success value */ success_t Open(const char *fname); /** * Closes the file. * * (No need for a return value since you are only reading the file.) */ void Close() { (void)fclose(f_); f_ = NULL; } /** * Are there more lines left? */ bool MoreLines() { return has_line_; } /** * Returns the currnet line number. */ int line_num() const { return line_num_; } /** * Gets the current line. */ String& Peek() { return line_; } /** * Gets the current line. */ const String& Peek() const { return line_; } /** * Tries to read one line from a file. * * @return true if a line was returned, false if end of file */ bool Gobble(); /** * Prints a formatted error message with line number info. */ COMPILER_PRINTF(2, 3) void Error(const char *msg, ...); private: char *ReadLine_(); }; /** * Simple text tokenizer. * * This tokenizes the input stream. It will identify the general type of * token, and ignore whitespace and different kinds of comments. * * This has the concept of 'next' token which you can peek at, and the * current token. The current token is always initialized to empty at * the very beginning, because you are encouraged to peek ahead one token. * * Example: * * @code * TextTokenizer tokenizer; * tokenizer.Open("file.txt"); * * if (tokenizer->Match("count")) { * if (tokenizer->Match(TextTokenizer::INTEGER)) { * printf("Found number: %d\n", atoi(tokenizer->Current())); * } else { Error(); } * } else { Error(); } * @endcode */ class TextTokenizer { FORBID_ACCIDENTAL_COPIES(TextTokenizer); public: enum TokenType { INVALID = -1, END, PUNCT, IDENTIFIER, STRING, DOUBLE, INTEGER }; enum Features { WANT_NEWLINE = 0x01 }; private: FILE *f_; String next_; TokenType next_type_; String cur_; TokenType cur_type_; const char *comment_start_; const char *ident_extra_; int features_; int line_; public: TextTokenizer() { f_ = NULL; } ~TextTokenizer() { if (unlikely(f_ != NULL)) { (void) fclose(f_); } DEBUG_POISON_PTR(f_); } success_t Open(const char *fname, const char *comment_chars = "", const char *ident_extra = "", int features = 0); const String& Peek() const { return next_; } TokenType PeekType() const { return next_type_; } const String& Current() const { return cur_; } TokenType CurrentType() const { return cur_type_; } void Gobble(); bool MoreTokens() const { return next_type_ != END; } bool Match(const char *exact) { if (next_ == exact) { Gobble(); return true; } else { return false; } } bool MatchNoCase(const char *str) { if (next_.EqualsNoCase(str)) { Gobble(); return true; } else { return false; } } bool MatchInteger() { return MatchType(INTEGER); } bool MatchDouble() { return MatchType(DOUBLE); } bool MatchNumber() { return MatchInteger() || MatchDouble(); } bool MatchIdentifier() { return MatchType(IDENTIFIER); } bool MatchQuasiIdentifier() { return MatchIdentifier() || MatchNumber(); } bool MatchString() { return MatchType(STRING); } bool MatchPunct() { return MatchType(PUNCT); } bool MatchType(TokenType type) { if (next_type_ == type) { Gobble(); return true; } else { return false; } } int line() const { return line_; } COMPILER_PRINTF(2, 3) void Error(const char *msg, ...); private: int GetChar_() { return ::getc(f_); } void Unget_(int c) { ::ungetc(c, f_); } bool IsEOF_() { return ::feof(f_); } char Skip_(ArrayList *token); char NextChar_(ArrayList *token); char NextChar_(); void UndoNextChar_(ArrayList *token); void Error_(const char *msg, const ArrayList& token); bool isident_begin_(int c) const { return isalpha(c) || unlikely(c == '_'); } bool isident_rest_(int c) const { return isalnum(c) || unlikely(c == '_') || (c != 0 && strchr(ident_extra_, c)); } void ScanNumber_(char c, ArrayList *token); void ScanString_(char ending, ArrayList *token); void Scan_(ArrayList *token); }; /** * Helper for writing text fo a file. */ class TextWriter { FORBID_ACCIDENTAL_COPIES(TextWriter); private: FILE *f_; public: /** * Creates an uninitialized text writer (you must initialize it). */ TextWriter() { f_ = NULL; } /** * Automatically closes the file when it gets out of scope; for * best error handling, you should call Close first. * * If you do not explicitly close the file beforehand, this will abort the * program on a write error. */ ~TextWriter() { if (f_) { MUST_PASS(SUCCESS_FROM_C(::fclose(f_))); } DEBUG_POISON_PTR(f_); } /** * Opens a file by name (initializer). * * @return success or failure */ success_t Open(const char *fname) { f_ = ::fopen(fname, "w"); return (unlikely(!f_)) ? SUCCESS_FAIL : SUCCESS_PASS; } /** * Explicitly closes the file. */ success_t Close() { int rv = fclose(f_); f_ = NULL; return unlikely(rv < 0) ? SUCCESS_FAIL : SUCCESS_PASS; } success_t Printf(const char *format, ...); success_t Write(const char *s) { return SUCCESS_FROM_C(fputs(s, f_)); } success_t Write(int i) { return SUCCESS_FROM_C(fprintf(f_, "%d", i)); } success_t Write(unsigned int i) { return SUCCESS_FROM_C(fprintf(f_, "%u", i)); } success_t Write(long i) { return SUCCESS_FROM_C(fprintf(f_, "%ld", i)); } success_t Write(unsigned long i) { return SUCCESS_FROM_C(fprintf(f_, "%lu", i)); } success_t Write(long long i) { return SUCCESS_FROM_C(fprintf(f_, "%lld", i)); } success_t Write(unsigned long long i) { return SUCCESS_FROM_C(fprintf(f_, "%llu", i)); } success_t Write(double d) { return SUCCESS_FROM_C(fprintf(f_, "%.15e", d)); } }; #endif