295 lines
6.7 KiB
Plaintext
295 lines
6.7 KiB
Plaintext
%{
|
|
#include <stdio.h>
|
|
#include <math.h> /* For math functions, cos(), sin(), etc. */
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include "xschem.h"
|
|
|
|
static const char *str, *strptr;
|
|
static char *ret = NULL;
|
|
|
|
static int dbglev = 1;
|
|
|
|
/* Data type for links in the chain of functions. */
|
|
struct symrec
|
|
{
|
|
char *name; /* name of symbol */
|
|
double (*fnctptr)(double); /* value of a FNCT */
|
|
double value;
|
|
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);
|
|
static void get_char(int c);
|
|
static double rnd6(double x) {return round_to_n_digits(x, 6);}
|
|
|
|
struct fn
|
|
{
|
|
char *fname;
|
|
double (*fnct)();
|
|
double value;
|
|
};
|
|
static int lex_state = 0;
|
|
struct fn fn_array[]
|
|
= {
|
|
{"int" , toint, 0},
|
|
{"sin" , sin, 0},
|
|
{"cos" , cos, 0},
|
|
{"exp" , exp, 0},
|
|
{"asin" , asin, 0},
|
|
{"acos" , acos, 0},
|
|
{"atan" , atan, 0},
|
|
{"log" , log10, 0},
|
|
{"ln" , log, 0},
|
|
{"exp" , exp, 0},
|
|
{"sqrt" , sqrt, 0},
|
|
{"round" , rnd6, 0},
|
|
{"pi" , NULL, 3.141592653589793},
|
|
{"e" , NULL, 2.718281828459045},
|
|
{"k" , NULL, 1.380649e-23},
|
|
{"h" , NULL, 6.62607e-34},
|
|
{"echarge" , NULL, 1.60217646e-19},
|
|
{"abszero" , NULL, 273.15},
|
|
{"c" , NULL, 2.99792458e8},
|
|
{0 , 0, 0}
|
|
};
|
|
%}
|
|
|
|
|
|
/* %define api.prefix {kk} */
|
|
%union {
|
|
int c;
|
|
double val; /* For returning numbers. */
|
|
symrec *tptr; /* For returning symbol-table pointers */
|
|
}
|
|
|
|
%token STREND 0
|
|
%token <c> XCHAR
|
|
%token EXPR /* expr( */
|
|
%token <val> NUM /* Simple double precision number */
|
|
%token <tptr> FNCT /* Variable and Function */
|
|
%type <val> exp
|
|
%right '='
|
|
%left '-' '+'
|
|
%left '*' '/' '%'
|
|
%left NEG /* Negation--unary minus */
|
|
%right '^' /* Exponentiation */
|
|
|
|
/* Grammar follows */
|
|
%%
|
|
input:
|
|
| input line
|
|
;
|
|
|
|
line:
|
|
XCHAR {get_char($1);}
|
|
| EXPR exp ')' {get_expr($2);lex_state = 0;}
|
|
| EXPR '\'' exp '\'' ')' {get_expr($3);lex_state = 0;}
|
|
| EXPR exp error
|
|
;
|
|
|
|
exp: NUM {$$ = $1;}
|
|
| FNCT '(' exp ')' {$$ = $1 ? ($1->fnctptr ? (*($1->fnctptr))($3) : 0.0) : 0.0;}
|
|
| FNCT {$$ = $1 ? $1->value : 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_char(int c)
|
|
{
|
|
char s[2];
|
|
dbg(dbglev, "get_char: %c |%s|\n", c, str);
|
|
s[0] = (char)c;
|
|
s[1] = '\0';
|
|
my_mstrcat(_ALLOC_ID_, &ret, s, NULL);
|
|
strptr = str;
|
|
}
|
|
|
|
static void get_expr(double x)
|
|
{
|
|
char xx[100];
|
|
dbg(dbglev,"get_expr(): x=%g\n", x);
|
|
my_snprintf(xx, S(xx), "%.15g", 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);
|
|
}
|
|
|
|
/* ad=expr(3*xa) pd=expr(2*(3+xa)) --> ad=3*xa pd=2*(3+xa) */
|
|
static void remove_expr(char *s)
|
|
{
|
|
char *ptr = s;
|
|
int plev = 0;
|
|
while(*ptr) {
|
|
if(strstr(ptr, "expr(") == ptr) {
|
|
ptr += 5;
|
|
plev++;
|
|
}
|
|
if(*ptr == '(') plev++;
|
|
if(*ptr == ')') {
|
|
plev--;
|
|
if(plev == 0) ptr++;
|
|
if(!ptr) break;
|
|
}
|
|
*s = *ptr;
|
|
ptr++;
|
|
s++;
|
|
}
|
|
*s = *ptr;
|
|
}
|
|
|
|
|
|
static void kkerror(char *s) /* Called by kkparse on error */
|
|
{
|
|
char *ss = NULL;
|
|
dbg(dbglev, "error: |%s|\n\n |%s|\n", s, str ? str : "<NULL>");
|
|
my_strdup2(_ALLOC_ID_, &ss, strptr);
|
|
remove_expr(ss);
|
|
my_mstrcat(_ALLOC_ID_, &ret, ss, NULL);
|
|
my_free(_ALLOC_ID_, &ss);
|
|
lex_state = 0;
|
|
}
|
|
|
|
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;
|
|
ptr->value = fn_array[i].value;
|
|
}
|
|
}
|
|
|
|
void eval_expr_clear_table(void)
|
|
{
|
|
symrec *ptr = sym_table;
|
|
while(ptr) {
|
|
symrec *tmp = ptr;
|
|
ptr = ptr->next;
|
|
my_free(_ALLOC_ID_, &(tmp->name));
|
|
my_free(_ALLOC_ID_, &tmp);
|
|
}
|
|
}
|
|
|
|
|
|
static int kklex()
|
|
{
|
|
int c;
|
|
|
|
if(!str) { return 0; }
|
|
if(strstr(str, "expr(") == str) {
|
|
lex_state = 1;
|
|
str += 5;
|
|
dbg(dbglev, "lex(): EXPR\n");
|
|
return EXPR;
|
|
}
|
|
if(!lex_state) {
|
|
c = *str++;
|
|
if(c) {
|
|
kklval.c = c;
|
|
dbg(dbglev, "lex(): XCHAR; %c\n", c);
|
|
return XCHAR;
|
|
} else {
|
|
dbg(dbglev, "lex(): STREND\n");
|
|
return STREND;
|
|
}
|
|
}
|
|
/* 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_eng(s);
|
|
str += rd;
|
|
dbg(dbglev, "lex(): 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;
|
|
dbg(dbglev, "ylex: FNCT=%s\n", symbuf);
|
|
if(s) return FNCT;
|
|
return 0; /* error : undefined identifier */
|
|
}
|
|
/* Any other character is a token by itself. */
|
|
return c;
|
|
}
|
|
|
|
char *eval_expr(const char *s)
|
|
{
|
|
lex_state = 0;
|
|
if(ret) my_free(_ALLOC_ID_, &ret);
|
|
strptr = str = s;
|
|
kkparse();
|
|
return ret;
|
|
}
|