Initial commit
diff --git a/parse.y b/parse.y
new file mode 100644
index 0000000..10e401d
--- /dev/null
+++ b/parse.y
@@ -0,0 +1,584 @@
+/*
+ *  parse.y: the parser for GNU units, a program for units conversion
+ *  Copyright (C) 1999-2002, 2007, 2009, 2014, 2017-2018, 2020, 2024 
+ *  Free Software Foundation, Inc
+ *
+ *  This program 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 3 of the License, or
+ *  (at your option) any later version.
+ * 
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *     
+ *  This program was written by Adrian Mariano (adrianm@gnu.org)
+ */
+
+
+%{
+#include<stdio.h>
+#include<float.h>
+#include "units.h"
+
+struct commtype {
+   int location;
+   const char *data;
+   struct unittype *result;
+   int errorcode;
+};
+
+static int err;  /* value used by parser to store return values */
+
+/* 
+   The CHECK macro aborts parse if an error has occurred.  It optionally
+   destroys a variable.  Call with CHECK(0) if no variables need destruction 
+   on error. 
+*/
+ 
+#define CHECK(var) if (err) { comm->errorcode=err; \
+                              if (var) destroyunit(var); \
+                              YYABORT; }
+ 
+int yylex();
+void yyerror(struct commtype *comm, char *);
+
+#define MAXMEM 100
+int unitcount=0;    /* Counts the number of units allocated by the parser */
+
+struct function { 
+   char *name; 
+   double (*func)(double); 
+   int type;
+}; 
+
+#define DIMENSIONLESS 0
+#define ANGLEIN 1
+#define ANGLEOUT 2
+#define NATURAL 3
+
+struct unittype *
+getnewunit()
+{
+  struct unittype *unit;
+
+  if (unitcount>=MAXMEM)
+    return 0;
+  unit = (struct unittype *) 
+    mymalloc(sizeof(struct unittype),"(getnewunit)");
+  if (!unit)
+    return 0;
+  initializeunit(unit);
+  unitcount++;
+  return unit;
+}
+
+
+void
+destroyunit(struct unittype *unit)
+{
+  freeunit(unit);
+  free(unit);
+  unitcount--;
+}  
+ 
+
+struct unittype *
+makenumunit(double num,int *myerr)
+{
+  struct unittype *ret;
+  ret=getnewunit();
+  if (!ret){
+    *myerr = E_PARSEMEM;
+    return 0;  
+  }
+  ret->factor = num;
+  *myerr = 0;
+  return ret;
+}
+
+int
+logunit(struct unittype *theunit, int base)
+{  
+  if ((err=unit2num(theunit)))
+    return err;
+  if (base==2)
+    theunit->factor = log2(theunit->factor);
+  else if (base==10)
+    theunit->factor = log10(theunit->factor);
+  else
+    theunit->factor = log(theunit->factor)/log((double)base);
+  if (errno)
+    return E_FUNC;
+  return 0;
+}
+ 
+int
+funcunit(struct unittype *theunit, struct function const *fun)
+{
+  struct unittype angleunit;
+  if (fun->type==ANGLEIN){
+    err=unit2num(theunit);
+    if (err==E_NOTANUMBER){
+      initializeunit(&angleunit);
+      angleunit.denominator[0] = dupstr("radian");
+      angleunit.denominator[1] = 0;
+      err = multunit(theunit, &angleunit);
+      freeunit(&angleunit);
+      if (!err)
+        err = unit2num(theunit);
+    }
+    if (err)
+      return err;
+  } else if (fun->type==ANGLEOUT || fun->type == DIMENSIONLESS || fun->type == NATURAL) {
+    if ((err=unit2num(theunit)))
+      return err;
+    if (fun->type==NATURAL && (theunit->factor<0 || trunc(theunit->factor)!=theunit->factor))
+      return E_NOTINDOMAIN;
+  } else 
+     return E_BADFUNCTYPE;
+  errno = 0;
+  theunit->factor = (*(fun->func))(theunit->factor);
+  if (errno)
+    return E_FUNC;
+  if (fun->type==ANGLEOUT) {
+    theunit->numerator[0] = dupstr("radian");
+    theunit->numerator[1] = 0;
+  }
+  return 0;
+}
+
+
+%}
+
+%parse-param {struct commtype *comm}
+%lex-param {struct commtype *comm}
+%define api.pure full
+%define api.prefix {units}
+
+%union {
+  double number;
+  int integer;
+  struct unittype *unit;
+  struct function *realfunc;
+  struct func *unitfunc;
+}
+
+%token <number> REAL
+%token <unit> UNIT
+%token <realfunc> REALFUNC
+%token <integer> LOG
+%token <unitfunc> UNITFUNC
+%token <integer> EXPONENT
+%token <integer> MULTIPLY
+%token <integer> MULTSTAR
+%token <integer> DIVIDE
+%token <integer> NUMDIV
+%token <integer> SQRT
+%token <integer> CUBEROOT
+%token <integer> MULTMINUS
+%token <integer> EOL
+%token <integer> FUNCINV
+%token <integer> MEMERROR
+%token <integer> BADNUMBER
+%token <integer> NUMOVERFLOW
+%token <integer> NUMUNDERFLOW
+%token <integer> UNITEND
+%token <integer> LASTUNSET
+
+%type <number> numexpr
+%type <unit> expr
+%type <unit> list
+%type <unit> pexpr
+%type <unit> unitexpr
+%type <unit> divlist
+
+%destructor { destroyunit($$);} <unit>
+
+%left ADD MINUS
+%left UNARY
+%left DIVIDE MULTSTAR
+%left MULTIPLY MULTMINUS
+%nonassoc '(' SQRT CUBEROOT REALFUNC LOG UNIT REAL UNITFUNC FUNCINV MEMERROR BADNUMBER NUMOVERFLOW NUMUNDERFLOW UNITEND LASTUNSET
+%right EXPONENT
+%left NUMDIV
+
+
+%%
+ input: EOL           { comm->result = makenumunit(1,&err); CHECK(0);
+                       comm->errorcode = 0; YYACCEPT; }
+      | unitexpr EOL { comm->result = $1; comm->errorcode = 0; YYACCEPT; }
+      | error        { YYABORT; }
+      ;
+
+ unitexpr:  expr                    { $$ = $1;}
+         |  divlist                 { $$ = $1;}
+         ;
+
+ divlist: DIVIDE list               { invertunit($2); $$=$2;}
+        | divlist divlist %prec MULTIPLY {err = multunit($1,$2); destroyunit($2);
+                                          CHECK($1);$$=$1;}
+        ;
+
+ expr: list                         { $$ = $1; }
+     | MULTMINUS list %prec UNARY   { $$ = $2; $$->factor *= -1; }
+     | MINUS list %prec UNARY       { $$ = $2; $$->factor *= -1; }
+     | expr ADD expr                { err = addunit($1,$3); destroyunit($3);
+                                      CHECK($1);$$=$1;}
+     | expr MINUS expr              { $3->factor *= -1;
+                                      err = addunit($1,$3); destroyunit($3);
+                                      CHECK($1);$$=$1;}
+     | expr DIVIDE expr             { err = divunit($1, $3); destroyunit($3);
+                                      CHECK($1);$$=$1;}
+     | expr MULTIPLY expr           { err = multunit($1,$3); destroyunit($3);
+                                      CHECK($1);$$=$1;}
+     | expr MULTSTAR expr           { err = multunit($1,$3); destroyunit($3);
+                                      CHECK($1);$$=$1;}
+     ; 
+
+numexpr:  REAL                      { $$ = $1;         }
+         | numexpr NUMDIV numexpr   { $$ = $1 / $3;    }
+     ;
+
+ pexpr: '(' expr ')'                { $$ = $2;  }
+       ;
+
+ /* list is a list of units, possibly raised to powers, to be multiplied
+    together. */
+
+list:  numexpr                     { $$ = makenumunit($1,&err); CHECK(0);}
+      | UNIT                       { $$ = $1; }
+      | list EXPONENT list         { err = unitpower($1,$3);destroyunit($3);
+                                     CHECK($1);$$=$1;}
+      | list MULTMINUS list        { err = multunit($1,$3); destroyunit($3);
+                                     CHECK($1);$$=$1;}
+      | list list %prec MULTIPLY   { err = multunit($1,$2); destroyunit($2);
+                                     CHECK($1);$$=$1;}
+      | pexpr                      { $$=$1; }
+      | SQRT pexpr                 { err = rootunit($2,2); CHECK($2); $$=$2;}
+      | CUBEROOT pexpr             { err = rootunit($2,3); CHECK($2); $$=$2;}
+      | REALFUNC pexpr             { err = funcunit($2,$1);CHECK($2); $$=$2;}
+      | LOG pexpr                  { err = logunit($2,$1); CHECK($2); $$=$2;}
+      | UNITFUNC pexpr             { err = evalfunc($2,$1,0,0); CHECK($2);$$=$2;}
+      | FUNCINV UNITFUNC pexpr     { err = evalfunc($3,$2,1,0); CHECK($3);$$=$3;}
+      | list EXPONENT MULTMINUS list %prec EXPONENT  
+                                   { $4->factor *= -1; err = unitpower($1,$4);
+                                     destroyunit($4);CHECK($1);$$=$1;}
+      | list EXPONENT MINUS list %prec EXPONENT  
+                                   { $4->factor *= -1; err = unitpower($1,$4);
+                                     destroyunit($4);CHECK($1);$$=$1;}
+      | BADNUMBER                  { err = E_BADNUM;   CHECK(0); }
+      | NUMOVERFLOW                { err = E_OVERFLOW; CHECK(0); }
+      | NUMUNDERFLOW               { err = E_UNDERFLOW;CHECK(0); }
+      | MEMERROR                   { err = E_PARSEMEM; CHECK(0); }        
+      | UNITEND                    { err = E_UNITEND;  CHECK(0); }
+      | LASTUNSET                  { err = E_LASTUNSET;CHECK(0); }
+      | FUNCINV UNIT               { err = E_NOTAFUNC; CHECK($2);}
+   ;
+
+%%
+
+double
+factorial(double x)
+{
+  return tgamma(x+1);
+}  
+
+struct function 
+  realfunctions[] = { {"sin", sin,    ANGLEIN},
+                      {"cos", cos,    ANGLEIN},
+                      {"tan", tan,    ANGLEIN},
+                      {"ln", log,     DIMENSIONLESS},
+                      {"log", log10,  DIMENSIONLESS},
+                      {"exp", exp,    DIMENSIONLESS},
+                      {"acos", acos,  ANGLEOUT},
+                      {"atan", atan,  ANGLEOUT},
+                      {"asin", asin,  ANGLEOUT},
+		      {"sinh", sinh, DIMENSIONLESS},
+		      {"cosh", cosh, DIMENSIONLESS},		      
+		      {"tanh", tanh, DIMENSIONLESS},
+		      {"asinh", asinh, DIMENSIONLESS},
+		      {"acosh", acosh, DIMENSIONLESS},		      
+		      {"atanh", atanh, DIMENSIONLESS},
+                      {"round", round, DIMENSIONLESS},
+                      {"floor", floor, DIMENSIONLESS},
+                      {"ceil", ceil, DIMENSIONLESS},
+                      {"erf", erf, DIMENSIONLESS},
+                      {"erfc", erfc, DIMENSIONLESS},
+                      {"Gamma", tgamma, DIMENSIONLESS},
+                      {"lnGamma", lgamma, DIMENSIONLESS},
+                      {"factorial", factorial, NATURAL},
+                      {0, 0, 0}};
+
+struct {
+  char op;
+  int value;
+} optable[] = { {'*', MULTIPLY},
+                {'/', DIVIDE},
+                {'|', NUMDIV},
+                {'+', ADD},
+                {'(', '('},
+                {')', ')'},
+                {'^', EXPONENT},
+                {'~', FUNCINV},
+                {0, 0}};
+
+struct {
+  char *name;
+  int value;
+} strtable[] = { {"sqrt", SQRT},
+                 {"cuberoot", CUBEROOT},
+                 {"per" , DIVIDE},
+                 {0, 0}};
+
+#define LASTUNIT '_'     /* Last unit symbol */
+
+
+int yylex(YYSTYPE *lvalp, struct commtype *comm)
+{
+  int length, count;
+  struct unittype *output;
+  const char *inptr;
+  char *name;
+
+  char *nonunitchars = "~;+-*/|\t\n^ ()"; /* Chars not allowed in unit name --- also defined in units.c */
+  char *nonunitstart = ".,";              /* Can't start a unit */
+  char *nonunitend = ".,_";              /* Can't end a unit */
+  char *number_start = ".,0123456789";    /* Can be first char of a number */
+  
+  if (comm->location==-1) return 0;
+  inptr = comm->data + comm->location;   /* Point to start of data */
+
+  /* Skip spaces */
+  while(*inptr==' ') inptr++, comm->location++;
+
+  if (*inptr==0) {
+    comm->location = -1;
+    return EOL;  /* Return failure if string has ended */
+  }  
+
+  /* Check for **, an exponent operator.  */
+
+  if (0==strncmp("**",inptr,2)){
+    comm->location += 2;
+    return EXPONENT;
+  }
+
+  /* Check for '-' and '*' which get special handling */
+
+  if (*inptr=='-'){
+    comm->location++;
+    if (parserflags.minusminus)
+      return MINUS;
+    return MULTMINUS;
+  }      
+
+  if (*inptr=='*'){
+    comm->location++;
+    if (parserflags.oldstar)
+      return MULTIPLY;
+    return MULTSTAR;
+  }      
+
+  /* Look for single character ops */
+
+  for(count=0; optable[count].op; count++){
+    if (*inptr==optable[count].op) {
+       comm->location++;
+       return optable[count].value;
+    }
+  }
+
+  /* Look for numbers */
+
+  if (strchr(number_start,*inptr)){  /* prevent "nan" from being recognized */
+    char *endloc;
+    errno=0;
+    lvalp->number = strtod(inptr, &endloc);
+    if (inptr != endloc) { 
+      comm->location += (endloc-inptr);
+      if (*endloc && strchr(number_start,*endloc))
+        return BADNUMBER;
+      else if (errno){
+        errno=0;
+        if (fabs(lvalp->number)==HUGE_VAL) return NUMOVERFLOW;
+        else return NUMUNDERFLOW;
+      }
+      else
+        return REAL;
+    }
+  }
+
+  /* Look for a word (function name or unit name) */
+
+  length = strcspn(inptr,nonunitchars);   
+
+  if (!length){  /* Next char is not a valid unit char */
+     comm->location++;
+     return 0;
+  }
+
+  /* Check for the "last unit" symbol, with possible exponent */ 
+
+  if (*inptr == LASTUNIT &&
+      (length==1 || length==2 && strchr("23456789",inptr[1]))){
+    comm->location++;
+    if (!lastunitset) 
+      return LASTUNSET;
+    output = getnewunit();
+    if (!output)
+      return MEMERROR;
+    unitcopy(output, &lastunit);
+    if (length==2){
+      expunit(output, inptr[1]-'0');
+      comm->location++;
+    }
+    lvalp->unit = output;
+    return UNIT;
+  } 
+
+  /* Check that unit name doesn't start or end with forbidden chars */
+  if (strchr(nonunitstart,*inptr)){
+    comm->location++;
+    return 0;
+  }
+  if (strchr(nonunitend, inptr[length-1])){
+    comm->location+=length;
+    return 0;
+  }
+
+  name = dupnstr(inptr, length);
+
+  /* Look for string operators */
+
+  for(count=0;strtable[count].name;count++){
+    if (!strcmp(name,strtable[count].name)){
+      free(name);
+      comm->location += length;
+      return strtable[count].value;
+    }
+  }
+  
+  /* Look for real function names */
+
+  for(count=0;realfunctions[count].name;count++){
+    if (!strcmp(name,realfunctions[count].name)){
+      lvalp->realfunc = realfunctions+count;
+      comm->location += length;
+      free(name);
+      return REALFUNC;
+    }
+  }
+
+  /* Check for arbitrary base log */
+  
+  if (!strncmp(name, "log",3)){
+    count = strspn(name+3,"1234567890");
+    if (count+3 == strlen(name)){
+      lvalp->integer=atoi(name+3);
+      if (lvalp->integer>1){      /* Log base must be larger than 1 */
+	comm->location += length;
+	free(name);
+	return LOG;
+      }
+    }
+  }
+      
+  /* Look for function parameter */
+
+  if (function_parameter && !strcmp(name,function_parameter)){
+    free(name);
+    output = getnewunit();
+    if (!output)
+      return MEMERROR;
+    unitcopy(output, parameter_value);
+    lvalp->unit = output;
+    comm->location += length;
+    return UNIT;
+  } 
+
+  /* Look for user defined function */
+
+  lvalp->unitfunc = fnlookup(name);
+  if (lvalp->unitfunc){
+    comm->location += length;
+    free(name);
+    return UNITFUNC;
+  }
+
+  /* Didn't find a special string, so treat it as unit name */
+
+  comm->location+=length;
+  if (strchr("23456789",inptr[length-1]) && !hassubscript(name)) {
+    /* ends with digit but not a subscript, so do exponent handling like m3 */
+    count = name[length-1] - '0';
+    length--;
+    if (strchr(number_start, name[length-1])){
+      free(name);
+      return UNITEND;
+    }
+  } else count=1;
+
+  free(name);
+    
+  output = getnewunit();
+  if (!output)
+    return MEMERROR;
+  output->numerator[count--]=0;
+  for(;count>=0;count--)
+    output->numerator[count] = dupnstr(inptr, length);
+  lvalp->unit=output;
+  return UNIT;
+}
+
+
+void yyerror(struct commtype *comm, char *s){}
+
+
+int
+parseunit(struct unittype *output, char const *input,char **errstr,int *errloc)
+{
+  struct commtype comm;
+  int saveunitcount;
+
+  saveunitcount = unitcount;
+  initializeunit(output);
+  comm.result = 0;
+  comm.location = 0;
+  comm.data = input;
+  comm.errorcode = E_PARSE;    /* Assume parse error */
+  errno=0;
+  /* errno should only be set in the case of invalid function arguments */
+  if (yyparse(&comm) || errno){
+    if (comm.location==-1) 
+      comm.location = strlen(input);
+    if (errstr){
+      if (comm.errorcode==E_FUNC || errno)
+        *errstr = strerror(errno);
+      else
+        *errstr=errormsg[comm.errorcode];
+    }
+    if (errloc)
+      *errloc = comm.location;
+    if (unitcount!=saveunitcount)
+      fprintf(stderr,"units: Parser leaked memory with error: %d in %d out\n",
+             saveunitcount, unitcount);
+    return comm.errorcode;
+  } else {
+    if (errstr)
+      *errstr = 0;
+    multunit(output,comm.result);
+    destroyunit(comm.result);
+    if (unitcount!=saveunitcount)
+      fprintf(stderr,"units: Parser leaked memory without error: %d in %d out\n",
+	      saveunitcount, unitcount);
+    return 0;
+  }
+}
+
+