home *** CD-ROM | disk | FTP | other *** search
- #if 0
- ReadAssignments.c
- This is a runtime C interpreter that only accepts assignments and comments, e.g.
-
- /* C comment */
- // C++ comment
- pupilMm=2.0;
- logC=-INF;
- viewingDistance=57.0;
- // result=0;
- result = NAN;
- trials=40; // short int, or char or long, signed or unsigned
- // The next line is blank, separating "blocks" of assignments
-
- logC=-1;
- trials='E';
- kindOfTrial="2IFC"; /* string */
- message="I will beep\007 now.\n";
-
- Whitespace and comments are ignored (except that you can break off processing at
- ends of lines, and blocks separated by blank lines). Both C style /* */ and C++
- style // comments are allowed. No expression evaluation is supported beyond
- simple reading of values. No casts, e.g. (unsigned long), number suffixes, e.g.
- UL, or preproprocessor directives, e.g. #if, are allowed. Adjacent strings are
- concatenated, as specified by ANSI C. Backslash escapes, e.g. \007, within
- single and double quotes are fully supported. Continuing a line by putting a
- backslash just before the newline character is not supported. Each string is
- allocated on the heap, by malloc(). You can recover the space assigned to a
- string by calling free().
-
- This C interpreter is quite dumb and very strict. If it can't interpret your
- file it will PrintfExit an error message pinpointing the error.
- Assignment statements must be terminated by semicolons. Comments opened, /*,
- must be closed, */.
-
- The main use of these routines will be to read parameter files that control
- experiments. Typical psychophysical and physiological experiments are VERY
- complicated and require lots of adjustable parameters. One needs an easy way to
- change them all that is self documenting and easily reproduced, possibly months
- later. The routines provided here come in various flavors allowing you to read a
- line at a time (with any number of assignments on the line), a block at a time
- (blocks are separated by blank lines), or a whole file at a time. In controlling
- psychophysical experiments I use a block at the beginning of the file to define
- the general experiment followed by a block for each psychophysical
- "block", with a line for each condition.
-
- Since the interpreter will fail if the paramater file contains even a single
- error, it is strongly suggested that your experimental program begin by doing a
- dry run, reading the whole file through to check for errors, before initializing
- your variables and reading it again a bit at a time as the experiment
- progresses. It would obviously be very bad to have the interpreter quit near the
- end of a 3 hour experiment.
-
- The interpreter uses a line buffer of BUFFER_SIZE bytes (currently 512), so keep
- your lines of text shorter than this to avoid breaking tokens at the end of the
- buffer.
-
- Here is a sample program to read the assignments given above:
- #include "VideoToolbox.h"
- void main(void)
- {
- double pupilMm,logC,viewingDistance,result;
- short int trials,i;
- char *kindOfTrial,*message;
- Variable v[8];
- FILE *file;
-
- i=0;
- v[i++]=SetVariable(stringType,&kindOfTrial,"kindOfTrial");
- v[i++]=SetVariable(stringType,&message,"message");
- v[i++]=SetVariable(doubleType,&pupilMm,"pupilMm");
- v[i++]=SetVariable(doubleType,&logC,"logC");
- v[i++]=SetVariable(doubleType,&viewingDistance,"viewingDistance");
- v[i++]=SetVariable(doubleType,&result,"result");
- v[i++]=SetVariable(shortType,&trials,"trials");
- v[i++]=SetVariable(0,NULL,NULL); // mark end of array
- ReadAssignmentFile("myFile",v,0); // make sure file is ok!
- file=fopen("myFile","r");
- InitializeVariables(v);
- do{
- printf("********New block********\n");
- ReadAssignmentBlock(file,v,echoAssignments);
- // a real program would do something useful here
- } while(!feof(file));
- fclose(file);
- }
-
- Here are the routines:
-
- v[i++]=SetVariable(doubleType,&x,"x");
- v[i++]=SetVariable(0,NULL,NULL); /* mark end of array */
- SetVariable merely returns a Variable with the field values that you supply. Use
- it to make your programs more readable. For example,
- v[i++]=SetVariable(doubleType,&x,"x");
- is equivalent to
- v[i].type=doubleType;
- v[i].ptr=&x;
- v[i++].name="x";
- The available types are: charType, shortType, longType, unsignedCharType,
- unsignedShortType, unsignedLongType, doubleType, and stringType. (They are
- defined by an enum statement in the VideoToolbox.h header file, which you'll
- #include in your source file.) Note that when you indicate stringType, you
- should supply the address of a char pointer variable. Later, calling
- InitializeVariables() will initialize your variable to point to a null string,
- "", and calling ReadAssignmentLine, if it finds a match, will allocate storage
- by calling malloc() and place the address of the string in your variable.
-
- n=InitializeVariables(variable);
- InitializeAVariable(variable);
- These utilities use your Variable array or a single Variable to initialize. All
- char, short, and long variables are set to zero, all double variables are set to
- NAN, and all the strings are set to "". (All the string pointers are initialized
- to point to the same empty string.)
-
- n=ReadAssignmentLine(file,variables,echo);
- Interprets one line from the file, and continues to read further lines, if
- necessary, to reach the end of any incomplete comment or assignment.
- ReadAssignmentBlock() and ReadAssignmentFile() do all their reading by
- repeatedly calling ReadAssignmentLine().
-
- blank=AssignmentLineWasBlank();
- Returns true (i.e. 1) unless the most recent call to ReadAssignmentLine() read some
- non-blank text.
-
- n=ReadAssignmentBlock(file,variables,echo);
- Interprets lines from the file until it reads an isolated blank line. (Blank
- lines within an assignment or comment /* */ are ignored.) So separate your
- blocks by a blank line. A blank line may contain spaces, tabs, returns,
- newlines, formfeeds, and vertical tabs. Implemented by repeatedly calling
- ReadAssignmentLine() until AssignmentLineWasBlank() is true.
-
- n=ReadAssignmentFile(filename,variables,echo);
- Use this to interpret a whole file at once. It even opens and closes the file for
- you. Implemented by repeatedly calling ReadAssignmentLine().
-
- PrintAssignment(file,&variable);
- Prints out the name and value of the variable, as an assignment statement, suitable
- for reading by any of the ReadAssignment routines.
-
- n=ReadLuminanceRecord(filename,LP,echo)
- This uses ReadAssignmentFile() to interpret a LuminanceRecord?.h file. This
- routine is in ReadLuminanceRecord.c.
-
- The returned value, n, is the number of assignments that were interpreted.
-
- The second argument, "variables", is an array of "Variable" structs describing
- the names, addresses, and types of your variables. The number of array elements
- is not given. Instead you mark the end by an element with the "type" field set
- to zero. The "Variable" data structure is defined in VideoToolbox.h.
-
- The third argument, "echo", was developed for debugging but it is so handy that it
- stayed in the final version. It allows you to request that the assignment and/or
- the comments and/or the original file be listed on the console by printf. "echo"
- should be an integer value formed by summing some or all of predefined
- constants: echoAssignments, echoComments, echoFile.
-
- echoFile prints each line from the file.
-
- echoAssignments regenerates the assigment statement by printing the variable
- name, an equal sign, the stored value, a semicolon and a space. Uncommented
- newline characters in the source file are echoed as well.
-
- echoComments prints both /*..*/ and //-style comments. Uncommented
- newline characters in the source file are echoed as well.
-
- They are defined by an enum statement in the VideoToolbox.h header file, which
- you should #include in your source file. Set echo to zero for no printf's.
- Turning them all on allows you to monitor the operation of ReadAssignmentLine.
- Turning on just comments, for example, might be helpful in reminding you of
- what's in the file being interpreted.
-
- IMPORTANT: Don't forget to mark the end of your variable array by setting the
- last element's type to zero.
-
- ReadAssignments was inspired by an idea Beau Watson mentioned to me in the 1980's:
- a routine to read free-form parameter values at run time.
-
- HISTORY:
- 7/30/91 dgp wrote it.
- 8/4/91 dgp added new routines and renamed the old ones. Everything
- seems to work, but has not been thoroughly tested.
- 8/5/91 dgp MPW C 3.2 now compiles it without error messages. The MPW C macro processor,
- contrary to ANSI C, finds comment symbols inside strings and finds
- preprocessor directives even when # is not the first nonblank character.
- 8/24/91 dgp Made compatible with THINK C 5.0.
- Changed ReadAssignment file to return an error code instead of aborting
- if it can't open the file.
- 8/26/91 dgp Added SetVariable() and noted, above, that the Variable array
- is terminated by an element with its "type" field set to zero. (Thanks
- to Evan Relkin for pointing out the omission.)
- 4/1/92 dgp Tidied up the documentation above.
- 4/2/92 dgp Introduced ReadAssignmentBlock() and AssignmentLineWasBlank().
- Deleted ReadAssignments().
- 4/5/92 dgp ReadAssignmentFile() now closes the file before returning.
- 4/17/92 dgp Expanded the explanation of echo, as requested by Evan Relkin.
- 5/14/92 dgp Expanded the explanation of stringType, "".
- 8/4/92 dgp Added PrintAssignment(), which prints out the value of a variable,
- as an assignment statement, suitable for reading by the ReadAssignment
- routines.
- 10/24/92 dgp Eliminated double spacing that occurred after //-style comment when
- echoComment was requested.
- 3/4/93 dgp changed the definition of emptyString slightly so that this file could be
- compiled as a code resource.
- #endif
- #include "VideoToolbox.h"
-
- /* the following are private, not intended for use outside this file */
- static Boolean lineWasBlank=1; /* used in ReadAssignmentLine(), */
- /* GetToken1(), and AssignmentLineWasBlank().*/
- void ParsingError(char *s);
- char *GetToken(FILE *file,char *lineBuffer,char *s,int echo);
- char *GetToken0(FILE *file,char *lineBuffer,char *s,int echo);
- char *GetToken1(FILE *file,char *lineBuffer,char *s,int echo);
- double strtodN(char *s,char **sPtr);
- long strtolN(char *s,char **sPtr,short flag);
- unsigned long strtoulN(char *s,char **sPtr,short flag);
- char strtoc(char *s,char **sPtr);
- #define streq2(s1,s2) (strncmp(s1,s2,strlen(s2))==0)
-
- #define ECHO_ASSIGNMENTS (echo & echoAssignments)
- #define ECHO_COMMENTS (echo & echoComments)
- #define ECHO_FILE (echo & echoFile)
- #define BUFFER_SIZE 512
-
- void PrintAssignment(FILE *file,Variable *v)
- {
- fprintf(file,"%s=",v->name);
- switch(v->type){
- default:
- PrintfExit("\nPrintVariable: %s has unknown type %d.\n\007",v->name,v->type);
- case charType:
- if(isalnum(*(unsigned char *)v->ptr))
- fprintf(file,"'%c';\n",*(unsigned char *)v->ptr);
- else
- fprintf(file,"%d;\n",*(char *)v->ptr);
- break;
- case unsignedCharType:
- if(isalnum(*(unsigned char *)v->ptr))
- fprintf(file,"'%c';\n",*(unsigned char *)v->ptr);
- else
- fprintf(file,"%d;\n",*(unsigned char *)v->ptr);
- break;
- case shortType:
- fprintf(file,"%d;\n",*(short *)v->ptr);
- break;
- case unsignedShortType:
- fprintf(file,"%u;\n",*(unsigned short *)v->ptr);
- break;
- case longType:
- fprintf(file,"%ld;\n",*(long *)v->ptr);
- break;
- case unsignedLongType:
- fprintf(file,"%lu;\n",*(unsigned long *)v->ptr);
- break;
- case doubleType:
- fprintf(file,"%f;\n",*(double *)v->ptr);
- break;
- case stringType:
- fprintf(file,"\"%s\";\n",*(char **)v->ptr);
- break;
- }
- }
-
- Variable SetVariable(int type,void *ptr,char *name)
- {
- static Variable v;
-
- v.name=name;
- v.ptr=ptr;
- v.type=type;
- return v;
- }
-
- int InitializeVariables(Variable variable[])
- {
- int i;
-
- for(i=0;variable[i].type!=0;i++)InitializeAVariable(&variable[i]);
- return i;
- }
-
- void InitializeAVariable(Variable *v)
- {
- static const char emptyString[]="";
-
- switch(v->type){
- default:
- PrintfExit("\nInitializeAVariable: %s has unknown type %d.\n\007"
- ,v->name,v->type);
- case charType:
- case unsignedCharType:
- *(char *)v->ptr=0;
- break;
- case shortType:
- case unsignedShortType:
- *(short *)v->ptr=0;
- break;
- case longType:
- case unsignedLongType:
- *(long *)v->ptr=0;
- break;
- case doubleType:
- *(double *)v->ptr=NAN;
- break;
- case stringType:
- *(const char **)v->ptr=emptyString;
- break;
- }
- }
-
- Boolean AssignmentLineWasBlank(void)
- {
- extern Boolean lineWasBlank;
-
- return lineWasBlank;
- }
-
- int ReadAssignmentBlock(FILE *file,Variable variable[],int echo)
- {
- int n;
-
- n=0;
- do{
- n+=ReadAssignmentLine(file,variable,echo);
- }while(!AssignmentLineWasBlank());
- return n;
- }
-
- int ReadAssignmentFile(char *filename,Variable variable[],int echo)
- {
- int n;
- FILE *file;
-
- file=fopen(filename,"r");
- if(file==NULL)return -1; /* error */
- n=0;
- do{
- n+=ReadAssignmentLine(file,variable,echo);
- }while(!feof(file));
- fclose(file);
- return n;
- }
-
- static char *lineBuffer; /* global because it would be messy to pass this address to
- all the routines below that need to call ParsingError */
-
- int ReadAssignmentLine(FILE *file,Variable variable[],int echo)
- {
- register Variable *v;
- Variable *vLast,*w;
- int j,n;
- char *s,*s2,*stringBuffer;
- extern Boolean lineWasBlank;
-
- lineWasBlank=TRUE;
- lineBuffer=(char *)malloc(BUFFER_SIZE);
- stringBuffer=(char *)malloc(BUFFER_SIZE);
- if(lineBuffer==NULL||stringBuffer==NULL){
- PrintfExit("ReadAssignmentLine: couldn't malloc %d bytes for buffers.\007\n"
- ,2*BUFFER_SIZE);
- }
- if(file==NULL)return 0;
- for(vLast=&variable[0];vLast->type>0;vLast++);
- vLast->name="";
- vLast->ptr=NULL;
- s=NULL;
- n=0;
- do{
- /* parse a parameter name */
- if(s==NULL) s=GetToken1(file,lineBuffer,s,echo);/* read in a new line */
- else s=GetToken0(file,lineBuffer,s,echo); /* don't read in a new line */
- if(s==NULL){
- free(lineBuffer);
- free(stringBuffer);
- return n;
- }
- v=vLast;
- for(w=&variable[0];w!=vLast;w++) /* find longest match */
- if(streq2(s,w->name)&&strlen(w->name)>strlen(v->name))v=w;
- if(v==vLast)ParsingError(s);
- s+=strlen(v->name);
- if(ECHO_ASSIGNMENTS)printf("%s",v->name);
-
- /* parse the equal sign */
- s=GetToken(file,lineBuffer,s,echo);
- if(s==NULL)ParsingError(s);
- if(s[0] != '=')ParsingError(s);
- s++;
- if(ECHO_ASSIGNMENTS)printf("=");
-
- /* parse the value */
- s=GetToken(file,lineBuffer,s,echo);
- if(s==NULL)ParsingError(s);
- switch(v->type){
- default:
- PrintfExit("\nReadAssignmentLine: %s has unknown type %d.\n\007"
- ,v->name,v->type);
- case charType:
- *(char *)v->ptr=strtolN(s,&s,0);
- if(ECHO_ASSIGNMENTS)printf("%d",*(char *)v->ptr);
- break;
- case shortType:
- *(short *)v->ptr=strtolN(s,&s,0);
- if(ECHO_ASSIGNMENTS)printf("%d",*(short *)v->ptr);
- break;
- case longType:
- *(long *)v->ptr=strtolN(s,&s,0);
- if(ECHO_ASSIGNMENTS)printf("%ld",*(long *)v->ptr);
- break;
- case unsignedCharType:
- *(unsigned char *)v->ptr=strtoulN(s,&s,0);
- if(ECHO_ASSIGNMENTS)printf("%u",*(unsigned char *)v->ptr);
- break;
- case unsignedShortType:
- *(unsigned short *)v->ptr=strtoulN(s,&s,0);
- if(ECHO_ASSIGNMENTS)printf("%u",*(unsigned short *)v->ptr);
- break;
- case unsignedLongType:
- *(unsigned long *)v->ptr=strtoulN(s,&s,0);
- if(ECHO_ASSIGNMENTS)printf("%lu",*(unsigned long *)v->ptr);
- break;
- case doubleType:
- *(double *)v->ptr=strtodN(s,&s);
- if(ECHO_ASSIGNMENTS)printf("%g",*(double *)v->ptr);
- break;
- case stringType:
- /* parse the opening quote mark */
- if(s[0]!='"')ParsingError(s);
- s++;
- s2=stringBuffer;
- do{
- /* copy string but translate backslash escape sequences */
- do{
- j=strcspn(s,"\\\"");
- strncpy(s2,s,j);
- s+=j;
- s2+=j;
- s2[0]=0;
- if(s[0]=='\\'){
- s2++[0]=strtoc(s,&s);
- s2[0]=0;
- }
- else break;
- } while (1);
-
- /* parse the closing quote mark */
- if(s[0]!='"')ParsingError(s);
- s++;
-
- /* look for another opening quote mark. Concatenate adjacent strings */
- s=GetToken(file,lineBuffer,s,echo);
- if(s==NULL)ParsingError(s);
- if(s[0]!='"')break;
- s++;
- }while(1);
- /* allocate the new string */
- *(char **)v->ptr=malloc(1+strlen(stringBuffer));
- if(v->ptr==NULL){
- PrintfExit("\nReadAssignmentLine: couldn't allocate %ld bytes for string.\007\n",1L+strlen(stringBuffer));
- }
- strcpy(*(char **)v->ptr,stringBuffer);
- if(ECHO_ASSIGNMENTS)printf("\"%s\"",*(char **)v->ptr);
- break;
- }
-
- /* parse the semicolon */
- s=GetToken(file,lineBuffer,s,echo);
- if(s==NULL)ParsingError(s);
- if(s[0]!=';')ParsingError(s);
- s++;
- if(ECHO_ASSIGNMENTS)printf("; ");
-
- n++;
- } while(1);
- }
-
- void ParsingError(char *s)
- {
- int i;
-
- if(s==NULL)PrintfExit("\nReadAssignmentLine: premature end of file.\007\n");
- printf("\nReadAssignmentLine: error location indicated by the caret ^.\007\n");
- printf("%s",lineBuffer);
- s[0]='^';
- s[1]=0;
- s=lineBuffer;
- for(i=0;i<strlen(s)-1;i++)if(isprint(s[i]))s[i]=' ';
- PrintfExit("%s\n",lineBuffer);
- }
-
- /*
- Skip past white space and comments to the beginning of the next token,
- reading as many lines as necessary.
- */
- char *GetToken(FILE *file,char *lineBuffer,char *s,int echo)
- {
- do{
- s=GetToken1(file,lineBuffer,s,echo);
- if(s!=NULL || feof(file))return s;
- }while(1);
- }
-
- /*
- Skip past white space and comments to the beginning of the next token,
- reading one line if necessary. The lineWasBlank Boolean global is only used
- here, ReadAssignmentLine(), and in AssignmentLineWasBlank().
- */
- char *GetToken1(FILE *file,char *lineBuffer,char *s,int echo)
- {
- static char whiteSpace[]=" \t\n\r\v\f";
- extern Boolean lineWasBlank;
-
- s=GetToken0(file,lineBuffer,s,echo);
- if(s!=NULL)return s;
- s=fgets(lineBuffer,BUFFER_SIZE,file);
- if(s==NULL)return NULL;
- if(ECHO_FILE)printf("%s",s);
- if(strlen(s)!=strspn(s,whiteSpace))lineWasBlank=FALSE;
- s=GetToken0(file,lineBuffer,s,echo);
- return s;
- }
-
- /*
- Skip past white space and comments to the beginning of the next token.
- GetToken0 will read in new lines only if needed to find the end of an
- unfinished comment. Input from file is buffered one line at a time in lineBuffer,
- and s points to the next unparsed character in that buffer.
- */
- char *GetToken0(FILE *file,char *lineBuffer,char *s,int echo)
- {
- static char whiteSpace[]=" \t\n\r\v\f";
- char *sTemp;
-
- if(s==NULL)goto endOfLine;
- do{
- /* skip white space and comments */
- do {
- if(strlen(s)==0)goto endOfLine;
- s+=strspn(s,whiteSpace);
- if(strlen(s)==0)goto endOfLine;
- if(streq2(s,"/\*")){
- do{
- sTemp=s;
- s=strstr(s,"*\/");
- if(s!=NULL)break;
- if(ECHO_COMMENTS)printf("%s",sTemp);
- s=fgets(lineBuffer,BUFFER_SIZE,file);
- if(s==NULL)ParsingError(s);
- if(ECHO_FILE)printf("%s",s);
- } while(1);
- s+=2;
- if(ECHO_COMMENTS){
- s[-1]=0;
- printf("%s/",sTemp);
- }
- continue;
- }
- if(streq2(s,"/\/")){
- if(ECHO_COMMENTS){
- if(s[strlen(s)-1]=='\n')s[strlen(s)-1]=0;
- printf(" %s",s);
- }
- goto endOfLine;
- }
- break;
- } while(1);
- return s; /* normal return */
- }while(1);
- endOfLine:
- if((ECHO_ASSIGNMENTS || ECHO_COMMENTS) && s!=NULL)printf("\n");
- return NULL;
- }
-
- double strtodN(char *s,char **sPtr)
- /* Supplement standard C routine strtod() by handling NAN and INF */
- /* The NAN type, if any, is skipped, whether in THINK C style, e.g. NANFF, or in
- MPW C style, e.g. Nan[255]. */
- {
- if(streq2(s,"NAN")||streq2(s,"NaN")||streq2(s,"Nan")||streq2(s,"nan")){
- s+=3;
- s+=strspn(s,"0123456789abcdefABCDEF[]");
- *sPtr=s;
- return NAN;
- }
- if(strncmp(s,"INF",3)==0||strncmp(s,"Inf",3)==0||strncmp(s,"inf",3)==0){
- s+=3;
- *sPtr=s;
- return INF;
- }
- if(strncmp(s,"-INF",4)==0||strncmp(s,"-Inf",4)==0||strncmp(s,"-inf",4)==0){
- s+=4;
- *sPtr=s;
- return -INF;
- }
- return strtod(s,sPtr);
- }
-
- long strtolN(char *s,char **sPtr,short flag)
- /* Supplement standard C routine strtol() by handling quoted char, e.g. 'e' */
- {
- long j;
-
- if(s[0]!='\'') return strtol(s,sPtr,flag);
- s++;
- j=strtoc(s,&s);
- if(s[0]!='\'')ParsingError(s);
- *sPtr=s+1;
- return j;
- }
-
- unsigned long strtoulN(char *s,char **sPtr,short flag)
- /* Supplement standard C routine by handling quoted char, e.g. 'e' */
- {
- unsigned long j;
-
- if(s[0]!='\'') return strtoul(s,sPtr,flag);
- s++;
- j=strtoc(s,&s);
- j &= 255L; /* strip off any sign extension, since char was signed */
- if(s[0]!='\'')ParsingError(s);
- *sPtr=s+1;
- return j;
- }
-
- char strtoc(char *s,char **sPtr)
- /* Extract a character from a string, translating backslash escapes */
- {
- char c;
-
- if(s[0]!='\\'){
- *sPtr=s+1;
- if(s[0]=='"')return 0;
- else return s[0];
- }
- s++;
- switch(s++[0]){
- case 'n': c='\n'; break;
- case 'r': c='\r'; break;
- case 't': c='\t'; break;
- case 'b': c='\b'; break;
- case 'v': c='\v'; break;
- case 'f': c='\f'; break;
- case 'a': c='\a'; break;
- case 'x': c=strtol(s,&s,16); break;
- case '0': case '1':case '2': case '3':case '4': case '5':case '6': case '7':
- c=strtol(s-1,&s,8); break;
- default: c=s[-1]; break;
- }
- *sPtr=s;
- return c;
- }
-