macro_body.cc

Go to the documentation of this file.
00001 /*
00002    The lestes compiler suite
00003    Copyright (C) 2002, 2003, 2004, 2005 Miroslav Tichy
00004    Copyright (C) 2002, 2003, 2004, 2005 Petr Zika
00005    Copyright (C) 2002, 2003, 2004, 2005 Vojtech Hala
00006    Copyright (C) 2002, 2003, 2004, 2005 Jiri Kosina
00007    Copyright (C) 2002, 2003, 2004, 2005 Pavel Sanda
00008    Copyright (C) 2002, 2003, 2004, 2005 Jan Zouhar
00009    Copyright (C) 2002, 2003, 2004, 2005 Rudolf Thomas
00010 
00011    This program is free software; you can redistribute it and/or modify
00012    it under the terms of the GNU General Public License as published by
00013    the Free Software Foundation; version 2 of the License.
00014 
00015    This program is distributed in the hope that it will be useful,
00016    but WITHOUT ANY WARRANTY; without even the implied warranty of
00017    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018    GNU General Public License for more details.
00019 
00020    See the full text of the GNU General Public License version 2, and
00021    the limitations in the file doc/LICENSE.
00022 
00023    By accepting the license the licensee waives any and all claims
00024    against the copyright holder(s) related in whole or in part to the
00025    work, its use, and/or the inability to use it.
00026  
00027  */
00028 /*! \file
00029   \brief Macro expansion list.
00030   
00031   Definition of macro_body class representing macro expansion list.
00032   \author pt
00033 */
00034 #include <lestes/common.hh>
00035 #include <lestes/std/source_location.hh>
00036 #include <lestes/lang/cplus/lex/macro_body.hh>
00037 #include <lestes/lang/cplus/lex/macro_body.m.hh>
00038 #include <lestes/lang/cplus/lex/macro_head.hh>
00039 #include <lestes/lang/cplus/lex/macro_item.hh>
00040 #include <lestes/lang/cplus/lex/token_sequence.hh>
00041 #include <lestes/lang/cplus/lex/pp_token.hh>
00042 #include <lestes/lang/cplus/lex/token_value.hh>
00043 #include <lestes/lang/cplus/lex/macro_argument.hh>
00044 #include <lestes/lang/cplus/lex/macro_arguments.hh>
00045 #include <lestes/lang/cplus/lex/concat.hh>
00046 
00047 package(lestes);
00048 package(lang);
00049 package(cplus);
00050 package(lex);
00051 
00052 using namespace ::std;
00053 
00054 /*!
00055   Constructs empty expansion list.
00056   \post state == BEGIN
00057   \post items->size() == 0
00058 */
00059 macro_body::macro_body(void):
00060         state(BEGIN),
00061         items(items_type::create())
00062 {
00063 }
00064 
00065 /*!
00066   Parses function like macro replacement list and stores values into self.
00067   \pre state == BEGIN
00068   \param input  The source for tokens, starting after parameter list.
00069   \return false  If the replacement list was ill-formed.
00070 */
00071 bool macro_body::parse(const ptr<token_input> &input, const ptr<macro_head> &head)
00072 {
00073         lassert(state == BEGIN);
00074         // state of parser
00075         enum {
00076                 BEGIN,
00077                 LITERAL,
00078                 EXPAND,
00079                 NONEXPAND,
00080                 STRINGIFY,
00081                 CONCAT,
00082                 NEWLINE,
00083                 END
00084         } fstate = BEGIN;
00085         ptr<pp_token> t;
00086         ptr<token_sequence> literal;
00087         ulint idx;
00088 
00089         do {
00090                 // TODO pt ::std::cerr << "fstate = " << fstate["blenscXQ"] << '\n';
00091                 if (fstate == BEGIN)
00092                         t = input->read_front_skip_ws();
00093                 else
00094                         t = input->read_front();
00095                 
00096                 if (t->is_name()) {
00097                         idx = head->index_of(t->value_get());
00098                         // if idx is invalid
00099                         if (idx == head->length()) {
00100                                 // ordinary identifier is a part of a literal
00101                                 if (fstate != LITERAL) {
00102                                         literal = token_sequence::create();
00103                                         fstate = LITERAL;
00104                                 }
00105                                 literal->add_back(t);
00106                         } else {
00107                                 if (fstate == CONCAT) {
00108                                         // no expansion after concatenation
00109                                         items->push_back(macro_item::create_copy(idx));
00110                                         fstate = NONEXPAND;
00111                                 } else {
00112                                         if (fstate == LITERAL) {
00113                                                 // flush the literal
00114                                                 items->push_back(macro_item::create_literal(literal));
00115                                         }
00116                                         items->push_back(macro_item::create_expansion(idx));
00117                                         fstate = EXPAND;
00118                                 }
00119                         }
00120                 } else if (t->type_get() == pp_token::TOK_HASH) {
00121                         if (fstate == LITERAL) {
00122                                 // flush the literal
00123                                 items->push_back(macro_item::create_literal(literal));
00124                         }
00125                   
00126                         literal = token_sequence::create();
00127                         // save the hash
00128                         literal->add_back(t);
00129                         t = input->read_front();
00130 
00131                         if (t->type_get() == pp_token::TOK_BLANK) {
00132                                 // add the blank
00133                                 literal->add_back(t);
00134                                 // get the parameter
00135                                 t = input->read_front_skip_ws();
00136                         }
00137 
00138                         if (!t->is_name() ||
00139                                  (idx = head->index_of(t->value_get())) == head->length()) {
00140                                 // # operator without parameter
00141                                 report << hash_without_parameter << t->location_get();
00142                                 // need cleanup till the end of line
00143                                 if (t->type_get() != pp_token::TOK_LINE_END) goto error_cleanup;
00144                                 state = DEAD;
00145                                 return false;
00146                         }
00147                         items->push_back(macro_item::create_str(idx,literal));
00148 
00149                         fstate = STRINGIFY;
00150                 } else if (t->type_get() == pp_token::TOK_HASH_HASH) {
00151                         switch (fstate) {
00152                                 case BEGIN:
00153                                         // ## at the beginning of expansion list
00154                                         report << hash_hash_at_beginning << t->location_get();
00155                                         goto error_cleanup;
00156                                 case LITERAL:
00157                                         // check whether there is EXPAND preceding
00158                                         if (!(items->size() && literal->length() == 1 && literal->peek_front() == pp_token::TOK_BLANK &&
00159                                                  items->back()->action_get() == macro_item::EXPANSION)) break;
00160                                         // fall through
00161                                 case EXPAND:
00162                                         {
00163                                                 // update preceding expansion
00164                                                 ptr<macro_item> mbi = items->back();
00165                                                 items->pop_back();
00166                                                 // update to avoid expansion
00167                                                 items->push_back(macro_item::create_copy(mbi->index_get()));
00168                                         }
00169                                         break;
00170                                 case STRINGIFY:
00171                                 case CONCAT:
00172                                         // okay
00173                                         break;
00174                                 case END:
00175                                 default:
00176                                         lassert2(false,"You should never get here");
00177                                         break;
00178                         }
00179                         if (fstate == LITERAL) {
00180                                 // flush the literal
00181                                 items->push_back(macro_item::create_literal(literal));
00182                         }
00183 
00184                         literal = token_sequence::create();
00185                         literal->add_back(t);
00186 
00187                         items->push_back(macro_item::create_concat(literal));
00188                         fstate = CONCAT;
00189                 } else if (t->type_get() == pp_token::TOK_LINE_END) {
00190                         if (fstate == CONCAT) {
00191                                 // ## at the end of the expansion list
00192                                 report << hash_hash_at_end << t->location_get();
00193                                 state = DEAD;
00194                                 return false;
00195                         }
00196                         if (fstate == LITERAL && literal->skip_back_ws()) {
00197                                 // flush the literal
00198                                 items->push_back(macro_item::create_literal(literal));
00199                         }
00200                         fstate = END;
00201                 } else {
00202                         if (fstate != LITERAL) {
00203                                 literal = token_sequence::create();
00204                                 fstate = LITERAL;
00205                         }
00206                         literal->add_back(t);
00207                 }
00208         } while (fstate != END);
00209         state = PARSED;
00210         return true;
00211 error_cleanup:
00212         // discard tokens till end of line
00213         do {
00214                 t = input->read_front_skip_ws();
00215         } while (t->type_get() != pp_token::TOK_LINE_END);   
00216         state = DEAD;
00217         return false;
00218 }
00219 
00220 /*!
00221   Parses object-like macro replacement list and stores values into self.
00222   The input is parsed including the TOK_LINE_END even in case it is ill-formed.
00223   \pre state == BEGIN
00224   \param input  The source for tokens, starting at the beginning of the replacement list.
00225   \return false  If the replacement list was ill-formed.
00226 */
00227 bool macro_body::parse(const ptr<token_input> &input)
00228 {
00229         lassert(state == BEGIN);
00230         
00231         ptr<token_sequence> literal;
00232         enum { BEGIN, LITERAL, CONCAT, END } fstate = BEGIN;
00233         ptr<pp_token> t;
00234         
00235         do {
00236                 if (fstate == BEGIN)
00237                         t = input->read_front_skip_ws();
00238                 else
00239                         t = input->read_front();
00240 
00241                 if (t->type_get() == pp_token::TOK_HASH_HASH) {
00242                         // check whether the ## is not at the beginning
00243                         if (fstate == BEGIN) {
00244                                 // ## at the beginning of expansion list
00245                                 report << hash_hash_at_beginning << t->location_get();
00246                                 // discard tokens till end of line
00247                                 do {
00248                                         t = input->read_front_skip_ws();
00249                                 } while (t->type_get() != pp_token::TOK_LINE_END);   
00250                                 state = DEAD;
00251                                 return false;
00252                         }
00253                         
00254                         if (fstate == LITERAL) {
00255                                 // flush the literal
00256                                 items->push_back(macro_item::create_literal(literal));
00257                         }
00258 
00259                         literal = token_sequence::create();
00260                         literal->add_back(t);
00261                         
00262                         items->push_back(macro_item::create_concat(literal));
00263                         fstate = CONCAT;
00264                 } else if (t->type_get() == pp_token::TOK_LINE_END) {
00265                         switch (fstate) {
00266                                 case BEGIN:
00267                                         // macro with no content has no macro items
00268                                         break;
00269                                 case CONCAT:
00270                                         // ## at the end of the expansion list
00271                                         report << hash_hash_at_end << t->location_get();
00272                                         state = DEAD;
00273                                         return false;
00274                                 case LITERAL:
00275                                         if (literal->skip_back_ws()) {
00276                                                 // flush the literal
00277                                                 items->push_back(macro_item::create_literal(literal));
00278                                         }
00279                                         break;
00280                                 case END:
00281                                 default:
00282                                         lassert2(false,"You should never get here");
00283                                         break;
00284                         }
00285                         fstate = END;
00286                 } else {
00287                         if (fstate != LITERAL) {
00288                                 // start new literal
00289                                 literal = token_sequence::create();
00290                                 fstate = LITERAL;
00291                         }
00292                         literal->add_back(t);
00293                 }
00294         } while (fstate != END);
00295         state = PARSED;
00296         return true;
00297 }
00298 
00299 /*!
00300   Expand this object-like macro body.
00301   \pre state == PARSED
00302   \param loc  The location of the expansion.
00303   \return The expanded, but not rescanned sequence.
00304 */
00305 ptr<token_sequence> macro_body::expand(const ptr<source_location> &loc)
00306 {
00307         lassert(state == PARSED);
00308         
00309         ptr<token_sequence> result = token_sequence::create();
00310         ptr<token_sequence> current;
00311         bool concat = false;
00312         items_type::iterator it = items->begin();
00313         items_type::iterator last = items->end();
00314 
00315         for ( ; it != last; ++it) {
00316                 ptr<macro_item> mi = *it;
00317 
00318                 switch (mi->action_get()) {
00319                         case macro_item::CONCAT:
00320                                 if (concat) {
00321                                         // concatenating with ## literal
00322                                         current = mi->value_get();
00323                                 } else {
00324                                         concat = true;
00325                                         // signal first round
00326                                         current = NULL;
00327                                 }
00328                                 break;
00329                         case macro_item::LITERAL:
00330                                 // assign because current is NULL or empty
00331                                 current = mi->value_get();
00332                                 break;
00333                         default:
00334                                 lassert2(false,"You should never get here");
00335                                 break;
00336                 }
00337 
00338                 if (concat) {
00339 
00340                         // if there is token for the right side
00341                         if (current && current->skip_front_ws()) {
00342                                 
00343                                 // TODO pt remove ::std::cerr << "WILL run concat on " << current << ::std::endl;
00344                                 
00345                                 // concat is always after tokens
00346                                 ptr<pp_token> left = result->read_back_skip_ws();
00347                                 // whitespace already cleared
00348                                 ptr<pp_token> right = current->read_front();
00349                                 
00350                                 // errors are handled transparently inside concatenate
00351                                 result->append(concat::instance()->process(left,right));
00352                                 // add the rest of the right part
00353                                 result->append(current);
00354 
00355                                 concat = false;
00356                         }
00357                 } else {
00358                         // no pending concatenation
00359                         result->append(current);
00360                 }
00361         }
00362 
00363         // concatenation shall not be at the end
00364         lassert(concat != true);
00365         // TODO clone(taboo macros, loc)
00366         return result->clone(loc);
00367 }
00368 
00369 /*!
00370   Expands this function-like macro body.
00371   \todo pt Add error message.
00372   \todo pt Add location setting.
00373   \pre state == PARSED
00374   \param mas  The arguments for the macro.
00375   \param loc  The location of the expansion.
00376   \return The expanded, but not rescanned sequence.
00377   \return NULL in case of error.
00378 */
00379 ptr<token_sequence> macro_body::expand(const ptr<source_location> &loc, 
00380                 const ptr<macro_arguments> &mas, const ptr<macro_storage> &macros)
00381 {
00382         lassert(state == PARSED);
00383         
00384         ptr<token_sequence> result = token_sequence::create();
00385         ptr<token_sequence> current;
00386         ptr<pp_token> left;
00387         bool concat = false;
00388         items_type::iterator it = items->begin();
00389         items_type::iterator last = items->end();
00390 
00391         for ( ; it != last; ++it) {
00392                 ptr<macro_item> mi = *it;
00393                 ptr<macro_argument> ma;
00394 
00395                 switch (mi->action_get()) {
00396                         case macro_item::CONCAT:
00397                                 if (concat) {
00398                                         // concatenating with ## literal
00399                                         current = mi->value_get();
00400                                 } else {
00401                                         concat = true;
00402                                         current = NULL;
00403                                 }
00404                                 break;
00405                         case macro_item::LITERAL:
00406                                 current = mi->value_get();
00407                                 break;
00408                         case macro_item::EXPANSION:
00409                                 ma = mas->argument_get(mi->index_get());
00410                                 current = ma->expanded_get(macros);
00411                                 break;
00412                         case macro_item::COPY:
00413                                 ma = mas->argument_get(mi->index_get());
00414                                 current = ma->nonexpanded_get();
00415                                 break;
00416                         case macro_item::STR:
00417                                 ma = mas->argument_get(mi->index_get());
00418                                 current = ma->stringified_get();
00419                                 break;
00420                         default:
00421                                 lassert2(false,"You should never get here");
00422                                 break;
00423                 }
00424 
00425                 if (concat) {
00426                         // if there is token for the right side
00427                         if (current && current->skip_front_ws()) {
00428                                 
00429                                 // TODO pt remove ::std::cerr << "WILL run () concat on " << current << ::std::endl;
00430                                 
00431                                 // check back token
00432                                 ptr<pp_token> left = 
00433                                         result->skip_back_ws() ? result->read_back() : ptr<pp_token>(NULL);
00434                                 ptr<pp_token> right = current->read_front();
00435                                 
00436                                 // errors are handled transparently inside concatenate
00437                                 result->append(concat::instance()->process(left,right));
00438                                 // add the rest of the right part
00439                                 result->append(current);
00440 
00441                                 concat = false;
00442                         }
00443                 } else {
00444                         // no pending concatenation
00445                         result->append(current);
00446                 }
00447                         
00448         }
00449 
00450         if (concat) {
00451                 // check back token
00452                 ptr<pp_token> left = 
00453                         result->skip_back_ws() ? result->read_back() : ptr<pp_token>(NULL);
00454                 // check front token
00455                 ptr<pp_token> right = 
00456                         current->skip_front_ws() ? current->read_front() : ptr<pp_token>(NULL);
00457                 
00458                 // errors are handled transparently inside concatenate
00459                 result->append(concat::instance()->process(left,right));
00460                 // add the rest of the right part
00461                 result->append(current);
00462 
00463                 concat = false;
00464         }
00465         
00466         return result->clone(loc);
00467 }
00468 
00469 /*!
00470   Returns internal state of the object.
00471   \return  The internal state of the object.
00472 */
00473 macro_body::state_type macro_body::state_get(void) const
00474 {
00475         return state;
00476 }
00477 
00478 /*!
00479   Tests equality.
00480   \param other The macro body to compare with.
00481   \return true If both expansion lists have the same order, spelling and whitespace separation of tokens.
00482 */
00483 bool macro_body::equals(const ptr<macro_body> &other) const
00484 {
00485         if (!other || state != other->state_get()) return false;
00486 
00487         if (state != PARSED) return true;
00488 
00489         if (items->size() != other->items->size()) return false;
00490 
00491         items_type::iterator it = items->begin();
00492         items_type::iterator last = items->end();
00493 
00494         items_type::iterator ot = other->items->begin();
00495 
00496         for ( ; it != last; ++it, ++ot) {
00497                 ptr<macro_item> mi = *it;
00498                 ptr<macro_item> oi = *ot;
00499 
00500                 if (!is_equal(mi,oi)) return false;
00501         }
00502                 
00503         return true;
00504 }
00505 
00506 /*!
00507   Marks the object.
00508 */
00509 void macro_body::gc_mark(void)
00510 {
00511         items.gc_mark();
00512 	::lestes::std::object::gc_mark();
00513 }
00514 
00515 /*!
00516   Returns empty expansion list.
00517   \post state == BEGIN
00518   \return The new empty expansion list.
00519 */  
00520 ptr<macro_body> macro_body::create(void)
00521 {
00522         return new macro_body();
00523 }
00524 
00525 end_package(lex);
00526 end_package(cplus);
00527 end_package(lang);
00528 end_package(lestes);
00529 /* vim: set ft=lestes : */

Generated on Mon Feb 12 18:22:40 2007 for lestes by doxygen 1.5.1-20070107