paule22 Geschrieben 26. Juli 2002 Teilen Geschrieben 26. Juli 2002 Hi C coders 4, I try to program a interpreter and have the problem of a IF - ELSE - ENDIF statement. here is the pseudo code: Note: if_stmt = 0 stands for execute flag for the following IF statement, 0 for ELSE - not true. stmt[pos] is a variable that store, if the code block is true for EXEC - YES or NO code in two brackets [ ] are code that should be handle by the program if a token like ENDIF is reading from the file. IF <var1> = 1 Â if_pos++ Â stmt[if_pos] = YES_EXEC Â if_stmt = 0 Â IF <var2> = 1 Â if_pos++ Â stmt[if_pos] = YES_EXEC Â Â ELSE stmt[if_pos] = NO_EXEC if_stmt = 1 // here comes a second problem: // IF var2 not 1 // than if_stmt must be true, also // if_stmt = 0 IF <var3> = 1 if_pos++; stmt[if_pos] = YES_EXEC if_stmt = 0 ELSE stmt[if_pos} = NO_EXEC if_stmt = 1 ENDIF [ if_pos-- ] ENDIF [ if_pos-- ] ELSE stmt[if_pos] = NO_EXEC if_stmt = 1 ENDIF [ if_pos-- ] // ------------------------------------------------------------------------- ----- // no more if statement - set the execution flag to true: 0 // ------------------------------------------------------------------------- ----- if (if_pos == 0) { if_stmt = 0; } next stage of pharse: // --------------------------------------------------------------- // here comes code that can be appear in a IF // statement block or outside ... // --------------------------------------------------------------- switch (get_token()) { case DT_PRINT: { handle_string(); if (if_stmt == 0) Memo->Lines->Add( parsed_token_string ); else if (if_stmt == 1) { /* do nothing - no output */ } } break; } here is the handle procedure that should handle the IF block: (raw code) please correct it ... // global values ... #define YES_EXEC 0 #define NO_EXEC 1 int if_pos = 0; int stmt[2048]; int handle_procedure(void) { int count_if_statments = 0; for (; { switch (get_token()) { case DT_PRINT: { handle_string(); if (if_stmt == 0) Memo->Lines->Add( parsed_token_string ); } break; case DT_ENDIF: { if (count_if_statments == 0) return fatal("ENDIF without IF-Block start"); if_pos--; count_if_statments--; // no more IF blocks, enable exec if (if_pos == 0) { if_stmt = 0; } } break; case DT_ELSE: { // here comes the problem ... // what is, when the previous IF check is false ?? // and vice versa true ?? // if it true, than must the next IF in the ELSE block // false, too // Also so: // // var1 = 2 // var2 = 4 // IF <var1> = 3 // ELSE // IF <var2> = 4 // ELSE // ENDIF // ENDIF and so on // if (stmt[if_pos] == YES_EXEC) if_stmt = 1; else if_stmt = 0; } break; case DT_IF: { count_if_statements++; if_pos++; handle_if_statment(); } break; case DT_RETURN: return RET_OK; // to leave the for-loop break; } } } any help are very welcome psst: please tell me your money account to sent you 5 Dollars for solving this problem :-) okay, joke, but not impossible :-) If you would like to have the working code for "get_token()" -- let me know. thanks forward Paul okay, here comes the get_token() code, to make it easier to following the example code ... FILE *input_file; AnsiString token_buffer; #define ENDFILE -1 #define IDENTIFIER 1 #define NUMBER 2 #define POINT_TOKEN 100 #define EQUALSIGN 124 #define MINUS 125 #define ILLEGAL 140 #define DT_PRINT 1000 #define DT_IF 1001 #define DT_ENDIF 1002 #define DT_ELSE 1003 // ------------------------------------------------------------------------- -- // get one charachter an handle comments in a dbase file ... // ------------------------------------------------------------------------- -- int skip_white_space(void) { AnsiString buffer; char c; char inside; c = fgetc(input_file); for (; { int cplus_comment; switch © { case '/': c = fgetc(input_file); // -------------------------------------------- // at back of a comment can be a // nummeric operator ... // -------------------------------------------- if (c >= '0' && c <= '9' || c == '(' || c == ' ' || c == '\n') { ungetc(c,input_file); return '/'; } else if (c != '*' && c != '/') { AnsiString str; str = "undefined char `/"; str += c; str += "' found"; return fatal(str); } cplus_comment = (c == '/'); c = fgetc(input_file); inside = 1; while (inside) { if (!cplus_comment && c == '*') { while (c == '*') c = fgetc(input_file); if (c == '/') { inside = 0; c = fgetc(input_file); } } else if (c == '\n') { line_no++; if (cplus_comment) inside = 0; c = fgetc(input_file); } else if (c == EOF) return fatal("comment line not correctly end."); else c = fgetc(input_file); } break; case '\n': line_no++; case ' ': case '\t': case '\f': c = fgetc(input_file); break; default: return ©; } } } int lex(void) { char buffer[5]; int pos = 0; int c; c = skip_white_space(); switch © { case EOF: { return (ENDFILE); } break; case '.': return POINT_TOKEN; case '?': return DT_PRINT; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case '_': { sprintf(buffer,"%c",c); token_buffer = buffer; while (isalnum© || c == '_') { if (pos) { sprintf(buffer,"%c",c); token_buffer += buffer; } c = fgetc(input_file); pos++; } ungetc(c, input_file); return (IDENTIFIER); } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { int token_val = 0; token_value = 0; while (isdigit© || c == '.') { if (c == '.') { token_val = 0; c = fgetc(input_file); while (isdigit©) { token_val = token_val*10 + c - '0'; c = fgetc(input_file); } goto n2; } else { token_value = token_value*10 + c - '0'; c = fgetc(input_file); } } n2: AnsiString str; str = token_value; str += ","; str += token_val; double wert = StrToFloat(str); token_value = wert; ungetc(c, input_file); return (NUMBER); } break; case '=': { return EQUALSIGN; } break; case '-': { return '-'; } break; case '*': { return '*'; } break; case '/': { return '/'; } break; case ';': { return ';'; } break; case '(': { return '('; } break; case ')': { return ')'; } break; case '>': { return '>'; } break; case '<': { return '<'; } break; default: { return c; } } return ILLEGAL; } struct DT_variables { char *name; int id; } dt_vars[] = { "if", DT_IF, "endif", DT_ENDIF, "else", DT_ELSE, 0,0 }; // ------------------------------------------------------------------------- --- // get token, and return an id number of it ... // ------------------------------------------------------------------------- --- int get_token(void) { int code; for (; { code = lex(); if (code == IDENTIFIER || code == DT_PRINT) { if (code == DT_PRINT) return DT_PRINT; int c; for (c = 0; c < MAX_TOKEN; c++) { if (LowerCase(token_buffer) == dt_vars[c].name) return dt_vars[c].id; } return DT_USERDEF; } else if (code == NUMBER) { return fatal("expected a string-Token here."); } else if (code == ENDFILE) { return ENDFILE; } } return RET_OK; } hoffentlich kann sich da einer reinwusseln ? thanks for help Paul Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
gajUli Geschrieben 27. Juli 2002 Teilen Geschrieben 27. Juli 2002 Mit welchem Ziel soll sich denn da jemand "reinwusseln"? Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
nic_power Geschrieben 27. Juli 2002 Teilen Geschrieben 27. Juli 2002 From my understanding he want's to write a parser which is able to parse the code fragment (IF - ELSE - ENDIF) mentioned above. Which obviously does not work as expected . To be honest I'm not keen on going through the code but I can give an recommendation for an optional (and for Unix users straight forward) implementation: Use lex and yacc (or flex and bision) especially if you plan to extend your program to understand more keywords. lex/flex is a tool for generating scanners based on a rule file where a rule is a combination of and regular expression and some C/C++ code. yacc/bison are used to generate parsers. Both tools generate C code. For simple tasks the use of bison should be sufficient without any need for flex. I think one of the problems in your code is that there is no destinction between code which belongs to the "IF" statement and code which is evaluated within the "IF" statement. Your parser does not no when to start with the calculation of the result of the "IF". This is the reason why most language use something like "then" or ";" to indicate the end of the comparison and easy to fix: IF <statement> THEN [... do something ] ELSE [... do something else ] ENDIF Nic PS: Answer in english because initial question was also in english. Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
Crush Geschrieben 27. Juli 2002 Teilen Geschrieben 27. Juli 2002 Without a correct syntax the compiler don´t wanna run it. IF <var3> = 1 // should this be a template? Where´s the Macro for IF? stmt[if_pos] = YES_EXEC // as Nic told-> no ; in sight [ if_pos-- ] // a strange kind of Field-Index without array and ;? return fatal("ENDIF without IF-Block start"); // is fatal an own String-Generator? Why not returning the String itself? if (stmt[if_pos] == YES_EXEC) if_stmt = 1; else // no good style if_stmt = 0; handle_if_statment(); // one line before you use the expression if_statement with 'e'-> My advice: Don´t use similar names if possible Except of these small hints I would say your code is a good example why people get frightened of going into C. I think it´s nearly impossible to draw the teeth without knowing which one hurts or not... we could choose all - but then there´s nothing to byte any more. Where´s the detailed question? Is this some strange kind of test? Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
gajUli Geschrieben 27. Juli 2002 Teilen Geschrieben 27. Juli 2002 Answer in english because initial question was also in english. Nope. Eine "initial question" gab es gar nicht, und am Ende stand noch dazu ein deutscher Satz. Also halt mal lieber die Baelle flach. Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
nic_power Geschrieben 27. Juli 2002 Teilen Geschrieben 27. Juli 2002 Original geschrieben von gajUli Nope. Eine "initial question" gab es gar nicht, und am Ende stand noch dazu ein deutscher Satz. Stimmt, da hast Du recht. Im Ursprungsposting wurde keine Frage gestellt. So gesehen koennen wir eigentlich nur raten worum es geht (sofern die Frage nicht gestellt/praezisiert wird). Den deutschsprachigen Satz am Ende habe ich natuerlich auch gesehen. Allerdings hoffe ich, dass es einen triftigen Grund fuer die Wahl der Sprache gibt (und das es nicht blosse Faulheit war, den Beitrag ins deutsche zu uebersetzen), daher die Antwort auf englisch. Nic Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
paule22 Geschrieben 27. Juli 2002 Autor Teilen Geschrieben 27. Juli 2002 moin, blose faulheit, okay, ja habt ja recht. das gleiche posting hatte ich in einen englischen forum gepostet und wollte mir die mühe ersparren sorry leute. ist doch trotzdem mal ne abwechslung im tristen alltag - oder ? was Linux/Unix anbelangt - das Ding mit Lex/Yacc und Bison - hab ich mal probiert auf DOS, klappt auch soweit recht gut mit den GNU GCC Tools, allerdings verwende ich den Borland CBuilder und da kommt es zu komplikationen beim code übersetzten. Wäre vielleicht mal ne interessante Aufgabe für jemanden, diese teile - also Lex/Bison für Borland produkte gangfähig zu machen - würde mich auch erkenntlich zeigen :-) also wie gesagt, ich versuche einen interpreter zu schreiben, der IF ELSE ENDIF auswerten kann. allerdings scheitert die ganze sache nicht am eigentlichen auslesen von IF ELSE ENDIF, sondern darin, dass, wenn ein ELSE oder ein ENDIF innerhalb eines IF Blocks vorkommt. dann verwende ich nämlich folgenden Code: switch(get_token()) { case DT_ENDIF: if (!if_rec) fatal("ENDIF ohne IF-Bedingung nicht erlaubt."); if_rec--; if (!if_rec) if_stmt = 0; else { for (int pos = 0; pos < if_rec; pos++) if (exec_if_stmt[pos] == YES_EXEC) if_stmt = 0; } break; case DT_ELSE: if (if_rec > 0) if (exec_if_stmt[if_rec] == YES_EXEC) if_stmt = 0; else if_stmt = 1; break; case DT_IF: if_rec++ handle_if_statment(); break; } und nun der code zum parsen der IF anweisung ... handle_if_statement() { AnsiString saved_token; switch (get_token()) { case DT_USERDEF: { saved_token = AnsiLowerCase(token_buffer); AnsiString str; str = "procedure_local_"; str += saved_token; if (can_exec && !if_stmt) { if (!(var_ptr = get_variable(str))) fatal("IF-Variable ist nicht definiert/bekannt."); int code = lex(); if (code != EQUALSIGN) fatal("Gleichheitszeichen '=' hier erwartet."); // ------------------------ // IF <variable> = EXPR ... // ------------------------ if (var_ptr->type == VARTYPE_VALUE) { double wert1 = var_ptr->var_value; double wert2 = handle_expr(EP_AND); if (wert1 == wert2) { ShowMessage("wert ist gleich"); exec_if_stmt[if_rec] = YES_EXEC; if_stmt = 0; return RET_OK; } else { ShowMessage("wert ist ungleich nicht gleich nix gleich"); exec_if_stmt[if_rec] = NO_EXEC; if_stmt = 1; return RET_OK; } } } else { int code = lex(); if (code != EQUALSIGN) fatal("Gleichheitszeichen '=' hier erwartet."); next_t: double wert2 = handle_expr(EP_AND); if_stmt = 1; exec_if_stmt[if_rec] = NO_EXEC; return RET_OK; } } break; default: fatal("Benutzerdefiniertes token hier erwartet."); break; } } der code funktioniert einwandfrei - auch die Auswertung des IF ELSE ENDIF Blocks, sofern es immer nur einer is. Aber was is, wenn mehrere IF-ELSE-ENDIF blöcke geschachtelt werden ?? dafür brauche ich ne Lösung. so das wars eigentlich. Paul Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
gajUli Geschrieben 27. Juli 2002 Teilen Geschrieben 27. Juli 2002 Aha. - Riecht ganz verdaechtig stark nach einem typischen Anwendungsfall fuer eine rekursive Funktion! Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
paule22 Geschrieben 27. Juli 2002 Autor Teilen Geschrieben 27. Juli 2002 und wie rekursive ich die Funktion mit den bestehenden Code am besten ?? Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
Crush Geschrieben 27. Juli 2002 Teilen Geschrieben 27. Juli 2002 Laß die Funktion sich selbst aufrufen mit dem String gekürzt um den vorher abgearbeiteten Teil oder zähle einfach die Ifs entsprechend mit. Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
paule22 Geschrieben 27. Juli 2002 Autor Teilen Geschrieben 27. Juli 2002 jo danke crush, die IFs zähle ich mit. hab mal daran gedacht ne struktur mit in spiel zu bringen etwa so: #define NO_EXEC 1 #define YES_EXEC 0 struct if_stmt_block { char yes; char no; } struc if_stmt_block stmt[2048]; wenn .yes gesetzt ist mit YES_EXEC und .no = NO_EXEC, dann normalen IF block abarbeiten ansonsten (.yes = NO_EXEC und .no = YES_EXEC - dann ELSE block abarbeiten) dann schaut in etwa der pseudo code so aus, der abgearbeitet werden muss - irgendwie - halt mal ne neue idee: if_rec = 0; IF <var1> # 2      // if zählen ...      if_rec++;      // dieser Pseudo code muss natürlich erstmal auf korrektheit      // überprüft werden - siehe handle_if_statemnt()      //      if (var1 ist ungleich 2)      dann         stmt[0].yes = YES_EXEC;         stmt[0].no = NO_EXEC;     // ELSE block nicht beachten      ansonsten         stmt[0].yes = NO_EXEC;         stmt[0].no = YES_EXEC;     // ELSE block abarbeiten      end      IF <var2> = 2      if_rec++;      // hier muss überprüft werden, ob der momentane CodeBlock      // abgearbeitet werden kann - siehe .yes .no          stmt[1].yes = YES_EXEC;          stmt[1].no = NO_EXEC;      ENDIF      // nach jedem ENDIF, if_rec runterschrauben      if_rec--;      // hier muss auch/nochmals überprüft werden, ob der momentane CodeBlock      // abgearbeitet werden kann - könnte ja sein, das hinter dem      // ENDIF weitere Anweisungen stehen ENDIF if_rec--; // ab hier gehts dann normal weiter if (if_rec == 0) ..... muss halt nur noch eine entsprechende routine for jedem DT_IF und DT_ENDIF und natürlich auch dT_ELSE, die überprüft, ob die bedingungen zutreffen. dann wäre eigentlich das Problem gelöst ? denke ich jedenfalls. in der vorliegenden Pseudo form können IF's maximal 2047 mal verschachtelt sein - man könnte des auch dynamisch machen um auf unbegrenzt (gut 2 Millarden etwa hochschrauben kann. Aber der übersichtlichkeit halber hab ich des mal statisch gelassen. jede idea des händlings ist willkommen !!! vielleicht is des auch ne anregung für jemand anderen, der das gleiche Problemchen hat ... Paul Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
Crush Geschrieben 27. Juli 2002 Teilen Geschrieben 27. Juli 2002 Solltest Du das doch mal rekursiv probieren mußt Du alles was in der inneren Schleife abgearbeitet wird vom String abschneiden und diesen wieder an die äußere aufrufende Funktion zurück geben. Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
paule22 Geschrieben 27. Juli 2002 Autor Teilen Geschrieben 27. Juli 2002 jo danke für dein Hinweis. das Problem was bei diesen Interpreter ist, die Zeichenketten werden linear abgearbeitet - wie eine BATCH datei. Ansonsten müsste ich mir was anderes überlegen - sowas wie ne STACK machine oder so was in der art ? erst den gesammten code einlesen in nen string und dann den string (Stack) abarbeiten. Ist das net doppelt gemoppelt ? Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
Crush Geschrieben 27. Juli 2002 Teilen Geschrieben 27. Juli 2002 Leider ist das anders nicht machbar. Arbeite doch einfach mit einem String-Zeiger und den reichst Du immer nur weiter und paßt ihn jeweils an. Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
paule22 Geschrieben 28. Juli 2002 Autor Teilen Geschrieben 28. Juli 2002 juhu, ich haps hinbekommen und des ohne STACK automat - bloss ne strucktur geschrieben (hab ich ja schon mal erwähnt) und nen paar IF anweisungen im code und schon klappt das hervorragend danke an alle, die mir denkanstösse gegeben haben. Paul Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
Empfohlene Beiträge
Dein Kommentar
Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.