/*
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 ...
2019/04/19: tlcc was written in the 1990s. At this time, C++ did not
support declaration of classes inside functions. IN the example below
the class derived from _F_foo have to be defined outside the function
using it. Now C++ compilers do support class definition inside
functions, so this is not required anymore.
Support for class definition inside function opens some opportunity
to enhance the syntax of tlmp glocals. It allows glocals to hold
references for one. It allows also glocals to use types that were just
defined before in the function.
Here are the old explanations.
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 keep the various information
it collects. We will describe how TLMP code is translated 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.
2019/04/19: Class definition inside function avoids this problem since
their definition is local to the function. But it is good anyway to
avoid any clashes.
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( );
};
2019/04/19: Using class definition inside function, we can completly define
the functions f1 and f2 above inline in the class.
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 objects 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.
*/
/*
About A_glocal, B_glocal and the glid variable.
Glocal is both a keyword and the name of a variable. This variable
is used to access "glocal" variables along the tree.
glocal int a=1;
();
glocal int b=2;
();
printf ("glocal.a=%d glocal.b=%d\n",glocal.a,glocal.b);
glocals are created as nested struct. In the previous example, the code
looks like this
struct _glocal1{
int a;
};
.
struct _glocal2{
_glocal1 &glocal;
int b;
};
Using such nesting, the printf above is translated to
printf ("glocal.a=%d glocal.b=%d\n",glocal.glocal.a,glocal.b);
Now, there is an issue with nested glocals in the same scope. Now you
have code like
struct _glocal1{
int a;
};
_glocal1 glocal;
.
struct _glocal2{
_glocal1 &glocal;
_glocal2(_glocal1 &gl)
:glocal(_gl)
{
}
};
_glocal2 glocal(glocal); // Declare a second glocal
// using the first glocal as argument
Now, some C++ compilers like the above construct and correctly
identify the first glocal from the second. Some don't...
So the trick is to rename the glocal variable using a prefix. The
first glocal becomes A_glocal and the second, B_glocal. The variable
glid and GLOCAL_VARS::getnestlevel() are used to name the glocals.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
static bool debug = false;
#define TLCC_VERSION "2.0"
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
};
// ====== Lexical parser ====
class FEED{
const char *fname;
FILE *fin;
char buf[10000];
char *s;
int noline;
bool comment_mode;
bool some_errors;
struct {
FEED_TOKEN token;
string s1;
string s2;
string s3;
}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 (string &str, string &arg1, string &arg2);
void printerr (const char *s, ...);
void push (FEED_TOKEN token,
const string &s1,
const string &s2,
const string &s3);
bool errors();
/*~PROTOEND~ FEED */
};
PUBLIC FEED::FEED(const char *_fname, FILE *_fin)
{
some_errors = false;
fname = _fname;
fin = _fin;
noline = 0;
comment_mode = false;
fill();
pval.token = T_EOF;
}
PUBLIC bool FEED::errors()
{
return some_errors;
}
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);
some_errors = true;
}
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 string &s1,
const string &s2,
const string &s3)
{
pval.token = token;
pval.s1 = s1;
pval.s2 = s2;
pval.s3 = s3;
}
static char *tlcc_copystr(string &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 (string &str, string &arg1, string &arg2)
{
arg1.clear(); arg2.clear(); str.clear();
FEED_TOKEN ret = T_EOF;
if (pval.token != T_EOF){
ret = pval.token;
str = pval.s1;
arg1 = pval.s2;
arg2 = pval.s3;
pval.s1.clear();
pval.s2.clear();
pval.s3.clear();
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.size() > 0){
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])){
string id1,id2,id3;
char *ss = s+1;
while (isalnum(*ss) || *s == '_'){
id1 += *ss++;
}
while (isspace (*ss)) ss++;
if (id1 == "unsigned"){
// Probably a C++ template like set
while (s < ss) str += *s++;
}else if (*ss == '>'){
str = id1;
ret = T_MARK;
s = ss+1;
break;
}else if (isalpha(*ss)){
while (isalnum(*ss) || *ss == '_'){
id2 += *ss++;
}
while (isspace (*ss)) ss++;
if (*ss == '>'){
ret = T_MARK;
str = id1;
arg1 = id2;
s = ss+1;
break;
}else if (isalpha(*ss)){
while (isalnum(*ss) || *ss == '_'){
id3 += *ss++;
}
while (isspace (*ss)) ss++;
if (*ss == '>'){
ret = T_MARK;
str = id1;
arg1 = id2;
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;
}
// BUFSTR is an output buffer.
// tlcc output to multiple output buffers at once and used them to output their content out of order.
class BUFSTR{
char *buf;
int size = 0;
int pos = 0;
struct {
int pos = 0;
int line = 0;
string fname;
void clear(){
pos = 0;
line = 0;
fname.clear();
}
} start_of_statement; // Used for "something = 0 && pos > start_of_statement.pos){
// Avoid returning only spaces, for easier to read output
int start = start_of_statement.pos;
while (start < pos && isspace(buf[start])) start++;
if (pos > start){
char tmp[10000];
snprintf (tmp,sizeof(tmp)-1,"\n#line %d \"%s\"\n",start_of_statement.line,start_of_statement.fname.c_str());
ret = tmp;
ret += buf + start_of_statement.pos;
pos = start_of_statement.pos;
buf[pos] = '\0';
}
}
return ret;
}
PUBLIC void BUFSTR::append (const BUFSTR &b)
{
append (b.getbuf());
start_of_statement.clear();
}
PUBLIC void BUFSTR::append (const string &b)
{
append (b.c_str());
}
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;
start_of_statement.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[10000];
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() const
{
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[4100];
static char glocal_prefix[4100];
/*
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;
int nest_level;
/*~PROTOBEG~ GLOCAL_VARS */
public:
GLOCAL_VARS (GLOCAL_VARS *_parent,
int _glocal_level);
private:
void add (const char *name);
public:
int getglevel (void)const;
int getnestlevel (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;
nest_level = parent == NULL ? 1 : parent->getnestlevel() + 1;
}
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;
}
PUBLIC int GLOCAL_VARS::getnestlevel() const
{
return nest_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 (isspace(*src) && *src != '\0') src++;
if (strncmp(src,"#line ",6)==0){
src += 6;
}else if (*src == '"'){
src++;
while (*src != '"' && *src != '\0') src++;
if (*src == '"') src++;
}else if (isdigit(*src)){
while (isdigit(*src)) src++;
}else if (*src == ';'){
src++;
}else{
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'){
#if 0
printf ("ALLO parse %lu ",strlen(line));
const char *pt = line;
while (*pt != '\0'){
if (*pt >= ' '){
printf ("%c",*pt);
}else{
printf ("<%02x>",*pt);
}
pt++;
}
printf ("\n");
#endif
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,
string &s1,
int glid) // glocal ID
{
if (vars != NULL){
char tmps1[10000];
strcpy (tmps1,s1.c_str());
char *pt = tmps1;
while ((pt = strstr(pt,"glocal."))!=NULL){
if (glid > 0){
memmove (pt+2,pt,strlen(pt)+1);
pt[0] = glid + 'A';
pt[1] = '_';
pt += 2;
}
char word[1000];
pt += 7; // Skip 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,
int glid)
{
char carac;
while ((carac = feed.getcar()) <= ' ') cur.append (carac);
if (carac == '('){
tlcc_getendparm(vars,feed,cur,glid);
tlcc_getsemicolon(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;
}
struct GLOCALREF{
unsigned typeno; // Unique number of the "using ..." definition
string name; // Variable name;
GLOCALREF(unsigned _typeno, const string &_name)
:typeno(_typeno),name(_name)
{
}
};
static void tlcc_defrefs (BUFSTR &insert, const vector &refs, const char *comma)
{
for (auto &c:refs){
insert.appendf ("%sglocaltype%u &%s",comma,c.typeno,c.name.c_str());
comma = ",";
}
}
/*
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
const vector &refs)
{
st.pushstate (COMPSTATE_GLOCAL,feed);
glocal_level = ++st.noglocal;
insert.appendf ("\tstruct %s_GLOCAL%d_%d{\n"
,glocal_prefix,st.nomod,glocal_level);
const char *closebrac = "\t{}\n";
const char *comma = ",";
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"
,glocal_prefix
,st.nomod,glocal_level
,glocal_prefix
,st.nomod,parent_glocal
,inmodule);
tlcc_defrefs(insert,refs,",");
insert.appendf(")\n\t\t: glocal(gl),%s(*_c)\n",inmodule);
}else{
insert.appendf ("\t%s_GLOCAL%d_%d(%s_GLOCAL%d_%d &gl"
,glocal_prefix
,st.nomod,glocal_level
,glocal_prefix
,st.nomod,parent_glocal);
tlcc_defrefs(insert,refs,",");
insert.appendf(")\n\t\t: glocal(gl)\n");
}
}else if (inmodule != NULL){
insert.appendf ("\t_F_%s &%s;\n",inmodule,inmodule);
insert.appendf ("\t%s_GLOCAL%d_%d(_F_%s *_c"
,glocal_prefix
,st.nomod,glocal_level
,inmodule);
tlcc_defrefs(insert,refs,",");
insert.appendf( ")\n\t\t: %s(*_c)\n",inmodule);
}else if (refs.size() > 0){
insert.appendf ("\t%s_GLOCAL%d_%d("
,glocal_prefix
,st.nomod,glocal_level);
tlcc_defrefs(insert,refs,"");
insert.append (")\n\t\t: ");
comma = "";
}else{
closebrac="";
}
for (auto &c:refs){
insert.appendf ("\t%s%s(%s)",comma,c.name.c_str(),c.name.c_str());
comma = ",";
}
insert.append(closebrac);
cur = &insert;
vars = new GLOCAL_VARS (vars,glocal_level);
if (inmodule != NULL) vars->parse (inmodule);
}
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
{
vector refs;
tlcc_startglocal(feed,st,glocal_level,insert,cur,vars,nested_glocal,inmodule,refs);
}
/*
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 glid,
const vector &refs)
{
int glocal_level = vars->getglevel();
st.popstate (COMPSTATE_GLOCAL,feed);
insert.append ("\t};\n");
funcdef.append ("\t{\t// Inserted to support nested glocals\n");
char glidstr[3];
int glid_diff = vars->getnestlevel() - glid;
if (glid_diff > 0){
glidstr[0] = glid_diff + 'A';
glidstr[1] = '_';
glidstr[2] = '\0';
}else{
glidstr[0] = '\0';
}
const char *paropen = "";
const char *parclose = ");\n";
const char *comma = ",";
if (vars->getparent() != NULL){
if (nested_glocal){
int glid_1 = glid_diff - 1;
char lastgl[3];
if (glid_1 > 0){
lastgl[0] = glid_1 + 'A';
lastgl[1] = '_';
lastgl[2] = '\0';
}else{
lastgl[0] = '\0';
}
funcdef.appendf ("\t%s_GLOCAL%d_%d %sglocal(%sglocal"
,glocal_prefix,st.nomod,glocal_level,glidstr,lastgl);
}else if (inmodule != NULL){
funcdef.appendf ("\t%s_GLOCAL%d_%d %sglocal(this->glocal,this"
,glocal_prefix,st.nomod,glocal_level,glidstr);
}else{
funcdef.appendf ("\t%s_GLOCAL%d_%d %sglocal(this->glocal"
,glocal_prefix,st.nomod,glocal_level,glidstr);
}
}else if (inmodule != NULL){
funcdef.appendf ("\t%s_GLOCAL%d_%d %sglocal(this"
,glocal_prefix,st.nomod,glocal_level,glidstr);
}else{
paropen = "(";
comma = "";
parclose = ";\n";
funcdef.appendf ("\t%s_GLOCAL%d_%d %sglocal"
,glocal_prefix,st.nomod,glocal_level,glidstr);
}
if (refs.size() == 0){
funcdef.append(parclose);
}else{
funcdef.append(paropen);
for (auto &c:refs){
funcdef.append(comma);
funcdef.append(c.name);
comma = ",";
}
funcdef.append (");\n");
}
cur = &funccore;
funccore.setnoline (feed);
}
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 glid)
{
vector refs;
tlcc_endglocal(feed,st,insert,funcdef,funccore,cur,vars,inmodule,nested_glocal,glid,refs);
}
/*
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 definitions "glocal type var [ = value ] ;".
This function is called with the first token pointing to glocal
and will process all consecutive glocal declaration.
Process also glocal references. It works like this.
void foo(paramtype prm)
{
glocal prm;
...
This will make prm visible as a glocal. glocal.prm will be a reference to prm. No need to
repeat the type.
*/
static void tlcc_lineglocal(
FEED &feed,
BUFSTR &defs, // Record variable definition
BUFSTR &assigns, // Record variable assignment
vector &refs) // Will contains glocal references. See above
{
string s1,s2,s3;
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[10000];
strcpy (id,"**UNKNOWN**");
bool seen_equal = false;
while ((token=feed.gettoken(s1,s2,s3))!=T_EOF){
// fprintf (stderr,"lineglocal %d :%s:\n",token,s1);
if (token == T_COMMENT){
}else if (token == T_STR){
if (cur == NULL){
const char *pt = s1.c_str();
while (isspace(*pt)) pt++;
if (strncmp(pt,"glocal",6)==0
&& (isspace(pt[6]) || pt[6] == '\0')){
pt += 6;
while (isspace(*pt)) pt++;
// Is it just "glocal var;" or "glocal type ... var ...
bool reference = false;
//fprintf (stderr,"refere pt=%s\n",pt);
if (isalpha(*pt)){
const char *start = pt;
while (isalnum(*pt) || *pt == '_') pt++;
const char *end = pt;
while (isspace(*pt)) pt++;
if (*pt != '\0'){
pt = start;
}else{
string ss1;
FEED_TOKEN token1=feed.gettoken(ss1,s2,s3);
if (token1 != T_SPC || ss1[0] != ';'){
feed.push(token1,ss1,s2,s3);
pt = start;
}else{
reference = true;
static unsigned assigntype=0;
string name(start,end-start);
refs.emplace_back(assigntype,name);
defs.appendf ("\t\tglocaltype%u &%s;\n",assigntype,name.c_str());
assigntype++;
}
}
}
if (!reference){
cur = &defs;
cur->setnoline (feed);
cur->append ("\t\t");
cur->append (pt);
tlcc_pickid(pt,id);
}
}else if (pt[0] != '\0'){
break;
}
}else{
cur->append (s1);
if (cur == &defs){
tlcc_pickid (s1.c_str(),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 calldef; // Object used to perform the
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 = &funcdef;
char module[100];
module[0] = '\0';
string s1,s2,s3;
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);
int glid = vars == NULL ? 0 : vars->getnestlevel();
while ((token=feed.gettoken(s1,s2,s3))!=T_EOF){
//fprintf (stderr,"gettoken %d :%s: :%s:\n",token,s1.c_str(),s2.c_str());
int glid_diff = vars == NULL ? 0 : vars->getnestlevel() - glid;
if (token == T_STR){
if (st.state == COMPSTATE_GLOCAL){
assert (vars != NULL);
vars->parse (s1.c_str());
cur->append (s1);
}else{
const char *pt = s1.c_str();
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;
vector refs;
tlcc_lineglocal (feed,defs,assigns,refs);
for (auto const &c:refs){
cur->appendf ("\tusing glocaltype%u = decltype(%s);\n",c.typeno,c.name.c_str());
}
// fprintf (stderr,"defs=%s\nassigns=%s\n",defs.getbuf(),assigns.getbuf());
tlcc_startglocal(feed,st,glocal_level,callobj,cur
,vars,vars!=original_vars,inmodule,refs);
vars->parse (defs.getbuf());
cur->append (defs);
auto_glocal = false;
tlcc_endglocal (feed,st,callobj,callobj,funcdef,cur
,vars,inmodule,vars->getparent()!=original_vars,glid,refs);
//fprintf (stderr,"------------\ncallobj %d %d=%s\n-------\n",&callobj==cur,&funcdef==cur,callobj.getbuf());
string tmp(assigns.getbuf());
glid_diff = vars->getnestlevel() - glid;
tlcc_fixglocal (vars,tmp,glid_diff);
cur->append (callobj);
callobj.reset();
cur->append (tmp);
cur->setnoline (feed);
cur->end_of_statement(feed);
}else{
////fprintf (stderr,"avant fix s1=%s\n",s1.c_str());
tlcc_fixglocal (vars,s1,glid_diff);
//fprintf (stderr,"append s1: %s\n",s1.c_str());
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);
cur->end_of_statement(feed);
}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;
}
cur->end_of_statement(feed);
}else if (s1[0] == ';'){
cur->end_of_statement(feed);
}else if (s1[0] == '='){
continue;
}
if (cur != &funcdef){
funcdef.append (*cur);
cur->reset();
}
}
}else if (token == T_COMMENT){
cur->append (s1);
}else if (0 && s1 == "glocal"){
tlcc_startglocal(feed,st,glocal_level,insert,cur
,vars,vars!=original_vars,inmodule);
auto_glocal = false;
}else if (0 && s1 == "/glocal"){
tlcc_endglocal (feed,st,insert,funcdef,funccore,cur
,vars,inmodule,vars->getparent()!=original_vars,glid);
}else if (s1 == "mod"){
if (debug) fprintf (stderr,"%d \n",feed.getnoline());
st.pushstate (COMPSTATE_MOD,feed);
insert.append (glocal);
insert.append (callobj);
insert.append (callf);
mainbuf.append (funccore);
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 (s1 == "/mod"){
if (debug) fprintf (stderr,"%d \n",feed.getnoline());
cur->setnoline (feed);
st.popstate (COMPSTATE_MOD,feed);
glocal_level = 0;
st.in_module = false;
st.noglocal = 0;
}else if (s1 == "call"){
if (debug) fprintf (stderr,"%d \n",feed.getnoline());
string sof = cur->get_start_of_statement();
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,funcdef,cur
,vars,false,inmodule);
tlcc_endglocal (feed,st,funcdef,funcdef,funccore,cur
,vars,inmodule,false,glid);
glid_diff = vars->getnestlevel() - glid;
}
st.pushstate (COMPSTATE_CALL,feed);
st.nocall++;
nocall = st.nocall;
if (glocal_level > 0){
calldef.appendf ("\t%s%d _scopeobj%d"
,scope_prefix,nocall,nocall);
if (glid_diff > 0){
calldef.appendf ("(%c_glocal);\n",glid_diff+'A');
}else{
calldef.appendf ("(glocal);\n");
}
}else{
calldef.appendf ("\tstatic %s%d _scopeobj%d;\n"
,scope_prefix,nocall,nocall);
}
tlcc_setmodver (module,s2.c_str());
tlcc_checkifdef(insert,s2.c_str(),"module");
//string sof = cur->get_start_of_statement();
//if(sof.size() > 0) fprintf (stderr,"sof=:%s:\n",sof.c_str());
calldef.append (sof);
calldef.setnoline(feed);
calldef.appendf ("\t::%s",module);
calldef.appendf ("(_scopeobj%d",nocall);
// fprintf (stderr,"call :%s: :%s:\n",mainbuf.getbuf(),cur->getbuf());
tlcc_getparm (vars,feed,calldef,glid_diff);
callobj.appendf ("class %s%d: public _F_%s{\n"
,scope_prefix,nocall,module);
callobj.append ("\tpublic:\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");
funcdef.end_of_statement(feed);
}else if (s1 == "obj"){
if (debug) fprintf (stderr,"%d \n",feed.getnoline());
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,funcdef,cur
,vars,false,inmodule);
tlcc_endglocal (feed,st,funcdef,funcdef,funccore,cur
,vars,inmodule,false,glid);
glid_diff = vars->getnestlevel() - glid;
}
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);
insert.append (callobj);
insert.append (callf);
mainbuf.append (funccore);
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){
calldef.appendf ("\t%s%d _scopeobj%d"
,scope_prefix,nocall,nocall);
if (glid_diff > 0){
calldef.appendf ("(%c_glocal);\n",glid_diff+'A');
}else{
calldef.appendf ("(glocal);\n");
}
}else{
calldef.appendf ("\tstatic %s%d _scopeobj%d;\n"
,scope_prefix,nocall,nocall);
}
tlcc_setmodver (module,s2.c_str());
tlcc_checkifdef(insert,s2.c_str(),"class");
calldef.setnoline(feed);
calldef.appendf ("\t%s %s",module,s3.c_str());
{
calldef.appendf ("(_scopeobj%d",nocall);
char carac;
while (isspace(carac = feed.getcar()));
if (carac == ';'){
calldef.append (");");
}else if (carac == '('){
tlcc_getendparm (vars,feed,calldef,glid_diff);
tlcc_getsemicolon(feed,calldef);
}else{
feed.printerr ("Invalid obj definition\n");
ret = -1;
}
}
callobj.appendf ("class %s%d: public _F_%s{\n"
,scope_prefix,nocall,module);
callobj.append ("\tpublic:\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 (s1 == "/call"){
if (debug) fprintf (stderr,"%d \n",feed.getnoline());
st.popstate (COMPSTATE_CALL,feed);
callobj.append ("\t};\n");
funcdef.append (callobj);
callobj.reset();
funcdef.append (calldef);
calldef.reset();
module[0] = '\0';
cur->setnoline(feed);
funcdef.end_of_statement(feed);
}else if (s1 == "/obj"){
if (debug) fprintf (stderr,"%d \n",feed.getnoline());
st.popstate (COMPSTATE_OBJ,feed);
callobj.append ("\t};\n");
funcdef.append (callobj);
funcdef.append (calldef);
callobj.reset();
calldef.reset();
module[0] = '\0';
cur->setnoline(feed);
}else if (s1 == "f"){
if (debug) fprintf (stderr,"%d \n",feed.getnoline());
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.c_str());
insert.appendf ("\t#error Unknown logical function %s for module/class %s\n"
,s2.c_str(),module);
insert.appendf ("#endif\n");
callobj.appendf ("\t_F_%s_%s( )\n",module,s2.c_str());
//callf.appendf ("_F_%s_%s(%s%d::)\n",module,s2,scope_prefix,nocall);
callobj.append ("\t{\n");
callobj.setnoline(feed);
BUFSTR endcallf;
ret |= tlcc_do (feed,insert,callobj,endcallf,st,fout,glocal_level,vars,module);
callobj.append (endcallf);
}else if (s1 == "/f"){
if (debug) fprintf (stderr,"%d \n",feed.getnoline());
st.popstate (COMPSTATE_F,feed);
funccore.append ("} // \n");
funccore.setnoline (feed);
break;
}else if (s2[0] == '\0'){
cur->appendf ("<%s>",s1.c_str());
}else if (s1 == "typename" || s1 == "class"){
// Trick to support template
cur->appendf ("<%s %s>",s1.c_str(),s2.c_str());
}else{
fprintf (stderr,"Unknown directive %s\n",s1.c_str());
ret = -1;
}
}
insert.append (glocal);
insert.append (callobj);
insert.append (callf);
mainbuf.append (funccore);
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 [--debug] [ --name source_name ] input-file [ output-file ]\n"
"tlcc [--debug] --name source_name - [ output-file ]\n"
"\n"
"--compvers versions (file produced by tlccversion)\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);
if (feed.errors()) ret = -1;
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