Version 31 of odyce

Updated 2007-10-02 15:03:16 by suchenwi

Embedded C compiler. Similar to critcl, but without needing a compiler or linker to be installed on target machine. Currently available only as part of eTcl binary distribution. The name is for "Autonomous Dynamic C Environment" ("Au" in French is pronounced like "O").

List of supported architectures:

  • Linux: x86 and arm
  • Win32
  • WinCE (Windows Mobile)
  • Mac OS X: x86

Official home page: http://www.evolane.com/software/odyce/index.html

Partial emulation of Critcl on top of Odyce is also available.


RS 2007-10-01: http://www.evolane.com/software/odyce/index.html says: "This package contains softwares covered by other licenses: TinyCC .. LGPL". Is that tcc? Also, some examples would be very welcome. That pages mentions that no headers are required - not even <stdio.h> and friends? EH System headers, together with Tcl/Tk ones, are embedded into VFS, and system include directory for embedded C compiler is automatically setup to find them there. So you can use #include <stdio.h> and friends in your code without having to care about any header installed on target machine.

wdb 2007-10-01 -- just downloaded etcl, but sadly, no documentation available. Are there any users out there with some example applications? EH 2007-10-01 Odyce announce, together with 1.0-rc23, was a very early announce, to let others know about this previously hidden feature. We are working hard this week on providing samples, documentation, and improving critcl emulation (so that all critcl documentation should be adequate too). This page is good place to keep in sync until our official site gets updated.

RS: Eric Hassold himself posted an example today on fr.comp.lang.tcl:

 # Inutile de charger odyce 
 # package require odyce 
 # Car l'emulation de critcl s'en charge 
 package require critcl 


 # Bug stupide dans la version embarquee 
 # (ai oublie d'initialiser la liste a vide) 
 #::critcl::csources "" 
 set ::critcl::critcl(cfiles) [list] 

 # Un bout de code C a integer 
 critcl::ccode { 
 #include <stdio.h> 
 #include <math.h> 

   static int fib(int n) 
   { 
     if (n <= 2) { 
       return 1; 
     } else { 
       return fib(n-1) + fib(n-2); 
     } 
   } 
 } 
 # Et la definition de la commande Tcl "fib" a partir 
 # du code natif 
 critcl::ccommand fib {dummy interp objc objv} { 
   int n; 

   if (objc!=1) { 
     Tcl_WrongNumArgs(interp,1,objv,"int"); 
   } 
   if (Tcl_GetIntFromObj(interp,objv[1],&n)!=TCL_OK) { 
     return TCL_ERROR; 
   } 
   Tcl_SetObjResult(interp, Tcl_NewIntObj(fib(n))); 
   return TCL_OK; 
 } 


 # Ca va vite, tres vite... 
 puts [fib 30] 

Works by reporting 832040 on stdout - I suppose it's correct :) For comparison, I also did a Tcl version:

 proc fib0 n {
    if {$n <= 2} {return 1}
    expr {[fib0 [expr {$n-1}]] + [fib0 [expr {$n-2}]]}
 }
 puts Odyce:[fib 30],[time {fib 30}]
 puts Tcl:[fib0 30],[time {fib0 30}]

which gives the same result, but takes about 171 times longer:

 Odyce:832040,10578 microseconds per iteration
 Tcl:832040,1808314 microseconds per iteration

Next idea: to wrap a C command into a proc that has it compiled at first call:

 proc Fib x {
    critcl::ccommand Fib {dummy interp objc objv} { 
        int n; 
        if (objc!=1) {Tcl_WrongNumArgs(interp,1,objv,"int");} 
        if (Tcl_GetIntFromObj(interp,objv[1],&n)!=TCL_OK) {return TCL_ERROR;}
        Tcl_SetObjResult(interp, Tcl_NewIntObj(fib(n))); 
        return TCL_OK; 
    }
    uplevel 1 Fib $x
 }
 puts wrapped:[Fib 30],[time {Fib 30}]

which, not surprisingly, fares equally well:

 Odyce:832040,10203 microseconds per iteration
 Tcl:832040,1871121 microseconds per iteration
 wrapped:832040,10511 microseconds per iteration

