- Main Page
- Classes
- Files

# 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 }