aboutsummaryrefslogtreecommitdiffstats
path: root/main/ast_expr2.y
diff options
context:
space:
mode:
authormurf <murf@f38db490-d61c-443f-a65b-d21fe96a405b>2007-07-05 18:15:22 +0000
committermurf <murf@f38db490-d61c-443f-a65b-d21fe96a405b>2007-07-05 18:15:22 +0000
commitc66181df6174ec0fe12727a20d343eb908cbad0c (patch)
treee547079acc9ff3f5fd41827b21146c28d72030c8 /main/ast_expr2.y
parent3d7a0fea3e3c0b971b63d23184db06196dd1a771 (diff)
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: http://svn.digium.com/svn/asterisk/trunk@73449 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'main/ast_expr2.y')
-rw-r--r--main/ast_expr2.y407
1 files changed, 399 insertions, 8 deletions
diff --git a/main/ast_expr2.y b/main/ast_expr2.y
index 8b43223fc..d6c5e8eaa 100644
--- a/main/ast_expr2.y
+++ b/main/ast_expr2.y
@@ -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,23 +225,26 @@ 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
%left <val> TOK_EQ TOK_GT TOK_LT TOK_GE TOK_LE TOK_NE
%left <val> TOK_PLUS TOK_MINUS
-%left <val> TOK_MULT TOK_DIV TOK_MOD
+%left <val> TOK_MULT TOK_DIV TOK_MOD
%right <val> TOK_COMPL
%left <val> TOK_COLON TOK_EQTILDE
%left <val> TOK_RP TOK_LP
%token <val> TOKEN
+%type <arglist> arglist
%type <val> start expr
@@ -205,8 +269,24 @@ start: expr { ((struct parse_io *)parseio)->val = (struct val *)calloc(sizeof(st
;
-expr: TOKEN { $$= $1;}
- | TOK_LP expr TOK_RP { $$ = $2;
+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;
DESTROY($1); DESTROY($3); }
@@ -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);