The difference here is that only if Fib is called (the first time), the C code is compiled. If nobody calls Fib, we save the (not high) time and memory consumption...

Lars H, 2007-10-02: As I recall it, Critcl normally puts the command to actually compile C code into the auto_index array, meaning nothing gets compiled at critcl::ccommand time, but rather the first time the command is called. If odyce does it differently, then that is a difference that is worth observing.


Just tested, the examples run on Windows Mobile 5 too, and with an ever bigger gain of 619 (!):

 Odyce:832040,172293 microseconds per iteration
 Tcl:832040,106723982 microseconds per iteration
 wrapped:832040,172042 microseconds per iteration

Re available commands, good old introspection to the rescue:

 21% info commands critcl::*
 ::critcl::init ::critcl::cleanname ::critcl::ccode ::critcl::debug ::critcl::clibraries
 ::critcl::Log ::critcl::ccommand ::critcl::checkname ::critcl::cproc ::critcl::csources
 ::critcl::tk ::critcl::cheaders

Or, to see which header files come "in the box":

 % cd d:/Tcl/etcl/etcl.exe/.etcl/vfs/extensions/odyce0.1/include
 % lsort [glob *]
 X11 assert.h dlfcn.h features.h float.h limits.h malloc.h math.h memory.h stab.h stdarg.h
 stdbool.h stddef.h stdio.h stdlib.h string.h tcl.h tclDecls.h tclPlatDecls.h 
 tk.h tkDecls.h tkIntXlibDecls.h varargs.h
 % cd X11
 % lsort [glob *]
 X.h Xatom.h Xfuncproto.h Xlib.h Xutil.h cursorfont.h keysym.h keysymdef.h license.terms


RS 2007-10-01: Until odyce supports critcl::cproc properly, the following little "source code generator" can help (or serve as starter) in simple cases:

 proc cproc {name argl rtype body} {
    set n 0
    set cbody "critcl::ccommand $name {dummy interp objc objv} \{\n"
    foreach {type var} $argl {
        append cbody "$type $var;\n"
        incr n
    }
    append cbody "if (objc!=$n) Tcl_WrongNumArgs(interp,$n,objv,\"int\");\n" 
    set n 0
    foreach {type var} $argl {
        append cbody "if (Tcl_Get[tmap $type]FromObj(interp,objv\[[incr n]\],&$var)!=TCL_OK) return TCL_ERROR;\n"
    }
    foreach line [split $body \n] {
        if [regexp {return(.+)} $line -> ret] {
            set ret [string trimright $ret ";"]
            append cbody "Tcl_SetObjResult(interp, Tcl_New[tmap $rtype]Obj($ret));\n" 
        } else {append cbody $line\n}
    }

    append cbody "return TCL_OK;\n\}"
    puts $cbody
    eval $cbody
 }
 proc tmap type {
    switch -- $type {
        int     {return Int}
        default {error "unknown type $type"}
    }
 }

From the specification

 cproc fiba {int x int y} int {
    return fib(x)+y;
 }

it produces, and tcc takes it without complaining:

 critcl::ccommand fiba {dummy interp objc objv} {
 int x;
 int y;
 if (objc!=2) Tcl_WrongNumArgs(interp,2,objv,"int");
 if (Tcl_GetIntFromObj(interp,objv[1],&x)!=TCL_OK) return TCL_ERROR;
 if (Tcl_GetIntFromObj(interp,objv[2],&y)!=TCL_OK) return TCL_ERROR;

 Tcl_SetObjResult(interp, Tcl_NewIntObj( fib(x)+y));

 return TCL_OK;
 }

However, experimenting with an unknown function name

 cproc fiba {int x int y} int {
    return cfib(x)+y;
 }

crashes eTcl with an unknown exception, instead of reporting the problem, as it does with unknown variables.


