📄 expr2.c
字号:
c->operand = 1; return COMMA; case '\0': check_implied_arg(c); c->operand = 1; c->ptr--; return EOI; /* * The {...} operator is really a hack that is left over * from the old math parser. The support for it here is * a hack. The entire thing is a hack. */ case '{': { char *p = c->ptr; char oc = 0; ssize_t span; if (!c->operand) return lexerr(c, "Misplaced { token"); if ((span = MatchingBracket(p, '{', '}')) >= 0) { c->ptr = p + span; oc = *c->ptr; *c->ptr = 0; } else c->ptr = empty_string; c->last_token = 0; if (!c->noeval) { char * result; result = call_lambda_function(NULL, p, c->args); c->last_token = tokenize_expanded(c, result); new_free(&result); } if (oc) *c->ptr++ = oc; c->operand = 0; return ID; } /******************** OPERAND TYPES **********************/ /* * This is an UNEXPANDED-STRING operand type. * Extract everything inside the [...]'s, and then * tokenize that as a "raw" token. It will be expanded * on an as-needed basis. * * If we are in the no-eval section of a short-circuit, * then we throw away this token entirely. */ case '[': { char *p = c->ptr; char oc = 0; ssize_t span; if (!c->operand) return lexerr(c, "Misplaced [ token"); if ((span = MatchingBracket(p, '[', ']')) >= 0) { c->ptr = p + span; oc = *c->ptr; *c->ptr = 0; } else c->ptr = empty_string; if (c->noeval) c->last_token = 0; else c->last_token = tokenize_raw(c, p); if (oc) *c->ptr++ = oc; c->operand = 0; return ID; } /* The "space" characters. */ case 9: case 10: case 11: case 12: case 13: case ' ': start++; break; /* * This is a NUMBER operand type. This may seem unusual, * but we actually tokenize the string representation of * the number as an "expanded" token type. Why do we do * this? Because in the most common case, the user is * doing something like: * * @ var = 0 * * Now we already have the "0" in string format (natch), * and we will never actually reference the 0 as a number * per se -- all assignments are done on strings, so we * need the string anyhow. So when we lex a number in the * expression, we tokenize it as an 'expanded string' and * then if we ever have to actually use the number in any * particular context, it will be converted as-needed. */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { char *end; char endc; c->operand = 0; c->ptr--; strtod(c->ptr, &end); endc = *end; *end = 0; if (c->noeval) c->last_token = 0; else c->last_token = tokenize_expanded(c, c->ptr); *end = endc; c->ptr = end; return ID; } /* * Handle those weirdo $-values */ case '$': continue; /* * This is an LVAL operand type. This also may seem * unusual, but lval's may contain $'s, which need to * be expanded. They may contain function calls, which * must only be expanded *once*. When we lex out an lval * (such as "var" in "@ var = 5"), we tokenize the lval * as an unexpanded lval. Then, if we ever actually need * to reference the proper variable name, we will get the * "raw" value, which will be the lval after passed through * expand_alias(). If we want to get the *value of the * variable $lval), then we will get the "expanded" value, * which will use the "raw" value to do the variable name * lookup. See? It's really pretty straightforward. The * reason we do all this is to make sure that the expansion * of the variable name happens *at most once*, and that if * the variable is not actually referenced, then the expansion * isn't done at all. */ default:handle_expando: { char *end; char endc; c->operand = 0; c->ptr--; if ((end = after_expando_special(c))) { endc = *end; *end = 0; /* * If we are in the short-circuit of a noeval, * then we throw the token away. */ if (c->noeval) c->last_token = 0; else c->last_token = tokenize_lval(c, start); *end = endc; c->ptr = end; } else { c->last_token = 0; /* Empty token */ c->ptr = empty_string; } if (x_debug & DEBUG_NEW_MATH_DEBUG) yell("After token: [%s]", c->ptr); return ID; } } }}/******************************* STATE MACHINE *****************************//* * mathparse -- this is the state machine that actually parses the * expression. The parsing is done through a shift-reduce mechanism, * and all the precedence levels lower than 'pc' are evaluated. */static void mathparse (expr_info *c, int pc){ int otok, onoeval; /* * Drop out of parsing if an error has occured */ if (c->errflag) return; /* * Get the next token in the expression */ c->mtok = zzlex(c); /* * For as long as the next operator indicates a shift operation... */ while (prec[c->mtok] <= pc) { /* Drop out if an error has occured */ if (c->errflag) return; /* * Figure out what to do with this token that needs * to be shifted. */ switch (c->mtok) { /* * This is any kind of an indentifier. There are several * that we handle: * * VARIABLE REFERENCE ie, "foo" in "@ foo = 2" * NUMBER ie, "2" in "@ foo = 2" * UNEXPANDED STRING ie, "[boo]" in "@ foo = [boo]" * * The actual determination of which type is done in the lexer. * We just get a token id for the resulting identifier. * Getting the value is done on an as-needed basis. */ case ID: if (x_debug & DEBUG_NEW_MATH_DEBUG) yell("Parsed identifier token [%s]", get_token_expanded(c, c->last_token)); /* * The lexer sets the last token to * 0 if noeval is set. This saves us * from having to tokenize a string * that we expressly will not use. */ push_token(c, c->last_token); break; /* * An open-parenthesis indicates that we should * recursively evaluate the inside of the paren-set. */ case M_INPAR: { if (x_debug & DEBUG_NEW_MATH_DEBUG) yell("Parsed open paren"); mathparse(c, TOPPREC); /* * Of course if the expression ends without * a matching rparen, then we whine about it. */ if (c->mtok != M_OUTPAR) { if (!c->errflag) error("')' expected"); return; } break; } /* * A question mark requires that we check for short * circuiting. We check the lhs, and if it is true, * then we evaluate the lhs of the colon. If it is * false then we just parse the lhs of the colon and * evaluate the rhs of the colon. */ case QUEST: { BooL u = pop_boolean(c); push_boolean(c, u); if (!u) c->noeval++; mathparse(c, prec[QUEST] - 1); if (!u) c->noeval--; else c->noeval++; mathparse(c, prec[QUEST]); if (u) c->noeval--; reduce(c, QUEST); continue; } /* * All other operators handle normally */ default: { /* Save state */ otok = c->mtok; onoeval = c->noeval; /* * Check for short circuiting. */ if (assoc[otok] == BOOL) { if (x_debug & DEBUG_NEW_MATH_DEBUG) yell("Parsed short circuit operator"); switch (otok) { case DAND: case DANDEQ: { BooL u = pop_boolean(c); push_boolean(c, u); if (!u) c->noeval++; break; } case DOR: case DOREQ: { BooL u = pop_boolean(c); push_boolean(c, u); if (u) c->noeval++; break; } } } if (x_debug & DEBUG_NEW_MATH_DEBUG) yell("Parsed operator of type [%d]", otok); /* * Parse the right hand side through * recursion if we're doing things R->L. */ mathparse(c, prec[otok] - (assoc[otok] != RL)); /* * Then reduce this operation. */ c->noeval = onoeval; reduce(c, otok); continue; } } /* * Grab the next token */ c->mtok = zzlex(c); }}/******************************** HARNASS **********************************//* * This is the new math parser. It sets up an execution context, which * contains sundry information like all the extracted tokens, intermediate * tokens, shifted tokens, and the like. The expression context is passed * around from function to function, each function is totaly independant * of state information stored in global variables. Therefore, this math * parser is re-entrant safe. */static char * matheval (char *s, const char *args, int *args_flag){ expr_info context; char * ret = NULL; /* Sanity check */ if (!s || !*s) return malloc_strdup(empty_string); /* Create new state */ setup_expr_info(&context); context.ptr = s; context.args = args; context.args_flag = args_flag; /* Actually do the parsing */ mathparse(&context, TOPPREC); /* Check for error */ if (context.errflag) { ret = malloc_strdup(empty_string); goto cleanup; } /* Check for leftover operands */ if (context.sp) error("The expression has too many operands"); if (x_debug & DEBUG_NEW_MATH_DEBUG) { int i; yell("Terms left: %d", context.sp); for (i = 0; i <= context.sp; i++) yell("Term [%d]: [%s]", i, get_token_expanded(&context, context.stack[i])); } /* Get the return value, if requested */ ret = malloc_strdup(get_token_expanded(&context, pop_token(&context)));cleanup: /* Clean up and restore order */ destroy_expr_info(&context); if (x_debug & DEBUG_NEW_MATH_DEBUG) yell("Returning [%s]", ret); /* Return the result */ return ret;}/******************************* SUPPORT *************************************//* * after_expando_special: This is a special version of after_expando that * can handle parsing out lvalues in expressions. Due to the eclectic nature * of lvalues in expressions, this is quite a bit different than the normal * after_expando, requiring a different function. Ugh. * * This replaces some much more complicated logic strewn * here and there that attempted to figure out just how long an expando * name was supposed to be. Well, now this changes that. This will slurp * up everything in 'start' that could possibly be put after a $ that could * result in a syntactically valid expando. All you need to do is tell it * if the expando is an rvalue or an lvalue (it *does* make a difference) */static char * after_expando_special (expr_info *c){ char *start; char *rest; int call; if (!(start = c->ptr)) return c->ptr; for (;;) { rest = after_expando(start, 0, &call); if (*rest != '$') break; start = rest + 1; } if (c->ptr == rest) { yell("Erf. I'm trying to find an lval at [%s] and I'm not " "having much luck finding one. Punting the rest of " "this expression", c->ptr); return NULL; } /* * All done! */ return rest;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -