#include "a.h"

/*
* Section 1 - General Explanation.
*/

/* 1.3 - Numerical parameter input.  */
char *units = "icPmnpuvx";
int
scale2units(char c)
{
       int x;

       switch(c){
       case 'i':       /* inch */
               return UPI;
       case 'c':       /* centimeter */
               return 0.3937008 * UPI;
       case 'P':       /* pica = 1/6 inch */
               return UPI / 6;
       case 'm':       /* em = S points */
               return UPI / 72.0 * getnr(L(".s"));
       case 'n':       /* en = em/2 */
               return UPI / 72.0 * getnr(L(".s")) / 2;
       case 'p':       /* point = 1/72 inch */
               return UPI / 72;
       case 'u':       /* basic unit */
               return 1;
       case 'v':       /* vertical line space V */
               x = getnr(L(".v"));
               if(x == 0)
                       x = 12 * UPI / 72;
               return x;
       case 'x':       /* pixel (htmlroff addition) */
               return UPX;
       default:
               return 1;
       }
}

/* 1.4 - Numerical expressions. */
int eval0(Rune**, int, int);
int
eval(Rune *s)
{
       return eval0(&s, 1, 1);
}
long
runestrtol(Rune *a, Rune **p)
{
       long n;

       n = 0;
       while('0' <= *a && *a <= '9'){
               n = n*10 + *a-'0';
               a++;
       }
       *p = a;
       return n;
}

int
evalscale(Rune *s, int c)
{
       return eval0(&s, scale2units(c), 1);
}

int
eval0(Rune **pline, int scale, int recur)
{
       Rune *p;
       int neg;
       double f, p10;
       int x, y;

       neg = 0;
       p = *pline;
       while(*p == '-'){
               neg = 1 - neg;
               p++;
       }
       if(*p == '('){
               p++;
               x = eval0(&p, scale, 1);
               if (*p != ')'){
                       *pline = p;
                       return x;
               }
               p++;
       }else{
               f = runestrtol(p, &p);
               if(*p == '.'){
                       p10 = 1.0;
                       p++;
                       while('0' <= *p && *p <= '9'){
                               p10 /= 10;
                               f += p10*(*p++ - '0');
                       }
               }
               if(*p && strchr(units, *p)){
                       if(scale)
                               f *= scale2units(*p);
                       p++;
               }else if(scale)
                       f *= scale;
               x = f;
       }
       if(neg)
               x = -x;
       if(!recur){
               *pline = p;
               return x;
       }

       while(*p){
               switch(*p++) {
               case '+':
                       x += eval0(&p, scale, 0);
                       continue;
               case '-':
                       x -= eval0(&p, scale, 0);
                       continue;
               case '*':
                       x *= eval0(&p, scale, 0);
                       continue;
               case '/':
                       y = eval0(&p, scale, 0);
                       if (y == 0) {
                               fprint(2, "%L: divide by zero %S\n", p);
                               y = 1;
                       }
                       x /= y;
                       continue;
               case '%':
                       y = eval0(&p, scale, 0);
                       if (!y) {
                               fprint(2, "%L: modulo by zero %S\n", p);
                               y = 1;
                       }
                       x %= y;
                       continue;
               case '<':
                       if (*p == '=') {
                               p++;
                               x = x <= eval0(&p, scale, 0);
                               continue;
                       }
                       x = x < eval0(&p, scale, 0);
                       continue;
               case '>':
                       if (*p == '=') {
                               p++;
                               x = x >= eval0(&p, scale, 0);
                               continue;
                       }
                       x = x > eval0(&p, scale, 0);
                       continue;
               case '=':
                       if (*p == '=')
                               p++;
                       x = x == eval0(&p, scale, 0);
                       continue;
               case '&':
                       x &= eval0(&p, scale, 0);
                       continue;
               case ':':
                       x |= eval0(&p, scale, 0);
                       continue;
               }
       }
       *pline = p;
       return x;
}

void
t1init(void)
{
       Tm tm;

       tm = *localtime(time(0));
       nr(L("dw"), tm.wday+1);
       nr(L("dy"), tm.mday);
       nr(L("mo"), tm.mon);
       nr(L("yr"), tm.year%100);
}