basix_doc 0.1
|
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