/* 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 "tree.h" #include "main.h" #define _(x) (x) extern char *filename; 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; static GList *gtktypes = NULL; 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 void push_variable(char *name, int scope, int line_no) { Node *var; Type *type = typestack->data; typestack = g_list_remove(typestack,typestack->data); var = new_variable(scope,type,name,line_no); class_nodes = g_list_append(class_nodes, var); } static void push_function(int scope, char *oid, char *id, char *onerror, GString *cbuf,int line_no, int ccode_line, int vararg) { Node *node; Type *type; if(scope!=INIT_METHOD && scope!=CLASS_INIT_METHOD) { type = typestack->data; typestack = g_list_remove(typestack,typestack->data); } else { type = (Type *)new_type(0,g_strdup("void")); } node = new_method(scope,type,oid,gtktypes,id,funcargs, onerror,cbuf,line_no,ccode_line,vararg); gtktypes = NULL; funcargs = NULL; class_nodes = g_list_append(class_nodes, node); } static void push_funcarg(char *name) { Node *node; Type *type = typestack->data; typestack = g_list_remove(typestack,typestack->data); 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); 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)); 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); } %} %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 ONERROR %token TOKEN NUMBER TYPETOKEN %token CCODE HCODE %token PUBLIC PRIVATE ARGUMENT VIRTUAL SIGNAL OVERRIDE %% prog: ccodes class ccodes { ; } | class ccodes { ; } | ccodes class { ; } | class { ; } ; ccodes: ccodes CCODE { Node *node = new_ccode(FALSE,$2,ccode_line); nodes = g_list_append(nodes,node); } | ccodes HCODE { Node *node = new_ccode(TRUE,$2,ccode_line); nodes = g_list_append(nodes,node); } | CCODE { Node *node = new_ccode(FALSE,$1,ccode_line); nodes = g_list_append(nodes,node); } | HCODE { Node *node = new_ccode(TRUE,$1,ccode_line); nodes = g_list_append(nodes,node); } ; class: classdec '{' classcode '}' { ((Class *)class)->nodes = class_nodes; class_nodes = NULL; nodes = g_list_append(nodes,class); } ; classdec: CLASS TYPETOKEN FROM TYPETOKEN { class = new_class($2,$4,NULL); } ; classcode: classcode method { ; } | classcode variable { ; } | classcode argument { ; } | method { ; } | variable { ; } | argument { ; } ; variable: PUBLIC type TOKEN ';' { push_variable($3,PUBLIC_SCOPE,$1); } | PRIVATE type TOKEN ';' { push_variable($3,PRIVATE_SCOPE,$1); } ; argument: ARGUMENT argflags TOKEN TOKEN TOKEN '{' CCODE TOKEN '{' CCODE ';' { if(strcmp($5,"get")==0 && strcmp($8,"set")==0) { Node *node; g_free($5); g_free($8); node = new_argument($3,$2,$4, $7,$6, $10,$9, $1); class_nodes = g_list_append(class_nodes,node); } else if(strcmp($5,"set")==0 && strcmp($8,"get")==0) { Node *node; g_free($5); g_free($8); node = new_argument($3,$2,$4, $10,$9, $7,$6, $1); 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 argflags TOKEN TOKEN TOKEN '{' CCODE ';' { if(strcmp($5,"get")==0) { Node *node; g_free($5); node = new_argument($3,$2,$4, $7,$6,NULL,0, $1); class_nodes = g_list_append(class_nodes,node); } else if(strcmp($5,"set")==0) { Node *node; g_free($5); node = new_argument($3,$2,$4, NULL,0,$7,$6, $1); 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; } } ; argflags: '(' 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); typestack = g_list_prepend(typestack,node); } | type2 stars { Node *node = new_type(stars,$1); 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++; } ; fullsigtype: PRIVATE TOKEN sigtype { if(strcmp($2,"first")==0) $$ = PRIVATE_SIGNAL_FIRST_METHOD; else if(strcmp($2,"last")==0) $$ = PRIVATE_SIGNAL_LAST_METHOD; else { yyerror(_("signal must be 'first' or 'last'")); g_free($2); YYERROR; } g_free($2); } | TOKEN PRIVATE sigtype { if(strcmp($1,"first")==0) $$ = PRIVATE_SIGNAL_FIRST_METHOD; else if(strcmp($1,"last")==0) $$ = PRIVATE_SIGNAL_LAST_METHOD; else { yyerror(_("signal must be 'first' or 'last'")); g_free($1); YYERROR; } g_free($1); } | PRIVATE sigtype { $$ = PRIVATE_SIGNAL_LAST_METHOD; } | 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; } ; 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 fullsigtype type TOKEN '(' funcargs ')' onerror codenocode { if(!has_self) { yyerror(_("signal without 'self' as " "first parameter")); YYERROR; } push_function($2,NULL, $4, $8, $9,$1, ccode_line,vararg); } | VIRTUAL PRIVATE type TOKEN '(' funcargs ')' onerror codenocode { if(!has_self) { yyerror(_("virtual method without 'self' as " "first parameter")); YYERROR; } push_function(PRIVATE_VIRTUAL_METHOD, NULL, $4, $8, $9,$1, ccode_line,vararg); } | VIRTUAL type TOKEN '(' funcargs ')' onerror codenocode { if(!has_self) { yyerror(_("virtual method without 'self' as " "first parameter")); YYERROR; } push_function(VIRTUAL_METHOD, NULL, $3, $7, $8,$1, ccode_line,vararg); } | OVERRIDE '(' TYPETOKEN ')' type TOKEN '(' funcargs ')' onerror '{' CCODE { push_function(OVERRIDE_METHOD, $3, $6, $10, $12, $1,$11, vararg); } | PUBLIC type TOKEN '(' funcargs ')' onerror '{' CCODE { push_function(PUBLIC_SCOPE, NULL, $3, $7, $9,$1,$8, vararg); } | PRIVATE type TOKEN '(' funcargs ')' onerror '{' CCODE { push_function(PRIVATE_SCOPE, NULL, $3, $7, $9,$1,$8, vararg); } | TOKEN '(' TOKEN ')' codenocode { if(strcmp($1,"init")==0) { push_init_arg($3,FALSE); push_function(INIT_METHOD, NULL, $1, NULL, $5,$2, ccode_line,FALSE); } else if(strcmp($1,"class_init")==0) { push_init_arg($3,TRUE); push_function(CLASS_INIT_METHOD, NULL, $1, NULL, $5,$2, ccode_line,FALSE); } else { g_free($1); g_free($3); g_string_free($3,TRUE); yyerror(_("parse error")); YYERROR; } } ; onerror: ONERROR numtok { $$ = $2; } | ONERROR '{' CCODE { $$ = ($3)->str; g_string_free($3,FALSE); } | '=' '1' { ; } | { $$ = NULL; } ; funcargs: VOID { vararg = FALSE; has_self = FALSE; } | TOKEN { vararg = FALSE; has_self = TRUE; if(strcmp($1,"this")==0) { push_self($1); print_error(TRUE,_("Use of 'this' is " "depreciated, use 'self' " "instead"),line_no); } else if(strcmp($1,"self")==0) push_self($1); else { g_free($1); yyerror(_("parse error")); YYERROR; } } | TOKEN ',' arglist { has_self = TRUE; if(strcmp($1,"this")==0) { push_self($1); print_error(TRUE,_("Use of 'this' is " "depreciated, use 'self' " "instead"),line_no); } else 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); } | type TOKEN '(' TOKEN checklist ')' { if(strcmp($4,"check")!=0) { yyerror(_("parse error")); YYERROR; } g_free($4); push_funcarg($2); } ; 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; } ; %%