]> git.draconx.ca Git - gob-dx.git/blob - src/main.c
Release 0.90.0
[gob-dx.git] / src / main.c
1 /* GOB C Preprocessor
2  * Copyright (C) 1999 the Free Software Foundation.
3  *
4  * Author: George Lebl
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the  Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19  * USA.
20  */
21
22 #include "config.h"
23 #include <glib.h>
24 #include <popt.h>
25 #include <time.h>
26 #include <stdio.h>
27 #include <string.h>
28
29 #include "tree.h"
30 #include "parse.h"
31 #include "out.h"
32 #include "main.h"
33
34 char *filename = "stdin";
35
36 int yyparse(void);
37
38 extern int yydebug;
39 extern FILE * yyin;
40 extern Node *class;
41 extern GList *nodes;
42
43 char *filebase;
44 static char *funcbase;
45 static char *pfuncbase;
46 static char *macrobase;
47 static char *macrois;
48 static char *typebase;
49 static char *ptypebase;
50
51 static int signals = 0;
52 static int arguments = 0;
53 static int overrides = 0;
54
55 FILE *out;
56 FILE *outh;
57
58 int exit_on_warn = FALSE;
59 int exit_on_error = TRUE;
60 int got_error = FALSE;
61
62 void
63 print_error(int is_warn, char *error,int line)
64 {
65         char *w;
66         if(is_warn)
67                 w = "Warning:";
68         else {
69                 w = "Error:";
70                 got_error = TRUE;
71         }
72         if(line>0)
73                 fprintf(stderr,"%s:%d: %s %s\n",filename,line,w,error);
74         else
75                 fprintf(stderr,"%s: %s %s\n",filename,w,error);
76         if((!is_warn || exit_on_warn) && exit_on_error)
77                 exit(1);
78 }
79
80 static char *
81 remove_sep(char *base)
82 {
83         char *p;
84         char *s = g_strdup(base);
85         while((p=strchr(s,':')))
86                 strcpy(p,p+1);
87         return s;
88 }
89
90 static char *
91 replace_sep(char *base, char r)
92 {
93         char *p;
94         char *s = g_strdup(base);
95         while((p=strchr(s,':')))
96                 *p = r;
97         if(*s == r) {
98                 p = g_strdup(s+1);
99                 g_free(s);
100                 return p;
101         }
102         return s;
103 }
104
105 /*separate the namespace part and then replace rest of
106   separators with r*/
107 static void
108 separns_replace_sep(char *base, char **ns, char **name, char r)
109 {
110         char *p;
111         char *s = g_strdup(base);
112         *ns = NULL;
113         if((p=strchr(s,':')) && p!=s) {
114                 *p = '\0';
115                 *ns = g_strdup(s);
116                 p = g_strdup(p+1);
117                 g_free(s);
118                 s = p;
119         }
120         while((p=strchr(s,':')))
121                 *p = r;
122         if(*s == r) {
123                 *name = g_strdup(s+1);
124                 g_free(s);
125         } else
126                 *name = s;
127 }
128
129 static char *
130 make_is_macro(char *base)
131 {
132         char *s1,*s2;
133         char *s;
134
135         separns_replace_sep(base,&s1,&s2,'_');
136         if(s1)
137                 s = g_strconcat(s1,"_IS_",s2,NULL);
138         else
139                 s = g_strconcat("IS_",s2,NULL);
140
141         g_strup(s);
142         
143         g_free(s1);
144         g_free(s2);
145
146         return s;
147 }
148
149 static void
150 make_bases(void)
151 {
152         filebase = replace_sep(((Class *)class)->otype,'-');
153         g_strdown(filebase);
154
155         funcbase = replace_sep(((Class *)class)->otype,'_');
156         g_strdown(funcbase);
157
158         pfuncbase = replace_sep(((Class *)class)->ptype,'_');
159         g_strdown(pfuncbase);
160
161         macrobase = replace_sep(((Class *)class)->otype,'_');
162         g_strup(macrobase);
163         
164         macrois = make_is_macro(((Class *)class)->otype);
165
166         typebase = remove_sep(((Class *)class)->otype);
167
168         ptypebase = remove_sep(((Class *)class)->ptype);
169 }
170
171 static void
172 def_methods(Class *c)
173 {
174         GList *li;
175         
176         out_printf(out,"\n");
177         for(li=c->nodes;li;li=g_list_next(li)) {
178                 Node *node = li->data;
179                 if(node->type == METHOD_NODE) {
180                         Method *m = (Method *)node;
181                         
182                         if(m->scope == INIT_METHOD ||
183                            m->scope == CLASS_INIT_METHOD ||
184                            m->scope == OVERRIDE_METHOD)
185                                 continue;
186                         
187                         out_printf(out,"#define %s %s_%s\n",m->id,funcbase,m->id); 
188                 }
189         }
190         out_printf(out,"\n");
191 }
192
193 static void
194 undef_methods(Class *c)
195 {
196         GList *li;
197         
198         out_printf(out,"\n");
199         for(li=c->nodes;li;li=g_list_next(li)) {
200                 Node *node = li->data;
201                 if(node->type == METHOD_NODE) {
202                         Method *m = (Method *)node;
203
204                         if(m->scope == INIT_METHOD ||
205                            m->scope == CLASS_INIT_METHOD ||
206                            m->scope == OVERRIDE_METHOD)
207                                 continue;
208                         
209                         out_printf(out,"#undef %s\n",m->id); 
210                 }
211         }
212         out_printf(out,"\n");
213 }
214
215 static void
216 print_type(FILE *fp, Type *t)
217 {
218         char *s;
219         int i;
220         s = remove_sep(t->name);
221         out_printf(fp,"%s ",s); 
222         g_free(s);
223         
224         for(i=0;i<t->stars;i++)
225                 out_printf(fp,"*"); 
226 }
227
228 static void
229 put_variable(Variable *v)
230 {
231         out_printf(outh,"\t");
232         if(v->scope == PRIVATE_SCOPE)
233                 out_printf(outh,"/* private */ "); 
234         print_type(outh,v->vtype);
235
236         out_printf(outh,"%s;\n",v->id); 
237 }
238
239 static void
240 print_method(FILE *fp, char *typeprefix, char *nameprefix,
241              char *namepostfix,char *postfix, Method *m)
242 {
243         GList *li;
244
245         out_printf(fp,"%s",typeprefix); 
246         print_type(fp,m->mtype);
247         out_printf(fp,"%s%s_%s%s(",
248                 nameprefix,funcbase,m->id,namepostfix); 
249         
250         if(m->args) {
251                 for(li=m->args;li;li=g_list_next(li)) {
252                         FuncArg *arg = li->data;
253                         print_type(fp,arg->atype);
254                         if(li->next)
255                                 out_printf(fp,"%s, ",arg->name); 
256                         else
257                                 out_printf(fp,"%s",arg->name); 
258
259                 }
260         } else {
261                 out_printf(fp,"void"); 
262         }
263         out_printf(fp,")%s\n",postfix); 
264 }
265
266 static void
267 put_vs_method(Method *m)
268 {
269         if(m->scope != SIGNAL_LAST_METHOD &&
270            m->scope != SIGNAL_FIRST_METHOD &&
271            m->scope != VIRTUAL_METHOD)
272                 return;
273
274         print_method(outh,"\t","(* ",") ",";",m);
275 }
276
277 static void
278 put_pub_method(Method *m)
279 {
280         if(m->scope == PRIVATE_SCOPE ||
281            m->scope == OVERRIDE_METHOD ||
282            m->scope == INIT_METHOD ||
283            m->scope == CLASS_INIT_METHOD)
284                 return;
285
286         print_method(outh,"","\t","\t",";",m);
287 }
288
289 static void
290 put_priv_method_prot(Method *m)
291 {
292         if(m->scope == PUBLIC_SCOPE)
293                 return;
294
295         if(m->scope == SIGNAL_LAST_METHOD ||
296            m->scope == SIGNAL_FIRST_METHOD ||
297            m->scope == VIRTUAL_METHOD) {
298                 if(!m->cbuf)
299                         return;
300                 print_method(out,"static ","_real_"," ",";",m);
301         } else {
302                 print_method(out,"static ",""," ",";",m);
303         }
304 }
305
306 static GList *
307 make_init_args(Class *cl, char *name, int is_class)
308 {
309         Node *node;
310         Node *type;
311         char *tn;
312         
313         if(is_class)
314                 tn = g_strconcat(cl->otype,":Class",NULL);
315         else
316                 tn = g_strdup(cl->otype);
317
318         type = new_type(1,tn);
319         node = new_funcarg((Type *)type,name,NULL);
320         return g_list_prepend(NULL, node);
321 }
322
323 static void
324 make_inits(Class *cl)
325 {
326         int got_class_init = FALSE;
327         int got_init = FALSE;
328         GList *li;
329         Node *node;
330         for(li=cl->nodes;li;li=g_list_next(li)) {
331                 Node *n = li->data;
332                 if(n->type == METHOD_NODE) {
333                         Method *m = (Method *)n;
334                         if(m->scope == INIT_METHOD) {
335                                 if(got_init)
336                                         print_error(FALSE,"init defined more then once",m->line_no);
337                                 got_init = TRUE;
338                         } else if(m->scope == CLASS_INIT_METHOD) {
339                                 if(got_class_init)
340                                         print_error(FALSE,"class_init defined more then once",m->line_no);
341                                 got_class_init = TRUE;
342                         }
343                 }
344         }
345         if(!got_class_init) {
346                 node = new_method(CLASS_INIT_METHOD,
347                                   (Type *)new_type(0,g_strdup("void")),
348                                   NULL,NULL,g_strdup("class_init"),
349                                   make_init_args(cl,g_strdup("c"),TRUE),
350                                   NULL, NULL,0,0);
351                 cl->nodes = g_list_prepend(cl->nodes,node);
352         }
353         if(!got_init) {
354                 node = new_method(INIT_METHOD,
355                                   (Type *)new_type(0,g_strdup("void")),
356                                   NULL,NULL,g_strdup("init"),
357                                   make_init_args(cl,g_strdup("o"),FALSE),
358                                   NULL, NULL,0,0);
359                 cl->nodes = g_list_prepend(cl->nodes,node);
360         }
361 }
362
363 static GHashTable *marsh = NULL;
364
365 static void
366 add_signal_prots(Method *m)
367 {
368         GList *li;
369         static int sig = 1;
370         char *s;
371         
372         if(m->scope != SIGNAL_LAST_METHOD &&
373            m->scope != SIGNAL_FIRST_METHOD)
374                 return;
375
376         if(!marsh)
377                 marsh = g_hash_table_new(NULL,NULL);
378         
379         if(strcmp(m->gtktypes->data,"NONE")==0 &&
380            strcmp(m->gtktypes->next->data,"NONE")==0)
381                 return;
382         
383         s = g_strdup_printf("__Sig%d",sig++);
384         
385         g_hash_table_insert(marsh,m,s);
386         
387         out_printf(out,"\ntypedef ");
388         print_type(out,m->mtype);
389         
390         out_printf(out,"(*%s) (",s);
391         
392         for(li=m->args;li;li=g_list_next(li)) {
393                 FuncArg *arg = li->data;
394                 print_type(out,arg->atype);
395                 out_printf(out,", "); 
396         }
397         out_printf(out,"gpointer);\n"); 
398         
399         out_printf(out,"\nstatic void\n"
400                 "marshal_%s (GtkObject * object,\n"
401                 "\tGtkSignalFunc func,\n"
402                 "\tgpointer func_data,\n"
403                 "\tGtkArg * args)\n"
404                 "{\n",s);
405         
406         if(strcmp(m->gtktypes->data,"NONE")==0) {
407                 int i;
408                 out_printf(out, "\t%s rfunc;\n\n"
409                         "\trfunc = (%s)func;\n\n"
410                         "\t(*rfunc)((%s *)object",s,s,typebase);
411                 if(strcmp(m->gtktypes->next->data,"NONE")!=0) {
412                         for(i=0,li=m->gtktypes->next;li;
413                             i++,li=g_list_next(li)) {
414                                 out_printf(out, ",\n\t\tGTK_VALUE_%s(args[%d])",
415                                         (char *)li->data,i);
416                         }
417                 }
418                 out_printf(out, ",\n\t\tfunc_data);\n}\n\n");
419         } else {
420                 int i;
421                 out_printf(out, "\t%s rfunc;\n\t",s);
422                 print_type(out,m->mtype);
423                 out_printf(out, " *retval;\n\n"
424                         "\trfunc = (%s)func;\n\n"
425                         "\tretval = GTK_RETLOC_%s(args[%d]);\n\n"
426                         "\t*retval = (*rfunc)((%s *)object",
427                         s,(char *)m->gtktypes->data,
428                         g_list_length(m->gtktypes)-1,typebase);
429                 if(strcmp(m->gtktypes->next->data,"NONE")!=0) {
430                         for(i=0,li=m->gtktypes->next;li;
431                             i++,li=g_list_next(li)) {
432                                 out_printf(out, ",\n\t\tGTK_VALUE_%s(args[%d])",
433                                         (char *)li->data,i);
434                         }
435                 }
436                 out_printf(out, ",\n\t\tfunc_data);\n}\n\n");
437         }
438
439 }
440
441 static void
442 add_enums(Class *c)
443 {
444         GList *li;
445         out_printf(out,"\n");
446         if(signals>0) {
447                 out_printf(out,"enum {\n");
448                 for(li=c->nodes;li;li=g_list_next(li)) {
449                         Node *n = li->data;
450                         if(n->type == METHOD_NODE) {
451                                 Method *m = (Method *)n;
452                                 if(m->scope == SIGNAL_LAST_METHOD ||
453                                    m->scope == SIGNAL_FIRST_METHOD) {
454                                         char *s = g_strdup(m->id);
455                                         g_strup(s);
456                                         out_printf(out,"\t%s_SIGNAL,\n",s);
457                                         g_free(s);
458                                 }
459                         }
460                 }
461                 out_printf(out,"\tLAST_SIGNAL\n};\n\n");
462         }
463         if(arguments>0) {
464                 out_printf(out,"enum {\n\tARG_0,\n");
465                 for(li=c->nodes;li;li=g_list_next(li)) {
466                         Node *n = li->data;
467                         if(n->type == ARGUMENT_NODE) {
468                                 Argument *a = (Argument *)n;
469                                 char *s = g_strdup(a->name);
470                                 g_strup(s);
471                                 out_printf(out,"\tARG_%s,\n",s);
472                                 g_free(s);
473                         }
474                 }
475                 out_printf(out, "};\n\n");
476         }
477
478         if(signals>0)
479                 out_printf(out,
480                            "static guint object_signals[LAST_SIGNAL] = {0};\n\n");
481
482         out_printf(out, "static %sClass *parent_class = NULL;\n\n",ptypebase);
483 }
484
485 static void
486 add_get_type(void)
487 {
488         out_printf(out, "guint\n"
489                 "%s_get_type (void)\n"
490                 "{\n"
491                 "\tstatic guint type = 0;\n\n"
492                 "\tif (!type) {\n"
493                 "\t\tstatic const GtkTypeInfo info = {\n"
494                 "\t\t\t\"%s\",\n"
495                 "\t\t\tsizeof (%s),\n"
496                 "\t\t\tsizeof (%sClass),\n"
497                 "\t\t\t(GtkClassInitFunc) %s_class_init,\n"
498                 "\t\t\t(GtkObjectInitFunc) %s_init,\n"
499                 "\t\t\t/* reserved_1 */ NULL,\n"
500                 "\t\t\t/* reserved_2 */ NULL,\n"
501                 "\t\t\t(GtkClassInitFunc) NULL,\n"
502                 "\t\t};\n\n"
503                 "\t\ttype = gtk_type_unique (%s_get_type(), &info);\n"
504                 "\t}\n\n"
505                 "\treturn type;\n"
506                 "}\n\n",
507                 funcbase,typebase,typebase,typebase,
508                 funcbase,funcbase,pfuncbase);
509 }
510
511 static void
512 add_overrides(Class *c, char *oname)
513 {
514         GList *li;
515         GHashTable *done;
516         char *s;
517         
518         done = g_hash_table_new(g_str_hash,g_str_equal);
519         s = g_strdup("GtkObject"); /* This was already done */
520         g_hash_table_insert(done,s,s);
521         for(li=c->nodes;li;li=g_list_next(li)) {
522                 Node *n = li->data;
523                 char *f;
524                 Method *m;
525                 if(n->type != METHOD_NODE ||
526                    ((Method *)n)->scope != OVERRIDE_METHOD)
527                         continue;
528                 m = (Method *)n;
529                 
530                 s = remove_sep(m->otype);
531                 
532                 if(g_hash_table_lookup(done,s)) {
533                         g_free(s);
534                         continue;
535                 }
536                 g_hash_table_insert(done,s,s);
537
538                 f = replace_sep(m->otype,'_');
539                 g_strdown(f);
540
541                 out_printf(out,"\t%sClass *%s_class = (%sClass *)%s;\n",
542                         s,f,s,oname);
543                 
544                 g_free(f);
545         }
546         g_hash_table_foreach(done,(GHFunc)g_free,NULL);
547         g_hash_table_destroy(done);
548 }
549
550 static void
551 add_signals(Class *c)
552 {
553         GList *li;
554
555         out_printf(out,"\n");
556         for(li=c->nodes;li;li=g_list_next(li)) {
557                 Node *n = li->data;
558                 Method *m;
559                 char *mar;
560                 char *sig;
561                 int is_none;
562                 if(n->type != METHOD_NODE ||
563                    (((Method *)n)->scope != SIGNAL_FIRST_METHOD &&
564                     ((Method *)n)->scope != SIGNAL_LAST_METHOD))
565                         continue;
566
567                 m = (Method *)n;
568
569                 if(g_hash_table_lookup(marsh,m))
570                         mar = g_strconcat("marshal_",
571                                           (char *)g_hash_table_lookup(marsh,m),
572                                           NULL);
573                 else
574                         mar = g_strdup("gtk_signal_default_marshaller");
575                 
576                 is_none = (strcmp(m->gtktypes->next->data,"NONE")==0);
577                 
578                 sig = g_strdup(m->id);
579                 g_strup(sig);
580                 out_printf(out,"\tobject_signals[%s_SIGNAL] =\n"
581                         "\t\tgtk_signal_new (\"%s\",\n"
582                         "\t\t\tGTK_RUN_%s,\n"
583                         "\t\t\tgtk_object_class->type,\n"
584                         "\t\t\tGTK_SIGNAL_OFFSET (%sClass, %s),\n"
585                         "\t\t\t%s,\n"
586                         "\t\t\tGTK_TYPE_%s, %d",
587                         sig,m->id,
588                         m->scope==SIGNAL_LAST_METHOD?"LAST":"FIRST",
589                         typebase,m->id,mar,(char *)m->gtktypes->data,
590                         is_none?0:g_list_length(m->gtktypes->next));
591                 g_free(mar);
592                 g_free(sig);
593                 
594                 if(!is_none) {
595                         GList *l;
596                         for(l=m->gtktypes->next;l;l=g_list_next(l))
597                                 out_printf(out,",\n\t\t\tGTK_TYPE_%s",
598                                         (char *)l->data);
599                 }
600
601                 out_printf(out,");\n");
602         }
603         out_printf(out,"\tgtk_object_class_add_signals (gtk_object_class,\n"
604                 "\t\tobject_signals, LAST_SIGNAL);\n\n");
605 }
606
607 static void
608 set_def_handlers(Class *c, char *oname)
609 {
610         GList *li;
611
612         out_printf(out,"\n");
613         for(li=c->nodes;li;li=g_list_next(li)) {
614                 Node *n = li->data;
615                 Method *m;
616                 if(n->type != METHOD_NODE ||
617                    (((Method *)n)->scope != SIGNAL_FIRST_METHOD &&
618                     ((Method *)n)->scope != SIGNAL_LAST_METHOD &&
619                     ((Method *)n)->scope != VIRTUAL_METHOD &&
620                     ((Method *)n)->scope != OVERRIDE_METHOD))
621                         continue;
622
623                 m = (Method *)n;
624
625                 if(m->scope == OVERRIDE_METHOD) {
626                         char *s;
627                         s = replace_sep(m->otype,'_');
628                         g_strdown(s);
629                         out_printf(out,"\t%s_class->%s = %s_%s;\n",
630                                 s,m->id,funcbase,m->id);
631                 } else {
632                         if(m->cbuf)
633                                 out_printf(out,"\t%s->%s = _real_%s_%s;\n",
634                                         oname,m->id,funcbase,m->id);
635                         else
636                                 out_printf(out,"\t%s->%s = NULL;\n",
637                                         oname,m->id);
638                 }
639         }
640 }
641
642 static void
643 make_arguments(Class *c)
644 {
645         GList *li;
646
647         out_printf(out,"\n");
648         for(li=c->nodes;li;li=g_list_next(li)) {
649                 Node *n = li->data;
650                 Argument *a;
651                 GString *flags;
652                 GList *l;
653                 char *s;
654                 if(n->type != ARGUMENT_NODE)
655                         continue;
656
657                 a = (Argument *)n;
658                 
659                 if(a->get && a->set)
660                         flags = g_string_new("GTK_ARG_READWRITE");
661                 else if(a->get)
662                         flags = g_string_new("GTK_ARG_READABLE");
663                 else
664                         flags = g_string_new("GTK_ARG_WRITABLE");
665                 
666                 for(l=a->flags;l;l=g_list_next(l))
667                         g_string_sprintfa(flags," | GTK_ARG_%s",(char *)l->data);
668
669                 s = g_strdup(a->name);
670                 g_strup(s);
671                 out_printf(out,"\tgtk_object_add_arg_type(\"%s::%s\",\n"
672                         "\t\tGTK_TYPE_%s,\n"
673                         "\t\t%s,\n"
674                         "\t\tARG_%s);\n",
675                         typebase,a->name,a->gtktype,flags->str,s);
676                 g_free(s);
677                 g_string_free(flags,TRUE);
678         }
679         
680         out_printf(out,
681                    "\n\tgtk_object_class->set_arg = __object_set_arg;\n"
682                    "\tgtk_object_class->get_arg = __object_get_arg;\n");
683 }
684
685 static void
686 add_inits(Class *c)
687 {
688         GList *li;
689         for(li=c->nodes;li;li=g_list_next(li)) {
690                 Node *n = li->data;
691                 Method *m;
692                 if(n->type != METHOD_NODE)
693                         continue;
694                 m = (Method *)n;
695                 if(m->scope == INIT_METHOD) {
696                         if(m->line_no>0)
697                                 out_addline_infile(out,m->line_no);
698                         print_method(out,"static ","\n"," ","",m);
699                         if(m->line_no>0)
700                                 out_addline_outfile(out);
701                         out_printf(out,"{\n");
702                 } else if(m->scope == CLASS_INIT_METHOD) {
703                         if(m->line_no>0)
704                                 out_addline_infile(out,m->line_no);
705                         print_method(out,"static ","\n"," ","",m);
706                         if(m->line_no>0)
707                                 out_addline_outfile(out);
708                         out_printf(out,"{\n");
709                         if(signals>0 ||
710                            arguments>0 ||
711                            overrides>0)
712                                 out_printf(out,
713                                            "\tGtkObjectClass *"
714                                            "gtk_object_class = "
715                                            "(GtkObjectClass*) %s;\n",
716                                            ((FuncArg *)m->args->data)->name);
717                         
718                         if(overrides>0)
719                                 add_overrides(c,
720                                               ((FuncArg *)m->args->data)->name);
721                         
722                         out_printf(out,"\n\tparent_class = "
723                                 "gtk_type_class (%s_get_type ());\n",
724                                 pfuncbase);
725
726                         if(signals>0)
727                                 add_signals(c);
728
729                         set_def_handlers(c, ((FuncArg *)m->args->data)->name);
730                         
731                         if(arguments>0)
732                                 make_arguments(c);
733                 } else
734                         continue;
735
736                 if(m->cbuf) {
737                         out_printf(out," {\n");
738                         out_addline_infile(out,m->ccode_line);
739                         out_printf(out,"%s\n",m->cbuf->str);
740                         out_addline_outfile(out);
741                         out_printf(out," }\n",m->cbuf->str);
742                 } else {
743                         out_printf(out,"return;\n");
744                 }
745                 out_printf(out,"}\n");
746         }
747 }
748
749 static void
750 add_getset_arg(Class *c, int is_set)
751 {
752         GList *li;
753         out_printf(out,"\nstatic void\n"
754                 "__object_%s_arg (GtkObject *object,\n"
755                 "\tGtkArg *arg,\n"
756                 "\tguint arg_id)\n"
757                 "{\n"
758                 "\t%s *self, *this;\n\n"
759                 "\tself = this = %s (object);\n\n"
760                 "\tswitch (arg_id) {\n",
761                 is_set?"set":"get",typebase,macrobase);
762
763         for(li=c->nodes;li;li=g_list_next(li)) {
764                 Node *n = li->data;
765                 Argument *a;
766                 char *s;
767                 GString *cbuf;
768                 int line_no;
769                 if(n->type != ARGUMENT_NODE)
770                         continue;
771                 a = (Argument *)n;
772                 if(is_set) {
773                         cbuf = a->set;
774                         line_no = a->set_line;
775                 } else {
776                         cbuf = a->get;
777                         line_no = a->get_line;
778                 }
779                 if(!cbuf)
780                         continue;
781                 s = g_strdup(a->name);
782                 g_strup(s);
783                 out_printf(out,"\tcase ARG_%s:\n"
784                         "#define ARG (GTK_VALUE_%s(*arg))\n"
785                         "\t\t{\n",
786                         s,a->gtktype);
787                 g_free(s);
788                 out_addline_infile(out,line_no);
789                 out_printf(out,"%s\n",cbuf->str);
790                 out_addline_outfile(out);
791                 out_printf(out,"\t\t}\n\t\tbreak;\n"
792                         "#undef ARG\n");
793         }
794         out_printf(out,"\tdefault:\n\t\tbreak;\n\t}\n}\n");
795 }
796
797 static void
798 print_checks(Method *m, FuncArg *fa)
799 {
800         GList *li;
801         int is_void;
802         is_void = (strcmp(m->mtype->name,"void")==0 &&
803                    m->mtype->stars == 0);
804         
805         for(li=fa->checks;li;li=g_list_next(li)) {
806                 Check *ch = li->data;
807                 char *s;
808                 if(is_void)
809                         out_printf(out,"\tg_return_if_fail (");
810                 else
811                         out_printf(out,"\tg_return_val_if_fail (");
812                 switch(ch->chtype) {
813                 case NULL_CHECK:
814                         out_printf(out,"%s != NULL",fa->name);
815                         break;
816                 case TYPE_CHECK:
817                         s = make_is_macro(fa->atype->name);
818                         out_printf(out,"%s (%s)",s,fa->name);
819                         g_free(s);
820                         break;
821                 case LT_CHECK:
822                         out_printf(out,"%s < %s",fa->name,ch->number);
823                         break;
824                 case GT_CHECK:
825                         out_printf(out,"%s > %s",fa->name,ch->number);
826                         break;
827                 case LE_CHECK:
828                         out_printf(out,"%s <= %s",fa->name,ch->number);
829                         break;
830                 case GE_CHECK:
831                         out_printf(out,"%s >= %s",fa->name,ch->number);
832                         break;
833                 case EQ_CHECK:
834                         out_printf(out,"%s == %s",fa->name,ch->number);
835                         break;
836                 case NE_CHECK:
837                         out_printf(out,"%s != %s",fa->name,ch->number);
838                         break;
839                 }
840                 if(is_void)
841                         out_printf(out,");\n");
842                 else {
843                         out_printf(out,", (");
844                         print_type(out,m->mtype);
845                         out_printf(out,")%s);\n",
846                                 m->onerror?m->onerror:"0");
847                 }
848         }
849 }
850
851 static void
852 print_preconditions(Method *m)
853 {
854         GList *li;
855         
856         for(li=m->args;li;li=g_list_next(li)) {
857                 FuncArg *fa = li->data;
858                 if(fa->checks)
859                         print_checks(m,fa);
860         }
861 }
862
863 static void
864 print_method_body(Method *m, int pre)
865 {
866         out_printf(out,"{\n");
867         if(pre) {
868                 print_preconditions(m);
869                 out_printf(out,"\t{\n");
870         }
871
872         out_addline_infile(out,m->ccode_line);
873         out_printf(out,"\t\t%s\n",m->cbuf->str);
874         out_addline_outfile(out);
875
876         if(pre)
877                 out_printf(out,"\t}\n");
878         out_printf(out,"}\n");
879 }
880
881 static void
882 put_method(Method *m)
883 {
884         char *s;
885         out_printf(out,"\n");
886         switch(m->scope) {
887         case PUBLIC_SCOPE:
888                 out_addline_infile(out,m->line_no);
889                 print_method(out,"","\n"," ","",m);
890                 print_method_body(m,TRUE);
891                 break;
892         case PRIVATE_SCOPE:
893                 out_addline_infile(out,m->line_no);
894                 print_method(out,"static ","\n"," ","",m);
895                 print_method_body(m,TRUE);
896                 break;
897         case SIGNAL_FIRST_METHOD:
898         case SIGNAL_LAST_METHOD:
899                 out_addline_infile(out,m->line_no);
900                 print_method(out,"","\n"," ","",m);
901                 out_addline_outfile(out);
902                 out_printf(out,"{\n");
903                 s = g_strdup(m->id);
904                 g_strup(s);
905                 if(strcmp(m->mtype->name,"void")==0 &&
906                    m->mtype->stars==0) {
907                         GList *li;
908                         print_preconditions(m);
909                         if(((FuncArg *)m->args->data)->name)
910                         out_printf(out,"\tgtk_signal_emit (GTK_OBJECT (%s),\n"
911                                 "\t\tobject_signals[%s_SIGNAL]",
912                                 ((FuncArg *)m->args->data)->name,s);
913                         for(li=m->args->next;li;li=g_list_next(li)) {
914                                 FuncArg *fa = li->data;
915                                 out_printf(out,",\n\t\t%s",fa->name);
916                         }
917                         out_printf(out,");\n}\n");
918                 } else {
919                         GList *li;
920                         out_printf(out,"\t");
921                         print_type(out,m->mtype);
922                         out_printf(out,"return_val;\n");
923                         print_preconditions(m);
924                         out_printf(out,"\tgtk_signal_emit (GTK_OBJECT (%s),\n"
925                                 "\t\tobject_signals[%s_SIGNAL]",
926                                 ((FuncArg *)m->args->data)->name,s);
927                         for(li=m->args->next;li;li=g_list_next(li)) {
928                                 FuncArg *fa = li->data;
929                                 out_printf(out,",\n\t\t%s",fa->name);
930                         }
931                         out_printf(out,",\n\t\t&return_val);\n"
932                                 "\treturn return_val;\n}\n");
933                 }
934
935                 if(!m->cbuf)
936                         break;
937                 out_addline_infile(out,m->line_no);
938                 print_method(out,"static ","\n_real_"," ","",m);
939                 print_method_body(m,FALSE);
940                 break;
941         case VIRTUAL_METHOD:
942                 out_addline_infile(out,m->line_no);
943                 print_method(out,"","\n"," ","",m);
944                 out_addline_outfile(out);
945                 out_printf(out,"{\n"
946                         "\t%sClass *class;\n",typebase);
947                 print_preconditions(m);
948                 out_printf(out,"\tclass = %s_CLASS(GTK_OBJECT(%s)->klass);\n\n"
949                         "\tif(class->%s)\n",
950                         macrobase, ((FuncArg *)m->args->data)->name, m->id);
951                 if(strcmp(m->mtype->name,"void")==0 &&
952                    m->mtype->stars==0) {
953                         GList *li;
954                         out_printf(out,"\t\t(*class->%s)(%s",m->id,
955                                    ((FuncArg *)m->args->data)->name);
956                         for(li=m->args->next;li;li=g_list_next(li)) {
957                                 FuncArg *fa = li->data;
958                                 out_printf(out,",%s",fa->name);
959                         }
960                         out_printf(out,");\n}\n");
961                 } else {
962                         GList *li;
963                         out_printf(out,"\t\treturn (*class->%s)(%s",m->id,
964                                    ((FuncArg *)m->args->data)->name);
965                         for(li=m->args->next;li;li=g_list_next(li)) {
966                                 FuncArg *fa = li->data;
967                                 out_printf(out,",%s",fa->name);
968                         }
969                         out_printf(out,");\n"
970                                 "\telse\n"
971                                 "\t\treturn (");
972                         print_type(out,m->mtype);
973                         out_printf(out,")(%s);\n}\n",
974                                 m->onerror?m->onerror:"0");
975                 }
976
977                 if(!m->cbuf)
978                         break;
979                 out_addline_infile(out,m->line_no);
980                 print_method(out,"static ","\n_real_"," ","",m);
981                 print_method_body(m,FALSE);
982                 break;
983         case OVERRIDE_METHOD:
984                 out_addline_infile(out,m->line_no);
985                 print_method(out,"static ","\n"," ","",m);
986                 print_method_body(m,TRUE);
987                 break;
988         default:
989                 break;
990         }
991 }
992
993 static void
994 check_duplicate(Class *c,Node *node,char *id, int line_no)
995 {
996         GList *l;
997         for(l=c->nodes;l;l=g_list_next(l)) {
998                 Node *n = l->data;
999                 char *nid;
1000                 int nline_no;
1001                 char *s;
1002                 if(n->type == METHOD_NODE) {
1003                         Method *m = (Method *)n;
1004                         nid = m->id;
1005                         nline_no = m->line_no;
1006                 } else if(n->type == VARIABLE_NODE) {
1007                         Variable *v = (Variable *)n;
1008                         nid = v->id;
1009                         nline_no = v->line_no;
1010                 } else
1011                         continue;
1012                 if(n==node ||
1013                    line_no>=nline_no ||
1014                    strcmp(nid,id)!=0)
1015                         continue;
1016                 s = g_strdup_printf("symbol '%s' redefined, "
1017                                     "first defined on line %d",
1018                                     id,line_no);
1019                 print_error(FALSE,s,nline_no);
1020         }
1021 }
1022
1023 static void
1024 check_duplicate_symbols(Class *c)
1025 {
1026         GList *l;
1027         for(l=c->nodes;l;l=g_list_next(l)) {
1028                 Node *n = l->data;
1029                 if(n->type == METHOD_NODE) {
1030                         Method *m = (Method *)n;
1031                         check_duplicate(c,n,m->id,m->line_no);
1032                 } else if(n->type == VARIABLE_NODE) {
1033                         Variable *v = (Variable *)n;
1034                         check_duplicate(c,n,v->id,v->line_no);
1035                 }
1036         }
1037 }
1038
1039 static void
1040 check_duplicate_named(Class *c,Node *node,char *id, int line_no)
1041 {
1042         GList *l;
1043         for(l=c->nodes;l;l=g_list_next(l)) {
1044                 Node *n = l->data;
1045                 char *nid;
1046                 int nline_no;
1047                 char *s;
1048                 if(n->type == METHOD_NODE) {
1049                         Method *m = (Method *)n;
1050                         if(m->scope == SIGNAL_LAST_METHOD ||
1051                            m->scope == SIGNAL_FIRST_METHOD) {
1052                                 nid = m->id;
1053                                 nline_no = m->line_no;
1054                         } else
1055                                 continue;
1056                 } else if(n->type == ARGUMENT_NODE) {
1057                         Argument *a = (Argument *)n;
1058                         nid = a->name;
1059                         nline_no = a->line_no;
1060                 } else
1061                         continue;
1062                 if(n==node ||
1063                    line_no>=nline_no ||
1064                    strcmp(nid,id)!=0)
1065                         continue;
1066                 s = g_strdup_printf("named symbol (argument or signal) '%s' "
1067                                     "redefined, first defined on line %d",
1068                                     id,line_no);
1069                 print_error(FALSE,s,nline_no);
1070         }
1071 }
1072
1073 static void
1074 check_duplicate_signals_args(Class *c)
1075 {
1076         GList *l;
1077         for(l=c->nodes;l;l=g_list_next(l)) {
1078                 Node *n = l->data;
1079                 if(n->type == METHOD_NODE) {
1080                         Method *m = (Method *)n;
1081                         if(m->scope == SIGNAL_LAST_METHOD ||
1082                            m->scope == SIGNAL_FIRST_METHOD)
1083                                 check_duplicate_named(c,n,m->id,m->line_no);
1084                 } else if(n->type == ARGUMENT_NODE) {
1085                         Argument *a = (Argument *)n;
1086                         check_duplicate_named(c,n,a->name,a->line_no);
1087                 }
1088         }
1089 }
1090
1091 static void
1092 check_public_new(Class *c)
1093 {
1094         GList *l;
1095         for(l=c->nodes;l;l=g_list_next(l)) {
1096                 Node *n = l->data;
1097                 if(n->type == METHOD_NODE) {
1098                         Method *m = (Method *)n;
1099                         if(m->scope!=PUBLIC_SCOPE &&
1100                            strcmp(m->id,"new")==0)
1101                                 print_error(TRUE,
1102                                             "'new' should be a public method",
1103                                             m->line_no);
1104                 }
1105         }
1106 }
1107
1108 static int
1109 count_signals(Class *c)
1110 {
1111         int num = 0;
1112         GList *l;
1113         for(l=c->nodes;l;l=g_list_next(l)) {
1114                 Node *n = l->data;
1115                 if(n->type == METHOD_NODE) {
1116                         Method *m = (Method *)n;
1117                         if(m->scope == SIGNAL_LAST_METHOD ||
1118                            m->scope == SIGNAL_FIRST_METHOD)
1119                                 num++;
1120                 }
1121         }
1122         return num;
1123 }
1124
1125 static int
1126 count_arguments(Class *c)
1127 {
1128         int num = 0;
1129         GList *li;
1130
1131         for(li=c->nodes;li;li=g_list_next(li)) {
1132                 Node *n = li->data;
1133                 if(n->type == ARGUMENT_NODE)
1134                         num ++;
1135         }
1136         return num;
1137 }
1138
1139 static int
1140 count_overrides(Class *c)
1141 {
1142         int num = 0;
1143         GList *l;
1144         for(l=c->nodes;l;l=g_list_next(l)) {
1145                 Node *n = l->data;
1146                 if(n->type == METHOD_NODE) {
1147                         Method *m = (Method *)n;
1148                         if(m->scope == OVERRIDE_METHOD)
1149                                 num++;
1150                 }
1151         }
1152         return num;
1153 }
1154
1155
1156 static void
1157 open_files(void)
1158 {
1159         char *outfile,*outfileh;
1160
1161         outfile = g_strconcat(filebase,".c",NULL);
1162         outfileh = g_strconcat(filebase,".h",NULL);
1163         
1164         out = fopen(outfile,"w");
1165         if(!out) {
1166                 g_error("Cannot open outfile: %s",outfile);
1167         }
1168         outh = fopen(outfileh,"w");
1169         if(!outh) {
1170                 g_error("Cannot open outfile: %s",outfileh);
1171         }
1172 }
1173
1174 static void
1175 generate_outfiles(void)
1176 {
1177         char *p;
1178         GList *li;
1179         time_t curtime;
1180
1181         time(&curtime);
1182         out_printf(outh,"/* Generated by GOB (v%s) on %s"
1183                "   (do not edit directly) */\n\n",VERSION,ctime(&curtime));
1184         out_printf(out,"/* Generated by GOB (v%s) on %s"
1185                "   (do not edit directly) */\n\n",VERSION,ctime(&curtime));
1186         
1187         p = replace_sep(((Class *)class)->otype,'_');
1188         g_strup(p);
1189         out_printf(outh,"#ifndef __%s_H__\n#define __%s_H__\n\n"
1190                 "#include <gtk/gtk.h>\n\n",p,p);
1191         g_free(p);
1192
1193         out_printf(outh,"#ifdef __cplusplus\n"
1194                 "extern \"C\" {\n"
1195                 "#endif /* __cplusplus */\n\n");
1196         
1197         out_printf(out,"#include \"%s.h\"\n\n",filebase);
1198         
1199         for(li=nodes;li;li=g_list_next(li)) {
1200                 Node *node = li->data;
1201                 if(node->type == CCODE_NODE) {
1202                         CCode *cc = (CCode *)node;
1203                         FILE *fp;
1204                         if(cc->header)
1205                                 fp = outh;
1206                         else {
1207                                 fp = out;
1208                                 out_addline_infile(fp,cc->line_no);
1209                         }
1210                         out_printf(fp,"\n%s\n",cc->cbuf->str);
1211                         if(!cc->header)
1212                                 out_addline_outfile(fp);
1213                 } else if(node->type == CLASS_NODE) {
1214                         GList *l;
1215                         Class *c = (Class *)class;
1216                         char *otype,*ptype;
1217
1218                         signals = count_signals(c);
1219                         arguments = count_arguments(c);
1220                         overrides = count_overrides(c);
1221                         
1222                         out_printf(outh,"\n#define %s(obj)\t"
1223                                 "GTK_CHECK_CAST((obj),%s_get_type(),%s)\n",
1224                                 macrobase,funcbase,typebase);
1225                         out_printf(outh,"#define %s_CLASS(klass)\t"
1226                                 "GTK_CHECK_CLASS_CAST((klass),%s_get_type(),%sClass)\n",
1227                                 macrobase,funcbase,typebase);
1228                         out_printf(outh,"#define %s(obj)\t"
1229                                 "GTK_CHECK_TYPE((obj), %s_get_type ())\n\n",
1230                                 macrois,funcbase);
1231
1232                         otype = remove_sep(c->otype);
1233                         ptype = remove_sep(c->ptype);
1234                         out_printf(outh,"\ntypedef struct _%s %s;\n",otype,otype);
1235                         out_printf(outh,"struct _%s {\n\t%s __parent__;\n",
1236                                    otype,ptype);
1237                         for(l=c->nodes;l;l=g_list_next(l)) {
1238                                 Node *n = l->data;
1239                                 if(n->type == VARIABLE_NODE)
1240                                         put_variable((Variable *)n);
1241                         }
1242                         out_printf(outh,"};\n");
1243
1244                         out_printf(outh,"\ntypedef struct _%sClass %sClass;\n",
1245                                 otype,otype);
1246                         out_printf(outh,
1247                                 "struct _%sClass {\n\t%sClass __parent__;\n",
1248                                 otype,ptype);
1249                         for(l=c->nodes;l;l=g_list_next(l)) {
1250                                 Node *n = l->data;
1251                                 if(n->type == METHOD_NODE)
1252                                         put_vs_method((Method *)n);
1253                         }
1254                         out_printf(outh,"};\n\n");
1255
1256                         out_printf(outh,"guint\t%s_get_type\t(void);\n",funcbase);
1257                         
1258                         if(arguments>0) {
1259                                 out_printf(out,"static void __object_set_arg "
1260                                            "(GtkObject *object, GtkArg *arg, "
1261                                            "guint arg_id);\n"
1262                                            "static void __object_get_arg "
1263                                            "(GtkObject *object, GtkArg *arg, "
1264                                            "guint arg_id);\n");
1265                         }
1266
1267                         for(l=c->nodes;l;l=g_list_next(l)) {
1268                                 Node *n = l->data;
1269                                 if(n->type == METHOD_NODE) {
1270                                         put_pub_method((Method *)n);
1271                                         put_priv_method_prot((Method *)n);
1272                                 }
1273                         }
1274
1275                         if(signals>0) {
1276                                 for(l=c->nodes;l;l=g_list_next(l)) {
1277                                         Node *n = l->data;
1278                                         if(n->type == METHOD_NODE) {
1279                                                 add_signal_prots((Method *)n);
1280                                         }
1281                                 }
1282                         }
1283                         
1284                         add_enums(c);
1285                         
1286                         add_get_type();
1287
1288                         def_methods(c);
1289
1290                         out_printf(out,"#define GET_NEW (gtk_type_new(%s_get_type()))\n",
1291                                 funcbase);
1292                         
1293                         add_inits(c);
1294
1295                         if(arguments>0) {
1296                                 add_getset_arg(c, TRUE);
1297                                 add_getset_arg(c, FALSE);
1298                         }
1299
1300                         for(l=c->nodes;l;l=g_list_next(l)) {
1301                                 Node *n = l->data;
1302                                 if(n->type == METHOD_NODE) {
1303                                         put_method((Method *)n);
1304                                 }
1305                         }
1306
1307                         out_printf(out,"#undef GET_NEW\n");
1308
1309                         undef_methods(c);
1310
1311                         g_free(otype);
1312                         g_free(ptype);
1313                 } else
1314                         g_assert_not_reached();
1315         }
1316
1317         out_printf(outh,"\n#ifdef __cplusplus\n"
1318                 "}\n"
1319                 "#endif /* __cplusplus */\n\n"
1320                 "#endif");
1321 }
1322
1323 static void
1324 usage(poptContext optCon, int exitcode, char *error, char *addl)
1325 {
1326         poptPrintUsage(optCon, stderr, 0);
1327         if (error) fprintf(stderr, "%s: %s", error, addl);
1328         exit(exitcode);
1329 }
1330
1331
1332 int
1333 main(int argc, char *argv[])
1334 {
1335         int c;
1336         poptContext optCon;
1337         
1338         struct poptOption optionsTable[] = {
1339                 { "exit-on-warn", 'w', 0, &exit_on_warn, 0,
1340                          "exit on warnings" },
1341                 POPT_AUTOHELP
1342                 { NULL, 0, 0, NULL, 0 }
1343         };
1344         
1345         optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
1346         poptSetOtherOptionHelp(optCon, "[OPTIONS]* [filename]");
1347         
1348         while ((c = poptGetNextOpt(optCon)) >= 0)
1349                 ;
1350         
1351         filename = poptGetArg(optCon);
1352         if(!(poptPeekArg(optCon) == NULL))
1353                 usage(optCon, 1, "Specify only one file",
1354                       ".e.g., filename.gob");
1355                         
1356         if (c < -1) {
1357                 /* an error occurred during option processing */
1358                 fprintf(stderr, "%s: %s\n",
1359                         poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
1360                         poptStrerror(c));
1361                 return 1;
1362         }
1363         
1364         if(filename) {
1365                 yyin = fopen(filename,"r");
1366                 if(!yyin) {
1367                         fprintf(stderr,"Error: can't open file '%s'\n",
1368                                 filename);
1369                         exit(1);
1370                 }
1371         } else
1372                 filename = "stdin";
1373
1374         /*yydebug = 1;*/
1375         if(yyparse()!=0)
1376                 g_error("Parsing errors, quitting");
1377         if(!class)
1378                 print_error(FALSE," no class defined",0);
1379         
1380         make_bases();
1381         
1382         exit_on_error = FALSE;
1383         make_inits((Class *)class);
1384         check_duplicate_symbols((Class *)class);
1385         check_duplicate_signals_args((Class *)class);
1386         check_public_new((Class *)class);
1387         exit_on_error = TRUE;
1388         
1389         if(got_error)
1390                 exit(1);
1391
1392         open_files();
1393         
1394         generate_outfiles();
1395
1396         fclose(out);
1397         fclose(outh);
1398         
1399         poptFreeContext(optCon);
1400         return 0;
1401 }