basix_doc 0.1
/Users/mourrain/Devel/mmx/basix/src/source_track.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : source_track.cpp
00004 * DESCRIPTION: Tracking the source location of a generic object
00005 * COPYRIGHT  : (C) 2007  Gregoire Lecerf and Joris van der Hoeven
00006 *******************************************************************************
00007 * This software falls under the GNU general public license and comes WITHOUT
00008 * ANY WARRANTY WHATSOEVER. See the file $TEXMACS_PATH/LICENSE for more details.
00009 * If you don't have this file, write to the Free Software Foundation, Inc.,
00010 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00011 ******************************************************************************/
00012 
00013 #include <basix/source_track.hpp>
00014 #include <basix/literal.hpp>
00015 #include <basix/compound.hpp>
00016 #include <basix/system.hpp>
00017 
00018 namespace mmx {
00019 
00020 nat backtrace_depth= 8;
00021 
00022 string repeated (const string& s, nat n);
00023 nat get_indentation (const string& s);
00024 string add_indentation (const string& s, int delta);
00025 
00026 /******************************************************************************
00027 * Keep track of interactive sources and source files
00028 ******************************************************************************/
00029 
00030 static vector< vector<string> > interactive_sources;
00031 static table< vector<string>, string > file_sources;
00032 
00033 void
00034 store_interactive_number (nat n) {
00035   ASSERT (n <= N(interactive_sources), "input number out of range");
00036   interactive_sources= range (interactive_sources, 0, n);
00037 }
00038 
00039 nat
00040 get_interactive_number (void) {
00041   return N(interactive_sources) ;
00042 }
00043 
00044 void
00045 store_interactive_source (const string& data, nat n) {
00046   ASSERT (n <= get_interactive_number (), "input number out of range");
00047   vector<string> v= tokenize (data, "\n");
00048   if (n == get_interactive_number ()) interactive_sources << v;
00049   else interactive_sources[n]= v;
00050 }
00051 
00052 string
00053 get_interactive_source (nat i) {
00054   ASSERT (i < N (interactive_sources), "source out of range");
00055   return recompose (interactive_sources[i], '\n');
00056 }
00057 
00058 static string
00059 get_interactive_source (nat i, nat l) {
00060   ASSERT (i < N (interactive_sources), "source out of range");
00061   ASSERT (l < N (interactive_sources[i]), "source out of range");
00062   return interactive_sources[i][l];
00063 }
00064 
00065 void
00066 store_file_source (const string& file_name, const string& data) {
00067   file_sources [file_name] = tokenize (data, "\n");
00068 }
00069 
00070 string
00071 get_file_source (const string& file_name) {
00072   return recompose (file_sources [file_name], "\n");
00073 }
00074 
00075 static string
00076 get_file_source (const string& file_name, nat line) {
00077   if (!file_sources->contains (file_name)) return "";
00078   ASSERT (line < N (read (file_sources, file_name)), "source out of range");
00079   return read (file_sources, file_name) [line];
00080 }
00081 
00082 string
00083 get_source (const string& file_name, nat i) {
00084   if (file_name == "") return get_interactive_source (i);
00085   return get_file_source (file_name);
00086 }
00087 
00088 string
00089 get_source (const string& file_name, nat i, nat line) {
00090   if (file_name == "") return get_interactive_source (i, line);
00091   return get_file_source (file_name, line);
00092 }
00093 
00094 /******************************************************************************
00095 * Keep track of locations in source code
00096 ******************************************************************************/
00097 
00098 struct gen_eq_op { // equality for unique symbols
00099   static generic name () { return generic ("gen_eq"); }
00100   static inline bool
00101   op (const generic& x, const generic& y) { return gen_eq (x, y); }
00102   static inline bool
00103   not_op (const generic& x, const generic& y) { return gen_neq (x, y); }
00104   static inline nat
00105   hash_op (const generic& x) { return gen_hash (x); }
00106 };
00107 
00108 struct gen_eq_table {
00109   typedef gen_eq_op key_op;
00110   typedef equal_op val_op;
00111 };
00112 
00113 table<source_location, generic, gen_eq_table>&
00114 source_locs () {
00115   static table<source_location, generic, gen_eq_table> t=
00116     table<source_location, generic, gen_eq_table> ();
00117   return t;
00118 }
00119 
00120 // l = source_locs () [g] means that the
00121 // location of the generic g in the source code is stored in l.
00122 
00123 void
00124 source_insert (const generic& g, const source_location& l) {
00125   source_locs () [g] = l;
00126 }
00127 
00128 void
00129 source_delete (const generic& g) {
00130   reset (source_locs (), g);
00131 }
00132 
00133 generic
00134 source_extend (const generic& g1, const generic& g2) {
00135   //mmout << "source extending " << g1 << " with " << g2 <<"\n";
00136   source_location l;
00137   source_locate (g1, l);
00138   source_locate (g2, l);
00139   source_delete (g1);
00140   source_delete (g2);
00141   l.obj = g1;
00142   source_insert (g1, l);
00143   return g1;
00144 }
00145 
00146 /******************************************************************************
00147 * Obtaining source information about a generic object
00148 ******************************************************************************/
00149   
00150 void
00151 source_locate (const generic& g, source_location& l) {
00152   // mmout << "source_locate: " << g << " @ " << inside (g) << "\n";
00153   source_location arg_l = l;
00154   if (source_locs () -> contains (g)) {
00155     source_location l1 = read (source_locs (), g);
00156     if (is_nil (l))
00157       l = l1;
00158     else 
00159       if (!is_nil (l1)) {
00160         l.obj = l1.obj;
00161         if (l.file_name == l1.file_name && l.input_number == l1.input_number) {
00162           l.begin = min (l.begin, l1.begin);
00163           l.end = max (l.end, l1.end);
00164         }
00165         // If the location is not 'connected', we leave l unchanged. 
00166       }
00167     return;
00168   }
00169   if (is<compound> (g))
00170     for (nat i=0; i<N(g); i++)
00171       source_locate (g[i], l);
00172   l.obj = g;
00173 }
00174 
00175 source_location
00176 source_locate (const generic& g) {
00177   source_location l;
00178   source_locate (g, l);
00179   return l;
00180 }
00181 
00182 /******************************************************************************
00183 * Get high level information about source locations
00184 ******************************************************************************/
00185 
00186 bool
00187 source_exists (const generic& g) {
00188   return !is_nil (source_locate (g));
00189 }
00190 
00191 string
00192 source_file (const generic& g) {
00193   source_location l= source_locate (g);
00194   if (is_nil (l)) return "";
00195   else if (l.file_name == "")
00196     return "input[" * as_string (l.input_number + 1) * "]";
00197   else return l.file_name;
00198 }
00199 
00200 int
00201 source_line (const generic& g, const bool& last) {
00202   source_location l= source_locate (g);
00203   if (is_nil (l)) return 0;
00204   else if (last) return l.end.line;
00205   else return l.begin.line;
00206 }
00207 
00208 int
00209 source_column (const generic& g, const bool& last) {
00210   source_location l= source_locate (g);
00211   if (is_nil (l)) return 0;
00212   else if (last) return l.end.column;
00213   else return l.begin.column;
00214 }
00215 
00216 string
00217 source_begin (const generic& g) {
00218   if (!source_exists (g)) return "Unknown location";
00219   return source_file (g) * ":" *
00220          as_string (source_line (g, false)) * ":" *
00221          as_string (source_column (g, false));
00222 }
00223 
00224 string
00225 source_end (const generic& g) {
00226   if (!source_exists (g)) return "Unknown location";
00227   return source_file (g) * ":" *
00228          as_string (source_line (g, true)) * ":" *
00229          as_string (source_column (g, true));
00230 }
00231 
00232 string
00233 source_string (const generic& g) {
00234   source_location l= source_locate (g);
00235   if (is_nil (l)) return "";
00236   else {
00237     string src= get_source (l.file_name, l.input_number);
00238     return src (l.begin.position, l.end.position);
00239   }
00240 }
00241 
00242 string
00243 source_string_unindented (const generic& g) {
00244   source_location l= source_locate (g);
00245   if (is_nil (l)) return "";
00246   else {
00247     string src= get_source (l.file_name, l.input_number);
00248     string sub= src (l.begin.position, l.end.position);
00249     string bis= repeated (" ", l.begin.column) * sub;
00250     return add_indentation (bis, -((int) get_indentation (bis)));
00251   }
00252 }
00253 
00254 /******************************************************************************
00255 * Underline part of a source
00256 ******************************************************************************/
00257 
00258 static string
00259 underlined (const string& file_name, const nat& n,
00260             const source_position& b, const source_position& e)
00261 {
00262   // empty file_name means interactive input. In this only situation
00263   // n means the input number. n is not used for a regular file.
00264   nat j;
00265   string line;
00266   string sout;
00267 
00268   line = get_source (file_name, n, b.line);
00269 
00270   sout << line << "\n";
00271   for (j=0; j < b.column; ++j)
00272     sout << " ";
00273   if (b.line == e.line) {
00274     for (; j < e.column; ++j)
00275       sout << "~";
00276   }
00277   else {
00278     if (e.line == b.line+1 && e.column == 0)
00279       sout << "^";
00280     else {
00281       for (; j < N(line); ++j)
00282         sout << "~";
00283       sout << "\n";
00284       if (e.column == 0) {
00285         if (e.line - 1 > b.line + 1)
00286           sout << "...\n";
00287         line = get_source (file_name, n, e.line-1);
00288         sout << line << "\n";
00289         for (j=0; j < N(line); ++j)
00290           sout << "~";
00291         if (N(line) == 0)
00292           sout << "^";
00293       }
00294       else {
00295         if (e.line > b.line + 1)
00296           sout << "...\n";
00297         line = get_source (file_name, n, e.line);
00298         sout << line << "\n";
00299         for (j=0; j < e.column; ++j)
00300           sout << "~";
00301       }
00302     }
00303   }
00304   sout << "\n";
00305   return sout;
00306 }
00307 
00308 string
00309 source_underlined (const generic& g) {
00310   source_location l= source_locate (g);
00311   if (is_nil (l)) return "";
00312   else return underlined (l.file_name, l.input_number, l.begin, l.end);
00313 }
00314 
00315 string
00316 source_error (const string& msg, const generic& g) {
00317   mmerr << ">>> g= " << g << "\n";
00318   string s;
00319   source_location l= source_locate (g);
00320   if (is_nil (l) && is<literal> (g))
00321     s << "Inside " << literal_to_string (g) << ": ";
00322   else if (is_nil (l))
00323     s << "Unknown location: ";
00324   else
00325     s << strip_directory (source_file (g)) << ":"
00326       << as_string (l.begin.line + 1) << ":" 
00327       << as_string (l.begin.column + 1) << ": ";
00328   s << msg << "\n"
00329     << source_underlined (g);
00330   return s;
00331 }
00332 
00333 string
00334 backtrace (const generic& g) {
00335   if (has_trace (g))
00336     return backtrace (trace_pull (g)) * backtrace (trace_top (g));
00337   else return source_exception (as<exception> (g));
00338 }
00339 
00340 string
00341 source_exception (const exception& e) {
00342   generic g= as<generic> (e);
00343   if (has_trace (g))
00344     return backtrace (trace_bottom (g, backtrace_depth));
00345   else {
00346     generic err= *e;
00347     if (is<literal> (err[1]))
00348       return source_error (literal_to_string (err[1]), err[N(err)-1]);
00349     else return source_error ("unknown", err[N(err)-1]);
00350   }
00351 }
00352 
00353 } // namespace mmx
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines