home *** CD-ROM | disk | FTP | other *** search
-
- %{
- /*
- expr.ym
-
- This file defines the grammar for parsing expressions. To fully understand
- this code you will need to understand yacc. The basic idea is that
- the parse tree is built from the bottom up as larger and larger
- sub-expressions are recognized by the grammar. The nodes of the tree
- are created by the alloc* functions at the end of the file. These
- functions are called by the rules of the grammar as various types of
- sub-expressions are recognized. The one entry point to this file,
- _EXPParseExpression() encapsulates all the lex and yacc glue necessary
- to parse an expression.
- */
-
- #import <stdlib.h>
- #import <string.h>
- #import <stdio.h>
- #import "exprDefs.h"
-
- static Term *allocBinaryOpTerm(char op, Term *t1, Term *t2);
- static Term *allocVarTerm(char *name);
- static Term *allocConstantTerm(float value, BOOL isInt);
- static Term *allocFuncTerm(char *name, TermList *args);
- static void addAllocedTerm(Term *t);
- static void yyerror(char *s);
- extern yylex(), yyparse();
-
- /* globals used to build up results of the parse */
- static NXHashTable *ValidFuncTerms; /* list of funcs we allow */
- static NXHashTable *VarTerms; /* vars found in expression */
- static Term *CompleteExpr; /* top of parse tree */
- static BOOL ParseError; /* was there a parse error? */
- static Term **TermsAlloced; /* terms alloced during parse */
- static int NumTermsAlloced; /* #terms alloced during parse */
- static NXZone *ParseZone; /* zone to allocate parse results in */
-
- /* initial size of TermsAlloced ptr array */
- #define STACK_TERMS 200
-
- %}
-
- %token IDENTIFIER NUMBER INTEGER
-
- %union {
- Term *node;
- TermList *list;
- float real;
- int integer;
- char *string;
- char character;
- }
-
- %token <real> NUMBER
- %token <integer> INTEGER
- %token <string> IDENTIFIER
- %token <character> BADCHAR
- %type <node> complete_expr expr function
- %type <list> arglist
-
- %start complete_expr
-
- %left '+' '-'
- %left '*' '/' '%'
- %left UMINUS
- %right '^'
-
- %%
-
- complete_expr: expr
- { CompleteExpr = $$; }
- ;
-
- expr : '(' expr ')'
- { $$ = $2; }
- | expr '+' expr
- { $$ = allocBinaryOpTerm('+', $1, $3); }
- | expr '-' expr
- { $$ = allocBinaryOpTerm('-', $1, $3); }
- | expr '*' expr
- { $$ = allocBinaryOpTerm('*', $1, $3); }
- | expr '/' expr
- { $$ = allocBinaryOpTerm('/', $1, $3); }
- | expr '%' expr
- { $$ = allocBinaryOpTerm('%', $1, $3); }
- | expr '^' expr
- { $$ = allocBinaryOpTerm('^', $1, $3); }
- | '-' expr %prec UMINUS
- { $$ = _EXPAllocTerm(ParseZone, binOpTerm, 1, $2);
- $$->data.binOp.op = '-';
- }
- | function
- | IDENTIFIER
- { $$ = allocVarTerm($1); }
- | NUMBER
- { $$ = allocConstantTerm($1, NO); }
- | INTEGER
- { $$ = allocConstantTerm($1, YES); }
- ;
-
- function : IDENTIFIER '(' ')'
- { $$ = allocFuncTerm($1, NULL); }
- | IDENTIFIER '(' arglist ')'
- { $$ = allocFuncTerm($1, $3); }
- ;
-
- arglist : expr
- { $$ = NXZoneMalloc(NXDefaultMallocZone(), sizeof(TermList));
- $$->terms[0] = $1;
- $$->num = 1;
- }
- | arglist ',' expr
- { $$ = NXZoneRealloc(NXDefaultMallocZone(), $1,
- sizeof(TermList) + $1->num*sizeof(Term *));
- $$->terms[$$->num] = $3;
- $$->num++;
- }
- ;
-
- %%
-
- /*
- * The main entry point into this file. This routine sets up some globals
- * and calls yyparse(), a routine generated by yacc, to do the actual parse.
- * If the parse succeeds, the results are found in the globals, and are
- * returned to the caller.
- *
- * Note because of the globals this files uses to communicate between this
- * routine and the parsing guts, this is not thread safe (the yacc and lex
- * internals probably aren't thread safe either). One easy solution to this
- * would be to put a mutex lock around the whole parse.
- *
- * If there is a parse error, since the tree is built bottom up it can be
- * difficult to free the data structures allocated before the error is
- * detected. To solve this problem we keep a global buffer of pointers to
- * the parse tree nodes as they are allocated. In the event of an error
- * we can then easily free them all regardless of the state of the parse tree.
- * Its also very important to empty the varTerms hash table in this case, so
- * it is not returned full of freed nodes.
- */
- BOOL _EXPParseExpression(const char *text, NXHashTable *validTerms, Term **parseTree, NXHashTable *varTerms, NXZone *zone) {
- int parseRet;
- Term *termsAllocedStackSpace[STACK_TERMS];
- int i;
-
- CompleteExpr = NULL; /* set globals to prepare for parsing */
- VarTerms = varTerms;
- ParseError = NO;
- ValidFuncTerms = validTerms;
- TermsAlloced = termsAllocedStackSpace;
- NumTermsAlloced = 0;
- ParseZone = zone;
- _EXPPrepareToScan(text); /* sets up lex to scan the string */
- parseRet = yyparse();
- if (parseRet == 1 || ParseError) {
- for (i = 0; i < NumTermsAlloced; i++)
- _EXPFreeTerm(NULL, TermsAlloced[i]);
- *parseTree = NULL;
- NXEmptyHashTable(varTerms);
- } else
- *parseTree = CompleteExpr;
- if (NumTermsAlloced > STACK_TERMS)
- NXZoneFree(NXDefaultMallocZone(), TermsAlloced);
- return !(parseRet == 1 || ParseError);
- }
-
- /* allocates a new binary operator term */
- static Term *allocBinaryOpTerm(char op, Term *t1, Term *t2) {
- Term *newTerm;
-
- newTerm = _EXPAllocTerm(ParseZone, binOpTerm, 2, t1, t2);
- newTerm->data.binOp.op = op;
- addAllocedTerm(newTerm);
- return newTerm;
- }
-
- /*
- * Allocates a new variable term. We first check to see if the variable has
- * already been seen in this parse, and if so return the existing variable
- * term. Otherwise we go ahead and allocate a new one.
- */
- static Term *allocVarTerm(char *name) {
- Term *newTerm;
- Term key;
-
- key.tag = varTerm;
- key.data.var.name = name;
- newTerm = NXHashGet(VarTerms, &key);
- if (!newTerm) {
- newTerm = _EXPAllocTerm(ParseZone, varTerm, 0);
- newTerm->data.var.name = NXCopyStringBufferFromZone(name, ParseZone);
- NXHashInsert(VarTerms, newTerm);
- addAllocedTerm(newTerm);
- }
- free(name);
- return newTerm;
- }
-
- /* allocates a new constant term */
- static Term *allocConstantTerm(float value, BOOL isInt) {
- Term *newTerm;
-
- newTerm = _EXPAllocTerm(ParseZone, constantTerm, 0);
- newTerm->data.constant.val = value;
- newTerm->data.constant.isInt = isInt;
- addAllocedTerm(newTerm);
- return newTerm;
- }
-
- /*
- * Allocates a new function term. It looks up the function by name to see
- * if it is one we know how to parse. If so, it makes sure the number of
- * arguments being passed is correct for the function. If the function is
- * unknown or the number of arguments is wrong, we record the fact that
- * we've had a parse error in a global.
- */
- static Term *allocFuncTerm(char *name, TermList *args) {
- Term *newTerm;
- Function *func;
- Function key;
- TermList noArgs;
-
- if (!args) {
- args = &noArgs;
- args->num = 0;
- }
- key.name = (char *)name;
- func = NXHashGet(ValidFuncTerms, &key);
- newTerm = _EXPAllocTerm(ParseZone, funcTerm, args->num);
- bcopy(args->terms, newTerm->subterms, args->num * sizeof(Term *));
- newTerm->data.func.type = func;
- if (!func || args->num < func->minArgs ||
- (args->num > func->maxArgs && func->maxArgs != -1))
- ParseError = YES;
- NXZoneFree(NXDefaultMallocZone(), args);
- free(name);
- addAllocedTerm(newTerm);
- return newTerm;
- }
-
- /*
- * Adds a term to the list of terms that we have allocated during this parse.
- * This routine allocates more space for Term pointers if necessary. It knows
- * not to free the initial buffer of these pointers, since that buffer is
- * allocated on the stack by _EXPParseExpression().
- */
- static void addAllocedTerm(Term *t) {
- Term **newSpace;
-
- if (!(NumTermsAlloced % STACK_TERMS) && NumTermsAlloced > 0) {
- newSpace = NXZoneMalloc(NXDefaultMallocZone(),
- (NumTermsAlloced + STACK_TERMS) * sizeof(Term *));
- bcopy(TermsAlloced, newSpace, NumTermsAlloced * sizeof(Term *));
- if (NumTermsAlloced > STACK_TERMS)
- NXZoneFree(NXDefaultMallocZone(), TermsAlloced);
- TermsAlloced = newSpace;
- }
- TermsAlloced[NumTermsAlloced++] = t;
- }
-
- /* a piece of yacc glue called when there is a parse error */
- static void yyerror(char *s) {}
-