/* This program translate .tlcc files into C++ (.cpp) files. The C++ files are compiled and deleted. See ../rules.mak to see how it is done. (cctlcc is normally used to compile .tlcc file and uses tlcc to perform the translation, tlcc is seldom used by end users) tlcc files are TLMP source code. See http://www.solucorp.qc.ca/tlmp to understand what TLMP is. For short, TLMP allow generic reusable code and it is great :-) */ /* How it works ... First, TLMP is a superset of C++. This means that a .tlcc file may hold anything a C++ source file can hold. It may also contains extra TLMP constructs. (Unless you understand TLMP and how it is implemented (the _F_xxx classes for example), you will be completly lost...) This program is not a real compiler and as such, does not try to understand everything of the C++ syntax. It simply skip over and intercept the TLMP constructs. It uses a recursive function (tlcc_do) and multiple buffer object (BUFSTR) to collect the various information it collects. We will describe how TLMP code is translate to C++ and this will helps understand the purpose of the various buffers. int main() { (args); f1_code... f2_code... } This sequence is translated this way. First we must create a new class derived from _F_foo. We use a trick to create unique identifier using the path of the source file and some counters. class uniquetoken: public _F_foo { public: type f1(args); type f2(args); }; Now tlcc has no idea of the exact prototype of f1 and f2. To learn that it would have to parse the header file defining the foo component. It uses instead the special macros used to create the component. So the class become class uniquetoken: public _F_foo { public: _F_foo_f1( ); _F_foo_f2( ); }; Then later it creates the definition for the member functions, again using the special macros _F_foo_f1(uniquetoken::) { f1_code...; } _F_foo_f2(uniquetoken::) { f2_code...; } Now that we have defined the special uniquetoken object derived from _F_foo, we can use it int main() { uniquetoken scopeobj1; foo (scopeobj1,args); } So we see how some statements are diverted and reordered. Now here is another example, but using glocal. We see how the glocal scope object are passed around. int main() { glocal int a; (args); f1_code... glocal.a = 1; f2_code... glocal.a = 2; } The trick is to realise that is indead a member function of the uniquetoken object above. So the uniquetoken object is alive in the f1 and f2 functags. We end up with the following translation. struct uniqueglocal { int a; }; class uniquetoken: public _F_foo { public: uniqueglocal &glocal; uniquetoken(uniqueglocal &_gl): glocal(_gl){} _F_foo_f1( ); _F_foo_f2( ); }; _F_foo_f1(uniquetoken::) { f1_code...; glocal.a = 1; } _F_foo_f2(uniquetoken::) { f2_code...; glocal.a = 2; } And the calling code becomes int main() { uniqueglocal glocal1; uniquetoken scopeobj1(glocal1); foo (scopeobj1,args); } So some code has to be inserted (the uniqueglocal and uniquetoken definition) before the , some has to be insert before the main. And this has to work with nested calls int main() { glocal int a = 0; (args); f1_code... (args); glocal.a = 1; f2_code... glocal.a = 2; } Now tlcc is pretty dumb. It does not read the various include files. It simply assumes that for a call to foo, there is a class _F_foo. It assumes that for a functag f1 used in the call to foo there is a macro called _F_foo_f1. Now what happen if f1 does not exist at all or even foo is not a component, or you simply forgot to include the proper header. It can't tell. To help, it generates some code to test the proper macros. Before starting to generate stuff for the foo component, it generates a bunch of preprocessor tests. #ifndef _TLMP_foo #error TLMP module foo unknown #endif #ifndef _F_foo_f1 #error Unknown logical function f1 for module/class foo #endif #ifndef _F_foo_f2 #error Unknown logical function f2 for module/class foo #endif About using glocals. In the above case where we set glocal.a to 1 in the foo2 functag, there is an issue. Mostly, we end up with nested glocals. We end up with one class derived from _F_foo, holding a reference to our glocal object. Then we have a class derived from _F_foo2 holding a reference to another glocal object, itself holding a reference to the first glocal object. So "glocal.a=1" becomes "glocal.glocal.a=1". Also glocal object are used to track the various _F_ derived object so the following code works (see the call to glocal.foo.f2()). int main() { glocal int a = 0; (args); f1_code... (args); glocal.a = 1; glocal.foo.f2(...); f2_code... glocal.a = 2; } A note about glocals. Glocal variable are simply defined by prefixing the variable definition with the keyword glocal. All glocals defined in a row are ending in the same object. A glocal defined after a C++ statement starts a new glocal object. In the following sample, we will end up with 2 nested (one referencing this other) glocal objects. int main () { glocal int a = 0; int xyz = 0; glocal int b = 0; (args); glocal.a = 1; } In the previous example, the glocal.a assignment is turned to "glocal.glocal.a = 1". If we move the declaration of glocal.b before xyz, we would end up with a single object. tlcc does not do any attempt to move things around for optimisation. Note that working this way solves the scope issue. Here is another example. int main () { glocal int a = 0; if (some_condition){ glocal someclass b; (args); glocal.a = 1; // The destructor for object glocal.b will be executed }else{ // At this point, glocal.b does not exist } // At this point, glocal.b does not exist } Also, the original syntax for glocal declaration was based on and marker like this int main () { int a; glocal.a = 0; (args); } tlcc still supports this syntax but we generally avoid it. It is cumbersome and does not allow for simple initialisation (You have to define the variable and then do assignments. */ #include #include #include #include #include #include #include #include #include #define TLCC_VERSION "1.2" struct TLCC_MOD_VER{ char *module; char *version; TLCC_MOD_VER *next; TLCC_MOD_VER(const char *_module, const char *_version, TLCC_MOD_VER *_next) { module = strdup(_module); version = strdup(_version); next = _next; } ~TLCC_MOD_VER() { free (module); free (version); } void setver (const char *_newver) { free (version); version = strdup (_newver); } }; static TLCC_MOD_VER *first_modver = NULL; enum FEED_TOKEN { T_STR, // C++ stuff. Mostly go through without much checking T_MARK, // TLMP tag T_SPC, // Special character { } ; used to handle scope T_COMMENT, // This is a comment T_EOF // End of file }; class FEED{ const char *fname; FILE *fin; char buf[1000]; char *s; int noline; bool comment_mode; struct { FEED_TOKEN token; char s1[1000]; char s2[1000]; char s3[1000]; }pval; /*~PROTOBEG~ FEED */ public: FEED (const char *_fname, FILE *_fin); void fill (void); char getcar (void); const char *getfname (void)const; int getnoline (void)const; FEED_TOKEN gettoken (char *str, char *arg1, char *arg2); void printerr (const char *s, ...); void push (FEED_TOKEN tok, const char *s1, const char *s2, const char *s3); /*~PROTOEND~ FEED */ }; PUBLIC FEED::FEED(const char *_fname, FILE *_fin) { fname = _fname; fin = _fin; noline = 0; comment_mode = false; fill(); pval.token = T_EOF; pval.s1[0] = pval.s2[0] = pval.s3[0] = '\0'; } PUBLIC void FEED::fill() { s = buf; buf[0] = '\0'; fgets (buf,sizeof(buf)-1,fin); noline++; } PUBLIC int FEED::getnoline() const { return noline; } PUBLIC const char *FEED::getfname() const { return fname; } PUBLIC void FEED::printerr (const char *s, ...) { fprintf (stderr,"File %s, line %d: ",getfname(),getnoline()); va_list list; va_start (list,s); vfprintf (stderr,s,list); va_end (list); } PUBLIC char FEED::getcar() { char ret = '\0'; if (*s == '\0') fill(); if (*s != '\0'){ ret = *s++; } return ret; } /* Push back a token. gettoken will get this one first */ PUBLIC void FEED::push ( FEED_TOKEN token, const char *s1, const char *s2, const char *s3) { pval.token = token; strcpy (pval.s1,s1); strcpy (pval.s2,s2); strcpy (pval.s3,s3); } static char *tlcc_copystr(char *dst, char *s) { char spccar = *s++; *dst++ = spccar; while (*s != '\0'){ if (*s == spccar){ *dst++ = *s++; break; }else if (*s == '\\'){ *dst++ = *s++; if (*s != '\0') *dst++ = *s++; }else{ *dst++ = *s++; } } *dst = '\0'; return s; } PUBLIC FEED_TOKEN FEED::gettoken (char *str, char *arg1, char *arg2) { arg1[0] = arg2[0] = str[0] = '\0'; FEED_TOKEN ret = T_EOF; if (pval.token != T_EOF){ ret = pval.token; strcpy (str,pval.s1); strcpy (arg1,pval.s2); strcpy (arg2,pval.s3); pval.s1[0] = '\0'; pval.s2[0] = '\0'; pval.s3[0] = '\0'; pval.token = T_EOF; }else{ if (*s == '\0') fill(); char *start = str; while (*s != '\0'){ if (comment_mode){ while (*s != '\0' && !(s[0] == '*' && s[1] == '/')) *str++ = *s++; }else{ while (*s != '\0' && *s != '<' && *s != '/' && *s != '*' && *s != '"' && *s != '\'' && *s != '{' && *s != '}' && *s != ';' && *s != '=') *str++ = *s++; } *str = '\0'; if (str > start){ ret = comment_mode ? T_COMMENT : T_STR; break; }else if (s[0] == '{' || s[0] == '}' || s[0] == ';' || s[0] == '='){ *str++ = *s++; *str = '\0'; ret = T_SPC; break; }else if (s[0] == '/' && s[1] == '/'){ while (*s != '\0') *str++ = *s++; *str = '\0'; ret = T_COMMENT; break; }else if (s[0] == '/' && s[1] == '*'){ *str++ = '/'; *str++ = '*'; *str = '\0'; s += 2; ret = T_COMMENT; comment_mode = true; break; }else if (s[0] == '*' && s[1] == '/'){ *str++ = '*'; *str++ = '/'; *str = '\0'; s += 2; ret = T_COMMENT; comment_mode = false; break; }else if (s[0] == '/' || s[0] == '*'){ *str++ = *s++; *str = '\0'; ret = T_STR; break; }else if (*s == '"' || *s == '\''){ s = tlcc_copystr (str,s); ret = T_STR; break; }else if (*s == '<'){ if (comment_mode){ *str++ = *s++; }else if (isalpha(s[1])){ char id1[1000],id2[1000],id3[1000]; char *pt = id1; char *ss = s+1; while (isalnum(*ss) || *s == '_'){ *pt++ = *ss++; } *pt = '\0'; while (isspace (*ss)) ss++; if (*ss == '>'){ strcpy (str,id1); ret = T_MARK; s = ss+1; break; }else if (isalpha(*ss)){ pt = id2; while (isalnum(*ss) || *ss == '_'){ *pt++ = *ss++; } *pt = '\0'; while (isspace (*ss)) ss++; if (*ss == '>'){ ret = T_MARK; strcpy (str,id1); strcpy (arg1,id2); s = ss+1; break; }else if (isalpha(*ss)){ pt = id3; while (isalnum(*ss) || *ss == '_'){ *pt++ = *ss++; } *pt = '\0'; while (isspace (*ss)) ss++; if (*ss == '>'){ ret = T_MARK; strcpy (str,id1); strcpy (arg1,id2); strcpy (arg2,id3); s = ss+1; break; }else{ while (s < ss) *str++ = *s++; } }else{ while (s < ss) *str++ = *s++; } }else{ while (s < ss) *str++ = *s++; } }else if (s[1] == '/'){ *str++ = '/'; s += 2; while (isalpha(*s)) *str++ = *s++; *str = '\0'; while (isspace (*s)) s++; if (*s == '>'){ s++; }else{ fprintf (stderr,"File %s: Missing closing > at line %d\n" ,fname,noline); } ret = T_MARK; break; }else{ *str++ = *s++; if (*s == '\0'){ *str = '\0'; ret = T_STR; break; } } } } } //fprintf (stderr,"%d: %s\n",noline,start); return ret; } class BUFSTR{ char *buf; int size; int pos; /*~PROTOBEG~ BUFSTR */ public: BUFSTR (void); void append (char car); void append (const char *s); void appendf (const char *ctl, ...); const char *getbuf (void); void reset (void); void setnoline (FEED&feed); ~BUFSTR (void); /*~PROTOEND~ BUFSTR */ }; PUBLIC BUFSTR::BUFSTR() { buf = (char*)malloc(1000); assert (buf != NULL); size = 1000; pos = 0; buf[0] = '\0'; } PUBLIC BUFSTR::~BUFSTR() { free (buf); } PUBLIC void BUFSTR::append (const char *s) { int len = strlen (s); // fprintf (stderr,"%p %p size %d len %d pos %d\n",buf,s,size,len,pos); if (pos + len + 1 > size-2){ size += 1000; if (len >= 500) size += len; buf = (char*)realloc (buf,size); assert (buf != NULL); } strcpy (buf+pos,s); pos += len; } PUBLIC void BUFSTR::reset () { buf[0] = '\0'; pos = 0; } PUBLIC void BUFSTR::setnoline (FEED &feed) { appendf ("\n#line %d \"%s\"\n",feed.getnoline(),feed.getfname()); } PUBLIC void BUFSTR::appendf (const char *ctl, ...) { va_list list; va_start (list,ctl); char tmp[1000]; vsnprintf (tmp,sizeof(tmp)-1,ctl,list); va_end (list); append (tmp); } PUBLIC void BUFSTR::append (char car) { char tmp[2]; tmp[0] = car; tmp[1] = '\0'; append (tmp); } PUBLIC const char *BUFSTR::getbuf() { return buf; } enum COMPSTATE { COMPSTATE_NONE, // Not in a COMPSTATE_MOD, // Inside a block COMPSTATE_GLOCAL, // Inside block COMPSTATE_CALL, // Inside block, expecting COMPSTATE_OBJ, // Inside block, expecting COMPSTATE_F, // Inside , expecting other or }; class MODSTATE{ public: int nomod; int nocall; bool in_module; int noglocal; COMPSTATE states[100]; int nbstates; COMPSTATE state; bool some_errors; /*~PROTOBEG~ MODSTATE */ public: MODSTATE (void); void outofcontext (FEED&feed); int popstate (COMPSTATE expect, FEED&feed); void pushstate (COMPSTATE st, FEED&feed); /*~PROTOEND~ MODSTATE */ }; PUBLIC MODSTATE::MODSTATE() { nomod = 0; nocall = 0; noglocal = 0; in_module = false; state = COMPSTATE_NONE; nbstates = 0; some_errors = false; } PUBLIC void MODSTATE::outofcontext (FEED &feed) { feed.printerr ("Block is used out of context\n"); some_errors = true; } PUBLIC void MODSTATE::pushstate (COMPSTATE st, FEED &feed) { if (st == COMPSTATE_F){ if (state != COMPSTATE_CALL && state != COMPSTATE_OBJ){ outofcontext (feed); } }else if (st == COMPSTATE_CALL){ if (state != COMPSTATE_MOD && state != COMPSTATE_F){ outofcontext (feed); } }else if (st == COMPSTATE_GLOCAL){ if (state != COMPSTATE_MOD && state != COMPSTATE_F){ outofcontext (feed); } } states[nbstates++] = state; state = st; } PUBLIC int MODSTATE::popstate (COMPSTATE expect, FEED &feed) { int ret = -1; if (nbstates == 0){ feed.printerr ("Can't end a section, no sectio opened\n"); some_errors = true; }else if (state != expect){ feed.printerr ("Closing section missmatch\n"); }else{ state = states[--nbstates]; ret = 0; } return ret; } static char scope_prefix[200]; static char glocal_prefix[200]; /* Replace all the non digit and alpha characters by a _ */ static void tlcc_filterid (char *pt) { while (*pt != '\0'){ if (!isalpha(*pt) && !isdigit(*pt)) *pt = '_'; pt++; } } /* Compute the "somewhat" unique id prefix for scope objects and glocals. We are using both the source file name and the current working directory to create a unique identifier. Almost unique. It is assumed that a source A in directory B is a rather unique condition. Ideally, we need a way to declare classes with source scope. Is there a way to achieve that in C++ ? */ static void tlcc_setscopeprefix (const char *fname) { char cwd[PATH_MAX]; if (getcwd(cwd,sizeof(cwd)-1)!=NULL){ char *pt = strrchr(cwd,'/'); if (pt != NULL){ pt++; }else{ pt = cwd; } snprintf(scope_prefix,sizeof(scope_prefix)-1,"__sc_%s_%s" ,pt,fname); tlcc_filterid (scope_prefix); snprintf(glocal_prefix,sizeof(glocal_prefix)-1,"__gl_%s_%s" ,pt,fname); tlcc_filterid (glocal_prefix); }else{ fprintf (stderr,"Can't get the current working directory\n"); exit (-1); } } // Put some code to insure that the module exists or that // the header is include. If not, strange C++ errors are printed static void tlcc_checkifdef( BUFSTR &insert, const char *module, const char *type) // "class" or "module" { insert.appendf ("#ifndef _TLMP_%s\n",module); insert.appendf ("\t#error TLMP %s %s unknown\n",type,module); insert.appendf ("#endif\n"); } // One variable member of a glocal section // This is used to track in which glocal a glocal variable is available // so glocal.var may be translated to glocal.glocal.glocal.var as needed. class GLOCAL_VAR{ char *name; GLOCAL_VAR *next; /*~PROTOBEG~ GLOCAL_VAR */ public: GLOCAL_VAR (const char *_name, GLOCAL_VAR *_next); const char *getname (void)const; GLOCAL_VAR *getnext (void)const; ~GLOCAL_VAR (void); /*~PROTOEND~ GLOCAL_VAR */ }; PUBLIC GLOCAL_VAR::GLOCAL_VAR(const char *_name, GLOCAL_VAR *_next) { next = _next; name = strdup(_name); } PUBLIC GLOCAL_VAR::~GLOCAL_VAR() { free (name); } PUBLIC const char *GLOCAL_VAR::getname() const { return name; } PUBLIC GLOCAL_VAR *GLOCAL_VAR::getnext() const { return next; } // Variables member of a glocal section and pointer // to parent glocal sections. class GLOCAL_VARS{ GLOCAL_VARS *parent; GLOCAL_VAR *var; int glocal_level; /*~PROTOBEG~ GLOCAL_VARS */ public: GLOCAL_VARS (GLOCAL_VARS *_parent, int _glocal_level); private: void add (const char *name); public: int getglevel (void)const; GLOCAL_VARS *getparent (void)const; int getscope (const char *name); void parse (const char *line); ~GLOCAL_VARS (void); /*~PROTOEND~ GLOCAL_VARS */ }; PUBLIC GLOCAL_VARS::GLOCAL_VARS (GLOCAL_VARS *_parent, int _glocal_level) { parent = _parent; var = NULL; glocal_level = _glocal_level; } PUBLIC GLOCAL_VARS::~GLOCAL_VARS () { while (var != NULL){ GLOCAL_VAR *next = var->getnext(); delete var; var = next; } } PUBLIC GLOCAL_VARS* GLOCAL_VARS::getparent() const { return parent; } PUBLIC int GLOCAL_VARS::getglevel() const { return glocal_level; } PRIVATE void GLOCAL_VARS::add (const char *name) { var = new GLOCAL_VAR(name,var); } /* Find the distance of a variable from the current (closest) glocal block. Return 0 if it is in the current glocal block, 1 if it is in the one just before the current one and -1 if the variable is not in any glocal blocks. */ PUBLIC int GLOCAL_VARS::getscope(const char *name) { int ret = -1; GLOCAL_VAR *p = var; while (p != NULL){ if (strcmp(p->getname(),name)==0){ ret = 0; break; } p = p->getnext(); } if (ret == -1 && parent != NULL){ ret = parent->getscope(name); if (ret != -1) ret++; } return ret; } static const char *tlcc_copyid (char *dst, const char *src, int maxlen) { *dst = '\0'; while (!isalpha(*src) && *src != '\0') src++; if (isalpha(*src)){ int len = 0; while ((isalpha (*src) || isdigit(*src) || *src == '_') && len < maxlen){ len++; *dst++ = *src++; } *dst = '\0'; } return src; } PUBLIC void GLOCAL_VARS::parse (const char *line) { while (*line != '\0'){ char word[1000]; line = tlcc_copyid (word,line,sizeof(word)-1); // This is really minimal. We pick any valid C++ identifier // be it a keyword or a variable name and add it to the list // of known variable. // If the user enter glocal.const for example, we will correctly // find the place where const was used inside a glocal section // but the compiler won't be fooled. if (word[0] != '\0') add (word); } } static TLCC_MOD_VER *tlcc_locateversion (const char *module) { TLCC_MOD_VER *ret = NULL; TLCC_MOD_VER *pt = first_modver; while (pt != NULL){ if (strcmp(pt->module,module)==0){ ret = pt; break; } pt = pt->next; } return ret; } /* Locate the version of a module we must use and compose its name. The version is the suffix. Return 1 if the module has a version suffix. */ static int tlcc_setmodver(char *full, const char *module) { int ret = 0; TLCC_MOD_VER *pt = tlcc_locateversion (module); if (pt == NULL){ strcpy (full,module); }else{ sprintf (full,"%s%s",module,pt->version); ret = 1; } return ret; } /* Replace the sequences glocal.var in s1 with the proper glocal.glocal...var sequence by searching the variable var in the nested glocal blocks. */ static void tlcc_fixglocal( GLOCAL_VARS *vars, char *s1) { if (vars != NULL){ char *pt = s1; while ((pt = strstr(pt,"glocal."))!=NULL){ char word[1000]; pt += 7; // Skil glocal. const char *newpt = tlcc_copyid(word,pt,sizeof(word)-1); if (word[0] != '\0'){ // We have to locate in which glocal block // this variable was located const char *ptword = word; int level = vars->getscope(word); char newword[1000]; if (level == -1){ // Maybe this is a component and we must find the // version if (tlcc_setmodver (newword,word)==1){ level = vars->getscope(newword); if (level != -1) ptword = newword; } } // fprintf (stderr,"glocal scope %s %d\n",word,level); if (level >= 0){ // Normally, we do not need to fix // anything for a level 0, except if we are calling // a component with a version // For level > 0, we must insert as many // .glocal as needed int len = strlen(newpt); int level7 = level*7; int newlen = level7+strlen(ptword); memmove (pt+newlen,newpt,len+1); for (int i=0; i sequence */ static void tlcc_getparm (GLOCAL_VARS *vars, FEED &feed, BUFSTR &cur) { char carac; while ((carac = feed.getcar()) <= ' ') cur.append (carac); if (carac == '('){ tlcc_getendparm(vars,feed,cur); } } static void tlcc_delvars ( GLOCAL_VARS *&vars, GLOCAL_VARS *original_vars, BUFSTR &mainbuf) { while (vars != original_vars){ int level1 = vars->getglevel(); GLOCAL_VARS *parent = vars->getparent(); delete vars; vars = parent; int level0 = 0; if (vars != NULL) level0 = vars->getglevel(); char buf[100]; sprintf (buf,"\t}\t// For nested glocals %d -> %d\n",level1,level0); mainbuf.append (buf); } } class SCOPE{ public: GLOCAL_VARS *original_vars; bool original_auto_glocal; SCOPE *prec; /*~PROTOBEG~ SCOPE */ public: SCOPE (GLOCAL_VARS *_vars, SCOPE *_prec, bool auto_glocal); SCOPE *revert (GLOCAL_VARS *&vars, BUFSTR&mainbuf, bool&auto_glocal); /*~PROTOEND~ SCOPE */ }; PUBLIC SCOPE::SCOPE (GLOCAL_VARS *_vars, SCOPE *_prec, bool auto_glocal) { prec = _prec; original_vars = _vars; original_auto_glocal = auto_glocal; } /* Cleanup at the end of the SCOPE. Return the previous object */ PUBLIC SCOPE *SCOPE::revert(GLOCAL_VARS *&vars, BUFSTR &mainbuf, bool &auto_glocal) { // fprintf (stderr,"revert %p %p\n",vars,original_vars); tlcc_delvars (vars,original_vars,mainbuf); auto_glocal = original_auto_glocal; return prec; } /* Generate the glocal declaration */ static void tlcc_startglocal( FEED &feed, MODSTATE &st, int &glocal_level, BUFSTR &insert, BUFSTR *&cur, GLOCAL_VARS *&vars, bool nested_glocal, // There is already one glocal defined in this // block, so the new one points to it. const char *inmodule) // component name (call or obj) currently process // or NULL { st.pushstate (COMPSTATE_GLOCAL,feed); glocal_level = ++st.noglocal; insert.appendf ("struct %s_GLOCAL%d_%d{\n" ,glocal_prefix,st.nomod,glocal_level); if (vars != NULL){ int parent_glocal = vars->getglevel(); insert.appendf ("\t%s_GLOCAL%d_%d &glocal;\n" ,glocal_prefix,st.nomod,parent_glocal); if (nested_glocal){ // We must reference the previous glocal, not the module // Since the previous, or its ancestor has already done this inmodule = NULL; } if (inmodule){ insert.appendf ("\t_F_%s &%s;\n",inmodule,inmodule); insert.appendf ("\t%s_GLOCAL%d_%d(%s_GLOCAL%d_%d &gl,_F_%s *_c)\n" "\t\t: glocal(gl),%s(*_c)\n" "\t{}\n" ,glocal_prefix ,st.nomod,glocal_level ,glocal_prefix ,st.nomod,parent_glocal ,inmodule,inmodule); }else{ insert.appendf ("\t%s_GLOCAL%d_%d(%s_GLOCAL%d_%d &gl)\n" "\t\t: glocal(gl)\n" "\t{}\n" ,glocal_prefix ,st.nomod,glocal_level ,glocal_prefix ,st.nomod,parent_glocal); } }else if (inmodule != NULL){ insert.appendf ("\t_F_%s &%s;\n",inmodule,inmodule); insert.appendf ("\t%s_GLOCAL%d_%d(_F_%s *_c)\n" "\t\t: %s(*_c)\n" "\t{}\n" ,glocal_prefix ,st.nomod,glocal_level ,inmodule ,inmodule); } cur = &insert; vars = new GLOCAL_VARS (vars,glocal_level); if (inmodule != NULL) vars->parse (inmodule); } /* End the glocal declaration */ static void tlcc_endglocal ( FEED &feed, MODSTATE &st, BUFSTR &insert, BUFSTR &funcdef, BUFSTR &funccore, BUFSTR *&cur, GLOCAL_VARS *vars, const char *inmodule, bool nested_glocal) { int glocal_level = vars->getglevel(); st.popstate (COMPSTATE_GLOCAL,feed); insert.append ("};\n"); funcdef.append ("\t{\t// Inserted to support nested glocals\n"); if (vars->getparent() != NULL){ if (nested_glocal){ funcdef.appendf ("\t%s_GLOCAL%d_%d glocal(glocal);\n" ,glocal_prefix,st.nomod,glocal_level); }else if (inmodule != NULL){ funcdef.appendf ("\t%s_GLOCAL%d_%d glocal(this->glocal,this);\n" ,glocal_prefix,st.nomod,glocal_level); }else{ funcdef.appendf ("\t%s_GLOCAL%d_%d glocal(this->glocal);\n" ,glocal_prefix,st.nomod,glocal_level); } }else if (inmodule != NULL){ funcdef.appendf ("\t%s_GLOCAL%d_%d glocal(this);\n" ,glocal_prefix,st.nomod,glocal_level); }else{ funcdef.appendf ("\t%s_GLOCAL%d_%d glocal;\n" ,glocal_prefix,st.nomod,glocal_level); } cur = &funccore; funccore.setnoline (feed); } /* Load the versions of the various modules. This function may be used several time. The later module/version pairs override previous definition. In general, tlcc is called with option --compvers once or twice. The first option specify a file holding the lastest version of every components. The second specifies the version to use for this project. So a project is allowed to used older version of some components. */ static void tlcc_loadversion (const char *fname) { FILE *fin = fopen (fname,"r"); if (fin == NULL){ // A missing component/version file is ok // fprintf (stderr,"Can't open component version file %s (%s)\n" // ,fname,strerror(errno)); }else{ char line[200]; while (fgets(line,sizeof(line)-1,fin)!=NULL){ char module[100],version[100]; version[0] = '\0'; if (line[0] != '#' && sscanf(line,"%s %s\n",module,version)>=1){ TLCC_MOD_VER *pt = tlcc_locateversion(module); if (pt != NULL){ pt->setver (version); }else{ first_modver = new TLCC_MOD_VER(module,version,first_modver); } } } fclose (fin); } } // We must record the name of the variable, which is // the last id we see. Not 100% true if the variable // is an array. Later static void tlcc_pickid(const char *src, char *id) { while (*src != '\0'){ while (isspace(*src)) src++; while (!isalpha(*src) && src[0] != '\0') src++; if (isalpha(*src)){ char *dst = id; while (isalpha(*src) || isdigit(*src) || src[0] == '_'){ *dst++ = *src++; } *dst = '\0'; } } } /* Process glocal definition "glocal type var [ = value ] ;". This function is called with the first token pointing to glocal and will eat all consecutive glocal declaration. */ static void tlcc_lineglocal( FEED &feed, BUFSTR &defs, // Record variable definition BUFSTR &assigns) // Record variable assignment { char s1[1000],s2[1000],s3[1000]; FEED_TOKEN token; BUFSTR *cur = NULL; // Information is added to cur as it is read // cur acts both as an output selector and // also as a state: // cur == NULL: Start of the line, expect glocal // cur == &defs: Collecting variable definition // cur == &assigns: Collecting variable assignedment char id[1000]; strcpy (id,"**UNKNOWN**"); bool seen_equal = false; while ((token=feed.gettoken(s1,s2,s3))!=T_EOF){ // fprintf (stderr,"line %d :%s:\n",token,s1); if (token == T_COMMENT){ }else if (token == T_STR){ if (cur == NULL){ const char *pt = s1; while (isspace(*pt)) pt++; if (strncmp(pt,"glocal",6)==0 && (isspace(pt[6]) || pt[6] == '\0')){ cur = &defs; cur->setnoline (feed); cur->append (pt+6); tlcc_pickid(pt+6,id); }else if (pt[0] != '\0'){ break; } }else{ cur->append (s1); if (cur == &defs){ tlcc_pickid (s1,id); } } }else if (cur == NULL){ break; }else if (token == T_SPC){ if (s1[0] == '=' && !seen_equal){ cur->append (";\n"); cur = &assigns; cur->setnoline (feed); cur->append ("glocal."); cur->append (id); cur->append ("="); seen_equal = true; }else if (s1[0] == ';'){ cur->append (";\n"); strcpy (id,"**unknown**"); cur = NULL; seen_equal = false; }else{ cur->append (s1); } }else if (token == T_MARK){ cur->append ("<"); cur->append (s1); cur->append (s2); cur->append (s3); cur->append (">"); }else{ cur->append (s1); } } feed.push (token,s1,s2,s3); } static int tlcc_do ( FEED &feed, BUFSTR &insert, // Stuff which will go at the start of the C++ // source BUFSTR &funcdef, // Function definitions BUFSTR &mainbuf, // Code copied and generated MODSTATE &st, FILE *fout, int glocal_level, GLOCAL_VARS *vars, const char *inmodule) // component name (call or obj) currently process // or NULL { int ret = 0; BUFSTR glocal; // Definition of all glocals BUFSTR callobj; // Definition and initialisation of the // scope object passed to module // It is generated right after the glocal // instance BUFSTR funccore; // Will contain the body of a function // after the initialisation and the glocal BUFSTR callf; // Will contains the sections // callobj contains the definition of the // context object and callf contains the // implementation of each context object // member functions. //BUFSTR *cur = &mainbuf; BUFSTR *cur = &funccore; char module[100]; module[0] = '\0'; char s1[1000],s2[1000],s3[1000]; FEED_TOKEN token; int nocall = 0; GLOCAL_VARS *original_vars = vars; /* auto_glocal will insert a glocal section before the first section. (); // auto glocal section is inserted here, before the call (); glocal.comp1.some_function(); So if no glocal section has been defined prior to the call one is inserted. */ bool auto_glocal = inmodule != NULL; SCOPE *scope = new SCOPE (vars,NULL,auto_glocal); while ((token=feed.gettoken(s1,s2,s3))!=T_EOF){ // fprintf (stderr,"gettoken %d :%s: :%s:\n",token,s1,s2); if (token == T_STR){ if (st.state == COMPSTATE_GLOCAL){ assert (vars != NULL); vars->parse (s1); cur->append (s1); }else{ const char *pt = s1; while (isspace(*pt)) pt++; if (strncmp(pt,"glocal",6)==0 && (isspace(pt[6]) || pt[6] == '\0')){ // fprintf (stderr,"glocal tout seul: %s\n",pt+6); // We are defining a glocal variable on a single line // with some initialisation; feed.push (T_STR,pt,"",""); BUFSTR defs,assigns; tlcc_lineglocal (feed,defs,assigns); tlcc_startglocal(feed,st,glocal_level,insert,cur ,vars,vars!=original_vars,inmodule); vars->parse (defs.getbuf()); cur->append (defs.getbuf()); auto_glocal = false; tlcc_endglocal (feed,st,insert,funcdef,funccore,cur ,vars,inmodule,vars->getparent()!=original_vars); char tmp[strlen(assigns.getbuf())*2+1]; strcpy (tmp,assigns.getbuf()); tlcc_fixglocal (vars,tmp); cur->append (tmp); }else{ tlcc_fixglocal (vars,s1); cur->append (s1); } } }else if (token == T_SPC){ // fprintf (stderr,"T_SPC :%s:\n",s1); cur->append (s1); if (st.state != COMPSTATE_GLOCAL){ if (s1[0] == '{'){ scope = new SCOPE (vars,scope,auto_glocal); }else if (s1[0] == '}'){ if (scope == NULL){ feed.printerr ("Nesting error\n"); }else{ SCOPE *old = scope; scope = scope->revert(vars,*cur,auto_glocal); delete old; } // Nothing special to do for ; // }else if (s1[0] == ';'){ }else if (s1[0] == '='){ continue; } if (cur != &funcdef){ funcdef.append (cur->getbuf()); cur->reset(); } } }else if (token == T_COMMENT){ cur->append (s1); }else if (strcmp(s1,"glocal")==0){ tlcc_startglocal(feed,st,glocal_level,insert,cur ,vars,vars!=original_vars,inmodule); auto_glocal = false; }else if (strcmp(s1,"/glocal")==0){ tlcc_endglocal (feed,st,insert,funcdef,funccore,cur ,vars,inmodule,vars->getparent()!=original_vars); }else if (strcmp (s1,"mod")==0){ st.pushstate (COMPSTATE_MOD,feed); insert.append (glocal.getbuf()); insert.append (callobj.getbuf()); insert.append (callf.getbuf()); mainbuf.append (funccore.getbuf()); fputs (insert.getbuf(),fout); fputs (funcdef.getbuf(),fout); fputs (mainbuf.getbuf(),fout); insert.reset(); mainbuf.reset(); glocal.reset(); funcdef.reset(); funccore.reset(); callobj.reset(); callf.reset(); cur = &funcdef; funcdef.setnoline (feed); st.in_module = true; st.nomod++; }else if (strcmp (s1,"/mod")==0){ cur->setnoline (feed); st.popstate (COMPSTATE_MOD,feed); glocal_level = 0; st.in_module = false; st.noglocal = 0; }else if (strcmp (s1,"call")==0 ){ if (auto_glocal){ // We insert a glocal so the component may be referenced // using glocal.component syntax auto_glocal = false; tlcc_startglocal(feed,st,glocal_level,insert,cur ,vars,false,inmodule); tlcc_endglocal (feed,st,insert,funcdef,funccore,cur ,vars,inmodule,false); } st.pushstate (COMPSTATE_CALL,feed); st.nocall++; nocall = st.nocall; if (glocal_level > 0){ funcdef.appendf ("\t%s%d _scopeobj%d(glocal);\n" ,scope_prefix,nocall,nocall); }else{ funcdef.appendf ("\t%s%d _scopeobj%d;\n" ,scope_prefix,nocall,nocall); } tlcc_setmodver (module,s2); tlcc_checkifdef(insert,s2,"module"); cur->setnoline(feed); cur->appendf ("::%s",module); cur->appendf ("(_scopeobj%d",nocall); // fprintf (stderr,"call :%s: :%s:\n",mainbuf.getbuf(),cur->getbuf()); tlcc_getparm (vars,feed,*cur); callobj.appendf ("class %s%d: public _F_%s{\n" ,scope_prefix,nocall,module); callobj.append ("public:\n"); if (glocal_level > 0){ int gvar_level = vars->getglevel(); callobj.appendf ("\t%s_GLOCAL%d_%d &glocal;\n" ,glocal_prefix ,st.nomod ,gvar_level); callobj.appendf ("\t%s%d (%s_GLOCAL%d_%d &g)\n" ,scope_prefix,nocall ,glocal_prefix,st.nomod,gvar_level); callobj.append ("\t\t: glocal(g)\n"); }else{ callobj.appendf ("\t%s%d ()\n",scope_prefix,nocall); } callobj.append ("\t{\n\t}\n"); }else if (strcmp (s1,"obj")==0 ){ if (auto_glocal){ // We insert a glocal so the component may be referenced // using glocal.component syntax auto_glocal = false; tlcc_startglocal(feed,st,glocal_level,insert,cur ,vars,false,inmodule); tlcc_endglocal (feed,st,insert,funcdef,funccore,cur ,vars,inmodule,false); } st.pushstate (COMPSTATE_OBJ,feed); if (s2[0] == '\0'){ feed.printerr ("Missing class name\n"); ret = -1; }else if (s3[0] == '\0'){ feed.printerr ("Missing variable name\n"); ret = -1; } if (!st.in_module){ insert.append (glocal.getbuf()); insert.append (callobj.getbuf()); insert.append (callf.getbuf()); mainbuf.append (funccore.getbuf()); fputs (insert.getbuf(),fout); fputs (funcdef.getbuf(),fout); fputs (mainbuf.getbuf(),fout); insert.reset(); mainbuf.reset(); glocal.reset(); funcdef.reset(); funccore.reset(); callobj.reset(); callf.reset(); } st.nocall++; nocall = st.nocall; if (glocal_level > 0){ funcdef.appendf ("\t%s%d _scopeobj%d(glocal);\n" ,scope_prefix,nocall,nocall); }else{ funcdef.appendf ("\t%s%d _scopeobj%d;\n" ,scope_prefix,nocall,nocall); } tlcc_setmodver (module,s2); tlcc_checkifdef(insert,s2,"class"); cur->setnoline(feed); cur->appendf ("%s %s",module,s3); { cur->appendf ("(_scopeobj%d",nocall); char carac; while (isspace(carac = feed.getcar())); if (carac == ';'){ cur->append (");"); }else if (carac == '('){ tlcc_getendparm (vars,feed,*cur); }else{ feed.printerr ("Invalid obj definition\n"); ret = -1; } } callobj.appendf ("class %s%d: public _F_%s{\n" ,scope_prefix,nocall,module); callobj.append ("public:\n"); if (glocal_level > 0){ int gvar_level = vars->getglevel(); callobj.appendf ("\t%s_GLOCAL%d_%d &glocal;\n" ,glocal_prefix,st.nomod,gvar_level); callobj.appendf ("\t%s%d (%s_GLOCAL%d_%d &g)\n" ,scope_prefix,nocall ,glocal_prefix,st.nomod,gvar_level); callobj.append ("\t\t: glocal(g)\n"); }else{ callobj.appendf ("\t%s%d ()\n",scope_prefix,nocall); } callobj.append ("\t{\n\t}\n"); }else if (strcmp(s1,"/call")==0){ st.popstate (COMPSTATE_CALL,feed); callobj.append ("};\n"); module[0] = '\0'; cur->setnoline(feed); }else if (strcmp(s1,"/obj")==0){ st.popstate (COMPSTATE_OBJ,feed); callobj.append ("};\n"); module[0] = '\0'; cur->setnoline(feed); }else if (strcmp (s1,"f")==0){ st.pushstate (COMPSTATE_F,feed); // Put some code to insure that the functag exists // If not, strange C++ errors are printed if (module[0] == '\0'){ feed.printerr ("Out of scope construct\n",s2); ret = -1; } insert.appendf ("#ifndef _F_%s_%s\n",module,s2); insert.appendf ("\t#error Unknown logical function %s for module/class %s\n" ,s2,module); insert.appendf ("#endif\n"); callobj.appendf ("\t_F_%s_%s( );\n",module,s2,nocall); callf.appendf ("_F_%s_%s(%s%d::)\n",module,s2,scope_prefix,nocall); callf.append ("{\n"); callf.setnoline(feed); BUFSTR endcallf; ret |= tlcc_do (feed,insert,callf,endcallf,st,fout,glocal_level,vars,module); callf.append (endcallf.getbuf()); }else if (strcmp (s1,"/f")==0){ st.popstate (COMPSTATE_F,feed); funccore.append ("} // \n"); funccore.setnoline (feed); break; }else if (s2[0] == '\0'){ cur->appendf ("<%s>",s1); }else{ fprintf (stderr,"Unknown directive %s\n",s1); ret = -1; } } insert.append (glocal.getbuf()); insert.append (callobj.getbuf()); insert.append (callf.getbuf()); mainbuf.append (funccore.getbuf()); if (scope == NULL){ feed.printerr ("Nesting error\n"); }else{ SCOPE *old = scope; scope = scope->revert(vars,mainbuf,auto_glocal); delete old; if (scope != NULL){ feed.printerr ("Nesting error\n"); } } tlcc_delvars (vars,original_vars,mainbuf); return ret; } static void usage() { fprintf (stderr ,"tlmp translator version %s\n" "\n" "tlcc [ --name source_name ] input-file [ output-file ]\n" "tlcc --name source_name - [ output-file ]\n" ,TLCC_VERSION); exit (-1); } static int tlcc_process ( FILE *fin, FILE *fout, const char *source_name) { int ret = 0; tlcc_setscopeprefix (source_name); FEED feed (source_name,fin); MODSTATE state; BUFSTR insert,funcdef,mainbuf; ret = tlcc_do (feed,insert,funcdef,mainbuf,state,fout,0,NULL,NULL); if (state.some_errors) ret = -1; fputs (insert.getbuf(),fout); fputs (funcdef.getbuf(),fout); fputs (mainbuf.getbuf(),fout); return ret; } int main (int argc, char *argv[]) { int ret = 0; bool some_errors = false; const char *source_name = NULL; int i; for (i=1; i