The following examples contain the complete listing of our program, by file. They are listed here for reference: Example | File | Example 27-3 | The ch_type.h file | Example 27-4 | The ch_type.cpp file | Example 27-5 | The token.h file | Example 27-6 | The token.cpp file | Example 27-7 | The stat.cpp file | Example 27-8 | Unix Makefile for CC (Generic Unix) | Example 27-9 | Unix Makefile for g++ | Example 27-10 | Borland-C++ Makefile | Example 27-11 | Microsoft Visual C++ Makefile | Example 27-3. stat/ch_type.h /******************************************************** * char_type -- Character type class * * * * Member functions: * * type -- returns the type of a character. * * (Limited to simple types) * * is(ch, char_type) -- check to see if ch is * * a member of the given type. * * (Works for derrived types as well.) * ********************************************************/ class char_type { public: enum CHAR_TYPE { C_EOF, // End of file character C_WHITE, // Whitespace or control character C_NEWLINE, // A newline character C_ALPHA, // A Letter (includes _) C_DIGIT, // A Number C_OPERATOR, // Random operator C_SLASH, // The character '/' C_L_PAREN, // The character '(' C_R_PAREN, // The character ')' C_L_CURLY, // The character '{' C_R_CURLY, // The character '}' C_SINGLE, // The character '\'' C_DOUBLE, // The character '"' // End of simple types, more complex, derrived types follow C_HEX_DIGIT,// Hexidecimal digit C_ALPHA_NUMERIC // Alpha numeric }; private: static enum CHAR_TYPE type_info[256]; // Information on each character // Fill in a range of type info stuff void fill_range(int start, int end, CHAR_TYPE type); public: char_type( ); // Initialize the data // ~char_type -- default destructor // Returns true if character is a given type int is(int ch, CHAR_TYPE kind); CHAR_TYPE type(int ch); }; Example 27-4. stat/ch_type.cpp /******************************************************** * ch_type package * * * * The class ch_type is used to tell the type of * * various characters. * * * * The main member functions are: * * is -- True if the character is the indicated * * type. * * type -- Return type of character. * ********************************************************/ #include <iostream> #include <assert.h> #include "ch_type.h" // Define the type information array char_type::CHAR_TYPE char_type::type_info[256]; /******************************************************** * fill_range -- fill in a range of types for the * * character type class * * * * Parameters * * start, end -- range of items to fill in * * type -- type to use for filling * ********************************************************/ void char_type::fill_range(int start, int end, CHAR_TYPE type) { int cur_ch; for (cur_ch = start; cur_ch <= end; ++cur_ch) { assert(cur_ch >= 0); assert(cur_ch < sizeof(type_info)/sizeof(type_info[0])); type_info[cur_ch] = type; } } /********************************************************* * char_type::char_type -- initialize the char type table* *********************************************************/ char_type::char_type( ) { fill_range(0, 255, C_WHITE); fill_range('A', 'Z', C_ALPHA); fill_range('a', 'z', C_ALPHA); type_info['_'] = C_ALPHA; fill_range('0', '9', C_DIGIT); type_info['!'] = C_OPERATOR; type_info['#'] = C_OPERATOR; type_info['$'] = C_OPERATOR; type_info['%'] = C_OPERATOR; type_info['^'] = C_OPERATOR; type_info['&'] = C_OPERATOR; type_info['*'] = C_OPERATOR; type_info['-'] = C_OPERATOR; type_info['+'] = C_OPERATOR; type_info['='] = C_OPERATOR; type_info[''] = C_OPERATOR; type_info['~'] = C_OPERATOR; type_info[','] = C_OPERATOR; type_info[':'] = C_OPERATOR; type_info['?'] = C_OPERATOR; type_info['.'] = C_OPERATOR; type_info['<'] = C_OPERATOR; type_info['>'] = C_OPERATOR; type_info['/'] = C_SLASH; type_info['\n'] = C_NEWLINE; type_info['('] = C_L_PAREN; type_info[')'] = C_R_PAREN; type_info['{'] = C_L_CURLY; type_info['}'] = C_R_CURLY; type_info['"'] = C_DOUBLE; type_info['\''] = C_SINGLE; } int char_type::is(int ch, CHAR_TYPE kind) { if (ch == EOF) return (kind == C_EOF); switch (kind) { case C_HEX_DIGIT: assert(ch >= 0); assert(ch < sizeof(type_info)/sizeof(type_info[0])); if (type_info[ch] == C_DIGIT) return (1); if ((ch >= 'A') && (ch <= 'F')) return (1); if ((ch >= 'a') && (ch <= 'f')) return (1); return (0); case C_ALPHA_NUMERIC: assert(ch >= 0); assert(ch < sizeof(type_info)/sizeof(type_info[0])); return ((type_info[ch] == C_ALPHA) (type_info[ch] == C_DIGIT)); default: assert(ch >= 0); assert(ch < sizeof(type_info)/sizeof(type_info[0])); return (type_info[ch] == kind); } }; char_type::CHAR_TYPE char_type::type(const int ch) { if (ch == EOF) return (C_EOF); assert(ch >= 0); assert(ch < sizeof(type_info)/sizeof(type_info[0])); return (type_info[ch]); } Example 27-5. stat/token.h #include <string> #include <iostream> /******************************************************** * token -- token handling module * * * * Functions: * * next_token -- get the next token from the input * ********************************************************/ /* * A list of tokens * Note, how this list is used depends on defining the macro T. * This macro is used for defining the tokens types themselves * as well as the string version of the tokens. */ #define TOKEN_LIST \ T(T_NUMBER), /* Simple number (floating point or integer) */ \ T(T_STRING), /* String or character constant */ \ T(T_COMMENT), /* Comment */ \ T(T_NEWLINE), /* Newline character */ \ T(T_OPERATOR), /* Arithmetic operator */ \ T(T_L_PAREN), /* Character "(" */ \ T(T_R_PAREN), /* Character ")" */ \ T(T_L_CURLY), /* Character "{" */ \ T(T_R_CURLY), /* Character "}" */ \ T(T_ID), /* Identifier */ \ T(T_EOF) /* End of File */ /* * Define the enumerated list of tokens. * This makes use of a trick using the T macro * and our TOKEN_LIST */ #define T(x) x // Define T( ) as the name enum TOKEN_TYPE { TOKEN_LIST }; #undef T // Remove old temporary macro // A list of the names of the tokens extern const char *const TOKEN_NAMES[]; /******************************************************** * input_file -- data from the input file * * * * The current two characters are store in * * cur_char and next_char * * * * The member function read_char moves eveyone up * * one character. * * * * The line is buffered and output everytime a newline * * is passed. * ********************************************************/ class input_file: public std::ifstream { private: std::string line; // Current line public: int cur_char; // Current character (can be EOF) int next_char; // Next character (can be EOF) /* * Initialize the input file and read the first 2 * characters. */ input_file(const char *const name) : std::ifstream(name), line("") { if (bad( )) return; cur_char = get( ); next_char = get( ); } /* * Write the line to the screen */ void flush_line( ) { std::cout << line; std::cout.flush( ); line = ""; } /* * Advance one character */ void read_char( ) { line += cur_char; cur_char = next_char; next_char = get( ); } }; /******************************************************** * token class * * * * Reads the next token in the input stream * * and returns its type. * ********************************************************/ class token { private: // True if we are in the middle of a comment int in_comment; // True if we need to read a character // (This hack is designed to get the new lines right) int need_to_read_one; // Read a /* */ style comment TOKEN_TYPE read_comment(input_file& in_file); public: token( ) { in_comment = false; need_to_read_one = 0; } // Return the next token in the stream TOKEN_TYPE next_token(input_file& in_file); }; Example 27-6. stat/token.cpp /******************************************************** * token -- token handling module * * * * Functions: * * next_token -- get the next token from the input * ********************************************************/ #include <fstream> #include <cstdlib> #include "ch_type.h" #include "token.h" /* * Define the token name list * This makes use of a trick using the T macro * and our TOKEN_LIST */ #define T(x) #x // Define x as a string const char *const TOKEN_NAMES[] = { TOKEN_LIST }; #undef T // Remove old temporary macro static char_type char_type; // Character type information /******************************************************** * read_comment -- read in a comment * * * * Parameters * * in_file -- file to read * * * * Returns * * Token read. Can be a T_COMMENT or T_NEW_LINE * * depending on what we read. * * * * Multi-line comments are split into multiple * * tokens. * ********************************************************/ TOKEN_TYPE token::read_comment(input_file& in_file) { if (in_file.cur_char == '\n') { in_file.read_char( ); return (T_NEWLINE); } while (true) { in_comment = true; if (in_file.cur_char == EOF) { std::cerr << "Error: EOF inside comment\n"; return (T_EOF); } if (in_file.cur_char == '\n') return (T_COMMENT); if ((in_file.cur_char == '*') && (in_file.next_char == '/')) { in_comment = false; // Skip past the ending */ in_file.read_char( ); in_file.read_char( ); return (T_COMMENT); } in_file.read_char( ); } } /******************************************************** * next_token -- read the next token in an input stream * * * * Parameters * * in_file -- file to read * * * * Returns * * next token * ********************************************************/ TOKEN_TYPE token::next_token(input_file& in_file) { if (need_to_read_one) in_file.read_char( ); need_to_read_one = 0; if (in_comment) return (read_comment(in_file)); while (char_type.is(in_file.cur_char, char_type::C_WHITE)) { in_file.read_char( ); } if (in_file.cur_char == EOF) return (T_EOF); switch (char_type.type(in_file.cur_char)) { case char_type::C_NEWLINE: in_file.read_char( ); return (T_NEWLINE); case char_type::C_ALPHA: while (char_type.is(in_file.cur_char, char_type::C_ALPHA_NUMERIC)) in_file.read_char( ); return (T_ID); case char_type::C_DIGIT: in_file.read_char( ); if ((in_file.cur_char == 'X') (in_file.cur_char == 'x')) { in_file.read_char( ); while (char_type.is(in_file.cur_char, char_type::C_HEX_DIGIT)) { in_file.read_char( ); } return (T_NUMBER); } while (char_type.is(in_file.cur_char, char_type::C_DIGIT)) in_file.read_char( ); return (T_NUMBER); case char_type::C_SLASH: // Check for /* characters if (in_file.next_char == '*') { return (read_comment(in_file)); } // Now check for double slash comments if (in_file.next_char == '/') { while (true) { // Comment starting with // and ending with EOF is legal if (in_file.cur_char == EOF) return (T_COMMENT); if (in_file.cur_char == '\n') return (T_COMMENT); in_file.read_char( ); } } // Fall through case char_type::C_OPERATOR: in_file.read_char( ); return (T_OPERATOR); case char_type::C_L_PAREN: in_file.read_char( ); return (T_L_PAREN); case char_type::C_R_PAREN: in_file.read_char( ); return (T_R_PAREN); case char_type::C_L_CURLY: in_file.read_char( ); return (T_L_CURLY); case char_type::C_R_CURLY: in_file.read_char( ); return (T_R_CURLY); case char_type::C_DOUBLE: while (true) { in_file.read_char( ); // Check for end of string if (in_file.cur_char == '"') break; // Escape character, then skip the next character if (in_file.cur_char == '\') in_file.read_char( ); } in_file.read_char( ); return (T_STRING); case char_type::C_SINGLE: while (true) { in_file.read_char( ); // Check for end of character if (in_file.cur_char == '\'') break; // Escape character, then skip the next character if (in_file.cur_char == '\') in_file.read_char( ); } in_file.read_char( ); return (T_STRING); default: assert("Internal error: Very strange character" != 0); } assert("Internal error: We should never get here" != 0); return (T_EOF); // Should never get here either } Example 27-7. stat/stat.cpp /******************************************************** * stat * * Produce statistics about a program * * * * Usage: * * stat [options] <file-list> * * * ********************************************************/ #include <iostream> #include <fstream> #include <iomanip> #include <cstdlib> #include <cstring> #include <assert.h> #include "ch_type.h" #include "token.h" /******************************************************** * stat -- general purpose statistic * * * * Member functions: * * take_token -- receives token and uses it to * * compute statistic * * line_start -- output stat at the beginning of * * a line. * * eof -- output stat at the end of the file * ********************************************************/ class a_stat { public: virtual void take_token(TOKEN_TYPE token) = 0; virtual void line_start( ) {}; virtual void eof( ) {}; // Default constructor // Default destructor // Copy constructor defaults as well (probably not used) }; /******************************************************** * line_counter -- handle line number / line count * * stat. * * * * Counts the number of T_NEW_LINE tokens seen and * * output the current line number at the beginning * * of the line. * * * * At EOF it will output the total number of lines * ********************************************************/ class line_counter: public a_stat { private: int cur_line; // Line number for the current line public: // Initialize the line counter -- to zero line_counter( ) { cur_line = 0; }; // Default destrctor // Default copy constructor (probably never called) // Consume tokens, count the number of new line tokens void take_token(TOKEN_TYPE token) { if (token == T_NEWLINE) ++cur_line; } // Output start of line statistics // namely the current line number void line_start( ) { std::cout << std::setw(4) << cur_line << ' ' << std::setw(0); } // Output eof statistics // namely the total number of lines void eof( ) { std::cout << "Total number of lines: " << cur_line << '\n'; } }; /******************************************************** * paren_count -- count the nesting level of ( ) * * * * Counts the number of T_L_PAREN vs T_R_PAREN tokens * * and writes the current nesting level at the beginning* * of each line. * * * * Also keeps track of the maximum nesting level. * ********************************************************/ class paren_counter: public a_stat { private: int cur_level; // Current nesting level int max_level; // Maximum nesting level public: // Initialize the counter paren_counter( ) { cur_level = 0; max_level = 0; }; // Default destructor // Default copy constructor (probably never called) // Consume tokens, count the nesting of ( ) void take_token(TOKEN_TYPE token) { switch (token) { case T_L_PAREN: ++cur_level; if (cur_level > max_level) max_level = cur_level; break; case T_R_PAREN: --cur_level; break; default: // Ignore break; } } // Output start of line statistics // namely the current line number void line_start( ) { std::cout.setf(std::ios::left); std::cout.width(2); std::cout << '(' << cur_level << ' '; std::cout.unsetf(std::ios::left); std::cout.width( ); } // Output eof statistics // namely the total number of lines void eof( ) { std::cout << "Maximum nesting of ( ) : " << max_level << '\n'; } }; /******************************************************** * brace_counter -- count the nesting level of {} * * * * Counts the number of T_L_CURLY vs T_R_CURLY tokens * * and writes the current nesting level at the beginning* * of each line. * * * * Also keeps track of the maximum nesting level. * * * * Note: brace_counter and paren_counter should * * probably be combined. * ********************************************************/ class brace_counter: public a_stat { private: int cur_level; // Current nesting level int max_level; // Maximum nesting level public: // Initialize the counter brace_counter( ) { cur_level = 0; max_level = 0; }; // Default destructor // Default copy constructor (probably never called) // Consume tokens, count the nesting of ( ) void take_token(TOKEN_TYPE token) { switch (token) { case T_L_CURLY: ++cur_level; if (cur_level > max_level) max_level = cur_level; break; case T_R_CURLY: --cur_level; break; default: // Ignore break; } } // Output start of line statistics // namely the current line number void line_start( ) { std::cout.setf(std::ios::left); std::cout.width(2); std::cout << '{' << cur_level << ' '; std::cout.unsetf(std::ios::left); std::cout.width( ); } // Output eof statistics // namely the total number of lines void eof( ) { std::cout << "Maximum nesting of {} : " << max_level << '\n'; } }; /******************************************************** * comment_counter -- count the number of lines * * with and without comments. * * * * Outputs nothing at the beginning of each line, but * * will output a ratio at the end of file * * * * Note: This class makes use of two bits: * * CF_COMMENT -- a comment was seen * * CF_CODE -- code was seen * * to collect statistics. * * * * These are combined to form an index into the counter * * array so the value of these two bits is very * * important. * ********************************************************/ static const int CF_COMMENT = (1<<0); // Line contains comment static const int CF_CODE = (1<<1); // Line contains code // These bits are combined to form the statistics // // 0 -- [0] Blank line // CF_COMMENT -- [1] Comment only line // CF_CODE -- [2] Code only line // CF_COMMENTCF_CODE -- [3] Comments and code on this line class comment_counter: public a_stat { private: int counters[4]; // Count of various types of stats. int flags; // Flags for the current line public: // Initialize the counters comment_counter( ) { memset(counters, ' /******************************************************** * stat * * Produce statistics about a program * * * * Usage: * * stat [options] <file-list> * * * ********************************************************/ #include <iostream> #include <fstream> #include <iomanip> #include <cstdlib> #include <cstring> #include <assert.h> #include "ch_type.h" #include "token.h" /******************************************************** * stat -- general purpose statistic * * * * Member functions: * * take_token -- receives token and uses it to * * compute statistic * * line_start -- output stat at the beginning of * * a line. * * eof -- output stat at the end of the file * ********************************************************/ class a_stat { public: virtual void take_token(TOKEN_TYPE token) = 0; virtual void line_start( ) {}; virtual void eof( ) {}; // Default constructor // Default destructor // Copy constructor defaults as well (probably not used) }; /******************************************************** * line_counter -- handle line number / line count * * stat. * * * * Counts the number of T_NEW_LINE tokens seen and * * output the current line number at the beginning * * of the line. * * * * At EOF it will output the total number of lines * ********************************************************/ class line_counter: public a_stat { private: int cur_line; // Line number for the current line public: // Initialize the line counter -- to zero line_counter( ) { cur_line = 0; }; // Default destrctor // Default copy constructor (probably never called) // Consume tokens, count the number of new line tokens void take_token(TOKEN_TYPE token) { if (token == T_NEWLINE) ++cur_line; } // Output start of line statistics // namely the current line number void line_start( ) { std::cout << std::setw(4) << cur_line << ' ' << std::setw(0); } // Output eof statistics // namely the total number of lines void eof( ) { std::cout << "Total number of lines: " << cur_line << '\n'; } }; /******************************************************** * paren_count -- count the nesting level of ( ) * * * * Counts the number of T_L_PAREN vs T_R_PAREN tokens * * and writes the current nesting level at the beginning* * of each line. * * * * Also keeps track of the maximum nesting level. * ********************************************************/ class paren_counter: public a_stat { private: int cur_level; // Current nesting level int max_level; // Maximum nesting level public: // Initialize the counter paren_counter( ) { cur_level = 0; max_level = 0; }; // Default destructor // Default copy constructor (probably never called) // Consume tokens, count the nesting of ( ) void take_token(TOKEN_TYPE token) { switch (token) { case T_L_PAREN: ++cur_level; if (cur_level > max_level) max_level = cur_level; break; case T_R_PAREN: --cur_level; break; default: // Ignore break; } } // Output start of line statistics // namely the current line number void line_start( ) { std::cout.setf(std::ios::left); std::cout.width(2); std::cout << '(' << cur_level << ' '; std::cout.unsetf(std::ios::left); std::cout.width( ); } // Output eof statistics // namely the total number of lines void eof( ) { std::cout << "Maximum nesting of ( ) : " << max_level << '\n'; } }; /******************************************************** * brace_counter -- count the nesting level of {} * * * * Counts the number of T_L_CURLY vs T_R_CURLY tokens * * and writes the current nesting level at the beginning* * of each line. * * * * Also keeps track of the maximum nesting level. * * * * Note: brace_counter and paren_counter should * * probably be combined. * ********************************************************/ class brace_counter: public a_stat { private: int cur_level; // Current nesting level int max_level; // Maximum nesting level public: // Initialize the counter brace_counter( ) { cur_level = 0; max_level = 0; }; // Default destructor // Default copy constructor (probably never called) // Consume tokens, count the nesting of ( ) void take_token(TOKEN_TYPE token) { switch (token) { case T_L_CURLY: ++cur_level; if (cur_level > max_level) max_level = cur_level; break; case T_R_CURLY: --cur_level; break; default: // Ignore break; } } // Output start of line statistics // namely the current line number void line_start( ) { std::cout.setf(std::ios::left); std::cout.width(2); std::cout << '{' << cur_level << ' '; std::cout.unsetf(std::ios::left); std::cout.width( ); } // Output eof statistics // namely the total number of lines void eof( ) { std::cout << "Maximum nesting of {} : " << max_level << '\n'; } }; /******************************************************** * comment_counter -- count the number of lines * * with and without comments. * * * * Outputs nothing at the beginning of each line, but * * will output a ratio at the end of file * * * * Note: This class makes use of two bits: * * CF_COMMENT -- a comment was seen * * CF_CODE -- code was seen * * to collect statistics. * * * * These are combined to form an index into the counter * * array so the value of these two bits is very * * important. * ********************************************************/ static const int CF_COMMENT = (1<<0); // Line contains comment static const int CF_CODE = (1<<1); // Line contains code // These bits are combined to form the statistics // // 0 -- [0] Blank line // CF_COMMENT -- [1] Comment only line // CF_CODE -- [2] Code only line // CF_COMMENTCF_CODE -- [3] Comments and code on this line class comment_counter: public a_stat { private: int counters[4]; // Count of various types of stats. int flags; // Flags for the current line public: // Initialize the counters comment_counter( ) { memset (counters, '\0', sizeof(counters)); flags = 0; }; // Default destructor // Default copy constructor (probably never called) // Consume tokens, count the nesting of ( ) void take_token(TOKEN_TYPE token) { switch (token) { case T_COMMENT: flags = CF_COMMENT; break; default: flags = CF_CODE; break; case T_NEWLINE: assert(flags >= 0); assert(flags < sizeof(counters)/sizeof(counters[0])); ++counters[flags]; flags = 0; break; } } // void line_start( ) -- defaults to base // Output eof statistics // namely the total number of lines void eof( ) { std::cout << "Number of blank lines ................." << counters[0] << '\n'; std::cout << "Number of comment only lines .........." << counters[1] << '\n'; std::cout << "Number of code only lines ............." << counters[2] << '\n'; std::cout << "Number of lines with code and comments " << counters[3] << '\n'; std::cout.setf(std::ios::fixed); std::cout.precision(1); std::cout << "Comment to code ratio " << float(counters[1] + counters[3]) / float(counters[2] + counters[3]) * 100.0 << "%\n"; } }; static line_counter line_count; // Counter of lines static paren_counter paren_count; // Counter of ( ) levels static brace_counter brace_count; // Counter of {} levels static comment_counter comment_count; // Counter of comment info // A list of the statistics we are collecting static a_stat *stat_list[] = { &line_count, &paren_count, &brace_count, &comment_count, NULL }; /******************************************************** * do_file -- process a single file * * * * Parameters * * name -- the name of the file to process * ********************************************************/ static void do_file(const char *const name) { input_file in_file(name); // File to read token token; // Token reader/parser TOKEN_TYPE cur_token; // Current token type class a_stat **cur_stat; // Pointer to stat for collection/writing if (in_file.bad( )) { std::cerr << "Error: Could not open file " << name << " for reading\n"; return; } while (true) { cur_token = token.next_token(in_file); for (cur_stat = stat_list; *cur_stat != NULL; ++cur_stat) (*cur_stat)->take_token(cur_token); #ifdef DEBUG assert(cur_token >= 0); assert(cur_token < sizeof(TOKEN_NAMES)/sizeof(TOKEN_NAMES[0])); std::cout << " " << TOKEN_NAMES[cur_token] << '\n'; #endif /* DEBUG */ switch (cur_token) { case T_NEWLINE: for (cur_stat = stat_list; *cur_stat != NULL; ++cur_stat) (*cur_stat)->line_start( ); in_file.flush_line( ); break; case T_EOF: for (cur_stat = stat_list; *cur_stat != NULL; ++cur_stat) (*cur_stat)->eof( ); return; default: // Do nothing break; } } } int main(int argc, char *argv[]) { char *prog_name = argv[0]; // Name of the program if (argc == 1) { std::cerr << "Usage is " << prog_name << "[options] <file-list>\n"; exit (8); } for (/* argc set */; argc > 1; --argc) { do_file(argv[1]); ++argv; } return (0); } ', sizeof(counters)); flags = 0; }; // Default destructor // Default copy constructor (probably never called) // Consume tokens, count the nesting of ( ) void take_token(TOKEN_TYPE token) { switch (token) { case T_COMMENT: flags = CF_COMMENT; break; default: flags = CF_CODE; break; case T_NEWLINE: assert(flags >= 0); assert(flags < sizeof(counters)/sizeof(counters[0])); ++counters[flags]; flags = 0; break; } } // void line_start( ) -- defaults to base // Output eof statistics // namely the total number of lines void eof( ) { std::cout << "Number of blank lines ................." << counters[0] << '\n'; std::cout << "Number of comment only lines .........." << counters[1] << '\n'; std::cout << "Number of code only lines ............." << counters[2] << '\n'; std::cout << "Number of lines with code and comments " << counters[3] << '\n'; std::cout.setf(std::ios::fixed); std::cout.precision(1); std::cout << "Comment to code ratio " << float(counters[1] + counters[3]) / float(counters[2] + counters[3]) * 100.0 << "%\n"; } }; static line_counter line_count; // Counter of lines static paren_counter paren_count; // Counter of ( ) levels static brace_counter brace_count; // Counter of {} levels static comment_counter comment_count; // Counter of comment info // A list of the statistics we are collecting static a_stat *stat_list[] = { &line_count, &paren_count, &brace_count, &comment_count, NULL }; /******************************************************** * do_file -- process a single file * * * * Parameters * * name -- the name of the file to process * ********************************************************/ static void do_file(const char *const name) { input_file in_file(name); // File to read token token; // Token reader/parser TOKEN_TYPE cur_token; // Current token type class a_stat **cur_stat; // Pointer to stat for collection/writing if (in_file.bad( )) { std::cerr << "Error: Could not open file " << name << " for reading\n"; return; } while (true) { cur_token = token.next_token(in_file); for (cur_stat = stat_list; *cur_stat != NULL; ++cur_stat) (*cur_stat)->take_token(cur_token); #ifdef DEBUG assert(cur_token >= 0); assert(cur_token < sizeof(TOKEN_NAMES)/sizeof(TOKEN_NAMES[0])); std::cout << " " << TOKEN_NAMES[cur_token] << '\n'; #endif /* DEBUG */ switch (cur_token) { case T_NEWLINE: for (cur_stat = stat_list; *cur_stat != NULL; ++cur_stat) (*cur_stat)->line_start( ); in_file.flush_line( ); break; case T_EOF: for (cur_stat = stat_list; *cur_stat != NULL; ++cur_stat) (*cur_stat)->eof( ); return; default: // Do nothing break; } } } int main(int argc, char *argv[]) { char *prog_name = argv[0]; // Name of the program if (argc == 1) { std::cerr << "Usage is " << prog_name << "[options] <file-list>\n"; exit (8); } for (/* argc set */; argc > 1; --argc) { do_file(argv[1]); ++argv; } return (0); } Example 27-8. stat/makefile.unx # # Makefile for many Unix compilers using the # "standard" command name CC # CC=CC CFLAGS=-g OBJS= stat.o ch_type.o token.o all: stat.out stat stat.out: stat stat ../calc3/calc3.cpp >stat.out stat: $(OBJS) $(CC) $(CCFLAGS) -o stat $(OBJS) stat.o: stat.cpp token.h $(CC) $(CCFLAGS) -c stat.cpp ch_type.o: ch_type.cpp ch_type.h $(CC) $(CCFLAGS) -c ch_type.cpp token.o: token.cpp token.h ch_type.h $(CC) $(CCFLAGS) -c token.cpp clean: rm stat stat.o ch_type.o token.o Example 27-9. stat/makefile.gnu # # Makefile for the Free Software Foundations g++ compiler # CC=g++ CCFLAGS=-g -Wall OBJS= stat.o ch_type.o token.o all: stat.out stat stat.out: stat stat ../calc3/calc3.cpp >stat.out stat: $(OBJS) $(CC) $(CCFLAGS) -o stat $(OBJS) stat.o: stat.cpp token.h $(CC) $(CCFLAGS) -c stat.cpp ch_type.o: ch_type.cpp ch_type.h $(CC) $(CCFLAGS) -c ch_type.cpp token.o: token.cpp token.h ch_type.h $(CC) $(CCFLAGS) -c token.cpp clean: rm stat stat.o ch_type.o token.o Example 27-10. stat/makefile.bcc # # Makefile for Borland's Borland-C++ compiler # CC=bcc32 # # Flags # -N -- Check for stack overflow # -v -- Enable debugging # -w -- Turn on all warnings # -tWC -- Console application # CFLAGS=-N -v -w -tWC OBJS= stat.obj ch_type.obj token.obj all: stat.out stat.exe stat.out: stat.exe stat ..\calc3\calc3.cpp >stat.out stat.exe: $(OBJS) $(CC) $(CCFLAGS) -estat $(OBJS) stat.obj: stat.cpp token.h $(CC) $(CCFLAGS) -c stat.cpp ch_type.obj: ch_type.cpp ch_type.h $(CC) $(CCFLAGS) -c ch_type.cpp token.obj: token.cpp token.h ch_type.h $(CC) $(CCFLAGS) -c token.cpp clean: erase stat.exe stat.obj ch_type.obj token.obj Example 27-11. stat/makefile.msc # # Makefile for Microsoft Visual C++ # CC=cl # # Flags # AL -- Compile for large model # Zi -- Enable debugging # W1 -- Turn on warnings # CFLAGS=/AL /Zi /W1 OBJS= stat.obj ch_type.obj token.obj all: stat.out stat.exe stat.out: stat.exe stat ..\calc3\calc3.cpp >stat.out stat.exe: $(OBJS) $(CC) $(CCFLAGS) $(OBJS) stat.obj: stat.cpp token.h $(CC) $(CCFLAGS) -c stat.cpp ch_type.obj: ch_type.cpp ch_type.h $(CC) $(CCFLAGS) -c ch_type.cpp token.obj: token.cpp token.h ch_type.h $(CC) $(CCFLAGS) -c token.cpp clean: erase stat.exe stat.obj ch_type.obj token.obj |