/* Copyright comment */ %{ #include #include #include #include "type.h" #include "extern.h" static void yyerror(char *); /* * Variables containing simple states. */ int debugging = 0; static int struct_level = 0; static char *do_str = NULL; static int do_length = 0; /* temporarily store record members while creating the data structure */ struct ECPGrecord_member *record_member_list[128] = { NULL }; /* * Handle the filename and line numbering. */ char * input_filename = NULL; static void output_line_number() { if (input_filename) fprintf(yyout, "\n#line %d \"%s\"\n", yylineno, input_filename); } /* * store the whenever action here */ static struct when when_error, when_nf; static void print_action(struct when *w) { switch (w->code) { case W_CONTINUE: fprintf(yyout, "continue;"); break; case W_BREAK: fprintf(yyout, "break;"); break; case W_SQLPRINT: fprintf(yyout, "sqlprint();"); break; case W_GOTO: fprintf(yyout, "goto %s;", w->str); break; case W_DO: fprintf(yyout, "%s;", w->str); break; default: fprintf(yyout, "{/* not implemented yet */}"); break; } } static void whenever_action() { if (when_nf.code != W_NOTHING) { fprintf(yyout, "\nif (SQLCODE > 0) "); print_action(&when_nf); } if (when_error.code != W_NOTHING) { fprintf(yyout, "\nif (SQLCODE < 0) "); print_action(&when_error); } output_line_number(); } /* * Handling of the variables. */ /* * brace level counter */ int braces_open; /* This is a linked list of the variable names and types. */ struct variable { char * name; struct ECPGtype * type; int brace_level; struct variable * next; }; static struct variable * allvariables = NULL; static struct variable * find_variable(char * name) { struct variable * p; for (p = allvariables; p; p = p->next) { if (strcmp(p->name, name) == 0) return p; } { char * errorstring = (char *) malloc(strlen(name) + 100); sprintf(errorstring, "The variable :%s is not declared.", name); yyerror(errorstring); } return NULL; } static void new_variable(const char * name, struct ECPGtype * type) { struct variable * p = (struct variable*) malloc(sizeof(struct variable)); p->name = strdup(name); p->type = type; p->brace_level = braces_open; p->next = allvariables; allvariables = p; } static void remove_variables(int brace_level) { struct variable * p, *prev; for (p = prev = allvariables; p; p = p ? p->next : NULL) { if (p->brace_level >= brace_level) { /* remove it */ if (p == allvariables) prev = allvariables = p->next; else prev->next = p->next; ECPGfree_type(p->type); free(p->name); free(p); p = prev; } else prev = p; } } /* * Here are the variables that need to be handled on every request. * These are of two kinds: input and output. * I will make two lists for them. */ struct arguments { struct variable * variable; struct arguments * next; }; static struct arguments * argsinsert = NULL; static struct arguments * argsresult = NULL; void reset_variables() { argsinsert = NULL; argsresult = NULL; } /* Add a variable to a request. */ void add_variable(struct arguments ** list, struct variable * var) { struct arguments * p = (struct arguments *)malloc(sizeof(struct arguments)); p->variable = var; p->next = *list; *list = p; } /* Dump out a list of all the variable on this list. This is a recursive function that works from the end of the list and deletes the list as we go on. */ void dump_variables(struct arguments * list) { if (list == NULL) { return; } /* The list is build up from the beginning so lets first dump the end of the list: */ dump_variables(list->next); /* Then the current element. */ ECPGdump_a_type(yyout, list->variable->name, list->variable->type, NULL); /* Then release the list element. */ free(list); } %} %union { int tagname; struct ECPGtemp_type type; char * symbolname; int indexsize; enum ECPGttype type_enum; struct when action; } %token SQL_START SQL_SEMI SQL_STRING SQL_INTO %token SQL_BEGIN SQL_END SQL_DECLARE SQL_SECTION SQL_INCLUDE %token SQL_CONNECT SQL_OPEN SQL_EXECUTE SQL_IMMEDIATE %token SQL_COMMIT SQL_ROLLBACK SQL_RELEASE SQL_WORK SQL_WHENEVER %token SQL_SQLERROR SQL_NOT_FOUND SQL_BREAK SQL_CONTINUE %token SQL_DO SQL_GOTO SQL_SQLPRINT %token S_SYMBOL S_LENGTH S_ANYTHING S_LABEL %token S_VARCHAR S_VARCHAR2 %token S_EXTERN S_STATIC S_AUTO S_CONST S_REGISTER S_STRUCT S_SIGNED %token S_UNSIGNED S_SIGNED %token S_LONG S_SHORT S_INT S_CHAR S_FLOAT S_DOUBLE S_BOOL %token '[' ']' ';' ',' '{' '}' '=' '*' '(' ')' %type type type_detailed varchar_type simple_type array_type struct_type %type symbol label %type maybe_storage_clause varchar_tag db_name %type simple_tag %type index length %type action %type canything sqlanything both_anything vartext commit_release %% prog : statements; statements : /* empty */ | statements statement; statement : sqldeclaration | sqlinclude | sqlconnect | sqlopen | sqlcommit | sqlrollback | sqlexecute | sqlwhenever | sqlstatement | cthing | blockstart | blockend; sqldeclaration : sql_startdeclare variable_declarations sql_enddeclare; sql_startdeclare : SQL_START SQL_BEGIN SQL_DECLARE SQL_SECTION SQL_SEMI { fprintf(yyout, "/* exec sql begin declare section */\n"); output_line_number(); } sql_enddeclare : SQL_START SQL_END SQL_DECLARE SQL_SECTION SQL_SEMI { fprintf(yyout,"/* exec sql end declare section */\n"); output_line_number(); } variable_declarations : /* empty */ | variable_declarations variable_declaration; /* Here is where we can enter support for typedef. */ variable_declaration : type initializer ';' { /* don't worry about our list when we're working on a struct */ if (struct_level == 0) { new_variable($1.name, $1.typ); free($1.name); } fprintf(yyout, ";"); } initializer : /*empty */ | '=' {fwrite(yytext, yyleng, 1, yyout);} vartext; vartext : /* empty */ {} | vartext both_anything { if (do_length == 0) fwrite(yytext, yyleng, 1, yyout); else { if (strlen(do_str) + yyleng + 1 >= do_length) do_str = mm_realloc(do_str, do_length += yyleng); strcat(do_str, yytext); } } symbol : S_SYMBOL { char * name = (char *)malloc(yyleng + 1); strncpy(name, yytext, yyleng); name[yyleng] = '\0'; $$ = name; } type : maybe_storage_clause type_detailed { $$ = $2; }; type_detailed : varchar_type { $$ = $1; } | simple_type { $$ = $1; } | array_type {$$ = $1; } | pointer_type {$$ = $1; } | struct_type {$$ = $1; }; varchar_type : varchar_tag symbol index { if ($3 > 0) fprintf(yyout, "struct varchar_%s { int len; char arr[%d]; } %s", $2, $3, $2); else fprintf(yyout, "struct varchar_%s { int len; char arr[%d]; } %s", $2, $3, $2); if (struct_level == 0) { $$.name = $2; $$.typ = ECPGmake_varchar_type(ECPGt_varchar, $3); } else ECPGmake_record_member($2, ECPGmake_varchar_type(ECPGt_varchar, $3), &(record_member_list[struct_level-1])); } varchar_tag : S_VARCHAR { $$ = $1; } | S_VARCHAR2 { $$ = $1; }; simple_type : simple_tag symbol { fprintf(yyout, "%s %s", ECPGtype_name($1), $2); if (struct_level == 0) { $$.name = $2; $$.typ = ECPGmake_simple_type($1); } else ECPGmake_record_member($2, ECPGmake_simple_type($1), &(record_member_list[struct_level-1])); } array_type : simple_tag symbol index { if ($3 > 0) fprintf(yyout, "%s %s [%d]", ECPGtype_name($1), $2, $3); else fprintf(yyout, "%s %s []", ECPGtype_name($1), $2); if (struct_level == 0) { $$.name = $2; $$.typ = ECPGmake_array_type(ECPGmake_simple_type($1), $3); } else ECPGmake_record_member($2, ECPGmake_array_type(ECPGmake_simple_type($1), $3), &(record_member_list[struct_level-1])); } pointer_type : simple_tag '*' symbol { fprintf(yyout, "%s * %s", ECPGtype_name($1), $3); if (struct_level == 0) { $$.name = $3; $$.typ = ECPGmake_array_type(ECPGmake_simple_type($1), 0); } else ECPGmake_record_member($3, ECPGmake_array_type(ECPGmake_simple_type($1), 0), &(record_member_list[struct_level-1])); } s_struct : S_STRUCT symbol { struct_level++; fprintf(yyout, "struct %s {", $2); } struct_type : s_struct '{' variable_declarations '}' symbol { struct_level--; if (struct_level == 0) { $$.name = $5; $$.typ = ECPGmake_record_type(record_member_list[struct_level]); } else ECPGmake_record_member($5, ECPGmake_record_type(record_member_list[struct_level]), &(record_member_list[struct_level-1])); fprintf(yyout, "} %s", $5); record_member_list[struct_level] = NULL; } simple_tag : S_CHAR { $$ = ECPGt_char; } | S_UNSIGNED S_CHAR { $$ = ECPGt_unsigned_char; } | S_SHORT { $$ = ECPGt_short; } | S_UNSIGNED S_SHORT { $$ = ECPGt_unsigned_short; } | S_INT { $$ = ECPGt_int; } | S_UNSIGNED S_INT { $$ = ECPGt_unsigned_int; } | S_LONG { $$ = ECPGt_long; } | S_UNSIGNED S_LONG { $$ = ECPGt_unsigned_long; } | S_FLOAT { $$ = ECPGt_float; } | S_DOUBLE { $$ = ECPGt_double; } | S_BOOL { $$ = ECPGt_bool; }; maybe_storage_clause : S_EXTERN { fwrite(yytext, yyleng, 1, yyout); } | S_STATIC { fwrite(yytext, yyleng, 1, yyout); } | S_SIGNED { fwrite(yytext, yyleng, 1, yyout); } | S_CONST { fwrite(yytext, yyleng, 1, yyout); } | S_REGISTER { fwrite(yytext, yyleng, 1, yyout); } | S_AUTO { fwrite(yytext, yyleng, 1, yyout); } | /* empty */ { }; index : '[' length ']' { $$ = $2; } | '[' ']' { $$ = 0; } length : S_LENGTH { $$ = atoi(yytext); } sqlinclude : SQL_START SQL_INCLUDE { fprintf(yyout, "#include \""); } filename SQL_SEMI { fprintf(yyout, ".h\""); output_line_number(); }; filename : cthing | filename cthing; sqlconnect : SQL_START SQL_CONNECT { fprintf(yyout, "ECPGconnect("); } db_name SQL_SEMI { fprintf(yyout, ");"); whenever_action();} db_name : SQL_STRING { fprintf(yyout, "\""); fwrite(yytext + 1, yyleng - 2, 1, yyout); fprintf(yyout, "\""); } | ':' symbol { /* check if we have a char variabnle */ struct variable *p = find_variable($2); enum ECPGttype typ = p->type->typ; /* if array see what's inside */ if (typ == ECPGt_array) typ = p->type->u.element->typ; if (typ != ECPGt_char && typ != ECPGt_unsigned_char) yyerror("invalid datatype"); fprintf(yyout, "%s", $2); } /* Open is an open cursor. Removed. */ sqlopen : SQL_START SQL_OPEN sqlgarbage SQL_SEMI { output_line_number(); }; sqlgarbage : /* Empty */ | sqlgarbage sqlanything; sqlcommit : SQL_START commit_release SQL_SEMI { fprintf(yyout, "ECPGcommit(__LINE__);"); whenever_action(); } commit_release : SQL_COMMIT | SQL_COMMIT SQL_RELEASE | SQL_COMMIT SQL_WORK SQL_RELEASE; sqlrollback : SQL_START SQL_ROLLBACK SQL_SEMI { fprintf(yyout, "ECPGrollback(__LINE__);"); whenever_action(); }; sqlexecute : SQL_START { /* Reset stack */ reset_variables(); fprintf(yyout, "ECPGdo(__LINE__, \""); } SQL_EXECUTE SQL_IMMEDIATE sqlstatement_words SQL_SEMI { /* Dump */ fprintf(yyout, "\", "); dump_variables(argsinsert); fprintf(yyout, "ECPGt_EOIT, "); /* dump_variables(argsresult); output variables must not exist here */ fprintf(yyout, "ECPGt_EORT );"); whenever_action(); }; sqlwhenever : SQL_START SQL_WHENEVER SQL_SQLERROR action SQL_SEMI{ when_error = $4; } | SQL_START SQL_WHENEVER SQL_NOT_FOUND action SQL_SEMI{ when_nf = $4; } action : SQL_BREAK { $$.code = W_BREAK; $$.str = NULL; } | SQL_CONTINUE { $$.code = W_CONTINUE; $$.str = NULL; } | SQL_SQLPRINT { $$.code = W_SQLPRINT; $$.str = NULL; } | SQL_GOTO label { $$.code = W_GOTO; $$.str = $2; } | SQL_GOTO symbol { $$.code = W_GOTO; $$.str = $2; } | SQL_DO symbol '(' { do_str = (char *) mm_alloc(do_length = strlen($2) + 4); sprintf(do_str, "%s (", $2); } vartext ')' { do_str[strlen(do_str)+1]='\0'; do_str[strlen(do_str)]=')'; $$.code = W_DO; $$.str = do_str; do_str = NULL; do_length = 0; } label : S_LABEL { char * name = (char *)malloc(yyleng + 1); strncpy(name, yytext, yyleng); name[yyleng] = '\0'; $$ = name; } sqlstatement : SQL_START { /* Reset stack */ reset_variables(); fprintf(yyout, "ECPGdo(__LINE__, \""); } sqlstatement_words SQL_SEMI { /* Dump */ fprintf(yyout, "\", "); dump_variables(argsinsert); fprintf(yyout, "ECPGt_EOIT, "); dump_variables(argsresult); fprintf(yyout, "ECPGt_EORT );"); whenever_action(); }; sqlstatement_words : sqlstatement_word | sqlstatement_words sqlstatement_word; sqlstatement_word : ':' symbol { add_variable(&argsinsert, find_variable($2)); fprintf(yyout, " ;; "); } | SQL_INTO into_list { } | sqlanything { fwrite(yytext, yyleng, 1, yyout); fwrite(" ", 1, 1, yyout); } | SQL_INTO sqlanything { fprintf(yyout, " into "); fwrite(yytext, yyleng, 1, yyout); fwrite(" ", 1, 1, yyout); }; into_list : ':' symbol { add_variable(&argsresult, find_variable($2)); } | into_list ',' ':' symbol{ add_variable(&argsresult, find_variable($4)); }; cthing : canything { fwrite(yytext, yyleng, 1, yyout); } canything : both_anything | SQL_INTO | ';'; sqlanything : both_anything; both_anything : S_LENGTH | S_VARCHAR | S_VARCHAR2 | S_LONG | S_SHORT | S_INT | S_CHAR | S_FLOAT | S_DOUBLE | S_BOOL | SQL_OPEN | SQL_CONNECT | SQL_STRING | SQL_BEGIN | SQL_END | SQL_DECLARE | SQL_SECTION | SQL_INCLUDE | S_SYMBOL | S_LABEL | S_STATIC | S_EXTERN | S_AUTO | S_CONST | S_REGISTER | S_STRUCT | '[' | ']' | ',' | '=' | '*' | '(' | ')' | S_ANYTHING; blockstart : '{' { braces_open++; fwrite(yytext, yyleng, 1, yyout); } blockend : '}' { remove_variables(braces_open--); fwrite(yytext, yyleng, 1, yyout); } %% static void yyerror(char * error) { fprintf(stderr, "%s in line %d\n", error, yylineno); exit(1); }