diff --git a/src/Makefile.in b/src/Makefile.in index 4eec5b71..ec2d23ef 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -5,7 +5,7 @@ put /local/src { select.c font.c editprop.c save.c paste.c token.c psprint.c node_hash.c hilight.c options.c vhdl_netlist.c svgdraw.c spice_netlist.c tedax_netlist.c verilog_netlist.c parselabel.c expandlabel.c - in_memory_undo.c cairo_jpg.c + eval_expr.c in_memory_undo.c cairo_jpg.c } # list all files that need to be installed in "$(XSHAREDIR)" @@ -49,12 +49,15 @@ parselabel.c: parselabel.l expandlabel.h expandlabel.c expandlabel.h: expandlabel.y bison -d -o expandlabel.c expandlabel.y +eval_expr.c: eval_expr.y + bison -o eval_expr.c eval_expr.y + parselabel.o: expandlabel.h $(OBJ): xschem.h ../config.h Makefile clean: FORCE - rm -rf rawtovcd xschem *.o expandlabel.[ch] parselabel.c + rm -rf rawtovcd xschem *.o eval_expr.c expandlabel.[ch] parselabel.c # Explicit rule for each object: @] diff --git a/src/eval_expr.y b/src/eval_expr.y new file mode 100644 index 00000000..c7d14773 --- /dev/null +++ b/src/eval_expr.y @@ -0,0 +1,208 @@ +%{ +#include +#include /* For math functions, cos(), sin(), etc. */ +#include +#include +#include +#include +#include "xschem.h" + +static const char *str, *strptr; +static char *ret = NULL; + +/* Data type for links in the chain of functions. */ +struct symrec +{ + char *name; /* name of symbol */ + double (*fnctptr)(); /* value of a FNCT */ + struct symrec *next; /* link field */ +}; + +typedef struct symrec symrec; + +/* The symbol table: a chain of `struct symrec'. */ +symrec *sym_table = (symrec *)0; + +static symrec *getsym(char *sym_name); +static symrec *putsym(char *sym_name); +static int kklex(); +static void kkerror(char *s); +static double toint(double x); +static void get_expr(double x); + +struct fn +{ + char *fname; + double (*fnct)(); +}; + +struct fn fn_array[] + = { + {"int" , toint}, + {"sin" , sin}, + {"cos" , cos}, + {"asin", asin}, + {"acos", acos}, + {"atan", atan}, + {"log" , log10}, + {"ln" , log}, + {"exp" , exp}, + {"sqrt", sqrt}, + {0 , 0} + }; +%} + +%define api.prefix {kk} +%union { +double val; /* For returning numbers. */ +symrec *tptr; /* For returning symbol-table pointers */ +} + +%token STREND 0 +%token NUM /* Simple double precision number */ +%token FNCT /* Variable and Function */ +%type exp +%right '=' +%left '-' '+' +%left '*' '/' '%' +%left NEG /* Negation--unary minus */ +%right '^' /* Exponentiation */ + +/* Grammar follows */ +%% + +line: + | exp STREND {get_expr($1);} + | '\'' exp '\'' STREND {get_expr($2);} +; + +exp: NUM { $$ = $1;} + | FNCT '(' exp ')' { $$ = $1 ? (*($1->fnctptr))($3) : 0.0;} + | exp '+' exp { $$ = $1 + $3; } + | exp '-' exp { $$ = $1 - $3;} + | exp '*' exp { $$ = $1 * $3;} + | exp '%' exp { $$ = (int)$1 % (int)$3;} + | exp '/' exp { $$ = $1 / $3;} + | '-' exp %prec NEG { $$ = -$2;} + | exp '^' exp { $$ = pow ($1, $3);} + | '(' exp ')' { $$ = $2;} +; +/* End of grammar */ +%% + +static void get_expr(double x) +{ + char xx[100]; + my_snprintf(xx, S(xx), "%.17g", x); + my_mstrcat(_ALLOC_ID_, &ret, xx, NULL); + strptr = str; +} + +static double toint(double x) +{ + if(x < 0.0) return ceil(x); + return floor(x); +} + +static void kkerror(char *s) /* Called by kkparse on error */ +{ + /* printf("error: |%s|\n\n |%s|\n", s, strptr); */ + my_mstrcat(_ALLOC_ID_, &ret, strptr, NULL); +} + +static symrec *getsym(char *sym_name) +{ + symrec *ptr; + for (ptr = sym_table; ptr; ptr = ptr->next) + if (strcmp (ptr->name,sym_name) == 0) return ptr; + return NULL; +} + +symrec * putsym (char *sym_name) +{ + symrec *ptr; + ptr = (symrec *) my_malloc(_ALLOC_ID_, sizeof (symrec)); + ptr->name = (char *) my_malloc(_ALLOC_ID_, strlen (sym_name) + 1); + strcpy (ptr->name,sym_name); + ptr->next = (struct symrec *)sym_table; + sym_table = ptr; + return ptr; +} + +void eval_expr_init_table(void) /* puts arithmetic functions in table. */ +{ + int i; + symrec *ptr; + for (i = 0; fn_array[i].fname != 0; i++) + { + ptr = putsym (fn_array[i].fname); + ptr->fnctptr = fn_array[i].fnct; + } +} + +void eval_expr_clear_table(void) +{ + symrec *ptr; + for (ptr = sym_table; ptr; ptr = ptr->next) { + symrec *tmp = ptr; + my_free(_ALLOC_ID_, &(tmp->name)); + my_free(_ALLOC_ID_, &tmp); + } +} + +static int kklex() +{ + int c; + + if(!str) { return 0; } + /* Ignore whitespace, get first nonwhite character. */ + while ((c = *str++) == ' ' || c == '\t' || c == '\n'); + if (c == 0) { + str = NULL; + return 0; + } + /* Char starts a number => parse the number. */ + if (c == '.' || isdigit (c)) + { + char s[100] =""; + int rd = 0; + str--; + + sscanf(str, "%99[.0-9a-zA-Z_]%n", s, &rd); + kklval.val = atof_spice(s); + str += rd; + /* printf("yylex: NUM: %s\n", s); */ + return NUM; + } + /* Char starts an identifier => read the name. */ + if(isalpha(c)) { + symrec *s; + int length = 40; /* max length of function names */ + char symbuf[41]; + int i = 0; + /* Initially make the buffer int enough + * for a 40-character symbol name. */ + do + { + symbuf[i++] = (char) c; /* Add this character to the buffer.*/ + c = *str++; /* Get another character.*/ + } + while (c != 0 && isalnum(c) && i < length); + str--; + symbuf[i] = '\0'; + s = getsym (symbuf); + kklval.tptr = s; + /* printf("yylex: FNCT=%s\n", symbuf); */ + return FNCT; + } + /* Any other character is a token by itself. */ + return c; +} + +char *eval_expr(const char *s) +{ + if(ret) my_free(_ALLOC_ID_, &ret); + strptr = str = s; + kkparse(); + return ret; +} diff --git a/src/scheduler.c b/src/scheduler.c index 22322d33..8641ca0f 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -1008,6 +1008,16 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg } } + + /* eval_expr str + * debug function: evaluate arithmetic expression in str */ + else if(!strcmp(argv[1], "eval_expr")) + { + if(argc > 2) { + Tcl_SetResult(interp, eval_expr(argv[2]), TCL_VOLATILE); + } + } + /* exit [exit_code] [closewindow] [force] * Exit the program, ask for confirm if current file modified. * if exit_code is given exit with its value, otherwise use 0 exit code diff --git a/src/xinit.c b/src/xinit.c index 9ad21557..8e96bde1 100644 --- a/src/xinit.c +++ b/src/xinit.c @@ -955,6 +955,7 @@ static void xwin_exit(void) trim_chars(NULL, ""); /* clear static data in function */ tcl_hook2(NULL); /* clear static data in function */ save_ascii_string(NULL, NULL, 0); /* clear static data in function */ + eval_expr_clear_table(); /* clear expression parser data */ dbg(1, "xwin_exit(): removing font\n"); for(i=0;i<127; ++i) my_free(_ALLOC_ID_, &character[i]); dbg(1, "xwin_exit(): closed display\n"); @@ -2310,6 +2311,7 @@ int Tcl_AppInit(Tcl_Interp *inter) #ifdef __unix__ const char* home_buff; #endif + eval_expr_init_table(); /* get PWD and HOME */ if(!getcwd(pwd_dir, PATH_MAX)) { fprintf(errfp, "Tcl_AppInit(): getcwd() failed\n"); diff --git a/src/xschem.h b/src/xschem.h index 8dad9125..318e321f 100644 --- a/src/xschem.h +++ b/src/xschem.h @@ -1675,6 +1675,9 @@ extern void check_box_storage(int c); extern void check_arc_storage(int c); extern void check_line_storage(int c); extern void check_polygon_storage(int c); +extern void eval_expr_init_table(void); +extern void eval_expr_clear_table(void); +extern char *eval_expr(const char *s); extern const char *expandlabel(const char *s, int *m); extern void parse(const char *s); extern void clear_expandlabel_data(void);