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   token_(),
00009   kind_()
00010 {}
00011 
00012 std::string parser::charify(char c)
00013 {
00014   if (c == '\a') return "\'\\a\'";
00015   if (c == '\b') return "\'\\b\'";
00016   if (c == '\f') return "\'\\f\'";
00017   if (c == '\n') return "\'\\n\'";
00018   if (c == '\r') return "\'\\r\'";
00019   if (c == '\t') return "\'\\t\'";
00020   if (c == '\v') return "\'\\v\'";
00021   if (c == '\'') return "\'\\'\'";
00022   if (c == '\\') return "\'\\\\\'";
00023 
00024   if (isprint(c))
00025     return std::string("\'") + c + '\'';
00026   else {
00027     std::ostringstream stream;
00028     stream << "'\\x" << std::hex;
00029     stream.fill('0');
00030     stream.width(2);
00031     stream << (std::char_traits<char>::to_int_type(c) & 0xFF) << '\'';
00032     return stream.str();
00033   }
00034 }
00035 
00036 void parser::get_identifier(std::string& identifier)
00037 {
00038   identifier.clear();
00039   char c;
00040   if (not input_.get(c))
00041     return;
00042   if (not isalpha(c))
00043     throw parse_error("syntax error: expected alphabetic, got " + charify(c));
00044   identifier += c;
00045   while (input_.get(c)) {
00046     if (not isalnum(c)) {
00047       input_.unget();
00048       return;
00049     }
00050     identifier += c;
00051   }
00052   return;
00053 }
00054 
00055 /* Parse a token.
00056  * A token can be a keyword, a literal or a symbol.
00057  * TOKEN ::= IDENTIFIER | NUMBER | SYMBOL
00058  * IDENTIIFER ::= ALPHA (ALPHA | DIGIT)*
00059  * NUMBER ::= DIGIT+ ('.' DIGITS+)? ('E' SIGN? DIGITS+)?
00060  * SYMBOL ::= '+' | '-' | '*' | '/' | '%' | '(' | ')' | '='
00061  */
00062 parser::kind parser::get_token(std::string& token)
00063 {
00064   if (not token_.empty())
00065   {
00066     token = token_;
00067     kind result(kind_);
00068     token_.clear();
00069     kind_ = eof;
00070     return result;
00071   }
00072 
00073   char c;
00074   if (not (input_ >> c))
00075     return eof;
00076   if (isalpha(c)) {
00077     input_.unget();
00078     get_identifier(token);
00079     return identifier;
00080   }
00081 
00082   // Get a numeric literal.
00083   token.clear();
00084   if (c == '+' or c == '-' or c == '*' or c == '/' or c == '%' or c == '(' or c == ')' or c == '=') {
00085     token += c;
00086     return kind(c);
00087   }
00088 
00089   if (c < '0' or c > '9') {
00090     input_.unget();
00091     throw parse_error("syntax error: expected digit, got " + charify(c));
00092   }
00093   while (c >= '0' and c <= '9') {
00094     token += c;
00095     if (not input_.get(c))
00096       return number;
00097   }
00098   if (c == '.') {
00099     token += c;
00100     if (not input_.get(c))
00101       throw parse_error("unterminated number: expected digit after the decimal point");
00102     if (c < '0' or c > '9') {
00103       input_.unget();
00104       throw parse_error("syntax error: expected digit after decimal point, got " + charify(c));
00105     }
00106     while (c >= '0' and c <= '9') {
00107       token += c;
00108       if (not input_.get(c))
00109         return number;
00110     }
00111   }
00112   if (c == 'e' or c == 'E') {
00113     token += c;
00114     if (not input_.get(c))
00115       throw parse_error("unterminated number: expected digit in the exponent");
00116     if (c == '-' or c == '+') {
00117       token += c;
00118       if (not input_.get(c))
00119         throw parse_error("unterminated number: expected digit after sign in the exponent");
00120     }
00121     if (c < '0' or c > '9') {
00122       input_.unget();
00123       throw parse_error("syntax error: expected digit in the exponent, got " + charify(c));
00124     }
00125     while (c >= '0' and c <= '9') {
00126       token += c;
00127       if (not input_.get(c))
00128         return number;
00129     }
00130   }
00131   input_.unget();
00132   return number;
00133 }
00134 
00135 bool parser::get_number(std::string const& token, double& result)
00136 {
00137   std::istringstream stream(token);
00138   // If the value overflows or is otherwise invalid, return false.
00139   return (stream >> result);
00140 }
00141 
00142 bool parser::get_expr(double& result)
00143 {
00144   std::string token;
00145   kind k(get_token(token));
00146   if (k == eof)
00147     return false;
00148 
00149   if (k == identifier and token == "var") {
00150     std::string name;
00151     // Define a variable.
00152     k = get_token(name);
00153     if (k != identifier)
00154       throw parse_error("syntax error: expected IDENTIFIER, but got " + name);
00155     k = get_token(token);
00156     if (k != '=')
00157       throw parse_error("syntax error: expected =, but got " + token);
00158     if (not get_add_expr(result))
00159       throw parse_error("syntax error: expected additive-exprssion in assignment");
00160     set_variable(name, result);
00161     return true;
00162   }
00163 
00164   if (k == identifier and token == "quit")
00165     std::exit(0);
00166 
00167   push_back(token, k);
00168   if (not get_add_expr(result))
00169     throw parse_error("syntax error: expected an additive-expression");
00170 
00171   return true;
00172 }
00173 
00174 bool parser::get_add_expr(double& result)
00175 {
00176   if (not get_mul_expr(result))
00177     return false;
00178   std::string token;
00179   while (kind k = get_token(token)) {
00180     if (k != '+' and k != '-') {
00181       push_back(token, k);
00182       return true;
00183     } else {
00184       double right;
00185       if (not get_mul_expr(right))
00186         throw parse_error("syntax error: unterminated expression. Expected a multiplicative-expression after " + token);
00187       if (k == '+')
00188         result += right;
00189       else
00190         result -= right;
00191     }
00192   }
00193   return true;
00194 }
00195 
00196 bool parser::get_mul_expr(double& result)
00197 {
00198   if (not get_unary(result))
00199     return false;
00200   std::string token;
00201   while (kind k = get_token(token)) {
00202     if (k != '*' and k != '/') {
00203       push_back(token, k);
00204       return true;
00205     } else {
00206       double right;
00207       if (not get_unary(right))
00208         throw parse_error("syntax error: unterminated expression. Expected a unary-expression after " + token);
00209       if (k == '*')
00210         result *= right;
00211       else if (right == 0.0)
00212         throw parse_error("division by zero");
00213       else
00214         result /= right;
00215     }
00216   }
00217   return true;
00218 }
00219 
00220 bool parser::get_unary(double& result)
00221 {
00222   std::string token;
00223   if (kind k = get_token(token)) {
00224     if (k == '-') {
00225       if (not get_primary(result))
00226         return false;
00227       result = -result;
00228       return true;
00229     } else if (k == '+') {
00230       return get_primary(result);
00231     } else {
00232       push_back(token, k);
00233       return get_primary(result);
00234     }
00235   }
00236   return false;
00237 }
00238 
00239 bool parser::get_primary(double& result)
00240 {
00241   std::string token;
00242   if (kind k = get_token(token)) {
00243     if (k == '(') {
00244       if (not get_expr(result))
00245         return false;
00246       k = get_token(token);
00247       if (k == eof)
00248         throw parse_error("syntax error: EOF when expecting ')'");
00249       else if (k != ')')
00250         throw parse_error("syntax error: expected ')', but got " + token);
00251       else
00252         return true;
00253     } else if (k == number) {
00254       if (not get_number(token, result))
00255         throw parse_error("Invalid numeric literal: " + token);
00256       return true;
00257     } else if (k == identifier) {
00258       result = get_variable(token);
00259       return true;
00260     } else {
00261       throw parse_error("syntax error: expected a primary, but got " + token);
00262     }
00263   }
00264   return false;
00265 }
00266 
00267 void parse_loop(std::istream& input, std::ostream& output)
00268 {
00269   std::string line;
00270   // No portable way to test whether the console is an interactive terminal
00271   // vs. a non-interactive file. If you have a system-specific way to test,
00272   // output the prompt only for the interactive case.
00273   for (output << "> "; std::getline(input, line); output << "> ") {
00274     std::istringstream input(line);
00275     parser p(input);
00276     try {
00277       double x;
00278       while (p.get_expr(x))
00279         output << x << '\n';
00280     } catch(parse_error const& ex) {
00281       output << ex.what() << '\n';
00282     } catch(std::exception const& ex) {
00283       output << "exception: " << ex.what() << '\n';
00284     }
00285   }
00286 }

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