/* functions to handle the unit parser/comparison engine
Copyright (C) 1992-2000 Michigan State University
The CAPA system is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The CAPA system is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public
License along with the CAPA system; see the file COPYING. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
As a special exception, you have permission to link this program
with the TtH/TtM library and distribute executables, as long as you
follow the requirements of the GNU GPL in regard to all of the
software in the executable aside from TtH/TtM.
*/
/* =||>|===================== capaUnit.c =====================|<||= */
/* created by Isaac Tsai 1997 */
/* by Isaac Tsai 1997, 1998, 1999 */
/* =||>|========================================================|<||= */
#include <stdio.h> /* fopen() */
#include <stdlib.h>
#include <ctype.h> /* isalnum() */
#include <string.h>
#include <math.h>
#include <float.h>
#include "capaParser.h"
int PrefixTbl[QUARTER_K];
int BaseUnitcnt;
double CScale[BASEUNIT_LIMIT];
double CExp[BASEUNIT_LIMIT];
char CSymb[BASEUNIT_LIMIT][SYMBOL_MAXLEN];
Unit_t *UnitTree_p;
double MinSquared;
Unit_t *MinSquaredUnit_p;
Unit_t *InqueryUnit_p;
double *TmpAexp, *TmpBexp;
Unit_t *EquivUnit[BASEUNIT_LIMIT];
double MinValue[BASEUNIT_LIMIT];
int EquivUnitCnt;
char Sbuf[ONE_K_SIZE];
int Sidx;
Unit_t *Pstack[ONE_K_SIZE];
int Ptopidx;
int gUnitError;
FILE *ufp;
/* ==================================================================== */
void c_ignorewhite(FILE *f) /* ignore white spaces from a file stream */
{
register int c;
register int ok;
ok = 0;
do {
do { c = getc(f);
} while ( isspace(c) );
ungetc(c,f);
if (c == '#') {
while (getc(f) != '\n');
} else ok = 1;
} while( ! ok);
}
int c_getint(FILE *f) /* returns an integer from the file stream */
{
int c;
int value;
c_ignorewhite(f);
c = fgetc(f);
if (!isdigit(c)) {
fprintf(stderr,"Error: Expected digit, got %c\n", c);
exit(-1);
}
ungetc(c,f);
fscanf(f,"%d", &value);
return(value);
}
int c_getsec_range(FILE *f,int *low,int *high)
{
int c;
int tmp, result;
c_ignorewhite(f);
c = fgetc(f);
if( c == '[' ) { /* specify a range of sections */
do { c = getc(f); } while ( isspace(c) );
if (!isdigit(c)) {
fprintf(stderr,"Error in section range format, expecting a number.\n");
result = -1;
return (result);
}
ungetc(c,f);
fscanf(f,"%d", low);
do { c = getc(f); } while ( isspace(c) );
if( c == ',' ) {
do { c = getc(f); } while ( isspace(c) );
if (!isdigit(c)) {
fprintf(stderr,"Error in section range format, expecting a number.\n");
result = -1;
return (result);
}
ungetc(c,f);
fscanf(f,"%d", high);
do { c = getc(f); } while ( isspace(c) );
if( c == ']' ) {
if( *high < *low ) {
tmp= *high; *high = *low; *low =tmp;
}
if(*low <=0) {
*low = 1;
}
if(*high <=0) {
*high =1;
}
/* printf("Section range=>[%d,%d]\n",*low,*high); */
result = 2;
}
} else { /* no , specified */
result = -1;
return (result);
}
} else { /* specify a section only */
if (!isdigit(c)) {
fprintf(stderr,"Error: Expected digit, got %c\n", c);
result = -1;
return (result);
}
ungetc(c,f);
fscanf(f,"%d", low);
result = 1;
}
return (result);
}
double c_getdouble(FILE *f)
{
int c;
double value;
c_ignorewhite(f);
c = fgetc(f);
if (!isdigit(c)) {
fprintf(stderr,"Error: Expected digit, got %c\n", c);
exit(-1);
}
ungetc(c,f);
fscanf(f,"%lf", &value);
return(value);
}
/* read until encountered an unrecognizable char */
/* space, #, anything other than alphanum, {}-^_ */
char *c_getword(FILE *f)
{
register int c;
register int idx;
char tmp_string[ONE_K];
char *new_string;
idx = 0;
c_ignorewhite(f);
do { c = getc(f);
tmp_string[idx] = c;
idx++;
} while (isalnum(c) || c == '{' || c == '}' || c == '-' ||
c == '^' || c == '_' );
ungetc(c,f); idx--;
tmp_string[idx] = 0;
new_string = (char *)malloc( (idx+1)*sizeof(char) );
strncpy(new_string,tmp_string, (idx+1) );
return (new_string);
}
/* read until encountered a newline, # */
char *c_getstring(FILE *f)
{
register int c;
register int idx;
char tmp_string[1024];
char *new_string;
idx = 0;
c_ignorewhite(f);
do { c = getc(f);
tmp_string[idx] = c;
idx++;
} while (isalnum(c) || c == '{' || c == '}' || c == '-' ||
c == '^' || c == ' ' || c == ',' || c == ';' ||
c == '.' || c == '(' || c == ')' || c == '=' ||
c == '+' || c == '*' || c == '/' );
ungetc(c,f); idx--;
tmp_string[idx] = 0;
c = tmp_string[idx-1];
while( c == ' ') { /* get rid of trailing white space */
idx--;
c = tmp_string[idx-1];
}
tmp_string[idx] = 0;
new_string = (char *)malloc( (idx+1)*sizeof(char) );
strncpy(new_string,tmp_string, (idx+1) );
return (new_string);
}
char *c_getcomment(FILE *f)
{
register int c;
register int idx;
char tmp_string[ONE_K];
char *new_string;
idx = 0;
while (getc(f) != '#');
while ((c = getc(f)) == ' '); ungetc(c,f);
do { c = getc(f);
tmp_string[idx] = c;
idx++;
} while ( isprint(c) );
/*
} while (isalnum(c) || c == '{' || c == '}' || c == '-' ||
c == '^' || c == ' ' || c == ',' || c == ';' ||
c == '.' || c == '(' || c == ')' || c == '=' );
*/
ungetc(c,f); idx--;
tmp_string[idx] = 0;
c = tmp_string[idx-1];
while( c == ' ') { /* get rid of trailing white space */
idx--;
c = tmp_string[idx-1];
}
tmp_string[idx] = 0;
new_string = (char *)malloc( (idx+1)*sizeof(char) );
strncpy(new_string,tmp_string, (idx+1) );
return (new_string);
}
void c_moveto_unit(FILE *f)
{
register int c;
register int ok;
ok = 0;
do {
do { c = getc(f);
} while (c != '<' );
c = getc(f);
if (c == '<') {
ungetc(c,f); ungetc(c,f); ok=1;
}
} while( ! ok);
}
int c_gettype(FILE *f)
{
register int c;
register int idx;
char tmp_string[ONE_K];
char new_string[ONE_K];
idx = 0;
PRESTART:
c_ignorewhite(f);
while ((c=getc(f)) != '<') { if ( (char)c==(char)EOF ) return U_UNKNOWN; }
c = getc(f);
if( c == '<' ) {
c_ignorewhite(f);
PREEND:
do { c = getc(f);
tmp_string[idx] = toupper(c);
idx++;
} while ( c != '>' );
c = getc(f);
if( c == '>' ) {
idx--;
tmp_string[idx] = 0;
c = tmp_string[idx-1];
while( c == ' ') { /* get rid of trailing white space */
idx--;
c = tmp_string[idx-1];
}
tmp_string[idx] = 0;
strncpy(new_string,tmp_string, (idx+1) );
} else {
ungetc(c,f);
goto PREEND;
}
} else {
goto PRESTART;
}
if( !strcmp(new_string,"BASE UNIT") ) {
return (U_BASE);
}
if( strcmp(new_string, "DERIVED UNIT") == 0 ) {
return (U_DERIVED);
}
if( strcmp(new_string, "PREFIX") == 0 ) {
return (U_PREFIX);
}
if( strcmp(new_string, "CONSTANTS") == 0 ) {
return (U_CONSTANT);
}
if( strcasecmp(new_string, "DEFAULTS") == 0 ) {
return (U_DEFAULT);
}
return (U_UNKNOWN);
}
/* =================================================================== */
/* =================================================================== */
/* returns: 0 success */
/* 1 the first units string u1_str could not be reduce to a valid unit */
/* 2 the second units string could not be reduced to a valid unit */
int
u_convert_unit(char *u1_str,char *u2_str,double *ratio)
{
Unit_t *ap, *bp;
int result=0;
while( isspace(*u1_str) ) u1_str++;
while( isspace(*u2_str) ) u2_str++;
bp = parse_unit_expr(u2_str);
Ptopidx=0;
postwalk_utree(bp);
if( Ptopidx == 1 ) {
simplify_unit(Pstack[Ptopidx]);
bp = Pstack[Ptopidx];
/* print_unit_t(bp); */
ap = parse_unit_expr(u1_str);
Ptopidx=0;
postwalk_utree(ap);
if( Ptopidx == 1 ) {
simplify_unit(Pstack[Ptopidx]);
/* print_unit_t(Pstack[Ptopidx]); */
if( (Pstack[Ptopidx]->u_count != 0) ||
(Pstack[Ptopidx]->u_count == bp->u_count) ) { /* has unit */
*ratio = units_ratio(Pstack[Ptopidx], bp);
} else {
result = 1;
}
}
free_utree(ap);
} else {
result = 2;
}
free_utree(bp);
return (result);
}
/* =================================================================== */
Unit_t *
u_find_symb (char *name, Unit_t *t, int *result)
{
if (t == NULL) return t;
for (;;) {
if ( comp_unit_symb(name,t->u_symbol) < 0 ) {
if (t->u_left == NULL) {
/* printf("L not found\n"); */
*result = 0;
break;
}
t = t->u_left;
} else if ( comp_unit_symb(name,t->u_symbol) > 0 ) {
if (t->u_right == NULL) {
/* printf("R not found\n"); */
*result = 0;
break;
}
t = t->u_right;
} else {
*result = 1;
break;
}
}
return t;
}
/* ------------------------------------------------------------- */
/* use the input unit_t's element list to locate the min squared */
/* error fit of the unit tree */
/* report either exact fit or approx */
void
u_find_name(Unit_t *t)
{
int ii;
Unit_E *eu_p;
MinSquared = FLT_MAX;
EquivUnitCnt=0;
InqueryUnit_p = t;
/* printf("INQ[[%s,%s,%d]]\n", U_SYMB(t), U_NAME(t),U_COUNT(t)); */
TmpAexp = (double *)capa_malloc(BaseUnitcnt,sizeof(double));
TmpBexp = (double *)capa_malloc(BaseUnitcnt,sizeof(double));
for(ii=0;ii<BaseUnitcnt;ii++) {
TmpAexp[ii] = 0.0;
}
if( t->u_count > 0 ) {
for(eu_p = t->u_list; eu_p; eu_p = eu_p->ue_nextp) {
TmpAexp[eu_p->ue_index] = eu_p->ue_exp;
/* printf("(%d)^(%g) ",eu_p->ue_index,TmpAexp[eu_p->ue_exp]); */
}
/* printf("\n"); */
}
inorder_diff(UnitTree_p);
/*capa_mfree((char *)TmpAexp); capa_mfree((char *)TmpBexp);*/
}
void
print_matches(Unit_t *t)
{
double scale, factor;
Unit_t *tmp_p;
int ii;
scale = t->u_scale;
if( MinSquared == 0.0 ) { /* exact match */
if( EquivUnitCnt > 0 ) {
printf(" Entered unit is equivalent to:\n");
for(ii=0;ii<EquivUnitCnt;ii++) {
tmp_p = EquivUnit[ii];
if( MinSquared == MinValue[ii] ) {
if( tmp_p->u_type == U_BASE ) { /* if there is a base unit */
MinSquaredUnit_p = tmp_p;
}
factor = scale / tmp_p->u_scale;
printf(" <<%g %s>>", factor,U_SYMB(tmp_p));
}
}
printf("\n");
}
} else { /* no exact match */
if( EquivUnitCnt > 0 ) {
printf(" Entered unit is approximated by:\n");
for(ii=0;ii<EquivUnitCnt;ii++) {
tmp_p = EquivUnit[ii];
if( MinSquared == MinValue[ii] ) {
printf(" <<%s>> ", U_SYMB(tmp_p) );
}
}
printf("\n");
}
}
}
/* ------------------------------------ */
double
u_squared_diff(Unit_t *a, Unit_t *b)
{
double result;
double squared_diff = 0.0;
int ii;
Unit_E *eu_p;
for(ii=0;ii<BaseUnitcnt;ii++) {
TmpAexp[ii] = 0.0;
TmpBexp[ii] = 0.0;
}
if( a->u_count > 0 ) {
for(eu_p= a->u_list; eu_p; eu_p = eu_p->ue_nextp) {
TmpAexp[eu_p->ue_index] = eu_p->ue_exp;
}
}
if( b->u_count > 0 ) {
for(eu_p= b->u_list; eu_p; eu_p = eu_p->ue_nextp) {
TmpBexp[eu_p->ue_index] = eu_p->ue_exp;
/* printf("Exp[%d]=%g ",ii,TmpBexp[ii]); */
}
/* printf("\n"); */
}
for(ii=0;ii<BaseUnitcnt;ii++) {
result = TmpAexp[ii] - TmpBexp[ii];
squared_diff = squared_diff + result*result;
}
return (squared_diff);
}
double
u_sq_diff(Unit_t *b)
{
double result;
double squared_diff = 0.0;
int ii;
Unit_E *eu_p;
for(ii=0;ii<BaseUnitcnt;ii++) {
TmpBexp[ii] = 0.0;
}
if( b->u_count > 0 ) {
for(eu_p= b->u_list; eu_p; eu_p = eu_p->ue_nextp) {
TmpBexp[eu_p->ue_index] = eu_p->ue_exp;
/* printf("Exp[%d]=%g ",ii,TmpBexp[ii]); */
}
/* printf("\n"); */
} else if( b->u_type == U_BASE ) {
TmpBexp[b->u_index] = 1.0;
}
for(ii=0;ii<BaseUnitcnt;ii++) {
result = TmpAexp[ii] - TmpBexp[ii];
squared_diff = squared_diff + result*result;
}
return (squared_diff);
}
/* ------------------------------------ */
int
inorder_diff(node_p) Unit_t *node_p;
{
int result;
double sq_diff=0.0;
if( node_p == NULL ) return (1);
result = inorder_diff(U_LEFT(node_p));
if( result ) {
sq_diff = u_sq_diff(node_p);
/*
printf("DIFF [%s,%s,%d] - [%s,%s,%d] = %g\n",
U_SYMB(InqueryUnit_p), U_NAME(InqueryUnit_p),U_COUNT(InqueryUnit_p),
U_SYMB(node_p), U_NAME(node_p),U_COUNT(node_p),sq_diff);
*/
if( MinSquared > sq_diff) {
MinSquaredUnit_p = node_p;
MinSquared = sq_diff;
} else if ( MinSquared == sq_diff) {
EquivUnit[EquivUnitCnt] = node_p;
MinValue[EquivUnitCnt] = sq_diff;
EquivUnitCnt++;
}
}
result = inorder_diff(U_RIGHT(node_p));
return (result);
}
int
alphaorder_utree(node_p) Unit_t *node_p;
{
int result;
if( node_p == NULL ) return (1);
result = alphaorder_utree(U_LEFT(node_p));
if( result ) printf(" (%s,%s)\n", U_SYMB(node_p), U_NAME(node_p) );
result = alphaorder_utree(U_RIGHT(node_p));
return (result);
}
int
w_alphaorder_utree(node_p) Unit_t *node_p;
{
int result;
if( node_p == NULL ) return (1);
result = alphaorder_utree(U_LEFT(node_p));
if( result ) {
printf(" (%s,%s)\n", U_SYMB(node_p), U_NAME(node_p) );
}
result = alphaorder_utree(U_RIGHT(node_p));
return (result);
}
/* --------------------------------------------------------------------- */
void
print_unit_tree(int mode)
{
if( mode == 1 ) {
alphaorder_utree(UnitTree_p);
} else {
w_alphaorder_utree(UnitTree_p);
}
}
int
preorder_utree(node_p) Unit_t *node_p;
{
int result;
if( node_p == NULL ) return (1);
printf("Preorder=[[%s,%s,%d]]\n", U_SYMB(node_p), U_NAME(node_p),U_COUNT(node_p));
result = preorder_utree(U_LEFT(node_p));
if( result ) result = preorder_utree(U_RIGHT(node_p));
return (result);
}
int
inorder_utree(node_p) Unit_t *node_p;
{
int result;
if( node_p == NULL ) return (1);
result = inorder_utree(U_LEFT(node_p));
if( result ) printf("INorder=[[%s,%s,%d]]\n",
U_SYMB(node_p), U_NAME(node_p),U_COUNT(node_p));
result = inorder_utree(U_RIGHT(node_p));
return (result);
}
int
postorder_utree(node_p) Unit_t *node_p;
{
int result;
if( node_p == NULL ) return (1);
result = postorder_utree(U_LEFT(node_p));
if( result ) result = postorder_utree(U_RIGHT(node_p));
if( result ) {
switch(U_TYPE(node_p)) {
case U_DERIVED: print_unit_t(node_p);
break;
case U_CONSTANT: printf("(%g)",U_SCALE(node_p));
break;
case U_OP_POWER: printf("^");
break;
case U_OP_TIMES: printf("*");
break;
case U_OP_PLUS: printf("+");
break;
case U_OP_MINUS: printf("-");
break;
case U_OP_DIVIDE: printf("/");
break;
default: printf("()");
break;
}
}
return (result);
}
/* returns 1 on okay, 2 on error*/
int
postwalk_utree(Unit_t *n_p)
{
int result;
if( n_p == NULL ) return (1);
result = postwalk_utree(U_LEFT(n_p));
if (result !=2) {
if( result ) result = postwalk_utree(U_RIGHT(n_p));
if (result !=2) {
if( result ) {
switch(U_TYPE(n_p)) {
case U_DERIVED: Ptopidx++; Pstack[Ptopidx] = n_p; /* push into stack */
break;
case U_CONSTANT: Ptopidx++; Pstack[Ptopidx] = n_p; /* push into stack */
break;
case U_UNKNOWN: result=2;
/*push into stack anyway, try to parse rest of tree */
break;
case U_OP_POWER: printf("^"); result=2;
break;
case U_OP_TIMES: process_op(U_OP_TIMES); /* process operator */
break;
case U_OP_PLUS: printf("+"); result=2;
break;
case U_OP_MINUS: printf("-"); result=2;
break;
case U_OP_DIVIDE: process_op(U_OP_DIVIDE); /* process operator */
break;
default: printf("()"); result=2;
break;
}
}
}
}
return (result);
}
void
process_op(int op)
{
Unit_t *ap, *bp;
double exp_scale;
int no_error=1;
bp = Pstack[Ptopidx--];
ap = Pstack[Ptopidx--];
switch(op) {
case U_OP_TIMES: exp_scale = 1.0; break;
case U_OP_DIVIDE: exp_scale = -1.0; break;
case U_OP_PLUS:
case U_OP_MINUS: no_error = u_pm_op(ap,bp,op);
if(no_error) {
Ptopidx++;
Pstack[Ptopidx] = ap;
}
break;
default: no_error=0;
printf("No such op on the parse tree!\n");
break;
}
if(no_error) {
u_copy_unit(ap, bp, exp_scale);
Ptopidx++;
Pstack[Ptopidx] = ap;
}
}
Unit_t*
process_utree(Unit_t *t)
{
Ptopidx=0;
postwalk_utree(t);
if( Ptopidx == 1 ) {
//fprintf(stderr,"Correctly parsed!\n");
//fprintf(stderr,"Unit:%s\n",Sbuf);
simplify_unit(Pstack[Ptopidx]);
//Pstack[Ptopidx]->u_symbol[0]='\0';
//fprintf(stderr,Pstack[Ptopidx]->u_symbol,"");
//print_unit_t(Pstack[Ptopidx]);
//u_find_name(Pstack[Ptopidx]);
//print_matches(Pstack[Ptopidx]);
return(Pstack[Ptopidx]);
//free_utree(t);
}
return(t);
}
/* ============================================================== */
/* called from capaCommon.c */
/* */
/* UNIT_FAIL */
/* NO_UNIT */
/* result: UNIT_OK correct */
/* */
/* -------------------------------------------------------------- */
int check_correct_unit(char *u_symb,Unit_t *t,double *scale)
{
Unit_t *ap;
int result=UNIT_OK;
#ifdef UNIT_DBUG
if ((ufp=fopen("unit.DBUG","a"))==NULL) { fprintf(stderr,"Error: can't open login debug\n"); return UNIT_FAIL; }
#endif
while( isspace(*u_symb) ) u_symb++;
/* <= change this to search from the end of string */
/* or to get rid of all the white spaces */
ap = parse_unit_expr(u_symb);
Ptopidx=0;
if (postwalk_utree(ap)==1) {
#ifdef UNIT_DBUG
fprintf(ufp,"Ptopidx %d\n",Ptopidx);
#endif
if( Ptopidx == 1 ) {
simplify_unit(Pstack[Ptopidx]);
if( (Pstack[Ptopidx]->u_count != 0) ||
(Pstack[Ptopidx]->u_count == t->u_count) ) { /* has unit */
*scale = units_ratio(Pstack[Ptopidx], t);
if( *scale == 0.0 ) {
result = UNIT_IRRECONCIBLE;
}
free_utree(ap);
} else {
result = UNIT_INVALID_STUDENT3;
}
} else { /* invalid unit representation */
result = UNIT_INVALID_STUDENT2;
}
} else {
result = UNIT_INVALID_STUDENT1;
}
#ifdef UNIT_DBUG
fclose(ufp);
#endif
return (result);
}
/* ============================================================= */
int
free_units()
{
free_utree(UnitTree_p);
UnitTree_p=NULL;
return 0;
}
int
free_utree(Unit_t *t)
{
int result=1;
if( t == NULL ) return (1);
u_postfree(t);
t=NULL;
return (result);
}
int
u_postfree(Unit_t *t)
{
int result;
if( t == NULL ) return (1);
result = u_postfree(U_LEFT(t));
if( result ) result = u_postfree(U_RIGHT(t));
if( result ) {
if( t->u_comment ) {
capa_mfree((char *)t->u_comment);
}
freelist_unit_e(t->u_list);
capa_mfree((char *)t);
}
return (result);
}
void
print_unit_t(Unit_t *t)
{
Unit_E *ue_p;
/* printf(" Unit::[%s,%d]= %g * ", t->u_symbol,t->u_count,t->u_scale); */
printf(" Unit::[%s] = %g * ", t->u_symbol, t->u_scale);
for(ue_p=t->u_list; ue_p ; ue_p = ue_p->ue_nextp) {
/*
printf("<%s,%d,%g,%g> ",ue_p->ue_symbol,ue_p->ue_index,ue_p->ue_scale,ue_p->ue_exp);
*/
printf("(%g*%s^%g) ",ue_p->ue_scale,ue_p->ue_symbol,ue_p->ue_exp);
}
printf("\n");
}
/* ----------------------------------------------------------- */
/* copy the Unit_E linked list from b_p->u_list to a_p->u_list */
/* create some Unit_E nodes in a_p->u_list if needed and */
/* leave b_p->u_list intact */
/* a_p->u_scale is multiplied by pow(b_p->u_scale,exp_scale) */
/* ----------------------------------------------------------- */
void
u_copy_unit(Unit_t *a_p, Unit_t *b_p, double exp_scale)
{
Unit_E *oe_p, *ne_p, *last_p;
int ii;
double scale;
if( a_p->u_count > 0 ) {
for(last_p = a_p->u_list; last_p->ue_nextp; last_p = last_p->ue_nextp) { }
} else {
a_p->u_list = last_p = NULL;
}
if( b_p->u_count > 0 ) {
oe_p = b_p->u_list;
for(ii=0;ii<b_p->u_count;ii++) {
ne_p = (Unit_E *) capa_malloc(1, sizeof(Unit_E)); /* *** */
ne_p->ue_scale = oe_p->ue_scale;
ne_p->ue_exp = oe_p->ue_exp * exp_scale;
ne_p->ue_index = oe_p->ue_index;
strcpy(ne_p->ue_symbol, oe_p->ue_symbol);
oe_p = oe_p->ue_nextp;
if( last_p == NULL ) {
a_p->u_list = ne_p;
} else {
last_p->ue_nextp = ne_p;
}
last_p = ne_p;
a_p->u_count++;
}
scale = pow(b_p->u_scale, exp_scale);
a_p->u_scale = a_p->u_scale * scale;
/* printf("Found scale=%g=%g\n",a_p->u_scale,b_p->u_scale); */
} else {
if( b_p->u_type == U_BASE ) {
/* *b_p is a base unit, so create a one element unit */
ne_p = (Unit_E *) capa_malloc(1, sizeof(Unit_E)); /* *** */
ne_p->ue_scale = b_p->u_scale;
ne_p->ue_exp = exp_scale;
ne_p->ue_index = b_p->u_index;
strcpy(ne_p->ue_symbol, b_p->u_symbol);
if( last_p == NULL ) {
a_p->u_list = ne_p;
} else {
last_p->ue_nextp = ne_p;
}
last_p = ne_p;
a_p->u_count++;
} else if( b_p->u_type == U_DERIVED) {
/* derived units but without any units elements (scalar) */
/*a_p->u_count++;*/
scale = pow(b_p->u_scale, exp_scale);
a_p->u_scale = a_p->u_scale * scale;
} else if( b_p->u_type == U_CONSTANT ) {
scale = pow(b_p->u_scale, exp_scale);
a_p->u_scale = a_p->u_scale * scale;
} else {
printf("This node has no u_e list and Type unknown\n");
}
}
}
int
u_pm_op(Unit_t *a_p, Unit_t *b_p, int op)
{
int result=0;
if( a_p->u_count > 0 || b_p->u_count > 0 ) {
printf(" cannot add or sub units at this moment\n");
return result;
}
if( op == U_OP_PLUS ) {
a_p->u_scale = a_p->u_scale + b_p->u_scale;
} else {
a_p->u_scale = a_p->u_scale - b_p->u_scale;
}
return 1;
}
int
u_parsepower(char *unit_str)
{
int exp, ii;
char *ch_p, exp_str[16];
ch_p = unit_str;
while( isspace(*ch_p) ) { ch_p++; }
ii=0;
while( isdigit(*ch_p) ) {
ch_p++;
}
while( isspace(*ch_p) ) { ch_p++; }
if( *ch_p == '^' ) {
ch_p++;
}
while( isspace(*ch_p) ) { ch_p++; }
if( *ch_p == '{' ) {
ch_p++;
}
while( isspace(*ch_p) ) { ch_p++; }
ii=0;
while( isdigit(*ch_p) || *ch_p == '-' || *ch_p == '+' ) {
exp_str[ii++] = *ch_p;
ch_p++;
}
exp_str[ii]=0;
sscanf(exp_str,"%d", &exp);
return (exp);
}
/* ------------------------------------------- */
/* scan a number of the form indicated below from the input buffer */
/* 1.234^{2.3} */
/* 1e */
double
s_scan_number(char *buf, int idx, int *r_idx)
{
double num;
float exp;
double result;
int ii=0;
char num_str[QUARTER_K];
num_str[ii]=0;
if( buf[idx] == '-' ) {
num_str[ii++] = '-';
idx++;
}
while( isdigit(buf[idx]) || buf[idx] == '.' ) {
num_str[ii++] = buf[idx];
idx++;
}
if( buf[idx] == 'E' || buf[idx] == 'e' ) {
if( buf[idx+1] == '-' || isdigit(buf[idx+1]) ) {
num_str[ii++] = buf[idx++];
num_str[ii++] = buf[idx++];
while( isdigit(buf[idx]) ) {
num_str[ii++] = buf[idx];
idx++;
}
}
}
num_str[ii] = 0; /* terminate the str */
sscanf(num_str,"%lg", &num);
/* printf("Scan number %s got %g\n",num_str, num); fflush(stdout); */
result = num;
if( buf[idx] == '^' ) {
idx++;
while( isspace(buf[idx]) ) { idx++; }
if( buf[idx] == '{' ) { /* need to scan for a matching right bracket */
idx++;
}
while( isspace(buf[idx]) ) { idx++; }
num_str[0]=0;
if( isdigit(buf[idx]) || buf[idx] == '+' || buf[idx] == '-' ) {
ii=0;
while( isdigit(buf[idx]) || buf[idx] == '.' || buf[idx] == '+' || buf[idx] == '-' ) {
num_str[ii++] = buf[idx];
idx++;
}
num_str[ii]=0;
}
while( isspace(buf[idx]) ) { idx++; }
if( buf[idx] == '}' ) {
idx++;
}
sscanf(num_str,"%f", &exp);
/* printf("Scan exp number %s got %g\n",num_str, exp); fflush(stdout); */
result = pow(num, (double)exp);
/* printf("{%d^%d}=%g\n",num, exp,result); */
}
*r_idx = idx;
return (result);
}
double
s_scan_symbol(char *buf,char *symb_p,int idx, int *r_idx)
{
char num_str[QUARTER_K];
int ii=0;
double r_exp=1.0;
symb_p[0]=0;
while( isalnum(buf[idx]) || buf[idx] == '_' ) {
symb_p[ii++] = buf[idx];
idx++;
}
symb_p[ii]=0;
if( buf[idx] == '^' ) { /* look for either left bracket or a number */
idx++;
while( isspace(buf[idx]) ) { idx++; }
if( buf[idx] == '{' ) { /* need to scan for a matching right bracket */
idx++;
}
while( isspace(buf[idx]) ) { idx++; }
if( isdigit(buf[idx]) || buf[idx] == '.' || buf[idx] == '+' || buf[idx] == '-' ) {
ii=0; num_str[ii] = 0;
while( isdigit(buf[idx]) || buf[idx] == '.' || buf[idx] == '+' || buf[idx] == '-' ) {
num_str[ii++] = buf[idx];
idx++;
}
num_str[ii]=0;
}
while( isspace(buf[idx]) ) { idx++; }
if( buf[idx] == '}' ) {
idx++;
}
sscanf(num_str,"%lg", &r_exp); /* power could be of type float */
/* printf("[scan symb with power %s ^ %lg] ",symb_p, r_exp); fflush(stdout); */
}
*r_idx = idx;
return (r_exp);
}
/* return: err_code 0 parsed ok */
/* 1 symbol is of length 1, not found in the tree */
/* 2 symbol not found in the tree */
/* 3 symbol parsed as prefix symb, but symb not found */
/* 4 symbol length is 0 or negative */
int
s_process_symb(char *symb_str,Unit_t *cu_p,double exp)
{
int len;
Unit_t *au_p;
int c_result;
int ii;
char tmp_str[ANSWER_STRING_LENG];
int err_code = 0;
double d_exp;
len = strlen(symb_str);
if( len > 0 ) {
au_p = u_find_symb(symb_str, UnitTree_p, &c_result);
if( c_result == 1 ) { /* if found, copy the definition over */
u_copy_unit(cu_p, au_p, exp);
} else {
if( len > 1 ) {
if( PrefixTbl[ (int)symb_str[0] ] != 0 ) { /* prefix is defined */
for(ii=1;ii<len;ii++) {
tmp_str[ii-1] = symb_str[ii];
}
tmp_str[len-1]=0;
au_p = u_find_symb(tmp_str, UnitTree_p, &c_result);
if( c_result == 1 ) {
/* printf("[%s] ", tmp_str); */
u_copy_unit(cu_p, au_p, exp);
d_exp = (double)PrefixTbl[ (int)symb_str[0] ] * exp;
cu_p->u_scale = cu_p->u_scale * pow((double)10.0,d_exp);
} else { /* unit *tmp_str not found */
/*printf("The unit: %s, not defined\n",tmp_str);*/
err_code = 3;
}
} else {
/*printf("<<%s>>", symb_str);*/
err_code = 2;
}
} else {/* len == 1 */
/*printf("The unit: %s, not defined\n",symb_str);*/
err_code = 1;
}
}
} else {
err_code = 4;
}
return (err_code);
}
Unit_t *
u_parse_unit(char *unit_str)
{
char *ch;
char symb_str[QUARTER_K];
int idx;
double exp_sign;
int s_result;
int not_done;
double s_number, offset;
double tmp_scale, symb_exp, exp;
Unit_t *cu_p;
gUnitError=0;
ch = unit_str;
cu_p = (Unit_t *) capa_malloc(1, sizeof(Unit_t)); /* *** */
cu_p->u_scale = 1.0;
idx = 0; not_done = 1;
exp_sign = 1.0; exp = 1;
symb_str[0] = 0;
while( isspace(*ch) ) { ch++; } /* trim leading white spaces */
/* fprintf(stdout,"PARSE |%s|\n", unit_str); */
while( not_done ) {
if( isdigit(ch[idx]) || ch[idx] == '-' ) { /* rule 1: number */
s_number = s_scan_number(ch,idx,&idx);
tmp_scale = pow(s_number,exp_sign);
/* printf("S=%g,Power(%g,%d)=%g\n",
cu_p->u_scale, s_number,exp_sign, tmp_scale);
*/
cu_p->u_scale = cu_p->u_scale * tmp_scale;
/* printf("[Scale %g=%g^%g] ",tmp_scale,s_number,exp_sign); */
while( isspace(ch[idx]) ) { idx++; }
} else {
if( isalpha(ch[idx]) ) { /* rule 2: unit_symbol ^ exp */
symb_str[0] = 0;
symb_exp = s_scan_symbol(ch,symb_str,idx,&idx);
exp = (double)exp_sign * symb_exp;
/* printf("[scanned %s ^ (%g * %g)] ", symb_str,symb_exp,exp_sign); fflush(stdout); */
s_result = s_process_symb(symb_str,cu_p,exp);
if( s_result > 0 ) {
/* printf("Error processing symbol [%s]\n", symb_str); */
gUnitError = 1;
}
while( isspace(ch[idx]) ) { idx++; }
} else {
if( ch[idx] == '*' || ch[idx] == '/' ) {
if( ch[idx] == '/' ) { /* printf("[/] "); */ exp_sign = -1.0; }
idx++;
while( isspace(ch[idx]) ) { idx++; }
} else {
if( ch[idx] == '+' || ch[idx] == '-' ) {
idx++;
while( isspace(ch[idx]) ) { idx++; }
offset = s_scan_number(ch,idx,&idx);
/* printf("[Offset %g] ",offset); */
} else {
if( ch[idx] == 0 ) { /* end of input string */
not_done = 0;
/* printf("\n"); */
} else {
/* garbage in unit string */
gUnitError = 1;
not_done=0;
}
}
}
}
}
}
simplify_unit(cu_p);
return (cu_p);
}
void
u_getunit(FILE *f)
{
register int unit_type;
register int c;
int power, result;
char *name_p, *symbol_p, *comment_p, *unit_p;
BaseUnitcnt = 0;
free_utree(UnitTree_p);
UnitTree_p = NULL;
c_moveto_unit(f); /* move the file position to << */
do {
c_ignorewhite(f);
c = getc(f); ungetc(c,f);
if( c == '<' ) {
unit_type = c_gettype(f);
}
if( c != EOF ) {
switch(unit_type) {
case U_BASE:
name_p = c_getword(f); symbol_p = c_getword(f);
comment_p = c_getcomment(f);
/*
printf("B Unit: N=%s,S=%s,C=%s\n",name_p,symbol_p,comment_p);
*/
result = u_insert_baseunit(name_p,symbol_p,comment_p);
if( result == 1 ) {
printf("The entry %s is duplicated\n",symbol_p);
}
free(name_p); free(symbol_p); free(comment_p);
break;
case U_DERIVED:
name_p = c_getword(f); symbol_p = c_getword(f);
unit_p = c_getstring(f); comment_p = c_getcomment(f);
/*
printf("D Unit: N=%s,S=%s,C=%s,U=%s\n",
name_p,symbol_p,comment_p,unit_p);
*/
result = u_insert_derived(name_p,symbol_p,comment_p,unit_p);
if( result == 1 ) {
printf("The entry %s is duplicated\n",symbol_p);
}
/* preorder_utree(UnitTree_p); */
free(name_p); free(symbol_p); free(comment_p); free(unit_p);
break;
case U_PREFIX:
name_p = c_getword(f); symbol_p = c_getword(f);
unit_p = c_getstring(f);
/*
printf("Prefix: N=%s,S=%s,U=%s\n",
name_p,symbol_p,unit_p);
*/
power = u_parsepower(unit_p);
PrefixTbl[ (int)(*symbol_p) ] = power;
/* printf(" P[%c]=%d\n",*symbol_p,power); */
free(name_p); free(symbol_p); free(unit_p);
break;
case U_CONSTANT:
symbol_p = c_getword(f); unit_p = c_getstring(f);
comment_p = c_getcomment(f);
/*
printf("Const.: S=%s,C=%s,U=%s\n",
symbol_p,comment_p,unit_p);
*/
break;
case U_UNKNOWN:
/* printf("Unknown\n"); */
break;
}
}
} while ( c != EOF );
}
/* ----------------------------------------------------------------- */
/* comparing unit symbol names should be case sensitive */
int
comp_unit_symb(a, b) char *a; char *b;
{
return strncmp(a,b,SYMBOL_MAXLEN);
}
Unit_t *
u_splay (char *name, Unit_t *t)
{
Unit_t N;
Unit_t *l, *r, *y;
if (t == NULL) return t;
N.u_left = (Unit_t *)NULL;
N.u_right = (Unit_t *)NULL;
l = r = &N;
for (;;) {
if ( comp_unit_symb(name,t->u_symbol) < 0 ) {
if (t->u_left == NULL) break;
if ( comp_unit_symb(name, (t->u_left)->u_symbol ) < 0 ) {
y = t->u_left; t->u_left = y->u_right; y->u_right = t; t = y;
if (t->u_left == NULL) break;
}
r->u_left = t; r = t; t = t->u_left;
} else if ( comp_unit_symb(name,t->u_symbol) > 0 ) {
if (t->u_right == NULL) break;
if ( comp_unit_symb(name, (t->u_right)->u_symbol ) > 0 ) {
y = t->u_right; t->u_right = y->u_left; y->u_left = t; t = y;
if (t->u_right == NULL) break;
}
l->u_right = t; l = t; t = t->u_right;
} else {
break;
}
}
l->u_right = t->u_left; r->u_left = t->u_right; t->u_left = N.u_right;
t->u_right = N.u_left;
return t;
}
/* returns: 0 correctly inserted */
/* -1 error */
/* 1 duplicate entry */
int
u_insert_baseunit(n_p,s_p,c_p) char *n_p, *s_p, *c_p;
{
Unit_t *new_p, *t;
int len;
new_p = (Unit_t *) capa_malloc(1, sizeof(Unit_t)); /* *** */
if (new_p == NULL) {
printf("Ran out of space\n");
return(-1);
}
strcpy(new_p->u_symbol, s_p);
strcpy(new_p->u_name, n_p);
len = strlen(c_p);
new_p->u_comment = (char *) capa_malloc((len+1), sizeof(char)); /* *** */
strcpy(new_p->u_comment,c_p);
BaseUnitcnt++;
new_p->u_index = BaseUnitcnt;
new_p->u_type = U_BASE;
new_p->u_scale = 1.0;
new_p->u_offset = 0.0;
new_p->u_count = 0;
new_p->u_list = NULL;
if (UnitTree_p == NULL) { /* a new unit tree */
UnitTree_p = new_p;
return (0);
}
t = u_splay(s_p, UnitTree_p);
if ( comp_unit_symb(s_p,t->u_symbol) < 0 ) {
new_p->u_left = t->u_left; new_p->u_right = t;
t->u_left = NULL;
/* Splay_cnt++; */
UnitTree_p = new_p;
return (0);
} else if ( comp_unit_symb(s_p,t->u_symbol) > 0 ) {
new_p->u_right = t->u_right; new_p->u_left = t;
t->u_right = NULL;
/* Splay_cnt++; */
UnitTree_p = new_p;
return (0);
} else { /* name and t->u_symbol is the same, which means found it */
capa_mfree( (char *)new_p );
UnitTree_p = t;
return (1);
}
}
int
u_insert_derived(n_p,s_p,c_p,u_p)char *n_p, *s_p, *c_p, *u_p;
{
Unit_t *new_p, *t;
int c_result, len;
/* inorder_utree(UnitTree_p); */
t = u_splay(s_p, UnitTree_p);
UnitTree_p = t;
c_result = comp_unit_symb(s_p,t->u_symbol);
if ( c_result == 0 ) {
UnitTree_p = t;
return (1);
}
/* prepare a new Unit_t */
new_p = u_parse_unit(u_p);
strcpy(new_p->u_symbol,s_p);
strcpy(new_p->u_name, n_p);
new_p->u_type = U_DERIVED;
len = strlen(c_p);
new_p->u_comment = (char *) capa_malloc((len+1), sizeof(char)); /* *** */
strcpy(new_p->u_comment,c_p);
simplify_unit(new_p);
#ifdef UNIT_DBUG
printf("Derived Unit:%s\n",new_p->u_name);
print_unit_t(new_p);
#endif
if (c_result < 0 ) {
new_p->u_left = t->u_left; new_p->u_right = t;
t->u_left = NULL;
} else { /* c_result > 0 */
new_p->u_right = t->u_right; new_p->u_left = t;
t->u_right = NULL;
}
UnitTree_p = new_p;
return (0);
}
void
freelist_unit_e(Unit_E *ue_p)
{
Unit_E *curr_p, *next_p;
if( ue_p != NULL ) {
next_p = ue_p->ue_nextp;
curr_p = ue_p;
if( next_p == NULL ) {
capa_mfree((char *)curr_p);
} else {
for( curr_p = ue_p; next_p; curr_p = next_p, next_p = next_p->ue_nextp) {
capa_mfree((char *)curr_p);
}
capa_mfree((char *)curr_p);
}
}
}
void
simplify_unit(u_p) Unit_t *u_p;
{
Unit_E *eu_p, *prev_p;
int ii, idx;
/* walk through u_list and replace those u_index = -1 with */
/* a linked list of basic unit. */
/* u_msort_main() the whole u_list */
/* combine those units with same u_index */
for(ii=0;ii<BaseUnitcnt;ii++) {
CScale[ii] = 0.0;
CExp[ii] = 0.0;
}
/*
printf("Before Simplify:: \n");
print_unit_t(u_p);
*/
if( u_p->u_count > 0 ) {
for(eu_p=u_p->u_list; eu_p; eu_p = eu_p->ue_nextp) {
idx = eu_p->ue_index;
if( CScale[idx] == 0.0 ) {
CScale[idx] = 1.0;
strcpy(CSymb[idx],eu_p->ue_symbol);
}
CScale[idx] = CScale[idx] * eu_p->ue_scale;
CExp[idx] = CExp[idx] + eu_p->ue_exp;
}
/* debugging
for(ii=0;ii<BaseUnitcnt;ii++) {
if( CScale[ii] != 0.0 ) {
printf("(%d)%s,S=%g,E=%g\n",ii,CSymb[ii],CScale[ii], CExp[ii]);
}
if( CExp[ii] == 0.0 ) {
printf("(%d)%s,S=%g,Exp=%g\n",ii,CSymb[ii],CScale[ii], CExp[ii]);
}
}
*/
freelist_unit_e(u_p->u_list);
prev_p = u_p->u_list = NULL;
u_p->u_count = 0;
for(ii=0;ii<BaseUnitcnt;ii++) {
if( CScale[ii] != 0.0 && CExp[ii] != 0) {
eu_p = (Unit_E *)capa_malloc(1,sizeof(Unit_E)); /* ***************** */
eu_p->ue_scale = 1.0;
eu_p->ue_exp = CExp[ii];
eu_p->ue_index = ii;
strcpy(eu_p->ue_symbol,CSymb[ii]);
if( prev_p == NULL) {
u_p->u_list = prev_p = eu_p;
} else {
prev_p->ue_nextp = eu_p;
prev_p = eu_p;
}
u_p->u_count++;
}
}
}
/*
printf("After Simplify:: \n");
print_unit_t(u_p);
*/
}
/* before comparing two units, make sure they are of basic form */
/* compares if two units are equal */
/* equality returns 1 */
int is_units_equal(Unit_t *u1_p, Unit_t *u2_p)
{
int result=1;
Unit_E *a_p, *b_p;
if( (u1_p->u_count == u2_p->u_count) &&
(u1_p->u_scale == u2_p->u_scale) ) {
for(a_p=u1_p->u_list, b_p=u2_p->u_list;
a_p; a_p=a_p->ue_nextp, b_p=b_p->ue_nextp) {
if(a_p->ue_index != b_p->ue_index ||
a_p->ue_scale != b_p->ue_scale ||
a_p->ue_exp != b_p->ue_exp ) {
result=0;
break;
}
}
} else {
result=0;
}
return (result);
}
/* input : both are the simplest units */
/* result: 0.0 means they are not of euquvalent units */
/* the ratio of u1 / u2 */
double units_ratio(Unit_t *u1_p, Unit_t *u2_p)
{
double ratio=1.0;
Unit_E *a_p, *b_p;
if( (u1_p->u_count == u2_p->u_count) ) {
for(a_p=u1_p->u_list, b_p=u2_p->u_list;
a_p; a_p=a_p->ue_nextp, b_p=b_p->ue_nextp) {
if(a_p->ue_index != b_p->ue_index ||
a_p->ue_scale != b_p->ue_scale ||
a_p->ue_exp != b_p->ue_exp ) {
ratio=0.0;
break;
}
}
} else {
ratio=0.0;
}
if( (ratio != 0.0) && (u2_p->u_scale != 0.0 ) ) {
ratio = u1_p->u_scale / u2_p->u_scale;
}
return (ratio);
}
/* ------------- The Grammar of Units Parser --------------------
scan_unit_expr() --> scan_basic_block()
--> scan_basic_block() '+' scan_basic_block()
--> scan_basic_block() '-' scan_basic_block()
scan_num_expr() --> scan_num_block()
--> scan_num_block() '+' scan_num_block()
--> scan_num_block() '-' scan_num_block()
scan_basic_block()--> scan_basic_term()
--> scan_basic_term() '*' scan_basic_term()
--> scan_basic_term() ' ' scan_basic_term()
--> scan_basic_term() '/' scan_basic_term()
scan_num_block() --> scan_num_term()
--> scan_num_term() '*' scan_num_term()
--> scan_num_term() ' ' scan_num_term()
--> scan_num_term() '/' scan_num_term()
scan_basic_term() --> scan_unit_item()
--> scan_num_item()
--> '(' scan_basic_block() ')'
--> '{' scan_basic_block() '}'
scan_num_term() --> scan_num_item()<sp>*
--> '-' scan_num_item()<sp>*
--> '(' scan_num_expr() ')'
--> '{' scan_num_expr() '}'
scan_unit_item() --> UNIT<sp>*
--> UNIT<sp>* '^' <sp>* scan_num_term()
scan_num_item() --> FLOAT<sp>*
--> FLOAT<sp>* '^' <sp>* scan_num_term()
scan_FLOAT() --> [0-9]+([eE][+-]?[0-9]+)*
p_new_unit() --> [a-Z]+[a-Z0-9_]*
-----------------------------------------
U.expr := B.block
| B.block '+' B.block
| B.block '-' B.block
N.expr := N.block
| N.block '+' N.block
| N.block '-' N.block
To allow for operations like (J/N)^2 or {N/m}^2 (N/J)^3
B.block := B.term
| B.term ' ' B.term
| B.term '*' B.term
| B.term '/' B.term
N.block := N.term
| N.term ' ' N.term
| N.term '*' N.term
| N.term '/' N.term
B.term := U.item
| N.item
| '(' B.block ')'
| '{' B.block '}'
| '(' B.block ')' ^ N.term
| '{' B.block '}' ^ N.term
N.term := N.item
| '-' N.item
| '(' N.expr ')'
| '{' N.expr '}'
U.item := UNIT
| UNIT '^' N.term
N.item := FLOAT
| FLOAT '^' N.term
UNIT := [a-Z]+[a-Z0-9_]*
FLOAT := [0-9]+([eE][+-]?[0-9]+)*
------------------------------------------------------------------- */
Unit_t *
p_new_op(Unit_t *left_p, int op, Unit_t *right_p)
{
Unit_t *new_p;
new_p = (Unit_t *) capa_malloc(1, sizeof(Unit_t));
if (new_p == NULL) {
printf("Ran out of space\n");
return(NULL);
}
new_p->u_left = left_p;
new_p->u_right = right_p;
new_p->u_scale = 0.0;
new_p->u_type = op;
new_p->u_offset = 0.0;
new_p->u_count = 0;
new_p->u_list = NULL;
return (new_p);
}
Unit_t *
p_new_num(Unit_t *left_p, double num, Unit_t *right_p)
{
Unit_t *new_p;
new_p = (Unit_t *) capa_malloc(1, sizeof(Unit_t));
if (new_p == NULL) {
printf("Ran out of space\n");
return(NULL);
}
new_p->u_left = left_p;
new_p->u_right = right_p;
new_p->u_scale = num;
new_p->u_type = U_CONSTANT;
new_p->u_offset = 0.0;
new_p->u_count = 0;
new_p->u_list = NULL;
return (new_p);
}
Unit_t *
p_new_unit(Unit_t *left_p, Unit_t *right_p)
{
char symb_str[ANSWER_STRING_LENG];
int ii=0;
int len;
Unit_t *au_p, *cu_p;
int c_result;
char tmp_str[ANSWER_STRING_LENG];
int err_code = 0;
double d_exp;
symb_str[ii]=0;
while( isspace(Sbuf[Sidx]) ) { Sidx++; }
while( isalnum(Sbuf[Sidx]) || Sbuf[Sidx] == '_' ) {
symb_str[ii++] = Sbuf[Sidx];
Sidx++;
}
symb_str[ii]=0;
/* printf("<U %s>", symb_str); */
cu_p = (Unit_t *) capa_malloc(1, sizeof(Unit_t));
strcpy(cu_p->u_symbol,symb_str);
cu_p->u_left = left_p;
cu_p->u_right = right_p;
cu_p->u_scale = 1.0;
cu_p->u_type = U_DERIVED;
cu_p->u_offset = 0.0;
cu_p->u_count = 0;
cu_p->u_list = NULL;
len = strlen(symb_str);
if( len > 0 ) {
au_p = u_find_symb(symb_str, UnitTree_p, &c_result);
if( c_result == 1 ) { /* if found, copy the definition over */
u_copy_unit(cu_p, au_p, 1);
} else {
if( len > 1 ) {
if( PrefixTbl[ (int)symb_str[0] ] != 0 ) { /* prefix is defined */
for(ii=1;ii<len;ii++) {
tmp_str[ii-1] = symb_str[ii];
}
tmp_str[len-1]=0;
au_p = u_find_symb(tmp_str, UnitTree_p, &c_result);
if( c_result == 1 ) {
/* printf("[%s] ", tmp_str); */
u_copy_unit(cu_p, au_p, 1);
d_exp = (double)PrefixTbl[ (int)symb_str[0] ];
cu_p->u_scale = cu_p->u_scale * pow((double)10.0,d_exp);
} else { /* unit *tmp_str not found */
/* printf(" not found\n"); */
err_code = 3;
cu_p->u_type = U_UNKNOWN;
}
} else { /* symb_str is not in <prefix><units> form */
/* printf("<<%s>>", symb_str); */
err_code = 2;
cu_p->u_type = U_UNKNOWN;
}
} else {/* len == 1 */
/* printf(" not found in symbol tree \n"); */
err_code = 1;
cu_p->u_type = U_UNKNOWN;
}
}
} else { /* why would we have a length less than zero symb_str ? */
err_code = 4;
}
return (cu_p);
}
int s_peeknext_op()
{
char *ch;
int sp=0;
ch = (char *)&Sbuf[Sidx];
while( isspace(*ch) ) { ch++; sp=1; }
if( (*ch == '*') || (*ch == '/') || (*ch == '+') || (*ch == '-') || (*ch == '^')) {
return (*ch);
}
/* what if space is the last thing on the line?*/
if( sp && (*ch != '\0')) return '*';
return (*ch);
}
int s_getnext_op()
{
char *ch;
int inc = 0, sp=0;
/* printf("\n((op"); print_remains(); printf("\n"); */
ch = (char *)&Sbuf[Sidx];
while( isspace(*ch) ) { ch++; inc++; sp=1; }
Sidx = Sidx + inc;
if( (*ch == '*') || (*ch == '/') || (*ch == '+') || (*ch == '-') || (*ch == '^') ) {
Sidx++;
/* print_remains(); printf(" op))"); printf("\n"); */
return (*ch);
}
/* print_remains(); printf(" op))"); printf("\n"); */
/* what if space is the last thing on the line?*/
if( sp && (*ch != '\0')) return '*';
return (*ch);
}
int
s_getnext()
{
char ch;
ch = Sbuf[Sidx];
Sidx++;
return (ch);
}
int
s_peeknext()
{
char ch;
ch = Sbuf[Sidx];
return (ch);
}
int
s_peeknextNW() /* peek into the next non-whitespaces character */
{
char *ch;
ch = (char *)&Sbuf[Sidx];
while( isspace(*ch) ) { ch++; }
return (*ch);
}
int
s_getnextNW() /* get the next non-whitespaces character */
{
char *ch;
ch = (char *)&Sbuf[Sidx]; Sidx++;
while( isspace(*ch) ) { ch++; Sidx++; }
return (*ch);
}
/* peek into the next non-whitespaces character
which should be either a multiply or division */
int
s_peekMDWS()
{
char *ch;
int sp=0;
ch = (char *)&Sbuf[Sidx];
while( isspace(*ch) ) { ch++; sp=1;}
if( (*ch == '*') || (*ch == '/') ) {
return (*ch);
}
if( sp ) return ' ';
ch = (char *)&Sbuf[Sidx];
while( isspace(*ch) ) { ch++; }
return (*ch);
}
int
s_getnextMDWS()
{
char *ch;
int inc=0, sp=0;
ch = (char *)&Sbuf[Sidx]; Sidx++;
while( isspace(*ch) ) { ch++; inc++; sp=1; }
Sidx += inc;
if( (*ch == '*') || (*ch == '/') ) {
return (*ch);
}
if( sp ) return ' ';
return (*ch);
}
double
scan_FLOAT()
{
double num;
int ii=0, len;
char num_str[QUARTER_K];
num_str[ii]=0;
while( isspace(Sbuf[Sidx]) ) { Sidx++; }
if( Sbuf[Sidx] == '-' ) {
num_str[ii++] = Sbuf[Sidx++];
}
while( isdigit(Sbuf[Sidx]) || Sbuf[Sidx] == '.' ) {
num_str[ii++] = Sbuf[Sidx++];
}
if( Sbuf[Sidx] == 'E' || Sbuf[Sidx] == 'e' ) {
if( Sbuf[Sidx+1] == '-' || isdigit(Sbuf[Sidx+1]) ) {
num_str[ii++] = Sbuf[Sidx++];
num_str[ii++] = Sbuf[Sidx++];
while( isdigit(Sbuf[Sidx]) ) {
num_str[ii++] = Sbuf[Sidx++];
}
}
}
num_str[ii] = 0; /* terminate the str */
len = strlen(num_str);
if(len > 0 ) {
sscanf(num_str,"%lg", &num);
/* printf("<N %s %g>",num_str,num); fflush(stdout); print_remains(); */
} else {
num = 1.0;
}
return (num);
}
/* -----------------------------------------------
N.item := FLOAT
| FLOAT '^' N.term
----------------------------------------------- */
Unit_t *
scan_num_item()
{
Unit_t *node_p, *exp_p;
double num_const;
char ch;
num_const = scan_FLOAT();
node_p = p_new_num(NULL, num_const, NULL);
ch = s_peeknext_op();
if( ch == '^' ) {
ch = s_getnext_op();
exp_p = scan_num_term();
num_const = node_p->u_scale;
if( node_p->u_scale > 0.0 ) {
num_const = pow(node_p->u_scale,exp_p->u_scale);
}
node_p->u_scale = num_const;
capa_mfree((char *)exp_p);
}
return node_p;
}
/* -----------------------------------------------
U.item := UNIT
| UNIT '^' N.term
----------------------------------------------- */
Unit_t *
scan_unit_item()
{
Unit_t *node_p, *exp_p;
char ch;
double num_const;
Unit_E *oe_p;
node_p = p_new_unit(NULL,NULL);
ch = s_peeknext_op();
if( ch == '^' ) {
ch = s_getnext_op();
exp_p = scan_num_term();
num_const = exp_p->u_scale;
if( node_p->u_count > 0 ) {
oe_p = node_p->u_list;
for(oe_p = node_p->u_list; oe_p; oe_p = oe_p->ue_nextp ) {
oe_p->ue_exp = oe_p->ue_exp * num_const;
}
}
num_const = node_p->u_scale;
if( node_p->u_scale > 0.0 ) {
num_const = pow(node_p->u_scale,exp_p->u_scale);
}
node_p->u_scale = num_const;
capa_mfree((char *)exp_p);
}
return node_p;
}
void distribute_exp(Unit_t* node_p,Unit_t* exp_p)
{
Unit_E* oe_p;
double num_const;
num_const = exp_p->u_scale; /* should we check if num_const too large or small ? */
if( node_p->u_count > 0 ) {
oe_p = node_p->u_list;
for(oe_p = node_p->u_list; oe_p; oe_p = oe_p->ue_nextp ) {
oe_p->ue_exp = oe_p->ue_exp * num_const;
}
}
num_const = node_p->u_scale;
if( node_p->u_scale > 0.0 ) { /* what if u_scale <= 0.0 ? */
num_const = pow(node_p->u_scale,exp_p->u_scale);
}
node_p->u_scale = num_const;
if (node_p->u_left) distribute_exp(node_p->u_left,exp_p);
if (node_p->u_right) distribute_exp(node_p->u_right,exp_p);
}
/* ---------------------------------------------------------------
B.term := U.item
| N.item
| '(' B.block ')'
| '{' B.block '}'
| '(' B.block ')' '^' N.term <== July 6 1998
| '{' B.block '}' '^' N.term
--------------------------------------------------------------- */
Unit_t *
scan_basic_term()
{
Unit_t *node_p, *exp_p;
int ch, nch;
ch = s_peeknextNW();
if( ch == '(' || ch == '{' ) {
ch = s_getnextNW(); /* get rid of '(' or '{' */
node_p = scan_basic_block();
nch = s_peeknextNW();
if( nch == ')' || nch == '}' ) { /* should be either ')' or '}' */
if( ((ch == '(' ) && (nch == ')' )) ||
((ch == '{' ) && (nch == '}' )) ) { /* matching left paren with right paren */
} else {
/* printf(" WARN: %c matched by %c\n", ch, nch); */
}
nch = s_getnextNW();
/* ====== Added Jul 6, 1998 ====> */
ch = s_peeknext_op();
if( ch == '^' ) {
ch = s_getnext_op(); /* get rid of '^' char */
exp_p = scan_num_term();
distribute_exp(node_p,exp_p);
capa_mfree((char *)exp_p);
}
/* <== added Jul 6, 1998 == */
} else {
/* printf(" WARN: %c is not matched by %c\n", ch, nch); */
}
} else if( ch >= '0' && ch <= '9' ) {
node_p = scan_num_item();
} else { /* assume a unit symbol */
/* printf("<B.term>"); print_remains(); */
node_p = scan_unit_item();
/* print_remains(); */
}
return node_p;
}
/* --------------------------------------------------
N.term := N.item
| '-' N.item
| '(' N.expr ')'
| '{' N.expr '}'
-------------------------------------------------- */
Unit_t *
scan_num_term()
{
Unit_t *node_p;
char ch, nch;
ch = s_peeknextNW();
if( ch == '(' || ch == '{' ) {
ch = s_getnextNW();
node_p = scan_num_expr();
nch = s_peeknextNW();
if( nch == ')' || nch == '}' ) { /* should be either ')' or '}' */
if( ((ch == '(' ) && (nch == ')' )) ||
((ch == '{' ) && (nch == '}' )) ) {
} else {
/* printf(" WARN: %c matched by %c\n", ch, nch); */
}
nch = s_getnextNW();
} else {
/* printf(" WARN: %c is not matched by %c\n", ch, ch); */
}
} else if( ch == '-' ) {
ch = s_getnextNW();
node_p = scan_num_item();
node_p->u_scale = (-1)*node_p->u_scale;
} else {
if( isdigit(ch) ) {
node_p = scan_num_item();
} else { /* something other than a number */
/*
printf(" ERROR: expect a number: ");
print_remains();
*/
node_p = p_new_num(NULL, 0.0, NULL); /* make the unknown item */
}
}
return node_p;
}
/* --------------------------------------------------
B.block := B.term
| B.term ' ' B.term
| B.term '*' B.term
| B.term '/' B.term
-------------------------------------------------- */
Unit_t *
scan_basic_block()
{
Unit_t *node_p;
char ch;
int op;
/* printf("<B.block>(before B.term)"); print_remains(); */
node_p = scan_basic_term();
ch = s_peeknext_op();
while ( ch == '*' || ch == '/' ) {
op = ( ch == '/' ? U_OP_DIVIDE : U_OP_TIMES);
ch = s_getnext_op();
/* printf("<B.block>(/ *)"); print_remains(); */
node_p = p_new_op(node_p,op,scan_basic_term());
ch = s_peeknext_op();
}
return node_p;
}
/* --------------------------------------------------
N.block := N.term
| N.term ' ' N.term
| N.term '*' N.term
| N.term '/' N.term
-------------------------------------------------- */
Unit_t *
scan_num_block()
{
Unit_t *node_p, *opand_p;
char ch;
double result;
node_p = scan_num_term();
ch = s_peeknext_op();
while ( ch == '*' || ch == '/' ) {
s_getnext_op();
opand_p = scan_num_term();
if( ch == '*' ) {
result = node_p->u_scale * opand_p->u_scale;
} else {
result = node_p->u_scale / opand_p->u_scale;
}
node_p->u_scale = result;
capa_mfree((char *)opand_p);
ch = s_peeknext_op();
}
return node_p;
}
/* ---------------------------------------
U.expr := B.block
| B.block '+' B.block
| B.block '-' B.block
--------------------------------------- */
Unit_t *
scan_unit_expr()
{
Unit_t *node_p;
char ch;
int op;
/* printf("<U.expr>"); print_remains(); */
node_p = scan_basic_block();
ch = s_peeknext_op();
while ( ch == '+' || ch == '-' ) {
op = ( ch == '+' ? U_OP_PLUS : U_OP_MINUS);
ch = s_getnext_op();
/* printf("<U.expr>(+-)"); print_remains(); */
node_p = p_new_op(node_p,op,scan_basic_block());
ch = s_peeknext_op();
}
return node_p;
}
/* -----------------------------------------
N.expr := N.block
| N.block '+' N.block
| N.block '-' N.block
----------------------------------------- */
Unit_t *
scan_num_expr()
{
Unit_t *node_p, *opand_p;
char ch;
double result;
node_p = scan_num_block();
ch = s_peeknext_op();
while ( ch == '+' || ch == '-' ) {
ch = s_getnext_op();
opand_p = scan_num_block();
if( ch == '+' ) {
result = node_p->u_scale + opand_p->u_scale;
} else {
result = node_p->u_scale - opand_p->u_scale;
}
node_p->u_scale = result;
capa_mfree((char *)opand_p);
ch = s_peeknext_op();
}
return node_p;
}
/* ----------------------------------------------------------------------- */
/* <-- This is the major entry point to parse an units expression ------> */
Unit_t *
parse_unit_expr(char *symb_str)
{
Unit_t *root_p;
int len;
len = strlen(symb_str);
strcpy(Sbuf,symb_str); /* copy it into the global Sbuf */
Sidx=0;
root_p = scan_unit_expr();
if(Sidx < len-1 ) {
/* printf(" WARN: NOT PARSED:"); print_remains(); */
}
return (root_p);
}
void
print_remains()
{
int len, ii;
len = strlen(Sbuf);
printf("[[");
for(ii=Sidx;ii<len;ii++) {
printf("%c",Sbuf[ii]);
}
printf("]]");
}
/* =================================================================== */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>