mirror of
				git://git.code.sf.net/p/zsh/code
				synced 2025-10-31 06:00:54 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1180 lines
		
	
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1180 lines
		
	
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * math.c - mathematical expression evaluation
 | |
|  *
 | |
|  * This file is part of zsh, the Z shell.
 | |
|  *
 | |
|  * Copyright (c) 1992-1997 Paul Falstad
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * Permission is hereby granted, without written agreement and without
 | |
|  * license or royalty fees, to use, copy, modify, and distribute this
 | |
|  * software and to distribute modified versions of this software for any
 | |
|  * purpose, provided that the above copyright notice and the following
 | |
|  * two paragraphs appear in all copies of this software.
 | |
|  *
 | |
|  * In no event shall Paul Falstad or the Zsh Development Group be liable
 | |
|  * to any party for direct, indirect, special, incidental, or consequential
 | |
|  * damages arising out of the use of this software and its documentation,
 | |
|  * even if Paul Falstad and the Zsh Development Group have been advised of
 | |
|  * the possibility of such damage.
 | |
|  *
 | |
|  * Paul Falstad and the Zsh Development Group specifically disclaim any
 | |
|  * warranties, including, but not limited to, the implied warranties of
 | |
|  * merchantability and fitness for a particular purpose.  The software
 | |
|  * provided hereunder is on an "as is" basis, and Paul Falstad and the
 | |
|  * Zsh Development Group have no obligation to provide maintenance,
 | |
|  * support, updates, enhancements, or modifications.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include "zsh.mdh"
 | |
| #include "math.pro"
 | |
| 
 | |
| #include <math.h>
 | |
| 
 | |
| /* nonzero means we are not evaluating, just parsing */
 | |
|  
 | |
| /**/
 | |
| int noeval;
 | |
|  
 | |
| /* integer zero */
 | |
| 
 | |
| /**/
 | |
| mod_export mnumber zero_mnumber;
 | |
| 
 | |
| /* last input base we used */
 | |
| 
 | |
| /**/
 | |
| int lastbase;
 | |
|  
 | |
| static char *ptr;
 | |
| 
 | |
| static mnumber yyval;
 | |
| static char *yylval;
 | |
| 
 | |
| #define MAX_MLEVEL 256
 | |
| 
 | |
| static int mlevel = 0;
 | |
| 
 | |
| /* != 0 means recognize unary plus, minus, etc. */
 | |
| 
 | |
| static int unary = 1;
 | |
| 
 | |
| /* LR = left-to-right associativity *
 | |
|  * RL = right-to-left associativity *
 | |
|  * BOOL = short-circuiting boolean   */
 | |
| 
 | |
| #define LR   0x0000
 | |
| #define RL   0x0001
 | |
| #define BOOL 0x0002
 | |
| 
 | |
| #define MTYPE(x)  ((x) & 3)
 | |
| 
 | |
| /*
 | |
|  * OP_A2    2 arguments
 | |
|  * OP_A2IR  2 arguments, return integer
 | |
|  * OP_A2IO  2 arguments, must be integer, return integer
 | |
|  * OP_E2    2 arguments with assignment
 | |
|  * OP_E2IO  2 arguments with assignment, must be integer, return integer
 | |
|  * OP_OP    None of the above, but occurs where we are expecting an operator
 | |
|  *          rather than an operand.
 | |
|  * OP_OPF   Followed by an operator, not an operand.
 | |
|  *
 | |
|  * OP_A2*, OP_E2*, OP_OP*:
 | |
|  *   Occur when we need an operator; the next object must be an operand,
 | |
|  *   unless OP_OPF is also supplied.
 | |
|  *
 | |
|  * Others:
 | |
|  *   Occur when we need an operand; the next object must also be an operand,
 | |
|  *   unless OP_OPF is also supplied.
 | |
|  */
 | |
| #define OP_A2   0x0004
 | |
| #define OP_A2IR 0x0008
 | |
| #define OP_A2IO 0x0010
 | |
| #define OP_E2   0x0020
 | |
| #define OP_E2IO 0x0040
 | |
| #define OP_OP   0x0080
 | |
| #define OP_OPF  0x0100
 | |
| 
 | |
| #define M_INPAR 0
 | |
| #define M_OUTPAR 1
 | |
| #define NOT 2
 | |
| #define COMP 3
 | |
| #define POSTPLUS 4
 | |
| #define POSTMINUS 5
 | |
| #define UPLUS 6
 | |
| #define UMINUS 7
 | |
| #define AND 8
 | |
| #define XOR 9
 | |
| #define OR 10
 | |
| #define MUL 11
 | |
| #define DIV 12
 | |
| #define MOD 13
 | |
| #define PLUS 14
 | |
| #define MINUS 15
 | |
| #define SHLEFT 16
 | |
| #define SHRIGHT 17
 | |
| #define LES 18
 | |
| #define LEQ 19
 | |
| #define GRE 20
 | |
| #define GEQ 21
 | |
| #define DEQ 22
 | |
| #define NEQ 23
 | |
| #define DAND 24
 | |
| #define DOR 25
 | |
| #define DXOR 26
 | |
| #define QUEST 27
 | |
| #define COLON 28
 | |
| #define EQ 29
 | |
| #define PLUSEQ 30
 | |
| #define MINUSEQ 31
 | |
| #define MULEQ 32
 | |
| #define DIVEQ 33
 | |
| #define MODEQ 34
 | |
| #define ANDEQ 35
 | |
| #define XOREQ 36
 | |
| #define OREQ 37
 | |
| #define SHLEFTEQ 38
 | |
| #define SHRIGHTEQ 39
 | |
| #define DANDEQ 40
 | |
| #define DOREQ 41
 | |
| #define DXOREQ 42
 | |
| #define COMMA 43
 | |
| #define EOI 44
 | |
| #define PREPLUS 45
 | |
| #define PREMINUS 46
 | |
| #define NUM 47
 | |
| #define ID 48
 | |
| #define POWER 49
 | |
