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