In regards to changes for 9508, expr2 system choking on floating point numbers, I'm adding this update to round out (no pun intended) and make this FP-capable version of the Expr2 stuff interoperate better with previous integer-only usage, by providing Functions syntax, with 20 builtin functions for floating pt to integer conversions, and some general floating point math routines that might commonly be used also. Along with this, I made it so if a function was not a builtin, it will try and find it in the ast_custom_function list, and if found, execute it and collect the results. Thus, you can call system functions like CDR(), CHANNEL(), etc, from within $\[..\] exprs, without having to wrap them in $\{...\} (curly brace) notation. Did a valgrind on the standalone and made sure there's no mem leaks. Looks good. Updated the docs, too.

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@73449 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Steve Murphy
2007-07-05 18:15:22 +00:00
parent 046aa5332d
commit 6a4efe5d5a
13 changed files with 1744 additions and 741 deletions

View File

@@ -29,6 +29,11 @@ Core:
Where D is a string of base-10 digits. All math is now done in "long double",
if it is available on your compiler/architecture. This was half-way between
a bug-fix (because the MATH func returns fp by default), and an enhancement.
Also, for those counting on, or needing, integer operations, a series of
'functions' were also added to the expr language, to allow several styles
of rounding/truncation, along with a set of common floating point operations,
like sin, cos, tan, log, pow, etc. The ability to call external functions
like CDR(), etc. was also added, without having to use the ${...} notation.
Voicemail:

View File

@@ -207,19 +207,19 @@ with equal precedence are grouped within { } symbols.
an empty string or zero; otherwise, returns zero.
expr1 {=, >, >=, <, <=, !=} expr2
Return the results of integer comparison if both arguments are
integers; otherwise, returns the results of string comparison
Return the results of floating point comparison if both arguments are
numbers; otherwise, returns the results of string comparison
using the locale-specific collation sequence. The result of each
comparison is 1 if the specified relation is true, or 0 if the
relation is false.
expr1 {+, -} expr2
Return the results of addition or subtraction of integer-valued
Return the results of addition or subtraction of floating point-valued
arguments.
expr1 {*, /, %} expr2
Return the results of multiplication, integer division, or
remainder of integer-valued arguments.
Return the results of multiplication, floating point division, or
remainder of arguments.
- expr1
Return the result of subtracting expr1 from 0.
@@ -274,7 +274,69 @@ Parentheses are used for grouping in the usual manner.
Operator precedence is applied as one would expect in any of the C
or C derived languages.
\subsection{Examples}
subsection{Floating Point Numbers}
In 1.6 and above, we shifted the \$\[...\] expressions to be calculated
via floating point numbers instead of integers. We use 'long double' numbers
when possible, which provide around 16 digits of precision with 12 byte numbers.
To specify a floating point constant, the number has to have this format: D.D, where D is
a string of base 10 digits. So, you can say 0.10, but you can't say .10 or 20.-- we hope
this is not an excessive restriction!
Floating point numbers are turned into strings via the '%g'/'%Lg' format of the printf
function set. This allows numbers to still 'look' like integers to those counting
on integer behavior. If you were counting on 1/4 evaluating to 0, you need to now say
TRUNC(1/4). For a list of all the truncation/rounding capabilities, see the next section.
subsection{Functions}
In 1.6 and above, we upgraded the $[] expressions to handle floating point numbers.
Because of this, folks counting on integer behavior would be disrupted. To make
the same results possible, some rounding and integer truncation functions have been
added to the core of the Expr2 parser. Indeed, dialplan functions can be called from
$[..] expressions without the ${...} operators. The only trouble might be in the fact that
the arguments to these functions must be specified with a comma. If you try to call
the MATH function, for example, and try to say 3 + MATH(7*8), the expression parser will
evaluate 7*8 for you into 56, and the MATH function will most likely complain that its
input doesn't make any sense.
We also provide access to most of the floating point functions in the C library. (but not all of them).
While we don't expect someone to want to do Fourier analysis in the dialplan, we
don't want to preclude it, either.
Here is a list of the 'builtin' functions in Expr2. All other dialplan functions
are available by simply calling them (read-only). In other words, you don't need to
surround function calls in \$\[...\] expressions with \$\{...\}. Don't jump to conclusions,
though! -- you still need to wrap variable names in curly braces!
\begin{Enumerate}
\item COS(x) x is in radians. Results vary from -1 to 1.
\item SIN(x) x is in radians. Results vary from -1 to 1.
\item TAN(x) x is in radians.
\item ACOS(x) x should be a value between -1 and 1.
\item ASIN(x) x should be a value between -1 and 1.
\item ATAN(x) returns the arc tangent in radians; between -PI/2 and PI/2.
\item ATAN2(x,y) returns a result resembling y/x, except that the signs of both args are used to determine the quadrant of the result. Its result is in radians, between -PI and PI.
\item POW(x,y) returns the value of x raised to the power of y.
\item SQRT(x) returns the square root of x.
\item FLOOR(x) rounds x down to the nearest integer.
\item CEIL(x) rounds x up to the nearest integer.
\item ROUND(x) rounds x to the nearest integer, but round halfway cases away from zero.
\item RINT(x) rounds x to the nearest integer, rounding halfway cases to the nearest even integer.
\item TRUNC(x) rounds x to the nearest integer not larger in absolute value.
\item REMAINDER(x,y) computes the remainder of dividing x by y. The return value is x - n*y, where n is the value x/y, rounded to the nearest integer.
If this quotient is 1/2, it is rounded to the nearest even number.
\item EXP(x) returns e to the x power.
\item EXP2(x) returns 2 to the x power.
\item LOG(x) returns the natural logarithm of x.
\item LOG2(x) returns the base 2 log of x.
\item LOG10(x) returns the base 10 log of x.
\end{Enumerate}
subsection{Examples}
\begin{verbatim}
"One Thousand Five Hundred" =~ "(T[^ ]+)"
@@ -310,6 +372,55 @@ or C derived languages.
(2+8)/2
returns 5, of course.
(3+8)/2
returns 5.5 now.
TRUNC((3+8)/2)
returns 5.
FLOOR(2.5)
returns 2
FLOOR(-2.5)
returns -3
CEIL(2.5)
returns 3.
CEIL(-2.5)
returns -2.
ROUND(2.5)
returns 3.
ROUND(3.5)
returns 4.
ROUND(-2.5)
returns -3
RINT(2.5)
returns 2.
RINT(3.5)
returns 4.
RINT(-2.5)
returns -2.
RINT(-3.5)
returns -4.
TRUNC(2.5)
returns 2.
TRUNC(3.5)
returns 3.
TRUNC(-3.5)
returns -3.
\begin{verbatim}
Of course, all of the above examples use constants, but would work the
@@ -319,9 +430,10 @@ variable reference \${CALLERIDNUM}, for instance.
\subsection{Numbers Vs. Strings}
Tokens consisting only of numbers are converted to 64-bit numbers for
most of the operators. This means that overflows can occur when the
numbers get above 18 digits. Warnings will appear in the logs in this
Tokens consisting only of numbers are converted to 'long double' if possible, which
are from 80 bits to 128 bits depending on the OS, compiler, and hardware.
This means that overflows can occur when the
numbers get above 18 digits (depending on the number of bits involved). Warnings will appear in the logs in this
case.
\subsection{Conditionals}

