/* GOB C Preprocessor * Copyright (C) 1999 the Free Software Foundation. * * Author: George Lebl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ %{ #include "config.h" #include #include #include #include "treefuncs.h" #include "main.h" #include "util.h" #define _(x) (x) GList *nodes = NULL; static GList *class_nodes = NULL; Node *class = NULL; static GList *typestack = NULL; static int stars = 0; static GList *funcargs = NULL; static GList *checks = NULL; static int has_self = FALSE; static int vararg = FALSE; /* destructor and initializer for variables */ static char *destructor = NULL; static int destructor_line = 0; static gboolean destructor_simple = TRUE; static char *initializer = NULL; static int initializer_line = 0; static char *onerror = NULL; static char *defreturn = NULL; static GList *gtktypes = NULL; /* this can be a global as we will only do one function at a time anyway */ static int the_scope = NO_SCOPE; void free(void *ptr); int yylex(void); extern int ccode_line; extern int line_no; extern char *yytext; static void yyerror(char *str) { char *out=NULL; char *p; if(strcmp(yytext,"\n")==0) { out=g_strconcat("Error: ",str," before newline",NULL); } else if(yytext[0]=='\0') { out=g_strconcat("Error: ", str, " at end of input", NULL); } else { char *tmp = g_strdup(yytext); while((p=strchr(tmp, '\n'))) *p='.'; out=g_strconcat("Error: ", str, " before '", tmp, "'", NULL); g_free(tmp); } fprintf(stderr, "%s:%d: %s\n", filename, line_no, out); g_free(out); exit(1); } static Type * pop_type(void) { Type *type = typestack->data; typestack = g_list_remove(typestack,typestack->data); return type; } static void push_variable(char *name, int scope, int line_no, char *postfix) { Node *var; Type *type = pop_type(); type->postfix = postfix; var = new_variable(scope, type, name, line_no, destructor, destructor_line, destructor_simple, initializer, initializer_line); class_nodes = g_list_append(class_nodes, var); } static void push_function(int scope, int method, char *oid, char *id, GString *cbuf, int line_no, int ccode_line, gboolean vararg, GList *flags) { Node *node; Type *type; char *c_cbuf; if(method!=INIT_METHOD && method!=CLASS_INIT_METHOD) { type = pop_type(); } else { type = (Type *)new_type(0,g_strdup("void"),NULL); } /* a complicated and ugly test to figure out if we have the wrong number of types for a signal */ if((method == SIGNAL_FIRST_METHOD || method == SIGNAL_LAST_METHOD) && g_list_length(gtktypes) != g_list_length(funcargs) && !(g_list_length(funcargs) == 1 && g_list_length(gtktypes) == 2 && strcmp(gtktypes->next->data,"NONE")==0)) { print_error(TRUE, _("The number of GTK arguments and " "function arguments for a signal " "don't seem to match"), line_no); } if(cbuf) { char *p; c_cbuf = p = cbuf->str; while(p && *p && (*p==' ' || *p=='\t' || *p=='\n' || *p=='\r')) p++; if(!p || !*p) c_cbuf = NULL; } else c_cbuf = NULL; node = new_method(scope, method, type, oid, gtktypes, flags, id, funcargs, onerror, defreturn, c_cbuf, line_no, ccode_line, vararg); if(cbuf) g_string_free(cbuf, /*only free segment if we haven't passed it above */ c_cbuf?FALSE:TRUE); gtktypes = NULL; funcargs = NULL; onerror = NULL; defreturn = NULL; class_nodes = g_list_append(class_nodes, node); } static void free_all_global_state(void) { g_free(onerror); onerror = NULL; g_free(defreturn); defreturn = NULL; g_list_foreach(gtktypes, (GFunc)g_free, NULL); g_list_free(gtktypes); gtktypes = NULL; free_node_list(funcargs); funcargs = NULL; } static void push_funcarg(char *name, char *postfix) { Node *node; Type *type = pop_type(); type->postfix = postfix; node = new_funcarg(type, name, checks); checks = NULL; funcargs = g_list_append(funcargs, node); } static void push_init_arg(char *name, int is_class) { Node *node; Node *type; char *tn; if(is_class) tn = g_strconcat(((Class *)class)->otype,":Class",NULL); else tn = g_strdup(((Class *)class)->otype); type = new_type(1,tn,NULL); node = new_funcarg((Type *)type,name,NULL); funcargs = g_list_prepend(funcargs, node); } static void push_self(char *id) { Node *node; Node *type; GList *ch = NULL; type = new_type(1,g_strdup(((Class *)class)->otype),NULL); ch = g_list_append(ch,new_check(NULL_CHECK,NULL)); ch = g_list_append(ch,new_check(TYPE_CHECK,NULL)); node = new_funcarg((Type *)type,id,ch); funcargs = g_list_prepend(funcargs, node); } static Variable * find_var_or_die(const char *id, int line) { GList *li; char *s; for(li = class_nodes; li != NULL; li = li->next) { Variable *var; Node *node = li->data; if(node->type != VARIABLE_NODE) continue; var = li->data; if(strcmp(var->id, id)==0) return var; } s = g_strdup_printf(_("Variable %s not defined here"), id); print_error(FALSE, s, line); g_assert_not_reached(); return NULL; } static gboolean set_return_value(char *type, char *val) { if(strcmp(type, "onerror")==0) { if(!onerror) { onerror = val; return TRUE; } else return FALSE; } else if(strcmp(type, "defreturn")==0) { if(!defreturn) { defreturn = val; return TRUE; } else return FALSE; } return FALSE; } %} %union { char *id; GString *cbuf; GList *list; int line; int sigtype; } %token CLASS FROM %token CONST VOID STRUCT UNION ENUM THREEDOTS %token SIGNED UNSIGNED LONG SHORT INT FLOAT DOUBLE CHAR %token TOKEN NUMBER TYPETOKEN ARRAY_DIM %token CCODE HTCODE PHCODE HCODE ACODE ATCODE %token PUBLIC PRIVATE PROTECTED ARGUMENT VIRTUAL SIGNAL OVERRIDE %% prog: ccodes class ccodes { ; } | class ccodes { ; } | ccodes class { ; } | class { ; } ; ccode: CCODE { Node *node = new_ccode(C_CCODE,($1)->str, ccode_line); nodes = g_list_append(nodes,node); g_string_free($1,FALSE); } | HCODE { Node *node = new_ccode(H_CCODE,($1)->str, ccode_line); nodes = g_list_append(nodes,node); g_string_free($1,FALSE); } | HTCODE { Node *node = new_ccode(HT_CCODE,($1)->str, ccode_line); nodes = g_list_append(nodes,node); g_string_free($1,FALSE); } | PHCODE { Node *node = new_ccode(PH_CCODE,($1)->str, ccode_line); nodes = g_list_append(nodes,node); g_string_free($1,FALSE); } | ACODE { Node *node = new_ccode(A_CCODE,($1)->str, ccode_line); nodes = g_list_append(nodes,node); g_string_free($1,FALSE); } | ATCODE { Node *node = new_ccode(AT_CCODE,($1)->str, ccode_line); nodes = g_list_append(nodes,node); g_string_free($1,FALSE); } ; ccodes: ccodes ccode { ; } | ccode { ; } ; class: classdec '{' classcode '}' { ((Class *)class)->nodes = class_nodes; class_nodes = NULL; nodes = g_list_append(nodes,class); } | classdec '{' '}' { ((Class *)class)->nodes = NULL; class_nodes = NULL; nodes = g_list_append(nodes,class); } ; classdec: CLASS TYPETOKEN FROM TYPETOKEN { class = new_class($2,$4,NULL); } ; classcode: classcode thing { ; } | thing { ; } ; thing: method { ; } | variable { ; } | argument { ; } | ';' { ; } ; scope: PUBLIC { the_scope = PUBLIC_SCOPE; } | PRIVATE { the_scope = PRIVATE_SCOPE; } | PROTECTED { the_scope = PROTECTED_SCOPE; } ; destructor: TOKEN TOKEN { if(strcmp($1, "destroywith")==0) { g_free($1); destructor = $2; destructor_line = line_no; destructor_simple = TRUE; } else { g_free($1); g_free($2); yyerror(_("parse error")); YYERROR; } } | TOKEN '{' CCODE { if(strcmp($1, "destroy")==0) { g_free($1); destructor = ($3)->str; g_string_free($3, FALSE); destructor_line = ccode_line; destructor_simple = FALSE; } else { g_free($1); g_string_free($3, TRUE); yyerror(_("parse error")); YYERROR; } } ; initializer: '=' numtok { initializer = $2; initializer_line = ccode_line; } | '=' '{' CCODE { initializer = ($3)->str; initializer_line = ccode_line; g_string_free($3, FALSE); } ; varoptions: destructor initializer { ; } | initializer destructor { ; } | initializer { destructor = NULL; } | destructor { initializer = NULL; } | { destructor = NULL; initializer = NULL; } ; variable: scope type TOKEN varoptions ';' { push_variable($3, the_scope,$1, NULL); } | scope type TOKEN ARRAY_DIM varoptions ';' { push_variable($3, the_scope, $1, $4); } ; argument: ARGUMENT flags argtype TOKEN TOKEN '{' CCODE TOKEN '{' CCODE ';' { if(strcmp($5,"get")==0 && strcmp($8,"set")==0) { Node *node; Type *type = pop_type(); g_free($5); g_free($8); node = new_argument($3,type,$2,$4, ($7)->str,$6, ($10)->str,$9, $1); g_string_free($7,FALSE); g_string_free($10,FALSE); class_nodes = g_list_append(class_nodes,node); } else if(strcmp($5,"set")==0 && strcmp($8,"get")==0) { Node *node; Type *type = pop_type(); g_free($5); g_free($8); node = new_argument($3,type,$2,$4, ($10)->str,$9, ($7)->str,$6, $1); g_string_free($10,FALSE); g_string_free($7,FALSE); class_nodes = g_list_append(class_nodes,node); } else { g_free($3); g_free($4); g_free($5); g_free($8); g_list_foreach($2,(GFunc)g_free,NULL); g_string_free($10,TRUE); g_string_free($7,TRUE); yyerror(_("parse error")); YYERROR; } } | ARGUMENT flags argtype TOKEN TOKEN '{' CCODE ';' { if(strcmp($5,"get")==0) { Node *node; Type *type = pop_type(); g_free($5); node = new_argument($3,type,$2,$4, ($7)->str,$6, NULL,0, $1); g_string_free($7,FALSE); class_nodes = g_list_append(class_nodes,node); } else if(strcmp($5,"set")==0) { Node *node; Type *type = pop_type(); g_free($5); node = new_argument($3,type,$2,$4, NULL,0,($7)->str, $6, $1); g_string_free($7,FALSE); class_nodes = g_list_append(class_nodes,node); } else { g_free($5); g_free($3); g_free($4); g_list_foreach($2,(GFunc)g_free,NULL); g_string_free($7,TRUE); yyerror(_("parse error")); YYERROR; } } | ARGUMENT flags argtype TOKEN TOKEN { Node *node; char *get, *set = NULL; Variable *var; Type *type; char *root; if(strcmp($5,"link")!=0 && strcmp($5,"stringlink")!=0 && strcmp($5,"objectlink")!=0) { g_free($5); g_free($3); g_free($4); g_list_foreach($2,(GFunc)g_free,NULL); yyerror(_("parse error")); YYERROR; } type = pop_type(); var = find_var_or_die($4, $1); if(var->scope == PRIVATE_SCOPE) root = "self->_priv"; else root = "self"; if(strcmp($5,"link")==0) { set = g_strdup_printf("%s->%s = ARG;", root, $4); } else if(strcmp($5,"stringlink")==0) { set = g_strdup_printf("g_free(%s->%s); " "%s->%s = g_strdup(ARG);", root, $4, root, $4); } else if(strcmp($5,"objectlink")==0) { set = g_strdup_printf( "if(%s->%s) " "gtk_object_unref(GTK_OBJECT(%s->%s)); " "%s->%s = ARG; " "if(%s->%s) " "gtk_object_ref(GTK_OBJECT(%s->%s)); ", root, $4, root, $4, root, $4, root, $4, root, $4); } else { g_assert_not_reached(); } /* get is the same for everything */ get = g_strdup_printf("ARG = %s->%s;", root, $4); g_free($5); if(!type) type = copy_type(var->vtype); node = new_argument($3, type, $2, $4, get, $1, set, $1, $1); class_nodes = g_list_append(class_nodes,node); } ; argtype: TOKEN '(' TOKEN type ')' { if(strcmp($3,"type")!=0) { g_free($1); g_free($3); yyerror(_("parse error")); YYERROR; } $$ = $1; } | TOKEN { $$ = $1; typestack = g_list_prepend(typestack,NULL); } ; flags: '(' flaglist ')' { $$ = $2; } | { $$ = NULL; } ; flaglist: TOKEN '|' flaglist { $$ = g_list_append($3,$1); } | TOKEN { $$ = g_list_append(NULL,$1); } ; type: type1 { ; } | CONST type1 { Type *type = typestack->data; char *oldname = type->name; type->name = g_strconcat("const ",oldname,NULL); g_free(oldname); } ; type1: type2 { Node *node = new_type(0,$1,NULL); typestack = g_list_prepend(typestack,node); } | type2 stars { Node *node = new_type(stars,$1,NULL); stars = 0; typestack = g_list_prepend(typestack,node); } ; type2: UNSIGNED integer { $$ = g_strconcat("unsigned ",$2,NULL); } | SIGNED integer { $$ = g_strconcat("signed ",$2,NULL); } | integer { $$ = g_strdup($1); } | UNSIGNED CHAR { $$ = g_strdup("unsigned char"); } | SIGNED CHAR { $$ = g_strdup("signed char"); } | CHAR { $$ = g_strdup("char"); } | DOUBLE { $$ = g_strdup("double"); } | FLOAT { $$ = g_strdup("float"); } | TOKEN { $$ = $1; } | tspecifier TOKEN { $$ = g_strconcat($1,$2,NULL); g_free($2); } | TYPETOKEN { $$ = $1; } | VOID { $$ = g_strdup("void"); } ; integer: LONG INT { $$ = "long int"; } | LONG { $$ = "long"; } | SHORT INT { $$ = "short int"; } | SHORT { $$ = "short"; } | INT { $$ = "int"; } ; tspecifier: ENUM { $$ = "enum "; } | UNION { $$ = "union "; } | STRUCT { $$ = "struct "; } ; stars: '*' stars { stars++; } | '*' { stars++; } ; /* this never sets the_scope */ simplesigtype: TOKEN sigtype { if(strcmp($1, "first")==0) $$ = SIGNAL_FIRST_METHOD; else if(strcmp($1, "last")==0) $$ = SIGNAL_LAST_METHOD; else { yyerror(_("signal must be 'first' or 'last'")); g_free($1); YYERROR; } g_free($1); } | sigtype { $$ = SIGNAL_LAST_METHOD; } ; /* this always sets the_scope */ fullsigtype: scope TOKEN sigtype { if(strcmp($2,"first")==0) $$ = SIGNAL_FIRST_METHOD; else if(strcmp($2,"last")==0) $$ = SIGNAL_LAST_METHOD; else { yyerror(_("signal must be 'first' or 'last'")); g_free($2); YYERROR; } g_free($2); } | TOKEN scope sigtype { if(strcmp($1,"first")==0) $$ = SIGNAL_FIRST_METHOD; else if(strcmp($1,"last")==0) $$ = SIGNAL_LAST_METHOD; else { yyerror(_("signal must be 'first' or 'last'")); g_free($1); YYERROR; } g_free($1); } | scope sigtype { $$ = SIGNAL_LAST_METHOD; } | simplesigtype { /* the_scope was default thus public */ the_scope = PUBLIC_SCOPE; } ; sigtype: TOKEN '(' tokenlist ')' { gtktypes = g_list_prepend(gtktypes, $1); } ; tokenlist: tokenlist ',' TOKEN { gtktypes = g_list_append(gtktypes, $3); } | TOKEN { gtktypes = g_list_append(gtktypes, $1); } ; codenocode: '{' CCODE { $$ = $2; } | ';' { $$ = NULL; } ; /*here CCODE will include the ending '}' */ method: SIGNAL flags fullsigtype type TOKEN '(' funcargs ')' returnvals codenocode { if(!has_self) { yyerror(_("signal without 'self' as " "first parameter")); free_all_global_state(); YYERROR; } push_function(the_scope, $3,NULL, $5, $10,$1, ccode_line, vararg, $2); } | scope SIGNAL flags simplesigtype type TOKEN '(' funcargs ')' returnvals codenocode { if(!has_self) { yyerror(_("signal without 'self' as " "first parameter")); free_all_global_state(); YYERROR; } push_function(the_scope, $4, NULL, $6, $11, $2, ccode_line, vararg, $3); } | VIRTUAL scope type TOKEN '(' funcargs ')' returnvals codenocode { if(!has_self) { yyerror(_("virtual method without 'self' as " "first parameter")); free_all_global_state(); YYERROR; } push_function(the_scope, VIRTUAL_METHOD, NULL, $4, $9, $1, ccode_line, vararg, NULL); } | scope VIRTUAL type TOKEN '(' funcargs ')' returnvals codenocode { if(!has_self) { yyerror(_("virtual method without 'self' as " "first parameter")); free_all_global_state(); YYERROR; } push_function(the_scope, VIRTUAL_METHOD, NULL, $4, $9, $2, ccode_line, vararg, NULL); } | VIRTUAL type TOKEN '(' funcargs ')' returnvals codenocode { if(!has_self) { yyerror(_("virtual method without 'self' as " "first parameter")); free_all_global_state(); YYERROR; } push_function(PUBLIC_SCOPE, VIRTUAL_METHOD, NULL, $3, $8, $1, ccode_line, vararg, NULL); } | OVERRIDE '(' TYPETOKEN ')' type TOKEN '(' funcargs ')' returnvals codenocode { push_function(NO_SCOPE, OVERRIDE_METHOD, $3, $6, $11, $1, ccode_line, vararg, NULL); } | scope type TOKEN '(' funcargs ')' returnvals codenocode { push_function(the_scope, REGULAR_METHOD, NULL, $3, $8, $1, ccode_line, vararg, NULL); } | TOKEN '(' TOKEN ')' codenocode { if(strcmp($1, "init")==0) { push_init_arg($3,FALSE); push_function(NO_SCOPE, INIT_METHOD, NULL, $1, $5, $2, ccode_line, FALSE, NULL); } else if(strcmp($1, "class_init")==0) { push_init_arg($3,TRUE); push_function(NO_SCOPE, CLASS_INIT_METHOD, NULL, $1, $5, $2, ccode_line, FALSE, NULL); } else { g_free($1); g_free($3); g_string_free($3,TRUE); yyerror(_("parse error")); YYERROR; } } ; returnvals: TOKEN retcode { g_free(onerror); onerror = NULL; g_free(defreturn); defreturn = NULL; if(!set_return_value($1, $2)) { g_free($1); g_free($2); yyerror(_("parse error")); YYERROR; } g_free($1); } | TOKEN retcode TOKEN retcode { g_free(onerror); onerror = NULL; g_free(defreturn); defreturn = NULL; if(!set_return_value($1, $2)) { g_free($1); g_free($2); g_free($3); g_free($4); yyerror(_("parse error")); YYERROR; } if(!set_return_value($3, $4)) { onerror = defreturn = NULL; g_free($1); g_free($2); g_free($3); g_free($4); yyerror(_("parse error")); YYERROR; } g_free($1); g_free($3); } | { g_free(onerror); onerror = NULL; g_free(defreturn); defreturn = NULL; } ; retcode: numtok { $$ = $1; } | '{' CCODE { $$ = ($3)->str; g_string_free($3, FALSE); } ; funcargs: VOID { vararg = FALSE; has_self = FALSE; } | TOKEN { vararg = FALSE; has_self = TRUE; if(strcmp($1,"self")==0) push_self($1); else { g_free($1); yyerror(_("parse error")); YYERROR; } } | TOKEN ',' arglist { has_self = TRUE; if(strcmp($1,"self")==0) push_self($1); else { g_free($1); yyerror(_("parse error")); YYERROR; } } | arglist { has_self = FALSE; } ; arglist: arglist1 ',' THREEDOTS { vararg = TRUE; } | arglist1 { vararg = FALSE; } ; arglist1: arglist1 ',' arg { ; } | arg { ; } ; arg: type TOKEN { push_funcarg($2,NULL); } | type TOKEN ARRAY_DIM { push_funcarg($2,$3); } | type TOKEN '(' TOKEN checklist ')' { if(strcmp($4,"check")!=0) { yyerror(_("parse error")); YYERROR; } g_free($4); push_funcarg($2,NULL); } | type TOKEN ARRAY_DIM '(' TOKEN checklist ')' { if(strcmp($5,"check")!=0) { yyerror(_("parse error")); YYERROR; } g_free($5); push_funcarg($2,$3); } ; checklist: checklist check { ; } | check { ; } ; check: TOKEN { if(strcmp($1,"type")==0) { Node *node = new_check(TYPE_CHECK,NULL); checks = g_list_append(checks,node); } else if(strcmp($1,"null")==0) { Node *node = new_check(NULL_CHECK,NULL); checks = g_list_append(checks,node); } else { yyerror(_("parse error")); YYERROR; } g_free($1); } | '>' numtok { Node *node = new_check(GT_CHECK,$2); checks = g_list_append(checks,node); } | '<' numtok { Node *node = new_check(LT_CHECK,$2); checks = g_list_append(checks,node); } | '>' '=' numtok { Node *node = new_check(GE_CHECK,$3); checks = g_list_append(checks,node); } | '<' '=' numtok { Node *node = new_check(LE_CHECK,$3); checks = g_list_append(checks,node); } | '=' '=' numtok { Node *node = new_check(EQ_CHECK,$3); checks = g_list_append(checks,node); } | '!' '=' numtok { Node *node = new_check(NE_CHECK,$3); checks = g_list_append(checks,node); } ; numtok: NUMBER { $$ = $1; } | '-' NUMBER { $$ = g_strconcat("-",$2,NULL); g_free($2); } | TOKEN { $$ = $1; } ; %%