]> git.draconx.ca Git - gob-dx.git/blob - src/parse.y
Release 2.0.9
[gob-dx.git] / src / parse.y
1 /* GOB C Preprocessor
2  * Copyright (C) 1999-2000 the Free Software Foundation.
3  * Copyright (C) 2000 Eazel, Inc.
4  * Copyright (C) 2001-2004 George (Jiri) Lebl
5  *
6  * Author: George Lebl
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the  Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
21  * USA.
22  */
23 %{
24
25 #include "config.h"
26 #include <glib.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include "treefuncs.h"
32 #include "main.h"
33 #include "util.h"
34
35 /* FIXME: add gettext support */
36 #define _(x) (x)
37         
38 GList *nodes = NULL;
39
40 static GList *class_nodes = NULL;
41 Node *class = NULL;
42 GList *enums = NULL;
43 static GList *enum_vals = NULL;
44 static GList *flag_vals = NULL;
45 static GList *error_vals = NULL;
46
47 static char *chunk_size = NULL;
48 static char *bonobo_object_class = NULL;
49 static GList *interfaces = NULL;
50 static GList *typestack = NULL;
51 static GList *funcargs = NULL;
52 static GList *checks = NULL;
53 static int has_self = FALSE;
54 static int vararg = FALSE;
55 static Method *last_added_method = NULL;
56
57 /* destructor and initializer for variables */
58 static gboolean destructor_unref = FALSE;
59 static char *destructor = NULL;
60 static int destructor_line = 0;
61 static gboolean destructor_simple = TRUE;
62 static char *initializer = NULL;
63 static int initializer_line = 0;
64
65 static char *onerror = NULL;
66 static char *defreturn = NULL;
67
68 static GList *gtktypes = NULL;
69
70 static Property *property = NULL;
71
72 /* this can be a global as we will only do one function at a time
73    anyway */
74 static int the_scope = NO_SCOPE;
75
76 void free(void *ptr);
77 int yylex(void);
78
79 extern int ccode_line;
80 extern int line_no;
81 extern gboolean for_cpp;
82
83 extern char *yytext;
84
85 static void
86 yyerror(char *str)
87 {
88         char *out=NULL;
89         char *p;
90         
91         if(strcmp(yytext,"\n")==0) {
92                 out=g_strconcat("Error: ",str," before newline",NULL);
93         } else if(yytext[0]=='\0') {
94                 out=g_strconcat("Error: ", str, " at end of input", NULL);
95         } else {
96                 char *tmp = g_strdup(yytext);
97                 while((p=strchr(tmp, '\n')))
98                         *p='.';
99
100                 out=g_strconcat("Error: ", str, " before '", tmp, "'", NULL);
101                 g_free(tmp);
102         }
103
104         fprintf(stderr, "%s:%d: %s\n", filename, line_no, out);
105         g_free(out);
106         
107         exit(1);
108 }
109
110 static Type *
111 pop_type(void)
112 {
113         Type *type = typestack->data;
114         typestack = g_list_remove(typestack,typestack->data);
115         return type;
116 }
117
118 static void
119 push_variable (char *name, int scope, int line_no, char *postfix)
120 {
121         Node *var;
122         Type *type = pop_type ();
123
124         type->postfix = postfix;
125         
126         var = node_new (VARIABLE_NODE,
127                         "scope", scope,
128                         "vtype:steal", type,
129                         "id:steal", name,
130                         "line_no", line_no,
131                         "destructor_unref", destructor_unref,
132                         "destructor:steal", destructor,
133                         "destructor_line", destructor_line,
134                         "destructor_simple", destructor_simple,
135                         "initializer:steal", initializer,
136                         "initializer_line", initializer_line,
137                         NULL);
138         class_nodes = g_list_append(class_nodes, var);
139 }
140
141 static void
142 push_function (int scope, int method, char *oid, char *id,
143                GString *cbuf, int line_no, int ccode_line,
144                gboolean vararg, GList *flags)
145 {
146         Node *node;
147         Type *type;
148         char *c_cbuf;
149
150         g_assert(scope != CLASS_SCOPE);
151        
152         if(method == INIT_METHOD || method == CLASS_INIT_METHOD) {
153                 type = (Type *)node_new (TYPE_NODE,
154                                          "name", "void",
155                                          NULL);
156         } else {
157                 type = pop_type();
158         }
159         
160         /* a complicated and ugly test to figure out if we have
161            the wrong number of types for a signal */
162         if((method == SIGNAL_FIRST_METHOD ||
163             method == SIGNAL_LAST_METHOD) &&
164            g_list_length(gtktypes) != g_list_length(funcargs) &&
165            !(g_list_length(funcargs) == 1 &&
166              g_list_length(gtktypes) == 2 &&
167              strcmp(gtktypes->next->data, "NONE")==0)) {
168                 error_print(GOB_WARN, line_no,
169                             _("The number of GTK arguments and "
170                               "function arguments for a signal "
171                               "don't seem to match"));
172         }
173         if(g_list_length(gtktypes) > 2) {
174                 GList *li;
175                 for(li = gtktypes->next; li; li = li->next) {
176                         if(strcmp(li->data, "NONE")==0) {
177                                 error_print(GOB_ERROR, line_no,
178                                             _("NONE can only appear in an "
179                                               "argument list by itself"));
180                         }
181                 }
182         }
183         if(cbuf) {
184                 char *p;
185                 c_cbuf = p = cbuf->str;
186                 while(p && *p && (*p==' ' || *p=='\t' || *p=='\n' || *p=='\r'))
187                         p++;
188                 if(!p || !*p)
189                         c_cbuf = NULL;
190         } else
191                 c_cbuf = NULL;
192
193         node = node_new (METHOD_NODE,
194                          "scope", scope,
195                          "method", method,
196                          "mtype:steal", type,
197                          "otype:steal", oid,
198                          "gtktypes:steal", gtktypes,
199                          "flags:steal", flags,
200                          "id:steal", id,
201                          "args:steal", funcargs,
202                          "onerror:steal", onerror,
203                          "defreturn:steal", defreturn,
204                          "cbuf:steal", c_cbuf,
205                          "line_no", line_no,
206                          "ccode_line", ccode_line,
207                          "vararg", vararg,
208                          "unique_id", method_unique_id++,
209                          NULL);
210
211         last_added_method = (Method *)node;
212
213         if(cbuf)
214                 g_string_free(cbuf,
215                               /*only free segment if we haven't passed it
216                                 above */
217                               c_cbuf?FALSE:TRUE);
218         gtktypes = NULL;
219         funcargs = NULL;
220
221         onerror = NULL;
222         defreturn = NULL;
223
224         class_nodes = g_list_append(class_nodes, node);
225 }
226
227 static void
228 free_all_global_state(void)
229 {
230         g_free(onerror);
231         onerror = NULL;
232         g_free(defreturn);
233         defreturn = NULL;
234
235         g_free(chunk_size);
236         chunk_size = NULL;
237         
238         g_list_foreach(gtktypes, (GFunc)g_free, NULL);
239         g_list_free(gtktypes);
240         gtktypes = NULL;
241
242         node_list_free (funcargs);
243         funcargs = NULL;
244 }
245
246 static void
247 push_funcarg(char *name, char *postfix)
248 {
249         Node *node;
250         Type *type = pop_type();
251
252         type->postfix = postfix;
253         
254         node = node_new (FUNCARG_NODE,
255                          "atype:steal", type,
256                          "name:steal", name,
257                          "checks:steal", checks,
258                          NULL);
259         checks = NULL;
260         
261         funcargs = g_list_append(funcargs, node);
262 }
263
264 static void
265 push_init_arg(char *name, int is_class)
266 {
267         Node *node;
268         Node *type;
269         char *tn;
270         
271         if(is_class)
272                 tn = g_strconcat(((Class *)class)->otype,":Class",NULL);
273         else
274                 tn = g_strdup(((Class *)class)->otype);
275
276         type = node_new (TYPE_NODE,
277                          "name:steal", tn,
278                          "pointer", "*",
279                          NULL);
280         node = node_new (FUNCARG_NODE,
281                          "atype:steal", (Type *)type,
282                          "name:steal", name,
283                          NULL);
284         funcargs = g_list_prepend(funcargs, node);
285 }
286
287 static void
288 push_self(char *id, gboolean constant)
289 {
290         Node *node;
291         Node *type;
292         GList *ch = NULL;
293         type = node_new (TYPE_NODE,
294                          "name", ((Class *)class)->otype,
295                          "pointer", constant ? "const *" : "*",
296                          NULL);
297         ch = g_list_append (ch, node_new (CHECK_NODE,
298                                           "chtype", NULL_CHECK,
299                                           NULL));
300         ch = g_list_append (ch, node_new (CHECK_NODE,
301                                           "chtype", TYPE_CHECK,
302                                           NULL));
303         node = node_new (FUNCARG_NODE,
304                          "atype:steal", (Type *)type,
305                          "name:steal", id,
306                          "checks:steal", ch,
307                          NULL);
308         funcargs = g_list_prepend(funcargs, node);
309 }
310
311 static Variable *
312 find_var_or_die(const char *id, int line)
313 {
314         GList *li;
315
316         for(li = class_nodes; li != NULL; li = li->next) {
317                 Variable *var;
318                 Node *node = li->data;
319                 if(node->type != VARIABLE_NODE)
320                         continue;
321                 var = li->data;
322                 if(strcmp(var->id, id)==0)
323                         return var;
324         }
325
326         error_printf(GOB_ERROR, line, _("Variable %s not defined here"), id);
327
328         g_assert_not_reached();
329         return NULL;
330 }
331
332 static gboolean
333 set_return_value(char *type, char *val)
334 {
335         if(strcmp(type, "onerror")==0) {
336                 if(!onerror) {
337                         onerror = val;
338                         return TRUE;
339                 } else
340                         return FALSE;
341         } else if(strcmp(type, "defreturn")==0) {
342                 if(!defreturn) {
343                         defreturn = val;
344                         return TRUE;
345                 } else
346                         return FALSE;
347         }
348         return FALSE;
349 }
350
351 static void
352 export_accessors (const char *var_name,
353                   gboolean do_get,
354                   int get_lineno,
355                   gboolean do_set,
356                   int set_lineno,
357                   Type *type,
358                   const char *gtktype,
359                   int lineno)
360 {       
361         Type *the_type;
362
363         if (type != NULL)
364                 the_type = (Type *)node_copy ((Node *)type);
365         else
366                 the_type = get_tree_type (gtktype, TRUE);
367
368         if (the_type == NULL) {
369                 error_print (GOB_ERROR, line_no,
370                              _("Cannot determine type of property or argument"));
371                 return;
372         }
373
374         if (do_get) {
375                 char *get_id = g_strdup_printf ("get_%s", var_name);
376                 GString *get_cbuf = g_string_new (NULL);
377                 Node *node1 = node_new (TYPE_NODE,
378                                         "name", the_type->name,
379                                         "pointer", the_type->pointer,
380                                         "postfix", the_type->postfix,
381                                         NULL);
382                 Node *node3 = node_new (TYPE_NODE,
383                                         "name", class->class.otype,
384                                         "pointer", "*",
385                                         NULL);
386
387                 g_string_sprintf (get_cbuf,
388                                   "\t%s%s val; "
389                                   "g_object_get (G_OBJECT (self), \"%s\", "
390                                   "&val, NULL); "
391                                   "return val;\n",
392                                   the_type->name, 
393                                   the_type->pointer ? the_type->pointer : "",
394                                   var_name);
395                 
396                 typestack = g_list_prepend (typestack, node1);
397                 typestack = g_list_prepend (typestack, node3);
398                 
399                 push_funcarg ("self", FALSE);
400                 
401                 push_function (PUBLIC_SCOPE, REGULAR_METHOD, NULL,
402                                get_id, get_cbuf, get_lineno,
403                                lineno, FALSE, NULL);
404         }
405         
406         if (do_set) {
407                 char *set_id = g_strdup_printf ("set_%s", var_name);
408                 GString *set_cbuf = g_string_new (NULL);
409                 Node *node1 = node_new (TYPE_NODE, 
410                                         "name", the_type->name,
411                                         "pointer", the_type->pointer,
412                                         "postfix", the_type->postfix,
413                                         NULL);
414                 Node *node2 = node_new (TYPE_NODE, 
415                                         "name", "void",
416                                         NULL);
417                 Node *node3 = node_new (TYPE_NODE, 
418                                         "name", class->class.otype,
419                                         "pointer", "*",
420                                         NULL);
421
422                 g_string_sprintf (set_cbuf,
423                                   "\tg_object_set (G_OBJECT (self), "
424                                   "\"%s\", val, NULL);\n",
425                                   var_name);
426
427                 typestack = g_list_prepend (typestack, node2);
428                 typestack = g_list_prepend (typestack, node1);
429                 typestack = g_list_prepend (typestack, node3);
430                 
431                 push_funcarg ("self", FALSE);
432                 push_funcarg ("val", FALSE);
433         
434                 typestack = g_list_prepend (typestack, node2);
435                 push_function (PUBLIC_SCOPE, REGULAR_METHOD, NULL,
436                                set_id, set_cbuf, set_lineno,
437                                lineno, FALSE, NULL);
438         }
439
440         node_free ((Node *)the_type);
441 }
442
443 static char *
444 get_prop_enum_flag_cast (Property *prop)
445 {
446         char *tmp, *ret;
447         if (prop->extra_gtktype == NULL ||
448         /* HACK!  just in case someone made this
449          * work with 2.0.0 by using the TYPE
450          * macro directly */
451             ((strstr (prop->extra_gtktype, "_TYPE_") != NULL ||
452               strstr (prop->extra_gtktype, "TYPE_") == prop->extra_gtktype) &&
453              strchr (prop->extra_gtktype, ':') == NULL)) {
454                 if (prop->ptype != NULL)
455                         return get_type (prop->ptype, TRUE);
456                 else
457                         return g_strdup ("");
458         }
459         tmp = remove_sep (prop->extra_gtktype);
460         ret = g_strdup_printf ("(%s) ", tmp);
461         g_free (tmp);
462         return ret;
463 }
464
465 static void
466 property_link_and_export (Node *node)
467 {
468         Property *prop = (Property *)node;
469
470         if (prop->link) {
471                 const char *root;
472                 char *get = NULL, *set = NULL;
473                 Variable *var;
474
475                 if (prop->set != NULL ||
476                     prop->get != NULL) {        
477                         error_print (GOB_ERROR, prop->line_no,
478                                      _("Property linking requested, but "
479                                        "getters and setters exist"));
480                 }
481
482                 var = find_var_or_die (prop->name, prop->line_no);
483                 if(var->scope == PRIVATE_SCOPE) {
484                         root = "self->_priv";
485                 } else if (var->scope == CLASS_SCOPE) {
486                         root = "SELF_GET_CLASS(self)";
487                         if (no_self_alias)
488                                 error_print (GOB_ERROR, prop->line_no,
489                                              _("Self aliases needed when autolinking to a classwide member"));
490                 } else {
491                         root = "self";
492                 }
493
494                 if (strcmp (prop->gtktype, "STRING") == 0) {
495                         set = g_strdup_printf("{ char *old = %s->%s; "
496                                               "%s->%s = g_value_dup_string (VAL); g_free (old); }",
497                                               root, prop->name,
498                                               root, prop->name);
499                         get = g_strdup_printf("g_value_set_string (VAL, %s->%s);",
500                                               root, prop->name);
501                 } else if (strcmp (prop->gtktype, "OBJECT") == 0) {
502                         char *cast;
503                         if (prop->extra_gtktype != NULL) {
504                                 cast = remove_sep (prop->extra_gtktype);
505                         } else {
506                                 cast = g_strdup ("void");
507                         }
508                         set = g_strdup_printf("{ GObject *___old = (GObject *)%s->%s; "
509                                               "%s->%s = (%s *)g_value_dup_object (VAL); "
510                                               "if (___old != NULL) { "
511                                                 "g_object_unref (G_OBJECT (___old)); "
512                                               "} "
513                                               "}",
514                                               root, prop->name,
515                                               root, prop->name,
516                                               cast);
517                         get = g_strdup_printf ("g_value_set_object (VAL, "
518                                                "(gpointer)%s->%s);",
519                                                root, prop->name);
520                         g_free (cast);
521                 } else if (strcmp (prop->gtktype, "BOXED") == 0) {
522                         char *type = make_me_type (prop->extra_gtktype,
523                                                    "G_TYPE_BOXED");
524                         if (prop->extra_gtktype == NULL) {
525                                 error_print (GOB_ERROR, prop->line_no,
526                                              _("Property linking requested for BOXED, but "
527                                                "boxed_type not set"));
528                         }
529                         set = g_strdup_printf("{ gpointer ___old = (gpointer)%s->%s; "
530                                               "gpointer ___new = (gpointer)g_value_get_boxed (VAL); "
531                                               "if (___new != ___old) { "
532                                                 "if (___old != NULL) g_boxed_free (%s, ___old); "
533                                                 "if (___new != NULL) %s->%s = g_boxed_copy (%s, ___new); "
534                                                 "else %s->%s = NULL;"
535                                               "} "
536                                               "}",
537                                               root, prop->name,
538                                               type,
539                                               root, prop->name,
540                                               type,
541                                               root, prop->name);
542                         get = g_strdup_printf("g_value_set_boxed (VAL, %s->%s);",
543                                               root, prop->name);
544                         g_free (type);
545                 } else {
546                         char *set_func;
547                         char *get_func;
548                         const char *getcast = "";
549                         const char *setcast = "";
550                         char *to_free = NULL;
551                         set_func = g_strdup_printf ("g_value_set_%s", prop->gtktype);
552                         g_strdown (set_func);
553                         get_func = g_strdup_printf ("g_value_get_%s", prop->gtktype);
554                         g_strdown (get_func);
555
556                         if (for_cpp) {
557                                 if (strcmp (prop->gtktype, "FLAGS") == 0) {
558                                         setcast = "(guint) ";
559                                         getcast = to_free =
560                                                 get_prop_enum_flag_cast (prop);
561                                 } else if (strcmp (prop->gtktype, "ENUM") == 0) {
562                                         setcast = "(gint) ";
563                                         getcast = to_free =
564                                                 get_prop_enum_flag_cast (prop);
565                                 }
566                         }
567
568                         set = g_strdup_printf("%s->%s = %s%s (VAL);",
569                                               root, prop->name,
570                                               getcast,
571                                               get_func);
572                         get = g_strdup_printf("%s (VAL, %s%s->%s);",
573                                               set_func,
574                                               setcast,  
575                                               root, prop->name);
576
577                         g_free (get_func);
578                         g_free (set_func);
579                         g_free (to_free);
580                 }
581
582                 node_set (node,
583                           "get:steal", get,
584                           "get_line", prop->line_no,
585                           "set:steal", set,
586                           "set_line", prop->line_no,
587                           NULL);
588         }
589
590         if (prop->export) {
591                 export_accessors (prop->name,
592                                   prop->get != NULL, prop->get_line,
593                                   prop->set != NULL,  prop->set_line,
594                                   prop->ptype,
595                                   prop->gtktype,
596                                   prop->line_no);
597         } 
598 }
599
600
601 static char *
602 debool (char *s)
603 {
604         if (strcmp (s, "BOOL") == 0) {
605                 error_print (GOB_WARN, line_no,
606                             _("BOOL type is deprecated, please use BOOLEAN"));
607                 g_free (s);
608                 return g_strdup ("BOOLEAN");
609         } else {
610                 return s;
611         }
612 }
613
614 static void
615 ensure_property (void)
616 {
617         if (property == NULL)
618                 property = (Property *)node_new (PROPERTY_NODE, NULL);
619 }
620
621 %}
622
623 %union {
624         char *id;
625         GString *cbuf;
626         GList *list;
627         int line;
628         int sigtype;
629 }
630
631 %token CLASS FROM
632 %token CONST VOID STRUCT UNION ENUM THREEDOTS
633 %token SIGNED UNSIGNED LONG SHORT INT FLOAT DOUBLE CHAR
634
635 %token <id> TOKEN NUMBER TYPETOKEN ARRAY_DIM SINGLE_CHAR
636 %token <cbuf> CCODE HTCODE PHCODE HCODE ACODE ATCODE STRING
637 %token <line> PUBLIC PRIVATE PROTECTED CLASSWIDE PROPERTY ARGUMENT
638 %token <line> VIRTUAL SIGNAL OVERRIDE
639 %token <line> NICK BLURB MAXIMUM MINIMUM DEFAULT_VALUE ERROR FLAGS TYPE
640 %token <line> FLAGS_TYPE ENUM_TYPE PARAM_TYPE BOXED_TYPE OBJECT_TYPE
641
642 %%
643
644 prog:           ccodes class ccodes     { ; }
645         |       class ccodes            { ; }
646         |       ccodes class            { ; }
647         |       class                   { ; }
648         ;
649
650 ccode:          CCODE                   {
651                         Node *node = node_new (CCODE_NODE,
652                                                "cctype", C_CCODE,
653                                                "cbuf:steal", ($<cbuf>1)->str,
654                                                "line_no", ccode_line,
655                                                NULL);
656                         nodes = g_list_append(nodes,node);
657                         g_string_free($<cbuf>1,FALSE);
658                                         }
659         |       HCODE                   {
660                         Node *node = node_new (CCODE_NODE,
661                                                "cctype", H_CCODE,
662                                                "cbuf:steal", ($<cbuf>1)->str,
663                                                "line_no", ccode_line,
664                                                NULL);
665                         nodes = g_list_append(nodes,node);
666                         g_string_free($<cbuf>1,FALSE);
667                                         }
668         |       HTCODE                  {
669                         Node *node = node_new (CCODE_NODE,
670                                                "cctype", HT_CCODE,
671                                                "cbuf:steal", ($<cbuf>1)->str,
672                                                "line_no", ccode_line,
673                                                NULL);
674                         nodes = g_list_append(nodes,node);
675                         g_string_free($<cbuf>1,FALSE);
676                                         }
677         |       PHCODE                  {
678                         Node *node = node_new (CCODE_NODE,
679                                                "cctype", PH_CCODE,
680                                                "cbuf:steal", ($<cbuf>1)->str,
681                                                "line_no", ccode_line,
682                                                NULL);
683                         nodes = g_list_append(nodes,node);
684                         g_string_free($<cbuf>1,FALSE);
685                                         }
686         |       ACODE                   {
687                         Node *node = node_new (CCODE_NODE,
688                                                "cctype", A_CCODE,
689                                                "cbuf:steal", ($<cbuf>1)->str,
690                                                "line_no", ccode_line,
691                                                NULL);
692                         nodes = g_list_append(nodes,node);
693                         g_string_free($<cbuf>1,FALSE);
694                                         }
695         |       ATCODE                  {
696                         Node *node = node_new (CCODE_NODE,
697                                                "cctype", AT_CCODE,
698                                                "cbuf:steal", ($<cbuf>1)->str,
699                                                "line_no", ccode_line,
700                                                NULL);
701                         nodes = g_list_append(nodes,node);
702                         g_string_free($<cbuf>1,FALSE);
703                                         }
704         ;
705
706 ccodes:         ccodes ccode            { ; }
707         |       ccodes enumcode         { ; }
708         |       ccodes flagcode         { ; }
709         |       ccodes errorcode        { ; }
710         |       ccode                   { ; }
711         |       enumcode                { ; }
712         |       flagcode                { ; }
713         |       errorcode               { ; }
714         ;
715
716 class:          classdec '{' classcode '}'      {
717                         ((Class *)class)->nodes = class_nodes;
718                         class_nodes = NULL;
719                         nodes = g_list_append(nodes,class);
720                                                 }
721         |       classdec '{' '}'                {
722                         ((Class *)class)->nodes = NULL;
723                         class_nodes = NULL;
724                         nodes = g_list_append(nodes,class);
725                                                 }
726         ;
727
728 classdec:       CLASS TYPETOKEN FROM TYPETOKEN  classflags {
729                         class = node_new (CLASS_NODE,
730                                           "otype:steal", $<id>2,
731                                           "ptype:steal", $<id>4,
732                                           "bonobo_object_class:steal", bonobo_object_class,
733                                           "interfaces:steal", interfaces,
734                                           "chunk_size:steal", chunk_size,
735                                           NULL);
736                         bonobo_object_class = NULL;
737                         chunk_size = NULL;
738                         interfaces = NULL;
739                                                 }
740         ;
741
742 classflags:
743         | '(' TOKEN TOKEN ')' classflags {
744                         if(strcmp($<id>2,"chunks") == 0) {
745                                 g_free (chunk_size);
746                                 chunk_size = g_strdup($<id>3);
747                         } else if(strcmp($<id>2,"BonoboObject") == 0) {
748                                 g_free (bonobo_object_class);
749                                 bonobo_object_class = g_strdup($<id>3);
750                         } else {
751                                 yyerror(_("parse error"));
752                                 YYERROR;
753                         }
754                 }
755         | '(' TOKEN TYPETOKEN ')' classflags {
756                         if (strcmp ($<id>2, "interface") == 0) {
757                                 interfaces = g_list_append (interfaces,
758                                                             g_strdup ($<id>3));
759                         } else {
760                                 yyerror(_("parse error"));
761                                 YYERROR;
762                         }
763                 }
764         | '(' TOKEN NUMBER ')' classflags {
765                         if(strcmp($<id>2,"chunks") == 0) {
766                                 g_free (chunk_size);
767                                 if(atoi($<id>3) != 0)
768                                         chunk_size = g_strdup($<id>3);
769                                 else
770                                         chunk_size = NULL;
771                         } else {
772                                 yyerror(_("parse error"));
773                                 YYERROR;
774                         }
775                 }
776         ;       
777
778 classcode:      classcode thing                 { ; }
779         |       thing                           { ; }
780         ;
781
782 thing:          method                          { ; }
783         |       TOKEN method                    {
784                         if (strcmp ($<id>1, "BonoboObject") != 0) {
785                                 g_free ($<id>1);
786                                 yyerror (_("parse error"));
787                                 YYERROR;
788                         }
789                         g_free ($<id>1);
790                         last_added_method->bonobo_object_func = TRUE;
791                                                 }
792         |       TOKEN TYPETOKEN method                  {
793                         if (strcmp ($<id>1, "interface") != 0) {
794                                 g_free ($<id>1);
795                                 g_free ($<id>2);
796                                 yyerror (_("parse error"));
797                                 YYERROR;
798                         }
799                         g_free ($<id>1);
800                         node_set ((Node *)last_added_method,
801                                   "interface:steal", $<id>2,
802                                   NULL);
803                                                 }
804         |       variable                        { ; }
805         |       argument                        { ; }
806         |       property                        { ; }
807         |       ';'                             { ; }
808         ;
809
810 scope:          PUBLIC                  { the_scope = PUBLIC_SCOPE; }
811         |       PRIVATE                 { the_scope = PRIVATE_SCOPE; }
812         |       PROTECTED               { the_scope = PROTECTED_SCOPE; }
813         |       CLASSWIDE               { the_scope = CLASS_SCOPE; }
814         ;
815
816 destructor:     TOKEN TOKEN     {
817                         if (strcmp ($<id>1, "destroywith") == 0) {
818                                 g_free ($<id>1);
819                                 destructor_unref = FALSE;
820                                 destructor = $<id>2;
821                                 destructor_line = line_no;
822                                 destructor_simple = TRUE;
823                         } else if (strcmp ($<id>1, "unrefwith") == 0) {
824                                 g_free ($<id>1);
825                                 destructor_unref = TRUE;
826                                 destructor = $<id>2;
827                                 destructor_line = line_no;
828                                 destructor_simple = TRUE;
829                         } else {
830                                 g_free ($<id>1);
831                                 g_free ($<id>2);
832                                 yyerror (_("parse error"));
833                                 YYERROR;
834                         }
835                                 }
836         |       TOKEN '{' CCODE         {
837                         if (strcmp ($<id>1, "destroy") == 0) {
838                                 g_free($<id>1);
839                                 destructor_unref = FALSE;
840                                 destructor = ($<cbuf>3)->str;
841                                 g_string_free($<cbuf>3, FALSE);
842                                 destructor_line = ccode_line;
843                                 destructor_simple = FALSE;
844                         } else if (strcmp ($<id>1, "unref") == 0) {
845                                 g_free ($<id>1);
846                                 destructor_unref = TRUE;
847                                 destructor = ($<cbuf>3)->str;
848                                 g_string_free ($<cbuf>3, FALSE);
849                                 destructor_line = ccode_line;
850                                 destructor_simple = FALSE;
851                         } else {
852                                 g_free ($<id>1);
853                                 g_string_free ($<cbuf>3, TRUE);
854                                 yyerror (_("parse error"));
855                                 YYERROR;
856                         }
857                                         }
858         ;
859
860 initializer:    '=' numtok      {
861                         initializer = $<id>2;
862                         initializer_line = ccode_line;
863                                 }
864         |       '=' '{' CCODE   {
865                         initializer = ($<cbuf>3)->str;
866                         initializer_line = ccode_line;
867                         g_string_free($<cbuf>3, FALSE);
868                                 }
869         ;
870
871
872 varoptions:     destructor initializer  { ; }
873         |       initializer destructor  { ; }
874         |       initializer             { destructor = NULL; }
875         |       destructor              { initializer = NULL; }
876         |                               {
877                         destructor = NULL;
878                         initializer = NULL;
879                                         }
880         ;
881
882 variable:       scope type TOKEN varoptions ';'         {
883                         push_variable($<id>3, the_scope,$<line>1, NULL);
884                                                 }
885         |       scope type TOKEN ARRAY_DIM varoptions ';'       {
886                         push_variable($<id>3, the_scope, $<line>1, $<id>4);
887                                                 }
888         ;
889
890 argument:       ARGUMENT flags argtype TOKEN export TOKEN '{' CCODE TOKEN '{' CCODE ';' {
891                         Node *node = NULL;
892                         if(strcmp($<id>6,"get")==0 &&
893                            strcmp($<id>9,"set")==0) {
894                                 Type *type = pop_type();
895                                 g_free ($<id>6); 
896                                 g_free ($<id>9);
897                                 node = node_new (ARGUMENT_NODE,
898                                                  "gtktype:steal", $<id>3,
899                                                  "atype:steal", type,
900                                                  "flags:steal", $<list>2,
901                                                  "name:steal", $<id>4,
902                                                  "get:steal", ($<cbuf>8)->str,
903                                                  "get_line", $<line>7,
904                                                  "set:steal", ($<cbuf>11)->str,
905                                                  "set_line", $<line>10,
906                                                  "line_no", $<line>1,
907                                                  NULL);
908
909                                 class_nodes = g_list_append(class_nodes,node);
910
911                                 g_string_free ($<cbuf>8, FALSE);
912                                 g_string_free ($<cbuf>11, FALSE);
913
914                         } else if(strcmp($<id>6,"set")==0 &&
915                                 strcmp($<id>9,"get")==0) {
916                                 Type *type = pop_type();
917                                 g_free ($<id>6); 
918                                 g_free ($<id>9);
919                                 node = node_new (ARGUMENT_NODE,
920                                                  "gtktype:steal", $<id>3,
921                                                  "atype:steal", type,
922                                                  "flags:steal", $<list>2,
923                                                  "name:steal", $<id>4,
924                                                  "get:steal", ($<cbuf>11)->str,
925                                                  "get_line", $<line>10,
926                                                  "set:steal", ($<cbuf>8)->str,
927                                                  "set_line", $<line>7,
928                                                  "line_no", $<line>1,
929                                                  NULL);
930                                 g_string_free ($<cbuf>11, FALSE);
931                                 g_string_free ($<cbuf>8, FALSE);
932                                 class_nodes = g_list_append(class_nodes,node);
933                         } else {
934                                 g_free ($<id>3); 
935                                 g_free ($<id>4);
936                                 g_free ($<id>6); 
937                                 g_free ($<id>9);
938                                 g_list_foreach ($<list>2, (GFunc)g_free, NULL);
939                                 g_list_free ($<list>2);
940                                 g_string_free ($<cbuf>11, TRUE);
941                                 g_string_free ($<cbuf>8, TRUE);
942                                 yyerror (_("parse error"));
943                                 YYERROR;
944                         }
945
946                         if ($<id>5 != NULL) {
947                                 Argument *arg = (Argument *)node;
948                                 export_accessors (arg->name,
949                                                   arg->get != NULL, arg->get_line,
950                                                   arg->set != NULL, arg->set_line,
951                                                   arg->atype,
952                                                   arg->gtktype,
953                                                   arg->line_no);
954                                 g_free ($<id>5);
955                         } 
956
957                                                 }
958         |       ARGUMENT flags argtype TOKEN export TOKEN '{' CCODE ';' {
959                         Node *node = NULL;
960                         if(strcmp($<id>6, "get") == 0) {
961                                 Type *type = pop_type();
962                                 g_free ($<id>6);
963                                 node = node_new (ARGUMENT_NODE,
964                                                  "gtktype:steal", $<id>3,
965                                                  "atype:steal", type,
966                                                  "flags:steal", $<list>2,
967                                                  "name:steal", $<id>4,
968                                                  "get:steal", ($<cbuf>8)->str,
969                                                  "get_line", $<line>7,
970                                                  "line_no", $<line>1,
971                                                  NULL);
972
973                                 g_string_free ($<cbuf>8, FALSE);
974                                 class_nodes = g_list_append(class_nodes, node);
975                         } else if(strcmp($<id>6, "set") == 0) {
976                                 Type *type = pop_type();
977                                 g_free ($<id>6);
978                                 node = node_new (ARGUMENT_NODE,
979                                                  "gtktype:steal", $<id>3,
980                                                  "atype:steal", type,
981                                                  "flags:steal", $<list>2,
982                                                  "name:steal", $<id>4,
983                                                  "set:steal", ($<cbuf>8)->str,
984                                                  "set_line", $<line>7,
985                                                  "line_no", $<line>1,
986                                                  NULL);
987
988                                 g_string_free ($<cbuf>8, FALSE);
989                                 class_nodes = g_list_append (class_nodes, node);
990                         } else {
991                                 g_free ($<id>6); 
992                                 g_free ($<id>3);
993                                 g_free ($<id>4);
994                                 g_list_foreach ($<list>2, (GFunc)g_free, NULL);
995                                 g_list_free ($<list>2);
996                                 g_string_free ($<cbuf>8, TRUE);
997                                 yyerror(_("parse error"));
998                                 YYERROR;
999                         }
1000
1001                         if ($<id>5 != NULL) {
1002                                 Argument *arg = (Argument *)node;
1003                                 export_accessors (arg->name,
1004                                                   arg->get != NULL, arg->get_line,
1005                                                   arg->set != NULL, arg->set_line,
1006                                                   arg->atype,
1007                                                   arg->gtktype,
1008                                                   arg->line_no);
1009                                 g_free ($<id>5);
1010                         } 
1011                                                 }
1012         |       ARGUMENT flags argtype TOKEN export TOKEN {
1013                         Node *node;
1014                         char *get, *set = NULL;
1015                         Variable *var;
1016                         Type *type;
1017                         const char *root;
1018                         
1019                         if(strcmp($<id>6, "link")!=0 &&
1020                            strcmp($<id>6, "stringlink")!=0 && 
1021                            strcmp($<id>6, "objectlink")!=0) {
1022                                 g_free($<id>6); 
1023                                 g_free($<id>3);
1024                                 g_free($<id>4);
1025                                 g_list_foreach($<list>2,(GFunc)g_free,NULL);
1026                                 g_list_free($<list>2);
1027                                 yyerror(_("parse error"));
1028                                 YYERROR;
1029                         }
1030
1031                         type = pop_type();
1032
1033                         var = find_var_or_die($<id>4, $<line>1);
1034                         if(var->scope == PRIVATE_SCOPE) {
1035                                 root = "self->_priv";
1036                         } else if(var->scope == CLASS_SCOPE) {
1037                                 root = "SELF_GET_CLASS(self)";
1038                                 if(no_self_alias)
1039                                         error_print(GOB_ERROR, $<line>1,
1040                                                     _("Self aliases needed when autolinking to a classwide member"));
1041                         } else {
1042                                 root = "self";
1043                         }
1044
1045                         if(strcmp($<id>6, "link")==0) {
1046                                 set = g_strdup_printf("%s->%s = ARG;",
1047                                                       root, $<id>4);
1048                         } else if(strcmp($<id>6, "stringlink")==0) {
1049                                 set = g_strdup_printf("g_free (%s->%s); "
1050                                                       "%s->%s = g_strdup (ARG);",
1051                                                       root, $<id>4,
1052                                                       root, $<id>4);
1053                         } else if(strcmp($<id>6, "objectlink")==0) {
1054                                 set = g_strdup_printf(
1055                                   "if (ARG != NULL) "
1056                                    "g_object_ref (G_OBJECT (ARG)); "
1057                                   "if (%s->%s != NULL) "
1058                                    "g_object_unref (G_OBJECT (%s->%s)); "
1059                                   "%s->%s = ARG;",
1060                                   root, $<id>4,
1061                                   root, $<id>4,
1062                                   root, $<id>4);
1063                         } else {
1064                                 g_assert_not_reached();
1065                         }
1066
1067                         get = g_strdup_printf("ARG = %s->%s;", root, $<id>4);
1068   
1069                         g_free ($<id>6);
1070
1071                         if (type == NULL)
1072                                 type = (Type *)node_copy ((Node *)var->vtype);
1073
1074                         node = node_new (ARGUMENT_NODE,
1075                                          "gtktype:steal", $<id>3,
1076                                          "atype:steal", type,
1077                                          "flags:steal", $<list>2,
1078                                          "name:steal", $<id>4,
1079                                          "get:steal", get,
1080                                          "get_line", $<line>1,
1081                                          "set:steal", set,
1082                                          "set_line", $<line>1,
1083                                          "line_no", $<line>1,
1084                                          NULL);
1085
1086                         if ($<id>5 != NULL) {
1087                                 Argument *arg = (Argument *)node;
1088                                 export_accessors (arg->name,
1089                                                   arg->get != NULL, arg->get_line,
1090                                                   arg->set != NULL, arg->set_line,
1091                                                   arg->atype,
1092                                                   arg->gtktype,
1093                                                   arg->line_no);
1094                                 g_free ($<id>5);
1095                         } 
1096
1097                         class_nodes = g_list_append (class_nodes, node);
1098                                                 }
1099         ;
1100
1101 export:         '(' TOKEN ')'                   {
1102                         if (strcmp ($<id>2, "export")!=0) {
1103                                 g_free ($<id>2); 
1104                                 yyerror (_("parse error"));
1105                                 YYERROR;
1106                         }
1107                         $<id>$ = $<id>2;
1108                                                 }
1109          |                                      {
1110                         $<id>$ = NULL;
1111                                                 }
1112          ;
1113
1114 property:       PROPERTY TOKEN TOKEN param_spec TOKEN '{' CCODE TOKEN '{' CCODE ';' {
1115                         ensure_property ();
1116                         node_set ((Node *)property,
1117                                   "line_no", $<line>1,
1118                                   "gtktype:steal", debool ($<id>2),
1119                                   "name:steal", $<id>3,
1120                                   NULL);
1121                         if (strcmp ($<id>5, "get") == 0 &&
1122                             strcmp ($<id>8, "set") == 0) {
1123                                 node_set ((Node *)property,
1124                                           "get:steal", ($<cbuf>7)->str,
1125                                           "get_line", $<line>6,
1126                                           "set:steal", ($<cbuf>10)->str,
1127                                           "set_line", $<line>9,
1128                                           NULL);
1129                                 g_string_free ($<cbuf>7, FALSE);
1130                                 g_string_free ($<cbuf>10, FALSE);
1131                                 g_free ($<id>5); 
1132                                 g_free ($<id>8);
1133                         } else if (strcmp ($<id>5, "set") == 0 &&
1134                                    strcmp ($<id>8, "get") == 0) {
1135                                 node_set ((Node *)property,
1136                                           "get:steal", ($<cbuf>10)->str,
1137                                           "get_line", $<line>9,
1138                                           "set:steal", ($<cbuf>7)->str,
1139                                           "set_line", $<line>6,
1140                                           NULL);
1141                                 g_string_free ($<cbuf>7, FALSE);
1142                                 g_string_free ($<cbuf>10, FALSE);
1143                                 g_free ($<id>5); 
1144                                 g_free ($<id>8);
1145                         } else {
1146                                 g_string_free ($<cbuf>7, TRUE);
1147                                 g_string_free ($<cbuf>10, TRUE);
1148                                 g_free ($<id>5); 
1149                                 g_free ($<id>8);
1150                                 node_free ((Node *)property);
1151                                 property = NULL;
1152                                 yyerror (_("parse error"));
1153                                 YYERROR;
1154                         }
1155                         property_link_and_export ((Node *)property);
1156                         if (property != NULL) {
1157                                 class_nodes = g_list_append (class_nodes,
1158                                                              property);
1159                                 property = NULL;
1160                         }
1161                 }
1162         |       PROPERTY TOKEN TOKEN param_spec TOKEN '{' CCODE ';' {
1163                         ensure_property ();
1164                         node_set ((Node *)property,
1165                                   "line_no", $<line>1,
1166                                   "gtktype:steal", debool ($<id>2),
1167                                   "name:steal", $<id>3,
1168                                   NULL);
1169                         if (strcmp ($<id>5, "get") == 0) {
1170                                 node_set ((Node *)property,
1171                                           "get:steal", ($<cbuf>7)->str,
1172                                           "get_line", $<line>6,
1173                                           NULL);
1174                                 g_string_free ($<cbuf>7, FALSE);
1175                                 g_free ($<id>5); 
1176                         } else if (strcmp ($<id>5, "set") == 0) {
1177                                 node_set ((Node *)property,
1178                                           "set:steal", ($<cbuf>7)->str,
1179                                           "set_line", $<line>6,
1180                                           NULL);
1181                                 g_string_free ($<cbuf>7, FALSE);
1182                                 g_free ($<id>5); 
1183                         } else {
1184                                 g_string_free ($<cbuf>7, TRUE);
1185                                 g_free ($<id>5); 
1186                                 node_free ((Node *)property);
1187                                 property = NULL;
1188                                 yyerror (_("parse error"));
1189                                 YYERROR;
1190                         }
1191                         property_link_and_export ((Node *)property);
1192                         if (property != NULL) {
1193                                 class_nodes = g_list_append (class_nodes,
1194                                                              property);
1195                                 property = NULL;
1196                         }
1197                 }
1198         |       PROPERTY TOKEN TOKEN param_spec ';' {
1199                         ensure_property ();
1200                         node_set ((Node *)property,
1201                                   "line_no", $<line>1,
1202                                   "gtktype:steal", debool ($<id>2),
1203                                   "name:steal", $<id>3,
1204                                   NULL);
1205                         property_link_and_export ((Node *)property);
1206                         if (property != NULL) {
1207                                 class_nodes = g_list_append (class_nodes,
1208                                                              property);
1209                                 property = NULL;
1210                         }
1211                 }
1212         ;
1213
1214 param_spec:     '(' param_spec_list ')' { ; }
1215         |                               { ; }
1216         ;
1217
1218 param_spec_list:        param_spec_list ',' param_spec_value    { ; }
1219         |               param_spec_value                        { ; }
1220         ;
1221
1222 string:         STRING                          { $<id>$ = $<id>1; }
1223         |       TOKEN '(' STRING ')'            {
1224                         if (strcmp ($<id>1, "_") != 0) {
1225                                 g_free ($<id>1);
1226                                 yyerror(_("parse error"));
1227                                 YYERROR;
1228                         }
1229                         g_free ($<id>1);
1230                         $<id>$ = g_strconcat ("_(", $<id>3, ")", NULL);
1231                         g_free ($<id>3);
1232                 }
1233         ;
1234
1235 anyval:         numtok          { $<id>$ = $<id>1; }
1236         |       string          { $<id>$ = $<id>1; }
1237         ;
1238
1239 param_spec_value: NICK '=' string               {
1240                 ensure_property ();
1241                 node_set ((Node *)property,
1242                           "nick:steal", $<id>3,
1243                           NULL);
1244                   }
1245         |       BLURB '=' string                {
1246                 ensure_property ();
1247                 node_set ((Node *)property,
1248                           "blurb:steal", $<id>3,
1249                           NULL);
1250                   }
1251         |       MAXIMUM '=' numtok              {
1252                 ensure_property ();
1253                 node_set ((Node *)property,
1254                           "maximum:steal", $<id>3,
1255                           NULL);
1256                   }
1257         |       MINIMUM '=' numtok              {
1258                 ensure_property ();
1259                 node_set ((Node *)property,
1260                           "minimum:steal", $<id>3,
1261                           NULL);
1262                   }
1263         |       DEFAULT_VALUE '=' anyval        {
1264                 ensure_property ();
1265                 node_set ((Node *)property,
1266                           "default_value:steal", $<id>3,
1267                           NULL);
1268                   }
1269         |       FLAGS '=' flaglist              {
1270                 ensure_property ();
1271                 node_set ((Node *)property,
1272                           "flags:steal", $<list>3,
1273                           NULL);
1274                   }
1275         |       TYPE '=' type                   {
1276                 Type *type = pop_type ();
1277                 ensure_property ();
1278                 node_set ((Node *)property,
1279                           "ptype:steal", type,
1280                           NULL);
1281                   }
1282         |       FLAGS_TYPE '=' TYPETOKEN        {
1283                 ensure_property ();
1284                 node_set ((Node *)property,
1285                           "extra_gtktype:steal", $<id>3,
1286                           NULL);
1287                   }
1288         |       FLAGS_TYPE '=' TOKEN            {
1289                 ensure_property ();
1290                 node_set ((Node *)property,
1291                           "extra_gtktype:steal", $<id>3,
1292                           NULL);
1293                   }
1294         |       ENUM_TYPE '=' TYPETOKEN         {
1295                 ensure_property ();
1296                 node_set ((Node *)property,
1297                           "extra_gtktype:steal", $<id>3,
1298                           NULL);
1299                   }
1300         |       ENUM_TYPE '=' TOKEN             {
1301                 ensure_property ();
1302                 node_set ((Node *)property,
1303                           "extra_gtktype:steal", $<id>3,
1304                           NULL);
1305                   }
1306         |       PARAM_TYPE '=' TYPETOKEN        {
1307                 ensure_property ();
1308                 node_set ((Node *)property,
1309                           "extra_gtktype:steal", $<id>3,
1310                           NULL);
1311                   }
1312         |       PARAM_TYPE '=' TOKEN            {
1313                 ensure_property ();
1314                 node_set ((Node *)property,
1315                           "extra_gtktype:steal", $<id>3,
1316                           NULL);
1317                   }
1318         |       BOXED_TYPE '=' TYPETOKEN        {
1319                 ensure_property ();
1320                 node_set ((Node *)property,
1321                           "extra_gtktype:steal", $<id>3,
1322                           NULL);
1323                   }
1324         |       BOXED_TYPE '=' TOKEN            {
1325                 ensure_property ();
1326                 node_set ((Node *)property,
1327                           "extra_gtktype:steal", $<id>3,
1328                           NULL);
1329                   }
1330         |       OBJECT_TYPE '=' TYPETOKEN       {
1331                 ensure_property ();
1332                 node_set ((Node *)property,
1333                           "extra_gtktype:steal", $<id>3,
1334                           NULL);
1335                   }
1336         |       OBJECT_TYPE '=' TOKEN           {
1337                 ensure_property ();
1338                 node_set ((Node *)property,
1339                           "extra_gtktype:steal", $<id>3,
1340                           NULL);
1341                   }
1342         |       TOKEN           {
1343                 ensure_property ();
1344                 if (strcmp ($<id>1, "link") == 0) {
1345                         g_free($<id>1);
1346                         node_set ((Node *)property,
1347                                   "link", TRUE,
1348                                   NULL);
1349                 } else if (strcmp ($<id>1, "export") == 0) {
1350                         g_free($<id>1);
1351                         node_set ((Node *)property,
1352                                   "export", TRUE,
1353                                   NULL);
1354                 } else {
1355                         g_free($<id>1);
1356                         yyerror(_("parse error"));
1357                         YYERROR;
1358                 }
1359                   }
1360         ;
1361
1362 argtype:        TOKEN '(' TOKEN type ')'        {
1363                         if(strcmp($<id>3,"type")!=0) {
1364                                 g_free($<id>1);
1365                                 g_free($<id>3);
1366                                 yyerror(_("parse error"));
1367                                 YYERROR;
1368                         }
1369                         $<id>$ = debool ($<id>1);
1370                                                 }
1371         |       TOKEN                           {
1372                         $<id>$ = debool ($<id>1);
1373                         typestack = g_list_prepend(typestack,NULL);
1374                                                 }
1375         ;
1376         
1377 flags:          '(' flaglist ')'                { $<list>$ = $<list>2; }
1378         |                                       { $<list>$ = NULL; }
1379         ;
1380
1381 flaglist:       TOKEN '|' flaglist              {
1382                         $<list>$ = g_list_append($<list>3,$<id>1);
1383                                                 }
1384         |       TOKEN                           {
1385                         $<list>$ = g_list_append(NULL,$<id>1);
1386                                                 }
1387         ;
1388
1389
1390 type:           specifier_list pointer                          {
1391                         Node *node = node_new (TYPE_NODE, 
1392                                                "name:steal", $<id>1,
1393                                                "pointer:steal", $<id>2,
1394                                                NULL);
1395                         typestack = g_list_prepend(typestack,node);
1396                                                         }
1397         |       specifier_list                          {
1398                         Node *node = node_new (TYPE_NODE, 
1399                                                "name:steal", $<id>1,
1400                                                NULL);
1401                         typestack = g_list_prepend(typestack,node);
1402                                                         }
1403         ;
1404
1405 /* The special cases are neccessary to avoid conflicts */
1406 specifier_list: spec_list                               {
1407                         $<id>$ = $<id>1;
1408                                                         }
1409         |       TOKEN                                   {
1410                         $<id>$ = $<id>1;
1411                                                         }
1412         |       CONST TOKEN                             {
1413                         $<id>$ = g_strconcat("const ", $<id>2, NULL);
1414                         g_free($<id>2);
1415                                                         }
1416         |       TOKEN CONST                             {
1417                         $<id>$ = g_strconcat($<id>1, " const", NULL);
1418                         g_free($<id>1);
1419                                                         }
1420         |       strunionenum TOKEN                      {
1421                         $<id>$ = g_strconcat($<id>1, " ", $<id>2, NULL);
1422                         g_free($<id>2);
1423                                                         }
1424         |       CONST strunionenum TOKEN                {
1425                         $<id>$ = g_strconcat("const ", $<id>2, " ",
1426                                              $<id>3, NULL);
1427                         g_free($<id>3);
1428                                                         }
1429         |       strunionenum TOKEN CONST                {
1430                         $<id>$ = g_strconcat($<id>1, " ",
1431                                              $<id>2, " const", NULL);
1432                         g_free($<id>2);
1433                                                         }
1434         ;
1435
1436 /* The special const cases take care of conflicts ! */
1437 spec_list:      specifier spec_list                     {
1438                         $<id>$ = g_strconcat($<id>1, " ", $<id>2, NULL);
1439                         g_free($<id>2);
1440                                                         }
1441         |       TYPETOKEN spec_list                     {
1442                         $<id>$ = g_strconcat($<id>1, " ", $<id>2, NULL);
1443                         g_free($<id>1);
1444                         g_free($<id>2);
1445                                                         }
1446         |       CONST spec_list                         {
1447                         $<id>$ = g_strconcat("const ", $<id>2, NULL);
1448                         g_free($<id>2);
1449                                                         }
1450         |       TYPETOKEN                               {
1451                         $<id>$ = $<id>1;
1452                                                         }
1453         |       TYPETOKEN CONST                         {
1454                         $<id>$ = g_strconcat($<id>1, " const", NULL);
1455                         g_free($<id>1);
1456                                                         }
1457         |       specifier                               {
1458                         $<id>$ = g_strdup($<id>1);
1459                                                         }
1460         |       specifier CONST                         {
1461                         $<id>$ = g_strconcat($<id>1, " const", NULL);
1462                                                         }
1463         ;
1464
1465 specifier:      VOID                    { $<id>$ = "void"; }
1466         |       CHAR                    { $<id>$ = "char"; }
1467         |       SHORT                   { $<id>$ = "short"; }
1468         |       INT                     { $<id>$ = "int"; }
1469         |       LONG                    { $<id>$ = "long"; }
1470         |       FLOAT                   { $<id>$ = "float"; }
1471         |       DOUBLE                  { $<id>$ = "double"; }
1472         |       SIGNED                  { $<id>$ = "signed"; }
1473         |       UNSIGNED                { $<id>$ = "unsigned"; }
1474         ;
1475
1476 strunionenum:   STRUCT                  { $<id>$ = "struct"; }
1477         |       UNION                   { $<id>$ = "union"; }
1478         |       ENUM                    { $<id>$ = "enum"; }
1479         ;
1480
1481 pointer:        '*'                     { $<id>$ = g_strdup("*"); }
1482         |       '*' CONST               { $<id>$ = g_strdup("* const"); }
1483         |       '*' pointer             {
1484                                 $<id>$ = g_strconcat("*", $<id>2, NULL);
1485                                 g_free($<id>2);
1486                                         }
1487         |       '*' CONST pointer       {
1488                                 $<id>$ = g_strconcat("* const", $<id>3, NULL);
1489                                 g_free($<id>3);
1490                                         }
1491         ;
1492
1493 /* this never sets the_scope */
1494 simplesigtype:  TOKEN sigtype   {
1495                         if(strcmp($<id>1, "first")==0)
1496                                 $<sigtype>$ = SIGNAL_FIRST_METHOD;
1497                         else if(strcmp($<id>1, "last")==0)
1498                                 $<sigtype>$ = SIGNAL_LAST_METHOD;
1499                         else {
1500                                 yyerror(_("signal must be 'first' or 'last'"));
1501                                 g_free($<id>1);
1502                                 YYERROR;
1503                         }
1504                         g_free($<id>1);
1505                                         }
1506         |       sigtype                 {
1507                         $<sigtype>$ = SIGNAL_LAST_METHOD;
1508                                         }
1509         ;
1510
1511 /* this always sets the_scope */
1512 fullsigtype:    scope TOKEN sigtype     {
1513                         if(strcmp($<id>2,"first")==0)
1514                                 $<sigtype>$ = SIGNAL_FIRST_METHOD;
1515                         else if(strcmp($<id>2,"last")==0)
1516                                 $<sigtype>$ = SIGNAL_LAST_METHOD;
1517                         else {
1518                                 yyerror(_("signal must be 'first' or 'last'"));
1519                                 g_free($<id>2);
1520                                 YYERROR;
1521                         }
1522                         g_free($<id>2);
1523                                         }
1524         |       TOKEN scope sigtype     {
1525                         if(strcmp($<id>1,"first")==0)
1526                                 $<sigtype>$ = SIGNAL_FIRST_METHOD;
1527                         else if(strcmp($<id>1,"last")==0)
1528                                 $<sigtype>$ = SIGNAL_LAST_METHOD;
1529                         else {
1530                                 yyerror(_("signal must be 'first' or 'last'"));
1531                                 g_free($<id>1);
1532                                 YYERROR;
1533                         }
1534                         g_free($<id>1);
1535                                         }
1536         |       scope sigtype           {
1537                         $<sigtype>$ = SIGNAL_LAST_METHOD;
1538                                         }
1539         |       simplesigtype           {
1540                         /* the_scope was default thus public */
1541                         the_scope = PUBLIC_SCOPE;
1542                                         }
1543         ;
1544         
1545 sigtype:        TOKEN '(' tokenlist ')'         {
1546                         gtktypes = g_list_prepend(gtktypes, debool ($<id>1));
1547                                                 }
1548         ;
1549
1550 tokenlist:      tokenlist ',' TOKEN             {
1551                         gtktypes = g_list_append(gtktypes, debool ($<id>3));
1552                                                 }
1553         |       TOKEN                           { 
1554                         gtktypes = g_list_append(gtktypes, debool ($<id>1));
1555                                                 }
1556         ;
1557
1558 codenocode:     '{' CCODE                       { $<cbuf>$ = $<cbuf>2; }
1559         |       ';'                             { $<cbuf>$ = NULL; }
1560         ;
1561
1562 /*here CCODE will include the ending '}' */
1563 method:         SIGNAL flags fullsigtype type TOKEN '(' funcargs ')' returnvals codenocode {
1564                         if(!has_self) {
1565                                 yyerror(_("signal without 'self' as "
1566                                           "first parameter"));
1567                                 free_all_global_state();
1568                                 YYERROR;
1569                         }
1570                         if(the_scope == CLASS_SCOPE) {
1571                                 yyerror(_("a method cannot be of class scope"));
1572                                 free_all_global_state();
1573                                 YYERROR;
1574                         }
1575                         push_function(the_scope, $<sigtype>3,NULL,
1576                                       $<id>5, $<cbuf>10,$<line>1,
1577                                       ccode_line, vararg, $<list>2);
1578                                                                         }
1579         |       scope SIGNAL flags simplesigtype type TOKEN '(' funcargs ')' returnvals codenocode {
1580                         if(!has_self) {
1581                                 yyerror(_("signal without 'self' as "
1582                                           "first parameter"));
1583                                 free_all_global_state();
1584                                 YYERROR;
1585                         }
1586                         if(the_scope == CLASS_SCOPE) {
1587                                 yyerror(_("a method cannot be of class scope"));
1588                                 free_all_global_state();
1589                                 YYERROR;
1590                         }
1591                         push_function(the_scope, $<sigtype>4, NULL,
1592                                       $<id>6, $<cbuf>11, $<line>2,
1593                                       ccode_line, vararg, $<list>3);
1594                                                                         }
1595         |       VIRTUAL scope type TOKEN '(' funcargs ')' returnvals codenocode {
1596                         if(!has_self) {
1597                                 yyerror(_("virtual method without 'self' as "
1598                                           "first parameter"));
1599                                 free_all_global_state();
1600                                 YYERROR;
1601                         }
1602                         if(the_scope == CLASS_SCOPE) {
1603                                 yyerror(_("a method cannot be of class scope"));
1604                                 free_all_global_state();
1605                                 YYERROR;
1606                         }
1607                         push_function(the_scope, VIRTUAL_METHOD, NULL, $<id>4,
1608                                       $<cbuf>9, $<line>1,
1609                                       ccode_line, vararg, NULL);
1610                                                                         }
1611         |       scope VIRTUAL type TOKEN '(' funcargs ')' returnvals codenocode {
1612                         if(!has_self) {
1613                                 yyerror(_("virtual method without 'self' as "
1614                                           "first parameter"));
1615                                 free_all_global_state();
1616                                 YYERROR;
1617                         }
1618                         if(the_scope == CLASS_SCOPE) {
1619                                 yyerror(_("a method cannot be of class scope"));
1620                                 free_all_global_state();
1621                                 YYERROR;
1622                         }
1623                         push_function(the_scope, VIRTUAL_METHOD, NULL, $<id>4,
1624                                       $<cbuf>9, $<line>2,
1625                                       ccode_line, vararg, NULL);
1626                                                                         }
1627         |       VIRTUAL type TOKEN '(' funcargs ')' returnvals codenocode       {
1628                         if(!has_self) {
1629                                 yyerror(_("virtual method without 'self' as "
1630                                           "first parameter"));
1631                                 free_all_global_state();
1632                                 YYERROR;
1633                         }
1634                         push_function(PUBLIC_SCOPE, VIRTUAL_METHOD, NULL,
1635                                       $<id>3, $<cbuf>8, $<line>1,
1636                                       ccode_line, vararg, NULL);
1637                                                                         }
1638         |       OVERRIDE '(' TYPETOKEN ')' type TOKEN '(' funcargs ')' returnvals codenocode    {
1639                         push_function(NO_SCOPE, OVERRIDE_METHOD, $<id>3,
1640                                       $<id>6, $<cbuf>11,
1641                                       $<line>1, ccode_line,
1642                                       vararg, NULL);
1643                                                                         }
1644         |       scope type TOKEN '(' funcargs ')' returnvals codenocode {
1645                         if(the_scope == CLASS_SCOPE) {
1646                                 yyerror(_("a method cannot be of class scope"));
1647                                 free_all_global_state();
1648                                 YYERROR;
1649                         }
1650                         push_function(the_scope, REGULAR_METHOD, NULL, $<id>3,
1651                                       $<cbuf>8, $<line>1, ccode_line,
1652                                       vararg, NULL);
1653                                                                 }
1654         |       TOKEN '(' TOKEN ')' codenocode  {
1655                         if(strcmp($<id>1, "init")==0) {
1656                                 push_init_arg($<id>3,FALSE);
1657                                 push_function(NO_SCOPE, INIT_METHOD, NULL,
1658                                               $<id>1, $<cbuf>5, $<line>2,
1659                                               ccode_line, FALSE, NULL);
1660                         } else if(strcmp($<id>1, "class_init")==0) {
1661                                 push_init_arg($<id>3,TRUE);
1662                                 push_function(NO_SCOPE, CLASS_INIT_METHOD, NULL,
1663                                               $<id>1, $<cbuf>5, $<line>2,
1664                                               ccode_line, FALSE, NULL);
1665                         } else {
1666                                 g_free($<id>1);
1667                                 g_free($<id>3);
1668                                 g_string_free($<cbuf>5,TRUE);
1669                                 yyerror(_("parse error "
1670                                           "(untyped blocks must be init or "
1671                                           "class_init)"));
1672                                 YYERROR;
1673                         }
1674                                                 }
1675         ;
1676
1677 returnvals:     TOKEN retcode           {
1678                         g_free(onerror); onerror = NULL;
1679                         g_free(defreturn); defreturn = NULL;
1680                         if(!set_return_value($<id>1, $<id>2)) {
1681                                 g_free($<id>1);
1682                                 g_free($<id>2);
1683                                 yyerror(_("parse error"));
1684                                 YYERROR;
1685                         }
1686                         g_free($<id>1);
1687                                         }
1688         |       TOKEN retcode TOKEN retcode     {
1689                         g_free(onerror); onerror = NULL;
1690                         g_free(defreturn); defreturn = NULL;
1691                         if(!set_return_value($<id>1, $<id>2)) {
1692                                 g_free($<id>1); g_free($<id>2);
1693                                 g_free($<id>3); g_free($<id>4);
1694                                 yyerror(_("parse error"));
1695                                 YYERROR;
1696                         }
1697                         if(!set_return_value($<id>3, $<id>4)) {
1698                                 onerror = defreturn = NULL;
1699                                 g_free($<id>1); g_free($<id>2);
1700                                 g_free($<id>3); g_free($<id>4);
1701                                 yyerror(_("parse error"));
1702                                 YYERROR;
1703                         }
1704                         g_free($<id>1);
1705                         g_free($<id>3);
1706                                                 }
1707         |                               {
1708                         g_free(onerror); onerror = NULL;
1709                         g_free(defreturn); defreturn = NULL;
1710                                         }
1711         ;
1712
1713 retcode:        numtok                  { $<id>$ = $<id>1; }
1714         |       '{' CCODE               {
1715                         $<id>$ = ($<cbuf>2)->str;
1716                         g_string_free($<cbuf>2, FALSE);
1717                                         }
1718         ;
1719         
1720 funcargs:       VOID                    { vararg = FALSE; has_self = FALSE; }
1721         |       TOKEN                   {
1722                         vararg = FALSE;
1723                         has_self = TRUE;
1724                         if(strcmp($<id>1,"self")==0)
1725                                 push_self($<id>1, FALSE);
1726                         else {
1727                                 g_free($<id>1);
1728                                 yyerror(_("parse error"));
1729                                 YYERROR;
1730                         }
1731                                         }
1732         |       TOKEN CONST {
1733                         vararg = FALSE;
1734                         has_self = TRUE;
1735                         if(strcmp($<id>1,"self")==0)
1736                                 push_self($<id>1, TRUE);
1737                         else {
1738                                 g_free($<id>1);
1739                                 yyerror(_("parse error"));
1740                                 YYERROR;
1741                         }
1742                                         }
1743         |       CONST TOKEN {
1744                         vararg = FALSE;
1745                         has_self = TRUE;
1746                         if(strcmp($<id>2,"self")==0)
1747                                 push_self($<id>2, TRUE);
1748                         else {
1749                                 g_free($<id>2);
1750                                 yyerror(_("parse error"));
1751                                 YYERROR;
1752                         }
1753                                         }
1754         |       TOKEN ',' arglist       {
1755                         has_self = TRUE;
1756                         if(strcmp($<id>1,"self")==0)
1757                                 push_self($<id>1, FALSE);
1758                         else {
1759                                 g_free($<id>1);
1760                                 yyerror(_("parse error"));
1761                                 YYERROR;
1762                         }
1763                                         }
1764         |       TOKEN CONST ',' arglist {
1765                         has_self = TRUE;
1766                         if(strcmp($<id>1,"self")==0)
1767                                 push_self($<id>1, TRUE);
1768                         else {
1769                                 g_free($<id>1);
1770                                 yyerror(_("parse error"));
1771                                 YYERROR;
1772                         }
1773                                         }
1774         |       CONST TOKEN ',' arglist {
1775                         has_self = TRUE;
1776                         if(strcmp($<id>2,"self")==0)
1777                                 push_self($<id>2, TRUE);
1778                         else {
1779                                 g_free($<id>2);
1780                                 yyerror(_("parse error"));
1781                                 YYERROR;
1782                         }
1783                                         }
1784         |       arglist                 { has_self = FALSE; }
1785         ;
1786
1787 arglist:        arglist1 ',' THREEDOTS  { vararg = TRUE; }
1788         |       arglist1                { vararg = FALSE; }
1789         ;
1790         
1791 arglist1:       arglist1 ',' arg        { ; }
1792         |       arg                     { ; }
1793         ;
1794
1795 arg:            type TOKEN                                      {
1796                         push_funcarg($<id>2,NULL);
1797                                                                 }
1798         |       type TOKEN ARRAY_DIM                            {
1799                         push_funcarg($<id>2,$<id>3);
1800                                                                 }
1801         |       type TOKEN '(' TOKEN checklist ')'              {
1802                         if(strcmp($<id>4,"check")!=0) {
1803                                 yyerror(_("parse error"));
1804                                 YYERROR;
1805                         }
1806                         g_free($<id>4);
1807                         push_funcarg($<id>2,NULL);
1808                                                                 }
1809         |       type TOKEN ARRAY_DIM '(' TOKEN checklist ')'    {
1810                         if(strcmp($<id>5,"check")!=0) {
1811                                 yyerror(_("parse error"));
1812                                 YYERROR;
1813                         }
1814                         g_free($<id>5);
1815                         push_funcarg($<id>2,$<id>3);
1816                                                                 }
1817         ;
1818         
1819 checklist:      checklist check         { ; }
1820         |       check                   { ; }
1821         ;
1822
1823 check:          TOKEN                   {
1824                         if(strcmp($<id>1,"type")==0) {
1825                                 Node *node = node_new (CHECK_NODE,
1826                                                        "chtype", TYPE_CHECK,
1827                                                        NULL);
1828                                 checks = g_list_append(checks,node);
1829                         } else if(strcmp($<id>1,"null")==0) {
1830                                 Node *node = node_new (CHECK_NODE,
1831                                                        "chtype", NULL_CHECK,
1832                                                        NULL);
1833                                 checks = g_list_append(checks,node);
1834                         } else {
1835                                 yyerror(_("parse error"));
1836                                 YYERROR;
1837                         }
1838                         g_free($<id>1);
1839                                         }
1840         |       '>' numtok              {
1841                         Node *node = node_new (CHECK_NODE,
1842                                                "chtype", GT_CHECK,
1843                                                "number:steal", $<id>2,
1844                                                NULL);
1845                         checks = g_list_append(checks,node);
1846                                         }
1847         |       '<' numtok              {
1848                         Node *node = node_new (CHECK_NODE,
1849                                                "chtype", LT_CHECK,
1850                                                "number:steal", $<id>2,
1851                                                NULL);
1852                         checks = g_list_append(checks,node);
1853                                         }
1854         |       '>' '=' numtok          {
1855                         Node *node = node_new (CHECK_NODE,
1856                                                "chtype", GE_CHECK,
1857                                                "number:steal", $<id>3,
1858                                                NULL);
1859                         checks = g_list_append(checks,node);
1860                                         }
1861         |       '<' '=' numtok          {
1862                         Node *node = node_new (CHECK_NODE,
1863                                                "chtype", LE_CHECK,
1864                                                "number:steal", $<id>3,
1865                                                NULL);
1866                         checks = g_list_append(checks,node);
1867                                         }
1868         |       '=' '=' numtok          {
1869                         Node *node = node_new (CHECK_NODE,
1870                                                "chtype", EQ_CHECK,
1871                                                "number:steal", $<id>3,
1872                                                NULL);
1873                         checks = g_list_append(checks,node);
1874                                         }
1875         |       '!' '=' numtok          {
1876                         Node *node = node_new (CHECK_NODE,
1877                                                "chtype", NE_CHECK,
1878                                                "number:steal", $<id>3,
1879                                                NULL);
1880                         checks = g_list_append(checks,node);
1881                                         }
1882         ;
1883
1884 enumcode:       ENUM TOKEN '{' enumvals '}' TYPETOKEN ';' {
1885                         Node *node = node_new (ENUMDEF_NODE,
1886                                                "etype:steal", $<id>6,
1887                                                "prefix:steal", $<id>2,
1888                                                "values:steal", enum_vals,
1889                                                NULL);
1890                         enum_vals = NULL;
1891                         nodes = g_list_append (nodes, node);
1892                         }
1893         |       ENUM TOKEN '{' enumvals ',' '}' TYPETOKEN ';' {
1894                         Node *node = node_new (ENUMDEF_NODE,
1895                                                "etype:steal", $<id>7,
1896                                                "prefix:steal", $<id>2,
1897                                                "values:steal", enum_vals,
1898                                                NULL);
1899                         enum_vals = NULL;
1900                         nodes = g_list_append (nodes, node);
1901                         }
1902         ;
1903
1904 enumvals:       enumvals ',' enumval    {;}
1905         |       enumval                 {;}
1906         ;
1907
1908 enumval:        TOKEN '=' numtok        {
1909                         Node *node;
1910                         char *num = $<id>3;
1911
1912                         /* A float value, that's a bad enum */
1913                         if (num[0] >= '0' &&
1914                             num[0] <= '9' &&
1915                             strchr (num, '.') != NULL) {
1916                                 g_free ($<id>1);
1917                                 g_free (num);
1918                                 yyerror(_("parse error (enumerator value not integer constant)"));
1919                                 YYERROR;
1920                         }
1921                        
1922                         node = node_new (ENUMVALUE_NODE,
1923                                          "name:steal", $<id>1,
1924                                          "value:steal", num,
1925                                          NULL);
1926                         enum_vals = g_list_append (enum_vals, node);
1927                         }
1928         |       TOKEN                   {
1929                         Node *node;
1930
1931                         node = node_new (ENUMVALUE_NODE,
1932                                          "name:steal", $<id>1,
1933                                          NULL);
1934                         enum_vals = g_list_append (enum_vals, node);
1935         }
1936         ;
1937
1938 flagcode:       FLAGS TOKEN '{' flagvals '}' TYPETOKEN ';' {
1939                         Node *node = node_new (FLAGS_NODE,
1940                                                "ftype:steal", $<id>6,
1941                                                "prefix:steal", $<id>2,
1942                                                "values:steal", flag_vals,
1943                                                NULL);
1944                         flag_vals = NULL;
1945                         nodes = g_list_append (nodes, node);
1946                         }
1947         |       FLAGS TOKEN '{' flagvals ',' '}' TYPETOKEN ';' {
1948                         Node *node = node_new (FLAGS_NODE,
1949                                                "ftype:steal", $<id>7,
1950                                                "prefix:steal", $<id>2,
1951                                                "values:steal", flag_vals,
1952                                                NULL);
1953                         flag_vals = NULL;
1954                         nodes = g_list_append (nodes, node);
1955                         }
1956         ;
1957
1958 flagvals:       flagvals ',' TOKEN      {
1959                         flag_vals = g_list_append (flag_vals, $<id>3);
1960                 }
1961         |       TOKEN                   {
1962                         flag_vals = g_list_append (flag_vals, $<id>1);
1963                 }
1964         ;
1965
1966 errorcode:      ERROR TOKEN '{' errorvals '}' TYPETOKEN ';' {
1967                         Node *node = node_new (ERROR_NODE,
1968                                                "etype:steal", $<id>6,
1969                                                "prefix:steal", $<id>2,
1970                                                "values:steal", error_vals,
1971                                                NULL);
1972                         error_vals = NULL;
1973                         nodes = g_list_append (nodes, node);
1974                         }
1975         |       ERROR TOKEN '{' errorvals ',' '}' TYPETOKEN ';' {
1976                         Node *node = node_new (ERROR_NODE,
1977                                                "etype:steal", $<id>7,
1978                                                "prefix:steal", $<id>2,
1979                                                "values:steal", error_vals,
1980                                                NULL);
1981                         error_vals = NULL;
1982                         nodes = g_list_append (nodes, node);
1983                         }
1984         ;
1985
1986 errorvals:      errorvals ',' TOKEN     {
1987                         error_vals = g_list_append (error_vals, $<id>3);
1988                 }
1989         |       TOKEN                   {
1990                         error_vals = g_list_append (error_vals, $<id>1);
1991                 }
1992         ;
1993
1994
1995 numtok:         NUMBER                  { $<id>$ = $<id>1; }
1996         |       '-' NUMBER              {
1997                         $<id>$ = g_strconcat("-",$<id>2,NULL);
1998                         g_free($<id>2);
1999                                         }
2000         |       SINGLE_CHAR             { $<id>$ = $<id>1; }
2001         |       TOKEN                   { $<id>$ = $<id>1; }
2002         ;
2003         
2004 %%