about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/Modules/mathfunc.c482
-rw-r--r--Src/Modules/mathfunc.mdd3
-rw-r--r--Src/math.c52
3 files changed, 513 insertions, 24 deletions
diff --git a/Src/Modules/mathfunc.c b/Src/Modules/mathfunc.c
new file mode 100644
index 000000000..770894ce8
--- /dev/null
+++ b/Src/Modules/mathfunc.c
@@ -0,0 +1,482 @@
+/*
+ * mathfunc.c - basic mathematical functions for use in math evaluations
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1999 Peter Stephenson
+ * 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 Peter Stephenson 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 Peter Stephenson and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Peter Stephenson 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 Peter Stephenson and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "mathfunc.mdh"
+#include "mathfunc.pro"
+
+#include <math.h>
+
+enum {
+MF_ABS,
+MF_ACOS,
+MF_ACOSH,
+MF_ASIN,
+MF_ASINH,
+MF_ATAN,
+MF_ATANH,
+MF_CBRT,
+MF_CEIL,
+MF_COPYSIGN,
+MF_COS,
+MF_COSH,
+MF_DREM,
+MF_ERF,
+MF_ERFC,
+MF_EXP,
+MF_EXPM1,
+MF_FABS,
+MF_FLOAT,
+MF_FLOOR,
+MF_FMOD,
+MF_GAMMA,
+MF_HYPOT,
+MF_ILOGB,
+MF_INT,
+MF_J0,
+MF_J1,
+MF_JN,
+MF_LDEXP,
+MF_LGAMMA,
+MF_LOG,
+MF_LOG10,
+MF_LOG1P,
+MF_LOGB,
+MF_NEXTAFTER,
+MF_RINT,
+MF_SCALB,
+MF_SIGNGAM,
+MF_SIN,
+MF_SINH,
+MF_SQRT,
+MF_TAN,
+MF_TANH,
+MF_Y0,
+MF_Y1,
+MF_YN,
+};
+
+/*
+ * also to do, but differently argument or returned: abs (no type
+ * conversion), atan2.
+ */
+
+/* Flags for bounds.  Note these must start at 1, not 0. */
+
+enum {
+  BF_POS    = 1,		/* must be positive */
+  BF_NONNEG = 2,		/* must be non-negative */
+  BF_FRAC   = 3,		/* must be -1 <= x <= 1 */
+  BF_GE1    = 4,		/* must be >= 1 */
+  BF_FRACO  = 5,		/* must be in open range -1 < x < 1 */
+  BF_INTPOS = 6,		/* must be non-integer or positive */
+  BF_GTRM1  = 7,		/* must be > -1 */
+  BF_NONZ   = 8,		/* must be nonzero */
+  BF_POS2   = 9			/* second argument must be positive */
+};
+
+#define BFLAG(x) ((x) << 8)
+
+/*
+ * Flags for type of function: unlike the above, these must
+ * be individually bit-testable.
+ */
+
+enum {
+    TF_NOCONV = 1,		/* don't convert to float */
+    TF_INT1   = 2,		/* first argument is integer */
+    TF_INT2   = 4,		/* second argument is integer */
+    TF_NOASS  = 8		/* don't assign result as double */
+};
+
+#define TFLAG(x) ((x) << 16)
+
+
+static struct mathfunc mftab[] = {
+  NUMMATHFUNC("abs", math_func, 1, 1, MF_ABS | BFLAG(BF_FRAC) |
+	      TFLAG(TF_NOCONV|TF_NOASS)),
+  NUMMATHFUNC("acos", math_func, 1, 1, MF_ACOS | BFLAG(BF_FRAC)),
+  NUMMATHFUNC("acosh", math_func, 1, 1, MF_ACOSH | BFLAG(BF_GE1)),
+  NUMMATHFUNC("asin", math_func, 1, 1, MF_ASIN | BFLAG(BF_FRAC)),
+  NUMMATHFUNC("asinh", math_func, 1, 1, MF_ASINH),
+  NUMMATHFUNC("atan", math_func, 1, 2, MF_ATAN),
+  NUMMATHFUNC("atanh", math_func, 1, 1, MF_ATANH | BFLAG(BF_FRACO)),
+  NUMMATHFUNC("cbrt", math_func, 1, 1, MF_CBRT),
+  NUMMATHFUNC("ceil", math_func, 1, 1, MF_CEIL),
+  NUMMATHFUNC("copysign", math_func, 2, 2, MF_COPYSIGN),
+  NUMMATHFUNC("cos", math_func, 1, 1, MF_COS),
+  NUMMATHFUNC("cosh", math_func, 1, 1, MF_COSH),
+  NUMMATHFUNC("drem", math_func, 2, 2, MF_DREM),
+  NUMMATHFUNC("erf", math_func, 1, 1, MF_ERF),
+  NUMMATHFUNC("erfc", math_func, 1, 1, MF_ERFC),
+  NUMMATHFUNC("exp", math_func, 1, 1, MF_EXP),
+  NUMMATHFUNC("expm1", math_func, 1, 1, MF_EXPM1),
+  NUMMATHFUNC("fabs", math_func, 1, 1, MF_FABS),
+  NUMMATHFUNC("float", math_func, 1, 1, MF_FLOAT),
+  NUMMATHFUNC("floor", math_func, 1, 1, MF_FLOOR),
+  NUMMATHFUNC("fmod", math_func, 2, 2, MF_FMOD),
+  NUMMATHFUNC("gamma", math_func, 1, 1, MF_GAMMA | BFLAG(BF_INTPOS)),
+  NUMMATHFUNC("hypot", math_func, 2, 2, MF_HYPOT),
+  NUMMATHFUNC("ilogb", math_func, 1, 1, MF_ILOGB | BFLAG(BF_NONZ) |
+	      TFLAG(TF_NOASS)),
+  NUMMATHFUNC("int", math_func, 1, 1, MF_INT | TFLAG(TF_NOASS)),
+  NUMMATHFUNC("j0", math_func, 1, 1, MF_J0),
+  NUMMATHFUNC("j1", math_func, 1, 1, MF_J1),
+  NUMMATHFUNC("jn", math_func, 2, 2, MF_JN | TFLAG(TF_INT1)),
+  NUMMATHFUNC("ldexp", math_func, 2, 2, MF_LDEXP | TFLAG(TF_INT2)),
+  NUMMATHFUNC("lgamma", math_func, 1, 1, MF_LGAMMA | BFLAG(BF_INTPOS)),
+  NUMMATHFUNC("log", math_func, 1, 1, MF_LOG | BFLAG(BF_POS)),
+  NUMMATHFUNC("log10", math_func, 1, 1, MF_LOG10 | BFLAG(BF_POS)),
+  NUMMATHFUNC("log1p", math_func, 1, 1, MF_LOG1P | BFLAG(BF_GTRM1)),
+  NUMMATHFUNC("logb", math_func, 1, 1, MF_LOGB | BFLAG(BF_NONZ)),
+  NUMMATHFUNC("nextafter", math_func, 2, 2, MF_NEXTAFTER),
+  NUMMATHFUNC("rint", math_func, 1, 1, MF_RINT),
+  NUMMATHFUNC("scalb", math_func, 2, 2, MF_SCALB | TFLAG(TF_INT2)),
+  NUMMATHFUNC("signgam", math_func, 0, 0, MF_SIGNGAM | TFLAG(TF_NOASS)),
+  NUMMATHFUNC("sin", math_func, 1, 1, MF_SIN),
+  NUMMATHFUNC("sinh", math_func, 1, 1, MF_SINH),
+  NUMMATHFUNC("sqrt", math_func, 1, 1, MF_SQRT | BFLAG(BF_NONNEG)),
+  NUMMATHFUNC("tan", math_func, 1, 1, MF_TAN),
+  NUMMATHFUNC("tanh", math_func, 1, 1, MF_TANH),
+  NUMMATHFUNC("y0", math_func, 1, 1, MF_Y0 | BFLAG(BF_POS)),
+  NUMMATHFUNC("y1", math_func, 1, 1, MF_Y1 | BFLAG(BF_POS)),
+  NUMMATHFUNC("yn", math_func, 2, 2, MF_YN | BFLAG(BF_POS2) | TFLAG(TF_INT1))
+};
+
+/**/
+static mnumber
+math_func(char *name, int argc, mnumber *argv, int id)
+{
+  mnumber ret;
+  double argd = 0, argd2 = 0, retd = 0;
+  int argi = 0;
+
+  if (argc && !(id & TFLAG(TF_NOCONV))) {
+      if (id & TFLAG(TF_INT1))
+	  argi = (argv->type == MN_FLOAT) ? (zlong)argv->u.d : argv->u.l;
+      else
+	  argd = (argv->type == MN_INTEGER) ? (double)argv->u.l : argv->u.d;
+      if (argc > 1) {
+	  if (id & TFLAG(TF_INT2))
+	      argi = (argv[1].type == MN_FLOAT) ? (zlong)argv[1].u.d :
+	      argv[1].u.l;
+	  else
+	      argd2 = (argv[1].type == MN_INTEGER) ? (double)argv[1].u.l :
+	      argv[1].u.d;
+      }
+  }
+
+  ret.type = MN_FLOAT;
+  ret.u.d = 0;
+
+  if (errflag)
+    return ret;
+
+  if (id & 0xff00) {
+      int rtst = 0;
+
+      switch ((id >> 8) & 0xff) {
+      case BF_POS:
+	  rtst = (argd <= 0.0);
+	  break;
+	  
+      case BF_NONNEG:
+	  rtst = (argd < 0.0);
+	  break;
+
+      case BF_FRAC:
+	  rtst = (fabs(argd) > 1.0);
+	  break;
+
+      case BF_GE1:
+	  rtst = (argd < 1.0);
+	  break;
+
+      case BF_FRACO:
+	  rtst = (fabs(argd) >= 1.0);
+	  break;
+
+      case BF_INTPOS:
+	  rtst = (argd <= 0 && (double)(zlong)argd == argd);
+	  break;
+
+      case BF_GTRM1:
+	  rtst = (argd <= -1);
+	  break;
+
+      case BF_POS2:
+	  rtst = (argd2 <= 0.0);
+	  break;
+      }
+
+      if (rtst) {
+	  zerr("math: argument to %s out of range", name, 0);
+	  return ret;
+      }
+  }
+
+  switch (id & 0xff) {
+  case MF_ABS:
+      ret.type = argv->type;
+      if (argv->type == MN_INTEGER)
+	  ret.u.l = (argv->u.l < 0) ? - argv->u.l : argv->u.l;
+      else
+	  ret.u.d = fabs(argv->u.d);
+      break;
+
+  case MF_ACOS:
+      retd = acos(argd);
+      break;
+
+  case MF_ACOSH:
+      retd = acosh(argd);
+      break;
+
+  case MF_ASIN:
+      retd = asin(argd);
+      break;
+
+  case MF_ASINH:
+      retd = asinh(argd);
+      break;
+
+  case MF_ATAN:
+      if (argc == 2)
+	  retd = atan2(argd, argd2);
+      else
+	  retd = atan(argd);
+      break;
+
+  case MF_ATANH:
+      retd = atanh(argd);
+      break;
+
+  case MF_CBRT:
+      retd = cbrt(argd);
+      break;
+
+  case MF_CEIL:
+      retd = ceil(argd);
+      break;
+
+  case MF_COPYSIGN:
+      retd = copysign(argd, argd2);
+      break;
+
+  case MF_COS:
+      retd = cos(argd);
+      break;
+
+  case MF_COSH:
+      retd = cosh(argd);
+      break;
+
+  case MF_DREM:
+      retd = drem(argd, argd2);
+      break;
+
+  case MF_ERF:
+      retd = erf(argd);
+      break;
+
+  case MF_ERFC:
+      retd = erfc(argd);
+      break;
+
+  case MF_EXP:
+      retd = exp(argd);
+      break;
+
+  case MF_EXPM1:
+      retd = expm1(argd);
+      break;
+
+  case MF_FABS:
+      retd = fabs(argd);
+      break;
+
+  case MF_FLOAT:
+      retd = argd;
+      break;
+
+  case MF_FLOOR:
+      retd = floor(argd);
+      break;
+
+  case MF_FMOD:
+      retd = fmod(argd, argd2);
+      break;
+
+  case MF_GAMMA:
+      retd = gamma(argd);
+      break;
+
+  case MF_HYPOT:
+      retd = hypot(argd, argd2);
+      break;
+
+  case MF_ILOGB:
+      ret.type = MN_INTEGER;
+      ret.u.l = ilogb(argd); 
+      break;
+
+  case MF_INT:
+      ret.type = MN_INTEGER;
+      ret.u.l = (zlong)argd;
+      break;
+
+  case MF_J0:
+      retd = j0(argd);
+      break;
+
+  case MF_J1:
+      retd = j1(argd);
+      break;
+
+  case MF_JN:
+      retd = jn(argi, argd2);
+      break;
+
+  case MF_LDEXP:
+      retd = ldexp(argd, argi);
+      break;
+
+  case MF_LGAMMA:
+      retd = lgamma(argd);
+      break;
+
+  case MF_LOG:
+      retd = log(argd);
+      break;
+
+  case MF_LOG10:
+      retd = log10(argd);
+      break;
+
+  case MF_LOG1P:
+      retd = log1p(argd);
+      break;
+
+  case MF_LOGB:
+      retd = logb(argd); 
+      break;
+
+  case MF_NEXTAFTER:
+      retd = nextafter(argd, argd2);
+      break;
+
+  case MF_RINT:
+      retd = rint(argd);
+      break;
+
+  case MF_SCALB:
+      retd = scalb(argd, argi);
+      break;
+
+  case MF_SIGNGAM:
+      ret.type = MN_INTEGER;
+      ret.u.l = signgam;
+      break;
+
+  case MF_SIN:
+      retd = sin(argd);
+      break;
+
+  case MF_SINH:
+      retd = sinh(argd);
+      break;
+
+  case MF_SQRT:
+      retd = sqrt(argd);
+      break;
+
+  case MF_TAN:
+      retd = tan(argd);
+      break;
+
+  case MF_TANH:
+      retd = tanh(argd);
+      break;
+
+  case MF_Y0:
+      retd = y0(argd);
+      break;
+
+  case MF_Y1:
+      retd = y1(argd);
+      break;
+
+  case MF_YN:
+      retd = yn(argi, argd2);
+      break;
+
+#ifdef DEBUG
+  default:
+      fprintf(stderr, "BUG: mathfunc type not handled: %d", id);
+      break;
+#endif
+  }
+
+  if (!(id & TFLAG(TF_NOASS)))
+      ret.u.d = retd;
+
+  return ret;
+}
+
+/**/
+int
+setup_mathfunc(Module m)
+{
+    return 0;
+}
+
+/**/
+int
+boot_mathfunc(Module m)
+{
+    return !addmathfuncs(m->nam, mftab, sizeof(mftab)/sizeof(*mftab));
+}
+
+#ifdef MODULE
+
+/**/
+int
+cleanup_mathfunc(Module m)
+{
+    deletemathfuncs(m->nam, mftab, sizeof(mftab)/sizeof(*mftab));
+    return 0;
+}
+
+/**/
+int
+finish_mathfunc(Module m)
+{
+    return 0;
+}
+
+#endif
diff --git a/Src/Modules/mathfunc.mdd b/Src/Modules/mathfunc.mdd
new file mode 100644
index 000000000..33a861f77
--- /dev/null
+++ b/Src/Modules/mathfunc.mdd
@@ -0,0 +1,3 @@
+autobins="mathfunc"
+
+objects="mathfunc.o"
diff --git a/Src/math.c b/Src/math.c
index 836e0c671..9edfeb651 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -161,7 +161,7 @@ static int prec[TOKCOUNT] =
      0,  16, 0
 };
 
-#define TOPPREC 16
+#define TOPPREC 17
 #define ARGPREC (TOPPREC-1)
 
 static int type[TOKCOUNT] =
@@ -498,31 +498,35 @@ callmathfunc(char *o)
 	    mnumber *argv = NULL, *q;
 	    LinkList l = newlinklist();
 	    LinkNode node;
-	    char *p;
-
-	    if (*a) {
-		for (p = a; *a; a++) {
-		    if (*a == '\\' && a[1])
-			a++;
-		    else if (*a == ',') {
-			*a = '\0';
-			addlinknode(l, p);
-			argc++;
-			p = a + 1;
-		    }
+
+	    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;
+		    a++;
 		}
-		addlinknode(l, p);
-		argc++;
 	    }
-	    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++ = matheval((char *) getdata(node));
-		}
-		return f->nfunc(n, argc, argv, f->funcid);
-	    } else
-		zerr("wrong number of argument: %s", o, 0);
+	    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);