mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-02-22 17:42:39 +00:00
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@10762 d0543943-73ff-0310-b7d9-9358b9ac24b2
1421 lines
34 KiB
C
1421 lines
34 KiB
C
/*
|
|
File: exprpars.c
|
|
Auth: Brian Allen Vanderburg II
|
|
Date: Wednesday, April 30, 2003
|
|
Desc: Actual parsing routines for this library
|
|
|
|
This file is part of ExprEval.
|
|
*/
|
|
|
|
/* Includes */
|
|
#include "exprincl.h"
|
|
|
|
#include "exprpriv.h"
|
|
#include "exprmem.h"
|
|
#include "switch_utils.h"
|
|
|
|
/* Data structure used by parser */
|
|
typedef struct _exprToken {
|
|
int type; /* token type */
|
|
int start; /* token start position */
|
|
int end; /* token end position */
|
|
|
|
union _tdata {
|
|
char *str; /* string data */
|
|
EXPRTYPE val; /* value data */
|
|
} data;
|
|
} exprToken;
|
|
|
|
/* Defines for token types */
|
|
#define EXPR_TOKEN_UNKNOWN 0
|
|
#define EXPR_TOKEN_OPAREN 1
|
|
#define EXPR_TOKEN_CPAREN 2
|
|
#define EXPR_TOKEN_IDENTIFIER 3
|
|
#define EXPR_TOKEN_VALUE 4
|
|
#define EXPR_TOKEN_PLUS 5
|
|
#define EXPR_TOKEN_HYPHEN 6
|
|
#define EXPR_TOKEN_ASTERISK 7
|
|
#define EXPR_TOKEN_FSLASH 8
|
|
#define EXPR_TOKEN_AMPERSAND 9
|
|
#define EXPR_TOKEN_SEMICOLON 10
|
|
#define EXPR_TOKEN_COMMA 11
|
|
#define EXPR_TOKEN_EQUAL 12
|
|
#define EXPR_TOKEN_HAT 13
|
|
|
|
/* Internal functions */
|
|
int exprMultiParse(exprObj * obj, exprNode * node, exprToken * tokens, int count);
|
|
int exprInternalParse(exprObj * obj, exprNode * node, exprToken * tokens, int start, int end);
|
|
int exprInternalParseAssign(exprObj * obj, exprNode * node, exprToken * tokens, int start, int end, int index);
|
|
int exprInternalParseAdd(exprObj * obj, exprNode * node, exprToken * tokens, int start, int end, int index);
|
|
int exprInternalParseSub(exprObj * obj, exprNode * node, exprToken * tokens, int start, int end, int index);
|
|
int exprInternalParseMul(exprObj * obj, exprNode * node, exprToken * tokens, int start, int end, int index);
|
|
int exprInternalParseDiv(exprObj * obj, exprNode * node, exprToken * tokens, int start, int end, int index);
|
|
int exprInternalParsePosNeg(exprObj * obj, exprNode * node, exprToken * tokens, int start, int end, int index);
|
|
int exprInternalParseExp(exprObj * obj, exprNode * node, exprToken * tokens, int start, int end, int index);
|
|
int exprInternalParseFunction(exprObj * obj, exprNode * node, exprToken * tokens, int start, int end, int p1, int p2);
|
|
int exprInternalParseVarVal(exprObj * obj, exprNode * node, exprToken * tokens, int start, int end);
|
|
int exprStringToTokenList(exprObj * obj, char *expr, exprToken ** tokens, int *count);
|
|
void exprFreeTokenList(exprToken * tokens, int count);
|
|
|
|
/* This frees a token list */
|
|
void exprFreeTokenList(exprToken * tokens, int count)
|
|
{
|
|
int pos;
|
|
|
|
if (tokens == NULL)
|
|
return;
|
|
|
|
for (pos = 0; pos < count; pos++) {
|
|
if (tokens[pos].type == EXPR_TOKEN_IDENTIFIER)
|
|
exprFreeMem(tokens[pos].data.str);
|
|
}
|
|
|
|
exprFreeMem(tokens);
|
|
}
|
|
|
|
/* This converts an expression string to a token list */
|
|
int exprStringToTokenList(exprObj * obj, char *expr, exprToken ** tokens, int *count)
|
|
{
|
|
int found;
|
|
exprToken *list;
|
|
int pass;
|
|
int pos, len;
|
|
int tpos;
|
|
int comment; /* Is a comment active */
|
|
int start, ilen;
|
|
char buf[EXPR_MAXIDENTSIZE + 1];
|
|
|
|
/* Set initial variables */
|
|
found = 0;
|
|
tpos = 0;
|
|
list = NULL;
|
|
comment = 0;
|
|
*tokens = NULL;
|
|
*count = 0;
|
|
|
|
|
|
/* Check string length */
|
|
len = (int) strlen(expr);
|
|
if (len == 0)
|
|
return EXPR_ERROR_EMPTYEXPR;
|
|
|
|
/* Two passes, one to count, one to tokenize */
|
|
for (pass = 0; pass <= 1; pass++) {
|
|
for (pos = 0; pos < len; pos++) {
|
|
switch (expr[pos]) {
|
|
/* Comment */
|
|
case '#':
|
|
{
|
|
/* Only set it if a comment is not already active */
|
|
if (!comment)
|
|
comment = 1;
|
|
|
|
break;
|
|
}
|
|
|
|
/* Newline characters turn off comments */
|
|
case '\r':
|
|
case '\n':
|
|
{
|
|
/* If a comment is active, unset it */
|
|
if (comment)
|
|
comment = 0;
|
|
|
|
break;
|
|
}
|
|
|
|
/* Open parenthesis */
|
|
case '(':
|
|
{
|
|
if (!comment) {
|
|
if (pass == 0)
|
|
found++;
|
|
else {
|
|
list[tpos].type = EXPR_TOKEN_OPAREN;
|
|
list[tpos].start = pos;
|
|
list[tpos].end = pos;
|
|
tpos++;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/* Close parenthesis */
|
|
case ')':
|
|
{
|
|
if (!comment) {
|
|
if (pass == 0)
|
|
found++;
|
|
else {
|
|
list[tpos].type = EXPR_TOKEN_CPAREN;
|
|
list[tpos].start = pos;
|
|
list[tpos].end = pos;
|
|
tpos++;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/* Plus */
|
|
case '+':
|
|
{
|
|
if (!comment) {
|
|
if (pass == 0)
|
|
found++;
|
|
else {
|
|
list[tpos].type = EXPR_TOKEN_PLUS;
|
|
list[tpos].start = pos;
|
|
list[tpos].end = pos;
|
|
tpos++;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/* Hyphen */
|
|
case '-':
|
|
{
|
|
if (!comment) {
|
|
if (pass == 0)
|
|
found++;
|
|
else {
|
|
list[tpos].type = EXPR_TOKEN_HYPHEN;
|
|
list[tpos].start = pos;
|
|
list[tpos].end = pos;
|
|
tpos++;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/* Asterisk */
|
|
case '*':
|
|
{
|
|
if (!comment) {
|
|
if (pass == 0)
|
|
found++;
|
|
else {
|
|
list[tpos].type = EXPR_TOKEN_ASTERISK;
|
|
list[tpos].start = pos;
|
|
list[tpos].end = pos;
|
|
tpos++;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/* Forward slash */
|
|
case '/':
|
|
{
|
|
if (!comment) {
|
|
if (pass == 0)
|
|
found++;
|
|
else {
|
|
list[tpos].type = EXPR_TOKEN_FSLASH;
|
|
list[tpos].start = pos;
|
|
list[tpos].end = pos;
|
|
tpos++;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/* Hat */
|
|
case '^':
|
|
{
|
|
if (!comment) {
|
|
if (pass == 0)
|
|
found++;
|
|
else {
|
|
list[tpos].type = EXPR_TOKEN_HAT;
|
|
list[tpos].start = pos;
|
|
list[tpos].end = pos;
|
|
tpos++;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/* Ampersand */
|
|
case '&':
|
|
{
|
|
if (!comment) {
|
|
if (pass == 0)
|
|
found++;
|
|
else {
|
|
list[tpos].type = EXPR_TOKEN_AMPERSAND;
|
|
list[tpos].start = pos;
|
|
list[tpos].end = pos;
|
|
tpos++;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/* Semicolon */
|
|
case ';':
|
|
{
|
|
if (!comment) {
|
|
if (pass == 0)
|
|
found++;
|
|
else {
|
|
list[tpos].type = EXPR_TOKEN_SEMICOLON;
|
|
list[tpos].start = pos;
|
|
list[tpos].end = pos;
|
|
tpos++;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/* Comma */
|
|
case ',':
|
|
{
|
|
if (!comment) {
|
|
if (pass == 0)
|
|
found++;
|
|
else {
|
|
list[tpos].type = EXPR_TOKEN_COMMA;
|
|
list[tpos].start = pos;
|
|
list[tpos].end = pos;
|
|
tpos++;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/* Equal sign */
|
|
case '=':
|
|
{
|
|
if (!comment) {
|
|
if (pass == 0)
|
|
found++;
|
|
else {
|
|
list[tpos].type = EXPR_TOKEN_EQUAL;
|
|
list[tpos].start = pos;
|
|
list[tpos].end = pos;
|
|
tpos++;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/* Identifiers and values */
|
|
default:
|
|
{
|
|
if (!comment) {
|
|
if (expr[pos] == '.' || switch_isdigit(expr[pos])) {
|
|
/* Value */
|
|
start = pos;
|
|
|
|
/* Find digits before a period */
|
|
while (switch_isdigit(expr[pos]))
|
|
pos++;
|
|
|
|
/* Find a period */
|
|
if (expr[pos] == '.')
|
|
pos++;
|
|
|
|
/* Find digits after a period */
|
|
while (switch_isdigit(expr[pos]))
|
|
pos++;
|
|
|
|
/* pos is AFTER last item, back up */
|
|
pos--;
|
|
|
|
if (pass == 0)
|
|
found++;
|
|
else {
|
|
ilen = pos - start + 1;
|
|
|
|
/* Is the value to large */
|
|
if (ilen > EXPR_MAXIDENTSIZE) {
|
|
obj->starterr = start;
|
|
obj->enderr = pos;
|
|
exprFreeTokenList(list, found);
|
|
return EXPR_ERROR_BADIDENTIFIER;
|
|
}
|
|
|
|
/* Create value token */
|
|
strncpy(buf, expr + start, ilen);
|
|
buf[ilen] = '\0';
|
|
|
|
list[tpos].type = EXPR_TOKEN_VALUE;
|
|
list[tpos].start = start;
|
|
list[tpos].end = pos;
|
|
list[tpos].data.val = (EXPRTYPE) atof(buf);
|
|
tpos++;
|
|
}
|
|
} else if (expr[pos] == '_' || switch_isalpha(expr[pos])) {
|
|
/* Identifier */
|
|
start = pos;
|
|
|
|
/* Find rest of identifier */
|
|
while (expr[pos] == '_' || switch_isalnum(expr[pos]))
|
|
pos++;
|
|
|
|
/* pos is AFTER last item, back up */
|
|
pos--;
|
|
|
|
if (pass == 0)
|
|
found++;
|
|
else {
|
|
ilen = pos - start + 1;
|
|
|
|
/* Is the value to large */
|
|
if (ilen > EXPR_MAXIDENTSIZE) {
|
|
obj->starterr = start;
|
|
obj->enderr = pos;
|
|
exprFreeTokenList(list, found);
|
|
return EXPR_ERROR_BADIDENTIFIER;
|
|
}
|
|
|
|
/* Create value token */
|
|
strncpy(buf, expr + start, ilen);
|
|
buf[ilen] = '\0';
|
|
|
|
/* Allocate memory for identifier */
|
|
list[tpos].data.str = exprAllocMem(ilen + 1);
|
|
if (list[tpos].data.str == NULL) {
|
|
exprFreeTokenList(list, found);
|
|
return EXPR_ERROR_MEMORY;
|
|
}
|
|
|
|
list[tpos].type = EXPR_TOKEN_IDENTIFIER;
|
|
list[tpos].start = start;
|
|
list[tpos].end = pos;
|
|
strcpy(list[tpos].data.str, buf);
|
|
tpos++;
|
|
}
|
|
} else if (switch_isspace(expr[pos])) {
|
|
/* Spaces are ignored, do nothing */
|
|
} else {
|
|
/* Unknown */
|
|
obj->starterr = obj->enderr = pos;
|
|
exprFreeTokenList(list, found);
|
|
return EXPR_ERROR_INVALIDCHAR;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If pass is 0, allocate memory for next pass */
|
|
if (pass == 0) {
|
|
/* First, make sure all comments were ended */
|
|
if (comment)
|
|
comment = 0;
|
|
|
|
/* Make sure the expression is not empty */
|
|
if (found == 0)
|
|
return EXPR_ERROR_EMPTYEXPR;
|
|
|
|
/* Allocate memory for token list */
|
|
list = exprAllocMem(found * sizeof(exprToken));
|
|
if (list == NULL)
|
|
return EXPR_ERROR_MEMORY;
|
|
|
|
tpos = 0;
|
|
}
|
|
}
|
|
|
|
*count = found;
|
|
*tokens = list;
|
|
return EXPR_ERROR_NOERROR;
|
|
}
|
|
|
|
|
|
/* This is the main parsing routine */
|
|
int exprParse(exprObj * obj, char *expr)
|
|
{
|
|
exprToken *tokens;
|
|
int count;
|
|
int err;
|
|
exprNode *tmp;
|
|
|
|
/* Make sure an object was passed */
|
|
if (obj == NULL)
|
|
return EXPR_ERROR_NULLPOINTER;
|
|
|
|
/* Clear expression error position */
|
|
obj->starterr = obj->enderr = -1;
|
|
|
|
/* Have we already been parsed? */
|
|
if (obj->parsedbad != 0)
|
|
return EXPR_ERROR_ALREADYPARSEDBAD;
|
|
|
|
if (obj->parsedgood != 0)
|
|
return EXPR_ERROR_ALREADYPARSEDGOOD;
|
|
|
|
/* Make sure an expression was passed */
|
|
if (expr == NULL)
|
|
return EXPR_ERROR_NULLPOINTER;
|
|
|
|
/* Create token list */
|
|
err = exprStringToTokenList(obj, expr, &tokens, &count);
|
|
if (err != EXPR_ERROR_NOERROR)
|
|
return err;
|
|
|
|
/* Create head pointer */
|
|
tmp = exprAllocNodes(1);
|
|
if (tmp == NULL) {
|
|
exprFreeTokenList(tokens, count);
|
|
return EXPR_ERROR_MEMORY;
|
|
}
|
|
|
|
obj->headnode = tmp;
|
|
|
|
/* Call the multiparse routine to parse subexpressions */
|
|
err = exprMultiParse(obj, tmp, tokens, count);
|
|
|
|
/* Free the token list */
|
|
exprFreeTokenList(tokens, count);
|
|
|
|
/* successful parse? */
|
|
if (err == EXPR_ERROR_NOERROR) {
|
|
obj->parsedgood = 1;
|
|
obj->parsedbad = 0;
|
|
} else {
|
|
obj->parsedbad = 1;
|
|
obj->parsedgood = 0;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
/* Parse the subexpressions, each ending with semicolons */
|
|
int exprMultiParse(exprObj * obj, exprNode * node, exprToken * tokens, int count)
|
|
{
|
|
int pos, plevel, last;
|
|
int num, cur, err;
|
|
exprNode *tmp;
|
|
|
|
plevel = 0;
|
|
num = 0;
|
|
last = -1;
|
|
|
|
/* First count the number of arguments */
|
|
for (pos = 0; pos < count; pos++) {
|
|
switch (tokens[pos].type) {
|
|
case EXPR_TOKEN_OPAREN:
|
|
/* increase plevel */
|
|
plevel++;
|
|
break;
|
|
|
|
case EXPR_TOKEN_CPAREN:
|
|
/* decrease plevel */
|
|
plevel--;
|
|
|
|
if (plevel < 0) {
|
|
obj->starterr = tokens[pos].start;
|
|
obj->enderr = tokens[pos].end;
|
|
return EXPR_ERROR_UNMATCHEDPAREN;
|
|
}
|
|
|
|
break;
|
|
|
|
case EXPR_TOKEN_SEMICOLON:
|
|
if (plevel == 0) {
|
|
if (last == pos - 1 || pos == 0) {
|
|
/* last semicolon is before us or we are at the start */
|
|
obj->starterr = tokens[pos].start;
|
|
obj->enderr = tokens[pos].end;
|
|
return EXPR_ERROR_SYNTAX;
|
|
} else {
|
|
/* last semicolon is not right before us */
|
|
num++;
|
|
}
|
|
} else {
|
|
/* Semicolon should not be in a parenthesis */
|
|
obj->starterr = tokens[pos].start;
|
|
obj->enderr = tokens[pos].end;
|
|
return EXPR_ERROR_SYNTAX;
|
|
}
|
|
|
|
last = pos; /* update position of last semicolon */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* plevel should be zero now */
|
|
if (plevel != 0)
|
|
return EXPR_ERROR_UNMATCHEDPAREN;
|
|
|
|
/* the last character should be a semicolon */
|
|
if (last != pos - 1)
|
|
return EXPR_ERROR_MISSINGSEMICOLON;
|
|
|
|
/* Now we know how many arguments there are */
|
|
|
|
/* Allocate array of subnodes */
|
|
tmp = exprAllocNodes(num);
|
|
if (tmp == NULL)
|
|
return EXPR_ERROR_MEMORY;
|
|
|
|
/* Set the current node's data */
|
|
node->type = EXPR_NODETYPE_MULTI;
|
|
node->data.oper.nodes = tmp;
|
|
node->data.oper.nodecount = num;
|
|
|
|
/* now we parse each subexpression */
|
|
last = 0; /* Not for last semicolon, but for first char of subexpr */
|
|
cur = 0;
|
|
|
|
for (pos = 0; pos < count; pos++) {
|
|
if (tokens[pos].type == EXPR_TOKEN_SEMICOLON) {
|
|
/* Everything from last up to pos - 1 is a parameter */
|
|
err = exprInternalParse(obj, &(tmp[cur]), tokens, last, pos - 1);
|
|
if (err != EXPR_ERROR_NOERROR)
|
|
return err;
|
|
|
|
/* Update last position and current argument */
|
|
last = pos + 1;
|
|
cur++;
|
|
}
|
|
}
|
|
|
|
return EXPR_ERROR_NOERROR;
|
|
}
|
|
|
|
/* This function parses each subnode and recurses if needed */
|
|
int exprInternalParse(exprObj * obj, exprNode * node, exprToken * tokens, int start, int end)
|
|
{
|
|
int pos;
|
|
int plevel = 0; /* Paren level */
|
|
int fgopen = -1; /* First paren group open index */
|
|
int fgclose = -1; /* First paren group close index */
|
|
int assignindex = -1; /* First = at plevel 0 for assignment */
|
|
int addsubindex = -1; /* Last + or - at plevel 0 for adding or subtracting */
|
|
int muldivindex = -1; /* Last * or / at plevel 0 for multiplying or dividing */
|
|
int expindex = -1; /* Last ^ fount at plevel 0 for exponents */
|
|
int posnegindex = -1; /* First +,- at plevel 0 for positive,negative */
|
|
|
|
/* Make sure some conditions are right */
|
|
if (start > end)
|
|
return EXPR_ERROR_UNKNOWN;
|
|
|
|
/* Scan the string for certain characters */
|
|
for (pos = start; pos <= end; pos++) {
|
|
switch (tokens[pos].type) {
|
|
case EXPR_TOKEN_OPAREN:
|
|
plevel++;
|
|
|
|
/* First group open? */
|
|
if (plevel == 1 && fgopen == -1)
|
|
fgopen = pos;
|
|
break;
|
|
|
|
case EXPR_TOKEN_CPAREN:
|
|
plevel--;
|
|
|
|
/* First group close? */
|
|
if (plevel == 0 && fgclose == -1)
|
|
fgclose = pos;
|
|
|
|
if (plevel < 0) {
|
|
obj->starterr = tokens[pos].start;
|
|
obj->enderr = tokens[pos].end;
|
|
return EXPR_ERROR_UNMATCHEDPAREN;
|
|
}
|
|
break;
|
|
|
|
case EXPR_TOKEN_EQUAL:
|
|
/* Assignment found */
|
|
if (plevel == 0) {
|
|
if (assignindex == -1)
|
|
assignindex = pos;
|
|
}
|
|
break;
|
|
|
|
case EXPR_TOKEN_ASTERISK:
|
|
case EXPR_TOKEN_FSLASH:
|
|
/* Multiplication or division */
|
|
if (plevel == 0)
|
|
muldivindex = pos;
|
|
break;
|
|
|
|
case EXPR_TOKEN_HAT:
|
|
/* Exponent */
|
|
if (plevel == 0)
|
|
expindex = pos;
|
|
break;
|
|
|
|
|
|
case EXPR_TOKEN_PLUS:
|
|
case EXPR_TOKEN_HYPHEN:
|
|
/* Addition or positive or subtraction or negative */
|
|
if (plevel == 0) {
|
|
if (pos == start) {
|
|
/* At the start area, positive/negative */
|
|
if (posnegindex == -1)
|
|
posnegindex = pos;
|
|
} else {
|
|
/* Not at start, check item in front */
|
|
switch (tokens[pos - 1].type) {
|
|
case EXPR_TOKEN_EQUAL: /* Equal sign */
|
|
case EXPR_TOKEN_PLUS: /* Add/positive sign */
|
|
case EXPR_TOKEN_HYPHEN: /* Subtract/negative sign */
|
|
case EXPR_TOKEN_ASTERISK: /* Multiply sign */
|
|
case EXPR_TOKEN_FSLASH: /* Divide sign */
|
|
case EXPR_TOKEN_HAT: /* Exponent sign */
|
|
|
|
/* After theses, it is positive/negative */
|
|
if (posnegindex == -1)
|
|
posnegindex = pos;
|
|
|
|
break;
|
|
|
|
default:
|
|
/* Otherwise it is addition/subtraction */
|
|
addsubindex = pos;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
/* plevel should now be zero */
|
|
if (plevel != 0)
|
|
return EXPR_ERROR_UNMATCHEDPAREN;
|
|
|
|
/* We must parse the data in a certain order to maintain the
|
|
correct order of operators at evaluation time */
|
|
|
|
/* First, take care of assignment */
|
|
if (assignindex != -1)
|
|
return exprInternalParseAssign(obj, node, tokens, start, end, assignindex);
|
|
|
|
/* Addition or subtraction is next */
|
|
if (addsubindex != -1) {
|
|
if (tokens[addsubindex].type == EXPR_TOKEN_PLUS)
|
|
return exprInternalParseAdd(obj, node, tokens, start, end, addsubindex);
|
|
else
|
|
return exprInternalParseSub(obj, node, tokens, start, end, addsubindex);
|
|
}
|
|
|
|
|
|
/* Multiplycation or division */
|
|
if (muldivindex != -1) {
|
|
if (tokens[muldivindex].type == EXPR_TOKEN_ASTERISK)
|
|
return exprInternalParseMul(obj, node, tokens, start, end, muldivindex);
|
|
else
|
|
return exprInternalParseDiv(obj, node, tokens, start, end, muldivindex);
|
|
}
|
|
|
|
/* Exponent */
|
|
if (expindex != -1)
|
|
return exprInternalParseExp(obj, node, tokens, start, end, expindex);
|
|
|
|
/* Negation */
|
|
if (posnegindex != -1)
|
|
return exprInternalParsePosNeg(obj, node, tokens, start, end, posnegindex);
|
|
|
|
|
|
/* Grouped parenthesis */
|
|
if (fgopen == start) {
|
|
/* Closing paren. should be at the end */
|
|
if (fgclose == end) {
|
|
/* Anything between them */
|
|
if (fgclose > fgopen + 1) {
|
|
return exprInternalParse(obj, node, tokens, fgopen + 1, fgclose - 1);
|
|
} else {
|
|
/* Nothing between them */
|
|
obj->starterr = tokens[fgopen].start;
|
|
obj->enderr = tokens[fgclose].end;
|
|
return EXPR_ERROR_SYNTAX;
|
|
}
|
|
} else /* Closing paren not at the end */
|
|
return EXPR_ERROR_SYNTAX;
|
|
}
|
|
|
|
/* Functions */
|
|
if (fgopen > start) {
|
|
/* Closing paren should be at end */
|
|
if (fgclose == end) {
|
|
return exprInternalParseFunction(obj, node, tokens, start, end, fgopen, fgclose);
|
|
} else /* Closing paren not at end */
|
|
return EXPR_ERROR_SYNTAX;
|
|
}
|
|
|
|
/* If it was none of the above, it must be a variable or value */
|
|
return exprInternalParseVarVal(obj, node, tokens, start, end);
|
|
}
|
|
|
|
/* Function to parse an assignment node */
|
|
int exprInternalParseAssign(exprObj * obj, exprNode * node, exprToken * tokens, int start, int end, int index)
|
|
{
|
|
exprNode *tmp;
|
|
exprValList *l;
|
|
EXPRTYPE *addr;
|
|
|
|
/* Make sure the equal sign is not at the start or end */
|
|
if (index != start + 1 || index >= end) {
|
|
obj->starterr = tokens[index].start;
|
|
obj->enderr = tokens[index].end;
|
|
return EXPR_ERROR_SYNTAX;
|
|
}
|
|
|
|
/* Make sure item before equal sign is an identifier */
|
|
if (tokens[index - 1].type != EXPR_TOKEN_IDENTIFIER) {
|
|
obj->starterr = tokens[index - 1].start;
|
|
obj->enderr = tokens[index].end;
|
|
return EXPR_ERROR_SYNTAX;
|
|
}
|
|
|
|
/* Create expression subnode */
|
|
tmp = exprAllocNodes(1);
|
|
if (tmp == NULL) {
|
|
return EXPR_ERROR_MEMORY;
|
|
}
|
|
|
|
|
|
/* Set the data */
|
|
node->type = EXPR_NODETYPE_ASSIGN;
|
|
node->data.assign.node = tmp;
|
|
|
|
|
|
/*
|
|
The fast access method directly accesses the memory address
|
|
of the variable's value at evaluation time. Because of this,
|
|
we must make sure the variable does exists in the variable list.
|
|
*/
|
|
|
|
/* Make sure name is not a constant name */
|
|
l = exprGetConstList(obj);
|
|
if (l) {
|
|
exprValListGetAddress(l, tokens[index - 1].data.str, &addr);
|
|
if (addr) {
|
|
obj->starterr = tokens[index - 1].start;
|
|
obj->enderr = tokens[index].end;
|
|
return EXPR_ERROR_CONSTANTASSIGN;
|
|
}
|
|
}
|
|
|
|
/* Get the variable list */
|
|
l = exprGetVarList(obj);
|
|
if (l == NULL)
|
|
return EXPR_ERROR_NOVARLIST;
|
|
|
|
/* Get variable address if already in the list */
|
|
exprValListGetAddress(l, tokens[index - 1].data.str, &addr);
|
|
if (addr == NULL) { /* Variable not in the list, add it */
|
|
exprValListAdd(l, tokens[index - 1].data.str, 0.0);
|
|
|
|
/* Try to get address again */
|
|
exprValListGetAddress(l, tokens[index - 1].data.str, &addr);
|
|
if (addr == NULL) /* Could not add variable */
|
|
return EXPR_ERROR_MEMORY; /* Could not add variable to list */
|
|
}
|
|
|
|
node->data.assign.vaddr = addr;
|
|
|
|
/* Parse the subnode */
|
|
return exprInternalParse(obj, tmp, tokens, index + 1, end);
|
|
}
|
|
|
|
/* Function to parse an addition operator */
|
|
int exprInternalParseAdd(exprObj * obj, exprNode * node, exprToken * tokens, int start, int end, int index)
|
|
{
|
|
exprNode *tmp;
|
|
int err;
|
|
|
|
/* Make sure plus sign is at a good place */
|
|
if (index <= start || index >= end) {
|
|
obj->starterr = tokens[index].start;
|
|
obj->enderr = tokens[index].end;
|
|
return EXPR_ERROR_SYNTAX;
|
|
}
|
|
|
|
/* Allocate space for 2 subnodes */
|
|
tmp = exprAllocNodes(2);
|
|
if (tmp == NULL)
|
|
return EXPR_ERROR_MEMORY;
|
|
|
|
|
|
/* Set the data */
|
|
node->type = EXPR_NODETYPE_ADD;
|
|
node->data.oper.nodes = tmp;
|
|
node->data.oper.nodecount = 2;
|
|
|
|
/* parse the left side */
|
|
err = exprInternalParse(obj, &(tmp[0]), tokens, start, index - 1);
|
|
if (err != EXPR_ERROR_NOERROR)
|
|
return err;
|
|
|
|
/* parse the right side */
|
|
return exprInternalParse(obj, &(tmp[1]), tokens, index + 1, end);
|
|
}
|
|
|
|
/* Function to parse a subtraction operator */
|
|
int exprInternalParseSub(exprObj * obj, exprNode * node, exprToken * tokens, int start, int end, int index)
|
|
{
|
|
exprNode *tmp;
|
|
int err;
|
|
|
|
/* Make sure minus sign is at a good place */
|
|
if (index <= start || index >= end) {
|
|
obj->starterr = tokens[index].start;
|
|
obj->enderr = tokens[index].end;
|
|
return EXPR_ERROR_SYNTAX;
|
|
}
|
|
|
|
/* Allocate space for 2 subnodes */
|
|
tmp = exprAllocNodes(2);
|
|
if (tmp == NULL)
|
|
return EXPR_ERROR_MEMORY;
|
|
|
|
|
|
/* Set the data */
|
|
node->type = EXPR_NODETYPE_SUBTRACT;
|
|
node->data.oper.nodes = tmp;
|
|
node->data.oper.nodecount = 2;
|
|
|
|
/* parse the left side */
|
|
err = exprInternalParse(obj, &(tmp[0]), tokens, start, index - 1);
|
|
if (err != EXPR_ERROR_NOERROR)
|
|
return err;
|
|
|
|
/* parse the right side */
|
|
return exprInternalParse(obj, &(tmp[1]), tokens, index + 1, end);
|
|
}
|
|
|
|
/* Function to parse a multiplication operator */
|
|
int exprInternalParseMul(exprObj * obj, exprNode * node, exprToken * tokens, int start, int end, int index)
|
|
{
|
|
exprNode *tmp;
|
|
int err;
|
|
|
|
/* Make sure times sign is at a good place */
|
|
if (index <= start || index >= end) {
|
|
obj->starterr = tokens[index].start;
|
|
obj->enderr = tokens[index].end;
|
|
return EXPR_ERROR_SYNTAX;
|
|
}
|
|
|
|
|
|
/* Allocate space for 2 subnodes */
|
|
tmp = exprAllocNodes(2);
|
|
if (tmp == NULL)
|
|
return EXPR_ERROR_MEMORY;
|
|
|
|
|
|
/* Set the data */
|
|
node->type = EXPR_NODETYPE_MULTIPLY;
|
|
node->data.oper.nodes = tmp;
|
|
node->data.oper.nodecount = 2;
|
|
|
|
/* parse the left side */
|
|
err = exprInternalParse(obj, &(tmp[0]), tokens, start, index - 1);
|
|
if (err != EXPR_ERROR_NOERROR)
|
|
return err;
|
|
|
|
/* parse the right side */
|
|
return exprInternalParse(obj, &(tmp[1]), tokens, index + 1, end);
|
|
}
|
|
|
|
/* Function to parse a division operator */
|
|
int exprInternalParseDiv(exprObj * obj, exprNode * node, exprToken * tokens, int start, int end, int index)
|
|
{
|
|
exprNode *tmp;
|
|
int err;
|
|
|
|
/* Make sure slash sign is at a good place */
|
|
if (index <= start || index >= end) {
|
|
obj->starterr = tokens[index].start;
|
|
obj->enderr = tokens[index].end;
|
|
return EXPR_ERROR_SYNTAX;
|
|
}
|
|
|
|
|
|
/* Allocate space for 2 subnodes */
|
|
tmp = exprAllocNodes(2);
|
|
if (tmp == NULL)
|
|
return EXPR_ERROR_MEMORY;
|
|
|
|
|
|
/* Set the data */
|
|
node->type = EXPR_NODETYPE_DIVIDE;
|
|
node->data.oper.nodes = tmp;
|
|
node->data.oper.nodecount = 2;
|
|
|
|
/* parse the left side */
|
|
err = exprInternalParse(obj, &(tmp[0]), tokens, start, index - 1);
|
|
if (err != EXPR_ERROR_NOERROR)
|
|
return err;
|
|
|
|
/* parse the right side */
|
|
return exprInternalParse(obj, &(tmp[1]), tokens, index + 1, end);
|
|
}
|
|
|
|
/* Function to parse an exponent operator */
|
|
int exprInternalParseExp(exprObj * obj, exprNode * node, exprToken * tokens, int start, int end, int index)
|
|
{
|
|
exprNode *tmp;
|
|
int err;
|
|
|
|
/* Make sure exponent sign is at a good place */
|
|
if (index <= start || index >= end) {
|
|
obj->starterr = tokens[index].start;
|
|
obj->enderr = tokens[index].end;
|
|
return EXPR_ERROR_SYNTAX;
|
|
}
|
|
|
|
|
|
/* Allocate space for 2 subnodes */
|
|
tmp = exprAllocNodes(2);
|
|
if (tmp == NULL)
|
|
return EXPR_ERROR_MEMORY;
|
|
|
|
|
|
/* Set the data */
|
|
node->type = EXPR_NODETYPE_EXPONENT;
|
|
node->data.oper.nodes = tmp;
|
|
node->data.oper.nodecount = 2;
|
|
|
|
/* parse the left side */
|
|
err = exprInternalParse(obj, &(tmp[0]), tokens, start, index - 1);
|
|
if (err != EXPR_ERROR_NOERROR)
|
|
return err;
|
|
|
|
/* parse the right side */
|
|
return exprInternalParse(obj, &(tmp[1]), tokens, index + 1, end);
|
|
}
|
|
|
|
/* Function to parse for positive and negative */
|
|
int exprInternalParsePosNeg(exprObj * obj, exprNode * node, exprToken * tokens, int start, int end, int index)
|
|
{
|
|
exprNode *tmp;
|
|
|
|
/* Position should be the same as start */
|
|
if (index != start) {
|
|
obj->starterr = tokens[index].start;
|
|
obj->enderr = tokens[index].end;
|
|
return EXPR_ERROR_UNKNOWN;
|
|
}
|
|
|
|
/* If it is a positive, just parse the internal of it */
|
|
if (tokens[index].type == EXPR_TOKEN_PLUS)
|
|
return exprInternalParse(obj, node, tokens, index + 1, end);
|
|
else {
|
|
/* Allocate subnode */
|
|
tmp = exprAllocNodes(1);
|
|
if (tmp == NULL)
|
|
return EXPR_ERROR_NOERROR;
|
|
|
|
|
|
/* Set data */
|
|
node->type = EXPR_NODETYPE_NEGATE;
|
|
node->data.oper.nodes = tmp;
|
|
node->data.oper.nodecount = 1;
|
|
|
|
/* Parse the subnode */
|
|
return exprInternalParse(obj, tmp, tokens, index + 1, end);
|
|
}
|
|
}
|
|
|
|
/* Function will parse a call to a function */
|
|
int exprInternalParseFunction(exprObj * obj, exprNode * node, exprToken * tokens, int start, int end, int p1, int p2)
|
|
{
|
|
int pos;
|
|
int num, cur;
|
|
int refnum, refcur;
|
|
int plevel = 0;
|
|
int lv, err;
|
|
exprNode *tmp;
|
|
exprFuncType fptr;
|
|
int argmin, argmax;
|
|
int refargmin, refargmax;
|
|
int type;
|
|
exprFuncList *l;
|
|
exprValList *vars;
|
|
EXPRTYPE *addr;
|
|
EXPRTYPE **reftmp;
|
|
|
|
/* We should have a function list */
|
|
l = exprGetFuncList(obj);
|
|
if (l == NULL)
|
|
return EXPR_ERROR_NOSUCHFUNCTION;
|
|
|
|
/* check paren. location */
|
|
if (p2 <= p1)
|
|
return EXPR_ERROR_SYNTAX;
|
|
|
|
/* second paren. should not be after the end */
|
|
if (p2 > end)
|
|
return EXPR_ERROR_SYNTAX;
|
|
|
|
/* Item before parenthesis should be an identifier */
|
|
if (tokens[p1 - 1].type != EXPR_TOKEN_IDENTIFIER) {
|
|
obj->starterr = tokens[p1 - 1].start;
|
|
obj->enderr = tokens[p1].end;
|
|
return EXPR_ERROR_SYNTAX;
|
|
}
|
|
|
|
|
|
/* Look up the function */
|
|
err = exprFuncListGet(l, tokens[p1 - 1].data.str, &fptr, &type, &argmin, &argmax, &refargmin, &refargmax);
|
|
if (err != EXPR_ERROR_NOERROR) {
|
|
if (err == EXPR_ERROR_NOTFOUND) {
|
|
obj->starterr = tokens[p1 - 1].start;
|
|
obj->enderr = tokens[p1 - 1].end;
|
|
return EXPR_ERROR_NOSUCHFUNCTION;
|
|
} else
|
|
return err;
|
|
}
|
|
|
|
/* Make sure the function exists */
|
|
if (fptr == NULL && type == 0) {
|
|
obj->starterr = tokens[p1 - 1].start;
|
|
obj->enderr = tokens[p1 - 1].end;
|
|
return EXPR_ERROR_NOSUCHFUNCTION;
|
|
}
|
|
|
|
/* Count arguments */
|
|
if (p2 == p1 + 1) {
|
|
num = 0;
|
|
refnum = 0;
|
|
} else {
|
|
num = 1;
|
|
refnum = 0;
|
|
|
|
|
|
/* count commas */
|
|
for (pos = p1 + 1; pos < p2; pos++) {
|
|
switch (tokens[pos].type) {
|
|
case EXPR_TOKEN_OPAREN:
|
|
plevel++;
|
|
break;
|
|
|
|
case EXPR_TOKEN_CPAREN:
|
|
plevel--;
|
|
if (plevel < 0) {
|
|
obj->starterr = tokens[pos].start;
|
|
obj->enderr = tokens[pos].end;
|
|
return EXPR_ERROR_UNMATCHEDPAREN;
|
|
}
|
|
break;
|
|
|
|
case EXPR_TOKEN_COMMA:
|
|
/* Found comma */
|
|
if (plevel == 0)
|
|
num++;
|
|
break;
|
|
|
|
case EXPR_TOKEN_AMPERSAND:
|
|
/* Found reference mark */
|
|
if (plevel == 0) {
|
|
/* This may only occur after the open parenthesis or comma */
|
|
if (tokens[pos - 1].type == EXPR_TOKEN_OPAREN || tokens[pos - 1].type == EXPR_TOKEN_COMMA)
|
|
refnum++;
|
|
else
|
|
return EXPR_ERROR_SYNTAX;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* plevel should be zero */
|
|
if (plevel != 0)
|
|
return EXPR_ERROR_UNMATCHEDPAREN;
|
|
}
|
|
|
|
/* We now have the number of total arguments and
|
|
number of ref arguments. Get number of normal
|
|
arguments */
|
|
num = num - refnum;
|
|
|
|
/* Make sure number of arguments is correct */
|
|
/* Here we make sure the limits are greater
|
|
or equal to zero because any negative number
|
|
could be used to specify no limit */
|
|
if (argmin >= 0 && num < argmin) {
|
|
obj->starterr = tokens[p1 - 1].start;
|
|
obj->enderr = tokens[p2].end;
|
|
return EXPR_ERROR_BADNUMBERARGUMENTS;
|
|
}
|
|
|
|
if (argmax >= 0 && num > argmax) {
|
|
obj->starterr = tokens[p1 - 1].start;
|
|
obj->enderr = tokens[p2].end;
|
|
return EXPR_ERROR_BADNUMBERARGUMENTS;
|
|
}
|
|
|
|
if (refargmin >= 0 && refnum < refargmin) {
|
|
obj->starterr = tokens[p1 - 1].start;
|
|
obj->enderr = tokens[p2].end;
|
|
return EXPR_ERROR_BADNUMBERARGUMENTS;
|
|
}
|
|
|
|
if (refargmax >= 0 && refnum > refargmax) {
|
|
obj->starterr = tokens[p1 - 1].start;
|
|
obj->enderr = tokens[p2].end;
|
|
return EXPR_ERROR_BADNUMBERARGUMENTS;
|
|
}
|
|
|
|
/* Set tmp to null in case of no arguments */
|
|
tmp = NULL;
|
|
reftmp = NULL;
|
|
|
|
if (num > 0) {
|
|
/* Allocate subnodes */
|
|
tmp = exprAllocNodes(num);
|
|
if (tmp == NULL)
|
|
return EXPR_ERROR_MEMORY;
|
|
}
|
|
|
|
if (refnum > 0) {
|
|
/* Allocate ref pointers */
|
|
reftmp = exprAllocMem(sizeof(EXPRTYPE *) * refnum);
|
|
if (reftmp == NULL) {
|
|
exprFreeMem(tmp);
|
|
return EXPR_ERROR_MEMORY;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* Set this node's data */
|
|
node->type = EXPR_NODETYPE_FUNCTION;
|
|
node->data.function.fptr = fptr;
|
|
node->data.function.nodecount = num;
|
|
node->data.function.nodes = tmp;
|
|
node->data.function.refcount = refnum;
|
|
node->data.function.refs = reftmp;
|
|
node->data.function.type = type;
|
|
|
|
/* parse each subnode */
|
|
if (num + refnum > 0) {
|
|
plevel = 0;
|
|
cur = 0;
|
|
refcur = 0;
|
|
lv = p1 + 1;
|
|
|
|
/* look for commas if more than 1 arg */
|
|
if (num + refnum > 1) {
|
|
for (pos = p1 + 1; pos < p2; pos++) {
|
|
switch (tokens[pos].type) {
|
|
case EXPR_TOKEN_OPAREN:
|
|
plevel++;
|
|
break;
|
|
|
|
case EXPR_TOKEN_CPAREN:
|
|
plevel--;
|
|
break; /* Already checked paren nesting above */
|
|
|
|
case EXPR_TOKEN_COMMA:
|
|
/* Found comma */
|
|
if (plevel == 0) {
|
|
/* parse inside */
|
|
if (tokens[lv].type == EXPR_TOKEN_AMPERSAND) {
|
|
if (lv != pos - 2) {
|
|
obj->starterr = tokens[lv].start;
|
|
obj->enderr = tokens[pos].end;
|
|
return EXPR_ERROR_SYNTAX;
|
|
}
|
|
|
|
/* It is a reference */
|
|
if (tokens[lv + 1].type != EXPR_TOKEN_IDENTIFIER) {
|
|
obj->starterr = tokens[lv].start;
|
|
obj->enderr = tokens[lv + 1].end;
|
|
return EXPR_ERROR_SYNTAX;
|
|
}
|
|
|
|
|
|
/* Make sure it is not a constant */
|
|
vars = exprGetConstList(obj);
|
|
if (vars) {
|
|
exprValListGetAddress(vars, tokens[lv + 1].data.str, &addr);
|
|
if (addr) {
|
|
obj->starterr = tokens[lv].start;
|
|
obj->enderr = tokens[lv + 1].start;
|
|
return EXPR_ERROR_REFCONSTANT;
|
|
}
|
|
}
|
|
|
|
/* Get variable list */
|
|
vars = exprGetVarList(obj);
|
|
if (vars == NULL)
|
|
return EXPR_ERROR_NOVARLIST;
|
|
|
|
/* Get variable address */
|
|
exprValListGetAddress(vars, tokens[lv + 1].data.str, &addr);
|
|
if (addr == NULL) {
|
|
/* Add variable to list */
|
|
exprValListAdd(vars, tokens[lv + 1].data.str, 0.0);
|
|
|
|
/* Try to get address again */
|
|
exprValListGetAddress(vars, tokens[lv + 1].data.str, &addr);
|
|
if (addr == NULL)
|
|
return EXPR_ERROR_MEMORY; /* Could not add variable */
|
|
}
|
|
|
|
/* Set reference item */
|
|
reftmp[refcur] = addr;
|
|
|
|
/* increase ref arg number and lv position */
|
|
refcur++;
|
|
lv = pos + 1;
|
|
} else {
|
|
err = exprInternalParse(obj, &(tmp[cur]), tokens, lv, pos - 1);
|
|
if (err != EXPR_ERROR_NOERROR)
|
|
return err;
|
|
|
|
/* increase arg number and lv position */
|
|
lv = pos + 1;
|
|
cur++;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* lv should point after the last comma, or open paren. if only 1 arg */
|
|
if (tokens[lv].type == EXPR_TOKEN_AMPERSAND) {
|
|
if (lv != p2 - 2) {
|
|
obj->starterr = tokens[lv].start;
|
|
obj->enderr = tokens[p2].end;
|
|
return EXPR_ERROR_SYNTAX;
|
|
}
|
|
|
|
/* It is a reference */
|
|
if (tokens[lv + 1].type != EXPR_TOKEN_IDENTIFIER) {
|
|
obj->starterr = tokens[lv].start;
|
|
obj->enderr = tokens[lv + 1].end;
|
|
return EXPR_ERROR_SYNTAX;
|
|
}
|
|
|
|
/* Make sure it is not a constant */
|
|
vars = exprGetConstList(obj);
|
|
if (vars) {
|
|
exprValListGetAddress(vars, tokens[lv + 1].data.str, &addr);
|
|
if (addr) {
|
|
obj->starterr = tokens[lv].start;
|
|
obj->enderr = tokens[lv + 1].start;
|
|
return EXPR_ERROR_REFCONSTANT;
|
|
}
|
|
}
|
|
|
|
/* Get variable list */
|
|
vars = exprGetVarList(obj);
|
|
if (vars == NULL)
|
|
return EXPR_ERROR_NOVARLIST;
|
|
|
|
/* Get variable address */
|
|
exprValListGetAddress(vars, tokens[lv + 1].data.str, &addr);
|
|
if (addr == NULL) {
|
|
/* Add variable to list */
|
|
exprValListAdd(vars, tokens[lv + 1].data.str, 0.0);
|
|
|
|
/* Try to get address again */
|
|
exprValListGetAddress(vars, tokens[lv + 1].data.str, &addr);
|
|
if (addr == NULL)
|
|
return EXPR_ERROR_MEMORY; /* Could not add variable */
|
|
}
|
|
|
|
/* Set reference item */
|
|
reftmp[refcur] = addr;
|
|
} else {
|
|
err = exprInternalParse(obj, &(tmp[cur]), tokens, lv, p2 - 1);
|
|
if (err != EXPR_ERROR_NOERROR)
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
return EXPR_ERROR_NOERROR;
|
|
}
|
|
|
|
/* Parse a variable or value */
|
|
int exprInternalParseVarVal(exprObj * obj, exprNode * node, exprToken * tokens, int start, int end)
|
|
{
|
|
exprValList *l;
|
|
EXPRTYPE *addr;
|
|
|
|
|
|
/* Make sure positions are correct */
|
|
if (start != end) {
|
|
return EXPR_ERROR_UNKNOWN;
|
|
}
|
|
|
|
|
|
/* Are we an identifier */
|
|
if (tokens[start].type == EXPR_TOKEN_IDENTIFIER) {
|
|
/* we are an identifier */
|
|
|
|
/* check to see if it is a constant */
|
|
l = exprGetConstList(obj);
|
|
if (l != NULL) {
|
|
if (exprValListGetAddress(l, tokens[start].data.str, &addr) == EXPR_ERROR_NOERROR) {
|
|
/* We found it in the constant list */
|
|
|
|
/*
|
|
Treat is like a variable node so application can change
|
|
constant value and it will reflect in expression
|
|
*/
|
|
|
|
node->type = EXPR_NODETYPE_VARIABLE;
|
|
node->data.variable.vaddr = addr;
|
|
return EXPR_ERROR_NOERROR;
|
|
}
|
|
}
|
|
|
|
/* Not found in the constant list, so it must be a variable */
|
|
|
|
/* Set node type */
|
|
node->type = EXPR_NODETYPE_VARIABLE;
|
|
|
|
/*
|
|
The fast access method directly accesses the memory address
|
|
of the variable's value at evaluation time. Because of this,
|
|
we must make sure the variable does exists in the variable list.
|
|
*/
|
|
|
|
/* Get the variable list */
|
|
l = exprGetVarList(obj);
|
|
if (l == NULL)
|
|
return EXPR_ERROR_NOVARLIST;
|
|
|
|
/* Get variable address if already in the list */
|
|
exprValListGetAddress(l, tokens[start].data.str, &addr);
|
|
if (addr == NULL) { /* Variable not in the list, add it */
|
|
exprValListAdd(l, tokens[start].data.str, 0.0);
|
|
|
|
/* Try to get address again */
|
|
exprValListGetAddress(l, tokens[start].data.str, &addr);
|
|
if (addr == NULL) /* Could not add variable */
|
|
return EXPR_ERROR_MEMORY; /* Could not add variable to list */
|
|
}
|
|
|
|
node->data.variable.vaddr = addr;
|
|
|
|
return EXPR_ERROR_NOERROR;
|
|
} else if (tokens[start].type == EXPR_TOKEN_VALUE) {
|
|
/* we are a value */
|
|
node->type = EXPR_NODETYPE_VALUE;
|
|
node->data.value.value = tokens[start].data.val;
|
|
return EXPR_ERROR_NOERROR;
|
|
} else {
|
|
obj->starterr = tokens[start].start;
|
|
obj->enderr = tokens[end].end;
|
|
return EXPR_ERROR_UNKNOWN;
|
|
}
|
|
}
|