This is COWSInterpreter.m in view mode; [Download] [Up]
/* Copyright (C) 1994 Sean Luke COWSInterpreter.m Version 1.4 Sean Luke */ #import "COWSInterpreter.h" #import "COWSSymbolNode.h" #import "COWSLibraryFunctionNode.h" #import "COWSStateNode.h" #import "COWSLibrary.h" #import "COWSArgumentList.h" // Convenience String Functions // These functions are used by the interpreter and by other objects // (most notably COWSStringNode) to generate new strings from old ones. char* newstr(const char* ct) /* New-string version of strcpy. Assumes ct is properly \0-terminated. Allocates enough memory (1 more than length of ct), then copies ct into the new memory area. Returns a pointer to the new area. */ { char* t=malloc(strlen(ct)+1); strcpy(t,ct); return t; } char* newstrn(const char* ct,int size) /* New-string version of strncpy. Allocates enough memory (1 more than length of ct), then copies size characters of ct into the new memory area, tacking on a \0 at the end to complete the new string. Returns a pointer to the new area. */ { char* t=malloc(size+1); strncpy(t,ct,size); t[size]='\0'; return t; } // Timed Entry DPSTimedEntryProc _COWS_timer (DPSTimedEntry teNum, double now, void* interpreter) { COWSInterpreter* the_interpreter=(COWSInterpreter*) interpreter; [the_interpreter _go]; return (void*) NULL; } @implementation COWSInterpreter - init { inited=YES; program=[[COWSStringNode alloc] init]; stack=[[COWSStack alloc] init]; running=NO; foreground=NO; locked=NO; printing_errors=YES; delegate=NULL; tempDelegate=NULL; function_completed=NO; repeats=100; teNum=0; te_speed=0.1; current_dictionary=NULL; current_function=[[COWSStringNode alloc] init]; current_position=0; library_dictionary=[[HashTable alloc] initKeyDesc:"*"valueDesc:"@"]; function_dictionary=[[HashTable alloc] initKeyDesc:"*"valueDesc:"@"]; global_dictionary=[[HashTable alloc] initKeyDesc:"*"valueDesc:"@"]; library_delegates=[[COWSArgumentList alloc] init]; return [super init]; } - awake { if (!inited) return [self init]; return self; } - printDictionaries:sender // prints each dictionary. Internal function dictionary is printed along with // variables and arguments for each function. { const void * key; const char *value; id value_node; NXHashState state=[global_dictionary initState]; printf ("GLOBAL VARIABLES\n\n"); while ([global_dictionary nextState: &state key: &key value: (void*) &value_node]) { value=[value_node string]; printf ("KEY: #%s#\nVALUE: #%s#\n",(char*) key, (const char*) value); } state=[function_dictionary initState]; printf ("\n\nINTERNAL FUNCTIONS\n\n"); while ([function_dictionary nextState: &state key: &key value: (void*) &value_node]) { value=[value_node string]; printf ("KEY: #%s#\nVALUE: #%s#\n",(char*) key, (const char*) value); [value_node printContents]; } state=[library_dictionary initState]; printf ("\n\nLIBRARY FUNCTIONS\n\n"); while ([library_dictionary nextState: &state key: &key value: (void*) &value_node]) { printf ("KEY: #%s#\n",(char*) key); } printf ("\n\nLIBRARY DELEGATES\n\n"); while ([library_dictionary nextState: &state key: &key value: (void*) &value_node]) { printf ("KEY: #%s#\n",(char*) key); } printf ("\n\n"); return self; } - printProgram:sender // prints the current COWS program. { printf ("PROGRAM\n\n#%s#\n\n",[program string]); return self; } - (int) setProgram:(const char*) this_string // clears out dictionaries, sets up COWS Program, and interprets it. // returns an error code if unable to at least initially parse the // program enough to break it into functions and global variables // for storage in dictionaries. Must also stop any currently running // COWS program. { int error; // zeroth, stop any current program... [self stopInterpreting]; // first, clear out dictionaries [function_dictionary freeObjects]; [global_dictionary freeObjects]; [function_dictionary empty]; [global_dictionary empty]; [program setString:this_string]; // Sets up program error=[self _program:[program string]:0]; // Uses the new program string instead of this_string for safety. return error; } - addLibrary:this_library { if (this_library!=NULL) //if ([this_library conformsTo:@protocol(InterpreterToLibrary)]) { [this_library loadLibrary:self]; return self; } return NULL; } - makeMeALibraryDelegate:sender { id temp=[[COWSLibraryNode alloc] init]; [temp setTarget:sender]; [library_delegates push:temp]; return self; } - clearLibraryFunctions // clears out all library functions. Must stop any current program. { [self stopInterpreting]; [library_dictionary freeObjects]; [library_dictionary empty]; [library_delegates clear]; return self; } - setTimedEntrySpeed:(float) this_speed { if (this_speed>0) te_speed=this_speed; return self; } - (float) timedEntrySpeed { return te_speed; } - addLibraryFunction:(const char*) this_name selector: (SEL) this_selector target: this_target // adds a library function to the library dictionary. { id new_node=[[COWSLibraryFunctionNode alloc] init]; [new_node setTarget:this_target]; [new_node setSelector:this_selector]; [library_dictionary insertKey:(const void*) this_name value:(void*)new_node]; return self; } - interpretFunction:(const char*) this_name arguments:(COWSArgumentList*)these_arguments // Starts interpreter up. Currently, the interpreter is _not_ // reentrant, so please don't call this function if the interpreter's // not stopped. It is your responsibility to free your own argument // list after calling this method. { COWSStringNode* temp=[[COWSStringNode alloc] init]; if (working) // i.e., already working on something { [temp free]; if (printing_errors) printf ("Whoops! Already working on something! Can't do %s\n",this_name); return NULL; } running=YES; working=YES; // remove old timed entry if any, then install new one. if (teNum) DPSRemoveTimedEntry(teNum); if (!foreground) // else, don't need a timed entry teNum=DPSAddTimedEntry(te_speed, (DPSTimedEntryProc) _COWS_timer, (void*) self, (int) NX_RUNMODALTHRESHOLD); // then clean out stack [stack clear]; if (tempDelegate&&[tempDelegate respondsTo:@selector(interpreterStarted:)]) [tempDelegate interpreterStarted:self]; if (delegate&&[delegate respondsTo:@selector(interpreterStarted:)]) [delegate interpreterStarted:self]; [library_delegates first]; while ([library_delegates now]) { id targ=[[library_delegates now] target]; if (targ&&[targ conformsTo:@protocol(InterpreterToLibraryDelegate)]) [targ interpreterDidStart:self]; [library_delegates next]; } [temp setString:this_name]; [self _executeProgram:these_arguments:temp]; return [self _go]; // this is just "one try" in the background // version, but the whole program execution // in the foreground version... } - stopInterpreting // Stops interpreter. This could happen from outside, if the user // wants to cancel the interpreter, or from inside, if the interpreter // is finished and wants to clean up. { running=NO; working=NO; function_completed=NO; if (teNum) DPSRemoveTimedEntry(teNum); teNum=0; [current_function setString:""]; // since it's just a copy of a dictionary current_position=0; if (tempDelegate&&[tempDelegate respondsTo:@selector(interpreterStopped:)]) [tempDelegate interpreterStopped:self]; if (delegate&&[delegate respondsTo:@selector(interpreterStopped:)]) [delegate interpreterStopped:self]; [library_delegates first]; while ([library_delegates now]) { id targ=[[library_delegates now] target]; if (targ&&[targ conformsTo:@protocol(InterpreterToLibraryDelegate)]) [targ interpreterDidStop:self]; [library_delegates next]; } return self; } - pauseInterpreting // Pauses interpreter. Why would you want to do this? In case you had // a reentrant library function, which needed more information from a // COWS function, you could simulate reentrance by pausing this // interpreter, instantiating another, feeding that one the function // request, getting a response back, destroying it, and resuming // this interpreter with the appropriate value (to push on the stack). { if (foreground) return NULL; // this function should not work // in foreground mode! if (teNum) DPSRemoveTimedEntry(teNum); teNum=0; running=NO; return self; } - resumeInterpreting // Resumes the interpreter. This would only be used in the rare case that // you're still in the scope of a function, but need to start the // interpreter back up. For example, the NXRunAlertPanel function // reenters the App's main event loop and starts up event servicing before // returning an answer for the panel. Thus a COWS library function using // the NXRunAlertPanel function might accidentally allow the interpreter // to process further down the line (timed entries are events, y'know), // before returning its answer! This would, as you might guess, create // a real mess. The solution is to surround the NXRunaAlertPanel call // with pauseInterpreting and resumeInterpreting functions, which remove // and replace the timed entry. { if (foreground) return NULL; // this function should not work // in foreground mode! if (teNum) DPSRemoveTimedEntry(teNum); teNum=DPSAddTimedEntry(te_speed, (DPSTimedEntryProc) _COWS_timer, (void*) self, (int) NX_RUNMODALTHRESHOLD); running=YES; // added May 11. How did I blow this? return self; } - resumeInterpretingWithValue:(COWSStringNode*) this_value // See pauseInterpreting above. // resumeInterpretingWithValue is used when you need to release control // of the app but don't have an answer for the interpreter yet. For // example, when the IPCLibrary receives a request to call a remote // application's function and get an answer back, to be safe it must // call the application's function and drop out, allowing the first // app to continue while waiting for the answer back through a timed-entry // pestering-function (to avoid true busy-waiting). But if it releases // control, the interpreter will start up again. So it must pause the // interpreter, release control, and when a timed entry calls it again, // start up the interpreter with the proper answer. // it is the responsibility of the calling function to destroy this_value { id push_val; if (foreground) return NULL; // this function should not work // in foreground mode! if (running) return NULL; // can't resume if not paused! if (!working) return NULL; // can't resume if stopped! push_val=[[COWSStringNode init] alloc]; [push_val copyValue:this_value]; [push_val setError:[this_value error]]; if ([push_val error]) { [self _error:COWSLIBRARYFUNCTIONERROR:"":current_position:[push_val string]]; [push_val free]; return NULL; } if (working) // not stopped-- note that this is different from library function calling { [stack push:push_val]; // remove old timed entry if any, then install new one. if (teNum) DPSRemoveTimedEntry(teNum); teNum=DPSAddTimedEntry(te_speed, (DPSTimedEntryProc) _COWS_timer, (void*) self, (int) NX_RUNMODALTHRESHOLD); running=YES; // added May 11. How did I blow this? [self _doKeywords]; } return self; } - free // frees interpreter, all dictionaries, and stack. Removes timed entry. { running=NO; working=NO; function_completed=NO; if (teNum) DPSRemoveTimedEntry(teNum); teNum=0; if (tempDelegate&&[tempDelegate respondsTo:@selector(interpreterStopped:)]) [tempDelegate interpreterStopped:self]; if (delegate&&[delegate respondsTo:@selector(interpreterStopped:)]) [delegate interpreterStopped:self]; [library_delegates first]; while ([library_delegates now]) { id targ=[[library_delegates now] target]; if (targ&&[targ conformsTo:@protocol(InterpreterToLibraryDelegate)]) [targ interpreterDidStop:self]; [library_delegates next]; } [function_dictionary freeObjects]; [global_dictionary freeObjects]; [library_dictionary freeObjects]; [function_dictionary empty]; [global_dictionary empty]; [library_dictionary empty]; [function_dictionary free]; [global_dictionary free]; [library_dictionary free]; [library_delegates free]; [stack free]; return [super free]; } - setTempDelegate:this_delegate { // sets the delegate for the delegate message below. The // delegate receives this message when the interpreter is done // performing an interpretFunction:arguments: loop. tempDelegate=this_delegate; return self; } - tempDelegate // returns the current delegate { return tempDelegate; } - setDelegate:this_delegate { delegate=this_delegate; return self; } - delegate { return delegate; } - setRepeats:(int) this_number { // sets the number of operations the interpreter will attempt to // do per timed entry. A higher number is more efficient, but // takes more control away from the user. if (this_number) { repeats=this_number; return self; } return NULL; } - (int) repeats // returns the number of operations the interpreter will attempt to // do per timed entry. A higher number is more efficient, but // takes more control away from the user. { return repeats; } - setForeground:(BOOL) yes_or_no { [self stopInterpreting]; // otherwise it could jump // into foreground mode with dire // consequences! foreground=yes_or_no; return self; } - (BOOL) foreground { return foreground; } - setLocked:(BOOL) yes_or_no { locked=yes_or_no; return self; } - (BOOL) locked { return locked; } - (BOOL) running { return running; } - (BOOL) working { return working; } // TOKENIZER // Chews through string starting at pos, placing the next token in string into // token, returning the new position in the string after token is out. Skips // white space, comments. Considers a "string" to be one token. Parentheses // are also tokens, as are keywords. Is not destructive to string. // Returns Error or next token position. - (int) _tokenize:(const char*) string:(int) pos:(COWSStringNode*) token { int x=pos; int length=strlen(string); int start; // Skip through white space and comments while (1) { if (x>=length) return COWSERRORNOMORETOKENS; if (string[x]=='[') { while (1) { x++; if (x>=length) return COWSERRORNOMORETOKENS; if (string[x]==']') break; } } else if (string[x]!=' '&&string[x]!='\t'&&string[x]!='\n') break; x++; } // Determine Type if (x>=length) return COWSERRORNOMORETOKENS; if (string[x]=='\"') { start=x; while (1) { x++; if (x>=length) return COWSERRORNOCLOSINGQUOTE; if (string[x]=='\"') break; } x++; [token setString:&string[start] size:x-start]; return x; } else if (string[x]=='('||string[x]==')') { [token setString:&string[x] size:1]; x++; return x; } else // symbol { start=x; while (1) { x++; if (x>=length) break; if (string[x]==' '||string[x]=='\t'||string[x]=='\n') break; if (string[x]=='['||string[x]=='\"') break; if (string[x]=='('||string[x]==')') break; } [token setString:&string[start] size:x-start]; return x; } } // RECURSIVE DECENT PARSER // Used to break up globals and functions into dictionaries // Entry is by passing _program the program string and 0 as a position // The parser is straight forward but fairly uncommented. Sorry! // variablelist loads globals into global_dictionary // localvariablelist loads variables into a local dictionary // argumentlist loads arguments into a local argument list // itemlist grabs the remainder of the function // functionform loads arguments from the argument list into the local // dictionary and puts the local dictionary and item list into // function_dictionary /* Parsing Grammar: NonTerminals: program :- <programlist> programlist :- EOF | <globalform> <programlist> | <functionform> <programlist> globalform :- ( <globalkeyword> <variablelist> ) functionform :- (function <functionname> <argumentlist> variable <localvariablelist> begin <itemlist> | (function <functionname> <argumentlist> begin <itemlist> variablelist :- <symbolname> <variablelist> | NULL localvariablelist :- <symbolname> <localvariablelist> | NULL argumentlist :- <symbolname> <argumentlist> | NULL itemlist :- <item>* ) <--note I use a while here. Less recursive. item :- <atom> | ( <itemlist> Terminals: globalkeyword is the word "variable" functionkeyword is the word "function" symbolname is any non-string, non-truth, non-number, non-keyword, non-delimiter atom is any non-delimiter */ - (int) _program:(const char*)string:(int)pos { return [self _programList:string:pos]; } - (int) _programList:(const char*)string:(int)pos { int rv; COWSStringNode* s=[[COWSStringNode alloc] init]; // NULL rv=[self _tokenize:string:pos:s]; if (rv==COWSERRORNOMORETOKENS) { [s free]; return COWSSUCCESS; } // <function-form> <program-list> rv=[self _functionForm:string:pos]; if (rv>=0) { rv=[self _programList:string:rv]; if (rv>=0) { [s free]; return rv; } } // <global-form> <program-list> rv=[self _globalForm:string:pos]; if (rv>=0) { rv=[self _programList:string:rv]; if (rv>=0) { [s free]; return rv; } } [s free]; return rv; } - (int) _functionForm:(const char*)string:(int)pos { int rv; int rvstart; COWSStringNode* s=[[COWSStringNode alloc] init]; id func=[[COWSStateNode alloc] init]; COWSArgumentList* arguments=[func arguments]; HashTable* variables=[func dictionary]; // (function <function-name> <argument-list> // variable <local-variable-list> begin <value-list>) // this version has both variables and arguments rv=[self _openParen:string:pos]; if (rv>=0) { rv=[self _functionKeyword:string:rv]; if (rv>=0) { rv=[self _symbolName:string:s:rv]; if (rv>=0) { rv=[self _argumentList:string:rv:arguments]; if (rv>=0) { // load argument list into dictionary as well! [arguments first]; while([arguments now]!=NULL) { const char* argstr=[[arguments now] string]; id var; if ([variables isKey:(const void*) argstr]) { [func free]; [s free]; return COWSERRORDUPLICATEARGUMENT; } var=[[COWSStringNode alloc] init]; [var setString:""]; [variables insertKey:newstr(argstr) value:var]; [arguments next]; } [arguments first]; // just in case rv=[self _variableKeyword:string:rv]; if (rv>=0) { rv=[self _localVariableList:string:rv:variables]; if (rv>=0) { rv=[self _beginKeyword:string:rv]; if (rv>=0) { rvstart=rv; rv=[self _itemList:string:rv]; if (rv>=0) { if ([function_dictionary isKey: (const void*) [s string]]) { [s free]; [func free]; return COWSERRORDUPLICATEFUNCTION; } [func setString:&string[rvstart] size:rv-rvstart]; [function_dictionary insertKey: newstr([s string]) value:func]; [s free]; return rv; } } } } } } } } [arguments clear]; [variables freeObjects]; [variables empty]; // (function <function-name> <argument-list> begin <value-list>) // this version has no variables rv=[self _openParen:string:pos]; if (rv>=0) { rv=[self _functionKeyword:string:rv]; if (rv>=0) { rv=[self _symbolName:string:s:rv]; if (rv>=0) { rv=[self _argumentList:string:rv:arguments]; if (rv>=0) { // load argument list into dictionary as well! [arguments first]; while([arguments now]!=NULL) { const char* argstr=[[arguments now] string]; id var; if ([variables isKey:(const void*) argstr]) { [func free]; [s free]; return COWSERRORDUPLICATEARGUMENT; } var=[[COWSStringNode alloc] init]; [var setString:""]; [variables insertKey:newstr(argstr) value:var]; [arguments next]; } [arguments first]; // just in case rv=[self _beginKeyword:string:rv]; if (rv>=0) { rvstart=rv; rv=[self _itemList:string:rv]; if (rv>=0) { if ([function_dictionary isKey: (const void*) [s string]]) { [s free]; [func free]; return COWSERRORDUPLICATEFUNCTION; } [func setString:&string[rvstart] size:rv-rvstart]; [function_dictionary insertKey: newstr([s string]) value:func]; [s free]; return rv; } } } } } } // otherwise... [s free]; [func free]; return rv; } - (int) _itemList:(const char*)string:(int)pos { int rv=pos; int temp; // an item list is lots of items ending with a )... while(1) { temp=[self _closeParen:string:rv]; if (temp>=0) return temp; //else... rv=[self _item:string:rv]; if (!(rv>=0)) return rv; } return rv; } - (int) _item:(const char*)string:(int)pos { int rv; // either an item is an atom... rv=[self _atom:string:pos]; if (rv>=0) return rv; // or it is a list of items beginning with a (... rv=[self _openParen:string:pos]; if (rv>=0) { rv=[self _itemList:string:rv]; return rv; } return rv; } - (int) _globalForm:(const char*)string:(int)pos { int rv; // (variable <variable-name>) rv=[self _openParen:string:pos]; if (rv>=0) { rv=[self _variableKeyword:string:rv]; if (rv>=0) { rv=[self _variableList:string:rv]; if (rv>=0) { rv=[self _closeParen:string:rv]; if (rv>=0) { return rv; } } } } return rv; } - (int) _variableList:(const char*) string:(int)pos { int rv; COWSStringNode* s=[[COWSStringNode alloc] init]; id var; // NULL variable lists always end with ")" rv=[self _tokenize:string:pos:s]; if (!strcmp([s string],")")) { [s free]; return pos; // note _not_ rv } // <variable-name> <variable-list> rv=[self _symbolName:string:s:pos]; // a variable is identical to a symbol if (rv>=0) { rv=[self _variableList:string:rv]; if (rv>=0) { var=[[COWSStringNode alloc] init]; [var setString:""]; [global_dictionary insertKey:newstr([s string]) value:var]; [s free]; return rv; } } [s free]; return rv; } - (int) _localVariableList:(const char*) string:(int)pos:(HashTable*) arguments { // arguments is hash table in which to place arguments int rv; COWSStringNode* s=[[COWSStringNode alloc] init]; id var; // NULL local variable lists always end with "do" rv=[self _tokenize:string:pos:s]; if (!strcmp([s string], "do")) { [s free]; return pos; // note _not_ rv } // <variable-name> <local-variable-list> rv=[self _symbolName:string:s:pos]; // a variable is identical to a symbol if (rv>=0) { rv=[self _localVariableList:string:rv:arguments]; if (rv>=0) { if ([arguments valueForKey:(const void*)[s string]]!=nil) { [s free]; return COWSERRORDUPLICATEVARIABLE; } var=[[COWSStringNode alloc] init]; [var setString:""]; [arguments insertKey:newstr([s string]) value:var]; [s free]; return rv; } } [s free]; return rv; } - (int) _argumentList:(const char*) string:(int)pos: (COWSArgumentList*) arguments { // arguments is an argument-list in which to place arguments int rv; COWSStringNode* s=[[COWSStringNode alloc] init]; const char* token; // NULL argument lists always end with "variable" or "do" rv=[self _tokenize:string:pos:s]; token=[s string]; if (!strcmp(token, "variable") || !strcmp(token, "do")) { [s free]; return pos; // note _not_ rv } // <argument> <argument-list> rv=[self _symbolName:string:s:pos]; // a variable is identical to a symbol if (rv>=0) { rv=[self _argumentList:string:rv:arguments]; if (rv>=0) { id arg=[[COWSStringNode alloc] init]; [arg copyValue:s]; [arguments push:arg]; [s free]; return rv; } } [s free]; return rv; } // TERMINAL HELPER FUNCTIONS // Help terminals decide if a string is what it's supposed to be. int anumber(const char* string) { // Numbers: // 1: May have 1 e, 1 ., and up to 2 +s or -s. // 2: May have any number of digits // 3: May have an e only after a digit // 4: May not have a . after an e or a . // 5: May have a + or - only at the beginning or after an e // 6: May not have any other characters but digits, +, -, ., or e // 7: Must have at least one digit // 8: Must have at least one digit after . or e // What a mess! Is there an easier way to parse numbers? /* int x=0; // assumes that numbers begin with a digit, or a -,+, or . (or ") if (string[x]=='\0') return 0; if (string[x]=='\"') x++; if (string[x]=='\0') return 0; if (string[x]=='-'||string[x]=='+') x++; if (string[x]=='\0') return 0; if (string[x]=='.') x++; if (string[x]=='\0') return 0; if (string[x]>=0x30&&string[x]<=0x39) return 1; return 0; */ int plusminusvalid=1; // can start with a plus or minus int evalid=0; // cannot start with an e int dotvalid=1; // can start with a . int plusminuscount=0; int ecount=0; int dotcount=0; int edigit=1; int dotdigit=1; int digitcount=0; int x; int len; if (string) // if string is not NULL { len=strlen(string); for (x=0;x<len;x++) { if (string[x]>=0x30&&string[x]<=0x39) // a digit { plusminusvalid=0; // + or - only valid at beginning or after e digitcount++; evalid=1; dotdigit=1; edigit=1; } else if (string[x]=='.') { if (!dotvalid) return 0; if (dotcount) return 0; // only one dot plusminusvalid=0; // + or - only valid at beginning or after e evalid=0; dotvalid=0; // dots are not valid after a dot dotcount++; dotdigit=0; } else if (string[x]=='-') { if (!plusminusvalid) return 0; if (plusminuscount==2) return 0; // only one plusminus evalid=0; plusminusvalid=0; // + or - only valid at beginning or after e plusminuscount++; } else if (string[x]=='+') { if (!plusminusvalid) return 0; if (plusminuscount==2) return 0; // only one plusminus evalid=0; plusminusvalid=0; // + or - only valid at beginning or after e plusminuscount++; } else if (string[x]=='e') { if (!dotdigit) return 0; // e encountered before dot digit if (!evalid) return 0; if (ecount) return 0; // only one e plusminusvalid=1; // +/- is valid after e dotvalid=0; // dots are not valid after e ecount++; edigit=0; } else return 0; // invalid character in string } if (dotdigit&&edigit&&digitcount) return 1; } return 0; } int astring(const char* string) { int x=0; // assumes that strings begin with a " if (string[x]=='\0') return 0; if (string[x]=='\"') return 1; return 0; } int atruth(const char* string) { // assumes that truths are t and f. "t" and "" are considered strings. if (!strcmp(string,"t")|| !strcmp(string,"f")) return 1; // || /*!strcmp(string,"\"t\"")|| !strcmp(string,"\"\"")) return 1;*/ // "" return 0; } int asymbol(const char* string) // a symbol isn't a number, a string, or a truth, nor ( or ) { if (!atruth(string) &&!anumber(string) &&!astring(string) &&strcmp(string,"(") &&strcmp(string,")")) return 1; return 0; } // TERMINALS // Terminals check tokens for being a certain kind of token, returning // truth or not. The only exception is _symbolName, which not only // checks to see if a token is valid, but also returns the token to be // used elsewhere. - (int) _symbolName:(const char*)string:(COWSStringNode*)s:(int)pos { // a symbol is anything that's not (, ), a keyword, a truth, // a number, or a string. _symbolName returns the name // in s. int rv=pos; const char* temp; rv=[self _tokenize:string:pos:s]; if (rv<0) return rv; temp=[s string]; if (strcmp(temp,"(")&& strcmp(temp,")")&& strcmp(temp,"function")&& strcmp(temp,"variable")&& strcmp(temp,"set")&& strcmp(temp,"if")&& strcmp(temp,"else")&& strcmp(temp,"do")&& strcmp(temp,"for")&& strcmp(temp,"then")&& strcmp(temp,"while")&& !astring(temp)&& !atruth(temp)&& !anumber(temp)) { return rv; } return COWSERRORSYNTAX; } - (int) _openParen:(const char*)string:(int)pos { // an openparen is a "(" int rv=pos; COWSStringNode*s=[[COWSStringNode alloc] init]; rv=[self _tokenize:string:pos:s]; if (rv<0) { [s free]; return rv; } if (!strcmp([s string],"(")) { [s free]; return rv; } [s free]; return COWSERRORSYNTAX; } - (int) _closeParen:(const char*)string:(int)pos { // a closeparen is a ")" int rv=pos; COWSStringNode*s=[[COWSStringNode alloc] init]; rv=[self _tokenize:string:pos:s]; if (rv<0) { [s free]; return rv; } if (!strcmp([s string],")")) { [s free]; return rv; } [s free]; return COWSERRORSYNTAX; } - (int) _variableKeyword:(const char*)string:(int)pos { // a variablekeyword is the word "variable" int rv=pos; COWSStringNode*s=[[COWSStringNode alloc] init]; rv=[self _tokenize:string:pos:s]; if (rv<0) { [s free]; return rv; } if (!strcmp([s string],"variable")) { [s free]; return rv; } [s free]; return COWSERRORSYNTAX; } - (int) _functionKeyword:(const char*)string:(int)pos { // a functionkeyword is the word "function" int rv=pos; COWSStringNode*s=[[COWSStringNode alloc] init]; rv=[self _tokenize:string:pos:s]; if (rv<0) { [s free]; return rv; } if (!strcmp([s string],"function")) { [s free]; return rv; } [s free]; return COWSERRORSYNTAX; } - (int) _beginKeyword:(const char*)string:(int)pos { // a beginkeyword is the word "do" int rv=pos; COWSStringNode*s=[[COWSStringNode alloc] init]; rv=[self _tokenize:string:pos:s]; if (rv<0) { [s free]; return rv; } if (!strcmp([s string],"do")) { [s free]; return rv; } [s free]; return COWSERRORSYNTAX; } - (int) _atom:(const char*)string:(int)pos { // atoms are used only when doing 1st level parsing. // an atom is anything that's not a ( or ). int rv=pos; COWSStringNode*s=[[COWSStringNode alloc] init]; rv=[self _tokenize:string:pos:s]; if (rv<0) { [s free]; return rv; } if (strcmp([s string],"(")&&strcmp([s string],")")) { [s free]; return rv; } [s free]; return COWSERRORSYNTAX; } // INTERPRETER - _executeProgram:(COWSArgumentList*) arguments:(COWSStringNode*) symbol // Starts executing function <symbol> in the current COWS Program, // passing the function arguments <arguments> { id temp_sym=[[COWSSymbolNode alloc] init]; id temp_val; interpretation=COWSINTERPRETATIONUNDEFINED; [temp_sym copyValue:symbol]; [stack push:temp_sym]; [arguments first]; while ([arguments now]) { temp_val=[[COWSStringNode alloc] init]; [temp_val copyValue:[arguments now]]; [stack push:temp_val]; [arguments next]; } return [self _performFunction]; } - _performFunction // Begins performing a function. It is assumed that the Symbol Name // for the function is on the stack, as well as the arguments (which // are on top). { id arguments=[[COWSArgumentList alloc] init]; id symbol_name; const char* string; while ([[stack top] isMemberOf:[COWSStringNode class]]) // eats down to the nearest non-string node, which should be a // symbol node... // this should put the arguments in forward order... // that is, the first argument is on top, and the last // (rightmost) argument is on bottom. { [arguments push:[stack pop]]; } symbol_name=[stack pop]; // this assumes that symbol_name is a Symbol string=[symbol_name string]; if ([function_dictionary isKey:string]) { id returnval=[self _performInternalFunction:arguments:symbol_name]; if (returnval!=NULL) [symbol_name free]; // else it's on the stack! [arguments free]; return returnval; } else if ([library_dictionary isKey:string]) { id returnval=[self _performLibraryFunction:arguments:symbol_name]; if (returnval!=NULL) [symbol_name free]; // else it's on the stack! [arguments free]; return returnval; } else // missing function { [self _error:COWSERRORNOSUCHFUNCTION: [current_function string]:current_position:[symbol_name string]]; [arguments free]; return NULL; } } - _performInternalFunction: (COWSArgumentList*) arguments:(COWSStringNode*) symbol // performs an internal function (a function defined in COWS script) // this is always called by _performFunction. { id function_node=(COWSStateNode*) [function_dictionary valueForKey:(const void*)[symbol string]]; id new_state=[function_node copy]; id new_args=[new_state arguments]; id new_dict=[new_state dictionary]; id val; id old_state=[stack topState]; if (interpretation==COWSINTERPRETATIONUNDEFINED) interpretation=COWSINTERPRETATIONFUNCTION; if (old_state!=NULL) // i.e., if it's not the beginning of the program { [old_state setPos:current_position]; // saves old state away... } [arguments first]; [new_args first]; while([arguments now]) { if ([new_args now]==NULL) { [self _error:COWSERRORTOOMANYARGUMENTS :[current_function string]:current_position:[symbol string]]; return NULL; } val=(COWSStringNode*) [new_dict valueForKey:(const void*)[[new_args now] string]]; if (val==nil) { [self _error:COWSINTERNALERROR :[current_function string]:current_position:[symbol string]]; return NULL; } else { [val copyValue:[arguments now]]; } [arguments next]; [[new_state arguments] next]; } if ([new_args now]!=NULL) { [self _error: COWSERRORNOTENOUGHARGUMENTS :[current_function string]:current_position:[symbol string]]; return NULL; } [stack push:new_state]; [new_state setPos:0]; [current_function copyValue:new_state]; current_dictionary=[new_state dictionary]; current_position=[new_state pos]; // which should always be 0, tho return self; } - _performLibraryFunction: (COWSArgumentList*) arguments:(COWSStringNode*) symbol // performs a library function; that is, one defined by the application // before the COWS program was run. Always called by _performFunction. { id temp_node=[library_dictionary valueForKey:[symbol string]]; id returnval; if (temp_node==NULL) { [self _error:COWSINTERNALERROR :[current_function string]:current_position:[symbol string]]; return NULL; } if (interpretation==COWSINTERPRETATIONUNDEFINED) interpretation=COWSINTERPRETATIONLIBRARY; returnval=[[temp_node target] perform:[temp_node selector] with:arguments]; // since we didn't use arguments to pass to the target, but built our own // arguments, we must free our own arguments here. if ([returnval error]) { [self _error:COWSLIBRARYFUNCTIONERROR :[current_function string]:current_position:[returnval string]]; return NULL; } if (running) // not paused...resumes push their own stuff on stack { [stack push:returnval]; return [self _doKeywords]; } return self; } - _completeFunction // finishes out an internal function // and cleans up before interpreter continues parent function. { id return_value; id new_state; if ([[stack top] isMemberOf:[COWSStringNode class]]) { return_value=[stack pop]; } else return_value=[[COWSStringNode alloc] init]; while ([[stack top] isMemberOf:[COWSStringNode class]]) { [[stack pop] free]; // clears out non-return values } if ([stack top]!=NULL) // else, could be a library function finishing [[stack pop] free]; // clears out Local State new_state=[stack topState]; if (new_state!=NULL) { [current_function copyValue:new_state]; current_position=[new_state pos]; current_dictionary=[new_state dictionary]; [stack push:return_value]; return [self _doKeywords]; } else // program is finished! { if (tempDelegate&&[tempDelegate conformsTo:@protocol (InterpreterToDelegate)]) [tempDelegate finishedInterpreting: [return_value string]:(int)COWSSUCCESS:self]; if (delegate&&[delegate conformsTo:@protocol (InterpreterToDelegate)]) [delegate finishedInterpreting: [return_value string]:(int)COWSSUCCESS:self]; [return_value free]; return [self stopInterpreting]; } } - _evaluateVariable:(COWSStringNode*) symbol // evaluates the variable <symbol> to its value, either in the current // dictionary or in the global dictionary { // this assumes current dictionary is proper... id temp_val=[current_dictionary valueForKey:(void*) [symbol string]]; if (temp_val==NULL) temp_val= [global_dictionary valueForKey:(void*) [symbol string]]; if (temp_val==NULL) { [self _error:COWSERRORNOSUCHVARIABLE :[current_function string]:current_position:[symbol string]]; return NULL; } else { id new_val=[[COWSStringNode alloc] init]; [new_val copyValue:temp_val]; [stack push:new_val]; [self _doKeywords]; return self; } } - _doKeywords // continues keywords, if there are any. // this happens every time a value if finished out; a keyword may need // to use it to set something up (like <for> needs 4 values) { id top=[stack topSymbol]; const char* string; if (top==NULL) return self; string=[top string]; if (!strcmp(string,"if")) { return [self _doIf]; } if (!strcmp(string,"set")) { return [self _doSet]; } if (!strcmp(string,"for")) { return [self _doFor]; } if (!strcmp(string,"while")) { return [self _doWhile]; } return self; } - _go // Drives _readEval, the main interpreter function // this superfunction allows the interpreter to perform <repeats> number // of _readEvals each clock cycle. If the interpreter has stopped before // the clock did, _go breaks out automatically so it doesn't waste time. // NEW IN 1.2: If the interpreter is foreground, _go works until it // encounters a command-period. If it's background, _go works until // it encounters an event. The event stuff is provided by Don Yacktman. // Foreground mode does _not_ work properly with pause and resume! // An interpreter _must_ be background for this to work. { int x; if (foreground) { while (running) { if (NXUserAborted()) { [self stopInterpreting]; return self; } else for (x=0;x<repeats;x++) { if (!running) break; [self _readEval]; } } } else // background { if (!running) return self; for (x=0;x<repeats;x++) { if (!running) break; [self _readEval]; } } return self; } - _readEval // Reads a token and evaluates it by calling one of several subordinate // functions. This function is ultimately driven by the clock through _go. { id token=[[COWSStringNode alloc] init]; const char* string; current_position=[self _tokenize:[current_function string]: current_position:token]; string=[token string]; if (!strcmp(string,"")) // token is empty... { if (interpretation==COWSINTERPRETATIONLIBRARY) // it was a library function { return [self _completeFunction]; } [self _error:COWSERRORNOMORETOKENS :[current_function string]:current_position:""]; [token free]; return NULL; } else if (atruth(string)) { // prepare string [self _makeTruth:token]; [stack push:token]; return [self _doKeywords]; } else if (anumber(string)) { // prepare string [self _makeNumber:token]; [stack push:token]; return [self _doKeywords]; } else if (astring(string)) { // prepare string [self _strip:token]; [stack push:token]; return [self _doKeywords]; } else if (asymbol(string)) // token is a variable... { [self _evaluateVariable:token]; [token free]; return self; } else if (!strcmp(string,"(")) { id new_node; current_position=[self _tokenize:[current_function string]: current_position:token]; string=[token string]; if (!strcmp(string,"")) // token is empty... { [self _error:COWSERRORNOMORETOKENS :[current_function string]:current_position:""]; [token free]; return NULL; } else if (!asymbol(string)) // token ain't a symbol { [self _error:COWSERRORNOFUNCTIONNAME :[current_function string]:current_position:[token string]]; [token free]; return NULL; } else if (!strcmp(string,"if")) { [self _startIf]; [token free]; return self; } else if (!strcmp(string,"set")) { [self _startSet]; [token free]; return self; } else if (!strcmp(string,"for")) { [self _startFor]; [token free]; return self; } else if (!strcmp(string,"while")) { [self _startWhile]; [token free]; return self; } // otherwise, token is a symbol but isn't a keyword, so it's a function // so push it for use later new_node=[[COWSSymbolNode alloc] init]; [new_node copyValue:token]; [stack push:new_node]; [token free]; return self; } else if (!strcmp(string,")")) { id symbol=[stack topSymbol]; if (symbol==NULL) { [self _completeFunction]; [token free]; return self; } string=[symbol string]; if (!strcmp(string,"if")) { id temp=[self _finishIf]; [token free]; if (temp!=NULL) [self _doKeywords]; return self; } else if (!strcmp(string,"set")) { id temp=[self _finishSet]; [token free]; if (temp!=NULL) [self _doKeywords]; return self; } else if (!strcmp(string,"for")) { id temp=[self _finishFor]; [token free]; if (temp!=NULL) [self _doKeywords]; return self; } else if (!strcmp(string,"while")) { id temp=[self _finishWhile]; [token free]; if (temp!=NULL) [self _doKeywords]; return self; } // else function is ready to be performed [self _performFunction]; } [token free]; return self; } -_startSet // starts processing a "set" token { const char* string; id token=[[COWSStringNode alloc] init]; id node; current_position=[self _tokenize:[current_function string]: current_position:token]; string=[token string]; if (asymbol(string)) { node=[[COWSSymbolNode alloc] init]; [node setState:COWSSTARTSET]; [node setString:"set"]; [[node variable] copyValue:token]; [token free]; [stack push:node]; return self; } [self _error:COWSERRORSETNOTVARIABLE :[current_function string]:current_position:[token string]]; [token free]; return NULL; } -_doSet // continues processing a "set" token { id node=[stack topSymbol]; if ([node state]==COWSSTARTSET) { [node setState:COWSDONESET]; return self; } [self _error:COWSERRORSETTOOMANYVALUES :[current_function string]:current_position:""]; return NULL; } -_finishSet // finishes processing a "set" token { id node=[stack topSymbol]; if ([node state]==COWSDONESET) { const char* string=[[node variable] string]; id value=[stack pop]; if ([current_dictionary isKey: (const void*) string]) { [(COWSStringNode*)[current_dictionary valueForKey:(const void*)string] copyValue:value]; [[stack pop] free]; // pops and frees set node [stack push:value]; // returns value return self; } else if ([global_dictionary isKey: (const void*) string]) { [(COWSStringNode*)[global_dictionary valueForKey:(const void*)string] copyValue:value]; [[stack pop] free]; // pops and frees set node [stack push:value]; // returns value return self; } else { [self _error:COWSERRORSETNOSUCHVARIABLE :[current_function string]:current_position:string]; [value free]; return NULL; } } [self _error:COWSERRORSETNOVALUE :[current_function string]:current_position:""]; return NULL; } -_startIf // begins processing "if" token { id node; node=[[COWSSymbolNode alloc] init]; [node setState:COWSSTARTIF]; [node setString:"if"]; [stack push:node]; return self; } -_doIf // continues processing "if" token { id node=[stack topSymbol]; int state=[node state]; if (state==COWSSTARTIF) { id value=[stack pop]; if ([value booleanVal]) { [node setState:COWSSTARTTHEN]; [value free]; return self; } else // f or other { int x=current_position; [node setState:COWSSTARTELSE]; [value free]; [self _skip]; if (x==current_position) // couldn't skip over the then! { [self _error:COWSERRORIFNOTHENCLAUSE :[current_function string]:current_position:""]; return NULL; } return self; } } else if (state==COWSSTARTTHEN) { [node setState:COWSDONEIF]; [self _skip]; return self; } else if (state==COWSSTARTELSE) { [node setState:COWSDONEIF]; return self; } else // COWSDONEIF { [self _error:COWSERRORIFTOOMANYVALUES :[current_function string]:current_position:""]; return NULL; } } -_finishIf // completes processing "if" token { id node=[stack topSymbol]; id value; int state=[node state]; if (state==COWSSTARTIF||state==COWSSTARTTHEN) { [self _error:COWSERRORIFNOTENOUGHVALUES :[current_function string]:current_position:""]; return NULL; } else if (state==COWSDONEIF) // finished a value { value=[stack pop]; } else // state= COWSSTARTELSE; no value { value=[[COWSStringNode alloc] init]; // empty value to push } while(1) { id top=[stack top]; if ([top isMemberOf: [COWSSymbolNode class]]) if (!strcmp([top string],"if")) break; [[stack pop] free]; // pops and frees to if node } [[stack pop] free]; // pops if node [stack push:value]; return self; } - _startWhile // begins processing "while" token { id node; node=[[COWSSymbolNode alloc] init]; [node setState:COWSSTARTWHILE]; [node setString:"while"]; [node setPos:current_position]; [stack push:node]; return self; } -_doWhile // continues processing "while" token { id node=[stack topSymbol]; int state=[node state]; if (state==COWSSTARTWHILE) { id test=[stack pop]; if ([test booleanVal]) { [node setState:COWSTRUEWHILE]; } else // false... { [node setState:COWSFALSEWHILE]; [self _skip]; } [stack push:test]; // placed back on stack in case need to return it return self; } else if (state==COWSTRUEWHILE) { [node setState:COWSDONEWHILE]; return self; } else // state==COWSDONEWHILE or COWSFALSEWHILE { [self _error:COWSERRORWHILETOOMANYVALUES :[current_function string]:current_position:""]; return NULL; } } -_finishWhile // finishes processing "while" token { id node=[stack topSymbol]; int state=[node state]; if (state==COWSTRUEWHILE||state==COWSDONEWHILE) { while(1) { id top=[stack top]; if ([top isMemberOf: [COWSSymbolNode class]]) if (!strcmp([top string],"while")) break; [[stack pop] free]; // pops and frees to while node } current_position=[node pos]; [node setState:COWSSTARTWHILE]; return NULL; // we don't want _doKeywords called again. } else if (state==COWSSTARTWHILE) { [self _error:COWSERRORWHILENOTENOUGHVALUES :[current_function string]:current_position:""]; return NULL; } else // ==COWSFALSEWHILE { id return_val=[stack pop]; while(1) { id top=[stack top]; if ([top isMemberOf: [COWSSymbolNode class]]) if (!strcmp([top string],"while")) break; [[stack pop] free]; // pops and frees to while node } [[stack pop] free]; // pops and frees while node [stack push:return_val]; return self; } } - _startFor // begins processing "for" token { const char* string; id token=[[COWSStringNode alloc] init]; id node; current_position=[self _tokenize:[current_function string]: current_position:token]; string=[token string]; if (asymbol(string)) { node=[[COWSSymbolNode alloc] init]; [node setState:COWSSTARTFOR]; [node setString:"for"]; [[node variable] copyValue:token]; [token free]; [stack push:node]; return self; } [self _error:COWSERRORFORNOTAVARIABLE :[current_function string]:current_position:[token string]]; [token free]; return NULL; } -_doFor // continues processing "for" token { id node=[stack topSymbol]; int state=[node state]; if (state==COWSSTARTFOR) { id val=[stack pop]; if (anumber([val string])) { [node setStart:[val floatVal]]; [node setState:COWSENDFOR]; [val free]; return self; } else { [self _error:COWSERRORFORSTARTNOTNUMBER :[current_function string]:current_position:[val string]]; [val free]; return NULL; } } else if (state==COWSENDFOR) { id val=[stack pop]; if (anumber([val string])) { [node setEnd:[val floatVal]]; [node setState:COWSSTEPFOR]; [val free]; return self; } else { [self _error:COWSERRORFORSTOPNOTNUMBER :[current_function string]:current_position:[val string]]; [val free]; return NULL; } } else if (state==COWSSTEPFOR) { id val=[stack pop]; if (anumber([val string])) { BOOL test; id var=[node variable]; const char* var_str=[var string]; [node setStep:[val floatVal]]; [node setState:COWSDOFOR]; if ([current_dictionary isKey: (const void*) var_str]) { char start_val[COWSLARGENUMBER]; sprintf(start_val,"%f",[node start]); [(COWSStringNode*)[current_dictionary valueForKey:(const void*)var_str] setString: start_val]; [node setPos:current_position]; } else if ([global_dictionary isKey: (const void*) var_str]) { char start_val[COWSLARGENUMBER]; sprintf(start_val,"%f",[node start]); [(COWSStringNode*)[global_dictionary valueForKey:(const void*)var_str] setString: start_val]; [node setPos:current_position]; } else { [self _error:COWSERRORFORNOSUCHVARIABLE :[current_function string]:current_position:[val string]]; [val free]; return NULL; } // now test limits if ([node step]>=0) { if ([node start] > [node end]) { test=YES; } else test=NO; } else { if ([node start] < [node end]) { test=YES; } else test=NO; } // and act on test by skipping ahead to finish if (test) { [node setState:COWSFALSEFOR]; [self _skip]; } [val free]; return self; } else { [self _error:COWSERRORFORSTEPNOTNUMBER :[current_function string]:current_position:[val string]]; [val free]; return NULL; } } else if (state==COWSDOFOR) { [node setState:COWSTESTFOR]; return self; } else if (state==COWSCONTINUEFOR) { [node setState:COWSTESTFOR]; return self; } else // state==COWSFALSEFOR||state==COWSTESTFOR { [self _error:COWSERRORFORTOOMANYVALUES :[current_function string]:current_position:""]; return NULL; } } -_finishFor // finishes processing "for" token { id node=[stack topSymbol]; int state=[node state]; /* if (state==COWSTRUEFOR) { [[stack pop] free]; // removes for value current_position=[node pos]; [node setState:COWSCONTINUEFOR]; return NULL; // we don't want _doKeywords running again. } else*/ if (state==COWSFALSEFOR) { id return_val=[stack pop]; [[stack pop] free]; // removes for node [stack push:return_val]; return self; } else if (state==COWSTESTFOR) { //char new_val[COWSLARGENUMBER]; BOOL test; float nv; id variable; id var=[node variable]; const char* var_str=[var string]; [node setState:COWSCONTINUEFOR]; // first, find variable if ([current_dictionary isKey: (const void*) var_str]) { variable=(COWSStringNode*)[current_dictionary valueForKey:(const void*)var_str]; } else if ([global_dictionary isKey: (const void*) var_str]) { variable=(COWSStringNode*)[global_dictionary valueForKey:(const void*)var_str]; } else { [self _error:COWSERRORFORNOSUCHVARIABLE :[current_function string]:current_position:var_str]; return NULL; } // then, increment variable //sprintf(new_val,"%f",atof([variable string])+[node step]); //[variable setString:new_val]; [variable setDoubleVal:[variable doubleVal]+[node step]]; nv=[variable doubleVal]; // now test limits if ([node step]>=0) { if (nv > [node end]) { test=YES; } else test=NO; } else { if (nv < [node end]) { test=YES; } else test=NO; } // and act on test by killing for or continuing if (test) { id return_val=[stack pop]; [[stack pop] free]; // removes for node [stack push:return_val]; return self; } else { [[stack pop] free]; // removes for value current_position=[node pos]; return NULL; // we don't want _doKeywords running again. } } else // state== anything else { [self _error:COWSERRORFORNOTENOUGHVALUES :[current_function string]:current_position:""]; return NULL; } } - _makeTruth:(COWSStringNode*) string // converts t to "t" and f to "" { char* str=newstr([string string]); /* if (str[0]=='\"') { str[0]=str[1]; str[1]='\0'; } */ if (!strcmp(str,"\"t\"")) strcpy(str,"t"); // ""t"" -> "t" if (!strcmp(str,"f")) strcpy(str,""); // f -> "" [string setString:str]; free(str); return self; } - _makeNumber:(COWSStringNode*) string // prepares a number. If the number's bad, it may turn out zero // or something different than the user expected { /* char str[256]; // a number big enough to hold any float float x=atof([string string]); sprintf(str,"%f",x); [string setString:str]; */ return self; // this function is now defunct--it never worked right anyway, // and now serves no purpose. } - _strip:(COWSStringNode*) string // strips a string of its "s { const char* str=[string string]; char* new_str=newstr(&str[1]); // gets rid of first " int x=strlen(new_str); if (x>0) new_str[x-1]='\0'; // gets rid of second " [string setString:new_str]; free(new_str); return self; } - (int) _skip // skips one value, if there is one // returns new position if successful, error if not. { int result=[self _item:[current_function string]:current_position]; if (result>=0) // not an error { current_position=result; } return result; } - (int) _error:(int) this_error:(const char*) this_function: (int) this_position: (const char*) this_string // performs error-reporting // this may entail sending an error message to a delegate // returns the error { // need to stop also... [self stopInterpreting]; if (printing_errors) { printf("\n================ERROR================\n"); switch (this_error) { case COWSERRORNOSUCHFUNCTION : printf ("No Such Function\n"); break; case COWSERRORNOTENOUGHARGUMENTS : printf ("Not Enough Arguments\n"); break; case COWSERRORTOOMANYARGUMENTS : printf ("Too Many Arguments\n"); break; case COWSLIBRARYFUNCTIONERROR : printf ("Library Function Error\n"); break; case COWSERRORNOSUCHVARIABLE : printf ("No Such Variable\n"); break; case COWSERRORNOMORETOKENS : printf ("No More Tokens\n"); break; case COWSERRORNOFUNCTIONNAME : printf ("No Function Name\n"); break; case COWSERRORSETNOTVARIABLE : printf ("Set: Not a Variable\n"); break; case COWSERRORSETTOOMANYVALUES : printf ("Set: Too Many Values\n"); break; case COWSERRORSETNOVALUE : printf ("Set: No Value to Set\n"); break; case COWSERRORSETNOSUCHVARIABLE : printf ("Set: No Such Variable\n"); break; case COWSERRORIFNOTHENCLAUSE : printf ("If: No Then Clause\n"); break; case COWSERRORIFTOOMANYVALUES : printf ("If: Too Many Values\n"); break; case COWSERRORIFNOTENOUGHVALUES : printf ("If: Not Enough Values\n"); break; case COWSERRORWHILETOOMANYVALUES : printf ("While: Too Many Values\n"); break; case COWSERRORWHILENOTENOUGHVALUES : printf ("While: Not Enough Values\n"); break; case COWSERRORFORNOTAVARIABLE : printf ("For: Not a Variable\n"); break; case COWSERRORFORSTARTNOTNUMBER : printf ("For: Start Value Not a Number\n"); break; case COWSERRORFORSTOPNOTNUMBER : printf ("For: Stop Value Not a Number\n"); break; case COWSERRORFORSTEPNOTNUMBER : printf ("For: Step Value Not a Number\n"); break; case COWSERRORFORNOSUCHVARIABLE : printf ("For: No Such Variable\n"); break; case COWSERRORFORTOOMANYVALUES : printf ("For: Too Many Values\n"); break; case COWSERRORFORNOTENOUGHVALUES : printf ("For: Not Enough Values\n"); break; case COWSINTERNALERROR : printf ("Interpreter Internal Error\n"); break; default : printf ("Unknown Error\n"); break; } if (strlen(this_string) && this_error!=COWSLIBRARYFUNCTIONERROR) { printf ("Offending Symbol: %s\n",this_string); } if (this_error==COWSLIBRARYFUNCTIONERROR) { printf ("Message: %s\n",this_string); } printf ("In Function: %s\n", this_function); printf ("At Position: %d\n",this_position); printf ("=====================================\n\n"); } if (tempDelegate!=NULL&&[tempDelegate conformsTo:@protocol (InterpreterToDelegate)]) [tempDelegate errorInterpreting:this_error:this_function: this_position:this_string:self]; if (delegate!=NULL&&[delegate conformsTo:@protocol (InterpreterToDelegate)]) [delegate errorInterpreting:this_error:this_function: this_position:this_string:self]; return this_error; } - (BOOL) canPause // returns YES if the interpreter can be paused. // in the case of this interpreter, it cannot be // paused unless it is in background mode only. { return (![self foreground]); } - printErrors:(BOOL) yes_or_no { printing_errors=yes_or_no; return self; } - (BOOL) printingErrors { return printing_errors; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.