|
|
1.1 ! root 1: ! 2: %{ ! 3: /* ! 4: expr.ym ! 5: ! 6: This file defines the grammar for parsing expressions. To fully understand ! 7: this code you will need to understand yacc. The basic idea is that ! 8: the parse tree is built from the bottom up as larger and larger ! 9: sub-expressions are recognized by the grammar. The nodes of the tree ! 10: are created by the alloc* functions at the end of the file. These ! 11: functions are called by the rules of the grammar as various types of ! 12: sub-expressions are recognized. The one entry point to this file, ! 13: _EXPParseExpression() encapsulates all the lex and yacc glue necessary ! 14: to parse an expression. ! 15: */ ! 16: ! 17: #import <stdlib.h> ! 18: #import <string.h> ! 19: #import <stdio.h> ! 20: #import "exprDefs.h" ! 21: ! 22: static Term *allocBinaryOpTerm(char op, Term *t1, Term *t2); ! 23: static Term *allocVarTerm(char *name); ! 24: static Term *allocConstantTerm(float value, BOOL isInt); ! 25: static Term *allocFuncTerm(char *name, TermList *args); ! 26: static void addAllocedTerm(Term *t); ! 27: static void yyerror(char *s); ! 28: extern yylex(), yyparse(); ! 29: ! 30: /* globals used to build up results of the parse */ ! 31: static NXHashTable *ValidFuncTerms; /* list of funcs we allow */ ! 32: static NXHashTable *VarTerms; /* vars found in expression */ ! 33: static Term *CompleteExpr; /* top of parse tree */ ! 34: static BOOL ParseError; /* was there a parse error? */ ! 35: static Term **TermsAlloced; /* terms alloced during parse */ ! 36: static int NumTermsAlloced; /* #terms alloced during parse */ ! 37: static NXZone *ParseZone; /* zone to allocate parse results in */ ! 38: ! 39: /* initial size of TermsAlloced ptr array */ ! 40: #define STACK_TERMS 200 ! 41: ! 42: %} ! 43: ! 44: %token IDENTIFIER NUMBER INTEGER ! 45: ! 46: %union { ! 47: Term *node; ! 48: TermList *list; ! 49: float real; ! 50: int integer; ! 51: char *string; ! 52: char character; ! 53: } ! 54: ! 55: %token <real> NUMBER ! 56: %token <integer> INTEGER ! 57: %token <string> IDENTIFIER ! 58: %token <character> BADCHAR ! 59: %type <node> complete_expr expr function ! 60: %type <list> arglist ! 61: ! 62: %start complete_expr ! 63: ! 64: %left '+' '-' ! 65: %left '*' '/' '%' ! 66: %left UMINUS ! 67: %right '^' ! 68: ! 69: %% ! 70: ! 71: complete_expr: expr ! 72: { CompleteExpr = $$; } ! 73: ; ! 74: ! 75: expr : '(' expr ')' ! 76: { $$ = $2; } ! 77: | expr '+' expr ! 78: { $$ = allocBinaryOpTerm('+', $1, $3); } ! 79: | expr '-' expr ! 80: { $$ = allocBinaryOpTerm('-', $1, $3); } ! 81: | expr '*' expr ! 82: { $$ = allocBinaryOpTerm('*', $1, $3); } ! 83: | expr '/' expr ! 84: { $$ = allocBinaryOpTerm('/', $1, $3); } ! 85: | expr '%' expr ! 86: { $$ = allocBinaryOpTerm('%', $1, $3); } ! 87: | expr '^' expr ! 88: { $$ = allocBinaryOpTerm('^', $1, $3); } ! 89: | '-' expr %prec UMINUS ! 90: { $$ = _EXPAllocTerm(ParseZone, binOpTerm, 1, $2); ! 91: $$->data.binOp.op = '-'; ! 92: } ! 93: | function ! 94: | IDENTIFIER ! 95: { $$ = allocVarTerm($1); } ! 96: | NUMBER ! 97: { $$ = allocConstantTerm($1, NO); } ! 98: | INTEGER ! 99: { $$ = allocConstantTerm($1, YES); } ! 100: ; ! 101: ! 102: function : IDENTIFIER '(' ')' ! 103: { $$ = allocFuncTerm($1, NULL); } ! 104: | IDENTIFIER '(' arglist ')' ! 105: { $$ = allocFuncTerm($1, $3); } ! 106: ; ! 107: ! 108: arglist : expr ! 109: { $$ = NXZoneMalloc(NXDefaultMallocZone(), sizeof(TermList)); ! 110: $$->terms[0] = $1; ! 111: $$->num = 1; ! 112: } ! 113: | arglist ',' expr ! 114: { $$ = NXZoneRealloc(NXDefaultMallocZone(), $1, ! 115: sizeof(TermList) + $1->num*sizeof(Term *)); ! 116: $$->terms[$$->num] = $3; ! 117: $$->num++; ! 118: } ! 119: ; ! 120: ! 121: %% ! 122: ! 123: /* ! 124: * The main entry point into this file. This routine sets up some globals ! 125: * and calls yyparse(), a routine generated by yacc, to do the actual parse. ! 126: * If the parse succeeds, the results are found in the globals, and are ! 127: * returned to the caller. ! 128: * ! 129: * Note because of the globals this files uses to communicate between this ! 130: * routine and the parsing guts, this is not thread safe (the yacc and lex ! 131: * internals probably aren't thread safe either). One easy solution to this ! 132: * would be to put a mutex lock around the whole parse. ! 133: * ! 134: * If there is a parse error, since the tree is built bottom up it can be ! 135: * difficult to free the data structures allocated before the error is ! 136: * detected. To solve this problem we keep a global buffer of pointers to ! 137: * the parse tree nodes as they are allocated. In the event of an error ! 138: * we can then easily free them all regardless of the state of the parse tree. ! 139: * Its also very important to empty the varTerms hash table in this case, so ! 140: * it is not returned full of freed nodes. ! 141: */ ! 142: BOOL _EXPParseExpression(const char *text, NXHashTable *validTerms, Term **parseTree, NXHashTable *varTerms, NXZone *zone) { ! 143: int parseRet; ! 144: Term *termsAllocedStackSpace[STACK_TERMS]; ! 145: int i; ! 146: ! 147: CompleteExpr = NULL; /* set globals to prepare for parsing */ ! 148: VarTerms = varTerms; ! 149: ParseError = NO; ! 150: ValidFuncTerms = validTerms; ! 151: TermsAlloced = termsAllocedStackSpace; ! 152: NumTermsAlloced = 0; ! 153: ParseZone = zone; ! 154: _EXPPrepareToScan(text); /* sets up lex to scan the string */ ! 155: parseRet = yyparse(); ! 156: if (parseRet == 1 || ParseError) { ! 157: for (i = 0; i < NumTermsAlloced; i++) ! 158: _EXPFreeTerm(NULL, TermsAlloced[i]); ! 159: *parseTree = NULL; ! 160: NXEmptyHashTable(varTerms); ! 161: } else ! 162: *parseTree = CompleteExpr; ! 163: if (NumTermsAlloced > STACK_TERMS) ! 164: NXZoneFree(NXDefaultMallocZone(), TermsAlloced); ! 165: return !(parseRet == 1 || ParseError); ! 166: } ! 167: ! 168: /* allocates a new binary operator term */ ! 169: static Term *allocBinaryOpTerm(char op, Term *t1, Term *t2) { ! 170: Term *newTerm; ! 171: ! 172: newTerm = _EXPAllocTerm(ParseZone, binOpTerm, 2, t1, t2); ! 173: newTerm->data.binOp.op = op; ! 174: addAllocedTerm(newTerm); ! 175: return newTerm; ! 176: } ! 177: ! 178: /* ! 179: * Allocates a new variable term. We first check to see if the variable has ! 180: * already been seen in this parse, and if so return the existing variable ! 181: * term. Otherwise we go ahead and allocate a new one. ! 182: */ ! 183: static Term *allocVarTerm(char *name) { ! 184: Term *newTerm; ! 185: Term key; ! 186: ! 187: key.tag = varTerm; ! 188: key.data.var.name = name; ! 189: newTerm = NXHashGet(VarTerms, &key); ! 190: if (!newTerm) { ! 191: newTerm = _EXPAllocTerm(ParseZone, varTerm, 0); ! 192: newTerm->data.var.name = NXCopyStringBufferFromZone(name, ParseZone); ! 193: NXHashInsert(VarTerms, newTerm); ! 194: addAllocedTerm(newTerm); ! 195: } ! 196: free(name); ! 197: return newTerm; ! 198: } ! 199: ! 200: /* allocates a new constant term */ ! 201: static Term *allocConstantTerm(float value, BOOL isInt) { ! 202: Term *newTerm; ! 203: ! 204: newTerm = _EXPAllocTerm(ParseZone, constantTerm, 0); ! 205: newTerm->data.constant.val = value; ! 206: newTerm->data.constant.isInt = isInt; ! 207: addAllocedTerm(newTerm); ! 208: return newTerm; ! 209: } ! 210: ! 211: /* ! 212: * Allocates a new function term. It looks up the function by name to see ! 213: * if it is one we know how to parse. If so, it makes sure the number of ! 214: * arguments being passed is correct for the function. If the function is ! 215: * unknown or the number of arguments is wrong, we record the fact that ! 216: * we've had a parse error in a global. ! 217: */ ! 218: static Term *allocFuncTerm(char *name, TermList *args) { ! 219: Term *newTerm; ! 220: Function *func; ! 221: Function key; ! 222: TermList noArgs; ! 223: ! 224: if (!args) { ! 225: args = &noArgs; ! 226: args->num = 0; ! 227: } ! 228: key.name = (char *)name; ! 229: func = NXHashGet(ValidFuncTerms, &key); ! 230: newTerm = _EXPAllocTerm(ParseZone, funcTerm, args->num); ! 231: bcopy(args->terms, newTerm->subterms, args->num * sizeof(Term *)); ! 232: newTerm->data.func.type = func; ! 233: if (!func || args->num < func->minArgs || ! 234: (args->num > func->maxArgs && func->maxArgs != -1)) ! 235: ParseError = YES; ! 236: NXZoneFree(NXDefaultMallocZone(), args); ! 237: free(name); ! 238: addAllocedTerm(newTerm); ! 239: return newTerm; ! 240: } ! 241: ! 242: /* ! 243: * Adds a term to the list of terms that we have allocated during this parse. ! 244: * This routine allocates more space for Term pointers if necessary. It knows ! 245: * not to free the initial buffer of these pointers, since that buffer is ! 246: * allocated on the stack by _EXPParseExpression(). ! 247: */ ! 248: static void addAllocedTerm(Term *t) { ! 249: Term **newSpace; ! 250: ! 251: if (!(NumTermsAlloced % STACK_TERMS) && NumTermsAlloced > 0) { ! 252: newSpace = NXZoneMalloc(NXDefaultMallocZone(), ! 253: (NumTermsAlloced + STACK_TERMS) * sizeof(Term *)); ! 254: bcopy(TermsAlloced, newSpace, NumTermsAlloced * sizeof(Term *)); ! 255: if (NumTermsAlloced > STACK_TERMS) ! 256: NXZoneFree(NXDefaultMallocZone(), TermsAlloced); ! 257: TermsAlloced = newSpace; ! 258: } ! 259: TermsAlloced[NumTermsAlloced++] = t; ! 260: } ! 261: ! 262: /* a piece of yacc glue called when there is a parse error */ ! 263: static void yyerror(char *s) {}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.