| #define CID 50
 | |
| #define POWEREQ 51
 | |
| #define FUNC 52
 | |
| #define TOKCOUNT 53
 | |
| 
 | |
| /* precedences */
 | |
| 
 | |
| static int prec[TOKCOUNT] =
 | |
| {
 | |
|      1, 137,  2,  2,   2,
 | |
|      2,   2,  2,  4,   5,
 | |
|      6,   8,  8,  8,   9,
 | |
|      9,   3,  3, 10,  10,
 | |
|     10,  10, 11, 11,  12,
 | |
|     13,  13, 14, 15,  16,
 | |
|     16,  16, 16, 16,  16,
 | |
|     16,  16, 16, 16,  16,
 | |
|     16,  16, 16, 17, 200,
 | |
|      2,   2,  0,  0,   7,
 | |
|      0,  16, 0
 | |
| };
 | |
| 
 | |
| #define TOPPREC 18
 | |
| #define ARGPREC 16
 | |
| 
 | |
| static int type[TOKCOUNT] =
 | |
| {
 | |
| /*  0 */  LR, LR|OP_OP|OP_OPF, RL, RL, RL|OP_OP|OP_OPF,
 | |
| /*  5 */  RL|OP_OP|OP_OPF, RL, RL, LR|OP_A2IO, LR|OP_A2IO,
 | |
| /* 10 */  LR|OP_A2IO, LR|OP_A2, LR|OP_A2, LR|OP_A2IO, LR|OP_A2,
 | |
| /* 15 */  LR|OP_A2, LR|OP_A2IO, LR|OP_A2IO, LR|OP_A2IR, LR|OP_A2IR,
 | |
| /* 20 */  LR|OP_A2IR, LR|OP_A2IR, LR|OP_A2IR, LR|OP_A2IR, BOOL|OP_A2IO,
 | |
| /* 25 */  BOOL|OP_A2IO, LR|OP_A2IO, RL|OP_OP, RL|OP_OP, RL|OP_E2,
 | |
| /* 30 */  RL|OP_E2, RL|OP_E2, RL|OP_E2, RL|OP_E2, RL|OP_E2IO,
 | |
| /* 35 */  RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO,
 | |
| /* 40 */  BOOL|OP_E2IO, BOOL|OP_E2IO, RL|OP_A2IO, RL|OP_A2, RL|OP_OP,
 | |
| /* 45 */  RL, RL, LR|OP_OPF, LR|OP_OPF, RL|OP_A2,
 | |
| /* 50 */  LR|OP_OPF, RL|OP_E2, LR|OP_OPF
 | |
| };
 | |
| 
 | |
| static int
 | |
| lexconstant(void)
 | |