View File

@@ -18,12 +18,14 @@
#ifndef _ASTERISK_EXPR_H
#define _ASTERISK_EXPR_H
#ifndef STANDALONE
#include "asterisk/channel.h"
#endif
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
int ast_expr(char *expr, char *buf, int length);
int ast_expr(char *expr, char *buf, int length, struct ast_channel *chan);
#if defined(__cplusplus) || defined(c_plusplus)
}

File diff suppressed because it is too large Load Diff

View File

@@ -36,12 +36,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#endif
#ifdef __USE_ISOC99
#define FP___PRINTF "%.16Lg"
#define FP___PRINTF "%.18Lg"
#define FP___FMOD fmodl
#define FP___STRTOD strtold
#define FP___TYPE long double
#else
#define FP___PRINTF "%.8g"
#define FP___PRINTF "%.16g"
#define FP___FMOD fmod
#define FP___STRTOD strtod
#define FP___TYPE double
@@ -62,7 +62,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/ast_expr.h"
#include "asterisk/logger.h"
#ifndef STANDALONE
#include "asterisk/strings.h"
#include "asterisk/channel.h"
#endif
enum valtype {
AST_EXPR_number, AST_EXPR_numeric_string, AST_EXPR_string
@@ -101,6 +104,7 @@ struct parse_io
char *string;
struct val *val;
yyscan_t scanner;
struct ast_channel *chan;
};
void ast_yyset_column(int column_no, yyscan_t yyscanner);
@@ -133,6 +137,7 @@ static char *expr2_token_subst(char *mess);
\<\= { SET_COLUMNS; SET_STRING; return TOK_LE;}
\!\= { SET_COLUMNS; SET_STRING; return TOK_NE;}
\+ { SET_COLUMNS; SET_STRING; return TOK_PLUS;}
\, { SET_COLUMNS; SET_STRING; return TOK_COMMA;}
\- { SET_COLUMNS; SET_STRING; return TOK_MINUS;}
\* { SET_COLUMNS; SET_STRING; return TOK_MULT;}
\/ { SET_COLUMNS; SET_STRING; return TOK_DIV;}
@@ -166,7 +171,7 @@ static char *expr2_token_subst(char *mess);
return TOKEN;
}
[a-zA-Z0-9,.';\\_^$#@]+ {
[a-zA-Z0-9\.';\\_^$#@]+ {
SET_COLUMNS;
SET_STRING;
return TOKEN;
@@ -230,13 +235,14 @@ static char *expr2_token_subst(char *mess);
int ast_yyparse(void *); /* need to/should define this prototype for the call to yyparse */
int ast_yyerror(const char *, YYLTYPE *, struct parse_io *); /* likewise */
int ast_expr(char *expr, char *buf, int length)
int ast_expr(char *expr, char *buf, int length, struct ast_channel *chan)
{
struct parse_io io;
int return_value = 0;
memset(&io, 0, sizeof(io));
io.string = expr; /* to pass to the error routine */
io.chan = chan;
ast_yylex_init(&io.scanner);
@@ -310,6 +316,7 @@ static char *expr2_token_equivs1[] =
"TOK_COMPL",
"TOK_COLON",
"TOK_EQTILDE",
"TOK_COMMA",
"TOK_RP",
"TOK_LP"
};
@@ -335,6 +342,7 @@ static char *expr2_token_equivs2[] =
"!",
":",
"=~",
",",
")",
"("
};

View File

@@ -29,63 +29,66 @@
/* Put the tokens into the symbol table, so that GDB and other debuggers
know about them. */
enum yytokentype {
TOK_COLONCOLON = 258,
TOK_COND = 259,
TOK_OR = 260,
TOK_AND = 261,
TOK_NE = 262,
TOK_LE = 263,
TOK_GE = 264,
TOK_LT = 265,
TOK_GT = 266,
TOK_EQ = 267,
TOK_MINUS = 268,
TOK_PLUS = 269,
TOK_MOD = 270,
TOK_DIV = 271,
TOK_MULT = 272,
TOK_COMPL = 273,
TOK_EQTILDE = 274,
TOK_COLON = 275,
TOK_LP = 276,
TOK_RP = 277,
TOKEN = 278
TOK_COMMA = 258,
TOK_COLONCOLON = 259,
TOK_COND = 260,
TOK_OR = 261,
TOK_AND = 262,
TOK_NE = 263,
TOK_LE = 264,
TOK_GE = 265,
TOK_LT = 266,
TOK_GT = 267,
TOK_EQ = 268,
TOK_MINUS = 269,
TOK_PLUS = 270,
TOK_MOD = 271,
TOK_DIV = 272,
TOK_MULT = 273,
TOK_COMPL = 274,
TOK_EQTILDE = 275,
TOK_COLON = 276,
TOK_LP = 277,
TOK_RP = 278,
TOKEN = 279
};
#endif
/* Tokens. */
#define TOK_COLONCOLON 258
#define TOK_COND 259
#define TOK_OR 260
#define TOK_AND 261
#define TOK_NE 262
#define TOK_LE 263
#define TOK_GE 264
#define TOK_LT 265
#define TOK_GT 266
#define TOK_EQ 267
#define TOK_MINUS 268
#define TOK_PLUS 269
#define TOK_MOD 270
#define TOK_DIV 271
#define TOK_MULT 272
#define TOK_COMPL 273
#define TOK_EQTILDE 274
#define TOK_COLON 275
#define TOK_LP 276
#define TOK_RP 277
#define TOKEN 278
#define TOK_COMMA 258
#define TOK_COLONCOLON 259
#define TOK_COND 260
#define TOK_OR 261
#define TOK_AND 262
#define TOK_NE 263
#define TOK_LE 264
#define TOK_GE 265
#define TOK_LT 266
#define TOK_GT 267
#define TOK_EQ 268
#define TOK_MINUS 269
#define TOK_PLUS 270
#define TOK_MOD 271
#define TOK_DIV 272
#define TOK_MULT 273
#define TOK_COMPL 274
#define TOK_EQTILDE 275
#define TOK_COLON 276
#define TOK_LP 277
#define TOK_RP 278
#define TOKEN 279
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
#line 165 "ast_expr2.y"
#line 226 "ast_expr2.y"
{
struct val *val;
struct expr_node *arglist;
}
/* Line 1536 of yacc.c. */
#line 89 "ast_expr2.h"
#line 92 "ast_expr2.h"
YYSTYPE;
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1

View File

@@ -22,15 +22,55 @@
#endif
#ifdef __USE_ISOC99
#define FP___PRINTF "%.16Lg"
#define FP___PRINTF "%.18Lg"
#define FP___FMOD fmodl
#define FP___STRTOD strtold
#define FP___TYPE long double
#define FUNC_COS cosl
#define FUNC_SIN sinl
#define FUNC_TAN tanl
#define FUNC_ACOS acosl
#define FUNC_ASIN asinl
#define FUNC_ATAN atanl
#define FUNC_ATAN2 atan2l
#define FUNC_POW powl
#define FUNC_SQRT sqrtl
#define FUNC_FLOOR floorl
#define FUNC_CEIL ceill
#define FUNC_ROUND roundl
#define FUNC_RINT rintl
#define FUNC_TRUNC truncl
#define FUNC_EXP expl
#define FUNC_EXP2 exp2l
#define FUNC_LOG logl
#define FUNC_LOG2 log2l
#define FUNC_LOG10 log10l
#define FUNC_REMAINDER remainderl
#else
#define FP___PRINTF "%.8g"
#define FP___PRINTF "%.16g"
#define FP___FMOD fmod
#define FP___STRTOD strtod
#define FP___TYPE double
#define FUNC_COS cos
#define FUNC_SIN sin
#define FUNC_TAN tan
#define FUNC_ACOS acos
#define FUNC_ASIN asin
#define FUNC_ATAN atan
#define FUNC_ATAN2 atan2
#define FUNC_POW pow
#define FUNC_SQRT sqrt
#define FUNC_FLOOR floor
#define FUNC_CEIL ceil
#define FUNC_ROUND round
#define FUNC_RINT rint
#define FUNC_TRUNC trunc
#define FUNC_EXP exp
#define FUNC_EXP2 exp2
#define FUNC_LOG log
#define FUNC_LOG2 log2
#define FUNC_LOG10 log10
#define FUNC_REMAINDER remainder
#endif
#include <stdlib.h>
@@ -54,6 +94,9 @@
#include "asterisk.h"
#include "asterisk/ast_expr.h"
#include "asterisk/logger.h"
#ifndef STANDALONE
#include "asterisk/pbx.h"
#endif
#if defined(LONG_LONG_MIN) && !defined(QUAD_MIN)
#define QUAD_MIN LONG_LONG_MIN
@@ -91,6 +134,19 @@ struct val {
} u;
} ;
enum node_type {
AST_EXPR_NODE_COMMA, AST_EXPR_NODE_STRING, AST_EXPR_NODE_VAL
} ;
struct expr_node
{
enum node_type type;
struct val *val;
struct expr_node *left;
struct expr_node *right;
};
typedef void *yyscan_t;
struct parse_io
@@ -98,6 +154,7 @@ struct parse_io
char *string;
struct val *val;
yyscan_t scanner;
struct ast_channel *chan;
};
static int chk_div __P((FP___TYPE, FP___TYPE));
@@ -127,8 +184,12 @@ static struct val *op_or __P((struct val *, struct val *));
static struct val *op_plus __P((struct val *, struct val *));
static struct val *op_rem __P((struct val *, struct val *));
static struct val *op_times __P((struct val *, struct val *));
static struct val *op_func(struct val *funcname, struct expr_node *arglist, struct ast_channel *chan);
static int to_number __P((struct val *));
static void to_string __P((struct val *));
static struct expr_node *alloc_expr_node(enum node_type);
static void destroy_arglist(struct expr_node *arglist);
static int is_really_num(char *str);
/* uh, if I want to predeclare yylex with a YYLTYPE, I have to predeclare the yyltype... sigh */
typedef struct yyltype
@@ -164,11 +225,13 @@ int ast_yyerror(const char *,YYLTYPE *, struct parse_io *);
%union
{
struct val *val;
struct expr_node *arglist;
}
%{
extern int ast_yylex __P((YYSTYPE *, YYLTYPE *, yyscan_t));
%}
%left <val> TOK_COMMA
%left <val> TOK_COND TOK_COLONCOLON
%left <val> TOK_OR
%left <val> TOK_AND
@@ -181,6 +244,7 @@ extern int ast_yylex __P((YYSTYPE *, YYLTYPE *, yyscan_t));
%token <val> TOKEN
%type <arglist> arglist
%type <val> start expr
@@ -205,7 +269,23 @@ start: expr { ((struct parse_io *)parseio)->val = (struct val *)calloc(sizeof(st
;
expr: TOKEN { $$= $1;}
arglist: expr { $$ = alloc_expr_node(AST_EXPR_NODE_VAL); $$->val = $1;}
| arglist TOK_COMMA expr %prec TOK_RP{struct expr_node *x = alloc_expr_node(AST_EXPR_NODE_VAL);
struct expr_node *t;
DESTROY($2);
for (t=$1;t->right;t=t->right)
;
$$ = $1; t->right = x; x->val = $3;}
;
expr:
TOKEN TOK_LP arglist TOK_RP { $$ = op_func($1,$3, ((struct parse_io *)parseio)->chan);
DESTROY($2);
DESTROY($4);
DESTROY($1);
destroy_arglist($3);
}
| TOKEN {$$ = $1;}
| TOK_LP expr TOK_RP { $$ = $2;
@$.first_column = @1.first_column; @$.last_column = @3.last_column;
@$.first_line=0; @$.last_line=0;
@@ -287,6 +367,19 @@ expr: TOKEN { $$= $1;}
%%
static struct expr_node *alloc_expr_node(enum node_type nt)
{
struct expr_node *x = calloc(1,sizeof(struct expr_node));
if (!x) {
ast_log(LOG_ERROR, "Allocation for expr_node FAILED!!\n");
return 0;
}
x->type = nt;
return x;
}
static struct val *
make_number (FP___TYPE i)
{
@@ -475,7 +568,7 @@ int main(int argc,char **argv) {
if( s[strlen(s)-1] == '\n' )
s[strlen(s)-1] = 0;
ret = ast_expr(s, out, sizeof(out));
ret = ast_expr(s, out, sizeof(out),NULL);
printf("Expression: %s Result: [%d] '%s'\n",
s, ret, out);
}
@@ -483,7 +576,7 @@ int main(int argc,char **argv) {
}
else
{
if (ast_expr(argv[1], s, sizeof(s)))
if (ast_expr(argv[1], s, sizeof(s), NULL))
printf("=====%s======\n",s);
else
printf("No result\n");
@@ -500,6 +593,304 @@ int main(int argc,char **argv) {
let it access the BUFFER stuff there and not trying
define all the structs, macros etc. in this file! */
static void destroy_arglist(struct expr_node *arglist)
{
struct expr_node *arglist_next;
while (arglist)
{
arglist_next = arglist->right;
if (arglist->val)
free_value(arglist->val);
arglist->val = 0;
arglist->right = 0;
free(arglist);
arglist = arglist_next;
}
}
static char *compose_func_args(struct expr_node *arglist)
{
struct expr_node *t = arglist;
char *argbuf;
int total_len = 0;
while (t) {
if (t != arglist)
total_len += 1; /* for the sep */
if (t->val) {
if (t->val->type == AST_EXPR_number)
total_len += 25; /* worst case */
else
total_len += strlen(t->val->u.s);
}
t = t->right;
}
total_len++; /* for the null */
ast_log(LOG_NOTICE,"argbuf allocated %d bytes;\n", total_len);
argbuf = malloc(total_len);
argbuf[0] = 0;
t = arglist;
while (t) {
char numbuf[30];
if (t != arglist)
strcat(argbuf,"|");
if (t->val) {
if (t->val->type == AST_EXPR_number) {
sprintf(numbuf,FP___PRINTF,t->val->u.i);
strcat(argbuf,numbuf);
} else
strcat(argbuf,t->val->u.s);
}
t = t->right;
}
ast_log(LOG_NOTICE,"argbuf uses %d bytes;\n", strlen(argbuf));
return argbuf;
}
static int is_really_num(char *str)
{
if ( strspn(str,"-0123456789. ") == strlen(str))
return 1;
else
return 0;
}
static struct val *op_func(struct val *funcname, struct expr_node *arglist, struct ast_channel *chan)
{
if (strspn(funcname->u.s,"ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789") == strlen(funcname->u.s))
{
struct val *result;
if (strcmp(funcname->u.s,"COS") == 0) {
if (arglist && !arglist->right && arglist->val){
to_number(arglist->val);
result = make_number(FUNC_COS(arglist->val->u.i));
return result;
} else {
ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
return make_number(0.0);
}
} else if (strcmp(funcname->u.s,"SIN") == 0) {
if (arglist && !arglist->right && arglist->val){
to_number(arglist->val);
result = make_number(FUNC_SIN(arglist->val->u.i));
return result;
} else {
ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
return make_number(0.0);
}
} else if (strcmp(funcname->u.s,"TAN") == 0) {
if (arglist && !arglist->right && arglist->val){
to_number(arglist->val);
result = make_number(FUNC_TAN(arglist->val->u.i));
return result;
} else {
ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
return make_number(0.0);
}
} else if (strcmp(funcname->u.s,"ACOS") == 0) {
if (arglist && !arglist->right && arglist->val){
to_number(arglist->val);
result = make_number(FUNC_ACOS(arglist->val->u.i));
return result;
} else {
ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
return make_number(0.0);
}
} else if (strcmp(funcname->u.s,"ASIN") == 0) {
if (arglist && !arglist->right && arglist->val){
to_number(arglist->val);
result = make_number(FUNC_ASIN(arglist->val->u.i));
return result;
} else {
ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
return make_number(0.0);
}
} else if (strcmp(funcname->u.s,"ATAN") == 0) {
if (arglist && !arglist->right && arglist->val){
to_number(arglist->val);
result = make_number(FUNC_ATAN(arglist->val->u.i));
return result;
} else {
ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
return make_number(0.0);
}
} else if (strcmp(funcname->u.s,"ATAN2") == 0) {
if (arglist && arglist->right && !arglist->right->right && arglist->val && arglist->right->val){
to_number(arglist->val);
to_number(arglist->right->val);
result = make_number(FUNC_ATAN2(arglist->val->u.i, arglist->right->val->u.i));
return result;
} else {
ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
return make_number(0.0);
}
} else if (strcmp(funcname->u.s,"POW") == 0) {
if (arglist && arglist->right && !arglist->right->right && arglist->val && arglist->right->val){
to_number(arglist->val);
to_number(arglist->right->val);
result = make_number(FUNC_POW(arglist->val->u.i, arglist->right->val->u.i));
return result;
} else {
ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
return make_number(0.0);
}
} else if (strcmp(funcname->u.s,"SQRT") == 0) {
if (arglist && !arglist->right && arglist->val){
to_number(arglist->val);
result = make_number(FUNC_SQRT(arglist->val->u.i));
return result;
} else {
ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
return make_number(0.0);
}
} else if (strcmp(funcname->u.s,"FLOOR") == 0) {
if (arglist && !arglist->right && arglist->val){
to_number(arglist->val);
result = make_number(FUNC_FLOOR(arglist->val->u.i));
return result;
} else {
ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
return make_number(0.0);
}
} else if (strcmp(funcname->u.s,"CEIL") == 0) {
if (arglist && !arglist->right && arglist->val){
to_number(arglist->val);
result = make_number(FUNC_CEIL(arglist->val->u.i));
return result;
} else {
ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
return make_number(0.0);
}
} else if (strcmp(funcname->u.s,"ROUND") == 0) {
if (arglist && !arglist->right && arglist->val){
to_number(arglist->val);
result = make_number(FUNC_ROUND(arglist->val->u.i));
return result;
} else {
ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
return make_number(0.0);
}
} else if (strcmp(funcname->u.s,"RINT") == 0) {
if (arglist && !arglist->right && arglist->val){
to_number(arglist->val);
result = make_number(FUNC_RINT(arglist->val->u.i));
return result;
} else {
ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
return make_number(0.0);
}
} else if (strcmp(funcname->u.s,"TRUNC") == 0) {
if (arglist && !arglist->right && arglist->val){
to_number(arglist->val);
result = make_number(FUNC_TRUNC(arglist->val->u.i));
return result;
} else {
ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
return make_number(0.0);
}
} else if (strcmp(funcname->u.s,"EXP") == 0) {
if (arglist && !arglist->right && arglist->val){
to_number(arglist->val);
result = make_number(FUNC_EXP(arglist->val->u.i));
return result;
} else {
ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
return make_number(0.0);
}
} else if (strcmp(funcname->u.s,"EXP2") == 0) {
if (arglist && !arglist->right && arglist->val){
to_number(arglist->val);
result = make_number(FUNC_EXP2(arglist->val->u.i));
return result;
} else {
ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
return make_number(0.0);
}
} else if (strcmp(funcname->u.s,"LOG") == 0) {
if (arglist && !arglist->right && arglist->val){
to_number(arglist->val);
result = make_number(FUNC_LOG(arglist->val->u.i));
return result;
} else {
ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
return make_number(0.0);
}
} else if (strcmp(funcname->u.s,"LOG2") == 0) {
if (arglist && !arglist->right && arglist->val){
to_number(arglist->val);
result = make_number(FUNC_LOG2(arglist->val->u.i));
return result;
} else {
ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
return make_number(0.0);
}
} else if (strcmp(funcname->u.s,"LOG10") == 0) {
if (arglist && !arglist->right && arglist->val){
to_number(arglist->val);
result = make_number(FUNC_LOG10(arglist->val->u.i));
return result;
} else {
ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
return make_number(0.0);
}
} else if (strcmp(funcname->u.s,"REMAINDER") == 0) {
if (arglist && arglist->right && !arglist->right->right && arglist->val && arglist->right->val){
to_number(arglist->val);
to_number(arglist->right->val);
result = make_number(FUNC_REMAINDER(arglist->val->u.i, arglist->right->val->u.i));
return result;
} else {
ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
return make_number(0.0);
}
} else {
/* is this a custom function we should execute and collect the results of? */
#ifndef STANDALONE
struct ast_custom_function *f = ast_custom_function_find(funcname->u.s);
if (!chan)
ast_log(LOG_WARNING,"Hey! chan is NULL.\n");
if (!f)
ast_log(LOG_WARNING,"Hey! could not find func %s.\n", funcname->u.s);
if (f && chan) {
if (f->read) {
char workspace[512];
char *argbuf = compose_func_args(arglist);
f->read(chan, funcname->u.s, argbuf, workspace, sizeof(workspace));
free(argbuf);
if (is_really_num(workspace))
return make_number(FP___STRTOD(workspace,(char **)NULL));
else
return make_str(workspace);
} else {
ast_log(LOG_ERROR,"Error! Function '%s' cannot be read!\n", funcname->u.s);
return (make_number ((FP___TYPE)0.0));
}
} else {
ast_log(LOG_ERROR,"Error! '%s' doesn't appear to be an available function!", funcname->u.s);
return (make_number ((FP___TYPE)0.0));
}
#else
ast_log(LOG_ERROR,"Error! '%s' is not available in the standalone version!", funcname->u.s);
return (make_number ((FP___TYPE)0.0));
#endif
}
}
else
{
ast_log(LOG_ERROR,"Error! '%s' is not possibly a function name!", funcname->u.s);
return (make_number ((FP___TYPE)0.0));
}
return (make_number ((FP___TYPE)0.0));
}
static struct val *
op_or (struct val *a, struct val *b)
@@ -519,7 +910,7 @@ op_and (struct val *a, struct val *b)
if (is_zero_or_null (a) || is_zero_or_null (b)) {
free_value (a);
free_value (b);
return (make_number ((double)0.0));
return (make_number ((FP___TYPE)0.0));
} else {
free_value (b);
return (a);

File diff suppressed because it is too large Load Diff

View File

@@ -1677,7 +1677,7 @@ static void pbx_substitute_variables_helper_full(struct ast_channel *c, struct v
vars = var;
}
length = ast_expr(vars, cp2, count);
length = ast_expr(vars, cp2, count, c);
if (length) {
ast_debug(1, "Expression result is '%s'\n", cp2);

View File

@@ -90,7 +90,7 @@ void check_pval_item(pval *item, struct argapp *apps, int in_globals);
void check_switch_expr(pval *item, struct argapp *apps);
void ast_expr_register_extra_error_info(char *errmsg);
void ast_expr_clear_extra_error_info(void);
int ast_expr(char *expr, char *buf, int length);
int ast_expr(char *expr, char *buf, int length,struct ast_channel *chan);
struct pval *find_macro(char *name);
struct pval *find_context(char *name);
struct pval *find_context(char *name);
@@ -2640,7 +2640,7 @@ void check_pval_item(pval *item, struct argapp *apps, int in_globals)
if( !in_globals ) { /* don't check stuff inside the globals context; no wrapping in $[ ] there... */
snprintf(errmsg,sizeof(errmsg), "file %s, line %d, columns %d-%d, variable declaration expr '%s':", config, item->startline, item->startcol, item->endcol, item->u2.val);
ast_expr_register_extra_error_info(errmsg);
ast_expr(item->u2.val, expr_output, sizeof(expr_output));
ast_expr(item->u2.val, expr_output, sizeof(expr_output),NULL);
ast_expr_clear_extra_error_info();
if ( strpbrk(item->u2.val,"~!-+<>=*/&^") && !strstr(item->u2.val,"${") ) {
ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
@@ -2686,12 +2686,12 @@ void check_pval_item(pval *item, struct argapp *apps, int in_globals)
strp = strchr(item->u1.for_init, '=');
if (strp) {
ast_expr(strp+1, expr_output, sizeof(expr_output));
ast_expr(strp+1, expr_output, sizeof(expr_output),NULL);
}
ast_expr(item->u2.for_test, expr_output, sizeof(expr_output));
ast_expr(item->u2.for_test, expr_output, sizeof(expr_output),NULL);
strp = strchr(item->u3.for_inc, '=');
if (strp) {
ast_expr(strp+1, expr_output, sizeof(expr_output));
ast_expr(strp+1, expr_output, sizeof(expr_output),NULL);
}
if ( strpbrk(item->u2.for_test,"~!-+<>=*/&^") && !strstr(item->u2.for_test,"${") ) {
ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
@@ -2717,7 +2717,7 @@ void check_pval_item(pval *item, struct argapp *apps, int in_globals)
*/
snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, while expr '%s':", config, item->startline, item->startcol, item->endcol, item->u1.str);
ast_expr_register_extra_error_info(errmsg);
ast_expr(item->u1.str, expr_output, sizeof(expr_output));
ast_expr(item->u1.str, expr_output, sizeof(expr_output),NULL);
ast_expr_clear_extra_error_info();
if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
@@ -2754,7 +2754,7 @@ void check_pval_item(pval *item, struct argapp *apps, int in_globals)
*/
snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, random expr '%s':", config, item->startline, item->startcol, item->endcol, item->u1.str);
ast_expr_register_extra_error_info(errmsg);
ast_expr(item->u1.str, expr_output, sizeof(expr_output));
ast_expr(item->u1.str, expr_output, sizeof(expr_output),NULL);
ast_expr_clear_extra_error_info();
if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: random expression '%s' has operators, but no variables. Interesting...\n",
@@ -2797,7 +2797,7 @@ void check_pval_item(pval *item, struct argapp *apps, int in_globals)
*/
snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, if expr '%s':", config, item->startline, item->startcol, item->endcol, item->u1.str);
ast_expr_register_extra_error_info(errmsg);
ast_expr(item->u1.str, expr_output, sizeof(expr_output));
ast_expr(item->u1.str, expr_output, sizeof(expr_output),NULL);
ast_expr_clear_extra_error_info();
if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression '%s' has operators, but no variables. Interesting...\n",

View File

@@ -110,6 +110,15 @@ struct ast_app *pbx_findapp(const char *app)
return (struct ast_app*)1; /* so as not to trigger an error */
}
struct ast_custom_function *ast_custom_function_find(const char *name);
struct ast_custom_function *ast_custom_function_find(const char *name)
{
return 0; /* in "standalone" mode, functions are just not avail */
}
void ast_add_profile(void)
{
if (!no_comp)

View File

@@ -161,6 +161,13 @@ unsigned int check_expr(char* buffer, char* error_report)
return warn_found;
}
struct ast_custom_function *ast_custom_function_find(const char *name);
struct ast_custom_function *ast_custom_function_find(const char *name)
{
return 0;
}
int check_eval(char *buffer, char *error_report)
{
char *cp, *ep;
@@ -221,7 +228,7 @@ int check_eval(char *buffer, char *error_report)
*ep++ = 0;
/* now, run the test */
result = ast_expr(evalbuf, s, sizeof(s));
result = ast_expr(evalbuf, s, sizeof(s),NULL);
if (result) {
sprintf(error_report,"line %d, evaluation of $[ %s ] result: %s\n", global_lineno, evalbuf, s);
return 1;

View File

@@ -93,4 +93,34 @@ something
2.1+4.2
1.500003+1.4999999999999898989898989898989898989889898
1/4
2.3 + COS(3.141592653)
REMAINDER(13,3)
2.3 + SIN(3.1415823)
TAN(45) + 2.3
POW(10.0,4.0)
SQRT(4)
SQRT(2)
FLOOR(2.4)
FLOOR(2.6)
CEIL(2.4)
CEIL(2.6)
ROUND(2.4)
ROUND(2.5)
ROUND(2.6)
RINT(2.4)
RINT(2.5)
RINT(2.6)
TRUNC(2.4)
TRUNC(2.5)
TRUNC(2.6)
EXP(1.0)
EXP2(1.0)
LOG(10)
LOG2(10)
LOG10(10)
ATAN2(4,5)
ACOS(12)
ASIN(1)
ATAN(10)
SQRT(2)*SQRT(2)
MATH(3*9)