Richard Suchenwirth 2007-10-09 - Playing with tcltcc and its Critcl emulation, I had the idea of writing a very little C "compiler" which takes a (moderately) well-formed C source file, compiles (to memory) and executes it (by calling its main() function), so more along the lines of a C interpreter.
One problem I encountered is that char** seems not to be supported yet, so for first simplification I made argv a single string. Here's my script (ridiculously simple, indeed):
#!/usr/bin/env tclsh #-- ci.tcl -- a tiny C interpreter built on tcltcc package require critcl set f [open [lindex $argv 0]] set data [read $f] close $f critcl::ccode $data critcl::cproc main {char* argv} int {main(1,argv);} main [lindex $argv 1]
and a tiny C program source to test it with (ci_test.c):
/* demo program for ci.tcl */ #include <stdio.h> int main(int argc, char* argv) { printf("hello, %s\n", argv); return 0; }
which, when run, provides yet another solution for the classic K+R "hello, world" problem:
/Tcl/lib/tcc02> ci.tcl ci_test.c world hello, world
Brian Theado 2007-10-09 - Nice. I saw in the tcc README file you can use the tcc executable in the shebang line on unix to get C-based shell scripting (i.e. '#!/usr/bin/tcc -run'). You could do something similar with your ci.tcl (I guess you would need to parse out the shebang line like 'tcc -run' must do).
I wonder if there are any ideas to borrow from C REPL (Read-Eval-Print-Loop) at [L1 ]
RS: Well, tcc is pretty incremental, so we can just use the read-eval-print loop of an interactive tclsh for that, witness this sample session:
/Tcl/lib/tcc02> tclsh % package req critcl 0.1.1 % critcl::cproc mul {double a double b} double {return a*b;} % mul 6 7 42.0
Or this:
% critcl::ccode {#include <string.h>} % critcl::cproc strerror {int n} char* {return strerror(n);} % strerror 1 Operation not permitted % strerror 2 No such file or directory % strerror 3 No such process
RS 2007-10-10: The breaking of the main() interface above left me restless, so I hacked up this variation, which in all ugliness (global, static, strtok..) at least accepts a canonical main():
#!/usr/bin/env tclsh #-- ci.tcl -- a tiny C interpreter built on tcltcc package require critcl set f [open [lindex $argv 0]] set data [read $f][close $f] critcl::ccode $data critcl::ccode { #include <string.h> #define MAX_ARGC 64 int g_argc = 0; static char** parse_args(char* args) { static char* s_argv[MAX_ARGC+1]; char *cp = strtok(args," "); while(cp) { s_argv[g_argc] = cp; if(++g_argc >= MAX_ARGC) break; cp = strtok(NULL, " "); } s_argv[g_argc] = NULL; return &s_argv; } } critcl::cproc main {char* args} int { char** argv = parse_args(args); return main(g_argc, argv); } main $argv
The sample C program is now more canonical and robust, and supplies a default argument ("you") if there is none in argv:
/* ci_test.c -- demo program for ci.tcl */ #include <stdio.h> int main(int argc, char* argv[]) { char* cp = "you"; if(argc>1) cp = argv[1]; printf("hello, %s\n", cp); return 0; }
Testing:
/Tcl/lib/tcc02> ci.tcl ci_test.c world hello, world /Tcl/lib/tcc02> ci.tcl ci_test.c hello, you
RFox 3/13/2012 - In case you are looking for a full fledged C interpreter: http://root.cern.ch/drupal/content/cint