| {
 | |
| #ifdef USE_LOCALE
 | |
|     char *prev_locale;
 | |
| #endif
 | |
|     char *nptr;
 | |
| 
 | |
|     nptr = ptr;
 | |
|     if (*nptr == '-')
 | |
| 	nptr++;
 | |
| 
 | |
|     if (*nptr == '0')
 | |
|     {
 | |
| 	nptr++;
 | |
| 	if (*nptr == 'x' || *nptr == 'X') {
 | |
| 	    /* Let zstrtol parse number with base */
 | |
| 	    yyval.u.l = zstrtol(ptr, &ptr, 0);
 | |
| 	    /* Should we set lastbase here? */
 | |
| 	    lastbase = 16;
 | |
| 	    return NUM;
 | |
| 	}
 | |
| 	else if (isset(OCTALZEROES) &&
 | |
| 		 (memchr(nptr, '.', strlen(nptr)) == NULL) &&
 | |
| 		 idigit(*nptr)) {
 | |
| 	    yyval.u.l = zstrtol(ptr, &ptr, 0);
 | |
| 	    lastbase = 8;
 | |
| 	    return NUM;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     while (idigit(*nptr))
 | |
| 	nptr++;
 | |
| 
 | |
|     if (*nptr == '.' || *nptr == 'e' || *nptr == 'E') {
 | |
| 	/* it's a float */
 | |
| 	yyval.type = MN_FLOAT;
 | |
| #ifdef USE_LOCALE
 | |
| 	prev_locale = dupstring(setlocale(LC_NUMERIC, NULL));
 | |
| 	setlocale(LC_NUMERIC, "POSIX");
 | |
| #endif
 | |
| 	yyval.u.d = strtod(ptr, &nptr);
 | |
| #ifdef USE_LOCALE
 | |
| 	if (prev_locale) setlocale(LC_NUMERIC, prev_locale);
 | |
| #endif
 | |
| 	if (ptr == nptr || *nptr == '.') {
 | |
| 	    zerr("bad floating point constant", NULL, 0);
 | |
| 	    return EOI;
 | |
| 	}
 | |
| 	ptr = nptr;
 | |
|     } else {
 | |
| 	/* it's an integer */
 | |
| 	yyval.u.l = zstrtol(ptr, &ptr, 10);
 | |
| 
 | |
| 	if (*ptr == '#') {
 | |
| 	    ptr++;
 | |
| 	    yyval.u.l = zstrtol(ptr, &ptr, lastbase = yyval.u.l);
 | |
| 	}
 | |
|     }
 | |
|     return NUM;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int outputradix;
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| zzlex(void)
 | |
| {
 | |
|     int cct = 0;
 | |
|     yyval.type = MN_INTEGER;
 | |
| 
 | |
|     for (;; cct = 0)
 | |
| 	switch (*ptr++) {
 | |
| 	case '+':
 | |
| 	    if (*ptr == '+' && (unary || !ialnum(*ptr))) {
 | |
| 		ptr++;
 | |
| 		return (unary) ? PREPLUS : POSTPLUS;
 | |
| 	    }
 | |
| 	    if (*ptr == '=') {
 | |
| 		ptr++;
 | |
| 		return PLUSEQ;
 | |
| 	    }
 | |
| 	    return (unary) ? UPLUS : PLUS;
 | |
| 	case '-':
 | |
| 	    if (*ptr == '-' && (unary || !ialnum(*ptr))) {
 | |
| 		ptr++;
 | |
| 		return (unary) ? PREMINUS : POSTMINUS;
 | |
| 	    }
 | |
| 	    if (*ptr == '=') {
 | |
| 		ptr++;
 | |
| 		return MINUSEQ;
 | |
| 	    }
 | |
| 	    if (unary) {
 | |
| 		if (idigit(*ptr) || *ptr == '.') {
 | |
| 		    ptr--;
 | |
| 		    return lexconstant();
 | |
| 		} else
 | |
| 		    return UMINUS;
 | |
| 	    } else
 | |
| 		return MINUS;
 | |
| 	case '(':
 | |
| 	    return M_INPAR;
 | |
| 	case ')':
 | |
| 	    return M_OUTPAR;
 | |
| 	case '!':
 | |
| 	    if (*ptr == '=') {
 | |
| 		ptr++;
 | |
| 		return NEQ;
 | |
| 	    }
 | |
| 	    return NOT;
 | |
| 	case '~':
 | |
| 	    return COMP;
 | |
| 	case '&':
 | |
| 	    if (*ptr == '&') {
 | |
| 		if (*++ptr == '=') {
 | |
| 		    ptr++;
 | |
| 		    return DANDEQ;
 | |
| 		}
 | |
| 		return DAND;
 | |
| 	    } else if (*ptr == '=') {
 | |
| 		ptr++;
 | |
| 		return ANDEQ;
 | |
| 	    }
 | |
| 	    return AND;
 | |
| 	case '|':
 | |
| 	    if (*ptr == '|') {
 | |
| 		if (*++ptr == '=') {
 | |
| 		    ptr++;
 | |
| 		    return DOREQ;
 | |
| 		}
 | |
| 		return DOR;
 | |
| 	    } else if (*ptr == '=') {
 | |
| 		ptr++;
 | |
| 		return OREQ;
 | |
| 	    }
 | |
| 	    return OR;
 | |
| 	case '^':
 | |
| 	    if (*ptr == '^') {
 | |
| 		if (*++ptr == '=') {
 | |
| 		    ptr++;
 | |
| 		    return DXOREQ;
 | |
| 		}
 | |
| 		return DXOR;
 | |
| 	    } else if (*ptr == '=') {
 | |
| 		ptr++;
 | |
| 		return XOREQ;
 | |
| 	    }
 | |
| 	    return XOR;
 | |
| 	case '*':
 | |
| 	    if (*ptr == '*') {
 | |
| 		if (*++ptr == '=') {
 | |
| 		    ptr++;
 | |
| 		    return POWEREQ;
 | |
| 		}
 | |
| 		return POWER;
 | |
| 	    }
 | |
| 	    if (*ptr == '=') {
 | |
| 		ptr++;
 | |
| 		return MULEQ;
 | |
| 	    }
 | |
| 	    return MUL;
 | |
| 	case '/':
 | |
| 	    if (*ptr == '=') {
 | |
| 		ptr++;
 | |
| 		return DIVEQ;
 | |
| 	    }
 | |
| 	    return DIV;
 | |
| 	case '%':
 | |
| 	    if (*ptr == '=') {
 | |
| 		ptr++;
 | |
| 		return MODEQ;
 | |
| 	    }
 | |
| 	    return MOD;
 | |
| 	case '<':
 | |
| 	    if (*ptr == '<') {
 | |
| 		if (*++ptr == '=') {
 | |
| 		    ptr++;
 | |
| 		    return SHLEFTEQ;
 | |
| 		}
 | |
| 		return SHLEFT;
 | |
| 	    } else if (*ptr == '=') {
 | |
| 		ptr++;
 | |
| 		return LEQ;
 | |
| 	    }
 | |
| 	    return LES;
 | |
| 	case '>':
 | |
| 	    if (*ptr == '>') {
 | |
| 		if (*++ptr == '=') {
 | |
| 		    ptr++;
 | |
| 		    return SHRIGHTEQ;
 | |
| 		}
 | |
| 		return SHRIGHT;
 | |
| 	    } else if (*ptr == '=') {
 | |
| 		ptr++;
 | |
| 		return GEQ;
 | |
| 	    }
 | |
| 	    return GRE;
 | |
| 	case '=':
 | |
| 	    if (*ptr == '=') {
 | |
| 		ptr++;
 | |
| 		return DEQ;
 | |
| 	    }
 | |
| 	    return EQ;
 | |
| 	case '$':
 | |
| 	    yyval.u.l = mypid;
 | |
| 	    return NUM;
 | |
| 	case '?':
 | |
| 	    if (unary) {
 | |
| 		yyval.u.l = lastval;
 | |
| 		return NUM;
 | |
| 	    }
 | |
| 	    return QUEST;
 | |
| 	case ':':
 | |
| 	    return COLON;
 | |
| 	case ',':
 | |
| 	    return COMMA;
 | |
| 	case '\0':
 | |
| 	    ptr--;
 | |
| 	    return EOI;
 | |
| 	case '[':
 | |
| 	    {
 | |
| 		int n;
 | |
| 
 | |
| 		if (idigit(*ptr)) {
 | |
| 		    n = zstrtol(ptr, &ptr, 10);
 | |
| 		    if (*ptr != ']' || !idigit(*++ptr)) {
 | |
| 			zerr("bad base syntax", NULL, 0);
 | |
| 			return EOI;
 | |
| 		    }
 | |
| 		    yyval.u.l = zstrtol(ptr, &ptr, lastbase = n);
 | |
| 		    return NUM;
 | |
| 		}
 | |
| 		if (*ptr == '#') {
 | |
| 		    n = 1;
 | |
| 		    if (*++ptr == '#') {
 | |
| 			n = -1;
 | |
| 			ptr++;
 | |
| 		    }
 | |
| 		    if (!idigit(*ptr))
 | |
| 			goto bofs;
 | |
| 		    outputradix = n * zstrtol(ptr, &ptr, 10);
 | |
| 		} else {
 | |
| 		    bofs:
 | |
| 		    zerr("bad output format specification", NULL, 0);
 | |
| 		    return EOI;
 | |
| 		}
 | |
| 		if(*ptr != ']')
 | |
| 			goto bofs;
 | |
| 		ptr++;
 | |
| 		break;
 | |
| 	    }
 | |
| 	case ' ':
 | |
| 	case '\t':
 | |
| 	case '\n':
 | |
| 	    break;
 | |
| 	/* Fall through! */
 | |
| 	default:
 | |
| 	    if (idigit(*--ptr) || *ptr == '.')
 | |
| 		return lexconstant();
 | |
| 	    if (*ptr == '#') {
 | |
| 		if (*++ptr == '\\' || *ptr == '#') {
 | |
| 		    int v;
 | |
| 
 | |
| 		    ptr++;
 | |
| 		    if (!*ptr) {
 | |
| 			zerr("character missing after ##", NULL, 0);
 | |
| 			return EOI;
 | |
| 		    }
 | |
| 		    ptr = getkeystring(ptr, NULL, 6, &v);
 | |
| 		    yyval.u.l = v;
 | |
| 		    return NUM;
 | |
| 		}
 | |
| 		cct = 1;
 | |
| 	    }
 | |
| 	    if (iident(*ptr)) {
 | |
| 		int func = 0;
 | |
| 		char *p;
 | |
| 
 | |
| 		p = ptr;
 | |
| 		while (iident(*++ptr));
 | |
| 		if (*ptr == '[' || (!cct && *ptr == '(')) {
 | |
| 		    char op = *ptr, cp = ((*ptr == '[') ? ']' : ')');
 | |
| 		    int l;
 | |
| 		    func = (op == '(');
 | |
| 		    for (ptr++, l = 1; *ptr && l; ptr++) {
 | |
| 			if (*ptr == op)
 | |
| 			    l++;
 | |
| 			if (*ptr == cp)
 | |
| 			    l--;
 | |
| 			if (*ptr == '\\' && ptr[1])
 | |
| 			    ptr++;
 | |
| 		    }
 | |
| 		}
 | |
| 		yylval = dupstrpfx(p, ptr - p);
 | |
| 		return (func ? FUNC : (cct ? CID : ID));
 | |
| 	    }
 | |
| 	    else if (cct) {
 | |
| 		yyval.u.l = poundgetfn(NULL);
 | |
| 		return NUM;
 | |
| 	    }
 | |
| 	    return EOI;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* the value stack */
 | |
| 
 | |
| #define STACKSZ 100
 | |
| static int mtok;			/* last token */
 | |
| static int sp = -1;			/* stack pointer */
 | |
| 
 | |
| struct mathvalue {
 | |
|     char *lval;
 | |
|     mnumber val;
 | |
| };
 | |
| 
 | |
| static struct mathvalue *stack;
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| push(mnumber val, char *lval, int getme)
 | |
| {
 | |
|     if (sp == STACKSZ - 1)
 | |
| 	zerr("stack overflow", NULL, 0);
 | |
|     else
 | |
| 	sp++;
 | |
|     stack[sp].val = val;
 | |
|     stack[sp].lval = lval;
 | |
|     if (getme)
 | |
| 	stack[sp].val.type = MN_UNSET;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static mnumber
 | |
| pop(int noget)
 | |
| {
 | |
|     struct mathvalue *mv = stack+sp;
 | |
| 
 | |
|     if (mv->val.type == MN_UNSET && !noget)
 | |
| 	mv->val = getnparam(mv->lval);
 | |
|     sp--;
 | |
|     return errflag ? zero_mnumber : mv->val;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static mnumber
 | |
| getcvar(char *s)
 | |
| {
 | |
|     char *t;
 | |
|     mnumber mn;
 | |
|     mn.type = MN_INTEGER;
 | |
| 
 | |
|     queue_signals();
 | |
|     if (!(t = getsparam(s)))
 | |
| 	mn.u.l = 0;
 | |
|     else
 | |
|         mn.u.l = STOUC(*t == Meta ? t[1] ^ 32 : *t);
 | |
|     unqueue_signals();
 | |
|     return mn;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**/
 | |
| static mnumber
 | |
| setvar(char *s, mnumber v)
 | |
| {
 | |
|     if (!s) {
 | |
| 	zerr("lvalue required", NULL, 0);
 | |
| 	v.type = MN_INTEGER;
 | |
| 	v.u.l = 0;
 | |
| 	return v;
 | |
|     }
 | |
|     if (noeval)
 | |
| 	return v;
 | |
|     untokenize(s);
 | |
|     setnparam(s, v);
 | |
|     return v;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**/
 | |
| static mnumber
 | |
| callmathfunc(char *o)
 | |
| {
 | |
|     MathFunc f;
 | |
|     char *a, *n;
 | |
|     static mnumber dummy;
 | |
| 
 | |
|     n = a = dupstring(o);
 | |
| 
 | |
|     while (*a != '(')
 | |
| 	a++;
 | |
|     *a++ = '\0';
 | |
|     a[strlen(a) - 1] = '\0';
 | |
| 
 | |
|     if ((f = getmathfunc(n, 1))) {
 | |
| 	if (f->flags & MFF_STR)
 | |
| 	    return f->sfunc(n, a, f->funcid);
 | |
| 	else {
 | |
| 	    int argc = 0;
 | |
| 	    mnumber *argv = NULL, *q;
 | |
| 	    LinkList l = newlinklist();
 | |
| 	    LinkNode node;
 | |
| 
 | |
| 	    while (iblank(*a))
 | |
| 		a++;
 | |
| 	    while (*a) {
 | |
| 		if (*a) {
 | |
| 		    argc++;
 | |
|  		    q = (mnumber *) zhalloc(sizeof(mnumber));
 | |
| 		    *q = mathevall(a, ARGPREC, &a);
 | |
| 		    addlinknode(l, q);
 | |
| 		    if (errflag || mtok != COMMA)
 | |
| 			break;
 | |
| 		}
 | |
| 	    }
 | |
| 	    if (*a && !errflag)
 | |
| 		zerr("bad math expression: illegal character: %c",
 | |
| 		     NULL, *a);
 | |
| 	    if (!errflag) {
 | |
| 		if (argc >= f->minargs && (f->maxargs < 0 ||
 | |
| 					   argc <= f->maxargs)) {
 | |
| 		    if (argc) {
 | |
| 			q = argv = (mnumber *)zhalloc(argc * sizeof(mnumber));
 | |
| 			for (node = firstnode(l); node; incnode(node))
 | |
| 			    *q++ = *(mnumber *)getdata(node);
 | |
| 		    }
 | |
| 		    return f->nfunc(n, argc, argv, f->funcid);
 | |
| 		} else
 | |
| 		    zerr("wrong number of arguments: %s", o, 0);
 | |
| 	    }
 | |
| 	}
 | |
|     } else
 | |
| 	zerr("unknown function: %s", n, 0);
 | |
| 
 | |
|     dummy.type = MN_INTEGER;
 | |
|     dummy.u.l = 0;
 | |
| 
 | |
|     return dummy;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| notzero(mnumber a)
 | |
| {
 | |
|     if ((a.type & MN_INTEGER) ? a.u.l == 0 : a.u.d == 0.0) {
 | |
| 	zerr("division by zero", NULL, 0);
 | |
| 	return 0;
 | |
|     }
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| /* macro to pop three values off the value stack */
 | |
| 
 | |
| /**/
 | |
| void
 | |
| op(int what)
 | |
| {
 | |
|     mnumber a, b, c, *spval;
 | |
|     char *lv;
 | |
|     int tp = type[what];
 | |
| 
 | |
|     if (errflag)
 | |
| 	return;
 | |
|     if (sp < 0) {
 | |
| 	zerr("bad math expression: stack empty", NULL, 0);
 | |
| 	return;
 | |
|     }
 | |
| 
 | |
|     if (tp & (OP_A2|OP_A2IR|OP_A2IO|OP_E2|OP_E2IO)) {
 | |
| 	/* Make sure anyone seeing this message reports it. */
 | |
| 	DPUTS(sp < 1, "BUG: math: not enough wallabies in outback.");
 | |
| 	b = pop(0);
 | |
| 	a = pop(what == EQ);
 | |
| 	if (errflag)
 | |
| 	    return;
 | |
| 
 | |
| 	if (tp & (OP_A2IO|OP_E2IO)) {
 | |
| 	    /* coerce to integers */
 | |
| 	    if (a.type & MN_FLOAT) {
 | |
| 		a.type = MN_INTEGER;
 | |
| 		a.u.l = (zlong)a.u.d;
 | |
| 	    }
 | |
| 	    if (b.type & MN_FLOAT) {
 | |
| 		b.type = MN_INTEGER;
 | |
| 		b.u.l = (zlong)b.u.d;
 | |
| 	    }
 | |
| 	} else if (a.type != b.type && what != COMMA &&
 | |
| 		   (a.type != MN_UNSET || what != EQ)) {
 | |
| 	    /*
 | |
| 	     * Different types, so coerce to float.
 | |
| 	     * It may happen during an assignment that the LHS
 | |
| 	     * variable is actually an integer, but there's still
 | |
| 	     * no harm in doing the arithmetic in floating point;
 | |
| 	     * the assignment will do the correct conversion.
 | |
| 	     * This way, if the parameter is actually a scalar, but
 | |
| 	     * used to contain an integer, we can write a float into it.
 | |
| 	     */
 | |
| 	    if (a.type & MN_INTEGER) {
 | |
| 		a.type = MN_FLOAT;
 | |
| 		a.u.d = (double)a.u.l;
 | |
| 	    }
 | |
| 	    if (b.type & MN_INTEGER) {
 | |
| 		b.type = MN_FLOAT;
 | |
| 		b.u.d = (double)b.u.l;
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
| 	if (noeval) {
 | |
| 	    c.type = MN_INTEGER;
 | |
| 	    c.u.l = 0;
 | |
| 	} else {
 | |
| 	    /*
 | |
| 	     * type for operation: usually same as operands, but e.g.
 | |
| 	     * (a == b) returns int.
 | |
| 	     */
 | |
| 	    c.type = (tp & OP_A2IR) ? MN_INTEGER : a.type;
 | |
| 
 | |
| 	    switch(what) {
 | |
| 	    case AND:
 | |
| 	    case ANDEQ:
 | |
| 		c.u.l = a.u.l & b.u.l;
 | |
| 		break;
 | |
| 	    case XOR:
 | |
| 	    case XOREQ:
 | |
| 		c.u.l = a.u.l ^ b.u.l;
 | |
| 		break;
 | |
| 	    case OR:
 | |
| 	    case OREQ:
 | |
| 		c.u.l = a.u.l | b.u.l;
 | |
| 		break;
 | |
| 	    case MUL:
 | |
| 	    case MULEQ:
 | |
| 		if (c.type == MN_FLOAT)
 | |
| 		    c.u.d = a.u.d * b.u.d;
 | |
| 		else
 | |
| 		    c.u.l = a.u.l * b.u.l;
 | |
| 		break;
 | |
| 	    case DIV:
 | |
| 	    case DIVEQ:
 | |
| 		if (!notzero(b))
 | |
| 		    return;
 | |
| 		if (c.type == MN_FLOAT)
 | |
| 		    c.u.d = a.u.d / b.u.d;
 | |
| 		else
 | |
| 		    c.u.l = a.u.l / b.u.l;
 | |
| 		break;
 | |
| 	    case MOD:
 | |
| 	    case MODEQ:
 | |
| 		if (!notzero(b))
 | |
| 		    return;
 | |
| 		c.u.l = a.u.l % b.u.l;
 | |
| 		break;
 | |
| 	    case PLUS:
 | |
| 	    case PLUSEQ:
 | |
| 		if (c.type == MN_FLOAT)
 | |
| 		    c.u.d = a.u.d + b.u.d;
 | |
| 		else
 | |
| 		    c.u.l = a.u.l + b.u.l;
 | |
| 		break;
 | |
| 	    case MINUS:
 | |
| 	    case MINUSEQ:
 | |
| 		if (c.type == MN_FLOAT)
 | |
| 		    c.u.d = a.u.d - b.u.d;
 | |
| 		else
 | |
| 		    c.u.l = a.u.l - b.u.l;
 | |
| 		break;
 | |
| 	    case SHLEFT:
 | |
| 	    case SHLEFTEQ:
 | |
| 		c.u.l = a.u.l << b.u.l;
 | |
| 		break;
 | |
| 	    case SHRIGHT:
 | |
| 	    case SHRIGHTEQ:
 | |
| 		c.u.l = a.u.l >> b.u.l;
 | |
| 		break;
 | |
| 	    case LES:
 | |
| 		c.u.l = (zlong)
 | |
| 		    (a.type == MN_FLOAT ? (a.u.d < b.u.d) : (a.u.l < b.u.l));
 | |
| 		break;
 | |
| 	    case LEQ:
 | |
| 		c.u.l = (zlong)
 | |
| 		    (a.type == MN_FLOAT ? (a.u.d <= b.u.d) : (a.u.l <= b.u.l));
 | |
| 		break;
 | |
| 	    case GRE:
 | |
| 		c.u.l = (zlong)
 | |
| 		    (a.type == MN_FLOAT ? (a.u.d > b.u.d) : (a.u.l > b.u.l));
 | |
| 		break;
 | |
| 	    case GEQ:
 | |
| 		c.u.l = (zlong)
 | |
| 		    (a.type == MN_FLOAT ? (a.u.d >= b.u.d) : (a.u.l >= b.u.l));
 | |
| 		break;
 | |
| 	    case DEQ:
 | |
| 		c.u.l = (zlong)
 | |
| 		    (a.type == MN_FLOAT ? (a.u.d == b.u.d) : (a.u.l == b.u.l));
 | |
| 		break;
 | |
| 	    case NEQ:
 | |
| 		c.u.l = (zlong)
 | |
| 		    (a.type == MN_FLOAT ? (a.u.d != b.u.d) : (a.u.l != b.u.l));
 | |
| 		break;
 | |
| 	    case DAND:
 | |
| 	    case DANDEQ:
 | |
| 		c.u.l = (zlong)(a.u.l && b.u.l);
 | |
| 		break;
 | |
| 	    case DOR:
 | |
| 	    case DOREQ:
 | |
| 		c.u.l = (zlong)(a.u.l || b.u.l);
 | |
| 		break;
 | |
| 	    case DXOR:
 | |
| 	    case DXOREQ:
 | |
| 		c.u.l = (zlong)((a.u.l && !b.u.l) || (!a.u.l && b.u.l));
 | |
| 		break;
 | |
| 	    case COMMA:
 | |
| 		c = b;
 | |
| 		break;
 | |
| 	    case POWER:
 | |
| 	    case POWEREQ:
 | |
| 		if (c.type == MN_INTEGER && b.u.l < 0) {
 | |
| 		    /* produces a real result, so cast to real. */
 | |
| 		    a.type = b.type = c.type = MN_FLOAT;
 | |
| 		    a.u.d = (double) a.u.l;
 | |
| 		    b.u.d = (double) b.u.l;
 | |
| 		}
 | |
| 		if (c.type == MN_INTEGER) {
 | |
| 		    for (c.u.l = 1; b.u.l--; c.u.l *= a.u.l);
 | |
| 		} else {
 | |
| 		    if (b.u.d <= 0 && !notzero(a))
 | |
| 			return;
 | |
| 		    if (a.u.d < 0) {
 | |
| 			/* Error if (-num ** b) and b is not an integer */
 | |
| 			double tst = (double)(zlong)b.u.d;
 | |
| 			if (tst != b.u.d) {
 | |
| 			    zerr("imaginary power", NULL, 0);
 | |
| 			    return;
 | |
| 			}
 | |
| 		    }
 | |
| 		    c.u.d = pow(a.u.d, b.u.d);
 | |
| 		}
 | |
| 		break;
 | |
| 	    case EQ:
 | |
| 		c = b;
 | |
| 		break;
 | |
| 	    }
 | |
| 	}
 | |
| 	if (tp & (OP_E2|OP_E2IO)) {
 | |
| 	    lv = stack[sp+1].lval;
 | |
| 	    push(setvar(lv,c), lv, 0);
 | |
| 	} else
 | |
| 	    push(c,NULL, 0);
 | |
| 	return;
 | |
|     }
 | |
| 
 | |
|     spval = &stack[sp].val;
 | |
|     if (stack[sp].val.type == MN_UNSET)
 | |
| 	*spval = getnparam(stack[sp].lval);
 | |
|     switch (what) {
 | |
|     case NOT:
 | |
| 	if (spval->type & MN_FLOAT) {
 | |
| 	    spval->u.l = !spval->u.d;
 | |
| 	    spval->type = MN_INTEGER;
 | |
| 	} else
 | |
| 	    spval->u.l = !spval->u.l;
 | |
| 	stack[sp].lval = NULL;
 | |
| 	break;
 | |
|     case COMP:
 | |
| 	if (spval->type & MN_FLOAT) {
 | |
| 	    spval->u.l = ~((zlong)spval->u.d);
 | |
| 	    spval->type = MN_INTEGER;
 | |
| 	} else
 | |
| 	    spval->u.l = ~spval->u.l;
 | |
| 	stack[sp].lval = NULL;
 | |
| 	break;
 | |
|     case POSTPLUS:
 | |
| 	a = *spval;
 | |
| 	if (spval->type & MN_FLOAT)
 | |
| 	    a.u.d++;
 | |
| 	else
 | |
| 	    a.u.l++;
 | |
| 	(void)setvar(stack[sp].lval, a);
 | |
| 	break;
 | |
|     case POSTMINUS:
 | |
| 	a = *spval;
 | |
| 	if (spval->type & MN_FLOAT)
 | |
| 	    a.u.d--;
 | |
| 	else
 | |
| 	    a.u.l--;
 | |
| 	(void)setvar(stack[sp].lval, a);
 | |
| 	break;
 | |
|     case UPLUS:
 | |
| 	stack[sp].lval = NULL;
 | |
| 	break;
 | |
|     case UMINUS:
 | |
| 	if (spval->type & MN_FLOAT)
 | |
| 	    spval->u.d = -spval->u.d;
 | |
| 	else
 | |
| 	    spval->u.l = -spval->u.l;
 | |
| 	stack[sp].lval = NULL;
 | |
| 	break;
 | |
|     case QUEST:
 | |
| 	DPUTS(sp < 2, "BUG: math: three shall be the number of the counting.");
 | |
| 	c = pop(0);
 | |
| 	b = pop(0);
 | |
| 	a = pop(0);
 | |
| 	if (errflag)
 | |
| 	    return;
 | |
| 	/* b and c can stay different types in this case. */
 | |
| 	push(((a.type & MN_FLOAT) ? a.u.d : a.u.l) ? b : c, NULL, 0);
 | |
| 	break;
 | |
|     case COLON:
 | |
| 	zerr("':' without '?'", NULL, 0);
 | |
| 	break;
 | |
|     case PREPLUS:
 | |
| 	if (spval->type & MN_FLOAT)
 | |
| 	    spval->u.d++;
 | |
| 	else
 | |
| 	    spval->u.l++;
 | |
| 	setvar(stack[sp].lval, *spval);
 | |
| 	break;
 | |
|     case PREMINUS:
 | |
| 	if (spval->type & MN_FLOAT)
 | |
| 	    spval->u.d--;
 | |
| 	else
 | |
| 	    spval->u.l--;
 | |
| 	setvar(stack[sp].lval, *spval);
 | |
| 	break;
 | |
|     default:
 | |
| 	zerr("out of integers", NULL, 0);
 | |
| 	return;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| bop(int tk)
 | |
| {
 | |
|     mnumber *spval = &stack[sp].val;
 | |
|     int tst;
 | |
| 
 | |
|     if (stack[sp].val.type == MN_UNSET)
 | |
| 	*spval = getnparam(stack[sp].lval);
 | |
|     tst = (spval->type & MN_FLOAT) ? (zlong)spval->u.d : spval->u.l; 
 | |
| 
 | |
|     switch (tk) {
 | |
|     case DAND:
 | |
|     case DANDEQ:
 | |
| 	if (!tst)
 | |
| 	    noeval++;
 | |
| 	break;
 | |
|     case DOR:
 | |
|     case DOREQ:
 | |
| 	if (tst)
 | |
| 	    noeval++;
 | |
| 	break;
 | |
|     };
 | |
| }
 | |
| 
 | |
| 
 | |
| /**/
 | |
| static mnumber
 | |
| mathevall(char *s, int prek, char **ep)
 | |
| {
 | |
|     int xlastbase, xnoeval, xunary;
 | |
|     char *xptr;
 | |
|     mnumber xyyval;
 | |
|     char *xyylval;
 | |
|     int xsp;
 | |
|     struct mathvalue *xstack = 0, nstack[STACKSZ];
 | |
|     mnumber ret;
 | |
| 
 | |
|     if (mlevel >= MAX_MLEVEL) {
 | |
| 	xyyval.type = MN_INTEGER;
 | |
| 	xyyval.u.l = 0;
 | |
| 
 | |
| 	zerr("math recursion limit exceeded", NULL, 0);
 | |
| 
 | |
| 	return xyyval;
 | |
|     }
 | |
|     if (mlevel++) {
 | |
| 	xlastbase = lastbase;
 | |
| 	xnoeval = noeval;
 | |
| 	xunary = unary;
 | |
| 	xptr = ptr;
 | |
| 	xyyval = yyval;
 | |
| 	xyylval = yylval;
 | |
| 
 | |
| 	xsp = sp;
 | |
| 	xstack = stack;
 | |
|     } else {
 | |
| 	xlastbase = xnoeval = xunary = xsp = 0;
 | |
| 	xyyval.type = MN_INTEGER;
 | |
| 	xyyval.u.l = 0;
 | |
| 	xyylval = NULL;
 | |
| 	xptr = NULL;
 | |
|     }
 | |
|     stack = nstack;
 | |
|     lastbase = -1;
 | |
|     ptr = s;
 | |
|     sp = -1;
 | |
|     unary = 1;
 | |
|     stack[0].val.type = MN_INTEGER;
 | |
|     stack[0].val.u.l = 0;
 | |
|     mathparse(prek);
 | |
|     *ep = ptr;
 | |
|     DPUTS(!errflag && sp > 0,
 | |
| 	  "BUG: math: wallabies roaming too freely in outback");
 | |
| 
 | |
|     if (errflag) {
 | |
| 	ret.type = MN_INTEGER;
 | |
| 	ret.u.l = errflag;
 | |
|     } else {
 | |
| 	if (stack[0].val.type == MN_UNSET)
 | |
| 	    ret = getnparam(stack[0].lval);
 | |
| 	else
 | |
| 	    ret = stack[0].val;
 | |
|     }
 | |
| 
 | |
|     if (--mlevel) {
 | |
| 	lastbase = xlastbase;
 | |
| 	noeval = xnoeval;
 | |
| 	unary = xunary;
 | |
| 	ptr = xptr;
 | |
| 	yyval = xyyval;
 | |
| 	yylval = xyylval;
 | |
| 
 | |
| 	sp = xsp;
 | |
| 	stack = xstack;
 | |
|     }
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**/
 | |
| mod_export mnumber
 | |
| matheval(char *s)
 | |
| {
 | |
|     char *junk;
 | |
|     mnumber x;
 | |
|     int xmtok = mtok;
 | |
|     /* maintain outputradix across levels of evaluation */
 | |
|     if (!mlevel)
 | |
| 	outputradix = 0;
 | |
| 
 | |
|     if (!*s) {
 | |
| 	x.type = MN_INTEGER;
 | |
| 	x.u.l = 0;
 | |
| 	return x;
 | |
|     }
 | |
|     x = mathevall(s, TOPPREC, &junk);
 | |
|     mtok = xmtok;
 | |
|     if (*junk)
 | |
| 	zerr("bad math expression: illegal character: %c", NULL, *junk);
 | |
|     return x;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| mod_export zlong
 | |
| mathevali(char *s)
 | |
| {
 | |
|     mnumber x = matheval(s);
 | |
|     return (x.type & MN_FLOAT) ? (zlong)x.u.d : x.u.l;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**/
 | |
| zlong
 | |
| mathevalarg(char *s, char **ss)
 | |
| {
 | |
|     mnumber x;
 | |
|     int xmtok = mtok;
 | |
| 
 | |
|     x = mathevall(s, ARGPREC, ss);
 | |
|     if (mtok == COMMA)
 | |
| 	(*ss)--;
 | |
|     mtok = xmtok;
 | |
|     return (x.type & MN_FLOAT) ? (zlong)x.u.d : x.u.l;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Make sure we have an operator or an operand, whatever is expected.
 | |
|  * For this purpose, unary operators constitute part of an operand.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| checkunary(int mtokc, char *mptr)
 | |
| {
 | |
|     int errmsg = 0;
 | |
|     int tp = type[mtokc];
 | |
|     if (tp & (OP_A2|OP_A2IR|OP_A2IO|OP_E2|OP_E2IO|OP_OP)) {
 | |
| 	if (unary)
 | |
| 	    errmsg = 1;
 | |
|     } else {
 | |
| 	if (!unary)
 | |
| 	    errmsg = 2;
 | |
|     }
 | |
|     if (errmsg) {
 | |
| 	char errbuf[80];
 | |
| 	int len, over = 0;
 | |
| 	while (inblank(*mptr))
 | |
| 	    mptr++;
 | |
| 	len = ztrlen(mptr);
 | |
| 	if (len > 10) {
 | |
| 	    len = 10;
 | |
| 	    over = 1;
 | |
| 	}
 | |
| 	sprintf(errbuf, "bad math expression: %s expected at `%%l%s'",
 | |
| 		errmsg == 2 ? "operator" : "operand",
 | |
| 		over ? "..." : ""); 
 | |
| 	zerr(errbuf, mptr, len);
 | |
|     }
 | |
|     unary = !(tp & OP_OPF);
 | |
| }
 | |
| 
 | |
| /* operator-precedence parse the string and execute */
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| mathparse(int pc)
 | |
| {
 | |
|     zlong q;
 | |
|     int otok, onoeval;
 | |
|     char *optr = ptr;
 | |
| 
 | |
|     if (errflag)
 | |
| 	return;
 | |
|     mtok = zzlex();
 | |
|     /* Handle empty input */
 | |
|     if (pc == TOPPREC && mtok == EOI)
 | |
| 	return;
 | |
|     checkunary(mtok, optr);
 | |
|     while (prec[mtok] <= pc) {
 | |
| 	if (errflag)
 | |
| 	    return;
 | |
| 	switch (mtok) {
 | |
| 	case NUM:
 | |
| 	    push(yyval, NULL, 0);
 | |
| 	    break;
 | |
| 	case ID:
 | |
| 	    push(zero_mnumber, yylval, !noeval);
 | |
| 	    break;
 | |
| 	case CID:
 | |
| 	    push((noeval ? zero_mnumber : getcvar(yylval)), yylval, 0);
 | |
| 	    break;
 | |
| 	case FUNC:
 | |
| 	    push((noeval ? zero_mnumber : callmathfunc(yylval)), yylval, 0);
 | |
| 	    break;
 | |
| 	case M_INPAR:
 | |
| 	    mathparse(TOPPREC);
 | |
| 	    if (mtok != M_OUTPAR) {
 | |
| 		if (!errflag)
 | |
| 		    zerr("')' expected", NULL, 0);
 | |
| 		return;
 | |
| 	    }
 | |
| 	    break;
 | |
| 	case QUEST:
 | |
| 	    if (stack[sp].val.type == MN_UNSET)
 | |
| 		stack[sp].val = getnparam(stack[sp].lval);
 | |
| 	    q = (stack[sp].val.type == MN_FLOAT) ? (zlong)stack[sp].val.u.d :
 | |
| 		stack[sp].val.u.l;
 | |
| 
 | |
| 	    if (!q)
 | |
| 		noeval++;
 | |
| 	    mathparse(prec[COLON] - 1);
 | |
| 	    if (!q)
 | |
| 		noeval--;
 | |
| 	    if (mtok != COLON) {
 | |
| 		if (!errflag)
 | |
| 		    zerr("':' expected", NULL, 0);
 | |
| 		return;
 | |
| 	    }
 | |
| 	    if (q)
 | |
| 		noeval++;
 | |
| 	    mathparse(prec[QUEST]);
 | |
| 	    if (q)
 | |
| 		noeval--;
 | |
| 	    op(QUEST);
 | |
| 	    continue;
 | |
| 	default:
 | |
| 	    otok = mtok;
 | |
| 	    onoeval = noeval;
 | |
| 	    if (MTYPE(type[otok]) == BOOL)
 | |
| 		bop(otok);
 | |
| 	    mathparse(prec[otok] - (MTYPE(type[otok]) != RL));
 | |
| 	    noeval = onoeval;
 | |
| 	    op(otok);
 | |
| 	    continue;
 | |
| 	}
 | |
| 	optr = ptr;
 | |
| 	mtok = zzlex();
 | |
| 	checkunary(mtok, optr);
 | |
|     }
 | |
| }
 |