blob: 10e401dd9b9961ba0ea2f68b4b9bf62bf4601137 [file] [log] [blame]
swissChili729acd52024-03-05 11:52:45 -05001/*
2 * parse.y: the parser for GNU units, a program for units conversion
3 * Copyright (C) 1999-2002, 2007, 2009, 2014, 2017-2018, 2020, 2024
4 * Free Software Foundation, Inc
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 * This program was written by Adrian Mariano (adrianm@gnu.org)
21 */
22
23
24%{
25#include<stdio.h>
26#include<float.h>
27#include "units.h"
28
29struct commtype {
30 int location;
31 const char *data;
32 struct unittype *result;
33 int errorcode;
34};
35
36static int err; /* value used by parser to store return values */
37
38/*
39 The CHECK macro aborts parse if an error has occurred. It optionally
40 destroys a variable. Call with CHECK(0) if no variables need destruction
41 on error.
42*/
43
44#define CHECK(var) if (err) { comm->errorcode=err; \
45 if (var) destroyunit(var); \
46 YYABORT; }
47
48int yylex();
49void yyerror(struct commtype *comm, char *);
50
51#define MAXMEM 100
52int unitcount=0; /* Counts the number of units allocated by the parser */
53
54struct function {
55 char *name;
56 double (*func)(double);
57 int type;
58};
59
60#define DIMENSIONLESS 0
61#define ANGLEIN 1
62#define ANGLEOUT 2
63#define NATURAL 3
64
65struct unittype *
66getnewunit()
67{
68 struct unittype *unit;
69
70 if (unitcount>=MAXMEM)
71 return 0;
72 unit = (struct unittype *)
73 mymalloc(sizeof(struct unittype),"(getnewunit)");
74 if (!unit)
75 return 0;
76 initializeunit(unit);
77 unitcount++;
78 return unit;
79}
80
81
82void
83destroyunit(struct unittype *unit)
84{
85 freeunit(unit);
86 free(unit);
87 unitcount--;
88}
89
90
91struct unittype *
92makenumunit(double num,int *myerr)
93{
94 struct unittype *ret;
95 ret=getnewunit();
96 if (!ret){
97 *myerr = E_PARSEMEM;
98 return 0;
99 }
100 ret->factor = num;
101 *myerr = 0;
102 return ret;
103}
104
105int
106logunit(struct unittype *theunit, int base)
107{
108 if ((err=unit2num(theunit)))
109 return err;
110 if (base==2)
111 theunit->factor = log2(theunit->factor);
112 else if (base==10)
113 theunit->factor = log10(theunit->factor);
114 else
115 theunit->factor = log(theunit->factor)/log((double)base);
116 if (errno)
117 return E_FUNC;
118 return 0;
119}
120
121int
122funcunit(struct unittype *theunit, struct function const *fun)
123{
124 struct unittype angleunit;
125 if (fun->type==ANGLEIN){
126 err=unit2num(theunit);
127 if (err==E_NOTANUMBER){
128 initializeunit(&angleunit);
129 angleunit.denominator[0] = dupstr("radian");
130 angleunit.denominator[1] = 0;
131 err = multunit(theunit, &angleunit);
132 freeunit(&angleunit);
133 if (!err)
134 err = unit2num(theunit);
135 }
136 if (err)
137 return err;
138 } else if (fun->type==ANGLEOUT || fun->type == DIMENSIONLESS || fun->type == NATURAL) {
139 if ((err=unit2num(theunit)))
140 return err;
141 if (fun->type==NATURAL && (theunit->factor<0 || trunc(theunit->factor)!=theunit->factor))
142 return E_NOTINDOMAIN;
143 } else
144 return E_BADFUNCTYPE;
145 errno = 0;
146 theunit->factor = (*(fun->func))(theunit->factor);
147 if (errno)
148 return E_FUNC;
149 if (fun->type==ANGLEOUT) {
150 theunit->numerator[0] = dupstr("radian");
151 theunit->numerator[1] = 0;
152 }
153 return 0;
154}
155
156
157%}
158
159%parse-param {struct commtype *comm}
160%lex-param {struct commtype *comm}
161%define api.pure full
162%define api.prefix {units}
163
164%union {
165 double number;
166 int integer;
167 struct unittype *unit;
168 struct function *realfunc;
169 struct func *unitfunc;
170}
171
172%token <number> REAL
173%token <unit> UNIT
174%token <realfunc> REALFUNC
175%token <integer> LOG
176%token <unitfunc> UNITFUNC
177%token <integer> EXPONENT
178%token <integer> MULTIPLY
179%token <integer> MULTSTAR
180%token <integer> DIVIDE
181%token <integer> NUMDIV
182%token <integer> SQRT
183%token <integer> CUBEROOT
184%token <integer> MULTMINUS
185%token <integer> EOL
186%token <integer> FUNCINV
187%token <integer> MEMERROR
188%token <integer> BADNUMBER
189%token <integer> NUMOVERFLOW
190%token <integer> NUMUNDERFLOW
191%token <integer> UNITEND
192%token <integer> LASTUNSET
193
194%type <number> numexpr
195%type <unit> expr
196%type <unit> list
197%type <unit> pexpr
198%type <unit> unitexpr
199%type <unit> divlist
200
201%destructor { destroyunit($$);} <unit>
202
203%left ADD MINUS
204%left UNARY
205%left DIVIDE MULTSTAR
206%left MULTIPLY MULTMINUS
207%nonassoc '(' SQRT CUBEROOT REALFUNC LOG UNIT REAL UNITFUNC FUNCINV MEMERROR BADNUMBER NUMOVERFLOW NUMUNDERFLOW UNITEND LASTUNSET
208%right EXPONENT
209%left NUMDIV
210
211
212%%
213 input: EOL { comm->result = makenumunit(1,&err); CHECK(0);
214 comm->errorcode = 0; YYACCEPT; }
215 | unitexpr EOL { comm->result = $1; comm->errorcode = 0; YYACCEPT; }
216 | error { YYABORT; }
217 ;
218
219 unitexpr: expr { $$ = $1;}
220 | divlist { $$ = $1;}
221 ;
222
223 divlist: DIVIDE list { invertunit($2); $$=$2;}
224 | divlist divlist %prec MULTIPLY {err = multunit($1,$2); destroyunit($2);
225 CHECK($1);$$=$1;}
226 ;
227
228 expr: list { $$ = $1; }
229 | MULTMINUS list %prec UNARY { $$ = $2; $$->factor *= -1; }
230 | MINUS list %prec UNARY { $$ = $2; $$->factor *= -1; }
231 | expr ADD expr { err = addunit($1,$3); destroyunit($3);
232 CHECK($1);$$=$1;}
233 | expr MINUS expr { $3->factor *= -1;
234 err = addunit($1,$3); destroyunit($3);
235 CHECK($1);$$=$1;}
236 | expr DIVIDE expr { err = divunit($1, $3); destroyunit($3);
237 CHECK($1);$$=$1;}
238 | expr MULTIPLY expr { err = multunit($1,$3); destroyunit($3);
239 CHECK($1);$$=$1;}
240 | expr MULTSTAR expr { err = multunit($1,$3); destroyunit($3);
241 CHECK($1);$$=$1;}
242 ;
243
244numexpr: REAL { $$ = $1; }
245 | numexpr NUMDIV numexpr { $$ = $1 / $3; }
246 ;
247
248 pexpr: '(' expr ')' { $$ = $2; }
249 ;
250
251 /* list is a list of units, possibly raised to powers, to be multiplied
252 together. */
253
254list: numexpr { $$ = makenumunit($1,&err); CHECK(0);}
255 | UNIT { $$ = $1; }
256 | list EXPONENT list { err = unitpower($1,$3);destroyunit($3);
257 CHECK($1);$$=$1;}
258 | list MULTMINUS list { err = multunit($1,$3); destroyunit($3);
259 CHECK($1);$$=$1;}
260 | list list %prec MULTIPLY { err = multunit($1,$2); destroyunit($2);
261 CHECK($1);$$=$1;}
262 | pexpr { $$=$1; }
263 | SQRT pexpr { err = rootunit($2,2); CHECK($2); $$=$2;}
264 | CUBEROOT pexpr { err = rootunit($2,3); CHECK($2); $$=$2;}
265 | REALFUNC pexpr { err = funcunit($2,$1);CHECK($2); $$=$2;}
266 | LOG pexpr { err = logunit($2,$1); CHECK($2); $$=$2;}
267 | UNITFUNC pexpr { err = evalfunc($2,$1,0,0); CHECK($2);$$=$2;}
268 | FUNCINV UNITFUNC pexpr { err = evalfunc($3,$2,1,0); CHECK($3);$$=$3;}
269 | list EXPONENT MULTMINUS list %prec EXPONENT
270 { $4->factor *= -1; err = unitpower($1,$4);
271 destroyunit($4);CHECK($1);$$=$1;}
272 | list EXPONENT MINUS list %prec EXPONENT
273 { $4->factor *= -1; err = unitpower($1,$4);
274 destroyunit($4);CHECK($1);$$=$1;}
275 | BADNUMBER { err = E_BADNUM; CHECK(0); }
276 | NUMOVERFLOW { err = E_OVERFLOW; CHECK(0); }
277 | NUMUNDERFLOW { err = E_UNDERFLOW;CHECK(0); }
278 | MEMERROR { err = E_PARSEMEM; CHECK(0); }
279 | UNITEND { err = E_UNITEND; CHECK(0); }
280 | LASTUNSET { err = E_LASTUNSET;CHECK(0); }
281 | FUNCINV UNIT { err = E_NOTAFUNC; CHECK($2);}
282 ;
283
284%%
285
286double
287factorial(double x)
288{
289 return tgamma(x+1);
290}
291
292struct function
293 realfunctions[] = { {"sin", sin, ANGLEIN},
294 {"cos", cos, ANGLEIN},
295 {"tan", tan, ANGLEIN},
296 {"ln", log, DIMENSIONLESS},
297 {"log", log10, DIMENSIONLESS},
298 {"exp", exp, DIMENSIONLESS},
299 {"acos", acos, ANGLEOUT},
300 {"atan", atan, ANGLEOUT},
301 {"asin", asin, ANGLEOUT},
302 {"sinh", sinh, DIMENSIONLESS},
303 {"cosh", cosh, DIMENSIONLESS},
304 {"tanh", tanh, DIMENSIONLESS},
305 {"asinh", asinh, DIMENSIONLESS},
306 {"acosh", acosh, DIMENSIONLESS},
307 {"atanh", atanh, DIMENSIONLESS},
308 {"round", round, DIMENSIONLESS},
309 {"floor", floor, DIMENSIONLESS},
310 {"ceil", ceil, DIMENSIONLESS},
311 {"erf", erf, DIMENSIONLESS},
312 {"erfc", erfc, DIMENSIONLESS},
313 {"Gamma", tgamma, DIMENSIONLESS},
314 {"lnGamma", lgamma, DIMENSIONLESS},
315 {"factorial", factorial, NATURAL},
316 {0, 0, 0}};
317
318struct {
319 char op;
320 int value;
321} optable[] = { {'*', MULTIPLY},
322 {'/', DIVIDE},
323 {'|', NUMDIV},
324 {'+', ADD},
325 {'(', '('},
326 {')', ')'},
327 {'^', EXPONENT},
328 {'~', FUNCINV},
329 {0, 0}};
330
331struct {
332 char *name;
333 int value;
334} strtable[] = { {"sqrt", SQRT},
335 {"cuberoot", CUBEROOT},
336 {"per" , DIVIDE},
337 {0, 0}};
338
339#define LASTUNIT '_' /* Last unit symbol */
340
341
342int yylex(YYSTYPE *lvalp, struct commtype *comm)
343{
344 int length, count;
345 struct unittype *output;
346 const char *inptr;
347 char *name;
348
349 char *nonunitchars = "~;+-*/|\t\n^ ()"; /* Chars not allowed in unit name --- also defined in units.c */
350 char *nonunitstart = ".,"; /* Can't start a unit */
351 char *nonunitend = ".,_"; /* Can't end a unit */
352 char *number_start = ".,0123456789"; /* Can be first char of a number */
353
354 if (comm->location==-1) return 0;
355 inptr = comm->data + comm->location; /* Point to start of data */
356
357 /* Skip spaces */
358 while(*inptr==' ') inptr++, comm->location++;
359
360 if (*inptr==0) {
361 comm->location = -1;
362 return EOL; /* Return failure if string has ended */
363 }
364
365 /* Check for **, an exponent operator. */
366
367 if (0==strncmp("**",inptr,2)){
368 comm->location += 2;
369 return EXPONENT;
370 }
371
372 /* Check for '-' and '*' which get special handling */
373
374 if (*inptr=='-'){
375 comm->location++;
376 if (parserflags.minusminus)
377 return MINUS;
378 return MULTMINUS;
379 }
380
381 if (*inptr=='*'){
382 comm->location++;
383 if (parserflags.oldstar)
384 return MULTIPLY;
385 return MULTSTAR;
386 }
387
388 /* Look for single character ops */
389
390 for(count=0; optable[count].op; count++){
391 if (*inptr==optable[count].op) {
392 comm->location++;
393 return optable[count].value;
394 }
395 }
396
397 /* Look for numbers */
398
399 if (strchr(number_start,*inptr)){ /* prevent "nan" from being recognized */
400 char *endloc;
401 errno=0;
402 lvalp->number = strtod(inptr, &endloc);
403 if (inptr != endloc) {
404 comm->location += (endloc-inptr);
405 if (*endloc && strchr(number_start,*endloc))
406 return BADNUMBER;
407 else if (errno){
408 errno=0;
409 if (fabs(lvalp->number)==HUGE_VAL) return NUMOVERFLOW;
410 else return NUMUNDERFLOW;
411 }
412 else
413 return REAL;
414 }
415 }
416
417 /* Look for a word (function name or unit name) */
418
419 length = strcspn(inptr,nonunitchars);
420
421 if (!length){ /* Next char is not a valid unit char */
422 comm->location++;
423 return 0;
424 }
425
426 /* Check for the "last unit" symbol, with possible exponent */
427
428 if (*inptr == LASTUNIT &&
429 (length==1 || length==2 && strchr("23456789",inptr[1]))){
430 comm->location++;
431 if (!lastunitset)
432 return LASTUNSET;
433 output = getnewunit();
434 if (!output)
435 return MEMERROR;
436 unitcopy(output, &lastunit);
437 if (length==2){
438 expunit(output, inptr[1]-'0');
439 comm->location++;
440 }
441 lvalp->unit = output;
442 return UNIT;
443 }
444
445 /* Check that unit name doesn't start or end with forbidden chars */
446 if (strchr(nonunitstart,*inptr)){
447 comm->location++;
448 return 0;
449 }
450 if (strchr(nonunitend, inptr[length-1])){
451 comm->location+=length;
452 return 0;
453 }
454
455 name = dupnstr(inptr, length);
456
457 /* Look for string operators */
458
459 for(count=0;strtable[count].name;count++){
460 if (!strcmp(name,strtable[count].name)){
461 free(name);
462 comm->location += length;
463 return strtable[count].value;
464 }
465 }
466
467 /* Look for real function names */
468
469 for(count=0;realfunctions[count].name;count++){
470 if (!strcmp(name,realfunctions[count].name)){
471 lvalp->realfunc = realfunctions+count;
472 comm->location += length;
473 free(name);
474 return REALFUNC;
475 }
476 }
477
478 /* Check for arbitrary base log */
479
480 if (!strncmp(name, "log",3)){
481 count = strspn(name+3,"1234567890");
482 if (count+3 == strlen(name)){
483 lvalp->integer=atoi(name+3);
484 if (lvalp->integer>1){ /* Log base must be larger than 1 */
485 comm->location += length;
486 free(name);
487 return LOG;
488 }
489 }
490 }
491
492 /* Look for function parameter */
493
494 if (function_parameter && !strcmp(name,function_parameter)){
495 free(name);
496 output = getnewunit();
497 if (!output)
498 return MEMERROR;
499 unitcopy(output, parameter_value);
500 lvalp->unit = output;
501 comm->location += length;
502 return UNIT;
503 }
504
505 /* Look for user defined function */
506
507 lvalp->unitfunc = fnlookup(name);
508 if (lvalp->unitfunc){
509 comm->location += length;
510 free(name);
511 return UNITFUNC;
512 }
513
514 /* Didn't find a special string, so treat it as unit name */
515
516 comm->location+=length;
517 if (strchr("23456789",inptr[length-1]) && !hassubscript(name)) {
518 /* ends with digit but not a subscript, so do exponent handling like m3 */
519 count = name[length-1] - '0';
520 length--;
521 if (strchr(number_start, name[length-1])){
522 free(name);
523 return UNITEND;
524 }
525 } else count=1;
526
527 free(name);
528
529 output = getnewunit();
530 if (!output)
531 return MEMERROR;
532 output->numerator[count--]=0;
533 for(;count>=0;count--)
534 output->numerator[count] = dupnstr(inptr, length);
535 lvalp->unit=output;
536 return UNIT;
537}
538
539
540void yyerror(struct commtype *comm, char *s){}
541
542
543int
544parseunit(struct unittype *output, char const *input,char **errstr,int *errloc)
545{
546 struct commtype comm;
547 int saveunitcount;
548
549 saveunitcount = unitcount;
550 initializeunit(output);
551 comm.result = 0;
552 comm.location = 0;
553 comm.data = input;
554 comm.errorcode = E_PARSE; /* Assume parse error */
555 errno=0;
556 /* errno should only be set in the case of invalid function arguments */
557 if (yyparse(&comm) || errno){
558 if (comm.location==-1)
559 comm.location = strlen(input);
560 if (errstr){
561 if (comm.errorcode==E_FUNC || errno)
562 *errstr = strerror(errno);
563 else
564 *errstr=errormsg[comm.errorcode];
565 }
566 if (errloc)
567 *errloc = comm.location;
568 if (unitcount!=saveunitcount)
569 fprintf(stderr,"units: Parser leaked memory with error: %d in %d out\n",
570 saveunitcount, unitcount);
571 return comm.errorcode;
572 } else {
573 if (errstr)
574 *errstr = 0;
575 multunit(output,comm.result);
576 destroyunit(comm.result);
577 if (unitcount!=saveunitcount)
578 fprintf(stderr,"units: Parser leaked memory without error: %d in %d out\n",
579 saveunitcount, unitcount);
580 return 0;
581 }
582}
583
584