/* * * Simple scanner and recursive descent parser used to handle raster file * editing expressions. * * These were the last routines I wrote and by the time I got done it was * pretty clear that I should have written writevalue(), readvalue(), and * getvalue() a little differently. Anyway things seem to work OK even * though everything could have been better, so I'm not going to change * things right now. * */ #include <stdio.h> #include <ctype.h> #include "gen.h" /* general purpose definitions */ #include "rast.h" /* Imagen raster file definitions */ #include "buildrast.h" /* really just for Charinfor def */ #include "editrast.h" /* defs primarily used by scanner */ FILE *fpin; /* everything comes from this FILE */ char buf[50]; /* input buffer */ Tokens tokens[] = { /* tokens recognized by scanner */ "=", ASSIGNOP, "(", LPAREN, ")", RPAREN, "+", PLUS, "-", MINUS, "*", TIMES, "/", DIVIDE, "xref", XREF, "yref", YREF, "height", HEIGHT, "width", WIDTH, "chwidth", CHWIDTH, "endedit", ENDEDIT, "build", BUILD }; int lasttoken; /* code identifying last token read */ extern Charinfo charinfo[]; /* used as the symbol table - sort of */ extern first, last; /* valid entries are in this closed interval */ /* * * Everything's done in floating point, so all the routines that handle the * parsing need to be declared as float. * */ float expr(), exprprime(), term(), termprime(), factor(); /*****************************************************************************/ edit(fp) FILE *fp; /* reading stuff from this file */ { char name[20]; /* of the character we're editing */ int lhs_index; /* location in charinfo[] */ int lhs_field; /* field we want to change */ int value; /* of the expression */ /* * * Called when we want to process editing expressions from file *fp. * */ fpin = fp; /* nexttoken() reads from fpin */ nexttoken(); /* loop assumes we've read first token */ while ( lasttoken != ENDEDIT && lasttoken != BUILD ) { if ( (lhs_field = lasttoken) != XREF && lhs_field != YREF && lhs_field != CHWIDTH ) error(FATAL, "illegal assignment"); fscanf(fpin, "%s", name); if ( (lhs_index = lookup(name)) == -1 ) error(FATAL, "can't find character %s", name); nexttoken(); /* better be an assignment operator */ if ( lasttoken != ASSIGNOP ) error(FATAL, "missing assignment operator"); nexttoken(); /* first token in the expression */ value = expr() + .5; if ( lhs_field == XREF ) writevalue(value, G_XREF, charinfo[lhs_index].glydir + rst[G_XREF].offset); else if ( lhs_field == YREF ) writevalue(value, G_YREF, charinfo[lhs_index].glydir + rst[G_YREF].offset); else if ( lhs_field == CHWIDTH ) charinfo[lhs_index].chwidth = value; } /* End while */ } /* End of edit */ /*****************************************************************************/ lookup(name) char *name; /* find this guy in charinfo[] */ { int i; /* loop index for lookups */ /* * * Looks for character *name in the charinfo[] array. If it's found its * index is returned to the caller, otherwise -1 is returned. We need to * know the values of first and last so we can properly restrict the search. * They're must be declared and set in whatever files use these routines. * For now it only includes buildrast.c, but we may want to make the editing * stuff more general. * */ for ( i = first; i <= last; i++ ) if ( strcmp(name, charinfo[i].name) == 0 ) return(i); return(-1); } /* End of lookup */ /*****************************************************************************/ nexttoken() { int i; /* loop index for lookups in tokens[] */ /* * * Reads the input file *fpin looking for the next token. A code identifying * it is saved in lasttoken and the actual token string is saved in buf[]. * If we reach EOF lasttoken will be set to ENDEDIT. * */ if ( fscanf(fpin, "%1s", buf) == EOF ) { lasttoken = ENDEDIT; return; } /* End if */ if ( isdigit(buf[0]) ) { /* must be a constant - integers only */ fscanf(fpin, "%[0-9]", &buf[1]); lasttoken = CONSTANT; return; } /* End if */ if ( isalpha(buf[0]) ) /* get the rest of the token */ fscanf(fpin, "%[a-zA-Z]", &buf[1]); for ( i = 0; i < sizeof(tokens); i++ ) if ( strcmp(buf, tokens[i].str) == 0 ) { lasttoken = tokens[i].code; return; } /* End if */ error(FATAL, "don't recognize token %s", buf); } /* End of nexttoken */ /*****************************************************************************/ float expr() { /* * * Handles the productions: * * EXPR -> TERM EXPR' * */ return(exprprime(term())); } /* End of expr */ /*****************************************************************************/ float exprprime(val) float val; /* add stuff to this guy */ { /* * * Handles the productions: * * EXPR' -> + TERM EXPR' * EXPR' -> * */ if ( lasttoken == PLUS ) { nexttoken(); return(exprprime(val + term())); } else if ( lasttoken == MINUS ) { nexttoken(); return(exprprime(val - term())); } else return(val); } /* End of exprprime */ /*****************************************************************************/ float term() { /* * * Handles the production: * * TERM -> FACTOR TERM' * */ return(termprime(factor())); } /* End of term */ /*****************************************************************************/ float termprime(val) float val; /* value up to this point */ { /* * * Handles the productions: * * TERM' -> * FACTOR TERM' * TERM' -> / FACTOR TERM' * TERM' -> * */ if ( lasttoken == TIMES ) { nexttoken(); return(termprime(val * factor())); } else if ( lasttoken == DIVIDE ) { nexttoken(); return(termprime(val / factor())); } else return(val); } /* End of termprime */ /*****************************************************************************/ float factor() { float val; /* value of the factor */ /* * * Handles the productions: * * FACTOR -> ( EXPR ) * FACTOR -> - EXPR * FACTOR -> CONSTANT * FACTOR -> IDENT * */ if ( lasttoken == CONSTANT ) { val = atoi(buf); nexttoken(); } else if ( lasttoken == MINUS ) val = exprprime(0); else if ( lasttoken == LPAREN ) { nexttoken(); val = expr(); if ( lasttoken == RPAREN ) nexttoken(); else error(FATAL, "expression syntax error - missing right paren"); } else if ( lasttoken == XREF || lasttoken == YREF || lasttoken == HEIGHT || lasttoken == WIDTH || lasttoken == CHWIDTH ) { val = charvalue(lasttoken); nexttoken(); } else error(FATAL, "syntax error - don't recognize factor"); return(val); } /* End of factor */ /*****************************************************************************/ charvalue(tok) int tok; /* number of the last token */ { char name[20]; /* name of the character to lookup */ int i; /* its index in charinfo[] */ /* * * Called when we want to get the value of XREF, YREF, HEIGHT, WIDTH, or * CHWIDTH for a character. The guy we'll be looking up is the next string * in the input file. * * It's really clear that things, especially readvalue(), could have been * done better - sorry. * */ fscanf(fpin, "%s", name); if ( (i = lookup(name)) == -1 ) error(FATAL, "can't find character %s", name); if ( tok == XREF ) return(readvalue(charinfo[i].glydir + rst[G_XREF].offset, rst[G_XREF].size, rst[G_XREF].type)); else if ( tok == YREF ) return(readvalue(charinfo[i].glydir + rst[G_YREF].offset, rst[G_YREF].size, rst[G_YREF].type)); else if ( tok == HEIGHT ) return(readvalue(charinfo[i].glydir + rst[G_HEIGHT].offset, rst[G_HEIGHT].size, rst[G_HEIGHT].type)); else if ( tok == WIDTH ) return(readvalue(charinfo[i].glydir + rst[G_WIDTH].offset, rst[G_WIDTH].size, rst[G_WIDTH].type)); else error(FATAL, "CHWIDTH not implemented yet"); } /* End of charvalue */ /*****************************************************************************/