[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 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 [http://neugierig.org/software/c-repl/] [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 } % 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 #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 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 ---- [Category Example]