- ginac/operators.cpp: added some missing debugmsg()'s, which would have
[ginac.git] / cint / ginaccint.bin.cpp
1 #include "G__ci.h"   /* G__atpause is defined in G__ci.h */
2
3 #if (!defined(G__CINTVERSION)) || (G__CINTVERSION < 501438)
4 #error You need at least cint 5.14.38 to compile GiNaC-cint. Download it via http from root.cern.ch/root/Cint.html or via ftp from ftpthep.physik.uni-mainz.de/pub/cint
5 #endif // (!defined(G__CINTVERSION)) || (G__CINTVERSION < 501438)
6
7 #include <iostream>
8 #include <fstream>
9 #include <string>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include "ginac/ginac.h"
13 #include "config.h"
14 #include <list>
15
16 #ifndef NO_NAMESPACE_GINAC
17 using namespace GiNaC;
18 #endif // ndef NO_NAMESPACE_GINAC
19
20 extern "C" G__value G__exec_tempfile G__P((char *file));
21 extern "C" void G__store_undo_position(void);
22
23 #define PROMPT1 "GiNaC> "
24 #define PROMPT2 "     > "
25
26 #ifdef OBSCURE_CINT_HACK
27
28 #include <strstream>
29
30 template<class T>
31 string ToString(const T & t)
32 {
33     char buf[256];
34     ostrstream(buf,sizeof(buf)) << t << ends;
35     return buf;
36 }
37
38 basic * ex::last_created_or_assigned_bp = 0;
39 basic * ex::dummy_bp = 0;
40 long ex::last_created_or_assigned_exp = 0;
41
42 #endif // def OBSCURE_CINT_HACK
43
44 G__value exec_tempfile(string const & command);
45 char * process_permanentfile(string const & command);
46 void process_tempfile(string const & command);
47 void greeting(void);
48 void helpmessage(void);
49 string preprocess(char const * const line, bool & comment, bool & single_quote,
50                   bool & double_quote, unsigned & open_braces);
51 void cleanup(void);
52 void sigterm_handler(int n);
53 void initialize(void);
54 void initialize_cint(void);
55 void restart(void);
56 bool is_command(string const & command, string & preprocessed,
57                 string const & comparevalue, bool substr=false);
58 bool readlines(istream * is, string & allcommands);
59 bool readfile(string const & filename, string & allcommands);
60 void savefile(string const & filename, string const & allcommands);
61
62 typedef list<char *> cplist;
63 cplist filenames;
64 bool redirect_output=false;
65
66 G__value exec_tempfile(string const & command)
67 {
68     G__value retval;
69     char *tmpfilename = tempnam(NULL,"ginac");
70     ofstream fout;
71     fout.open(tmpfilename);
72     fout << "{" << endl << command << endl << "}" << endl;
73     fout.close();
74     G__store_undo_position();
75     retval = G__exec_tempfile(tmpfilename);
76     G__security_recover(stdout);
77     remove(tmpfilename);
78     free(tmpfilename);
79     return retval;
80 }
81
82 char * process_permanentfile(string const & command)
83 {
84     char *tmpfilename = tempnam(NULL,"ginac");
85     cout << "creating file " << tmpfilename << endl;
86     ofstream fout;
87     fout.open(tmpfilename);
88     fout << command << endl;
89     fout.close();
90     G__store_undo_position();
91     G__loadfile(tmpfilename);
92     G__security_recover(stdout);
93     return tmpfilename;
94 }
95
96 void process_tempfile(string const & command)
97 {
98 #ifdef OBSCURE_CINT_HACK
99     static G__value ref_symbol = exec_tempfile("symbol ginac_cint_internal_symbol; ginac_cint_internal_symbol;");
100     static G__value ref_constant = exec_tempfile("constant ginac_cint_internal_constant; ginac_cint_internal_constant;");
101     static G__value ref_function = exec_tempfile("sin(ginac_cint_internal_symbol);");
102     static G__value ref_power = exec_tempfile("power(ex(ginac_cint_internal_symbol),ex(ginac_cint_internal_symbol));");
103     static G__value ref_numeric = exec_tempfile("numeric ginac_cint_internal_numeric; ginac_cint_internal_numeric;");
104     static G__value ref_ex = exec_tempfile("ex ginac_cint_internal_ex; ginac_cint_internal_ex;");
105     static bool basic_type_warning_already_displayed = false;
106 #endif // def OBSCURE_CINT_HACK
107
108     G__value retval = exec_tempfile(command);
109
110 #ifdef OBSCURE_CINT_HACK
111
112     #define TYPES_EQUAL(A,B) (((A).type==(B).type) && ((A).tagnum==(B).tagnum))
113     
114     static unsigned out_count = 0;
115     if (TYPES_EQUAL(retval,ref_ex)) {
116         string varname = "Out"+ToString(++out_count);
117         if (retval.obj.i!=ex::last_created_or_assigned_exp) {
118             // an ex was returned, but this is not the ex which was created last
119             // => this is not a temporary ex, but one that resides safely in memory
120             
121             // cout << "warning: using ex from retval (experimental)" << endl;
122             ex::dummy_bp=((ex *)(void *)(retval.obj.i))->bp;
123             exec_tempfile("ex "+varname+"(*ex::dummy_bp);");
124         } else if (ex::last_created_or_assigned_bp_can_be_converted_to_ex()) {
125             //string varfill;
126             //for (int i=4-int(log10(out_count)); i>0; --i)
127             //    varfill += ' ';
128             exec_tempfile("ex "+varname+"(*ex::last_created_or_assigned_bp);");
129         } else {
130             cout << "warning: last_created_or_assigned_bp modified 0 or not evaluated or not dynallocated" << endl;
131         }
132         exec_tempfile(string()+"LLLAST=LLAST;\n"
133                       +"LLAST=LAST;\n"
134                       +"LAST="+varname+";\n"
135                       +"if (ginac_cint_internal_redirect_output&&"
136                       +"    ginac_cint_internal_fout.good()) {" 
137                       +"    ginac_cint_internal_fout << \""+varname+" = \" << "+varname+" << endl << endl;"
138                       +"} else {"
139                       +"    cout << \""+varname+" = \" << "+varname+" << endl << endl;"
140                       +"}");
141     } else if (TYPES_EQUAL(retval,ref_symbol)||
142                TYPES_EQUAL(retval,ref_constant)||
143                TYPES_EQUAL(retval,ref_function)||
144                TYPES_EQUAL(retval,ref_power)||
145                TYPES_EQUAL(retval,ref_numeric)) {
146         if (!basic_type_warning_already_displayed) {
147             cout << endl
148                  <<"WARNING: The return value of the last expression you entered was a symbol," << endl
149                  << "constant, function, power or numeric, which cannot be safely displayed." << endl
150                  << "To force the output, cast it explicitly to type 'ex' or use 'cout'," << endl
151                  << "for example (assume 'x' is a symbol):" << endl
152                  << PROMPT1 "ex(x);" << endl
153                  << "OutX = x" << endl << endl
154                  << PROMPT1 "cout << x << endl;" << endl
155                  << "x" << endl << endl
156                  << "This warning will not be shown again." << endl;
157             basic_type_warning_already_displayed = true;
158         }
159     }
160 #endif // def OBSCURE_CINT_HACK
161     return;
162 }
163
164 void greeting(void)
165 {
166     cout << "Welcome to GiNaC-cint (" << PACKAGE << " V" << VERSION << ")" << endl;
167     cout << "  __,  _______  GiNaC: (C) 1999-2000 Johannes Gutenberg University Mainz," << endl
168          << " (__) *       | Germany.  Cint C/C++ interpreter: (C) 1995-2000 Masaharu" << endl
169          << "  ._) i N a C | Goto and Agilent Technologies, Japan.  This is free software" << endl
170          << "<-------------' with ABSOLUTELY NO WARRANTY.  For details, type `.warranty'" << endl
171          << "Type .help for help." << endl
172          << endl;
173     return;
174 }
175
176 void helpmessage(void)
177 {
178     cout << "GiNaC-cint recognizes some special commands which start with a dot:" << endl << endl
179          << "  .cint                    switch to cint interactive mode (see cint" << endl
180          << "                           documentation for further details)" << endl
181          << "  .function                define the body of a function (necessary due to a" << endl
182          << "                           cint limitation)" << endl
183          << "  .help                    the text you are currently reading" << endl
184          << "  .q, .quit, .exit, .bye   quit GiNaC-cint" << endl
185          << "  .read filename           read a file from disk and execute it in GiNaC-cint" << endl
186          << "                           (recursive call is possible)" << endl
187          << "  .redirect [filename]     redirect 'OutXY = ...' output to a file" << endl
188          << "                           (.redirect alone redirects output back to console)" << endl
189          << "  .restart                 restart GiNaC-cint (does not re-read command line" << endl
190          << "                           files)" << endl
191          << "  .save filename           save the commands you have entered so far in a file" << endl
192          << "  .silent                  suppress 'OutXY = ...' output (variables are still" << endl
193          << "                           accessible)" << endl
194          << "  .warranty                information on redistribution and warranty" << endl
195          << "  .> [filename]            same as .redirect [filename]" << endl << endl
196          << "Instead of '.cmd' you can also write '//GiNaC-cint.cmd' to be compatible with" << endl
197          << "programs that will be compiled later." << endl
198          << "Additionally you can exit GiNaC-cint with quit; exit; or bye;" << endl
199          << endl;
200     return;
201 }
202
203 void warrantymessage(void)
204 {
205     cout << "GiNaC is free software; you can redistribute it and/or modify it under the" << endl
206          << "the terms of the GNU General Public License as published by the Free Software" << endl
207          << "Foundation; either version 2 of the License, or (at your option) any later" << endl
208          << "version." << endl
209          << "This program is distributed in the hope that it will be useful, but WITHOUT" << endl
210          << "ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS" << endl
211          << "FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more" << endl
212          << "details." << endl
213          << "You should have received a copy of the GNU General Public License along with" << endl
214          << "this program. If not, write to the Free Software Foundation, 675 Mass Ave," << endl
215          << "Cambridge, MA 02139, USA." << endl << endl;
216     cout << "Cint and associated tools are copyright by Agilent Technologies Japan Company" << endl
217          << "and Masaharu Goto <MXJ02154@niftyserve.or.jp>." << endl
218          << "Source code, binary executable or library of Cint and associated tools can be" << endl
219          << "used, modified and distributed with no royalty for any purpose provided that" << endl
220          << "the copyright notice appear in all copies and that both that copyright notice" << endl
221          << "and this permission notice appear in supporting documentation." << endl
222          << "Agilent Technologies Japan and the author make no representations about the" << endl
223          << "suitability of this software for any purpose.  It is provided \"AS IS\"" << endl
224          << "without express or implied warranty." << endl;
225     return;
226 }
227
228 string preprocess(char const * const line, bool & comment, bool & single_quote,
229                   bool & double_quote, unsigned & open_braces)
230 {
231     // "preprocess" the line entered to be able to decide if the command shall be
232     // executed directly or more input is needed or this is a special command
233
234     // ALL whitespace will be removed
235     // all comments (/* */ and //) will be removed
236     // open and close braces ( { and } ) outside strings will be counted 
237
238     /*
239     cout << "line=" << line << endl;
240     cout << "comment=" << comment << ", single_quote=" << single_quote
241          << ",double_quote=" << double_quote << ", open_braces=" << open_braces
242          << endl;
243     */
244     
245     string preprocessed;
246     int pos = 0;
247     bool end = false;
248     bool escape = false;
249     bool slash = false;
250     bool asterisk = false;
251     while ((line[pos]!='\0')&&!end) {
252         if (escape) {
253             // last character was a \, ignore this one
254             escape = false;
255         } else if (slash) {
256             // last character was a /, test if * or /
257             slash = false;
258             if (line[pos]=='/') {
259                 end = true;
260             } else if (line[pos]=='*') {
261                 comment = true;
262             } else {
263                 preprocessed += '/';
264                 preprocessed += line[pos];
265             }
266         } else if (asterisk) {
267             // last character was a *, test if /
268             asterisk = false;
269             if (line[pos]=='/') {
270                 comment = false;
271             } else if (line[pos]=='*') {
272                 preprocessed += '*';
273                 asterisk = true;
274             }
275         } else {
276             switch (line[pos]) {
277             case ' ':
278             case '\t':
279             case '\n':
280             case '\r':
281                 // whitespace: ignore
282                 break;
283             case '\\':
284                 // escape character, ignore next
285                 escape=true;
286                 break;
287             case '"':
288                 if ((!single_quote)&&(!comment)) {
289                     double_quote = !double_quote;
290                 }
291                 break;
292                 case '\'':
293                     if ((!double_quote)&&(!comment)) {
294                         single_quote = !single_quote;
295                     }
296                     break;
297             case '{':
298                 if ((!single_quote)&&(!double_quote)&&(!comment)) {
299                     open_braces++;
300                 }
301                 break;
302             case '}':
303                 if ((!single_quote)&&(!double_quote)&&(!comment)&&(open_braces>0)) {
304                     open_braces--;
305                 }
306                 break;
307             case '/':
308                 slash = true;
309                 break;
310             case '*':
311                 asterisk = true;
312                 break;
313             default:
314                 preprocessed += line[pos];
315             }
316         }
317         pos++;
318     }
319
320     /*
321     cout << "preprocessed=" << preprocessed << endl;
322     cout << "comment=" << comment << ", single_quote=" << single_quote
323          << ",double_quote=" << double_quote << ", open_braces=" << open_braces
324          << endl;
325     */
326     
327     return preprocessed;
328 }
329
330 void cleanup(void)
331 {
332     for (cplist::iterator it=filenames.begin(); it!=filenames.end(); ++it) {
333         cout << "removing file " << *it << endl;
334         remove(*it);
335         free(*it);
336     }
337 }
338
339 void sigterm_handler(int n)
340 {
341     exit(1);
342 }
343
344 void initialize(void)
345 {
346     if (isatty(0))
347         greeting();
348
349     atexit(cleanup);
350     signal(SIGTERM,sigterm_handler);
351     initialize_cint();
352 }    
353
354 void initialize_cint(void)
355 {
356     G__init_cint("cint");    /* initialize cint */
357
358     // no longer needed as of cint 5.14.31:
359     // exec_tempfile("#include <string>\n");
360
361 #ifndef NO_NAMESPACE_GINAC
362     exec_tempfile("using namespace GiNaC;");
363 #endif // ndef NO_NAMESPACE_GINAC
364     
365     exec_tempfile("ex LAST,LLAST,LLLAST;\n");
366     exec_tempfile("bool ginac_cint_internal_redirect_output=false;\n");
367     exec_tempfile("ofstream ginac_cint_internal_fout;\n");
368 }    
369
370 void restart(void)
371 {
372     cout << "Restarting GiNaC-cint." << endl;
373     G__scratch_all();
374     initialize_cint();
375 }
376
377 void redirect(string const & filename)
378 {
379     if (filename=="") {
380         cout << "Redirecting output back to console..." << endl;
381         exec_tempfile( string()
382                       +"ginac_cint_internal_redirect_output=false;\n"
383                       +"ginac_cint_internal_fout.close();");
384     } else {
385         cout << "Redirecting output to " << filename << "..." << endl;
386         exec_tempfile( string()
387                       +"ginac_cint_internal_redirect_output=true;\n"
388                       +"ginac_cint_internal_fout.open(\""+filename+"\");\n");
389     }
390 }
391
392 bool is_command(string const & command, string & preprocessed,
393                 string const & comparevalue, bool substr)
394 {
395     bool single_quote = false;
396     bool double_quote = false;
397     bool comment = false;
398     unsigned open_braces = 0;
399     if ((preprocessed=="."+comparevalue)||
400         substr&&(preprocessed.substr(0,comparevalue.length()+1)==
401                  "."+comparevalue)) {
402         return true;
403     }
404     if ((command=="//GiNaC-cint."+comparevalue+"\n")||
405         substr&&(command.substr(0,comparevalue.length()+13)==
406                  "//GiNaC-cint."+comparevalue)) {
407         preprocessed = preprocess(command.substr(12).c_str(),comment,
408                                   single_quote,double_quote,open_braces);
409         return true;
410     }
411     return false;
412 }       
413
414 bool readlines(istream * is, string & allcommands)
415 {
416     char const * line;
417     char prompt[G__ONELINE];
418     string linebuffer;
419     
420     bool quit = false;
421     bool eof = false;
422     bool next_command_is_function = false;
423     bool single_quote = false;
424     bool double_quote = false;
425     bool comment = false;
426     unsigned open_braces = 0;
427
428     while ((!quit)&&(!eof)) {
429         strcpy(prompt,PROMPT1);
430         bool end_of_command = false;
431         string command;
432         string preprocessed;
433         while (!end_of_command) {
434             if (is==NULL) {
435                 line = G__input(prompt);
436             } else {
437                 getline(*is,linebuffer);
438                 line = linebuffer.c_str();
439             }
440             command += line;
441             command += "\n";
442             preprocessed += preprocess(line,comment,single_quote,double_quote,open_braces);
443             if ((open_braces==0)&&(!single_quote)&&(!double_quote)&&(!comment)) {
444                 unsigned l = preprocessed.length();
445                 if ((l==0)||
446                     (preprocessed[0]=='#')||
447                     (preprocessed[0]=='.')||
448                     (preprocessed[l-1]==';')||
449                     (preprocessed[l-1]=='}')) {
450                     end_of_command = true;
451                 }
452             }
453             strcpy(prompt,PROMPT2);
454         }
455         if ((preprocessed=="quit;")||
456             (preprocessed=="exit;")||
457             (preprocessed=="bye;")||
458             (is_command(command,preprocessed,"quit"))||
459             (is_command(command,preprocessed,"exit"))||
460             (is_command(command,preprocessed,"bye"))||
461             (is_command(command,preprocessed,"q"))) {
462             quit = true;
463         } else if (is_command(command,preprocessed,"function")) {
464             cout << "next expression can be a function definition" << endl;
465             next_command_is_function=true;
466         } else if (is_command(command,preprocessed,"cint")) {
467             cout << endl << "switching to cint interactive mode" << endl;
468             cout << "'h' for help, 'q' to quit, '{ statements }' or 'p [expression]' to evaluate" << endl;
469             G__pause();
470             cout << "back from cint" << endl;
471         } else if (is_command(command,preprocessed,"help")) {
472             helpmessage();
473         } else if (is_command(command,preprocessed,"read",true)) {
474             quit=readfile(preprocessed.substr(5),allcommands);
475         } else if (is_command(command,preprocessed,"save",true)) {
476             command = "/* "+command+" */"; // we do not want the .save command itself in saved files
477             savefile(preprocessed.substr(5),allcommands);
478         } else if (is_command(command,preprocessed,"restart")) {
479             restart();
480         } else if (is_command(command,preprocessed,"redirect",true)) {
481             redirect(preprocessed.substr(9));
482         } else if (is_command(command,preprocessed,">",true)) {
483             redirect(preprocessed.substr(2));
484         } else if (is_command(command,preprocessed,"silent")) {
485             redirect("/dev/null");
486         } else if (is_command(command,preprocessed,"warranty")) {
487             warrantymessage();
488         /* test for more special commands
489         } else if (preprocessed==".xyz") {
490             cout << "special command (TBD): " << command << endl;
491         */
492         } else if (command.substr(0,2)=="#!") {
493             // ignore lines which indicate that this file is executed as a script
494         } else {
495             // cout << "now processing: " << command << endl;
496             if (next_command_is_function) {
497                 next_command_is_function = false;
498                 filenames.push_back(process_permanentfile(command));
499             } else {
500                 process_tempfile(command);
501             }
502         }
503         if (is!=NULL) {
504             // test for end of file if reading from a stream
505             eof=is->eof();
506         } else {
507             // save commands only when reading from keyboard
508             allcommands += command;
509         }
510
511     }
512     return quit;
513 }    
514
515 bool readfile(string const & filename, string & allcommands)
516 {
517     cout << "Reading commands from file " << filename << "." << endl;
518     bool quit = false;
519     ifstream fin;
520     fin.open(filename.c_str());
521     if (fin.good()) {
522         quit = readlines(&fin,allcommands);
523     } else {
524         cout << "Cannot open " << filename << " for reading." << endl;
525     }
526     fin.close();
527     return quit;
528 }
529
530 void savefile(string const & filename, string const & allcommands)
531 {
532     cout << "Saving commands to file " << filename << "." << endl;
533     ofstream fout;
534     fout.open(filename.c_str());
535     if (fout.good()) {
536         fout << allcommands;
537         if (!fout.good()) {
538             cout << "Cannot save commands to " << filename << "." << endl;
539         }
540     } else {
541         cout << "Cannot open " << filename << " for writing." << endl;
542     }
543     fout.close();
544 }
545
546 int main(int argc, char ** argv) 
547 {
548     string allcommands;
549     initialize();
550
551     bool quit = false;
552     bool argsexist = argc>1;
553
554     if (argsexist) {
555         allcommands = "/* Files given as command line arguments:\n";
556     }
557     
558     argc--; argv++;
559     while (argc && !quit) {
560         allcommands += *argv;
561         allcommands += "\n";
562         quit=readfile(*argv,allcommands);
563         argc--; argv++;
564     }
565
566     if (argsexist) {
567         allcommands += "*/\n";
568     }
569
570     if (!quit) {
571         readlines(NULL,allcommands);
572     }
573
574     return 0;
575 }