EH 2007-10-01: From http://www.evolane.com/download/devel/ , you can get a zip archive containing an updated critcl.tcl package (version 0.1.1) and several demos (fib, md5 are Tcl only, LRI [L1 ] uses Tk). Critcl package included into this snapshot fixes several bugs, and provides an emulation for ::critcl::cproc API. More development snapshots will be made available in next days, until eTcl 1.0-rc24 is made available. Feedback and/or submisions for inclusion into this critcl emulation are welcome.

cche 2007-10-01: Tested the fib example on an older HTC9500 with the "pocketpc 2000/2002 no official support" and the odyce/critcl version was 190x faster than the pure tcl version. This is a really nice addition to eTcl. Thanks a lot!

RS Many thanks from me too! With the updated critcl.tcl as from above, the fib example boils down to

 #package require critcl
 source [info script]/../critcl.tcl

 # Un bout de code C a integer 
 critcl::ccode { 
    static int fib(int n) { 
        return n <= 2? 1 : fib(n-1) + fib(n-2); 
    }
 }
 critcl::cproc fib {int n} int {
    return fib(n);
 }
 #-- version Tcl 
 proc fib0 n {
    if {$n <= 2} {return 1}
    expr {[fib0 [expr {$n-1}]] + [fib0 [expr {$n-2}]]}
 }
 puts tcc:[fib 30],[time {fib 30}]
 puts Tcl:[fib0 30],[time {fib0 30}]

or even ...

 #package require critcl
 source [info script]/../critcl.tcl

 # Un bout de code C a integer 
 critcl::ccode { 
    static int fib(int n) {return n <= 2? 1 : fib(n-1) + fib(n-2);}
 }
 critcl::cproc fib {int n} int {return fib(n);}

 #-- version Tcl 
 proc fib0 n {
    expr {$n <= 2? 1 : [fib0 [expr {$n-1}]] + [fib0 [expr {$n-2}]]}
 }

Another example (if we had no string reverse, i.e. before 8.5):

 critcl::ccode {
    static size_t strlen(char* s) {
        size_t n = 0;
        while(*s) {s++; n++;}
        return n;
    }
 }
 critcl::cproc strrev {char* s} char* {
    char *cp0, *cp1, t;
    for (cp0=s, cp1=s+strlen(s)-1; cp1 > cp0; cp0++, cp1--) {
                 t=*cp0; *cp0=*cp1; *cp1=t;
    }
    return s;
 }
 puts [strrev "A man, a plan, a canal - Panama"]

 amanaP - lanac a ,nalp a ,nam A

I could include <string.h>, but strlen() was still unknown, so I had to roll my own above. eTcl must sure be linked with some lib that provides strlen()...

EH Odyce uses internally an hash table initialized at startup with all system symbols allowed in C codes. All Tcl/Tk symbols are there, on all platforms (so you could rewrite code above to use DString instead of char*, and use Tcl_DStringLength()). For system symbols (e.g. the one from libc), I took some time defining nearly all available symbols on Linux version of eTcl/Odyce. However, Win32 and WinCE initial tables has been filled only with a very minimal set for testing. This is of course only due to the early stage of the project, and will be fixed very quickly. For information, together with all public Tcl and Tk symbols, odyce version embedded into eTcl 1.0-rc23 for Win32/WinCE declares symbols fprintf, fputc, fputs, fread, fwrite, fscanf, fseek, ftell, ferror, fflush, fgetc, feof, fdopen, fgets, printf, snprintf, sprintf, vfprintf, vprintf, vsnprintf, vsprintf, fileno, putc, putchar, gets, malloc, free, memchr, memcmp, memcpy, memset, memmove, strcmp, tolower and toupper. Next version will provide same symbols table on all architectures.


RS Further playing with the new critcl.tcl, I changed two places: Logging on:

 /Tcl/etcl $ diff critcl.tcl odyce-demo/lib/
 10c10
 <   puts $args
 ---
 >   # puts $args

Avoid syntax errors in one-liners like critcl::ccode {int foo = 42;}

 64c64
 <   append critcl(code) $code\n
 ---
 >   append critcl(code) $code

Display generated code, for debugging:

 133c133
 <   Log "COMPILING MEMORY: $code"
 ---
 >   Log "COMPILING MEMORY:"

Category Package - Category Development