parse.cpp

Go to the documentation of this file.
00001 #include <sstream>
00002 
00003 #include "parse.hpp"
00004 
00005 parser::parser(std::istream& input)
00006 : input_(input),
00007   ctype_(std::use_facet<std::ctype<char> >(input.getloc()))
00008 {}
00009 
00010 std::string parser::charify(char c)
00011 {
00012   if (c == '\a') return "\'\\a\'";
00013   if (c == '\b') return "\'\\b\'";
00014   if (c == '\f') return "\'\\f\'";
00015   if (c == '\n') return "\'\\n\'";
00016   if (c == '\r') return "\'\\r\'";
00017   if (c == '\t') return "\'\\t\'";
00018   if (c == '\v') return "\'\\v\'";
00019   if (c == '\'') return "\'\\'\'";
00020   if (c == '\\') return "\'\\\\\'";
00021 
00022   if (ctype_.is(ctype_.print, c))
00023     return std::string("\'") + c + '\'';
00024   else {
00025     std::ostringstream stream;
00026     stream << "'\\x" << std::hex;
00027     stream.fill('0');
00028     stream.width(2);
00029     stream << (std::char_traits<char>::to_int_type(c) & 0xFF) << '\'';
00030     return stream.str();
00031   }
00032 }
00033 
00034 /* Parse a floating point number.
00035  * NUMBER ::= SIGN? DIGIT+ ('.' DIGITS+)? ('E' SIGN? DIGITS+)?
00036  */
00037 bool parser::get_number(double& result)
00038 {
00039   std::string token;
00040   char c;
00041   if (not input_.get(c))
00042     return false;
00043   if (c == '+' or c == '-') {
00044     token += c;
00045     if (not input_.get(c))
00046       throw parse_error("unterminated number: expected a digit after the sign");
00047   }
00048   if (c < '0' or c > '9') {
00049     input_.unget();
00050     throw parse_error("syntax error: expected digit, got " + charify(c));
00051   }
00052   while (c >= '0' and c <= '9') {
00053     token += c;
00054     if (not input_.get(c)) {
00055       std::istringstream tmp(token);
00056       // If the value overflows, return false.
00057       return (tmp >> result);
00058     }
00059   }
00060   if (c == '.') {
00061     token += c;
00062     if (not input_.get(c))
00063       throw parse_error("unterminated number: expected digit after the decimal point");
00064     if (c < '0' or c > '9') {
00065       input_.unget();
00066       throw parse_error("syntax error: expected digit after decimal point, got " + charify(c));
00067     }
00068     while (c >= '0' and c <= '9') {
00069       token += c;
00070       if (not input_.get(c)) {
00071         std::istringstream tmp(token);
00072         // If the value overflows or is otherwise invalid, return false.
00073         return (tmp >> result);
00074       }
00075     }
00076   }
00077   if (c == 'e' or c == 'E') {
00078     token += c;
00079     if (not input_.get(c))
00080       throw parse_error("unterminated number: expected digit in the exponent");
00081     if (c == '-' or c == '+') {
00082       token += c;
00083       if (not input_.get(c))
00084         throw parse_error("unterminated number: expected digit after sign in the exponent");
00085     }
00086     if (c < '0' or c > '9') {
00087       input_.unget();
00088       throw parse_error("syntax error: expected digit in the exponent, got " + charify(c));
00089     }
00090     while (c >= '0' and c <= '9') {
00091       token += c;
00092       if (not input_.get(c)) {
00093         std::istringstream tmp(token);
00094         // If the value overflows or is otherwise invalid, return false.
00095         return (tmp >> result);
00096       }
00097     }
00098   }
00099   input_.unget();
00100 
00101   std::istringstream tmp(token);
00102   // If the value overflows or is otherwise invalid, return false.
00103   return (tmp >> result);
00104 }
00105 
00106 /* Parse an expression.
00107  * EXPR ::= MULT_EXPR | EXPR + MULT_EXPR | EXPR - MULT_EXP
00108  */
00109 bool parser::get_expr(double& result)
00110 {
00111   if (not get_mult_expr(result))
00112     return false;
00113   char c;
00114   while (input_ >> c) {
00115     if (c != '+' and c != '-') {
00116       input_.unget();
00117       return true;
00118     } else {
00119       double right;
00120       if (not get_mult_expr(right))
00121         throw parse_error("syntax error: unterminated expression. Expected a multiplicative-exprssion after " + c);
00122       if (c == '+')
00123         result += right;
00124       else
00125         result -= right;
00126     }
00127   }
00128   return true;
00129 }
00130 
00131 /* Parse a multiplicative-expression.
00132  * MULT_EXPR ::= PRIMARY | MULT_EXPR + PRIMARY | MULT_EXPR - PRIMARY
00133  */
00134 bool parser::get_mult_expr(double& result)
00135 {
00136   if (not get_primary(result))
00137     return false;
00138   char c;
00139   while (input_ >> c) {
00140     if (c != '*' and c != '/') {
00141       input_.unget();
00142       return true;
00143     } else {
00144       double right;
00145       if (not get_primary(right))
00146         throw parse_error("syntax error: unterminated expression. Expected a primary after " + c);
00147       if (c == '*')
00148         result *= right;
00149       else if (right == 0.0)
00150         throw parse_error("division by zero");
00151       else
00152         result /= right;
00153     }
00154   }
00155   return true;
00156 }
00157 
00158 /* Parse a primary.
00159  * PRIMARY ::= NUMBER | '(' EXPR ')'
00160  */
00161 bool parser::get_primary(double& result)
00162 {
00163   char c;
00164   if (not (input_ >> c))
00165     // Can't read one character, so must be end-of-file
00166     return false;
00167   else if (c == '(') {
00168     if (not get_expr(result))
00169       return false;
00170     if (not (input_ >> c))
00171       throw parse_error("syntax error: EOF when expecting ')'");
00172     else if (c != ')')
00173       throw parse_error("syntax error: expected ')', but got " + charify(c));
00174     else
00175       return true;
00176   } else {
00177     input_.unget();
00178     return get_number(result);
00179   }
00180 }
00181 
00182 void parse_loop(std::istream& input, std::ostream& output)
00183 {
00184   std::string line;
00185   // No portable way to test whether the console is an interactive terminal
00186   // vs. a non-interactive file. If you have a system-specific way to test,
00187   // output the prompt only for the interactive case.
00188   for (output << "> "; std::getline(input, line); output << "> ") {
00189     std::istringstream input(line);
00190     parser p(input);
00191     try {
00192       double x;
00193       while (p.get_expr(x))
00194         output << x << '\n';
00195     } catch(parse_error const& ex) {
00196       output << ex.what() << '\n';
00197     } catch(std::exception const& ex) {
00198       output << "exception: " << ex.what() << '\n';
00199     }
00200   }
00201 }

Generated on Sun Nov 30 10:04:18 2008 for Calculator by  doxygen 1.5.3