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