parse.cpp

Go to the documentation of this file.
00001 #include <cstdlib>
00002 #include <iterator>
00003 #include <sstream>
00004 
00005 #include "calc_error.hpp"
00006 #include "node.hpp"
00007 #include "parse.hpp"
00008 #include "variables.hpp"
00009 
00010 parser::parser(std::istream& input)
00011 : input_(input),
00012   ctype_(std::use_facet<std::ctype<char> >(input.getloc())),
00013   token_(),
00014   kind_()
00015 {}
00016 
00017 std::string parser::charify(char c)
00018 {
00019   if (c == '\a') return "\'\\a\'";
00020   if (c == '\b') return "\'\\b\'";
00021   if (c == '\f') return "\'\\f\'";
00022   if (c == '\n') return "\'\\n\'";
00023   if (c == '\r') return "\'\\r\'";
00024   if (c == '\t') return "\'\\t\'";
00025   if (c == '\v') return "\'\\v\'";
00026   if (c == '\'') return "\'\\'\'";
00027   if (c == '\\') return "\'\\\\\'";
00028 
00029   if (isprint(c))
00030     return std::string("\'") + c + '\'';
00031   else {
00032     std::ostringstream stream;
00033     stream << "'\\x" << std::hex;
00034     stream.fill('0');
00035     stream.width(2);
00036     stream << (std::char_traits<char>::to_int_type(c) & 0xFF) << '\'';
00037     return stream.str();
00038   }
00039 }
00040 
00041 void parser::get_identifier(std::string& identifier)
00042 {
00043   identifier.clear();
00044   char c;
00045   if (not input_.get(c))
00046     return;
00047   if (not isalpha(c))
00048     throw syntax_error("expected alphabetic, got " + charify(c));
00049   identifier += c;
00050   while (input_.get(c)) {
00051     if (not isalnum(c)) {
00052       input_.unget();
00053       return;
00054     }
00055     identifier += c;
00056   }
00057   return;
00058 }
00059 
00060 void parser::push_back(std::string const& token, kind k)
00061 {
00062   kind_ = k;
00063   if (kind_ == eof)
00064     token_ = "end of line";
00065   else
00066     token_ = token;
00067 }
00068 
00069 parser::kind parser::get_token(std::string& token)
00070 {
00071   if (not token_.empty())
00072   {
00073     kind result(kind_);
00074     token = token_;
00075 
00076     token_.clear();
00077     kind_ = eof;
00078 
00079     return result;
00080   }
00081 
00082   char c;
00083   if (not (input_ >> c)) {
00084     token = "end of line";
00085     return eof;
00086   }
00087   if (isalpha(c)) {
00088     input_.unget();
00089     get_identifier(token);
00090     return identifier;
00091   }
00092 
00093   // Get a numeric literal.
00094   token.clear();
00095   if (c == '+' or c == '-' or c == '*' or c == '/' or c == '%' or c == '(' or c == ')' or c == '=' or c == ',') {
00096     token += c;
00097     return kind(c);
00098   }
00099 
00100   if (c < '0' or c > '9') {
00101     input_.unget();
00102     throw syntax_error("expected digit, got " + charify(c));
00103   }
00104   while (c >= '0' and c <= '9') {
00105     token += c;
00106     if (not input_.get(c))
00107       return number;
00108   }
00109   if (c == '.') {
00110     token += c;
00111     if (not input_.get(c))
00112       throw syntax_error("unterminated number: expected digit after the decimal point");
00113     if (c < '0' or c > '9') {
00114       input_.unget();
00115       throw syntax_error("expected digit after decimal point, got " + charify(c));
00116     }
00117     while (c >= '0' and c <= '9') {
00118       token += c;
00119       if (not input_.get(c))
00120         return number;
00121     }
00122   }
00123   if (c == 'e' or c == 'E') {
00124     token += c;
00125     if (not input_.get(c))
00126       throw syntax_error("unterminated number: expected digit in the exponent");
00127     if (c == '-' or c == '+') {
00128       token += c;
00129       if (not input_.get(c))
00130         throw syntax_error("unterminated number: expected digit after sign in the exponent");
00131     }
00132     if (c < '0' or c > '9') {
00133       input_.unget();
00134       throw syntax_error("expected digit in the exponent, got " + charify(c));
00135     }
00136     while (c >= '0' and c <= '9') {
00137       token += c;
00138       if (not input_.get(c))
00139         return number;
00140     }
00141   }
00142   input_.unget();
00143   return number;
00144 }
00145 
00146 bool parser::get_number(std::string const& token, node& result)
00147 {
00148   std::istringstream stream(token);
00149   // If the value overflows or is otherwise invalid, return false.
00150   double value;
00151   if (not (stream >> value))
00152     return false;
00153   result = node(value);
00154   return true;
00155 }
00156 
00157 void parser::get_definition(std::string& name, identifier_list& parameters, node& definition)
00158 {
00159   // Define a variable.
00160   kind k = get_token(name);
00161   if (k != identifier)
00162     throw syntax_error("expected IDENTIFIER, got " + name);
00163 
00164   std::string token;
00165   k = get_token(token);
00166   if (k == '(') {
00167     get_namelist(std::back_inserter(parameters));
00168     k = get_token(token);
00169   }
00170 
00171   if (k != '=')
00172     throw syntax_error("expected = in definition, got " + token);
00173 
00174   if (not get_expr(definition))
00175     throw syntax_error("expected exprssion in assignment");
00176 }
00177 
00178 bool parser::get_statement(std::ostream& output)
00179 {
00180   std::string token;
00181   kind k(get_token(token));
00182   if (k == eof)
00183     return false;
00184 
00185   if (k == identifier and token == "def") {
00186     node definition;
00187     identifier_list parameters;
00188     get_definition(token, parameters, definition);
00189     set_function(token, node(parameters, definition));
00190     return true;
00191   }
00192 
00193   if (k == identifier and token == "quit")
00194     std::exit(0);
00195 
00196   // Otherwise, the statement must be an expression.
00197   push_back(token, k);
00198   node n;
00199   if (not get_expr(n))
00200     return false;
00201   else {
00202     // Evaluate the expression and print the result.
00203     output << n.evaluate() << '\n';
00204     return true;
00205   }
00206 }
00207 
00208 bool parser::get_expr(node& result)
00209 {
00210   return get_add_expr(result);
00211 }
00212 
00213 bool parser::get_add_expr(node& result)
00214 {
00215   if (not get_mul_expr(result))
00216     return false;
00217   std::string token;
00218   while (kind k = get_token(token)) {
00219     if (k != '+' and k != '-') {
00220       push_back(token, k);
00221       return true;
00222     } else {
00223       node right;
00224       if (not get_mul_expr(right))
00225         throw syntax_error("unterminated expression. Expected a multiplicative-expression after " + token);
00226       result = node(result, k, right);
00227     }
00228   }
00229   return true;
00230 }
00231 
00232 bool parser::get_mul_expr(node& result)
00233 {
00234   if (not get_unary(result))
00235     return false;
00236   std::string token;
00237   while (kind k = get_token(token)) {
00238     if (k != '*' and k != '/') {
00239       push_back(token, k);
00240       return true;
00241     } else {
00242       node right;
00243       if (not get_unary(right))
00244         throw syntax_error("unterminated expression. Expected a unary-expression after " + token);
00245       result = node(result, k, right);
00246     }
00247   }
00248   return true;
00249 }
00250 
00251 bool parser::get_unary(node& result)
00252 {
00253   std::string token;
00254   kind k = get_token(token);
00255   if (k == eof)
00256     return false;
00257   if (k == '-') {
00258     if (not get_primary(result))
00259       throw syntax_error("expected primary after unary " + token + ", got end of line");
00260     result = node(k, result);
00261     return true;
00262   } else if (k == '+') {
00263     if (not get_primary(result))
00264       throw syntax_error("expected primary after unary +, got end of line");
00265     return true;
00266   } else {
00267     push_back(token, k);
00268     return get_primary(result);
00269   }
00270 }
00271 
00272 void parser::get_expr_list(node_list& result)
00273 {
00274   result.clear();
00275   std::string token;
00276   while (kind k = get_token(token)) {
00277     if (k == ')')
00278       return;
00279     push_back(token, k);
00280     node expr;
00281     if (not get_expr(expr))
00282       throw syntax_error("unexpected end of line in function argument");
00283     result.push_back(expr);
00284     k = get_token(token);
00285     if (k == ')')
00286       return;
00287     else if (k != ',')
00288       throw syntax_error("expected comma in argument list, got " + token);
00289   }
00290   throw syntax_error("unexpected end of line in function argument list");
00291 }
00292 
00293 bool parser::get_primary(node& result)
00294 {
00295   std::string token;
00296   kind k = get_token(token);
00297   if (k == eof)
00298     return false;
00299 
00300   if (k == '(') {
00301     // Parenthesized expression
00302     if (not get_expr(result))
00303       throw syntax_error("expected expression, got end of line");
00304     k = get_token(token);
00305     if (k != ')')
00306       throw syntax_error("expected ')', got " + token);
00307     else
00308       return true;
00309   }
00310 
00311   if (k == number) {
00312     // Numeric literal
00313     if (not get_number(token, result))
00314       throw syntax_error("Invalid numeric literal: " + token);
00315     return true;
00316   }
00317 
00318   if (k == identifier) {
00319     // Identifier: variable or function call
00320     std::string next;
00321     k = get_token(next);
00322     if (k == '(') {
00323         // function call
00324       node_list arguments;
00325       get_expr_list(arguments);
00326       result = node(token, arguments);
00327     } else {
00328       static const node_list no_arguments;
00329       // Variable reference or function call with no arguments
00330       push_back(next, k);
00331       result = node(token);
00332     }
00333     return true;
00334   }
00335   throw syntax_error("expected a primary, got " + token);
00336 }
00337 
00338 void parse_loop(std::istream& input, std::ostream& output)
00339 {
00340   std::string line;
00341   // No portable way to test whether the console is an interactive terminal
00342   // vs. a non-interactive file. If you have a system-specific way to test,
00343   // output the prompt only for the interactive case.
00344   for (output << "> "; std::getline(input, line); output << "> ") {
00345     std::istringstream input(line);
00346     parser p(input);
00347     try {
00348       while (p.get_statement(output)) {
00349         /* empty */
00350       }
00351     } catch(calc_error const& ex) {
00352       output << ex.what() << '\n';
00353     } catch(std::exception const& ex) {
00354       output << "exception: " << ex.what() << '\n';
00355     }
00356   }
00357 }

Generated on Sun Nov 30 10:05:26 2008 for Calculator by  doxygen 1.5.3