/* 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 #include "treefuncs.h" #include "main.h" #include "util.h" #define _(x) (x) GList *nodes = NULL; static GList *class_nodes = NULL; Node *class = NULL; char *chunk_size = NULL; static GList *typestack = NULL; 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; g_assert(scope != CLASS_SCOPE); if(method == INIT_METHOD || method == CLASS_INIT_METHOD) { type = (Type *)new_type(g_strdup("void"), NULL, NULL); } else { type = pop_type(); } /* 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)) { error_print(GOB_WARN, line_no, _("The number of GTK arguments and " "function arguments for a signal " "don't seem to match")); } if(g_list_length(gtktypes) > 2) { GList *li; for(li = gtktypes->next; li; li = li->next) { if(strcmp(li->data, "NONE")==0) { error_print(GOB_ERROR, line_no, _("NONE can only appear in an " "argument list by itself")); } } } 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, method_unique_id++); 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_free(chunk_size); chunk_size = 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(tn, g_strdup("*"), NULL); node = new_funcarg((Type *)type,name,NULL); funcargs = g_list_prepend(funcargs, node); } static void push_self(char *id, gboolean constant) { Node *node; Node *type; GList *ch = NULL; type = new_type(g_strdup(((Class *)class)->otype), g_strdup(constant ? "const *" : "*"), 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; 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; } error_printf(GOB_ERROR, line, _("Variable %s not defined here"), id); 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 CLASSWIDE 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 chunk { class = new_class($2,$4,chunk_size,NULL); } ; chunk: | '(' TOKEN TOKEN ')' { if(strcmp($2,"chunks") == 0) { chunk_size = g_strdup($4); } else { yyerror(_("parse error")); YYERROR; } } | '(' TOKEN NUMBER ')' { if(strcmp($2,"chunks") == 0) { if(atoi($4) != 0) chunk_size = g_strdup($4); } else { yyerror(_("parse error")); YYERROR; } } ; classcode: classcode thing { ; } | thing { ; } ; thing: method { ; } | variable { ; } | argument { ; } | ';' { ; } ; scope: PUBLIC { the_scope = PUBLIC_SCOPE; } | PRIVATE { the_scope = PRIVATE_SCOPE; } | PROTECTED { the_scope = PROTECTED_SCOPE; } | CLASSWIDE { the_scope = CLASS_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_list_free($2); 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_list_free($2); 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); g_list_free($2); yyerror(_("parse error")); YYERROR; } type = pop_type(); var = find_var_or_die($4, $1); if(var->scope == PRIVATE_SCOPE) root = "self->_priv"; else if(var->scope == CLASS_SCOPE) { root = "SELF_GET_CLASS(self)"; if(no_self_alias) error_print(GOB_ERROR, $1, _("Self aliases needed when autolinking to a classwide member")); } 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(); } if(strcmp($5, "stringlink")==0) { get = g_strdup_printf("ARG = g_strdup(%s->%s);", root, $4); } else /* For everything else, get is just straight assignment */ 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: specifier_list pointer { Node *node = new_type($1, $2, NULL); typestack = g_list_prepend(typestack,node); } | specifier_list { Node *node = new_type($1, NULL, NULL); typestack = g_list_prepend(typestack,node); } ; /* The special cases are neccessary to avoid conflicts */ specifier_list: spec_list { $$ = $1; } | TOKEN { $$ = $1; } | CONST TOKEN { $$ = g_strconcat("const ", $2, NULL); g_free($2); } | TOKEN CONST { $$ = g_strconcat($1, " const", NULL); g_free($1); } | strunionenum TOKEN { $$ = g_strconcat($1, " ", $2, NULL); g_free($2); } | CONST strunionenum TOKEN { $$ = g_strconcat("const ", $2, " ", $3, NULL); g_free($3); } | strunionenum TOKEN CONST { $$ = g_strconcat($1, " ", $2, " const", NULL); g_free($2); } ; /* The special const cases take care of conflicts ! */ spec_list: specifier spec_list { $$ = g_strconcat($1, " ", $2, NULL); g_free($2); } | TYPETOKEN spec_list { $$ = g_strconcat($1, " ", $2, NULL); g_free($1); g_free($2); } | CONST spec_list { $$ = g_strconcat("const ", $2, NULL); g_free($2); } | TYPETOKEN { $$ = $1; } | TYPETOKEN CONST { $$ = g_strconcat($1, " const", NULL); g_free($1); } | specifier { $$ = g_strdup($1); } | specifier CONST { $$ = g_strconcat($1, " const", NULL); } ; specifier: VOID { $$ = "void"; } | CHAR { $$ = "char"; } | SHORT { $$ = "short"; } | INT { $$ = "int"; } | LONG { $$ = "long"; } | FLOAT { $$ = "float"; } | DOUBLE { $$ = "double"; } | SIGNED { $$ = "signed"; } | UNSIGNED { $$ = "unsigned"; } ; strunionenum: STRUCT { $$ = "struct"; } | UNION { $$ = "union"; } | ENUM { $$ = "enum"; } ; pointer: '*' { $$ = g_strdup("*"); } | '*' CONST { $$ = g_strdup("* const"); } | '*' pointer { $$ = g_strconcat("*", $2, NULL); g_free($2); } | '*' CONST pointer { $$ = g_strconcat("* const", $3, NULL); g_free($3); } ; /* 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; } if(the_scope == CLASS_SCOPE) { yyerror(_("a method cannot be of class scope")); 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; } if(the_scope == CLASS_SCOPE) { yyerror(_("a method cannot be of class scope")); 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; } if(the_scope == CLASS_SCOPE) { yyerror(_("a method cannot be of class scope")); 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; } if(the_scope == CLASS_SCOPE) { yyerror(_("a method cannot be of class scope")); 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 { if(the_scope == CLASS_SCOPE) { yyerror(_("a method cannot be of class scope")); free_all_global_state(); YYERROR; } 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($5,TRUE); yyerror(_("parse error " "(untyped blocks must be init or " "class_init)")); 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, FALSE); else { g_free($1); yyerror(_("parse error")); YYERROR; } } | TOKEN CONST { vararg = FALSE; has_self = TRUE; if(strcmp($1,"self")==0) push_self($1, TRUE); else { g_free($1); yyerror(_("parse error")); YYERROR; } } | CONST TOKEN { vararg = FALSE; has_self = TRUE; if(strcmp($2,"self")==0) push_self($2, TRUE); else { g_free($2); yyerror(_("parse error")); YYERROR; } } | TOKEN ',' arglist { has_self = TRUE; if(strcmp($1,"self")==0) push_self($1, FALSE); else { g_free($1); yyerror(_("parse error")); YYERROR; } } | TOKEN CONST ',' arglist { has_self = TRUE; if(strcmp($1,"self")==0) push_self($1, TRUE); else { g_free($1); yyerror(_("parse error")); YYERROR; } } | CONST TOKEN ',' arglist { has_self = TRUE; if(strcmp($2,"self")==0) push_self($2, TRUE); else { g_free($2); 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; } ; %%