/*
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