aboutsummaryrefslogtreecommitdiff
path: root/sys/fmtio
diff options
context:
space:
mode:
authorJoseph Hunkeler <jhunkeler@gmail.com>2015-07-08 20:46:52 -0400
committerJoseph Hunkeler <jhunkeler@gmail.com>2015-07-08 20:46:52 -0400
commitfa080de7afc95aa1c19a6e6fc0e0708ced2eadc4 (patch)
treebdda434976bc09c864f2e4fa6f16ba1952b1e555 /sys/fmtio
downloadiraf-linux-fa080de7afc95aa1c19a6e6fc0e0708ced2eadc4.tar.gz
Initial commit
Diffstat (limited to 'sys/fmtio')
-rw-r--r--sys/fmtio/README6
-rw-r--r--sys/fmtio/cctoc.x67
-rw-r--r--sys/fmtio/chdeposit.x17
-rw-r--r--sys/fmtio/chfetch.x16
-rw-r--r--sys/fmtio/chrlwr.x16
-rw-r--r--sys/fmtio/chrupr.x16
-rw-r--r--sys/fmtio/clprintf.x17
-rw-r--r--sys/fmtio/clscan.x32
-rw-r--r--sys/fmtio/ctocc.x64
-rw-r--r--sys/fmtio/ctod.x154
-rw-r--r--sys/fmtio/ctoi.x48
-rw-r--r--sys/fmtio/ctol.x52
-rw-r--r--sys/fmtio/ctor.x34
-rw-r--r--sys/fmtio/ctotok.x167
-rw-r--r--sys/fmtio/ctowrd.x83
-rw-r--r--sys/fmtio/ctox.x48
-rw-r--r--sys/fmtio/doc/evexpr.hlp147
-rw-r--r--sys/fmtio/doc/fmtio.hd77
-rw-r--r--sys/fmtio/doc/fmtio.men59
-rw-r--r--sys/fmtio/doc/lexnum.hlp303
-rw-r--r--sys/fmtio/dtcscl.x35
-rw-r--r--sys/fmtio/dtoc.x129
-rw-r--r--sys/fmtio/dtoc3.x285
-rw-r--r--sys/fmtio/eprintf.x14
-rw-r--r--sys/fmtio/escchars.inc5
-rw-r--r--sys/fmtio/evexpr.com7
-rw-r--r--sys/fmtio/evexpr.x1477
-rw-r--r--sys/fmtio/evexpr.y1087
-rw-r--r--sys/fmtio/evvexpr.com12
-rw-r--r--sys/fmtio/evvexpr.gy2680
-rw-r--r--sys/fmtio/evvexpr.x5050
-rw-r--r--sys/fmtio/evvexpr.y4644
-rw-r--r--sys/fmtio/fmt.com17
-rw-r--r--sys/fmtio/fmterr.x25
-rw-r--r--sys/fmtio/fmtinit.x23
-rw-r--r--sys/fmtio/fmtread.x23
-rw-r--r--sys/fmtio/fmtsetcol.x28
-rw-r--r--sys/fmtio/fmtstr.x49
-rw-r--r--sys/fmtio/fpradv.x76
-rw-r--r--sys/fmtio/fprfmt.x180
-rw-r--r--sys/fmtio/fprintf.x14
-rw-r--r--sys/fmtio/fprntf.x40
-rw-r--r--sys/fmtio/fscan.x30
-rw-r--r--sys/fmtio/gargb.x33
-rw-r--r--sys/fmtio/gargc.x19
-rw-r--r--sys/fmtio/gargd.x20
-rw-r--r--sys/fmtio/gargi.x20
-rw-r--r--sys/fmtio/gargl.x20
-rw-r--r--sys/fmtio/gargr.x17
-rw-r--r--sys/fmtio/gargrad.x20
-rw-r--r--sys/fmtio/gargs.x20
-rw-r--r--sys/fmtio/gargstr.x24
-rw-r--r--sys/fmtio/gargtok.x18
-rw-r--r--sys/fmtio/gargwrd.x22
-rw-r--r--sys/fmtio/gargx.x19
-rw-r--r--sys/fmtio/gctod.x81
-rw-r--r--sys/fmtio/gctol.x78
-rw-r--r--sys/fmtio/gctox.x81
-rw-r--r--sys/fmtio/gltoc.x82
-rw-r--r--sys/fmtio/gstrcat.x26
-rw-r--r--sys/fmtio/gstrcpy.x19
-rw-r--r--sys/fmtio/itoc.x53
-rw-r--r--sys/fmtio/lexdata.inc28
-rw-r--r--sys/fmtio/lexnum.x190
-rw-r--r--sys/fmtio/ltoc.x17
-rw-r--r--sys/fmtio/mkpkg125
-rw-r--r--sys/fmtio/nscan.x12
-rw-r--r--sys/fmtio/parg.x283
-rw-r--r--sys/fmtio/pargb.x16
-rw-r--r--sys/fmtio/pargstr.x26
-rw-r--r--sys/fmtio/pargx.x57
-rw-r--r--sys/fmtio/patmatch.x568
-rw-r--r--sys/fmtio/printf.x13
-rw-r--r--sys/fmtio/resetscan.x14
-rw-r--r--sys/fmtio/scan.com10
-rw-r--r--sys/fmtio/scanc.x14
-rw-r--r--sys/fmtio/sprintf.x19
-rw-r--r--sys/fmtio/sscan.x24
-rw-r--r--sys/fmtio/strcat.x12
-rw-r--r--sys/fmtio/strcmp.x17
-rw-r--r--sys/fmtio/strcpy.x18
-rw-r--r--sys/fmtio/strdic.x73
-rw-r--r--sys/fmtio/streq.x16
-rw-r--r--sys/fmtio/strge.x16
-rw-r--r--sys/fmtio/strgt.x16
-rw-r--r--sys/fmtio/stridx.x17
-rw-r--r--sys/fmtio/stridxs.x43
-rw-r--r--sys/fmtio/strldx.x20
-rw-r--r--sys/fmtio/strldxs.x46
-rw-r--r--sys/fmtio/strle.x16
-rw-r--r--sys/fmtio/strlen.x14
-rw-r--r--sys/fmtio/strlt.x16
-rw-r--r--sys/fmtio/strlwr.x18
-rw-r--r--sys/fmtio/strmac.x86
-rw-r--r--sys/fmtio/strmatch.x136
-rw-r--r--sys/fmtio/strncmp.x20
-rw-r--r--sys/fmtio/strne.x16
-rw-r--r--sys/fmtio/strsearch.x55
-rw-r--r--sys/fmtio/strsrt.x73
-rw-r--r--sys/fmtio/strtbl.x81
-rw-r--r--sys/fmtio/strupr.x18
-rw-r--r--sys/fmtio/tokdata.inc32
-rw-r--r--sys/fmtio/xevgettok.x208
-rw-r--r--sys/fmtio/xtoc.x39
-rw-r--r--sys/fmtio/xvvgettok.x234
-rw-r--r--sys/fmtio/zzdebug.x319
106 files changed, 21116 insertions, 0 deletions
diff --git a/sys/fmtio/README b/sys/fmtio/README
new file mode 100644
index 00000000..08d8bd28
--- /dev/null
+++ b/sys/fmtio/README
@@ -0,0 +1,6 @@
+This directory contains the IRAF Formatted I/O (FMTIO) routines. The FMTIO
+package includes the string utilities (str___), character utilities (chr___),
+encode/decode primitives (ctod, itoc, etc.), and the high level SCAN and
+PRINTF routines. This is a full implementation of FMTIO (excluding the
+language dependent features that will be provided when the full language
+becomes available).
diff --git a/sys/fmtio/cctoc.x b/sys/fmtio/cctoc.x
new file mode 100644
index 00000000..6ad6ea8f
--- /dev/null
+++ b/sys/fmtio/cctoc.x
@@ -0,0 +1,67 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+include <chars.h>
+
+define OCTAL 8
+
+# CCTOC -- Convert a character constant into the ASCII value of the character
+# represented. A character constant may be any whitespace delimited
+# character, backslash escaped character, or a string of the form 'c', '\c',
+# or '\nnn'. The following are all legal character constants:
+#
+# c 'c' '\n' '\07' \ \\ \n
+#
+# The number of characters successfully converted is returned as the function
+# value.
+
+int procedure cctoc (str, ip, cval)
+
+char str[ARB] # input string
+int ip # index into input string
+char cval # receives character value
+
+long lval
+bool eat_tick
+int n, junk, ip_save
+int stridx(), gctol()
+include "escchars.inc"
+
+begin
+ while (IS_WHITE (str[ip]))
+ ip = ip + 1
+ ip_save = ip
+
+ if (str[ip] == SQUOTE) { # '...'
+ eat_tick = true
+ ip = ip + 1
+ } else
+ eat_tick = false
+
+ if (str[ip] == ESCAPE && str[ip+1] != EOS) { # \...
+ ip = ip + 1
+ n = stridx (str[ip], escape_chars) # \c
+ if (n > 0) {
+ cval = mapped_chars[n]
+ ip = ip + 1
+ } else if (IS_DIGIT (str[ip])) { # \nnn
+ junk = gctol (str, ip, lval, -OCTAL)
+ cval = lval
+ } else if (eat_tick) { # '\c'
+ cval = str[ip]
+ ip = ip + 1
+ } else
+ cval = ESCAPE # \ alone
+
+ } else if (str[ip] != EOS) {
+ cval = str[ip] # c or 'c'
+ ip = ip + 1
+
+ } else if (eat_tick)
+ cval = SQUOTE # 'EOS
+
+ if (eat_tick && str[ip] == SQUOTE)
+ ip = ip + 1
+
+ return (ip - ip_save)
+end
diff --git a/sys/fmtio/chdeposit.x b/sys/fmtio/chdeposit.x
new file mode 100644
index 00000000..0e0dc961
--- /dev/null
+++ b/sys/fmtio/chdeposit.x
@@ -0,0 +1,17 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# CHDEPOSIT -- Deposit a character in a string at the offset OP. Bump OP,
+# taking care not to overflow the string.
+
+procedure chdeposit (ch, str, maxch, op)
+
+char ch # character to be deposited
+char str[ARB] # output string
+int maxch # maxch chars in output string
+int op # pointer into output string
+
+begin
+ str[op] = ch
+ if (op < maxch)
+ op = op + 1
+end
diff --git a/sys/fmtio/chfetch.x b/sys/fmtio/chfetch.x
new file mode 100644
index 00000000..f9ab4197
--- /dev/null
+++ b/sys/fmtio/chfetch.x
@@ -0,0 +1,16 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# CHFETCH -- Return the next character from a string, bump pointer.
+
+char procedure chfetch (str, ip, ch)
+
+char str[ARB], ch
+int ip
+
+begin
+ ch = str[ip]
+ if (ch != EOS)
+ ip = ip + 1
+
+ return (ch)
+end
diff --git a/sys/fmtio/chrlwr.x b/sys/fmtio/chrlwr.x
new file mode 100644
index 00000000..a0050207
--- /dev/null
+++ b/sys/fmtio/chrlwr.x
@@ -0,0 +1,16 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+
+# CHRLWR -- Convert char to lower case
+
+char procedure chrlwr (ch)
+
+char ch
+
+begin
+ if (IS_UPPER (ch))
+ return (TO_LOWER (ch))
+ else
+ return (ch)
+end
diff --git a/sys/fmtio/chrupr.x b/sys/fmtio/chrupr.x
new file mode 100644
index 00000000..53f6582e
--- /dev/null
+++ b/sys/fmtio/chrupr.x
@@ -0,0 +1,16 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+
+# CHRUPR -- Convert char to upper case.
+
+char procedure chrupr (ch)
+
+char ch
+
+begin
+ if (IS_LOWER (ch))
+ return (TO_UPPER (ch))
+ else
+ return (ch)
+end
diff --git a/sys/fmtio/clprintf.x b/sys/fmtio/clprintf.x
new file mode 100644
index 00000000..d456c0a5
--- /dev/null
+++ b/sys/fmtio/clprintf.x
@@ -0,0 +1,17 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <printf.h>
+
+# CLPRINTF -- Format and output a string to the CL to set the value of the
+# named (string or struct type) parameter. For example, to set a cursor
+# struct parameter, "clprintf (param, "%8.4f %8.4f %c")" ...
+
+procedure clprintf (param, format_string)
+
+char param[ARB], format_string[ARB]
+
+begin
+ call putline (CLOUT, param)
+ call putline (CLOUT, " = \"")
+ call fprntf (CLOUT, format_string, CL_PARAM)
+end
diff --git a/sys/fmtio/clscan.x b/sys/fmtio/clscan.x
new file mode 100644
index 00000000..563954d3
--- /dev/null
+++ b/sys/fmtio/clscan.x
@@ -0,0 +1,32 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# CLSCAN -- Begin a scan of the value of a CL parameter
+
+int procedure clscan (param)
+
+char param[ARB]
+int getline(), strncmp(), clc_fetch()
+include "scan.com"
+errchk clreqpar, getline
+
+begin
+ # Fetch the value of a CL parameter. First look in the parameter
+ # cache, querying the CL for the value of the parameter only if it
+ # is not found in the cache.
+
+ if (clc_fetch (param, sc_scanbuf, SZ_SCANBUF) == ERR) {
+ call clreqpar (param)
+ if (getline (CLIN, sc_scanbuf) == EOF)
+ return (EOF)
+ }
+
+ # Check for EOF on a list structured parameter; if not EOF initialize
+ # formatted input for the clget procedures.
+
+ if (strncmp ("EOF\n", sc_scanbuf, 4) == 0)
+ return (EOF)
+ else {
+ call reset_scan()
+ return (OK)
+ }
+end
diff --git a/sys/fmtio/ctocc.x b/sys/fmtio/ctocc.x
new file mode 100644
index 00000000..b2f4197a
--- /dev/null
+++ b/sys/fmtio/ctocc.x
@@ -0,0 +1,64 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+
+define OCTAL 8
+
+# CTOCC -- Convert a character into a printable character constant.
+# Printable characters are output as is. The standard control characters
+# (newline, tab, etc.) are output as escape sequences (\n, \t, etc.).
+# Other control characters are output in the form '^X'. Characters which
+# are neither printable nor standard control characters are output as
+# octal constants of the form '\DDD'. Note that the ouput string is not
+# enclosed in ticks ('\n', etc.), because the generated character constant
+# might appear in a quoted string (or someplace other than an explicit
+# character constant).
+
+int procedure ctocc (ch, outstr, maxch)
+
+char ch # character to be output
+char outstr[ARB] # output string
+int maxch # max chars out
+
+int op, n
+int stridx()
+define output {outstr[op]=$1;op=op+1;if(op>maxch)goto overflow_}
+define overflow_ 99
+include "escchars.inc"
+
+begin
+ op = 1
+
+ if (maxch > 0) {
+ if (IS_PRINT(ch)) { # output char as is
+ output (ch)
+ } else if (IS_CNTRL (ch)) {
+ n = stridx (ch, mapped_chars)
+ if (n > 0) { # '\c'
+ output ('\\')
+ output (escape_chars[n])
+ } else {
+ output ('^') # control chars
+ output (ch + 'A' - 1)
+ }
+
+ } else { # '\nnn'
+ # Always output 3 digits so that strings like \0405 (a blank
+ # followed by a `5') can be interpreted during the reverse
+ # encoding operation.
+
+ output ('\\')
+ output (TO_DIGIT (mod (ch / 0100B, 010B)))
+ output (TO_DIGIT (mod (ch / 0010B, 010B)))
+ output (TO_DIGIT (mod (ch / 0001B, 010B)))
+ }
+ }
+
+ outstr[op] = EOS
+ return (op-1)
+
+overflow_
+ outstr[1] = '?' # no room, print '?'
+ outstr[2] = EOS
+ return (1)
+end
diff --git a/sys/fmtio/ctod.x b/sys/fmtio/ctod.x
new file mode 100644
index 00000000..06b1d351
--- /dev/null
+++ b/sys/fmtio/ctod.x
@@ -0,0 +1,154 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include <ctype.h>
+
+define DECIMAL 10
+
+.help
+.nf _________________________________________________________________________
+Attempt to convert a string to a number: nchar = ctod (str, ip, dval)
+The index IP must be set to the first character to be scanned upon entry
+to CTOD, and will be left pointing at the first untranslated character.
+
+If the string is successfully converted, the number of characters used
+is returned as the function argument. If the string (or the first few
+characters of the string) cannot be interpreted as a number, zero will be
+returned. Note that even if no numeric characters are encountered, the
+index IP may be incremented, if leading whitespace is encountered (but the
+return value N will still be zero).
+
+The upper case string "INDEF" is a legal real number, as is "." (. == 0.0).
+Sexagesimal numbers are permitted. Excess digits of precision are ignored.
+Out of range exponents are detected, and result in the value INDEF being
+returned (this is not considered an ERROR condition). Any number with an
+exponent greater than or equal to MAX_EXPONENT is interpreted as INDEF,
+regardless of the mantissa. The number need not contain a decimal point.
+
+Lexical form of a sexagesimal number:
+
+ D :== [0-9] numeric digit
+ E :== [eEdD] exponent symbol
+
+ ({D}*:)+{D}*(".")?{D}*({E}("+"|"-")?{D}+)?
+
+The format for sexagesimal numbers is fairly permissive. Any number of
+colon fields are permitted, with any number of digits (including zero) in
+each field. An exponent may occur at the end of a sexagesimal number.
+Leading zeros may be omitted in the fields.
+.endhelp ____________________________________________________________________
+
+
+# CTOD -- Convert a string to double precision real.
+
+int procedure ctod (str, ip, dval)
+
+char str[ARB] # string to be converted
+int ip # pointer into str
+double dval # receives binary value
+
+bool neg
+char dig[MAX_DIGITS]
+int j, e, vexp, ip_start
+long expon
+double value, scalar
+int strncmp(), gctol(), stridx()
+
+begin
+ while (IS_WHITE (str[ip])) # skip whitespace
+ ip = ip + 1
+ ip_start = ip
+ dval = INDEFD
+
+ if (strncmp (str[ip], "INDEF", 5) == 0) { # check for "INDEF"
+ for (ip=ip+5; IS_ALPHA (str[ip]) || str[ip] == '_'; ip=ip+1)
+ ;
+ return (ip - ip_start)
+ }
+
+ neg = (str[ip] == '-') # check for sign
+ if (neg || str[ip] == '+')
+ ip = ip + 1
+
+ while (str[ip] == '0') # ignore leading zeros
+ ip = ip + 1
+
+ dval = 0.0
+ scalar = 60.0
+
+ repeat { # accumulate digits
+ for (j=1; j <= MAX_DIGITS && IS_DIGIT(str[ip]); j=j+1) {
+ dig[j] = str[ip]
+ ip = ip + 1
+ }
+
+ for (e=0; IS_DIGIT(str[ip]); e=e+1) # ignore the rest
+ ip = ip + 1
+
+ scalar = scalar / 60.0
+ if (ip > 1 && stridx(str[ip], "'\":dDhHmMsS")>0) { # sexagesimal?
+ ip = ip + 1
+ dig[j] = EOS
+ value = 0.0 # convert digits
+ for (j=1; dig[j] != EOS; j=j+1)
+ value = value * 10.0D0 + TO_INTEG (dig[j])
+ dval = dval + value * scalar * (10.0 ** e)
+
+ while (str[ip] != EOS && # multiple spaces etc
+ stridx(str[ip]," '\":dDhHmMsS")>0)
+ ip = ip + 1
+ } else
+ break
+ }
+
+ if (str[ip] == '.') { # check for a fraction
+ ip = ip + 1
+ if (j == 1) # skip leading zeros
+ while (str[ip] == '0') { # if str = "0.00ddd"
+ ip = ip + 1
+ e = e - 1
+ }
+ for (; j <= MAX_DIGITS && IS_DIGIT(str[ip]); j=j+1) {
+ dig[j] = str[ip]
+ e = e - 1 # adjust scale factor
+ ip = ip + 1
+ } # discard insignificant
+ while (IS_DIGIT (str[ip])) # fractional digits
+ ip = ip + 1
+ }
+
+ dig[j] = EOS # no more digits
+ vexp = e + j - 1 # save for ovfl check
+ if (ip == ip_start) # not a number?
+ return (0)
+
+ value = 0.0 # convert the mantissa
+ for (j=1; dig[j] != EOS; j=j+1)
+ value = value * 10.0D0 + TO_INTEG (dig[j])
+ if (e != 0)
+ value = value * (10.0D0 ** e) # scale by e
+
+
+ # Check for exponent.
+
+ j = ip
+ expon = 0
+ if (stridx (str[ip], "eEdD") > 0) { # exponent?
+ ip = ip + 1
+ if (gctol (str, ip, expon, DECIMAL) <= 0) {
+ ip = j # return chars
+ expon = 0
+ }
+ }
+
+ if (abs(vexp+expon) > MAX_EXPONENTD) # check for overflow
+ return (ip - ip_start)
+
+ dval = dval + value * scalar
+ if (expon != 0)
+ dval = dval * (10.0D0 ** expon) # apply exponent
+
+ if (neg)
+ dval = -dval
+ return (ip - ip_start)
+end
diff --git a/sys/fmtio/ctoi.x b/sys/fmtio/ctoi.x
new file mode 100644
index 00000000..ce791222
--- /dev/null
+++ b/sys/fmtio/ctoi.x
@@ -0,0 +1,48 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+
+# CTOI -- Simple character to integer (decimal radix).
+
+int procedure ctoi (str, ip, ival)
+
+char str[ARB] # decimal encoded numeric string
+int ip # starting index in string (input/output)
+int ival # decoded integer value (output)
+
+bool neg
+int sum
+int ip_start
+int strncmp()
+
+begin
+ while (IS_WHITE (str[ip]))
+ ip = ip + 1
+ ip_start = ip
+
+ # Check for "INDEF".
+ if (str[ip] == 'I')
+ if (strncmp (str[ip], "INDEF", 5) == 0)
+ if (!IS_ALNUM (str[ip+5])) {
+ ival = INDEFI
+ ip = ip + 5
+ return (5)
+ }
+
+ neg = (str[ip] == '-')
+ if (neg)
+ ip = ip + 1
+
+ sum = 0
+ while (IS_DIGIT (str[ip])) {
+ sum = sum * 10 + TO_INTEG (str[ip])
+ ip = ip + 1
+ }
+
+ if (neg)
+ ival = -sum
+ else
+ ival = sum
+
+ return (ip - ip_start)
+end
diff --git a/sys/fmtio/ctol.x b/sys/fmtio/ctol.x
new file mode 100644
index 00000000..a83d1384
--- /dev/null
+++ b/sys/fmtio/ctol.x
@@ -0,0 +1,52 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+
+# CTOL -- Simple character to long integer (decimal radix).
+
+int procedure ctol (str, ip, lval)
+
+char str[ARB] # decimal encoded numeric string
+int ip # starting index in string (input/output)
+long lval # decoded integer value (output)
+
+bool neg
+long sum
+int ip_start
+int strncmp()
+
+begin
+ while (IS_WHITE (str[ip]))
+ ip = ip + 1
+ ip_start = ip
+
+ # Check for "INDEF".
+ if (str[ip] == 'I')
+ if (strncmp (str[ip], "INDEF", 5) == 0)
+ if (!IS_ALNUM (str[ip+5])) {
+ lval = INDEFL
+ ip = ip + 5
+ return (5)
+ }
+
+ neg = false
+ if (IS_DIGIT (str[ip+1]))
+ if (str[ip] == '-') {
+ neg = true
+ ip = ip + 1
+ } else if (str[ip] == '+')
+ ip = ip + 1
+
+ sum = 0
+ while (IS_DIGIT (str[ip])) {
+ sum = sum * 10 + TO_INTEG (str[ip])
+ ip = ip + 1
+ }
+
+ if (neg)
+ lval = -sum
+ else
+ lval = sum
+
+ return (ip - ip_start)
+end
diff --git a/sys/fmtio/ctor.x b/sys/fmtio/ctor.x
new file mode 100644
index 00000000..a8fd16f1
--- /dev/null
+++ b/sys/fmtio/ctor.x
@@ -0,0 +1,34 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+
+# CTOR -- Character to real. The number of characters converted to produce
+# the output number is returned as the function value (0 is returned if the
+# input cannot be interpreted as a number).
+
+int procedure ctor (str, ip, rval)
+
+char str[ARB] # input string to be decoded
+int ip # first character to be used in string
+real rval # decoded real value (output)
+
+double dval
+int nchars, expon
+int ctod()
+
+begin
+ nchars = ctod (str, ip, dval)
+ if (abs(dval) > EPSILOND)
+ expon = int (log10 (abs(dval)))
+ else
+ expon = 0
+
+ if (IS_INDEFD(dval))
+ rval = INDEFR
+ else if (expon > MAX_EXPONENTR)
+ return (0)
+ else
+ rval = dval
+
+ return (nchars)
+end
diff --git a/sys/fmtio/ctotok.x b/sys/fmtio/ctotok.x
new file mode 100644
index 00000000..333984c9
--- /dev/null
+++ b/sys/fmtio/ctotok.x
@@ -0,0 +1,167 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctotok.h>
+include <ctype.h>
+include <chars.h>
+include <lexnum.h>
+
+.help CTOTOK 2 "String Utilities"
+.ih ___________________________________________________________________________
+NAME
+CTOTOK -- Return next token from input text.
+.ih
+USAGE
+token = ctotok (string, ip, outstr, maxch)
+.ih
+PARAMETERS
+The integer value returned by CTOTOK is a code identifying the type of token
+matched. The predefined tokens recognized by CTOTOK (defined in <ctotok.h>)
+are the following:
+.ls
+.nf
+TOK_IDENTIFER [a-zA-Z][a-zA-Z0-9_$.]*
+TOK_NUMBER [0-9][-+0-9.:xXa-fA-F]*
+TOK_OPERATOR [-+*/!@#$%^&=`~<>?|]+
+TOK_PUNCTUATION [,:;(){}] or "[", "]"
+TOK_STRING "..."
+TOK_CHARCON '.'
+TOK_EOS end of string
+TOK_NEWLINE end of line
+TOK_UNKNOWN control characters
+.fi
+.le
+.ls string
+The EOS delimited character string from which the next token is to be
+extracted.
+.le
+.ls ip
+On input, contains the index of the first character to be scanned
+(initially 1). On output, left pointing at the first character after
+the current token, unless EOS was reached. IP should normally be left
+alone in successive calls to CTOTOK.
+.le
+.ls outstr
+String to receive the extracted token value.
+.le
+.ls maxch
+Capacity of the "outstr" buffer.
+.le
+.ih
+DESCRIPTION
+CTOTOK is useful for many simple parsing tasks. For example, it is used
+by the HELP utility to parse the ".help" directive, which consists of
+a list of keywords (delimited by commas), followed by two strings or
+identifiers.
+
+CTOTOK selects the type of token to be extracted based on the token
+class membership of the first nonwhitespace character encountered.
+Characters are copied to the output string until a character not belonging
+to the current class is encountered (or until MAXCH characters have been
+output). Whitespace is always a token delimiter. The integer code for the
+corresponding token is returned as the function value.
+
+An identifier is a letter followed by any number of letters, digits, or
+one of the characters [_.$]. A number is any legal integer, octal,
+hexadecimal, sexagesimal, or floating point number. All legal numbers are
+matched: however, many illegal numbers (e.g. "99.33.22") are matched as well.
+The numeric conversion routines may be used to verify that a number token
+is actually a legal number, as well as to convert the number to binary.
+
+An operator is one or more operator characters, or any of the characters
+[_.$], not occurring as part of an identifier, but occurring instead as the
+first character of an operator. Note that a string of operator characters
+is considered a single token, whereas punctuation characters are returned
+as separate tokens. Strings are enclosed by either single or double quotes,
+and all escape sequences are recognized and processed.
+Control characters and DEL match the "unknown" token.
+.ih
+SEE ALSO
+tokens(1), strmatch(), patmatch()
+.endhelp ______________________________________________________________________
+
+define TABLESIZE 95
+define NUMCHSIZE 6
+define OFFSET ' '
+
+
+# CTOTOK -- Character string to token. The token is returned in OUTSTR and the
+# token type code is returned as the function value.
+
+int procedure ctotok (str, ip, outstr, maxch)
+
+char str[ARB] # input string
+int ip # pointer into input string
+char outstr[ARB] # buffer to receive token
+int maxch # max chars in output buffer
+
+int currclass
+char class[TABLESIZE]
+int op, ch, i, junk, nchars
+int ctowrd(), lexnum(), cctoc()
+include "tokdata.inc"
+
+begin
+ while (IS_WHITE (str[ip]))
+ ip = ip + 1
+
+ ch = str[ip]
+ i = max(1, min(TABLESIZE, ch - OFFSET))
+ op = 1
+
+ if (ch == EOS) { # select class (token)
+ outstr[1] = EOS
+ return (TOK_EOS)
+
+ } else if (ch == NEWLINE) { # end of line
+ outstr[1] = ch
+ outstr[2] = EOS
+ ip = ip + 1
+ return (TOK_NEWLINE)
+
+ } else if (ch <= OFFSET) { # control characters
+ while (op <= maxch && ch != EOS && ch <= OFFSET) {
+ outstr[op] = ch
+ op = op + 1
+ ip = ip + 1
+ ch = str[ip]
+ }
+ outstr[op] = EOS
+ return (TOK_UNKNOWN)
+
+ } else if (ch == DQUOTE) { # string constant
+ junk = ctowrd (str, ip, outstr, maxch)
+ return (TOK_STRING)
+
+ } else if (ch == SQUOTE || ch == ESCAPE) {
+ nchars = cctoc (str, ip, junk)
+ call strcpy (str[ip-nchars], outstr, nchars)
+ return (TOK_CHARCON)
+
+ } else if (lexnum (str, ip, nchars) != LEX_NONNUM) {
+ call strcpy (str[ip], outstr, nchars)
+ ip = ip + nchars
+ return (TOK_NUMBER)
+
+ } else if (class[i] == TOK_IDENTIFIER && !IS_ALPHA (ch)) {
+ currclass = TOK_OPERATOR
+
+ } else if (class[i] == TOK_PUNCTUATION) { # only one at a time
+ outstr[1] = ch
+ outstr[2] = EOS
+ ip = ip + 1
+ return (TOK_PUNCTUATION)
+
+ } else
+ currclass = class[i]
+
+ repeat { # copy token to output
+ outstr[op] = ch
+ op = op + 1
+ ip = ip + 1
+ ch = str[ip]
+ i = max(1, min(TABLESIZE, ch - OFFSET))
+ } until (ch == EOS || ch <= OFFSET || class[i] != currclass)
+
+ outstr[op] = EOS
+ return (currclass)
+end
diff --git a/sys/fmtio/ctowrd.x b/sys/fmtio/ctowrd.x
new file mode 100644
index 00000000..5f511075
--- /dev/null
+++ b/sys/fmtio/ctowrd.x
@@ -0,0 +1,83 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+include <chars.h>
+
+# CTOWRD -- Break whitespace delimited token or quoted string out of input
+# stream. If string, process escape sequences. The number of characters
+# converted from the input string, excluding whitespace, is returned as the
+# function value.
+
+int procedure ctowrd (str, ip, outstr, maxch)
+
+char str[ARB] # input string
+int ip # pointer into input string
+char outstr[ARB] # receives extracted word
+int maxch
+
+char cch
+int ch, junk, op
+int ip_start, delim, i
+define qsput_ 91
+define wsput_ 92
+int cctoc()
+
+begin
+ while (IS_WHITE(str[ip]))
+ ip = ip + 1
+ ip_start = ip
+
+ delim = str[ip]
+ if (delim == DQUOTE || delim == SQUOTE) {
+ # Extract a quoted string.
+ op = 1
+ ip = ip + 1
+ do i = 1, ARB {
+ ch = str[ip]
+ if (ch == EOS) {
+ break
+ } else if (ch == ESCAPE) {
+ ch = str[ip+1]
+ if (ch == delim) {
+ ip = ip + 2
+ goto qsput_
+ } else {
+ junk = cctoc (str, ip, cch)
+ ch = cch
+ goto qsput_
+ }
+ } else if (ch == delim) {
+ ip = ip + 1
+ break
+ } else {
+ ip = ip + 1
+qsput_ if (op <= maxch) {
+ outstr[op] = ch
+ op = op + 1
+ }
+ }
+ }
+ } else {
+ # Extract a whitespace delimited string.
+ op = 1
+ do i = 1, ARB {
+ ch = str[ip]
+ if (IS_WHITE(ch) || ch == '\n' || ch == EOS) {
+ break
+ } else if (ch == ESCAPE) {
+ junk = cctoc (str, ip, cch)
+ ch = cch
+ goto wsput_
+ } else {
+ ip = ip + 1
+wsput_ if (op <= maxch) {
+ outstr[op] = ch
+ op = op + 1
+ }
+ }
+ }
+ }
+
+ outstr[op] = EOS
+ return (ip - ip_start)
+end
diff --git a/sys/fmtio/ctox.x b/sys/fmtio/ctox.x
new file mode 100644
index 00000000..3f0479a2
--- /dev/null
+++ b/sys/fmtio/ctox.x
@@ -0,0 +1,48 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+
+# CTOX -- Convert a character string into a complex number. The complex
+# number must have the form (r,i), with no embedded whitespace (GCTOX is
+# cabable of accepting numbers in other formats).
+
+int procedure ctox (str, ip, xval)
+
+char str[ARB]
+int ip, ip_save
+double dval1, dval2
+complex xval
+int ctod()
+define notanumber_ 99
+
+begin
+ while (IS_WHITE (str[ip]))
+ ip = ip + 1
+ ip_save = ip
+ dval2 = 0.0d0
+
+ if (str[ip] == '(') { # x = (r1,r2)
+ ip = ip + 1
+ if (ctod (str, ip, dval1) <= 0)
+ goto notanumber_
+ if (str[ip] != ',')
+ goto notanumber_
+ ip = ip + 1
+ if (ctod (str, ip, dval2) <= 0)
+ goto notanumber_
+ if (str[ip] != ')')
+ goto notanumber_
+ ip = ip + 1
+ } else
+ goto notanumber_
+
+ if (IS_INDEFD(dval1) || IS_INDEFD(dval2))
+ xval = INDEFX
+ else
+ xval = complex (dval1, dval2)
+ return (ip - ip_save)
+
+notanumber_
+ ip = ip_save
+ return (0)
+end
diff --git a/sys/fmtio/doc/evexpr.hlp b/sys/fmtio/doc/evexpr.hlp
new file mode 100644
index 00000000..386baf91
--- /dev/null
+++ b/sys/fmtio/doc/evexpr.hlp
@@ -0,0 +1,147 @@
+ EVEXPR
+ Evaluating Algebraic Expressions in SPP Programs
+ dct 17 April 1985
+
+
+
+1. Introduction
+
+ EVEXPR is a function which takes an algebraic expression as input,
+evaluates the expression, and returns the value of the expression as the
+function value. The input expression (a character string) is parsed and
+reduced to a single value. The operands to the expression may be either
+constants or identifiers (foreign strings). If an identifier is encountered
+the user supplied get operand procedure is called to return the value of
+the operand. Operands are described by the operand structure, and operands
+are passed about by a pointer to such a structure. The value of the
+expression is returned as the function value and is a pointer to an operand
+structure. Operands of different datatypes may be mixed in an expression
+with the usual automatic type coercion rules. All SPP datatypes are
+supported plus the string datatype. All SPP operators and intrinsic
+functions are recognized.
+
+
+2. Procedures
+
+ op = evexpr (expr, locpr(getop), locpr(ufcn))
+ getop (identifier, op)
+ ufcn (fcn, args, nargs, op)
+
+where
+
+ evexpr The main entry point.
+ expr A character string, the expression to be evaluated.
+ getop A user supplied procedure which returns the value
+ of a nonconstant operand given the NAME of the operand
+ (a character string) as input. If locpr(getop) is
+ NULL only constant operands are permitted in the
+ expression.
+ ufcn A user supplied procedure which returns the value of
+ a user defined function given the name of the function
+ as the first argument (a string). If locpr(ufcn) is
+ NULL only the standard functions are permitted.
+ fcn Name of the function to be evaluated.
+ args Array of pointers to operands (the arguments to the function).
+ nargs Number of arguments to function.
+ op A pointer to an operand structure
+
+
+A a simple example, consider the following statement which evaluates a
+constant expression and prints the value on the standard output.
+
+
+ include <evexpr.h>
+ pointer o, evexpr()
+
+ o = evexpr ("sin(.5)**2 + cos(.5)**2)", NULL, NULL)
+ switch (O_TYPE(o)) {
+ case TY_INT:
+ call printf ("result = %d\n")
+ call pargi (O_VALI(o))
+ case TY_REAL:
+ call printf ("result = %g\n")
+ call pargr (O_VALR(o))
+ case TY_CHAR:
+ call printf ("result = %s\n")
+ call pargstr (O_VALC(o))
+ }
+
+
+If a syntax error occurs while parsing the expression EVEXPR will take the
+error action "syntax error". The NULL arguments could be replaced by the
+LOCPR addresses of get operand and/or user function procedures if required
+by the application.
+
+
+3. Lexical Form
+
+ The lexical form of the input expression is the same as that of SPP and
+the CL for all numeric, character, and string constants and operators.
+Any other sequence of characters is considered an identifier and will be
+passed to the user supplied get operand function to be turned into an operand.
+
+
+4. Syntax
+
+ Parsing and evaluating of the input expression is carried out by an SPP/Yacc
+parser. The grammar recognized by the parser is given below.
+
+
+expr : CONST # numeric or string constant
+ | IDENT # external operand (getop)
+ | '-' expr %prec UMINUS
+ | expr '+' expr
+ | expr '-' expr
+ | expr '*' expr
+ | expr '/' expr
+ | expr '**' expr
+ | expr '//' expr
+ | '!' expr
+ | expr '<' expr
+ | expr '<=' expr
+ | expr '>' expr
+ | expr '>=' expr
+ | expr '==' expr
+ | expr '!=' expr
+ | expr '&&' expr
+ | expr '||' expr
+ | IDENT '(' arglist ')' # function call
+ | '?' expr ':' expr # conditional expression
+ | '(' expr ')'
+ ;
+
+arglist : # Empty.
+ | arglist ',' expr
+ ;
+
+
+2. Data Structures
+
+ The operand structure (size 3 su) is used to represent all operands in
+expressions and on the parser stack. Operands are passed to and from the
+outside world by means of a pointer to an operand structure. The caller
+is responsible for string storage of string operands passed to EVEXPR.
+EVEXPR manages string storage for temporary string operands created during
+expression evaluation, as well as storage for the final string value if
+the expression is string valued. In the latter case the value string should
+be used before EVEXPR is again called.
+
+
+ struct operand {
+ int type # operand datatype
+ union {
+ bool v_b # boolean value
+ int v_i # integer value
+ real v_r # real value
+ char *v_s # string value
+ } v
+ }
+
+
+SPP equivalent (<evexpr.h>)
+
+ O_TYPE(o) # operand datatype
+ O_VALB(o) # boolean value
+ O_VALI(o) # integer value (or string ptr)
+ O_VALR(o) # real value
+ O_VALC(o) # string value
diff --git a/sys/fmtio/doc/fmtio.hd b/sys/fmtio/doc/fmtio.hd
new file mode 100644
index 00000000..525911dc
--- /dev/null
+++ b/sys/fmtio/doc/fmtio.hd
@@ -0,0 +1,77 @@
+# Help directory for the FMTIO (formatted i/o) package.
+
+$fmtio = "sys$fmtio/"
+
+cctoc hlp = xtoc.hlp, src = fmtio$cctoc.x
+chdeposit hlp = chdeposit.hlp, src = fmtio$chdeposit.x
+chfetch hlp = chfetch.hlp, src = fmtio$chfetch.x
+chrlwr hlp = chrlwr.hlp, src = fmtio$chrlwr.x
+chrupr hlp = chrupr.hlp, src = fmtio$chrupr.x
+clprintf hlp = printf.hlp, src = fmtio$clprintf.x
+clscan hlp = scan.hlp, src = fmtio$clscan.x
+ctocc hlp = ctox.hlp, src = fmtio$ctocc.x
+ctod hlp = ctox.hlp, src = fmtio$ctod.x
+ctoi hlp = ctox.hlp, src = fmtio$ctoi.x
+ctol hlp = ctox.hlp, src = fmtio$ctol.x
+ctor hlp = ctox.hlp, src = fmtio$ctor.x
+ctotok hlp = ctox.hlp, src = fmtio$ctotok.x
+ctowrd hlp = ctox.hlp, src = fmtio$ctowrd.x
+ctox hlp = ctox.hlp, src = fmtio$ctox.x
+dtoc hlp = xtoc.hlp, src = fmtio$dtoc.x
+eprintf hlp = printf.hlp, src = fmtio$eprintf.x
+fprintf hlp = printf.hlp, src = fmtio$fprintf.x
+fscan hlp = scan.hlp, src = fmtio$fscan.x
+gargb hlp = garg.hlp, src = fmtio$gargb.x
+gargc hlp = garg.hlp, src = fmtio$gargc.x
+gargd hlp = garg.hlp, src = fmtio$gargd.x
+gargi hlp = garg.hlp, src = fmtio$gargi.x
+gargl hlp = garg.hlp, src = fmtio$gargl.x
+gargr hlp = garg.hlp, src = fmtio$gargr.x
+gargrad hlp = garg.hlp, src = fmtio$gargrad.x
+gargs hlp = garg.hlp, src = fmtio$gargs.x
+gargstr hlp = garg.hlp, src = fmtio$gargstr.x
+gargtok hlp = garg.hlp, src = fmtio$gargtok.x
+gargwrd hlp = garg.hlp, src = fmtio$gargwrd.x
+gargx hlp = garg.hlp, src = fmtio$gargx.x
+gctod hlp = ctox.hlp, src = fmtio$gctod.x
+gctol hlp = ctox.hlp, src = fmtio$gctol.x
+gltoc hlp = xtoc.hlp, src = fmtio$gltoc.x
+gstrcat hlp = gstrcat.hlp, src = fmtio$gstrcat.x
+gstrcpy hlp = gstrcpy.hlp, src = fmtio$gstrcpy.x
+itoc hlp = xtoc.hlp, src = fmtio$itoc.x
+lexnum hlp = lexnum.hlp, src = fmtio$lexnum.x
+ltoc hlp = xtoc.hlp, src = fmtio$ltoc.x
+nscan hlp = scan.hlp, src = fmtio$nscan.x
+pargb hlp = parg.hlp, src = fmtio$pargb.x
+pargc hlp = parg.hlp, src = fmtio$parg.x
+pargs hlp = parg.hlp, src = fmtio$parg.x
+pargi hlp = parg.hlp, src = fmtio$parg.x
+pargl hlp = parg.hlp, src = fmtio$parg.x
+pargr hlp = parg.hlp, src = fmtio$parg.x
+pargd hlp = parg.hlp, src = fmtio$parg.x
+pargx hlp = parg.hlp, src = fmtio$parg.x
+pargstr hlp = parg.hlp, src = fmtio$pargstr.x
+patmatch hlp = patmatch.hlp, src = fmtio$patmatch.x
+printf hlp = printf.hlp, src = fmtio$printf.x
+scanc hlp = scan.hlp, src = fmtio$scanc.x
+sprintf hlp = printf.hlp, src = fmtio$sprintf.x
+sscan hlp = scan.hlp, src = fmtio$sscan.x
+strcat hlp = strcat.hlp, src = fmtio$strcat.x
+strcpy hlp = strcpy.hlp, src = fmtio$strcpy.x
+strdic hlp = strdic.hlp, src = fmtio$strdic.x
+streq hlp = streq.hlp, src = fmtio$streq.x
+strge hlp = strge.hlp, src = fmtio$strge.x
+strgt hlp = strgt.hlp, src = fmtio$strgt.x
+stridx hlp = stridx.hlp, src = fmtio$stridx.x
+strldx hlp = strldx.hlp, src = fmtio$strldx.x
+strle hlp = strle.hlp, src = fmtio$strle.x
+strlen hlp = strlen.hlp, src = fmtio$strlen.x
+strlt hlp = strlt.hlp, src = fmtio$strlt.x
+strlwr hlp = strlwr.hlp, src = fmtio$strlwr.x
+strmatch hlp = strmatch.hlp, src = fmtio$strmatch.x
+strncmp hlp = strncmp.hlp, src = fmtio$strncmp.x
+strne hlp = strne.hlp, src = fmtio$strne.x
+strsearch hlp = strsearch.hlp, src = fmtio$strsearch.x
+strtbl hlp = strtbl.hlp, src = fmtio$strtbl.x
+strupr hlp = strupr.hlp, src = fmtio$strupr.x
+xtoc hlp = xtoc.hlp, src = fmtio$xtoc.x
diff --git a/sys/fmtio/doc/fmtio.men b/sys/fmtio/doc/fmtio.men
new file mode 100644
index 00000000..897a8139
--- /dev/null
+++ b/sys/fmtio/doc/fmtio.men
@@ -0,0 +1,59 @@
+ cctoc - Character constant to char
+ chdeposit - Deposit a character in a string with overflow protection
+ chfetch - Fetch a character from a string
+ chrlwr - Convert a character to lower case
+ chrupr - Convert a character to upper case
+ clprintf - Formatted print to a CL parameter
+ clscan - Scan a CL parameter
+ ctocc - Char to character constant
+ ctod - Character to double
+ ctoi - Character to integer
+ ctol - Character to long
+ ctor - Character to real
+ ctox - Character to complex
+ ctotok - Character to lexical token
+ ctowrd - Character to whitespace delimited word
+ dtoc - Double to character
+ eprintf - Formatted print to STDERR
+ fprintf - Formatted print to any file
+ fscan - Scan a file
+ garg[bcsilrdx] - Get scan argument
+ gargrad - Get scan argument in any numerical radix
+ gargstr - Get scan argument of type string
+ gargtok - Get scan argument of type token
+ gargwrd - Get scan argument of type word
+ gctod - General character to double
+ gctol - General character to long (any radix)
+ gltoc - General long to character (any radix)
+ gstrcat - String concatenation returning length of output string
+ gstrcpy - String copy returning length of output string
+ itoc - Integer to character
+ lexnum - Lexically analyze a string to determine if it is a number
+ ltoc - Long to character
+ nscan - Get number of arguments successfully converted in last scan
+ parg[bcsilrdx] - Pass an argument to a printf
+ pargstr - Pass a string type argument to a printf
+ patmatch - General pattern matching
+ printf - Formatted print to STDOUT
+ scanc - Get the next character from a scan
+ sprintf - Formatted print to a string buffer
+ sscan - Scan a string buffer
+ strcat - String concatenation
+ strcpy - String copy
+ strdic - Look a string up in a dictionary
+ streq - Compare strings for equality
+ strge - Is string A greater than or equal to string B
+ strgt - Is string A greater than string B
+ stridx - First occurrence of a character in a string
+ strldx - Last occurrence of a character in a string
+ strle - Is string A less than or equal to string B
+ strlen - Length of a string
+ strlt - Is string A less than string B
+ strlwr - Convert string to lower case
+ strmatch - Search a string for a pattern
+ strncmp - Compare the first N characters of two strings
+ strne - Is string A not equal to string B
+ strsearch - Fast string search, no metacharacters
+ strtbl - Print a list of strings in a table
+ strupr - Convert a string to upper case
+ xtoc - Complex to character
diff --git a/sys/fmtio/doc/lexnum.hlp b/sys/fmtio/doc/lexnum.hlp
new file mode 100644
index 00000000..647654b1
--- /dev/null
+++ b/sys/fmtio/doc/lexnum.hlp
@@ -0,0 +1,303 @@
+
+.help lexnum 2 "string utilities"
+.ih _________________________________________________________________________
+NAME
+lexnum -- Determine if string is a number
+.ih
+USAGE
+token_type = lexnum (str, ip, nchars)
+
+.ih
+PARAMETERS
+.ls str
+String to be scanned.
+.le
+.ls ip
+Index within the string as which scanning is to start. Not modified.
+.le
+.ls nchars
+On output, the number of characters in the number, not including any
+leading whitespace.
+.le
+.ih
+DESCRIPTION
+The character string is scanned to determine if the next token is a
+legal number, and if so, the type of number. The function value identifies
+the type of number. The possible return values, defined in <lexnum.h>,
+as as follows:
+
+.nf
+ LEX_OCTAL (+|-)?[0-7]+[bB]
+ LEX_DECIMAL (+|-)?[0-9]+
+ LEX_HEX (+|-)?[0-9a-fA-F]+[xX]
+ LEX_REAL floating, exponential [eEdD], sexagesimal
+ LEX_NONNUM not a number
+.fi
+.ih
+IMPLEMENTATION
+Numtype is implemented as a finite state automaton. Additional documentation
+is provided with the source code.
+.ih
+SEE ALSO
+gctod(2), ctotok(2).
+.endhelp ___________________________________________________________________
+
+
+
+.help states 2 "States of the LEXNUM Finite State Automaton"
+.fi
+
+
+.ks
+.nf
+start: (1)
+ +- shift unop_or_number
+ 0-7 shift odhr
+ 8-9 shift dhr
+ acf reduce not_a_number
+ ed reduce not_a_number
+ : shift maybe_real_number
+ . shift maybe_real_fraction
+ x reduce not_a_number
+ b reduce not_a_number
+ other reduce not_a_number
+.fi
+.ke
+
+
+.ks
+.nf
+unop_or_number: (+|-) (2)
+ +- revert
+ 0-7 shift odhr
+ 8-9 shift dhr
+ acf revert
+ ed revert
+ : revert
+ . shift maybe_real_fraction
+ x revert
+ b revert
+ other revert
+.fi
+.ke
+
+
+.ks
+.nf
+odhr: (+|-)?[0-7] (3)
+ +- reduce decimal_number
+ 0-7 accept
+ 8-9 shift dhr
+ acf shift h
+ ed shift maybe_hex_or_rexp
+ : shift maybe_real_number
+ . shift real_fraction
+ x reduce hex_number
+ b shift octal_or_hex_number
+ other reduce decimal_number
+.fi
+.ke
+
+
+.ks
+.nf
+dhr: (+|-)?[0-9]+ (4)
+ +- reduce decimal_number
+ 0-7 accept
+ 8-9 accept
+ acf shift h
+ ed shift maybe_hex_or_rexp
+ : shift maybe_real_number
+ . shift real_fraction
+ x reduce hex_number
+ b shift h
+ other reduce decimal_number
+.fi
+.ke
+
+
+.ks
+.nf
+maybe_real_fraction: (+|-)?"." (5)
+ +- revert
+ 0-7 shift real_fraction
+ 8-9 shift real_fraction
+ acf revert
+ ed revert
+ : revert
+ . revert
+ x revert
+ b revert
+ other revert
+.fi
+.ke
+
+
+.ks
+.nf
+h: (+|-)?[0-9]*[a-f] (6)
+ +- revert
+ 0-7 accept
+ 8-9 accept
+ acf accept
+ ed accept
+ : revert
+ . revert
+ x reduce hex_number
+ b accept
+ other revert
+.fi
+.ke
+
+
+.ks
+.nf
+maybe_hex_or_rexp: (+|-)?[0-9]+[ed] (7)
+ +- shift maybe_rexp
+ 0-7 shift hex_or_rexp
+ 8-9 shift hex_or_rexp
+ acf shift h
+ ed shift h
+ : revert
+ . revert
+ x reduce hex_number
+ b shift h
+ other revert
+.fi
+.ke
+
+
+.ks
+.nf
+maybe_real_number: (+|-)?[0-9]*":" (8)
+ +- revert
+ 0-7 shift r
+ 8-9 shift r
+ acf revert
+ ed revert
+ : accept
+ . revert
+ x revert
+ b revert
+ other revert
+.fi
+.ke
+
+
+.ks
+.nf
+octal_or_hex_number: (+|-)?[0-7]"b" (9)
+ +- reduce octal_number
+ 0-7 shift h
+ 8-9 shift h
+ acf shift h
+ ed shift h
+ : reduce octal_number
+ . reduce octal_number
+ x reduce hex_number
+ b shift h
+ other reduce octal_number
+.fi
+.ke
+
+
+.ks
+.nf
+real_fraction: (+|-)?"."[0-9] (10)
+ +- reduce real_number
+ 0-7 accept
+ 8-9 accept
+ acf reduce real_number
+ ed shift rfr_or_rexp
+ : reduce real_number
+ . reduce real_number
+ x reduce real_number
+ b reduce real_number
+ other reduce real_number
+.fi
+.ke
+
+
+.ks
+.nf
+rfr_or_rexp: (+|-)?"."[0-9]+[ed] (11)
+ +- shift maybe_rexp
+ 0-7 shift rexp
+ 8-9 shift rexp
+ acf revert
+ ed revert
+ : revert
+ . revert
+ x revert
+ b revert
+ other revert
+.fi
+.ke
+
+
+.ks
+.nf
+maybe_rexp: (+|-)?[0-9]+[ed](+|-) (12)
+ +- revert
+ 0-7 shift rexp
+ 8-9 shift rexp
+ acf revert
+ ed revert
+ : revert
+ . revert
+ x revert
+ b revert
+ other revert
+.fi
+.ke
+
+
+.ks
+.nf
+hex_or_rexp: (+|-)?[0-9]+[ed][0-9] (13)
+ +- reduce real_number
+ 0-7 accept
+ 8-9 accept
+ acf shift h
+ ed shift h
+ : reduce real_number
+ . reduce real_number
+ x reduce hex_number
+ b reduce real_number
+ other reduce real_number
+.fi
+.ke
+
+
+.ks
+.nf
+r: (+|-)?[0-9]*":"[0-9] (14)
+ +- reduce real_number
+ 0-7 accept
+ 8-9 accept
+ acf reduce real_number
+ ed shift maybe_rexp
+ : accept
+ . shift maybe_real_fraction
+ x reduce real_number
+ b reduce real_number
+ other reduce real_number
+.fi
+.ke
+
+
+.ks
+.nf
+rexp: (+|-)?[0-9]+[ed](+|-)[0-9] (15)
+ +- reduce real_number
+ 0-7 accept
+ 8-9 accept
+ acf reduce real_number
+ ed reduce real_number
+ : reduce real_number
+ . reduce real_number
+ x reduce real_number
+ b reduce real_number
+ other reduce real_number
+.fi
+.ke
diff --git a/sys/fmtio/dtcscl.x b/sys/fmtio/dtcscl.x
new file mode 100644
index 00000000..afd6adc5
--- /dev/null
+++ b/sys/fmtio/dtcscl.x
@@ -0,0 +1,35 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# DTCSCL -- Scales a double precision real, maintaining maximum precision.
+# Called by DTOC and CTOD.
+
+procedure dtcscl (v, e, sense)
+
+double v # value to be scaled
+int e # exponent
+int sense # sense of scaling (0=apply e to v; 1=calc e)
+
+begin
+ if (sense == 0) # scale v by 10 ** e
+ v = v * (10.0d0 ** e)
+
+ else { # scale number to 0.1 <= v < 1.0
+ if (v == 0.0d0)
+ e = 0
+ else {
+ e = -1
+ while (v < 0.1d0) {
+ v = v * 10.0d0
+ e = e - 1
+ if (v == 0.0d0) { # check for underflow to zero
+ e = 0
+ break
+ }
+ }
+ while (v >= 1.0d0) {
+ v = v / 10.0d0
+ e = e + 1
+ }
+ }
+ }
+end
diff --git a/sys/fmtio/dtoc.x b/sys/fmtio/dtoc.x
new file mode 100644
index 00000000..6bf7571e
--- /dev/null
+++ b/sys/fmtio/dtoc.x
@@ -0,0 +1,129 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+include <printf.h>
+
+# DTOC -- Format and output a floating point number, in any of the formats
+# F,E,G,H, or M (H and M are hours-minutes-seconds and minutes-seconds formats,
+# respectively).
+
+int procedure dtoc (dval, outstr, maxch, decpl, a_fmt, width)
+
+double dval # number to be output
+char outstr[ARB] # output string
+int maxch # size of the output string
+int decpl # number of decimal places or precision
+int a_fmt, fmt # format type (feghm)
+int width # field width
+
+bool neg
+double val
+int op, round, h, m, s, f, v, i
+int dtoc3(), ltoc()
+
+define output {outstr[op]=$1;op=op+1;if(op>maxch)goto retry_}
+define retry_ 91
+
+begin
+ # If HMS format is not desired, simply call DTOC3. Control also
+ # returns to this point in the event of overflow.
+
+ fmt = a_fmt
+ if (IS_UPPER (fmt))
+ fmt = TO_LOWER (fmt)
+
+ if (fmt == FMT_FIXED || fmt == FMT_EXPON || fmt == FMT_GENERAL ||
+ IS_INDEFD(dval)) {
+retry_
+ return (dtoc3 (dval, outstr, maxch, decpl, fmt, width))
+ }
+
+ # HMS format is implemented using calls to DTOC3, LTOC. Use zero
+ # fill to get two chars for the second and third fields, if necessary.
+ # The second field is omitted for "m" format. No whitespace is
+ # permitted in an HMS (or other) number. If the format is %H or %M
+ # (instead of the usual %h or %m) scale the number by 15 before output
+ # (converting degrees to hours).
+
+ if (IS_UPPER (a_fmt))
+ val = dval / 15.0
+ else
+ val = dval
+
+ # Working with a positive number simplifies things.
+ neg = (val < 0.0)
+ if (neg)
+ val = -val
+
+ # Decompose number into HMS or MS.
+ h = 0
+ if (fmt == FMT_HMS) {
+ h = int(val); val = (val - h) * 60.0
+ }
+ m = int(val); val = (val - m) * 60.0
+ s = int(val); val = (val - s)
+
+ # Round the fractional seconds field and carry if the rounded value
+ # is greater than 60. This has to be done explicitly due to the
+ # "base 60" sexagesimal arithmetic.
+
+ round = (10.0 ** decpl)
+ f = int (val * round + 0.5)
+ while (f >= round) {
+ f = f - round
+ s = s + 1
+ }
+ while (s >= 60) {
+ s = s - 60
+ m = m + 1
+ }
+ while (m >= 60) {
+ m = m - 60
+ h = h + 1
+ }
+
+ # Format the output string.
+ op = 1
+ if (neg)
+ output ('-')
+
+ # Output the first field, which is the hours field for HMS format,
+ # or the minutes field for MS format.
+
+ if (fmt == FMT_HMS)
+ v = h
+ else
+ v = h * 60 + m
+ op = op + ltoc (v, outstr[op], maxch-op+1)
+ output (':')
+
+ # Output the minutes field in HMS format.
+ if (fmt == FMT_HMS) {
+ output (TO_DIGIT (m / 10))
+ output (TO_DIGIT (mod (m, 10)))
+ output (':')
+ }
+
+ # Output the seconds field.
+ output (TO_DIGIT (s / 10))
+ output (TO_DIGIT (mod (s, 10)))
+
+ # Output the fraction, if any.
+ if (decpl > 0) {
+ output ('.')
+ do i = 1, decpl {
+ round = round / 10
+ output (TO_DIGIT (f / round))
+ f = mod (f, round)
+ }
+ }
+
+ # If the HMS format does not fit, go try a more compact format.
+ if (op-1 > abs(width) || op > maxch) {
+ fmt = FMT_GENERAL
+ goto retry_
+ }
+
+ outstr[op] = EOS
+ return (op-1)
+end
diff --git a/sys/fmtio/dtoc3.x b/sys/fmtio/dtoc3.x
new file mode 100644
index 00000000..76101dd1
--- /dev/null
+++ b/sys/fmtio/dtoc3.x
@@ -0,0 +1,285 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include <ctype.h>
+include <printf.h>
+
+.help dtoc3
+.nf ___________________________________________________________________________
+This routine is based on the TOOLS routine of the same name by J. Chong.
+The major changes are translation to the IRAF pp language, implementation
+of the "G" format, and some restructuring to avoid duplicated code.
+This procedure is called by the more general routine DTOC, which adds the
+additional format type "H" (sexageximal, or hms format).
+
+Formats: f Fixed format. D == number of decimal places. If D
+ is -1 (or less), the "." will not be printed. If the
+ number being printed is too large to fit in F format,
+ "E" format will be used instead. Nonsignificant
+ digits are returned as zeros.
+
+ e Exponential format. D == number of significant digits.
+
+ g General format. D == number of significant digits.
+ The actual format used may be either F or E, depending
+ on which is smaller. If D is negative, the "." will
+ be omitted if possible (abs(D) remains the precision).
+
+If the field width is too small to convert the number, the field will be
+filled with asterisks instead. If the number being converted is INDEF, the
+string "INDEF" will be returned. The number is rounded to the desired precision
+before being printed.
+.endhelp ______________________________________________________________________
+
+# DTOC3 --- Convert double precision real to string.
+
+int procedure dtoc3 (val, out, maxch, decpl, a_fmt, width)
+
+double val # value to be encoded
+char out[ARB] # output string
+int maxch # max chars out
+int decpl # precision
+int a_fmt # type of encoding ('f', 'e', etc.)
+int width # field width
+
+double v
+char digits[MAX_DIGITS]
+bool neg, small, exp_format, squeeze
+int i, w, d, e, j, len, no_digits, max_size, e_size, f_size, fmt
+int itoc(), gstrcpy()
+
+begin
+ # Set flags indicating whether the number is greater or less that zero,
+ # and whether its absolute value is greater or less than 1.
+
+ v = abs (val)
+ w = abs (width)
+
+ fmt = a_fmt
+ if (IS_UPPER(a_fmt))
+ fmt = TO_LOWER(a_fmt)
+ squeeze = (fmt == FMT_GENERAL)
+ neg = (val < 0.0)
+ small = (v < 0.1)
+
+ if (squeeze)
+ d = abs (decpl)
+ else
+ d = max (0, decpl)
+
+ if (IS_INDEFD (val)) # INDEF is a special case
+ return (gstrcpy ("INDEF", out, w))
+
+ # Scale number to 0.1 <= v < 1.0
+ call dtcscl (v, e, 1)
+
+ # Start tally for the maximum size of the number to determine if an
+ # error should be returned.
+ if (neg) # 1 for neg, plus 1 for .
+ max_size = 2
+ else
+ max_size = 1
+ no_digits = min (MAX_DIGITS, d)
+
+
+ # Determine exact format for printing.
+
+ len = abs (e) # base size of E format
+ e_size = 1
+ for (i=10; i <= 10000; i=i*10) {
+ if (len < i)
+ break
+ e_size = e_size + 1
+ }
+ e_size = e_size + max_size + 1
+ if (e < 0)
+ e_size = e_size + 1 # allow space for leading '0'
+
+ if (squeeze) { # G-format: find best format
+ e_size = e_size + d
+ if (e > 0)
+ f_size = max (d, e + 1)
+ else
+ f_size = d - e
+ f_size = f_size + max_size
+ } else if (fmt == FMT_FIXED)
+ f_size = max (e, 1) + max_size + d
+
+
+ if (squeeze) { # 'G' format
+ if (f_size <= e_size) {
+ exp_format = false
+ if (e > 0)
+ no_digits = f_size - max_size
+ max_size = f_size
+ } else {
+ exp_format = true
+ max_size = e_size
+ }
+ d = w # deactivate dec-places count
+
+ } else if (fmt == FMT_FIXED) { # Fortran 'F' format
+ exp_format = f_size > w
+
+ if (exp_format) { # is there too little space?
+ no_digits = max (1, w - e_size)
+ max_size = no_digits + e_size
+ } else {
+ no_digits = e + d + 1 # negative e is OK here
+ max_size = f_size
+ }
+
+ } else { # Fortran 'E' format
+ exp_format = true
+ max_size = e_size + d
+ d = w
+ }
+
+ # Round the number at digit (no_digits + 1).
+ if (no_digits >= 0)
+ v = v + 0.5 * 10. ** (-no_digits)
+
+ # Be sure the number of digits is in range.
+ no_digits = max(1, min(MAX_DIGITS, no_digits))
+
+ # Handle the unusual situation of rounding from .999.. up to 1.000.
+ if (v >= 1.0) {
+ v = v / 10.0
+ e = e + 1
+ if (!exp_format) {
+ max_size = max_size + 1
+ no_digits = min (MAX_DIGITS, no_digits + 1)
+ }
+ }
+
+ # See if the number will fit in 'w' characters
+ if (max_size > w) {
+ for (i=1; i <= w; i=i+1)
+ out[i] = OVFL_CHAR
+ out[i] = EOS
+ return (w)
+ }
+
+ # Extract the first <no_digits> digits. At the start V is normalized
+ # to a value less than 1.0. The algorithm is to multiply by ten and
+ # take the integer part to get each digit.
+
+ do i = 1, no_digits {
+ v = v * 10.0d0
+ j = int (v + EPSILOND) # truncate to integer
+ v = v - j # lop off the integral part
+
+ # Make sure the next iteration will produce a decimal J in the
+ # range 1-9. On some systems, due to precision problems J=int(V)
+ # can be off by one compared to V-J and this will result in a J
+ # of 10 in the next iteration. The expression below attempts to
+ # look ahead for J>=10 and adjusts the J for the current iteration
+ # up by one if this will occur.
+
+ if (int (v * 10.0d0 + EPSILOND) >= 10) {
+ j = j + 1
+ v = v - 1
+ if (v < 0)
+ v = 0
+ }
+
+ digits[i] = TO_DIGIT(j)
+ }
+
+ # Take digit string and exponent and arrange into desired format.
+ len = 1
+ if (neg) {
+ out[1] = '-'
+ len = len + 1
+ }
+
+ if (exp_format) { # set up exponential format
+ out[len] = digits[1]
+ out[len+1] = '.'
+ len = len + 2
+ for (i=2; i <= no_digits; i=i+1) {
+ out[len] = digits[i]
+ len = len + 1
+ }
+ out[len] = 'E'
+ len = len + 1
+ if (e < 0) {
+ out[len] = '-'
+ len = len + 1
+ e = -e
+ }
+ len = len + itoc (e, out[len], w - len + 1)
+
+ } else if (e >= no_digits) {
+ # Handle numbers >= 1 with dp after figures.
+ for (i=1; i <= no_digits; i=i+1) {
+ out[len] = digits[i]
+ len = len + 1
+ }
+ for (i=no_digits; i <= e; i=i+1) {
+ out[len] = '0'
+ len = len + 1
+ }
+ if (decpl > 0) {
+ out[len] = '.'
+ len = len + 1
+ if (!squeeze) {
+ for (i=1; i <= d; i=i+1) {
+ out[len] = '0'
+ len = len + 1
+ }
+ }
+ }
+
+ } else {
+ if (e < 0) {
+ # Handle fixed numbers < 1.
+ if (d == 0 && e == -1 && digits[1] >= '5')
+ out[len] = '1'
+ else
+ out[len] = '0'
+ out[len + 1] = '.'
+ len = len + 2
+ for (i=1; i < -e && d > 0; i=i+1) {
+ out[len] = '0'
+ len = len + 1
+ d = d - 1
+ }
+ i = 1
+ } else {
+ # Handle numbers > 1 with dp inside figures.
+ e = e + 2 # one more zero below
+ for (i=1; i < e; i=i+1) {
+ out[len] = digits[i]
+ len = len + 1
+ }
+ if (decpl > 0) {
+ out[len] = '.'
+ len = len + 1
+ }
+ }
+
+ for (j=1; i <= no_digits && j <= d; j=j+1) {
+ out[len] = digits[i]
+ i = i + 1
+ len = len + 1
+ }
+ if (squeeze) {
+ while (len > 2) { # skip trailing zeroes
+ len = len - 1
+ if (out[len] != '0') {
+ len = len + 1 # non-digit -- keep it
+ break
+ }
+ }
+ } else {
+ for (i=1; i < d+e-no_digits && i <= d; i=i+1) {
+ out[len] = '0'
+ len = len + 1
+ }
+ }
+ }
+
+ out[len] = EOS
+ return (len - 1)
+end
diff --git a/sys/fmtio/eprintf.x b/sys/fmtio/eprintf.x
new file mode 100644
index 00000000..51b2d196
--- /dev/null
+++ b/sys/fmtio/eprintf.x
@@ -0,0 +1,14 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <printf.h>
+
+# EPRINTF -- Format output to the standard error output.
+
+procedure eprintf (format_string)
+
+char format_string[ARB]
+
+begin
+ call flush (STDOUT)
+ call fprntf (STDERR, format_string, REGULAR_FILE)
+end
diff --git a/sys/fmtio/escchars.inc b/sys/fmtio/escchars.inc
new file mode 100644
index 00000000..d3c4d33e
--- /dev/null
+++ b/sys/fmtio/escchars.inc
@@ -0,0 +1,5 @@
+
+# Escape sequences
+
+string escape_chars "ntfr'\"\\"
+string mapped_chars "\n\t\f\r'\"\\"
diff --git a/sys/fmtio/evexpr.com b/sys/fmtio/evexpr.com
new file mode 100644
index 00000000..65488986
--- /dev/null
+++ b/sys/fmtio/evexpr.com
@@ -0,0 +1,7 @@
+# EVEXPR common.
+
+pointer ev_oval # pointer to expr value operand
+int ev_getop # user supplied get operand procedure
+int ev_ufcn # user supplied function call procedure
+
+common /xevcom/ ev_oval, ev_getop, ev_ufcn
diff --git a/sys/fmtio/evexpr.x b/sys/fmtio/evexpr.x
new file mode 100644
index 00000000..4d512a20
--- /dev/null
+++ b/sys/fmtio/evexpr.x
@@ -0,0 +1,1477 @@
+
+# line 2 "evexpr.y"
+include <lexnum.h>
+include <ctype.h>
+include <mach.h>
+include <evexpr.h>
+
+define YYMAXDEPTH 64 # parser stack length
+define MAX_ARGS 16 # max args in a function call
+define yyparse xev_parse
+
+define DTOR (($1)/57.2957795)
+define RTOD (($1)*57.2957795)
+
+# Arglist structure.
+define LEN_ARGSTRUCT (1+MAX_ARGS+(MAX_ARGS*LEN_OPERAND))
+define A_NARGS Memi[$1] # number of arguments
+define A_ARGP Memi[$1+$2] # array of pointers to operand structs
+define A_OPS ($1+MAX_ARGS+1) # offset to operand storage area
+
+# Intrinsic functions.
+
+define KEYWORDS "|abs|acos|asin|atan|atan2|bool|cos|exp|int|log|log10|\
+ |max|min|mod|nint|real|sin|sqrt|str|tan|"
+
+define F_ABS 01 # function codes
+define F_ACOS 02
+define F_ASIN 03
+define F_ATAN 04
+define F_ATAN2 05
+define F_BOOL 06
+define F_COS 07
+define F_EXP 08
+define F_INT 09
+define F_LOG 10
+define F_LOG10 11
+ # newline 12
+define F_MAX 13
+define F_MIN 14
+define F_MOD 15
+define F_NINT 16
+define F_REAL 17
+define F_SIN 18
+define F_SQRT 19
+define F_STR 20
+define F_TAN 21
+
+
+# EVEXPR -- Evaluate an expression. This is the top level procedure, and the
+# only externally callable entry point. Input consists of the expression to
+# be evaluated (a string) and, optionally, user procedures for fetching
+# external operands and executing external functions. Output is a pointer to
+# an operand structure containing the computed value of the expression.
+# The output operand structure is dynamically allocated by EVEXPR and must be
+# freed by the user.
+#
+# N.B.: this is not intended to be an especially efficient procedure. Rather,
+# this is a high level, easy to use procedure, intended to provide greater
+# flexibility in the parameterization of applications programs.
+
+pointer procedure evexpr (expr, getop_epa, ufcn_epa)
+
+char expr[ARB] # expression to be evaluated
+int getop_epa # user supplied get operand procedure
+int ufcn_epa # user supplied function call procedure
+
+int junk
+bool debug
+pointer sp, ip
+extern xev_gettok()
+int strlen(), xev_parse()
+
+errchk xev_parse, calloc
+include "evexpr.com"
+data debug /false/
+
+begin
+ call smark (sp)
+
+ # Set user function entry point addresses.
+ ev_getop = getop_epa
+ ev_ufcn = ufcn_epa
+
+ # Allocate an operand struct for the expression value.
+ call calloc (ev_oval, LEN_OPERAND, TY_STRUCT)
+
+ # Make a local copy of the input string.
+ call salloc (ip, strlen(expr), TY_CHAR)
+ call strcpy (expr, Memc[ip], ARB)
+
+ # Evaluate the expression. The expression value is copied into the
+ # output operand structure by XEV_PARSE, given the operand pointer
+ # passed in common. A common must be used since the standard parser
+ # subroutine has a fixed calling sequence.
+
+ junk = xev_parse (ip, debug, xev_gettok)
+
+ call sfree (sp)
+ return (ev_oval)
+end
+
+define CONSTANT 257
+define IDENTIFIER 258
+define NEWLINE 259
+define YYEOS 260
+define PLUS 261
+define MINUS 262
+define STAR 263
+define SLASH 264
+define EXPON 265
+define CONCAT 266
+define QUEST 267
+define COLON 268
+define LT 269
+define GT 270
+define LE 271
+define EQ 272
+define NE 273
+define SE 274
+define AND 275
+define OR 276
+define NOT 277
+define AT 278
+define GE 279
+define UMINUS 280
+define yyclearin yychar = -1
+define yyerrok yyerrflag = 0
+define YYMOVE call amovi (Memi[$1], Memi[$2], YYOPLEN)
+define YYERRCODE 256
+
+
+
+# XEV_UNOP -- Unary operation. Perform the indicated unary operation on the
+# input operand, returning the result as the output operand.
+
+procedure xev_unop (opcode, in, out)
+
+int opcode # operation to be performed
+pointer in # input operand
+pointer out # output operand
+
+errchk xev_error
+define badsw_ 91
+
+begin
+ call xev_initop (out, 0, O_TYPE(in))
+
+ switch (opcode) {
+ case MINUS:
+ # Unary negation.
+ switch (O_TYPE(in)) {
+ case TY_BOOL, TY_CHAR:
+ call xev_error ("negation of a nonarithmetic operand")
+ case TY_INT:
+ O_VALI(out) = -O_VALI(in)
+ case TY_REAL:
+ O_VALR(out) = -O_VALR(in)
+ default:
+ goto badsw_
+ }
+
+ case NOT:
+ switch (O_TYPE(in)) {
+ case TY_BOOL:
+ O_VALB(out) = !O_VALB(in)
+ case TY_CHAR, TY_INT, TY_REAL:
+ call xev_error ("not of a nonlogical")
+ default:
+ goto badsw_
+ }
+
+ default:
+badsw_ call xev_error ("bad switch in unop")
+ }
+end
+
+
+# XEV_BINOP -- Binary operation. Perform the indicated arithmetic binary
+# operation on the two input operands, returning the result as the output
+# operand.
+
+procedure xev_binop (opcode, in1, in2, out)
+
+int opcode # operation to be performed
+pointer in1, in2 # input operands
+pointer out # output operand
+
+real r1, r2
+int i1, i2, dtype, nchars
+int xev_newtype(), strlen()
+errchk xev_newtype
+
+begin
+ # Set the datatype of the output operand, taking an error action if
+ # the operands have incompatible datatypes.
+
+ dtype = xev_newtype (O_TYPE(in1), O_TYPE(in2))
+ call xev_initop (out, 0, dtype)
+
+ switch (dtype) {
+ case TY_BOOL:
+ call xev_error ("operation illegal for boolean operands")
+ case TY_CHAR:
+ if (opcode != CONCAT)
+ call xev_error ("operation illegal for string operands")
+ case TY_INT:
+ i1 = O_VALI(in1)
+ i2 = O_VALI(in2)
+ case TY_REAL:
+ if (O_TYPE(in1) == TY_INT)
+ r1 = O_VALI(in1)
+ else
+ r1 = O_VALR(in1)
+ if (O_TYPE(in2) == TY_INT)
+ r2 = O_VALI(in2)
+ else
+ r2 = O_VALR(in2)
+ default:
+ call xev_error ("unknown datatype code in binop")
+ }
+
+ # Perform the operation.
+ switch (opcode) {
+ case PLUS:
+ if (dtype == TY_INT)
+ O_VALI(out) = i1 + i2
+ else
+ O_VALR(out) = r1 + r2
+
+ case MINUS:
+ if (dtype == TY_INT)
+ O_VALI(out) = i1 - i2
+ else
+ O_VALR(out) = r1 - r2
+
+ case STAR:
+ if (dtype == TY_INT)
+ O_VALI(out) = i1 * i2
+ else
+ O_VALR(out) = r1 * r2
+
+ case SLASH:
+ if (dtype == TY_INT)
+ O_VALI(out) = i1 / i2
+ else
+ O_VALR(out) = r1 / r2
+
+ case EXPON:
+ if (dtype == TY_INT)
+ O_VALI(out) = i1 ** i2
+ else if (O_TYPE(in1) == TY_REAL && O_TYPE(in2) == TY_INT)
+ O_VALR(out) = r1 ** (O_VALI(in2))
+ else
+ O_VALR(out) = r1 ** r2
+
+ case CONCAT:
+ if (dtype != TY_CHAR)
+ call xev_error ("concatenation of a nonstring operand")
+ nchars = strlen (O_VALC(in1)) + strlen (O_VALC(in2))
+ call xev_makeop (out, nchars, TY_CHAR)
+ call strcpy (O_VALC(in1), O_VALC(out), ARB)
+ call strcat (O_VALC(in2), O_VALC(out), ARB)
+ call xev_freeop (in1)
+ call xev_freeop (in2)
+
+ default:
+ call xev_error ("bad switch in binop")
+ }
+end
+
+
+# XEV_BOOLOP -- Boolean binary operations. Perform the indicated boolean binary
+# operation on the two input operands, returning the result as the output
+# operand.
+
+procedure xev_boolop (opcode, in1, in2, out)
+
+int opcode # operation to be performed
+pointer in1, in2 # input operands
+pointer out # output operand
+
+bool result
+real r1, r2
+int i1, i2, dtype
+int xev_newtype(), xev_patmatch(), strncmp()
+errchk xev_newtype, xev_error
+define badsw_ 91
+
+begin
+ # Set the datatype of the output operand, taking an error action if
+ # the operands have incompatible datatypes.
+
+ dtype = xev_newtype (O_TYPE(in1), O_TYPE(in2))
+ call xev_initop (out, 0, dtype)
+
+ switch (opcode) {
+ case AND, OR:
+ if (dtype != TY_BOOL)
+ call xev_error ("AND or OR of nonlogical")
+ case LT, GT, LE, GE:
+ if (dtype == TY_BOOL)
+ call xev_error ("order comparison of a boolean operand")
+ }
+
+ if (dtype == TY_INT) {
+ i1 = O_VALI(in1)
+ i2 = O_VALI(in2)
+ } else if (dtype == TY_REAL) {
+ if (O_TYPE(in1) == TY_INT) {
+ i1 = O_VALI(in1)
+ r1 = i1
+ } else
+ r1 = O_VALR(in1)
+ if (O_TYPE(in2) == TY_INT) {
+ i2 = O_VALI(in2)
+ r2 = i2
+ } else
+ r2 = O_VALR(in2)
+ }
+
+ # Perform the operation.
+ switch (opcode) {
+ case AND:
+ result = O_VALB(in1) && O_VALB(in2)
+ case OR:
+ result = O_VALB(in1) || O_VALB(in2)
+
+ case LT, GE:
+ if (dtype == TY_INT)
+ result = i1 < i2
+ else if (dtype == TY_REAL)
+ result = r1 < r2
+ else
+ result = strncmp (O_VALC(in1), O_VALC(in2), ARB) < 0
+ if (opcode == GE)
+ result = !result
+
+ case GT, LE:
+ if (dtype == TY_INT)
+ result = i1 > i2
+ else if (dtype == TY_REAL)
+ result = r1 > r2
+ else
+ result = strncmp (O_VALC(in1), O_VALC(in2), ARB) > 0
+ if (opcode == LE)
+ result = !result
+
+ case EQ, SE, NE:
+ switch (dtype) {
+ case TY_BOOL:
+ if (O_VALB(in1))
+ result = O_VALB(in2)
+ else
+ result = !O_VALB(in2)
+ case TY_CHAR:
+ if (opcode == SE)
+ result = xev_patmatch (O_VALC(in1), O_VALC(in2)) > 0
+ else
+ result = strncmp (O_VALC(in1), O_VALC(in2), ARB) == 0
+ case TY_INT:
+ result = i1 == i2
+ case TY_REAL:
+ result = r1 == r2
+ default:
+ goto badsw_
+ }
+ if (opcode == NE)
+ result = !result
+
+ default:
+badsw_ call xev_error ("bad switch in boolop")
+ }
+
+ call xev_makeop (out, 0, TY_BOOL)
+ O_VALB(out) = result
+
+ # Free storage if there were any string type input operands.
+ call xev_freeop (in1)
+ call xev_freeop (in2)
+end
+
+
+# XEV_PATMATCH -- Match a string against a pattern, returning the patmatch
+# index if the string matches. The pattern may contain any of the conventional
+# pattern matching metacharacters. Closure (i.e., "*") is mapped to "?*".
+
+int procedure xev_patmatch (str, pat)
+
+char str[ARB] # operand string
+char pat[ARB] # pattern
+
+int junk, ip, index
+pointer sp, patstr, patbuf, op
+int patmake(), patmatch()
+
+begin
+ call smark (sp)
+ call salloc (patstr, SZ_FNAME, TY_CHAR)
+ call salloc (patbuf, SZ_LINE, TY_CHAR)
+ call aclrc (Memc[patstr], SZ_FNAME)
+ call aclrc (Memc[patbuf], SZ_LINE)
+
+ # Map pattern, changing '*' into '?*'.
+ op = patstr
+ for (ip=1; pat[ip] != EOS; ip=ip+1) {
+ if (pat[ip] == '*') {
+ Memc[op] = '?'
+ op = op + 1
+ }
+ Memc[op] = pat[ip]
+ op = op + 1
+ }
+
+ # Encode pattern.
+ junk = patmake (Memc[patstr], Memc[patbuf], SZ_LINE)
+
+ # Perform the pattern matching operation.
+ index = patmatch (str, Memc[patbuf])
+
+ call sfree (sp)
+ return (index)
+end
+
+
+# XEV_NEWTYPE -- Get the datatype of a binary operation, given the datatypes
+# of the two input operands. An error action is taken if the datatypes are
+# incompatible, e.g., boolean and anything else or string and anything else.
+
+int procedure xev_newtype (type1, type2)
+
+int type1, type2
+int newtype, p, q, i
+int tyindex[NTYPES], ttbl[NTYPES*NTYPES]
+data tyindex /TY_BOOL, TY_CHAR, TY_INT, TY_REAL/
+data (ttbl(i),i=1,4) /TY_BOOL, 0, 0, 0/
+data (ttbl(i),i=5,8) / 0, TY_CHAR, 0, 0/
+data (ttbl(i),i=9,12) / 0, 0, TY_INT, TY_REAL/
+data (ttbl(i),i=13,16) / 0, 0, TY_REAL, TY_REAL/
+
+begin
+ do i = 1, NTYPES {
+ if (tyindex[i] == type1)
+ p = i
+ if (tyindex[i] == type2)
+ q = i
+ }
+
+ newtype = ttbl[(p-1)*NTYPES+q]
+ if (newtype == 0)
+ call xev_error ("operands have incompatible types")
+ else
+ return (newtype)
+end
+
+
+# XEV_QUEST -- Conditional expression. If the condition operand is true
+# return the first (true) operand, else return the second (false) operand.
+
+procedure xev_quest (cond, trueop, falseop, out)
+
+pointer cond # pointer to condition operand
+pointer trueop, falseop # pointer to true,false operands
+pointer out # pointer to output operand
+errchk xev_error
+
+begin
+ if (O_TYPE(cond) != TY_BOOL)
+ call xev_error ("nonboolean condition operand")
+
+ if (O_VALB(cond)) {
+ YYMOVE (trueop, out)
+ call xev_freeop (falseop)
+ } else {
+ YYMOVE (falseop, out)
+ call xev_freeop (trueop)
+ }
+end
+
+
+# XEV_CALLFCN -- Call an intrinsic function. If the function named is not
+# one of the standard intrinsic functions, call an external user function
+# if a function call procedure was supplied.
+
+procedure xev_callfcn (fcn, args, nargs, out)
+
+char fcn[ARB] # function to be called
+pointer args[ARB] # pointer to arglist descriptor
+int nargs # number of arguments
+pointer out # output operand (function value)
+
+real rresult, rval[2], rtemp
+int iresult, ival[2], type[2], optype, oplen, itemp
+int opcode, v_nargs, i
+pointer sp, buf, ap
+include "evexpr.com"
+
+bool strne()
+int strdic(), strlen()
+errchk zcall4, xev_error1, xev_error2, malloc
+string keywords KEYWORDS
+define badtype_ 91
+define free_ 92
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_FNAME, TY_CHAR)
+
+ oplen = 0
+
+ # Lookup the function name in the dictionary. An exact match is
+ # required (strdic permits abbreviations).
+
+ opcode = strdic (fcn, Memc[buf], SZ_FNAME, keywords)
+ if (opcode > 0 && strne(fcn,Memc[buf]))
+ opcode = 0
+
+ # If the function named is not a standard one and the user has supplied
+ # the entry point of an external function evaluation procedure, call
+ # the user procedure to evaluate the function, otherwise abort.
+
+ if (opcode <= 0)
+ if (ev_ufcn != NULL) {
+ call zcall4 (ev_ufcn, fcn, args, nargs, out)
+ goto free_
+ } else
+ call xev_error1 ("unknown function `%s' called", fcn)
+
+ # Verify correct number of arguments.
+ switch (opcode) {
+ case F_MOD:
+ v_nargs = 2
+ case F_MAX, F_MIN, F_ATAN, F_ATAN2:
+ v_nargs = -1
+ default:
+ v_nargs = 1
+ }
+
+ if (v_nargs > 0 && nargs != v_nargs)
+ call xev_error2 ("function `%s' requires %d arguments",
+ fcn, v_nargs)
+ else if (v_nargs < 0 && nargs < abs(v_nargs))
+ call xev_error2 ("function `%s' requires at least %d arguments",
+ fcn, abs(v_nargs))
+
+ # Verify datatypes.
+ if (opcode != F_STR && opcode != F_BOOL) {
+ optype = TY_REAL
+ do i = 1, min(2,nargs) {
+ switch (O_TYPE(args[i])) {
+ case TY_INT:
+ ival[i] = O_VALI(args[i])
+ rval[i] = ival[i]
+ type[i] = TY_INT
+ case TY_REAL:
+ rval[i] = O_VALR(args[i])
+ ival[i] = nint (rval[i])
+ type[i] = TY_REAL
+ default:
+ goto badtype_
+ }
+ }
+ }
+
+ # Evaluate the function.
+
+ ap = args[1]
+
+ switch (opcode) {
+ case F_ABS:
+ if (type[1] == TY_INT) {
+ iresult = abs (ival[1])
+ optype = TY_INT
+ } else
+ rresult = abs (rval[1])
+
+ case F_ACOS:
+ rresult = RTOD (acos (rval[1]))
+ case F_ASIN:
+ rresult = RTOD (asin (rval[1]))
+ case F_COS:
+ rresult = cos (DTOR (rval[1]))
+ case F_EXP:
+ rresult = exp (rval[1])
+ case F_LOG:
+ rresult = log (rval[1])
+ case F_LOG10:
+ rresult = log10 (rval[1])
+ case F_SIN:
+ rresult = sin (DTOR (rval[1]))
+ case F_SQRT:
+ rresult = sqrt (rval[1])
+ case F_TAN:
+ rresult = tan (DTOR (rval[1]))
+
+ case F_ATAN, F_ATAN2:
+ if (nargs == 1)
+ rresult = RTOD (atan (rval[1]))
+ else
+ rresult = RTOD (atan2 (rval[1], rval[2]))
+
+ case F_MOD:
+ if (type[1] == TY_REAL || type[2] == TY_REAL)
+ rresult = mod (rval[1], rval[2])
+ else {
+ iresult = mod (ival[1], ival[2])
+ optype = TY_INT
+ }
+
+ case F_NINT:
+ iresult = nint (rval[1])
+ optype = TY_INT
+
+ case F_MAX, F_MIN:
+ # Determine datatype of result.
+ optype = TY_INT
+ do i = 1, nargs
+ if (O_TYPE(args[i]) == TY_REAL)
+ optype = TY_REAL
+ else if (O_TYPE(args[i]) != TY_INT)
+ goto badtype_
+
+ # Compute result.
+ if (optype == TY_INT) {
+ iresult = O_VALI(ap)
+ do i = 2, nargs {
+ itemp = O_VALI(args[i])
+ if (opcode == F_MAX)
+ iresult = max (iresult, itemp)
+ else
+ iresult = min (iresult, itemp)
+ }
+
+ } else {
+ if (O_TYPE(ap) == TY_INT)
+ rresult = O_VALI(ap)
+ else
+ rresult = O_VALR(ap)
+
+ do i = 2, nargs {
+ if (O_TYPE(args[i]) == TY_INT)
+ rtemp = O_VALI(args[i])
+ else
+ rtemp = O_VALR(args[i])
+ if (opcode == F_MAX)
+ rresult = max (rresult, rtemp)
+ else
+ rresult = min (rresult, rtemp)
+ }
+ }
+
+ case F_BOOL:
+ optype = TY_BOOL
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_VALB(ap))
+ iresult = 1
+ else
+ iresult = 0
+ case TY_CHAR:
+ iresult = strlen (O_VALC(ap))
+ case TY_INT:
+ iresult = O_VALI(ap)
+ case TY_REAL:
+ if (abs(rval[1]) > .001)
+ iresult = 1
+ else
+ iresult = 0
+ default:
+ goto badtype_
+ }
+
+ case F_INT:
+ optype = TY_INT
+ if (type[1] == TY_INT)
+ iresult = ival[1]
+ else
+ iresult = rval[1]
+
+ case F_REAL:
+ rresult = rval[1]
+
+ case F_STR:
+ # Convert operand to operand of type string.
+
+ optype = TY_CHAR
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ call malloc (iresult, 3, TY_CHAR)
+ oplen = 3
+ if (O_VALB(ap))
+ call strcpy ("yes", Memc[iresult], 3)
+ else
+ call strcpy ("no", Memc[iresult], 3)
+ case TY_CHAR:
+ oplen = strlen (O_VALC(ap))
+ call malloc (iresult, oplen, TY_CHAR)
+ call strcpy (O_VALC(ap), Memc[iresult], ARB)
+ case TY_INT:
+ oplen = MAX_DIGITS
+ call malloc (iresult, oplen, TY_CHAR)
+ call sprintf (Memc[iresult], SZ_FNAME, "%d")
+ call pargi (O_VALI(ap))
+ case TY_REAL:
+ oplen = MAX_DIGITS
+ call malloc (iresult, oplen, TY_CHAR)
+ call sprintf (Memc[iresult], SZ_FNAME, "%g")
+ call pargr (O_VALR(ap))
+ default:
+ goto badtype_
+ }
+
+ default:
+ call xev_error ("bad switch in callfcn")
+ }
+
+ # Write the result to the output operand. Bool results are stored in
+ # iresult as an integer value, string results are stored in iresult as
+ # a pointer to the output string, and integer and real results are
+ # stored in iresult and rresult without any tricks.
+
+ call xev_initop (out, oplen, optype)
+
+ switch (optype) {
+ case TY_BOOL:
+ O_VALB(out) = (iresult != 0)
+ case TY_CHAR:
+ O_VALP(out) = iresult
+ case TY_INT:
+ O_VALI(out) = iresult
+ case TY_REAL:
+ O_VALR(out) = rresult
+ }
+
+free_
+ # Free any storage used by the argument list operands.
+ do i = 1, nargs
+ call xev_freeop (args[i])
+
+ call sfree (sp)
+ return
+
+badtype_
+ call xev_error1 ("bad argument to function `%s'", fcn)
+ call sfree (sp)
+ return
+end
+
+
+# XEV_STARTARGLIST -- Allocate an argument list descriptor to receive
+# arguments as a function call is parsed. We are called with either
+# zero or one arguments. The argument list descriptor is pointed to by
+# a ficticious operand. The descriptor itself contains a count of the
+# number of arguments, an array of pointers to the operand structures,
+# as well as storage for the operand structures. The operands must be
+# stored locally since the parser will discard its copy of the operand
+# structure for each argument as the associated grammar rule is reduced.
+
+procedure xev_startarglist (arg, out)
+
+pointer arg # pointer to first argument, or NULL
+pointer out # output operand pointing to arg descriptor
+pointer ap
+
+errchk malloc
+
+begin
+ call xev_initop (out, 0, TY_POINTER)
+ call malloc (ap, LEN_ARGSTRUCT, TY_STRUCT)
+ O_VALP(out) = ap
+
+ if (arg == NULL)
+ A_NARGS(ap) = 0
+ else {
+ A_NARGS(ap) = 1
+ A_ARGP(ap,1) = A_OPS(ap)
+ YYMOVE (arg, A_OPS(ap))
+ }
+end
+
+
+# XEV_ADDARG -- Add an argument to the argument list for a function call.
+
+procedure xev_addarg (arg, arglist, out)
+
+pointer arg # pointer to argument to be added
+pointer arglist # pointer to operand pointing to arglist
+pointer out # output operand pointing to arg descriptor
+
+pointer ap, o
+int nargs
+
+begin
+ ap = O_VALP(arglist)
+
+ nargs = A_NARGS(ap) + 1
+ A_NARGS(ap) = nargs
+ if (nargs > MAX_ARGS)
+ call xev_error ("too many function arguments")
+
+ o = A_OPS(ap) + ((nargs - 1) * LEN_OPERAND)
+ A_ARGP(ap,nargs) = o
+ YYMOVE (arg, o)
+
+ YYMOVE (arglist, out)
+end
+
+
+# XEV_ERROR1 -- Take an error action, formatting an error message with one
+# format string plus one string argument.
+
+procedure xev_error1 (fmt, arg)
+
+char fmt[ARB] # printf format string
+char arg[ARB] # string argument
+pointer sp, buf
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_LINE, TY_CHAR)
+
+ call sprintf (Memc[buf], SZ_LINE, fmt)
+ call pargstr (arg)
+
+ call xev_error (Memc[buf])
+ call sfree (sp)
+end
+
+
+# XEV_ERROR2 -- Take an error action, formatting an error message with one
+# format string plus one string argument and one integer argument.
+
+procedure xev_error2 (fmt, arg1, arg2)
+
+char fmt[ARB] # printf format string
+char arg1[ARB] # string argument
+int arg2 # integer argument
+pointer sp, buf
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_LINE, TY_CHAR)
+
+ call sprintf (Memc[buf], SZ_LINE, fmt)
+ call pargstr (arg1)
+ call pargi (arg2)
+
+ call xev_error (Memc[buf])
+ call sfree (sp)
+end
+
+
+# XEV_ERROR -- Take an error action, given an error message string as the
+# sole argument.
+
+procedure xev_error (errmsg)
+
+char errmsg[ARB] # error message
+
+begin
+ call error (1, errmsg)
+end
+
+
+# XEV_INITOP -- Set up an unintialized operand structure.
+
+procedure xev_initop (o, o_len, o_type)
+
+pointer o # pointer to operand structure
+int o_len # length of operand (zero if scalar)
+int o_type # datatype of operand
+
+begin
+ O_LEN(o) = 0
+ call xev_makeop (o, o_len, o_type)
+end
+
+
+# XEV_MAKEOP -- Set up the operand structure. If the operand structure has
+# already been initialized and array storage allocated, free the old array.
+
+procedure xev_makeop (o, o_len, o_type)
+
+pointer o # pointer to operand structure
+int o_len # length of operand (zero if scalar)
+int o_type # datatype of operand
+
+errchk malloc
+
+begin
+ # Free old array storage if any.
+ if (O_TYPE(o) != 0 && O_LEN(o) > 1) {
+ call mfree (O_VALP(o), O_TYPE(o))
+ O_LEN(o) = 0
+ }
+
+ # Set new operand type.
+ O_TYPE(o) = o_type
+
+ # Allocate array storage if nonscalar operand.
+ if (o_len > 0) {
+ call malloc (O_VALP(o), o_len, o_type)
+ O_LEN(o) = o_len
+ }
+end
+
+
+# XEV_FREEOP -- Reinitialize an operand structure, i.e., free any associated
+# array storage and clear the operand datatype field, but do not free the
+# operand structure itself (which may be only a segment of an array and not
+# a separately allocated structure).
+
+procedure xev_freeop (o)
+
+pointer o # pointer to operand structure
+
+begin
+ # Free old array storage if any.
+ if (O_TYPE(o) != 0 && O_LEN(o) > 1) {
+ call mfree (O_VALP(o), O_TYPE(o))
+ O_LEN(o) = 0
+ }
+
+ # Clear the operand type to mark operand invalid.
+ O_TYPE(o) = 0
+end
+define YYNPROD 33
+define YYLAST 303
+# line 1 "/iraf/iraf/lib/yaccpar.x"
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# Parser for yacc output, translated to the IRAF SPP language. The contents
+# of this file form the bulk of the source of the parser produced by Yacc.
+# Yacc recognizes several macros in the yaccpar input source and replaces
+# them as follows:
+# A user suppled "global" definitions and declarations
+# B parser tables
+# C user supplied actions (reductions)
+# The remainder of the yaccpar code is not changed.
+
+define yystack_ 10 # statement labels for gotos
+define yynewstate_ 20
+define yydefault_ 30
+define yyerrlab_ 40
+define yyabort_ 50
+
+define YYFLAG (-1000) # defs used in user actions
+define YYERROR goto yyerrlab_
+define YYACCEPT return (OK)
+define YYABORT return (ERR)
+
+
+# YYPARSE -- Parse the input stream, returning OK if the source is
+# syntactically acceptable (i.e., if compilation is successful),
+# otherwise ERR. The parameters YYMAXDEPTH and YYOPLEN must be
+# supplied by the caller in the %{ ... %} section of the Yacc source.
+# The token value stack is a dynamically allocated array of operand
+# structures, with the length and makeup of the operand structure being
+# application dependent.
+
+int procedure yyparse (fd, yydebug, yylex)
+
+int fd # stream to be parsed
+bool yydebug # print debugging information?
+int yylex() # user-supplied lexical input function
+extern yylex()
+
+short yys[YYMAXDEPTH] # parser stack -- stacks tokens
+pointer yyv # pointer to token value stack
+pointer yyval # value returned by action
+pointer yylval # value of token
+int yyps # token stack pointer
+pointer yypv # value stack pointer
+int yychar # current input token number
+int yyerrflag # error recovery flag
+int yynerrs # number of errors
+
+short yyj, yym # internal variables
+pointer yysp, yypvt
+short yystate, yyn
+int yyxi, i
+errchk salloc, yylex
+
+
+# XEV_PARSE -- SPP/Yacc parser for the evaluation of an expression passed as
+# a text string. Expression evaluation is carried out as the expression is
+# parsed, rather than being broken into separate compile and execute stages.
+# There is only one statement in this grammar, the expression. Our function
+# is to reduce an expression to a single value of type bool, string, int,
+# or real.
+
+pointer ap
+bool streq()
+errchk zcall2, xev_error1, xev_unop, xev_binop, xev_boolop
+errchk xev_quest, xev_callfcn, xev_addarg
+include "evexpr.com"
+
+short yyexca[96]
+data (yyexca(i),i= 1, 8) / -1, 1, 0, -1, -2, 0, -1, 4/
+data (yyexca(i),i= 9, 16) / 40, 27, -2, 3, -1, 5, 40, 26/
+data (yyexca(i),i= 17, 24) / -2, 4, -1, 61, 269, 0, 270, 0/
+data (yyexca(i),i= 25, 32) / 271, 0, 279, 0, -2, 16, -1, 62/
+data (yyexca(i),i= 33, 40) / 269, 0, 270, 0, 271, 0, 279, 0/
+data (yyexca(i),i= 41, 48) / -2, 17, -1, 63, 269, 0, 270, 0/
+data (yyexca(i),i= 49, 56) / 271, 0, 279, 0, -2, 18, -1, 64/
+data (yyexca(i),i= 57, 64) / 269, 0, 270, 0, 271, 0, 279, 0/
+data (yyexca(i),i= 65, 72) / -2, 19, -1, 65, 272, 0, 273, 0/
+data (yyexca(i),i= 73, 80) / 274, 0, -2, 20, -1, 66, 272, 0/
+data (yyexca(i),i= 81, 88) / 273, 0, 274, 0, -2, 21, -1, 67/
+data (yyexca(i),i= 89, 96) / 272, 0, 273, 0, 274, 0, -2, 22/
+short yyact[303]
+data (yyact(i),i= 1, 8) / 12, 13, 14, 15, 16, 17, 27, 71/
+data (yyact(i),i= 9, 16) / 20, 21, 22, 24, 26, 25, 18, 19/
+data (yyact(i),i= 17, 24) / 51, 16, 23, 11, 12, 13, 14, 15/
+data (yyact(i),i= 25, 32) / 16, 17, 27, 28, 20, 21, 22, 24/
+data (yyact(i),i= 33, 40) / 26, 25, 18, 19, 31, 49, 23, 12/
+data (yyact(i),i= 41, 48) / 13, 14, 15, 16, 17, 27, 10, 20/
+data (yyact(i),i= 49, 56) / 21, 22, 24, 26, 25, 18, 19, 10/
+data (yyact(i),i= 57, 64) / 9, 23, 12, 13, 14, 15, 16, 17/
+data (yyact(i),i= 65, 72) / 10, 1, 20, 21, 22, 24, 26, 25/
+data (yyact(i),i= 73, 80) / 18, 14, 15, 16, 23, 12, 13, 14/
+data (yyact(i),i= 81, 88) / 15, 16, 17, 0, 0, 20, 21, 22/
+data (yyact(i),i= 89, 96) / 24, 26, 25, 69, 0, 0, 70, 23/
+data (yyact(i),i= 97,104) / 12, 13, 14, 15, 16, 17, 0, 0/
+data (yyact(i),i=105,112) / 20, 21, 22, 12, 13, 14, 15, 16/
+data (yyact(i),i=113,120) / 17, 2, 23, 12, 13, 14, 15, 16/
+data (yyact(i),i=121,128) / 0, 29, 30, 0, 32, 0, 0, 0/
+data (yyact(i),i=129,136) / 0, 0, 0, 0, 0, 0, 0, 0/
+data (yyact(i),i=137,144) / 0, 0, 0, 0, 0, 0, 0, 0/
+data (yyact(i),i=145,152) / 0, 50, 0, 52, 54, 55, 56, 57/
+data (yyact(i),i=153,160) / 58, 59, 60, 61, 62, 63, 64, 65/
+data (yyact(i),i=161,168) / 66, 67, 68, 0, 0, 0, 0, 0/
+data (yyact(i),i=169,176) / 0, 0, 0, 0, 0, 0, 0, 0/
+data (yyact(i),i=177,184) / 0, 0, 0, 0, 33, 0, 0, 0/
+data (yyact(i),i=185,192) / 72, 0, 0, 74, 0, 0, 0, 0/
+data (yyact(i),i=193,200) / 0, 0, 34, 35, 36, 37, 38, 39/
+data (yyact(i),i=201,208) / 40, 41, 42, 43, 44, 45, 46, 47/
+data (yyact(i),i=209,216) / 48, 0, 0, 0, 0, 0, 0, 0/
+data (yyact(i),i=217,224) / 0, 0, 0, 0, 0, 0, 0, 0/
+data (yyact(i),i=225,232) / 0, 0, 0, 0, 0, 0, 0, 0/
+data (yyact(i),i=233,240) / 0, 0, 0, 0, 12, 13, 14, 15/
+data (yyact(i),i=241,248) / 16, 17, 27, 0, 20, 21, 22, 24/
+data (yyact(i),i=249,256) / 26, 25, 18, 19, 73, 0, 23, 0/
+data (yyact(i),i=257,264) / 0, 0, 0, 0, 0, 0, 0, 4/
+data (yyact(i),i=265,272) / 5, 53, 0, 0, 7, 0, 0, 3/
+data (yyact(i),i=273,280) / 4, 5, 0, 0, 0, 7, 0, 0/
+data (yyact(i),i=281,288) / 0, 4, 5, 8, 6, 0, 7, 0/
+data (yyact(i),i=289,296) / 0, 0, 0, 0, 8, 6, 0, 0/
+data (yyact(i),i=297,303) / 0, 0, 0, 0, 0, 8, 6/
+short yypact[75]
+data (yypact(i),i= 1, 8) / 15,-1000,-241,-1000,-1000,-1000,-230, 24/
+data (yypact(i),i= 9, 16) / 24, -4, 24,-1000,-1000,-1000,-1000,-1000/
+data (yypact(i),i= 17, 24) /-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000/
+data (yypact(i),i= 25, 32) /-1000,-1000,-1000,-1000,-1000,-1000,-1000, 24/
+data (yypact(i),i= 33, 40) / -25, 6, 6, 6, 6, 6, 6, 6/
+data (yypact(i),i= 41, 48) / 6, 6, 6, 6, 6, 6, 6, 6/
+data (yypact(i),i= 49, 56) / 6, 50,-222,-1000,-190,-1000,-190,-248/
+data (yypact(i),i= 57, 64) /-248,-1000,-146,-184,-203,-154,-154,-154/
+data (yypact(i),i= 65, 72) /-154,-165,-165,-165,-261,-1000, 24,-1000/
+data (yypact(i),i= 73, 75) /-222, 6,-222/
+short yypgo[6]
+data (yypgo(i),i= 1, 6) / 0, 65, 113, 180, 56, 37/
+short yyr1[33]
+data (yyr1(i),i= 1, 8) / 0, 1, 1, 2, 2, 2, 2, 2/
+data (yyr1(i),i= 9, 16) / 2, 2, 2, 2, 2, 2, 2, 2/
+data (yyr1(i),i= 17, 24) / 2, 2, 2, 2, 2, 2, 2, 2/
+data (yyr1(i),i= 25, 32) / 2, 2, 4, 4, 5, 5, 5, 3/
+data (yyr1(i),i= 33, 33) / 3/
+short yyr2[33]
+data (yyr2(i),i= 1, 8) / 0, 2, 1, 1, 1, 2, 2, 2/
+data (yyr2(i),i= 9, 16) / 4, 4, 4, 4, 4, 4, 4, 4/
+data (yyr2(i),i= 17, 24) / 4, 4, 4, 4, 4, 4, 4, 7/
+data (yyr2(i),i= 25, 32) / 4, 3, 1, 1, 0, 1, 3, 0/
+data (yyr2(i),i= 33, 33) / 2/
+short yychk[75]
+data (yychk(i),i= 1, 8) /-1000, -1, -2, 256, 257, 258, 278, 262/
+data (yychk(i),i= 9, 16) / 277, -4, 40, 260, 261, 262, 263, 264/
+data (yychk(i),i= 17, 24) / 265, 266, 275, 276, 269, 270, 271, 279/
+data (yychk(i),i= 25, 32) / 272, 274, 273, 267, 257, -2, -2, 40/
+data (yychk(i),i= 33, 40) / -2, -3, -3, -3, -3, -3, -3, -3/
+data (yychk(i),i= 41, 48) / -3, -3, -3, -3, -3, -3, -3, -3/
+data (yychk(i),i= 49, 56) / -3, -5, -2, 41, -2, 259, -2, -2/
+data (yychk(i),i= 57, 64) / -2, -2, -2, -2, -2, -2, -2, -2/
+data (yychk(i),i= 65, 72) / -2, -2, -2, -2, -2, 41, 44, 268/
+data (yychk(i),i= 73, 75) / -2, -3, -2/
+short yydef[75]
+data (yydef(i),i= 1, 8) / 0, -2, 0, 2, -2, -2, 0, 0/
+data (yydef(i),i= 9, 16) / 0, 0, 0, 1, 31, 31, 31, 31/
+data (yydef(i),i= 17, 24) / 31, 31, 31, 31, 31, 31, 31, 31/
+data (yydef(i),i= 25, 32) / 31, 31, 31, 31, 5, 6, 7, 28/
+data (yydef(i),i= 33, 40) / 0, 0, 0, 0, 0, 0, 0, 0/
+data (yydef(i),i= 41, 48) / 0, 0, 0, 0, 0, 0, 0, 0/
+data (yydef(i),i= 49, 56) / 0, 0, 29, 25, 8, 32, 9, 10/
+data (yydef(i),i= 57, 64) / 11, 12, 13, 14, 15, -2, -2, -2/
+data (yydef(i),i= 65, 72) / -2, -2, -2, -2, 0, 24, 0, 31/
+data (yydef(i),i= 73, 75) / 30, 0, 23/
+
+begin
+ call smark (yysp)
+ call salloc (yyv, (YYMAXDEPTH+2) * YYOPLEN, TY_STRUCT)
+
+ # Initialization. The first element of the dynamically allocated
+ # token value stack (yyv) is used for yyval, the second for yylval,
+ # and the actual stack starts with the third element.
+
+ yystate = 0
+ yychar = -1
+ yynerrs = 0
+ yyerrflag = 0
+ yyps = 0
+ yyval = yyv
+ yylval = yyv + YYOPLEN
+ yypv = yylval
+
+yystack_
+ # SHIFT -- Put a state and value onto the stack. The token and
+ # value stacks are logically the same stack, implemented as two
+ # separate arrays.
+
+ if (yydebug) {
+ call printf ("state %d, char 0%o\n")
+ call pargs (yystate)
+ call pargi (yychar)
+ }
+ yyps = yyps + 1
+ yypv = yypv + YYOPLEN
+ if (yyps > YYMAXDEPTH) {
+ call sfree (yysp)
+ call eprintf ("yacc stack overflow\n")
+ return (ERR)
+ }
+ yys[yyps] = yystate
+ YYMOVE (yyval, yypv)
+
+yynewstate_
+ # Process the new state.
+ yyn = yypact[yystate+1]
+
+ if (yyn <= YYFLAG)
+ goto yydefault_ # simple state
+
+ # The variable "yychar" is the lookahead token.
+ if (yychar < 0) {
+ yychar = yylex (fd, yylval)
+ if (yychar < 0)
+ yychar = 0
+ }
+ yyn = yyn + yychar
+ if (yyn < 0 || yyn >= YYLAST)
+ goto yydefault_
+
+ yyn = yyact[yyn+1]
+ if (yychk[yyn+1] == yychar) { # valid shift
+ yychar = -1
+ YYMOVE (yylval, yyval)
+ yystate = yyn
+ if (yyerrflag > 0)
+ yyerrflag = yyerrflag - 1
+ goto yystack_
+ }
+
+yydefault_
+ # Default state action.
+
+ yyn = yydef[yystate+1]
+ if (yyn == -2) {
+ if (yychar < 0) {
+ yychar = yylex (fd, yylval)
+ if (yychar < 0)
+ yychar = 0
+ }
+
+ # Look through exception table.
+ yyxi = 1
+ while ((yyexca[yyxi] != (-1)) || (yyexca[yyxi+1] != yystate))
+ yyxi = yyxi + 2
+ for (yyxi=yyxi+2; yyexca[yyxi] >= 0; yyxi=yyxi+2) {
+ if (yyexca[yyxi] == yychar)
+ break
+ }
+
+ yyn = yyexca[yyxi+1]
+ if (yyn < 0) {
+ call sfree (yysp)
+ return (OK) # ACCEPT -- all done
+ }
+ }
+
+
+ # SYNTAX ERROR -- resume parsing if possible.
+
+ if (yyn == 0) {
+ switch (yyerrflag) {
+ case 0, 1, 2:
+ if (yyerrflag == 0) { # brand new error
+ call eprintf ("syntax error\n")
+yyerrlab_
+ yynerrs = yynerrs + 1
+ # fall through...
+ }
+
+ # case 1:
+ # case 2: incompletely recovered error ... try again
+ yyerrflag = 3
+
+ # Find a state where "error" is a legal shift action.
+ while (yyps >= 1) {
+ yyn = yypact[yys[yyps]+1] + YYERRCODE
+ if ((yyn >= 0) && (yyn < YYLAST) &&
+ (yychk[yyact[yyn+1]+1] == YYERRCODE)) {
+ # Simulate a shift of "error".
+ yystate = yyact[yyn+1]
+ goto yystack_
+ }
+ yyn = yypact[yys[yyps]+1]
+
+ # The current yyps has no shift on "error", pop stack.
+ if (yydebug) {
+ call printf ("error recovery pops state %d, ")
+ call pargs (yys[yyps])
+ call printf ("uncovers %d\n")
+ call pargs (yys[yyps-1])
+ }
+ yyps = yyps - 1
+ yypv = yypv - YYOPLEN
+ }
+
+ # ABORT -- There is no state on the stack with an error shift.
+yyabort_
+ call sfree (yysp)
+ return (ERR)
+
+
+ case 3: # No shift yet; clobber input char.
+
+ if (yydebug) {
+ call printf ("error recovery discards char %d\n")
+ call pargi (yychar)
+ }
+
+ if (yychar == 0)
+ goto yyabort_ # don't discard EOF, quit
+ yychar = -1
+ goto yynewstate_ # try again in the same state
+ }
+ }
+
+
+ # REDUCE -- Reduction by production yyn.
+
+ if (yydebug) {
+ call printf ("reduce %d\n")
+ call pargs (yyn)
+ }
+ yyps = yyps - yyr2[yyn+1]
+ yypvt = yypv
+ yypv = yypv - yyr2[yyn+1] * YYOPLEN
+ YYMOVE (yypv + YYOPLEN, yyval)
+ yym = yyn
+
+ # Consult goto table to find next state.
+ yyn = yyr1[yyn+1]
+ yyj = yypgo[yyn+1] + yys[yyps] + 1
+ if (yyj >= YYLAST)
+ yystate = yyact[yypgo[yyn+1]+1]
+ else {
+ yystate = yyact[yyj+1]
+ if (yychk[yystate+1] != -yyn)
+ yystate = yyact[yypgo[yyn+1]+1]
+ }
+
+ # Perform action associated with the grammar rule, if any.
+ switch (yym) {
+
+case 1:
+# line 135 "evexpr.y"
+{
+ # Normal exit. Move the final expression value operand
+ # into the operand structure pointed to by the global
+ # variable ev_oval.
+
+ YYMOVE (yypvt-YYOPLEN, ev_oval)
+ return (OK)
+ }
+case 2:
+# line 143 "evexpr.y"
+{
+ call error (1, "syntax error")
+ }
+case 3:
+# line 149 "evexpr.y"
+{
+ # Numeric constant.
+ YYMOVE (yypvt, yyval)
+ }
+case 4:
+# line 153 "evexpr.y"
+{
+ # The boolean constants "yes" and "no" are implemented
+ # as reserved operands.
+
+ call xev_initop (yyval, 0, TY_BOOL)
+ if (streq (O_VALC(yypvt), "yes"))
+ O_VALB(yyval) = true
+ else if (streq (O_VALC(yypvt), "no"))
+ O_VALB(yyval) = false
+ else if (ev_getop != NULL)
+ call zcall2 (ev_getop, O_VALC(yypvt), yyval)
+ else
+ call xev_error1 ("illegal operand `%s'", O_VALC(yypvt))
+ call xev_freeop (yypvt)
+ }
+case 5:
+# line 168 "evexpr.y"
+{
+ # e.g., @"param"
+ if (ev_getop != NULL)
+ call zcall2 (ev_getop, O_VALC(yypvt), yyval)
+ else
+ call xev_error1 ("illegal operand `%s'", O_VALC(yypvt))
+ call xev_freeop (yypvt)
+ }
+case 6:
+# line 176 "evexpr.y"
+{
+ # Unary arithmetic minus.
+ call xev_unop (MINUS, yypvt, yyval)
+ }
+case 7:
+# line 180 "evexpr.y"
+{
+ # Boolean not.
+ call xev_unop (NOT, yypvt, yyval)
+ }
+case 8:
+# line 184 "evexpr.y"
+{
+ # Addition.
+ call xev_binop (PLUS, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 9:
+# line 188 "evexpr.y"
+{
+ # Subtraction.
+ call xev_binop (MINUS, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 10:
+# line 192 "evexpr.y"
+{
+ # Multiplication.
+ call xev_binop (STAR, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 11:
+# line 196 "evexpr.y"
+{
+ # Division.
+ call xev_binop (SLASH, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 12:
+# line 200 "evexpr.y"
+{
+ # Exponentiation.
+ call xev_binop (EXPON, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 13:
+# line 204 "evexpr.y"
+{
+ # String concatenation.
+ call xev_binop (CONCAT, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 14:
+# line 208 "evexpr.y"
+{
+ # Boolean and.
+ call xev_boolop (AND, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 15:
+# line 212 "evexpr.y"
+{
+ # Boolean or.
+ call xev_boolop (OR, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 16:
+# line 216 "evexpr.y"
+{
+ # Boolean less than.
+ call xev_boolop (LT, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 17:
+# line 220 "evexpr.y"
+{
+ # Boolean greater than.
+ call xev_boolop (GT, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 18:
+# line 224 "evexpr.y"
+{
+ # Boolean less than or equal.
+ call xev_boolop (LE, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 19:
+# line 228 "evexpr.y"
+{
+ # Boolean greater than or equal.
+ call xev_boolop (GE, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 20:
+# line 232 "evexpr.y"
+{
+ # Boolean equal.
+ call xev_boolop (EQ, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 21:
+# line 236 "evexpr.y"
+{
+ # String pattern-equal.
+ call xev_boolop (SE, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 22:
+# line 240 "evexpr.y"
+{
+ # Boolean not equal.
+ call xev_boolop (NE, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 23:
+# line 244 "evexpr.y"
+{
+ # Conditional expression.
+ call xev_quest (yypvt-6*YYOPLEN, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 24:
+# line 248 "evexpr.y"
+{
+ # Call an intrinsic or external function.
+ ap = O_VALP(yypvt-YYOPLEN)
+ call xev_callfcn (O_VALC(yypvt-3*YYOPLEN),
+ A_ARGP(ap,1), A_NARGS(ap), yyval)
+ call mfree (ap, TY_STRUCT)
+ call xev_freeop (yypvt-3*YYOPLEN)
+ }
+case 25:
+# line 256 "evexpr.y"
+{
+ YYMOVE (yypvt-YYOPLEN, yyval)
+ }
+case 26:
+# line 262 "evexpr.y"
+{
+ YYMOVE (yypvt, yyval)
+ }
+case 27:
+# line 265 "evexpr.y"
+{
+ if (O_TYPE(yypvt) != TY_CHAR)
+ call error (1, "illegal function name")
+ YYMOVE (yypvt, yyval)
+ }
+case 28:
+# line 273 "evexpr.y"
+{
+ # Empty.
+ call xev_startarglist (NULL, yyval)
+ }
+case 29:
+# line 277 "evexpr.y"
+{
+ # First arg; start a nonnull list.
+ call xev_startarglist (yypvt, yyval)
+ }
+case 30:
+# line 281 "evexpr.y"
+{
+ # Add an argument to an existing list.
+ call xev_addarg (yypvt, yypvt-2*YYOPLEN, yyval)
+ } }
+
+ goto yystack_ # stack new state and value
+end
diff --git a/sys/fmtio/evexpr.y b/sys/fmtio/evexpr.y
new file mode 100644
index 00000000..297950bc
--- /dev/null
+++ b/sys/fmtio/evexpr.y
@@ -0,0 +1,1087 @@
+%{
+include <lexnum.h>
+include <ctype.h>
+include <mach.h>
+include <evexpr.h>
+
+define YYMAXDEPTH 64 # parser stack length
+define MAX_ARGS 16 # max args in a function call
+define yyparse xev_parse
+
+define DTOR (($1)/57.2957795)
+define RTOD (($1)*57.2957795)
+
+# Arglist structure.
+define LEN_ARGSTRUCT (1+MAX_ARGS+(MAX_ARGS*LEN_OPERAND))
+define A_NARGS Memi[$1] # number of arguments
+define A_ARGP Memi[$1+$2] # array of pointers to operand structs
+define A_OPS ($1+MAX_ARGS+1) # offset to operand storage area
+
+# Intrinsic functions.
+
+define KEYWORDS "|abs|acos|asin|atan|atan2|bool|cos|exp|int|log|log10|\
+ |max|min|mod|nint|real|sin|sqrt|str|tan|"
+
+define F_ABS 01 # function codes
+define F_ACOS 02
+define F_ASIN 03
+define F_ATAN 04
+define F_ATAN2 05
+define F_BOOL 06
+define F_COS 07
+define F_EXP 08
+define F_INT 09
+define F_LOG 10
+define F_LOG10 11
+ # newline 12
+define F_MAX 13
+define F_MIN 14
+define F_MOD 15
+define F_NINT 16
+define F_REAL 17
+define F_SIN 18
+define F_SQRT 19
+define F_STR 20
+define F_TAN 21
+
+
+# EVEXPR -- Evaluate an expression. This is the top level procedure, and the
+# only externally callable entry point. Input consists of the expression to
+# be evaluated (a string) and, optionally, user procedures for fetching
+# external operands and executing external functions. Output is a pointer to
+# an operand structure containing the computed value of the expression.
+# The output operand structure is dynamically allocated by EVEXPR and must be
+# freed by the user.
+#
+# N.B.: this is not intended to be an especially efficient procedure. Rather,
+# this is a high level, easy to use procedure, intended to provide greater
+# flexibility in the parameterization of applications programs.
+
+pointer procedure evexpr (expr, getop_epa, ufcn_epa)
+
+char expr[ARB] # expression to be evaluated
+int getop_epa # user supplied get operand procedure
+int ufcn_epa # user supplied function call procedure
+
+int junk
+bool debug
+pointer sp, ip
+extern xev_gettok()
+int strlen(), xev_parse()
+
+errchk xev_parse, calloc
+include "evexpr.com"
+data debug /false/
+
+begin
+ call smark (sp)
+
+ # Set user function entry point addresses.
+ ev_getop = getop_epa
+ ev_ufcn = ufcn_epa
+
+ # Allocate an operand struct for the expression value.
+ call calloc (ev_oval, LEN_OPERAND, TY_STRUCT)
+
+ # Make a local copy of the input string.
+ call salloc (ip, strlen(expr), TY_CHAR)
+ call strcpy (expr, Memc[ip], ARB)
+
+ # Evaluate the expression. The expression value is copied into the
+ # output operand structure by XEV_PARSE, given the operand pointer
+ # passed in common. A common must be used since the standard parser
+ # subroutine has a fixed calling sequence.
+
+ junk = xev_parse (ip, debug, xev_gettok)
+
+ call sfree (sp)
+ return (ev_oval)
+end
+
+%L
+# XEV_PARSE -- SPP/Yacc parser for the evaluation of an expression passed as
+# a text string. Expression evaluation is carried out as the expression is
+# parsed, rather than being broken into separate compile and execute stages.
+# There is only one statement in this grammar, the expression. Our function
+# is to reduce an expression to a single value of type bool, string, int,
+# or real.
+
+pointer ap
+bool streq()
+errchk zcall2, xev_error1, xev_unop, xev_binop, xev_boolop
+errchk xev_quest, xev_callfcn, xev_addarg
+include "evexpr.com"
+
+%}
+
+%token CONSTANT IDENTIFIER NEWLINE YYEOS
+%token PLUS MINUS STAR SLASH EXPON CONCAT QUEST COLON
+%token LT GT LE GT EQ NE SE AND OR NOT AT
+
+%nonassoc QUEST
+%left OR
+%left AND
+%nonassoc EQ NE SE
+%nonassoc LT GT LE GE
+%left CONCAT
+%left PLUS MINUS
+%left STAR SLASH
+%left EXPON
+%right UMINUS NOT
+%right AT
+
+%%
+
+stmt : expr YYEOS {
+ # Normal exit. Move the final expression value operand
+ # into the operand structure pointed to by the global
+ # variable ev_oval.
+
+ YYMOVE ($1, ev_oval)
+ return (OK)
+ }
+ | error {
+ call error (1, "syntax error")
+ }
+ ;
+
+
+expr : CONSTANT {
+ # Numeric constant.
+ YYMOVE ($1, $$)
+ }
+ | IDENTIFIER {
+ # The boolean constants "yes" and "no" are implemented
+ # as reserved operands.
+
+ call xev_initop ($$, 0, TY_BOOL)
+ if (streq (O_VALC($1), "yes"))
+ O_VALB($$) = true
+ else if (streq (O_VALC($1), "no"))
+ O_VALB($$) = false
+ else if (ev_getop != NULL)
+ call zcall2 (ev_getop, O_VALC($1), $$)
+ else
+ call xev_error1 ("illegal operand `%s'", O_VALC($1))
+ call xev_freeop ($1)
+ }
+ | AT CONSTANT {
+ # e.g., @"param"
+ if (ev_getop != NULL)
+ call zcall2 (ev_getop, O_VALC($2), $$)
+ else
+ call xev_error1 ("illegal operand `%s'", O_VALC($2))
+ call xev_freeop ($2)
+ }
+ | MINUS expr %prec UMINUS {
+ # Unary arithmetic minus.
+ call xev_unop (MINUS, $2, $$)
+ }
+ | NOT expr {
+ # Boolean not.
+ call xev_unop (NOT, $2, $$)
+ }
+ | expr PLUS opnl expr {
+ # Addition.
+ call xev_binop (PLUS, $1, $4, $$)
+ }
+ | expr MINUS opnl expr {
+ # Subtraction.
+ call xev_binop (MINUS, $1, $4, $$)
+ }
+ | expr STAR opnl expr {
+ # Multiplication.
+ call xev_binop (STAR, $1, $4, $$)
+ }
+ | expr SLASH opnl expr {
+ # Division.
+ call xev_binop (SLASH, $1, $4, $$)
+ }
+ | expr EXPON opnl expr {
+ # Exponentiation.
+ call xev_binop (EXPON, $1, $4, $$)
+ }
+ | expr CONCAT opnl expr {
+ # String concatenation.
+ call xev_binop (CONCAT, $1, $4, $$)
+ }
+ | expr AND opnl expr {
+ # Boolean and.
+ call xev_boolop (AND, $1, $4, $$)
+ }
+ | expr OR opnl expr {
+ # Boolean or.
+ call xev_boolop (OR, $1, $4, $$)
+ }
+ | expr LT opnl expr {
+ # Boolean less than.
+ call xev_boolop (LT, $1, $4, $$)
+ }
+ | expr GT opnl expr {
+ # Boolean greater than.
+ call xev_boolop (GT, $1, $4, $$)
+ }
+ | expr LE opnl expr {
+ # Boolean less than or equal.
+ call xev_boolop (LE, $1, $4, $$)
+ }
+ | expr GE opnl expr {
+ # Boolean greater than or equal.
+ call xev_boolop (GE, $1, $4, $$)
+ }
+ | expr EQ opnl expr {
+ # Boolean equal.
+ call xev_boolop (EQ, $1, $4, $$)
+ }
+ | expr SE opnl expr {
+ # String pattern-equal.
+ call xev_boolop (SE, $1, $4, $$)
+ }
+ | expr NE opnl expr {
+ # Boolean not equal.
+ call xev_boolop (NE, $1, $4, $$)
+ }
+ | expr QUEST opnl expr COLON opnl expr {
+ # Conditional expression.
+ call xev_quest ($1, $4, $7, $$)
+ }
+ | funct '(' arglist ')' {
+ # Call an intrinsic or external function.
+ ap = O_VALP($3)
+ call xev_callfcn (O_VALC($1),
+ A_ARGP(ap,1), A_NARGS(ap), $$)
+ call mfree (ap, TY_STRUCT)
+ call xev_freeop ($1)
+ }
+ | '(' expr ')' {
+ YYMOVE ($2, $$)
+ }
+ ;
+
+
+funct : IDENTIFIER {
+ YYMOVE ($1, $$)
+ }
+ | CONSTANT {
+ if (O_TYPE($1) != TY_CHAR)
+ call error (1, "illegal function name")
+ YYMOVE ($1, $$)
+ }
+ ;
+
+
+arglist : {
+ # Empty.
+ call xev_startarglist (NULL, $$)
+ }
+ | expr {
+ # First arg; start a nonnull list.
+ call xev_startarglist ($1, $$)
+ }
+ | arglist ',' expr {
+ # Add an argument to an existing list.
+ call xev_addarg ($3, $1, $$)
+ }
+ ;
+
+
+opnl : # Empty.
+ | opnl NEWLINE
+ ;
+
+%%
+
+
+# XEV_UNOP -- Unary operation. Perform the indicated unary operation on the
+# input operand, returning the result as the output operand.
+
+procedure xev_unop (opcode, in, out)
+
+int opcode # operation to be performed
+pointer in # input operand
+pointer out # output operand
+
+errchk xev_error
+define badsw_ 91
+
+begin
+ call xev_initop (out, 0, O_TYPE(in))
+
+ switch (opcode) {
+ case MINUS:
+ # Unary negation.
+ switch (O_TYPE(in)) {
+ case TY_BOOL, TY_CHAR:
+ call xev_error ("negation of a nonarithmetic operand")
+ case TY_INT:
+ O_VALI(out) = -O_VALI(in)
+ case TY_REAL:
+ O_VALR(out) = -O_VALR(in)
+ default:
+ goto badsw_
+ }
+
+ case NOT:
+ switch (O_TYPE(in)) {
+ case TY_BOOL:
+ O_VALB(out) = !O_VALB(in)
+ case TY_CHAR, TY_INT, TY_REAL:
+ call xev_error ("not of a nonlogical")
+ default:
+ goto badsw_
+ }
+
+ default:
+badsw_ call xev_error ("bad switch in unop")
+ }
+end
+
+
+# XEV_BINOP -- Binary operation. Perform the indicated arithmetic binary
+# operation on the two input operands, returning the result as the output
+# operand.
+
+procedure xev_binop (opcode, in1, in2, out)
+
+int opcode # operation to be performed
+pointer in1, in2 # input operands
+pointer out # output operand
+
+real r1, r2
+int i1, i2, dtype, nchars
+int xev_newtype(), strlen()
+errchk xev_newtype
+
+begin
+ # Set the datatype of the output operand, taking an error action if
+ # the operands have incompatible datatypes.
+
+ dtype = xev_newtype (O_TYPE(in1), O_TYPE(in2))
+ call xev_initop (out, 0, dtype)
+
+ switch (dtype) {
+ case TY_BOOL:
+ call xev_error ("operation illegal for boolean operands")
+ case TY_CHAR:
+ if (opcode != CONCAT)
+ call xev_error ("operation illegal for string operands")
+ case TY_INT:
+ i1 = O_VALI(in1)
+ i2 = O_VALI(in2)
+ case TY_REAL:
+ if (O_TYPE(in1) == TY_INT)
+ r1 = O_VALI(in1)
+ else
+ r1 = O_VALR(in1)
+ if (O_TYPE(in2) == TY_INT)
+ r2 = O_VALI(in2)
+ else
+ r2 = O_VALR(in2)
+ default:
+ call xev_error ("unknown datatype code in binop")
+ }
+
+ # Perform the operation.
+ switch (opcode) {
+ case PLUS:
+ if (dtype == TY_INT)
+ O_VALI(out) = i1 + i2
+ else
+ O_VALR(out) = r1 + r2
+
+ case MINUS:
+ if (dtype == TY_INT)
+ O_VALI(out) = i1 - i2
+ else
+ O_VALR(out) = r1 - r2
+
+ case STAR:
+ if (dtype == TY_INT)
+ O_VALI(out) = i1 * i2
+ else
+ O_VALR(out) = r1 * r2
+
+ case SLASH:
+ if (dtype == TY_INT)
+ O_VALI(out) = i1 / i2
+ else
+ O_VALR(out) = r1 / r2
+
+ case EXPON:
+ if (dtype == TY_INT)
+ O_VALI(out) = i1 ** i2
+ else if (O_TYPE(in1) == TY_REAL && O_TYPE(in2) == TY_INT)
+ O_VALR(out) = r1 ** (O_VALI(in2))
+ else
+ O_VALR(out) = r1 ** r2
+
+ case CONCAT:
+ if (dtype != TY_CHAR)
+ call xev_error ("concatenation of a nonstring operand")
+ nchars = strlen (O_VALC(in1)) + strlen (O_VALC(in2))
+ call xev_makeop (out, nchars, TY_CHAR)
+ call strcpy (O_VALC(in1), O_VALC(out), ARB)
+ call strcat (O_VALC(in2), O_VALC(out), ARB)
+ call xev_freeop (in1)
+ call xev_freeop (in2)
+
+ default:
+ call xev_error ("bad switch in binop")
+ }
+end
+
+
+# XEV_BOOLOP -- Boolean binary operations. Perform the indicated boolean binary
+# operation on the two input operands, returning the result as the output
+# operand.
+
+procedure xev_boolop (opcode, in1, in2, out)
+
+int opcode # operation to be performed
+pointer in1, in2 # input operands
+pointer out # output operand
+
+bool result
+real r1, r2
+int i1, i2, dtype
+int xev_newtype(), xev_patmatch(), strncmp()
+errchk xev_newtype, xev_error
+define badsw_ 91
+
+begin
+ # Set the datatype of the output operand, taking an error action if
+ # the operands have incompatible datatypes.
+
+ dtype = xev_newtype (O_TYPE(in1), O_TYPE(in2))
+ call xev_initop (out, 0, dtype)
+
+ switch (opcode) {
+ case AND, OR:
+ if (dtype != TY_BOOL)
+ call xev_error ("AND or OR of nonlogical")
+ case LT, GT, LE, GE:
+ if (dtype == TY_BOOL)
+ call xev_error ("order comparison of a boolean operand")
+ }
+
+ if (dtype == TY_INT) {
+ i1 = O_VALI(in1)
+ i2 = O_VALI(in2)
+ } else if (dtype == TY_REAL) {
+ if (O_TYPE(in1) == TY_INT) {
+ i1 = O_VALI(in1)
+ r1 = i1
+ } else
+ r1 = O_VALR(in1)
+ if (O_TYPE(in2) == TY_INT) {
+ i2 = O_VALI(in2)
+ r2 = i2
+ } else
+ r2 = O_VALR(in2)
+ }
+
+ # Perform the operation.
+ switch (opcode) {
+ case AND:
+ result = O_VALB(in1) && O_VALB(in2)
+ case OR:
+ result = O_VALB(in1) || O_VALB(in2)
+
+ case LT, GE:
+ if (dtype == TY_INT)
+ result = i1 < i2
+ else if (dtype == TY_REAL)
+ result = r1 < r2
+ else
+ result = strncmp (O_VALC(in1), O_VALC(in2), ARB) < 0
+ if (opcode == GE)
+ result = !result
+
+ case GT, LE:
+ if (dtype == TY_INT)
+ result = i1 > i2
+ else if (dtype == TY_REAL)
+ result = r1 > r2
+ else
+ result = strncmp (O_VALC(in1), O_VALC(in2), ARB) > 0
+ if (opcode == LE)
+ result = !result
+
+ case EQ, SE, NE:
+ switch (dtype) {
+ case TY_BOOL:
+ if (O_VALB(in1))
+ result = O_VALB(in2)
+ else
+ result = !O_VALB(in2)
+ case TY_CHAR:
+ if (opcode == SE)
+ result = xev_patmatch (O_VALC(in1), O_VALC(in2)) > 0
+ else
+ result = strncmp (O_VALC(in1), O_VALC(in2), ARB) == 0
+ case TY_INT:
+ result = i1 == i2
+ case TY_REAL:
+ result = r1 == r2
+ default:
+ goto badsw_
+ }
+ if (opcode == NE)
+ result = !result
+
+ default:
+badsw_ call xev_error ("bad switch in boolop")
+ }
+
+ call xev_makeop (out, 0, TY_BOOL)
+ O_VALB(out) = result
+
+ # Free storage if there were any string type input operands.
+ call xev_freeop (in1)
+ call xev_freeop (in2)
+end
+
+
+# XEV_PATMATCH -- Match a string against a pattern, returning the patmatch
+# index if the string matches. The pattern may contain any of the conventional
+# pattern matching metacharacters. Closure (i.e., "*") is mapped to "?*".
+
+int procedure xev_patmatch (str, pat)
+
+char str[ARB] # operand string
+char pat[ARB] # pattern
+
+int junk, ip, index
+pointer sp, patstr, patbuf, op
+int patmake(), patmatch()
+
+begin
+ call smark (sp)
+ call salloc (patstr, SZ_FNAME, TY_CHAR)
+ call salloc (patbuf, SZ_LINE, TY_CHAR)
+ call aclrc (Memc[patstr], SZ_FNAME)
+ call aclrc (Memc[patbuf], SZ_LINE)
+
+ # Map pattern, changing '*' into '?*'.
+ op = patstr
+ for (ip=1; pat[ip] != EOS; ip=ip+1) {
+ if (pat[ip] == '*') {
+ Memc[op] = '?'
+ op = op + 1
+ }
+ Memc[op] = pat[ip]
+ op = op + 1
+ }
+
+ # Encode pattern.
+ junk = patmake (Memc[patstr], Memc[patbuf], SZ_LINE)
+
+ # Perform the pattern matching operation.
+ index = patmatch (str, Memc[patbuf])
+
+ call sfree (sp)
+ return (index)
+end
+
+
+# XEV_NEWTYPE -- Get the datatype of a binary operation, given the datatypes
+# of the two input operands. An error action is taken if the datatypes are
+# incompatible, e.g., boolean and anything else or string and anything else.
+
+int procedure xev_newtype (type1, type2)
+
+int type1, type2
+int newtype, p, q, i
+int tyindex[NTYPES], ttbl[NTYPES*NTYPES]
+data tyindex /TY_BOOL, TY_CHAR, TY_INT, TY_REAL/
+data (ttbl(i),i=1,4) /TY_BOOL, 0, 0, 0/
+data (ttbl(i),i=5,8) / 0, TY_CHAR, 0, 0/
+data (ttbl(i),i=9,12) / 0, 0, TY_INT, TY_REAL/
+data (ttbl(i),i=13,16) / 0, 0, TY_REAL, TY_REAL/
+
+begin
+ do i = 1, NTYPES {
+ if (tyindex[i] == type1)
+ p = i
+ if (tyindex[i] == type2)
+ q = i
+ }
+
+ newtype = ttbl[(p-1)*NTYPES+q]
+ if (newtype == 0)
+ call xev_error ("operands have incompatible types")
+ else
+ return (newtype)
+end
+
+
+# XEV_QUEST -- Conditional expression. If the condition operand is true
+# return the first (true) operand, else return the second (false) operand.
+
+procedure xev_quest (cond, trueop, falseop, out)
+
+pointer cond # pointer to condition operand
+pointer trueop, falseop # pointer to true,false operands
+pointer out # pointer to output operand
+errchk xev_error
+
+begin
+ if (O_TYPE(cond) != TY_BOOL)
+ call xev_error ("nonboolean condition operand")
+
+ if (O_VALB(cond)) {
+ YYMOVE (trueop, out)
+ call xev_freeop (falseop)
+ } else {
+ YYMOVE (falseop, out)
+ call xev_freeop (trueop)
+ }
+end
+
+
+# XEV_CALLFCN -- Call an intrinsic function. If the function named is not
+# one of the standard intrinsic functions, call an external user function
+# if a function call procedure was supplied.
+
+procedure xev_callfcn (fcn, args, nargs, out)
+
+char fcn[ARB] # function to be called
+pointer args[ARB] # pointer to arglist descriptor
+int nargs # number of arguments
+pointer out # output operand (function value)
+
+real rresult, rval[2], rtemp
+int iresult, ival[2], type[2], optype, oplen, itemp
+int opcode, v_nargs, i
+pointer sp, buf, ap
+include "evexpr.com"
+
+bool strne()
+int strdic(), strlen()
+errchk zcall4, xev_error1, xev_error2, malloc
+string keywords KEYWORDS
+define badtype_ 91
+define free_ 92
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_FNAME, TY_CHAR)
+
+ oplen = 0
+
+ # Lookup the function name in the dictionary. An exact match is
+ # required (strdic permits abbreviations).
+
+ opcode = strdic (fcn, Memc[buf], SZ_FNAME, keywords)
+ if (opcode > 0 && strne(fcn,Memc[buf]))
+ opcode = 0
+
+ # If the function named is not a standard one and the user has supplied
+ # the entry point of an external function evaluation procedure, call
+ # the user procedure to evaluate the function, otherwise abort.
+
+ if (opcode <= 0)
+ if (ev_ufcn != NULL) {
+ call zcall4 (ev_ufcn, fcn, args, nargs, out)
+ goto free_
+ } else
+ call xev_error1 ("unknown function `%s' called", fcn)
+
+ # Verify correct number of arguments.
+ switch (opcode) {
+ case F_MOD:
+ v_nargs = 2
+ case F_MAX, F_MIN, F_ATAN, F_ATAN2:
+ v_nargs = -1
+ default:
+ v_nargs = 1
+ }
+
+ if (v_nargs > 0 && nargs != v_nargs)
+ call xev_error2 ("function `%s' requires %d arguments",
+ fcn, v_nargs)
+ else if (v_nargs < 0 && nargs < abs(v_nargs))
+ call xev_error2 ("function `%s' requires at least %d arguments",
+ fcn, abs(v_nargs))
+
+ # Verify datatypes.
+ if (opcode != F_STR && opcode != F_BOOL) {
+ optype = TY_REAL
+ do i = 1, min(2,nargs) {
+ switch (O_TYPE(args[i])) {
+ case TY_INT:
+ ival[i] = O_VALI(args[i])
+ rval[i] = ival[i]
+ type[i] = TY_INT
+ case TY_REAL:
+ rval[i] = O_VALR(args[i])
+ ival[i] = nint (rval[i])
+ type[i] = TY_REAL
+ default:
+ goto badtype_
+ }
+ }
+ }
+
+ # Evaluate the function.
+
+ ap = args[1]
+
+ switch (opcode) {
+ case F_ABS:
+ if (type[1] == TY_INT) {
+ iresult = abs (ival[1])
+ optype = TY_INT
+ } else
+ rresult = abs (rval[1])
+
+ case F_ACOS:
+ rresult = RTOD (acos (rval[1]))
+ case F_ASIN:
+ rresult = RTOD (asin (rval[1]))
+ case F_COS:
+ rresult = cos (DTOR (rval[1]))
+ case F_EXP:
+ rresult = exp (rval[1])
+ case F_LOG:
+ rresult = log (rval[1])
+ case F_LOG10:
+ rresult = log10 (rval[1])
+ case F_SIN:
+ rresult = sin (DTOR (rval[1]))
+ case F_SQRT:
+ rresult = sqrt (rval[1])
+ case F_TAN:
+ rresult = tan (DTOR (rval[1]))
+
+ case F_ATAN, F_ATAN2:
+ if (nargs == 1)
+ rresult = RTOD (atan (rval[1]))
+ else
+ rresult = RTOD (atan2 (rval[1], rval[2]))
+
+ case F_MOD:
+ if (type[1] == TY_REAL || type[2] == TY_REAL)
+ rresult = mod (rval[1], rval[2])
+ else {
+ iresult = mod (ival[1], ival[2])
+ optype = TY_INT
+ }
+
+ case F_NINT:
+ iresult = nint (rval[1])
+ optype = TY_INT
+
+ case F_MAX, F_MIN:
+ # Determine datatype of result.
+ optype = TY_INT
+ do i = 1, nargs
+ if (O_TYPE(args[i]) == TY_REAL)
+ optype = TY_REAL
+ else if (O_TYPE(args[i]) != TY_INT)
+ goto badtype_
+
+ # Compute result.
+ if (optype == TY_INT) {
+ iresult = O_VALI(ap)
+ do i = 2, nargs {
+ itemp = O_VALI(args[i])
+ if (opcode == F_MAX)
+ iresult = max (iresult, itemp)
+ else
+ iresult = min (iresult, itemp)
+ }
+
+ } else {
+ if (O_TYPE(ap) == TY_INT)
+ rresult = O_VALI(ap)
+ else
+ rresult = O_VALR(ap)
+
+ do i = 2, nargs {
+ if (O_TYPE(args[i]) == TY_INT)
+ rtemp = O_VALI(args[i])
+ else
+ rtemp = O_VALR(args[i])
+ if (opcode == F_MAX)
+ rresult = max (rresult, rtemp)
+ else
+ rresult = min (rresult, rtemp)
+ }
+ }
+
+ case F_BOOL:
+ optype = TY_BOOL
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_VALB(ap))
+ iresult = 1
+ else
+ iresult = 0
+ case TY_CHAR:
+ iresult = strlen (O_VALC(ap))
+ case TY_INT:
+ iresult = O_VALI(ap)
+ case TY_REAL:
+ if (abs(rval[1]) > .001)
+ iresult = 1
+ else
+ iresult = 0
+ default:
+ goto badtype_
+ }
+
+ case F_INT:
+ optype = TY_INT
+ if (type[1] == TY_INT)
+ iresult = ival[1]
+ else
+ iresult = rval[1]
+
+ case F_REAL:
+ rresult = rval[1]
+
+ case F_STR:
+ # Convert operand to operand of type string.
+
+ optype = TY_CHAR
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ call malloc (iresult, 3, TY_CHAR)
+ oplen = 3
+ if (O_VALB(ap))
+ call strcpy ("yes", Memc[iresult], 3)
+ else
+ call strcpy ("no", Memc[iresult], 3)
+ case TY_CHAR:
+ oplen = strlen (O_VALC(ap))
+ call malloc (iresult, oplen, TY_CHAR)
+ call strcpy (O_VALC(ap), Memc[iresult], ARB)
+ case TY_INT:
+ oplen = MAX_DIGITS
+ call malloc (iresult, oplen, TY_CHAR)
+ call sprintf (Memc[iresult], SZ_FNAME, "%d")
+ call pargi (O_VALI(ap))
+ case TY_REAL:
+ oplen = MAX_DIGITS
+ call malloc (iresult, oplen, TY_CHAR)
+ call sprintf (Memc[iresult], SZ_FNAME, "%g")
+ call pargr (O_VALR(ap))
+ default:
+ goto badtype_
+ }
+
+ default:
+ call xev_error ("bad switch in callfcn")
+ }
+
+ # Write the result to the output operand. Bool results are stored in
+ # iresult as an integer value, string results are stored in iresult as
+ # a pointer to the output string, and integer and real results are
+ # stored in iresult and rresult without any tricks.
+
+ call xev_initop (out, oplen, optype)
+
+ switch (optype) {
+ case TY_BOOL:
+ O_VALB(out) = (iresult != 0)
+ case TY_CHAR:
+ O_VALP(out) = iresult
+ case TY_INT:
+ O_VALI(out) = iresult
+ case TY_REAL:
+ O_VALR(out) = rresult
+ }
+
+free_
+ # Free any storage used by the argument list operands.
+ do i = 1, nargs
+ call xev_freeop (args[i])
+
+ call sfree (sp)
+ return
+
+badtype_
+ call xev_error1 ("bad argument to function `%s'", fcn)
+ call sfree (sp)
+ return
+end
+
+
+# XEV_STARTARGLIST -- Allocate an argument list descriptor to receive
+# arguments as a function call is parsed. We are called with either
+# zero or one arguments. The argument list descriptor is pointed to by
+# a ficticious operand. The descriptor itself contains a count of the
+# number of arguments, an array of pointers to the operand structures,
+# as well as storage for the operand structures. The operands must be
+# stored locally since the parser will discard its copy of the operand
+# structure for each argument as the associated grammar rule is reduced.
+
+procedure xev_startarglist (arg, out)
+
+pointer arg # pointer to first argument, or NULL
+pointer out # output operand pointing to arg descriptor
+pointer ap
+
+errchk malloc
+
+begin
+ call xev_initop (out, 0, TY_POINTER)
+ call malloc (ap, LEN_ARGSTRUCT, TY_STRUCT)
+ O_VALP(out) = ap
+
+ if (arg == NULL)
+ A_NARGS(ap) = 0
+ else {
+ A_NARGS(ap) = 1
+ A_ARGP(ap,1) = A_OPS(ap)
+ YYMOVE (arg, A_OPS(ap))
+ }
+end
+
+
+# XEV_ADDARG -- Add an argument to the argument list for a function call.
+
+procedure xev_addarg (arg, arglist, out)
+
+pointer arg # pointer to argument to be added
+pointer arglist # pointer to operand pointing to arglist
+pointer out # output operand pointing to arg descriptor
+
+pointer ap, o
+int nargs
+
+begin
+ ap = O_VALP(arglist)
+
+ nargs = A_NARGS(ap) + 1
+ A_NARGS(ap) = nargs
+ if (nargs > MAX_ARGS)
+ call xev_error ("too many function arguments")
+
+ o = A_OPS(ap) + ((nargs - 1) * LEN_OPERAND)
+ A_ARGP(ap,nargs) = o
+ YYMOVE (arg, o)
+
+ YYMOVE (arglist, out)
+end
+
+
+# XEV_ERROR1 -- Take an error action, formatting an error message with one
+# format string plus one string argument.
+
+procedure xev_error1 (fmt, arg)
+
+char fmt[ARB] # printf format string
+char arg[ARB] # string argument
+pointer sp, buf
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_LINE, TY_CHAR)
+
+ call sprintf (Memc[buf], SZ_LINE, fmt)
+ call pargstr (arg)
+
+ call xev_error (Memc[buf])
+ call sfree (sp)
+end
+
+
+# XEV_ERROR2 -- Take an error action, formatting an error message with one
+# format string plus one string argument and one integer argument.
+
+procedure xev_error2 (fmt, arg1, arg2)
+
+char fmt[ARB] # printf format string
+char arg1[ARB] # string argument
+int arg2 # integer argument
+pointer sp, buf
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_LINE, TY_CHAR)
+
+ call sprintf (Memc[buf], SZ_LINE, fmt)
+ call pargstr (arg1)
+ call pargi (arg2)
+
+ call xev_error (Memc[buf])
+ call sfree (sp)
+end
+
+
+# XEV_ERROR -- Take an error action, given an error message string as the
+# sole argument.
+
+procedure xev_error (errmsg)
+
+char errmsg[ARB] # error message
+
+begin
+ call error (1, errmsg)
+end
+
+
+# XEV_INITOP -- Set up an unintialized operand structure.
+
+procedure xev_initop (o, o_len, o_type)
+
+pointer o # pointer to operand structure
+int o_len # length of operand (zero if scalar)
+int o_type # datatype of operand
+
+begin
+ O_LEN(o) = 0
+ call xev_makeop (o, o_len, o_type)
+end
+
+
+# XEV_MAKEOP -- Set up the operand structure. If the operand structure has
+# already been initialized and array storage allocated, free the old array.
+
+procedure xev_makeop (o, o_len, o_type)
+
+pointer o # pointer to operand structure
+int o_len # length of operand (zero if scalar)
+int o_type # datatype of operand
+
+errchk malloc
+
+begin
+ # Free old array storage if any.
+ if (O_TYPE(o) != 0 && O_LEN(o) > 1) {
+ call mfree (O_VALP(o), O_TYPE(o))
+ O_LEN(o) = 0
+ }
+
+ # Set new operand type.
+ O_TYPE(o) = o_type
+
+ # Allocate array storage if nonscalar operand.
+ if (o_len > 0) {
+ call malloc (O_VALP(o), o_len, o_type)
+ O_LEN(o) = o_len
+ }
+end
+
+
+# XEV_FREEOP -- Reinitialize an operand structure, i.e., free any associated
+# array storage and clear the operand datatype field, but do not free the
+# operand structure itself (which may be only a segment of an array and not
+# a separately allocated structure).
+
+procedure xev_freeop (o)
+
+pointer o # pointer to operand structure
+
+begin
+ # Free old array storage if any.
+ if (O_TYPE(o) != 0 && O_LEN(o) > 1) {
+ call mfree (O_VALP(o), O_TYPE(o))
+ O_LEN(o) = 0
+ }
+
+ # Clear the operand type to mark operand invalid.
+ O_TYPE(o) = 0
+end
diff --git a/sys/fmtio/evvexpr.com b/sys/fmtio/evvexpr.com
new file mode 100644
index 00000000..34a98be6
--- /dev/null
+++ b/sys/fmtio/evvexpr.com
@@ -0,0 +1,12 @@
+# EVVEXPR common.
+
+pointer ev_oval # pointer to expr value operand
+int ev_st # symbol table
+int ev_getop # user supplied get operand procedure
+int ev_getop_data # client data for above
+int ev_ufcn # user supplied function call procedure
+int ev_ufcn_data # client data for above
+int ev_flags # flag bits
+
+common /xvvcom/ ev_oval, ev_st, ev_getop, ev_getop_data, ev_ufcn,
+ ev_ufcn_data, ev_flags
diff --git a/sys/fmtio/evvexpr.gy b/sys/fmtio/evvexpr.gy
new file mode 100644
index 00000000..32a91153
--- /dev/null
+++ b/sys/fmtio/evvexpr.gy
@@ -0,0 +1,2680 @@
+%{
+include <lexnum.h>
+include <ctype.h>
+include <mach.h>
+include <math.h>
+include <evvexpr.h>
+
+.help evvexpr
+.nf --------------------------------------------------------------------------
+EVVEXPR.GY -- Generic XYacc source for a general vector expression evaluator.
+
+ o = evvexpr (expr, getop, getop_data, ufcn, ufcn_data, flags)
+ evvfree (o)
+
+Client callbacks:
+
+ getop (client_data, opname, out)
+ ufcn (client_data, fcn, args, nargs, out)
+
+here "out" is the output operand returned to EVVEXPR. Client_data is any
+arbitrary integer or pointer value passed in to EVVEXPR when by the client
+when the callback was registered. "args" is an array of operand structs,
+the arguments for the user function being called. If the operand or
+function call cannot be completed normally an error exit may be made (call
+error) or an invalid operand may be returned (O_TYPE set to 0). The client
+should not free the "args" input operands, this will be handled by EVVEXPR.
+
+Operand struct (lib$evvexpr.h):
+
+ struct operand {
+ int O_TYPE # operand type (bcsilrd)
+ int O_LEN # operand length (0=scalar)
+ int O_FLAGS # O_FREEVAL, O_FREEOP
+ union {
+ char* O_VALC # string
+ short O_VALS
+ int O_VALI # int or bool
+ long O_VALL
+ real O_VALR
+ double O_VALD
+ pointer O_VALP # vector data
+ }
+ }
+
+The macro O_VALC references the string value of a TY_CHAR operand. The
+flags are O_FREEVAL and O_FREEOP, which tell EVVEXPR and EVVFREE whether or
+not to free any vector operand array or the operand struct when the operand
+is freed. The client should set these flags on operands returned to EVVEXPR
+if it wants EVVEXPR to free any operand storage.
+
+Supported types are bool, char (string), and SILRD. Bool is indicated as
+TY_BOOL in the O_TYPE field of the operand struct, but is stored internally
+as an integer and the value field of a boolean operand is given by O_VALI.
+
+Operands may be either scalars or vectors. A vector is indicated by a O_LEN
+value greater than zero. For vector operands O_VALP points to the data array.
+A special case is TY_CHAR (string), in which case O_LEN is the allocated
+length of the EOS-terminated string. A string is logically a scalar value
+even though it is physically stored in the operand as a character vector.
+
+The trig functions operate upon angles in units of radians. The intrinsic
+functions RAD and DEG are available for converting between radians and
+degrees. A string can be coerced to a binary value and vice versa, using
+the INT, STR, etc. intrinsic functions.
+
+This is a generalization of the older EVEXPR routine, adding additional
+datatypes, support for vector operands, and numerous minor enhancements.
+.endhelp ---------------------------------------------------------------------
+
+define YYMAXDEPTH 64 # parser stack length
+define MAX_ARGS 17 # max args in a function call
+define yyparse xvv_parse
+
+# Arglist structure.
+define LEN_ARGSTRUCT (1+MAX_ARGS+(MAX_ARGS*LEN_OPERAND))
+define A_NARGS Memi[$1] # number of arguments
+define A_ARGP Memi[$1+$2] # array of pointers to operand structs
+define A_OPS ($1+MAX_ARGS+1) # offset to operand storage area
+
+# Intrinsic functions.
+
+define LEN_STAB 300 # for symbol table
+define LEN_SBUF 256
+define LEN_INDEX 97
+
+define LEN_SYM 1 # symbol data
+define SYM_CODE Memi[$1]
+
+define KEYWORDS "|abs|acos|asin|atan|atan2|bool|cos|cosh|deg|double|\
+ |exp|hiv|int|len|log|log10|long|lov|max|mean|median|\
+ |min|mod|nint|rad|real|repl|stddev|shift|short|sin|\
+ |sinh|sort|sqrt|str|sum|tan|tanh|"
+
+define F_ABS 01 # function codes
+define F_ACOS 02
+define F_ASIN 03
+define F_ATAN 04
+define F_ATAN2 05
+define F_BOOL 06
+define F_COS 07
+define F_COSH 08
+define F_DEG 09 # radians to degrees
+define F_DOUBLE 10
+ # newline 11
+define F_EXP 12
+define F_HIV 13 # high value
+define F_INT 14
+define F_LEN 15 # vector length
+define F_LOG 16
+define F_LOG10 17
+define F_LONG 18
+define F_LOV 19 # low value
+define F_MAX 20
+define F_MEAN 21
+define F_MEDIAN 22
+ # newline 23
+define F_MIN 24
+define F_MOD 25
+define F_NINT 26
+define F_RAD 27 # degrees to radians
+define F_REAL 28
+define F_REPL 29 # replicate
+define F_STDDEV 30 # standard deviation
+define F_SHIFT 31
+define F_SHORT 32
+define F_SIN 33
+ # newline 34
+define F_SINH 35
+define F_SORT 36 # sort
+define F_SQRT 37 # square root
+define F_STR 38
+define F_SUM 39
+define F_TAN 40
+define F_TANH 41
+
+define T_B TY_BOOL
+define T_C TY_CHAR
+define T_S TY_SHORT
+define T_I TY_INT
+define T_L TY_LONG
+define T_R TY_REAL
+define T_D TY_DOUBLE
+
+
+# EVVEXPR -- Evaluate a general mixed type vector expression. Input consists
+# of the expression to be evaluated (a string) and, optionally, user
+# procedures for fetching external operands and executing external functions.
+# Output is a pointer to an operand structure containing the computed value of
+# the expression. The output operand structure is dynamically allocated by
+# EVVEXPR and must be freed by the user.
+#
+# NOTE: this is not intended to be an especially efficient procedure. Rather,
+# this is a high level, easy to use procedure, intended to provide greater
+# flexibility in the parameterization of applications programs. The main
+# inefficiency is that, since compilation and execution are not broken out as
+# separate steps, when the routine is repeatedly called to evaluate the same
+# expression with different data, all the compile time computation (parsing
+# etc.) has to be repeated.
+
+pointer procedure evvexpr (expr, getop, getop_data, ufcn, ufcn_data, flags)
+
+char expr[ARB] #I expression to be evaluated
+int getop #I user supplied get operand procedure
+int getop_data #I client data for above function
+int ufcn #I user supplied function call procedure
+int ufcn_data #I client data for above function
+int flags #I flag bits
+
+int junk
+pointer sp, ip
+bool debug, first_time
+int strlen(), xvv_parse()
+pointer xvv_loadsymbols()
+extern xvv_gettok()
+
+errchk xvv_parse, calloc
+include "evvexpr.com"
+data debug /false/
+data first_time /true/
+
+begin
+ call smark (sp)
+
+ if (first_time) {
+ # This creates data which remains for the life of the process.
+ ev_st = xvv_loadsymbols (KEYWORDS)
+ first_time = false
+ }
+
+ # Set user function entry point addresses.
+ ev_getop = getop
+ ev_getop_data = getop_data
+ ev_ufcn = ufcn
+ ev_ufcn_data = ufcn_data
+ ev_flags = flags
+
+ # Allocate an operand struct for the expression value.
+ call calloc (ev_oval, LEN_OPERAND, TY_STRUCT)
+
+ # Make a local copy of the input string.
+ call salloc (ip, strlen(expr), TY_CHAR)
+ call strcpy (expr, Memc[ip], ARB)
+
+ # Evaluate the expression. The expression value is copied into the
+ # output operand structure by XVV_PARSE, given the operand pointer
+ # passed in common. A common must be used since the standard parser
+ # subroutine has a fixed calling sequence.
+
+ junk = xvv_parse (ip, debug, xvv_gettok)
+ O_FLAGS(ev_oval) = or (O_FLAGS(ev_oval), O_FREEOP)
+
+ call sfree (sp)
+ return (ev_oval)
+end
+
+
+# EVVFREE -- Free an operand struct such as is returned by EVVEXPR.
+
+procedure evvfree (o)
+
+pointer o # operand struct
+
+begin
+ call xvv_freeop (o)
+end
+
+%L
+# XVV_PARSE -- SPP/Yacc parser for the evaluation of an expression passed as
+# a text string. Expression evaluation is carried out as the expression is
+# parsed, rather than being broken into separate compile and execute stages.
+# There is only one statement in this grammar, the expression. Our function
+# is to reduce an expression to a single value of type bool, string, int,
+# or real.
+
+pointer ap
+bool streq()
+errchk zcall3, xvv_error1, xvv_unop, xvv_binop, xvv_boolop
+errchk xvv_quest, xvv_callfcn, xvv_addarg
+include "evvexpr.com"
+
+%}
+
+# The $/ following causes the generic preprocessor to pass this block of code
+# through unchanged.
+
+$/
+
+%token CONSTANT IDENTIFIER NEWLINE YYEOS
+%token PLUS MINUS STAR SLASH EXPON CONCAT QUEST COLON
+%token LT GT LE GT EQ NE SE LAND LOR LNOT BAND BOR BXOR BNOT AT
+
+%nonassoc QUEST
+%left LAND LOR
+%left BAND BOR BXOR
+%nonassoc EQ NE SE
+%nonassoc LT GT LE GE
+%left CONCAT
+%left PLUS MINUS
+%left STAR SLASH
+%right UMINUS LNOT BNOT
+%left EXPON
+%right AT
+
+%%
+
+stmt : exprlist YYEOS {
+ # Normal exit. Move the final expression value operand
+ # into the operand structure pointed to by the global
+ # variable ev_oval.
+
+ YYMOVE ($1, ev_oval)
+ call sfree (yysp)
+ return (OK)
+ }
+ | error {
+ call error (1, "syntax error")
+ }
+ ;
+
+exprlist: expr {
+ YYMOVE ($1, $$)
+ }
+ | exprlist ',' opnl expr {
+ YYMOVE ($4, $$)
+ call xvv_freeop ($1)
+ }
+
+
+expr : CONSTANT {
+ # Numeric constant.
+ YYMOVE ($1, $$)
+ }
+ | IDENTIFIER {
+ # The boolean constants "yes" and "no" are implemented
+ # as reserved operands.
+
+ call xvv_initop ($$, 0, TY_BOOL)
+ if (streq (O_VALC($1), "yes")) {
+ O_VALI($$) = YES
+ } else if (streq (O_VALC($1), "no")) {
+ O_VALI($$) = NO
+ } else if (ev_getop != NULL) {
+ call zcall3 (ev_getop,ev_getop_data, O_VALC($1), $$)
+ if (O_TYPE($$) <= 0)
+ call xvv_error1 ("unknown operand `%s'",
+ O_VALC($1))
+ } else
+ call xvv_error1 ("illegal operand `%s'", O_VALC($1))
+ call xvv_freeop ($1)
+ }
+ | AT CONSTANT {
+ # e.g., @"param"
+ if (ev_getop != NULL) {
+ call zcall3 (ev_getop,ev_getop_data, O_VALC($2), $$)
+ if (O_TYPE($$) <= 0)
+ call xvv_error1 ("unknown operand `%s'",
+ O_VALC($1))
+ } else
+ call xvv_error1 ("illegal operand `%s'", O_VALC($2))
+ call xvv_freeop ($2)
+ }
+ | MINUS expr %prec UMINUS {
+ # Unary arithmetic minus.
+ call xvv_unop (MINUS, $2, $$)
+ }
+ | LNOT expr {
+ # Logical not.
+ call xvv_unop (LNOT, $2, $$)
+ }
+ | BNOT expr {
+ # Boolean not.
+ call xvv_unop (BNOT, $2, $$)
+ }
+ | expr PLUS opnl expr {
+ # Addition.
+ call xvv_binop (PLUS, $1, $4, $$)
+ }
+ | expr MINUS opnl expr {
+ # Subtraction.
+ call xvv_binop (MINUS, $1, $4, $$)
+ }
+ | expr STAR opnl expr {
+ # Multiplication.
+ call xvv_binop (STAR, $1, $4, $$)
+ }
+ | expr SLASH opnl expr {
+ # Division.
+ call xvv_binop (SLASH, $1, $4, $$)
+ }
+ | expr EXPON opnl expr {
+ # Exponentiation.
+ call xvv_binop (EXPON, $1, $4, $$)
+ }
+ | expr CONCAT opnl expr {
+ # Concatenate two operands.
+ call xvv_binop (CONCAT, $1, $4, $$)
+ }
+ | expr LAND opnl expr {
+ # Logical and.
+ call xvv_boolop (LAND, $1, $4, $$)
+ }
+ | expr LOR opnl expr {
+ # Logical or.
+ call xvv_boolop (LOR, $1, $4, $$)
+ }
+ | expr BAND opnl expr {
+ # Boolean and.
+ call xvv_binop (BAND, $1, $4, $$)
+ }
+ | expr BOR opnl expr {
+ # Boolean or.
+ call xvv_binop (BOR, $1, $4, $$)
+ }
+ | expr BXOR opnl expr {
+ # Boolean xor.
+ call xvv_binop (BXOR, $1, $4, $$)
+ }
+ | expr LT opnl expr {
+ # Boolean less than.
+ call xvv_boolop (LT, $1, $4, $$)
+ }
+ | expr GT opnl expr {
+ # Boolean greater than.
+ call xvv_boolop (GT, $1, $4, $$)
+ }
+ | expr LE opnl expr {
+ # Boolean less than or equal.
+ call xvv_boolop (LE, $1, $4, $$)
+ }
+ | expr GE opnl expr {
+ # Boolean greater than or equal.
+ call xvv_boolop (GE, $1, $4, $$)
+ }
+ | expr EQ opnl expr {
+ # Boolean equal.
+ call xvv_boolop (EQ, $1, $4, $$)
+ }
+ | expr SE opnl expr {
+ # String pattern-equal.
+ call xvv_boolop (SE, $1, $4, $$)
+ }
+ | expr NE opnl expr {
+ # Boolean not equal.
+ call xvv_boolop (NE, $1, $4, $$)
+ }
+ | expr QUEST opnl expr COLON opnl expr {
+ # Conditional expression.
+ call xvv_quest ($1, $4, $7, $$)
+ }
+ | funct '(' arglist ')' {
+ # Call an intrinsic or external function.
+ ap = O_VALP($3)
+ call xvv_callfcn (O_VALC($1),
+ A_ARGP(ap,1), A_NARGS(ap), $$)
+ call xvv_freeop ($1)
+ call xvv_freeop ($3)
+ }
+ | '(' expr ')' {
+ YYMOVE ($2, $$)
+ }
+ ;
+
+
+funct : IDENTIFIER {
+ YYMOVE ($1, $$)
+ }
+ | CONSTANT {
+ if (O_TYPE($1) != TY_CHAR)
+ call error (1, "illegal function name")
+ YYMOVE ($1, $$)
+ }
+ ;
+
+
+arglist : {
+ # Empty.
+ call xvv_startarglist (NULL, $$)
+ }
+ | expr {
+ # First arg; start a nonnull list.
+ call xvv_startarglist ($1, $$)
+ }
+ | arglist ',' opnl expr {
+ # Add an argument to an existing list.
+ call xvv_addarg ($4, $1, $$)
+ }
+ ;
+
+
+opnl : # Empty.
+ | opnl NEWLINE
+ ;
+
+%%
+
+# End generic preprocessor escape.
+/
+
+
+# XVV_UNOP -- Unary operation. Perform the indicated unary operation on the
+# input operand, returning the result as the output operand.
+
+procedure xvv_unop (opcode, in, out)
+
+int opcode #I operation to be performed
+pointer in #I input operand
+pointer out #I output operand
+
+short val_s
+long val_l
+int val_i, nelem
+errchk xvv_error, xvv_initop
+string s_badswitch "unop: bad switch"
+
+begin
+ nelem = O_LEN(in)
+
+ switch (opcode) {
+ case MINUS:
+ # Unary negation.
+ call xvv_initop (out, nelem, O_TYPE(in))
+ switch (O_TYPE(in)) {
+ case TY_BOOL, TY_CHAR:
+ call xvv_error ("negation of a nonarithmetic operand")
+$for (silrd)
+ case TY_PIXEL:
+ if (nelem > 0)
+ call aneg$t (Mem$t[O_VALP(in)], Mem$t[O_VALP(out)], nelem)
+ else
+ O_VAL$T(out) = -O_VAL$T(in)
+$endfor
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ case LNOT:
+ # Logical NOT.
+
+ call xvv_initop (out, nelem, TY_BOOL)
+ switch (O_TYPE(in)) {
+ case TY_BOOL:
+ if (nelem > 0)
+ call abeqki (Memi[O_VALP(in)], NO, Memi[O_VALP(out)], nelem)
+ else {
+ if (O_VALI(in) == NO)
+ O_VALI(out) = YES
+ else
+ O_VALI(out) = NO
+ }
+$for (sil)
+ case TY_PIXEL:
+ if (nelem > 0) {
+ val_$t = NO
+ call abeqk$t (Mem$t[O_VALP(in)], val_$t, Memi[O_VALP(out)],
+ nelem)
+ } else {
+ if (O_VAL$T(in) == NO)
+ O_VAL$T(out) = YES
+ else
+ O_VAL$T(out) = NO
+ }
+$endfor
+ case TY_CHAR, TY_REAL, TY_DOUBLE:
+ call xvv_error ("not of a nonlogical")
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ case BNOT:
+ # Bitwise boolean NOT.
+
+ call xvv_initop (out, nelem, O_TYPE(in))
+ switch (O_TYPE(in)) {
+ case TY_BOOL, TY_CHAR, TY_REAL, TY_DOUBLE:
+ call xvv_error ("boolean not of a noninteger operand")
+$for (sil)
+ case TY_PIXEL:
+ if (nelem > 0)
+ call anot$t (Mem$t[O_VALP(in)], Mem$t[O_VALP(out)], nelem)
+ else
+ O_VAL$T(out) = not(O_VAL$T(in))
+$endfor
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ call xvv_freeop (in)
+end
+
+
+# XVV_BINOP -- Binary operation. Perform the indicated arithmetic binary
+# operation on the two input operands, returning the result as the output
+# operand.
+
+procedure xvv_binop (opcode, in1, in2, out)
+
+int opcode #I operation to be performed
+pointer in1, in2 #I input operands
+pointer out #I output operand
+
+$for (silrd)
+PIXEL v_$t
+PIXEL xvv_null$t()
+extern xvv_null$t()
+$endfor
+pointer sp, otemp, p1, p2, po
+int dtype, nelem, len1, len2
+include "evvexpr.com"
+
+int xvv_newtype(), strlen()
+errchk xvv_newtype, xvv_initop, xvv_chtype, xvv_error
+string s_badswitch "binop: bad case in switch"
+string s_boolop "binop: bitwise boolean operands must be an integer type"
+define done_ 91
+
+begin
+ # Set the datatype of the output operand, taking an error action if
+ # the operands have incompatible datatypes.
+
+ dtype = xvv_newtype (O_TYPE(in1), O_TYPE(in2))
+
+ # Compute the size of the output operand. If both input operands are
+ # vectors the length of the output vector is the shorter of the two.
+
+ switch (dtype) {
+ case TY_BOOL:
+ call xvv_error ("binop: operation illegal for boolean operands")
+ case TY_CHAR:
+ nelem = strlen (O_VALC(in1)) + strlen (O_VALC(in2))
+ default:
+ if (opcode == CONCAT)
+ nelem = max (1, O_LEN(in1)) + max (1, O_LEN(in2))
+ else {
+ if (O_LEN(in1) > 0 && O_LEN(in2) > 0)
+ nelem = min (O_LEN(in1), O_LEN(in2))
+ else if (O_LEN(in1) > 0)
+ nelem = O_LEN(in1)
+ else if (O_LEN(in2) > 0)
+ nelem = O_LEN(in2)
+ else
+ nelem = 0
+ }
+ }
+
+ # Convert input operands to desired type.
+ if (O_TYPE(in1) != dtype)
+ call xvv_chtype (in1, in1, dtype)
+ if (O_TYPE(in2) != dtype)
+ call xvv_chtype (in2, in2, dtype)
+
+ # If this is a scalar/vector operation make sure the vector is the
+ # first operand.
+
+ len1 = O_LEN(in1)
+ len2 = O_LEN(in2)
+
+ if (len1 == 0 && len2 > 0) {
+ switch (opcode) {
+ case PLUS:
+ # Swap operands.
+ call smark (sp)
+ call salloc (otemp, LEN_OPERAND, TY_STRUCT)
+ YYMOVE (in1, otemp)
+ YYMOVE (in2, in1)
+ YYMOVE (otemp, in2)
+ call sfree (sp)
+
+ case CONCAT:
+ ; # Do nothing
+
+ default:
+ # Promote operand to a constant vector. Inefficient, but
+ # better than aborting.
+
+ switch (dtype) {
+ $for (silrd)
+ case TY_PIXEL:
+ v_$t = O_VAL$T(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovk$t (v_$t, Mem$t[O_VALP(in1)], nelem)
+ $endfor
+ }
+ }
+
+ len1 = O_LEN(in1)
+ len2 = O_LEN(in2)
+ }
+
+ # Initialize the output operand.
+ call xvv_initop (out, nelem, dtype)
+
+ p1 = O_VALP(in1)
+ p2 = O_VALP(in2)
+ po = O_VALP(out)
+
+ # The bitwise boolean binary operators a special case since only the
+ # integer datatypes are permitted. Otherwise the bitwise booleans
+ # are just like arithmetic booleans.
+
+ if (opcode == BAND || opcode == BOR || opcode == BXOR) {
+ switch (dtype) {
+$for (sil)
+ case TY_PIXEL:
+ switch (opcode) {
+ case BAND:
+ if (len1 <= 0) {
+ O_VAL$T(out) = and (O_VAL$T(in1), O_VAL$T(in2))
+ } else if (len2 <= 0) {
+ call aandk$t (Mem$t[p1], O_VAL$T(in2),
+ Mem$t[po], nelem)
+ } else {
+ call aand$t (Mem$t[p1], Mem$t[p2],
+ Mem$t[po], nelem)
+ }
+ case BOR:
+ if (len1 <= 0) {
+ O_VAL$T(out) = or (O_VAL$T(in1), O_VAL$T(in2))
+ } else if (len2 <= 0) {
+ call abork$t (Mem$t[p1], O_VAL$T(in2),
+ Mem$t[po], nelem)
+ } else {
+ call abor$t (Mem$t[p1], Mem$t[p2],
+ Mem$t[po], nelem)
+ }
+ case BXOR:
+ if (len1 <= 0) {
+ O_VAL$T(out) = xor (O_VAL$T(in1), O_VAL$T(in2))
+ } else if (len2 <= 0) {
+ call axork$t (Mem$t[p1], O_VAL$T(in2),
+ Mem$t[po], nelem)
+ } else {
+ call axor$t (Mem$t[p1], Mem$t[p2],
+ Mem$t[po], nelem)
+ }
+ }
+$endfor
+ default:
+ call xvv_error (s_boolop)
+ }
+
+ goto done_
+ }
+
+ # Perform an arithmetic binary operation.
+ switch (dtype) {
+ case TY_CHAR:
+ switch (opcode) {
+ case CONCAT:
+ call strcpy (O_VALC(in1), O_VALC(out), ARB)
+ call strcat (O_VALC(in2), O_VALC(out), ARB)
+ default:
+ call xvv_error ("binop: operation illegal for string operands")
+ }
+$for (silrd)
+ case TY_PIXEL:
+ switch (opcode) {
+ case PLUS:
+ if (len1 <= 0) {
+ O_VAL$T(out) = O_VAL$T(in1) + O_VAL$T(in2)
+ } else if (len2 <= 0) {
+ call aaddk$t (Mem$t[p1], O_VAL$T(in2),
+ Mem$t[po], nelem)
+ } else {
+ call aadd$t (Mem$t[p1], Mem$t[p2],
+ Mem$t[po], nelem)
+ }
+ case MINUS:
+ if (len1 <= 0)
+ O_VAL$T(out) = O_VAL$T(in1) - O_VAL$T(in2)
+ else if (len2 <= 0)
+ call asubk$t (Mem$t[p1], O_VAL$T(in2), Mem$t[po], nelem)
+ else
+ call asub$t (Mem$t[p1], Mem$t[p2], Mem$t[po], nelem)
+
+ case STAR:
+ if (len1 <= 0)
+ O_VAL$T(out) = O_VAL$T(in1) * O_VAL$T(in2)
+ else if (len2 <= 0)
+ call amulk$t (Mem$t[p1], O_VAL$T(in2), Mem$t[po], nelem)
+ else
+ call amul$t (Mem$t[p1], Mem$t[p2], Mem$t[po], nelem)
+
+ case SLASH:
+ if (and (ev_flags, EV_RNGCHK) == 0) {
+ # No range checking.
+ if (len1 <= 0)
+ O_VAL$T(out) = O_VAL$T(in1) / O_VAL$T(in2)
+ else if (len2 <= 0)
+ call adivk$t (Mem$t[p1], O_VAL$T(in2), Mem$t[po], nelem)
+ else
+ call adiv$t (Mem$t[p1], Mem$t[p2], Mem$t[po], nelem)
+ } else {
+ # Check for divide by zero.
+ if (len1 <= 0) {
+ if (O_VAL$T(in2) == 0$f)
+ O_VAL$T(out) = xvv_null$t(0$f)
+ else
+ O_VAL$T(out) = O_VAL$T(in1) / O_VAL$T(in2)
+ } else if (len2 <= 0) {
+ if (O_VAL$T(in2) == 0$f)
+ call amovk$t (xvv_null$t(0$f), Mem$t[po], nelem)
+ else {
+ call adivk$t (Mem$t[p1], O_VAL$T(in2), Mem$t[po],
+ nelem)
+ }
+ } else {
+ call advz$t (Mem$t[p1], Mem$t[p2], Mem$t[po], nelem,
+ xvv_null$t)
+ }
+ }
+ case EXPON:
+ if (len1 <= 0)
+ O_VAL$T(out) = O_VAL$T(in1) ** O_VAL$T(in2)
+ else if (len2 <= 0)
+ call aexpk$t (Mem$t[p1], O_VAL$T(in2), Mem$t[po], nelem)
+ else
+ call aexp$t (Mem$t[p1], Mem$t[p2], Mem$t[po], nelem)
+
+ case CONCAT:
+ # Concatenate two numeric operands.
+ if (len1 <= 0) {
+ Mem$t[po] = O_VAL$T(in1)
+ po = po + 1
+ } else {
+ call amov$t (Mem$t[p1], Mem$t[po], len1)
+ po = po + len1
+ }
+ if (len2 <= 0)
+ Mem$t[po] = O_VAL$T(in2)
+ else
+ call amov$t (Mem$t[p2], Mem$t[po], len2)
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+$endfor
+ default:
+ call xvv_error (s_badswitch)
+ }
+done_
+ # Free any storage in input operands.
+ call xvv_freeop (in1)
+ call xvv_freeop (in2)
+end
+
+
+# XVV_BOOLOP -- Boolean (actually logical) binary operations. Perform the
+# indicated logical operation on the two input operands, returning the result
+# as the output operand. The opcodes implemented by this routine are
+# characterized by the fact that they all return a logical result (YES or NO
+# physically expressed as an integer).
+
+procedure xvv_boolop (opcode, in1, in2, out)
+
+int opcode #I operation to be performed
+pointer in1, in2 #I input operands
+pointer out #I output operand
+
+$for (silrd)
+PIXEL v_$t
+$endfor
+pointer sp, otemp, p1, p2, po
+int dtype, nelem, len1, len2
+int xvv_newtype(), xvv_patmatch(), strncmp(), btoi()
+errchk xvv_newtype, xvv_initop, xvv_chtype, xvv_error
+string s_badop "boolop: illegal operation"
+string s_badswitch "boolop: illegal switch"
+
+begin
+ # Boolean operands are treated as integer within this routine.
+ if (O_TYPE(in1) == TY_BOOL)
+ O_TYPE(in1) = TY_INT
+ if (O_TYPE(in2) == TY_BOOL)
+ O_TYPE(in2) = TY_INT
+
+ # Determine the computation type for the operation, i.e., the type
+ # both input operands must have. This is not the same as the type
+ # of the output operand, which is always boolean for the operations
+ # implemented by this routine.
+
+ dtype = xvv_newtype (O_TYPE(in1), O_TYPE(in2))
+
+ # Compute the size of the output operand. If both input operands are
+ # vectors the length of the output vector is the shorter of the two.
+
+ if (dtype == TY_CHAR)
+ nelem = 0
+ else {
+ if (O_LEN(in1) > 0 && O_LEN(in2) > 0)
+ nelem = min (O_LEN(in1), O_LEN(in2))
+ else if (O_LEN(in1) > 0)
+ nelem = O_LEN(in1)
+ else if (O_LEN(in2) > 0)
+ nelem = O_LEN(in2)
+ else
+ nelem = 0
+ }
+
+ # Convert input operands to desired computation type.
+ if (O_TYPE(in1) != dtype)
+ call xvv_chtype (in1, in1, dtype)
+ if (O_TYPE(in2) != dtype)
+ call xvv_chtype (in2, in2, dtype)
+
+ # If this is a scalar/vector operation make sure the vector is the
+ # first operand.
+
+ len1 = O_LEN(in1)
+ len2 = O_LEN(in2)
+
+ if (len1 == 0 && len2 > 0) {
+ switch (opcode) {
+ case EQ, NE:
+ call smark (sp)
+ call salloc (otemp, LEN_OPERAND, TY_STRUCT)
+ YYMOVE (in1, otemp)
+ YYMOVE (in2, in1)
+ YYMOVE (otemp, in2)
+ call sfree (sp)
+ default:
+ # Promote operand to a constant vector. Inefficient, but
+ # better than aborting.
+
+ switch (dtype) {
+ $for (silrd)
+ case TY_PIXEL:
+ v_$t = O_VAL$T(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovk$t (v_$t, Mem$t[O_VALP(in1)], nelem)
+ $endfor
+ }
+ }
+
+ len1 = O_LEN(in1)
+ len2 = O_LEN(in2)
+ }
+
+ # Initialize the output operand.
+ call xvv_initop (out, nelem, TY_BOOL)
+
+ p1 = O_VALP(in1)
+ p2 = O_VALP(in2)
+ po = O_VALP(out)
+
+ # Perform the operation.
+ if (dtype == TY_CHAR) {
+ # Character data is a special case.
+
+ switch (opcode) {
+ case SE:
+ O_VALI(out) = btoi(xvv_patmatch (O_VALC(in1), O_VALC(in2)) > 0)
+ case LT:
+ O_VALI(out) = btoi(strncmp (O_VALC(in1), O_VALC(in2), ARB) < 0)
+ case LE:
+ O_VALI(out) = btoi(strncmp (O_VALC(in1), O_VALC(in2), ARB) <= 0)
+ case GT:
+ O_VALI(out) = btoi(strncmp (O_VALC(in1), O_VALC(in2), ARB) > 0)
+ case GE:
+ O_VALI(out) = btoi(strncmp (O_VALC(in1), O_VALC(in2), ARB) >= 0)
+ case EQ:
+ O_VALI(out) = btoi(strncmp (O_VALC(in1), O_VALC(in2), ARB) == 0)
+ case NE:
+ O_VALI(out) = btoi(strncmp (O_VALC(in1), O_VALC(in2), ARB) != 0)
+ default:
+ call xvv_error (s_badop)
+ }
+
+ } else if (opcode == LAND || opcode == LOR) {
+ # Operations supporting only the integer types.
+
+ switch (dtype) {
+$for (sil)
+ case TY_PIXEL:
+ switch (opcode) {
+ case LAND:
+ if (len1 <= 0) {
+ O_VALI(out) =
+ btoi (O_VAL$T(in1) != 0 && O_VAL$T(in2) != 0)
+ } else if (len2 <= 0) {
+ call alank$t (Mem$t[p1], O_VAL$T(in2), Memi[po], nelem)
+ } else
+ call alan$t (Mem$t[p1], Mem$t[p2], Memi[po], nelem)
+ case LOR:
+ if (len1 <= 0) {
+ O_VALI(out) =
+ btoi (O_VAL$T(in1) != 0 || O_VAL$T(in2) != 0)
+ } else if (len2 <= 0) {
+ call alork$t (Mem$t[p1], O_VAL$T(in2), Memi[po], nelem)
+ } else
+ call alor$t (Mem$t[p1], Mem$t[p2], Memi[po], nelem)
+ default:
+ call xvv_error (s_badop)
+ }
+$endfor
+ default:
+ call xvv_error (s_badswitch)
+ }
+ } else {
+ # Operations supporting any arithmetic type.
+
+ switch (dtype) {
+$for (silrd)
+ case TY_PIXEL:
+ switch (opcode) {
+ case LT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VAL$T(in1) < O_VAL$T(in2))
+ else if (len2 <= 0)
+ call abltk$t (Mem$t[p1], O_VAL$T(in2), Memi[po], nelem)
+ else
+ call ablt$t (Mem$t[p1], Mem$t[p2], Memi[po], nelem)
+
+ case LE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VAL$T(in1) <= O_VAL$T(in2))
+ else if (len2 <= 0)
+ call ablek$t (Mem$t[p1], O_VAL$T(in2), Memi[po], nelem)
+ else
+ call able$t (Mem$t[p1], Mem$t[p2], Memi[po], nelem)
+
+ case GT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VAL$T(in1) > O_VAL$T(in2))
+ else if (len2 <= 0)
+ call abgtk$t (Mem$t[p1], O_VAL$T(in2), Memi[po], nelem)
+ else
+ call abgt$t (Mem$t[p1], Mem$t[p2], Memi[po], nelem)
+
+ case GE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VAL$T(in1) >= O_VAL$T(in2))
+ else if (len2 <= 0)
+ call abgek$t (Mem$t[p1], O_VAL$T(in2), Memi[po], nelem)
+ else
+ call abge$t (Mem$t[p1], Mem$t[p2], Memi[po], nelem)
+
+ case EQ:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VAL$T(in1) == O_VAL$T(in2))
+ else if (len2 <= 0)
+ call abeqk$t (Mem$t[p1], O_VAL$T(in2), Memi[po], nelem)
+ else
+ call abeq$t (Mem$t[p1], Mem$t[p2], Memi[po], nelem)
+
+ case NE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VAL$T(in1) != O_VAL$T(in2))
+ else if (len2 <= 0)
+ call abnek$t (Mem$t[p1], O_VAL$T(in2), Memi[po], nelem)
+ else
+ call abne$t (Mem$t[p1], Mem$t[p2], Memi[po], nelem)
+
+ default:
+ call xvv_error (s_badop)
+ }
+$endfor
+ default:
+ call xvv_error (s_badswitch)
+ }
+ }
+
+ # Free any storage in input operands.
+ call xvv_freeop (in1)
+ call xvv_freeop (in2)
+end
+
+
+# XVV_PATMATCH -- Match a string against a pattern, returning the patmatch
+# index if the string matches. The pattern may contain any of the conventional
+# pattern matching metacharacters. Closure (i.e., "*") is mapped to "?*".
+
+int procedure xvv_patmatch (str, pat)
+
+char str[ARB] #I operand string
+char pat[ARB] #I pattern
+
+int junk, ip, index
+pointer sp, patstr, patbuf, op
+int patmake(), patmatch()
+
+begin
+ call smark (sp)
+ call salloc (patstr, SZ_FNAME, TY_CHAR)
+ call salloc (patbuf, SZ_LINE, TY_CHAR)
+ call aclrc (Memc[patstr], SZ_FNAME)
+ call aclrc (Memc[patbuf], SZ_LINE)
+
+ # Map pattern, changing '*' into '?*'.
+ op = patstr
+ for (ip=1; pat[ip] != EOS; ip=ip+1) {
+ if (pat[ip] == '*') {
+ Memc[op] = '?'
+ op = op + 1
+ }
+ Memc[op] = pat[ip]
+ op = op + 1
+ }
+
+ # Encode pattern.
+ junk = patmake (Memc[patstr], Memc[patbuf], SZ_LINE)
+
+ # Perform the pattern matching operation.
+ index = patmatch (str, Memc[patbuf])
+
+ call sfree (sp)
+ return (index)
+end
+
+
+# XVV_NEWTYPE -- Get the datatype of a binary operation, given the datatypes
+# of the two input operands. An error action is taken if the datatypes are
+# incompatible, e.g., boolean and anything else or string and anything else.
+
+int procedure xvv_newtype (type1, type2)
+
+int type1 #I datatype of first operand
+int type2 #I datatype of second operand
+
+int newtype, p, q, i
+int tyindex[NTYPES], ttbl[NTYPES*NTYPES]
+data tyindex /T_B, T_C, T_S, T_I, T_L, T_R, T_D/
+
+data (ttbl(i),i= 1, 7) /T_B, 0, 0, 0, 0, 0, 0/
+data (ttbl(i),i= 8,14) / 0, T_C, 0, 0, 0, 0, 0/
+data (ttbl(i),i=15,21) / 0, 0, T_S, T_I, T_L, T_R, T_D/
+data (ttbl(i),i=22,28) / 0, 0, T_I, T_I, T_L, T_R, T_D/
+data (ttbl(i),i=29,35) / 0, 0, T_L, T_L, T_L, T_R, T_D/
+data (ttbl(i),i=36,42) / 0, 0, T_R, T_R, T_R, T_R, T_D/
+data (ttbl(i),i=43,49) / 0, 0, T_D, T_D, T_D, T_D, T_D/
+
+begin
+ do i = 1, NTYPES {
+ if (tyindex[i] == type1)
+ p = i
+ if (tyindex[i] == type2)
+ q = i
+ }
+
+ newtype = ttbl[(p-1)*NTYPES+q]
+ if (newtype == 0)
+ call xvv_error ("operands have incompatible types")
+ else
+ return (newtype)
+end
+
+
+# XVV_QUEST -- Conditional expression. If the condition operand is true
+# return the first (true) operand, else return the second (false) operand.
+
+procedure xvv_quest (cond, in1, in2, out)
+
+pointer cond #I pointer to condition operand
+pointer in1, in2 #I pointer to true,false operands
+pointer out #I pointer to output operand
+
+int dtype, nelem, i
+pointer sp, otemp, ip1, ip2, op, sel
+errchk xvv_error, xvv_newtype, xvv_initop, xvv_chtype
+int xvv_newtype(), btoi()
+
+begin
+ switch (O_TYPE(cond)) {
+ case TY_BOOL, TY_INT:
+ ;
+ case TY_SHORT, TY_LONG:
+ call xvv_chtype (cond, cond, TY_BOOL)
+ default:
+ call xvv_error ("evvexpr: nonboolean condition operand")
+ }
+
+ if (O_LEN(cond) <= 0 &&
+ (O_LEN(in1) <= 0 || O_TYPE(in1) == TY_CHAR) &&
+ (O_LEN(in2) <= 0 || O_TYPE(in2) == TY_CHAR)) {
+
+ # Both operands and the conditional are scalars; the expression
+ # type is the type of the selected operand.
+
+ if (O_VALI(cond) != 0) {
+ YYMOVE (in1, out)
+ call xvv_freeop (in2)
+ } else {
+ YYMOVE (in2, out)
+ call xvv_freeop (in1)
+ }
+
+ } else if (O_TYPE(in1) == TY_CHAR || O_TYPE(in2) == TY_CHAR) {
+ # This combination is not legal.
+ call xvv_error ("evvexpr: character and vector in cond expr")
+
+ } else {
+ # Vector/scalar or vector/vector operation. Both operands must
+ # be of the same type.
+
+ dtype = xvv_newtype (O_TYPE(in1), O_TYPE(in2))
+
+ # Compute the size of the output operand. If both input operands
+ # are vectors the length of the output vector is the shorter of
+ # the two. The condition operand contributes to the dimension of
+ # the expression result, although not to the datatype.
+
+ nelem = 0
+ if (O_LEN(in1) > 0 && O_LEN(in2) > 0)
+ nelem = min (O_LEN(in1), O_LEN(in2))
+ else if (O_LEN(in1) > 0)
+ nelem = O_LEN(in1)
+ else if (O_LEN(in2) > 0)
+ nelem = O_LEN(in2)
+
+ if (O_LEN(cond) > 0 && nelem > 0)
+ nelem = min (O_LEN(cond), nelem)
+ else if (O_LEN(cond) > 0)
+ nelem = O_LEN(cond)
+
+ # If this is a scalar/vector operation make sure the vector is the
+ # first operand.
+
+ if (O_LEN(in1) == 0 && O_LEN(in2) > 0) {
+ call smark (sp)
+ call salloc (otemp, LEN_OPERAND, TY_STRUCT)
+ YYMOVE (in1, otemp)
+ YYMOVE (in2, in1)
+ YYMOVE (otemp, in2)
+ call sfree (sp)
+
+ # Since we are swapping arguments we need to negate the cond.
+ if (O_LEN(cond) <= 0)
+ O_VALI(cond) = btoi (O_VALI(cond) == 0)
+ else {
+ call abeqki (Memi[O_VALP(cond)], NO, Memi[O_VALP(cond)],
+ nelem)
+ }
+ }
+
+ # Initialize the output operand.
+ call xvv_initop (out, nelem, dtype)
+
+ # Convert input operands to desired computation type.
+ if (O_TYPE(in1) != dtype)
+ call xvv_chtype (in1, in1, dtype)
+ if (O_TYPE(in2) != dtype)
+ call xvv_chtype (in2, in2, dtype)
+
+ ip1 = O_VALP(in1)
+ ip2 = O_VALP(in2)
+ op = O_VALP(out)
+ sel = O_VALP(cond)
+
+ # Perform the operation.
+ switch (dtype) {
+ $for (silrd)
+ case TY_PIXEL:
+ if (O_LEN(in1) <= 0 && O_LEN(in2) <= 0) {
+ # Vector conditional, both operands are scalars.
+ do i = 1, nelem
+ if (Memi[sel+i-1] != 0)
+ Mem$t[op+i-1] = O_VAL$T(in1)
+ else
+ Mem$t[op+i-1] = O_VAL$T(in2)
+
+ } else if (O_LEN(in2) <= 0) {
+ # Operand 1 is a vector, operand 2 is a scalar.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amov$t (Mem$t[ip1], Mem$t[op], nelem)
+ else
+ call amovk$t (O_VAL$T(in2), Mem$t[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call aselk$t (Mem$t[ip1], O_VAL$T(in2), Mem$t[op],
+ Memi[sel], nelem)
+ }
+ } else {
+ # Both operands are vectors.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amov$t (Mem$t[ip1], Mem$t[op], nelem)
+ else
+ call amov$t (Mem$t[ip2], Mem$t[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call asel$t (Mem$t[ip1], Mem$t[ip2], Mem$t[op],
+ Memi[sel], nelem)
+ }
+ }
+ $endfor
+ default:
+ call xvv_error ("evvexpr: bad datatype in cond expr")
+ }
+
+ call xvv_freeop (in1)
+ call xvv_freeop (in2)
+ }
+
+ call xvv_freeop (cond)
+end
+
+
+# XVV_CALLFCN -- Call an intrinsic function. If the function named is not
+# one of the standard intrinsic functions, call an external user function
+# if a function call procedure was supplied.
+
+procedure xvv_callfcn (fcn, args, nargs, out)
+
+char fcn[ARB] #I function to be called
+pointer args[ARB] #I pointer to arglist descriptor
+int nargs #I number of arguments
+pointer out #I output operand (function value)
+
+$for (silrd)
+PIXEL v_$t
+PIXEL ahiv$t(), alov$t()
+PIXEL amed$t()
+int arav$t()
+$endfor
+
+real mean_r, sigma_r
+double mean_d, sigma_d
+real asums(), asumi(), asumr()
+double asuml(), asumd()
+
+bool rangecheck
+int optype, opcode
+int chunk, repl, nelem, v_nargs, ch, shift, i, j
+pointer sp, sym, buf, ap, ip, op, in1, in2
+include "evvexpr.com"
+
+pointer stfind()
+int xvv_newtype(), strlen(), gctod(), btoi()
+errchk xvv_chtype, xvv_initop, xvv_newtype, xvv_error1, xvv_error2
+errchk zcall5, malloc
+
+string s_badtype "%s: illegal operand type"
+define free_ 91
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_FNAME, TY_CHAR)
+
+ # Lookup the function name in the symbol table.
+ sym = stfind (ev_st, fcn)
+ if (sym != NULL)
+ opcode = SYM_CODE(sym)
+ else
+ opcode = 0
+
+ # If the function named is not a standard one and the user has supplied
+ # the entry point of an external function evaluation procedure, call
+ # the user procedure to evaluate the function, otherwise abort.
+
+ if (opcode <= 0)
+ if (ev_ufcn != NULL) {
+ call zcall5 (ev_ufcn, ev_ufcn_data, fcn, args, nargs, out)
+ if (O_TYPE(out) <= 0)
+ call xvv_error1 ("unrecognized macro or function `%s'", fcn)
+ goto free_
+ } else
+ call xvv_error1 ("unknown function `%s' called", fcn)
+
+ # Range checking on functions that need it?
+ rangecheck = (and (ev_flags, EV_RNGCHK) != 0)
+
+ # Verify correct number of arguments.
+ switch (opcode) {
+ case F_MOD, F_REPL, F_SHIFT:
+ v_nargs = 2
+ case F_MAX, F_MIN, F_ATAN, F_ATAN2, F_MEAN, F_STDDEV, F_MEDIAN:
+ v_nargs = -1
+ default:
+ v_nargs = 1
+ }
+ if (v_nargs > 0 && nargs != v_nargs)
+ call xvv_error2 ("function `%s' requires %d arguments",
+ fcn, v_nargs)
+ else if (v_nargs < 0 && nargs < abs(v_nargs))
+ call xvv_error2 ("function `%s' requires at least %d arguments",
+ fcn, abs(v_nargs))
+
+ # Some functions require that the input operand be a certain type,
+ # e.g. floating. Handle the simple cases, converting input operands
+ # to the desired type.
+
+ switch (opcode) {
+ case F_ACOS, F_ASIN, F_ATAN, F_ATAN2, F_COS, F_COSH, F_DEG, F_EXP,
+ F_LOG, F_LOG10, F_RAD, F_SIN, F_SINH, F_SQRT, F_TAN, F_TANH:
+
+ # These functions want a floating point input operand.
+ optype = TY_REAL
+ do i = 1, nargs {
+ if (O_TYPE(args[i]) == TY_DOUBLE || O_TYPE(args[i]) == TY_LONG)
+ optype = TY_DOUBLE
+ }
+ do i = 1, nargs {
+ if (O_TYPE(args[i]) != optype)
+ call xvv_chtype (args[i], args[i], optype)
+ }
+ call xvv_initop (out, O_LEN(args[1]), optype)
+
+ case F_MOD, F_MIN, F_MAX, F_MEDIAN:
+ # These functions may have multiple arguments, all of which
+ # should be the same type.
+
+ optype = O_TYPE(args[1])
+ nelem = O_LEN(args[1])
+ do i = 2, nargs {
+ optype = xvv_newtype (optype, args[i])
+ if (O_LEN(args[i]) > 0)
+ if (nelem > 0)
+ nelem = min (nelem, O_LEN(args[i]))
+ else if (nelem == 0)
+ nelem = O_LEN(args[i])
+ }
+
+ do i = 1, nargs
+ if (O_TYPE(args[i]) != optype)
+ call xvv_chtype (args[i], args[i], optype)
+
+ if (nargs == 1 && opcode == F_MEDIAN)
+ nelem = 0
+ call xvv_initop (out, nelem, optype)
+
+ case F_LEN:
+ # This function always returns an integer scalar value.
+ nelem = 0
+ optype = TY_INT
+ call xvv_initop (out, nelem, optype)
+
+ case F_HIV, F_LOV:
+ # These functions return a scalar value.
+ nelem = 0
+ optype = O_TYPE(args[1])
+ if (optype == TY_BOOL)
+ optype = TY_INT
+ call xvv_initop (out, nelem, optype)
+
+ case F_SUM, F_MEAN, F_STDDEV:
+ # These functions require a vector argument and return a scalar
+ # value.
+
+ nelem = 0
+ optype = O_TYPE(args[1])
+ if (optype == TY_BOOL)
+ optype = TY_INT
+
+ if (optype == TY_DOUBLE)
+ call xvv_initop (out, nelem, TY_DOUBLE)
+ else
+ call xvv_initop (out, nelem, TY_REAL)
+
+ case F_SORT, F_SHIFT:
+ # Vector to vector, no type conversions.
+ nelem = O_LEN(args[1])
+ optype = O_TYPE(args[1])
+ call xvv_initop (out, nelem, optype)
+
+ default:
+ optype = 0
+ }
+
+ # Evaluate the function.
+ ap = args[1]
+
+ switch (opcode) {
+ case F_ABS:
+ call xvv_initop (out, O_LEN(ap), O_TYPE(ap))
+ switch (O_TYPE(ap)) {
+ $for (silrd)
+ case TY_PIXEL:
+ if (O_LEN(ap) > 0) {
+ call aabs$t (Mem$t[O_VALP(ap)], Mem$t[O_VALP(out)],
+ O_LEN(ap))
+ } else
+ O_VAL$T(out) = abs(O_VAL$T(ap))
+ $endfor
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_ACOS:
+ $for (rd)
+ if (optype == TY_PIXEL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Mem$t[O_VALP(out)+i-1] = acos (Mem$t[O_VALP(ap)+i-1])
+ } else
+ O_VAL$T(out) = acos (O_VAL$T(ap))
+ $endfor
+ case F_ASIN:
+ $for (rd)
+ if (optype == TY_PIXEL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Mem$t[O_VALP(out)+i-1] = asin (Mem$t[O_VALP(ap)+i-1])
+ } else
+ O_VAL$T(out) = asin (O_VAL$T(ap))
+ $endfor
+ case F_COS:
+ $for (rd)
+ if (optype == TY_PIXEL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Mem$t[O_VALP(out)+i-1] = cos (Mem$t[O_VALP(ap)+i-1])
+ } else
+ O_VAL$T(out) = cos (O_VAL$T(ap))
+ $endfor
+ case F_COSH:
+ $for (rd)
+ if (optype == TY_PIXEL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Mem$t[O_VALP(out)+i-1] = cosh (Mem$t[O_VALP(ap)+i-1])
+ } else
+ O_VAL$T(out) = cosh (O_VAL$T(ap))
+ $endfor
+ case F_DEG:
+ $for (rd)
+ if (optype == TY_PIXEL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Mem$t[O_VALP(out)+i-1] = RADTODEG(Mem$t[O_VALP(ap)+i-1])
+ } else
+ O_VAL$T(out) = RADTODEG (O_VAL$T(ap))
+ $endfor
+ case F_EXP:
+ $for (rd)
+ if (optype == TY_PIXEL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Mem$t[O_VALP(out)+i-1] = exp (Mem$t[O_VALP(ap)+i-1])
+ } else
+ O_VAL$T(out) = exp (O_VAL$T(ap))
+ $endfor
+ case F_LOG:
+ $for (rd)
+ if (optype == TY_PIXEL)
+ if (O_LEN(ap) > 0) {
+ op = O_VALP(out)
+ do i = 1, O_LEN(ap) {
+ v_$t = Mem$t[O_VALP(ap)+i-1]
+ if (rangecheck && v_$t <= 0)
+ Mem$t[op] = 0
+ else
+ Mem$t[op] = log (v_$t)
+ op = op + 1
+ }
+ } else {
+ v_$t = O_VAL$T(ap)
+ if (rangecheck && v_$t <= 0)
+ O_VAL$T(out) = 0
+ else
+ O_VAL$T(out) = log (v_$t)
+ }
+ $endfor
+ case F_LOG10:
+ $for (rd)
+ if (optype == TY_PIXEL)
+ if (O_LEN(ap) > 0) {
+ op = O_VALP(out)
+ do i = 1, O_LEN(ap) {
+ v_$t = Mem$t[O_VALP(ap)+i-1]
+ if (rangecheck && v_$t <= 0)
+ Mem$t[op] = 0
+ else
+ Mem$t[op] = log10 (v_$t)
+ op = op + 1
+ }
+ } else {
+ v_$t = O_VAL$T(ap)
+ if (rangecheck && v_$t <= 0)
+ O_VAL$T(out) = 0
+ else
+ O_VAL$T(out) = log10 (v_$t)
+ }
+ $endfor
+ case F_RAD:
+ $for (rd)
+ if (optype == TY_PIXEL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Mem$t[O_VALP(out)+i-1] = DEGTORAD(Mem$t[O_VALP(ap)+i-1])
+ } else
+ O_VAL$T(out) = DEGTORAD (O_VAL$T(ap))
+ $endfor
+ case F_SIN:
+ $for (rd)
+ if (optype == TY_PIXEL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Mem$t[O_VALP(out)+i-1] = sin (Mem$t[O_VALP(ap)+i-1])
+ } else
+ O_VAL$T(out) = sin (O_VAL$T(ap))
+ $endfor
+ case F_SINH:
+ $for (rd)
+ if (optype == TY_PIXEL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Mem$t[O_VALP(out)+i-1] = sinh (Mem$t[O_VALP(ap)+i-1])
+ } else
+ O_VAL$T(out) = sinh (O_VAL$T(ap))
+ $endfor
+ case F_SQRT:
+ $for (rd)
+ if (optype == TY_PIXEL)
+ if (O_LEN(ap) > 0) {
+ op = O_VALP(out)
+ do i = 1, O_LEN(ap) {
+ v_$t = Mem$t[O_VALP(ap)+i-1]
+ if (rangecheck && v_$t < 0)
+ Mem$t[op] = 0
+ else
+ Mem$t[op] = sqrt (v_$t)
+ op = op + 1
+ }
+ } else {
+ v_$t = O_VAL$T(ap)
+ if (rangecheck && v_$t <= 0)
+ O_VAL$T(out) = 0
+ else
+ O_VAL$T(out) = sqrt (v_$t)
+ }
+ $endfor
+ case F_TAN:
+ $for (rd)
+ if (optype == TY_PIXEL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Mem$t[O_VALP(out)+i-1] = tan (Mem$t[O_VALP(ap)+i-1])
+ } else
+ O_VAL$T(out) = tan (O_VAL$T(ap))
+ $endfor
+ case F_TANH:
+ $for (rd)
+ if (optype == TY_PIXEL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Mem$t[O_VALP(out)+i-1] = tanh (Mem$t[O_VALP(ap)+i-1])
+ } else
+ O_VAL$T(out) = tanh (O_VAL$T(ap))
+ $endfor
+
+ case F_LEN:
+ # Vector length.
+ O_VALI(out) = O_LEN(ap)
+
+ case F_HIV:
+ # High value.
+ switch (optype) {
+ $for (silrd)
+ case TY_PIXEL:
+ if (O_LEN(ap) > 0)
+ O_VAL$T(out) = ahiv$t (Mem$t[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VAL$T(out) = O_VAL$T(ap)
+ $endfor
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+ case F_LOV:
+ # Low value.
+ switch (optype) {
+ $for (silrd)
+ case TY_PIXEL:
+ if (O_LEN(ap) > 0)
+ O_VAL$T(out) = alov$t (Mem$t[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VAL$T(out) = O_VAL$T(ap)
+ $endfor
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_SUM:
+ # Vector sum.
+ switch (optype) {
+ $for (silr)
+ case TY_PIXEL:
+ if (O_LEN(ap) > 0)
+ v_r = asum$t (Mem$t[O_VALP(ap)], O_LEN(ap))
+ else
+ v_r = O_VAL$T(ap)
+ $endfor
+ case TY_DOUBLE:
+ if (O_LEN(ap) > 0)
+ v_d = asumd (Memd[O_VALP(ap)], O_LEN(ap))
+ else
+ v_d = O_VALD(ap)
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ if (optype == TY_DOUBLE)
+ O_VALD(out) = v_d
+ else
+ O_VALR(out) = v_r
+
+ case F_MEAN, F_STDDEV:
+ # Compute the mean or standard deviation of a vector. An optional
+ # second argument may be supplied to compute a K-sigma rejection
+ # mean and sigma.
+
+ if (nargs == 2) {
+ if (O_LEN(args[2]) > 0)
+ call xvv_error1 ("%s: ksigma arg must be a scalar" , fcn)
+
+ switch (O_TYPE(args[2])) {
+ case TY_REAL:
+ v_r = O_VALR(args[2])
+ v_d = v_r
+ case TY_DOUBLE:
+ v_d = O_VALD(args[2])
+ v_r = v_d
+ default:
+ call xvv_chtype (args[2], args[2], TY_REAL)
+ v_r = O_VALR(args[2])
+ v_d = v_r
+ }
+ } else {
+ v_r = 0.0
+ v_d = 0.0
+ }
+
+ switch (optype) {
+ $for (sir)
+ case TY_PIXEL:
+ v_i = arav$t (Mem$t[O_VALP(ap)], O_LEN(ap), mean_r,sigma_r,v_r)
+ $endfor
+ $for (ld)
+ case TY_PIXEL:
+ v_i = arav$t (Mem$t[O_VALP(ap)], O_LEN(ap), mean_d,sigma_d,v_d)
+ $endfor
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ if (opcode == F_MEAN) {
+ if (O_TYPE(out) == TY_REAL)
+ O_VALR(out) = mean_r
+ else
+ O_VALD(out) = mean_d
+ } else {
+ if (O_TYPE(out) == TY_REAL)
+ O_VALR(out) = sigma_r
+ else
+ O_VALD(out) = sigma_d
+ }
+
+ case F_MEDIAN:
+ # Compute the median value of a vector, or the vector median
+ # of 3 or more vectors.
+
+ switch (nargs) {
+ case 1:
+ switch (optype) {
+ $for (silrd)
+ case TY_PIXEL:
+ O_VAL$T(out) = amed$t (Mem$t[O_VALP(ap)], O_LEN(ap))
+ $endfor
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+ case 3:
+ switch (optype) {
+ $for (silrd)
+ case TY_PIXEL:
+ call amed3$t (Mem$t[O_VALP(args[1])],
+ Mem$t[O_VALP(args[2])],
+ Mem$t[O_VALP(args[3])],
+ Mem$t[O_VALP(out)], nelem)
+ $endfor
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+ case 4:
+ switch (optype) {
+ $for (silrd)
+ case TY_PIXEL:
+ call amed4$t (Mem$t[O_VALP(args[1])],
+ Mem$t[O_VALP(args[2])],
+ Mem$t[O_VALP(args[3])],
+ Mem$t[O_VALP(args[4])],
+ Mem$t[O_VALP(out)], nelem)
+ $endfor
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+ case 5:
+ switch (optype) {
+ $for (silrd)
+ case TY_PIXEL:
+ call amed5$t (Mem$t[O_VALP(args[1])],
+ Mem$t[O_VALP(args[2])],
+ Mem$t[O_VALP(args[3])],
+ Mem$t[O_VALP(args[4])],
+ Mem$t[O_VALP(args[5])],
+ Mem$t[O_VALP(out)], nelem)
+ $endfor
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+ default:
+ call xvv_error1 ("%s: wrong number of arguments", fcn)
+ }
+
+ case F_REPL:
+ # Replicate an item to make a longer vector.
+
+ chunk = O_LEN(ap)
+ optype = O_TYPE(ap)
+ if (optype == TY_BOOL)
+ optype = TY_INT
+
+ if (O_LEN(args[2]) > 0)
+ call xvv_error1 ("%s: replication factor must be a scalar", fcn)
+ if (O_TYPE(args[2]) != TY_INT)
+ call xvv_chtype (args[2], args[2], TY_INT)
+ repl = max (1, O_VALI(args[2]))
+
+ if (chunk <= 0)
+ nelem = repl
+ else
+ nelem = chunk * repl
+ call xvv_initop (out, nelem, optype)
+
+ switch (optype) {
+ $for (silrd)
+ case TY_PIXEL:
+ if (chunk > 0) {
+ ip = O_VALP(ap)
+ op = O_VALP(out)
+ do i = 1, repl {
+ call amov$t (Mem$t[ip], Mem$t[op], chunk)
+ op = op + chunk
+ }
+ } else
+ call amovk$t (O_VAL$T(ap), Mem$t[O_VALP(out)], nelem)
+ $endfor
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_SHIFT:
+ # Vector shift.
+ if (O_LEN(args[2]) > 0)
+ call xvv_error1 ("%s: shift arg must be a scalar" , fcn)
+ if (O_TYPE(args[2]) != TY_INT)
+ call xvv_chtype (args[2], args[2], TY_INT)
+ shift = O_VALI(args[2])
+
+ if (abs(shift) > nelem) {
+ if (shift > 0)
+ shift = nelem
+ else
+ shift = -nelem
+ }
+
+ switch (optype) {
+ $for (silrd)
+ case TY_PIXEL:
+ if (nelem > 0) {
+ do i = 1, nelem {
+ j = i - shift
+ if (j < 1)
+ j = j + nelem
+ else if (j > nelem)
+ j = j - nelem
+ Mem$t[O_VALP(out)+i-1] = Mem$t[O_VALP(ap)+j-1]
+ }
+ } else
+ O_VAL$T(out) = (O_VAL$T(ap))
+ $endfor
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_SORT:
+ # Sort a vector.
+ switch (optype) {
+ $for (silrd)
+ case TY_PIXEL:
+ if (nelem > 0)
+ call asrt$t (Mem$t[O_VALP(ap)], Mem$t[O_VALP(out)], nelem)
+ else
+ O_VAL$T(out) = (O_VAL$T(ap))
+ $endfor
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_ATAN, F_ATAN2:
+ $for (rd)
+ if (optype == TY_PIXEL) {
+ if (nargs == 1) {
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Mem$t[O_VALP(out)+i-1] =
+ atan (Mem$t[O_VALP(ap)+i-1])
+ } else
+ O_VAL$T(out) = atan (O_VAL$T(ap))
+ } else {
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Mem$t[O_VALP(out)+i-1] =
+ atan2 (Mem$t[O_VALP(args[1])+i-1],
+ Mem$t[O_VALP(args[2])+i-1])
+ } else
+ O_VAL$T(out) = atan2(O_VAL$T(args[1]), O_VAL$T(args[2]))
+ }
+ }
+ $endfor
+
+ case F_MOD:
+ in1 = args[1]
+ in2 = args[2]
+
+ switch (optype) {
+ $for (silrd)
+ case TY_PIXEL:
+ if (O_LEN(in1) <= 0) {
+ O_VAL$T(out) = mod (O_VAL$T(in1), O_VAL$T(in2))
+ } else if (O_LEN(in2) <= 0) {
+ call amodk$t (Mem$t[O_VALP(in1)], O_VAL$T(in2),
+ Mem$t[O_VALP(out)], nelem)
+ } else {
+ call amod$t (Mem$t[O_VALP(in1)], Mem$t[O_VALP(in2)],
+ Mem$t[O_VALP(out)], nelem)
+ }
+ $endfor
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_MAX:
+ switch (optype) {
+ $for (silrd)
+ case TY_PIXEL:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovk$t (O_VAL$T(ap), Mem$t[O_VALP(out)], nelem)
+ else
+ O_VAL$T(out) = O_VAL$T(ap)
+ } else
+ call amov$t (Mem$t[O_VALP(ap)], Mem$t[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VAL$T(out) = max (O_VAL$T(ap), O_VAL$T(out))
+ else {
+ call amaxk$t (Mem$t[O_VALP(out)], O_VAL$T(ap),
+ Mem$t[O_VALP(out)], nelem)
+ }
+ } else {
+ call amax$t (Mem$t[O_VALP(out)], Mem$t[O_VALP(ap)],
+ Mem$t[O_VALP(out)], nelem)
+ }
+ }
+ $endfor
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_MIN:
+ switch (optype) {
+ $for (silrd)
+ case TY_PIXEL:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovk$t (O_VAL$T(ap), Mem$t[O_VALP(out)], nelem)
+ else
+ O_VAL$T(out) = O_VAL$T(ap)
+ } else
+ call amov$t (Mem$t[O_VALP(ap)], Mem$t[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VAL$T(out) = min (O_VAL$T(ap), O_VAL$T(out))
+ else {
+ call amink$t (Mem$t[O_VALP(out)], O_VAL$T(ap),
+ Mem$t[O_VALP(out)], nelem)
+ }
+ } else {
+ call amin$t (Mem$t[O_VALP(out)], Mem$t[O_VALP(ap)],
+ Mem$t[O_VALP(out)], nelem)
+ }
+ }
+ $endfor
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_BOOL:
+ nelem = 0
+ if (O_LEN(ap) > 0 && O_TYPE(ap) != TY_CHAR)
+ nelem = O_LEN(ap)
+ call xvv_initop (out, nelem, TY_BOOL)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALI(ap)
+ else
+ call amovi (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_CHAR:
+ ch = O_VALC(ap)
+ O_VALI(out) = btoi (ch == 'y' || ch == 'Y')
+
+ $for (silrd)
+ case TY_PIXEL:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = btoi (O_VAL$T(ap) != 0$f)
+ else {
+ v_$t = 0$f
+ call abnek$t (Mem$t[O_VALP(ap)], v_$t, Memi[O_VALP(out)],
+ nelem)
+ }
+ $endfor
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_SHORT:
+ nelem = 0
+ if (O_LEN(ap) > 0 && O_TYPE(ap) != TY_CHAR)
+ nelem = O_LEN(ap)
+ call xvv_initop (out, nelem, TY_SHORT)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_LEN(ap) <= 0)
+ O_VALS(out) = O_VALI(ap)
+ else
+ call achtis (Memi[O_VALP(ap)], Mems[O_VALP(out)], nelem)
+
+ case TY_CHAR:
+ ip = O_VALP(ap)
+ if (gctod (Memc, ip, v_d) <= 0)
+ O_VALS(out) = 0
+ else
+ O_VALS(out) = v_d
+
+ $for (silrd)
+ case TY_PIXEL:
+ if (O_LEN(ap) <= 0)
+ O_VALS(out) = O_VAL$T(ap)
+ else
+ call acht$ts (Mem$t[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+ $endfor
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_INT:
+ nelem = 0
+ if (O_LEN(ap) > 0 && O_TYPE(ap) != TY_CHAR)
+ nelem = O_LEN(ap)
+ call xvv_initop (out, nelem, TY_INT)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALI(ap)
+ else
+ call amovi (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_CHAR:
+ ip = O_VALP(ap)
+ if (gctod (Memc, ip, v_d) <= 0)
+ O_VALI(out) = 0
+ else
+ O_VALI(out) = v_d
+
+ $for (silrd)
+ case TY_PIXEL:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VAL$T(ap)
+ else
+ call acht$ti (Mem$t[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+ $endfor
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_LONG:
+ nelem = 0
+ if (O_LEN(ap) > 0 && O_TYPE(ap) != TY_CHAR)
+ nelem = O_LEN(ap)
+ call xvv_initop (out, nelem, TY_LONG)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_LEN(ap) <= 0)
+ O_VALL(out) = O_VALI(ap)
+ else
+ call amovi (Memi[O_VALP(ap)], Meml[O_VALP(out)], nelem)
+
+ case TY_CHAR:
+ ip = O_VALP(ap)
+ if (gctod (Memc, ip, v_d) <= 0)
+ O_VALL(out) = 0
+ else
+ O_VALL(out) = v_d
+
+ $for (silrd)
+ case TY_PIXEL:
+ if (O_LEN(ap) <= 0)
+ O_VALL(out) = O_VAL$T(ap)
+ else
+ call acht$tl (Mem$t[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+ $endfor
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_NINT:
+ nelem = 0
+ if (O_LEN(ap) > 0 && O_TYPE(ap) != TY_CHAR)
+ nelem = O_LEN(ap)
+ call xvv_initop (out, nelem, TY_INT)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALI(ap)
+ else
+ call amovi (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_CHAR:
+ ip = O_VALP(ap)
+ if (gctod (Memc, ip, v_d) <= 0)
+ O_VALI(out) = 0
+ else
+ O_VALI(out) = nint (v_d)
+
+ $for (sil)
+ case TY_PIXEL:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VAL$T(ap)
+ else
+ call acht$ti (Mem$t[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+ $endfor
+
+ $for (rd)
+ case TY_PIXEL:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = nint (O_VAL$T(ap))
+ else {
+ do i = 1, nelem
+ Memi[O_VALP(out)+i-1] = nint (Mem$t[O_VALP(ap)+i-1])
+ }
+ $endfor
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_REAL:
+ nelem = 0
+ if (O_LEN(ap) > 0 && O_TYPE(ap) != TY_CHAR)
+ nelem = O_LEN(ap)
+ call xvv_initop (out, nelem, TY_REAL)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_LEN(ap) <= 0)
+ O_VALR(out) = O_VALI(ap)
+ else
+ call achtir (Memi[O_VALP(ap)], Memr[O_VALP(out)], nelem)
+
+ case TY_CHAR:
+ ip = O_VALP(ap)
+ if (gctod (Memc, ip, v_d) <= 0)
+ O_VALR(out) = 0
+ else
+ O_VALR(out) = v_d
+
+ $for (silrd)
+ case TY_PIXEL:
+ if (O_LEN(ap) <= 0)
+ O_VALR(out) = O_VAL$T(ap)
+ else
+ call acht$tr (Mem$t[O_VALP(ap)], Memr[O_VALP(out)], nelem)
+ $endfor
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_DOUBLE:
+ nelem = 0
+ if (O_LEN(ap) > 0 && O_TYPE(ap) != TY_CHAR)
+ nelem = O_LEN(ap)
+ call xvv_initop (out, nelem, TY_DOUBLE)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_LEN(ap) <= 0)
+ O_VALD(out) = O_VALI(ap)
+ else
+ call achtid (Memi[O_VALP(ap)], Memd[O_VALP(out)], nelem)
+
+ case TY_CHAR:
+ ip = O_VALP(ap)
+ if (gctod (Memc, ip, v_d) <= 0)
+ O_VALD(out) = 0
+ else
+ O_VALD(out) = v_d
+
+ $for (silrd)
+ case TY_PIXEL:
+ if (O_LEN(ap) <= 0)
+ O_VALD(out) = O_VAL$T(ap)
+ else
+ call acht$td (Mem$t[O_VALP(ap)], Memd[O_VALP(out)], nelem)
+ $endfor
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_STR:
+ optype = TY_CHAR
+ if (O_TYPE(ap) == TY_CHAR)
+ nelem = strlen (O_VALC(ap))
+ else
+ nelem = MAX_DIGITS
+ call xvv_initop (out, nelem, TY_CHAR)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ call sprintf (O_VALC(out), nelem, "%b")
+ call pargi (O_VALI(ap))
+ case TY_CHAR:
+ call sprintf (O_VALC(out), nelem, "%s")
+ call pargstr (O_VALC(ap))
+ $for (sil)
+ case TY_PIXEL:
+ call sprintf (O_VALC(out), nelem, "%d")
+ call parg$t (O_VAL$T(ap))
+ $endfor
+ $for (rd)
+ case TY_PIXEL:
+ call sprintf (O_VALC(out), nelem, "%g")
+ call parg$t (O_VAL$T(ap))
+ $endfor
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ default:
+ call xvv_error ("callfcn: unknown function type")
+ }
+
+free_
+ # Free any storage used by the argument list operands.
+ do i = 1, nargs
+ call xvv_freeop (args[i])
+
+ call sfree (sp)
+end
+
+
+# XVV_STARTARGLIST -- Allocate an argument list descriptor to receive
+# arguments as a function call is parsed. We are called with either
+# zero or one arguments. The argument list descriptor is pointed to by
+# a ficticious operand. The descriptor itself contains a count of the
+# number of arguments, an array of pointers to the operand structures,
+# as well as storage for the operand structures. The operands must be
+# stored locally since the parser will discard its copy of the operand
+# structure for each argument as the associated grammar rule is reduced.
+
+procedure xvv_startarglist (arg, out)
+
+pointer arg #I pointer to first argument, or NULL
+pointer out #I output operand pointing to arg descriptor
+
+pointer ap
+errchk xvv_initop
+
+begin
+ call xvv_initop (out, LEN_ARGSTRUCT, TY_STRUCT)
+ ap = O_VALP(out)
+
+ if (arg == NULL)
+ A_NARGS(ap) = 0
+ else {
+ A_NARGS(ap) = 1
+ A_ARGP(ap,1) = A_OPS(ap)
+ YYMOVE (arg, A_OPS(ap))
+ }
+end
+
+
+# XVV_ADDARG -- Add an argument to the argument list for a function call.
+
+procedure xvv_addarg (arg, arglist, out)
+
+pointer arg #I pointer to argument to be added
+pointer arglist #I pointer to operand pointing to arglist
+pointer out #I output operand pointing to arg descriptor
+
+pointer ap, o
+int nargs
+
+begin
+ ap = O_VALP(arglist)
+
+ nargs = A_NARGS(ap) + 1
+ A_NARGS(ap) = nargs
+ if (nargs > MAX_ARGS)
+ call xvv_error ("too many function arguments")
+
+ o = A_OPS(ap) + ((nargs - 1) * LEN_OPERAND)
+ A_ARGP(ap,nargs) = o
+ YYMOVE (arg, o)
+
+ YYMOVE (arglist, out)
+end
+
+
+# XVV_ERROR1 -- Take an error action, formatting an error message with one
+# format string plus one string argument.
+
+procedure xvv_error1 (fmt, arg)
+
+char fmt[ARB] #I printf format string
+char arg[ARB] #I string argument
+
+pointer sp, buf
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_LINE, TY_CHAR)
+
+ call sprintf (Memc[buf], SZ_LINE, fmt)
+ call pargstr (arg)
+
+ call xvv_error (Memc[buf])
+ call sfree (sp)
+end
+
+
+# XVV_ERROR2 -- Take an error action, formatting an error message with one
+# format string plus one string argument and one integer argument.
+
+procedure xvv_error2 (fmt, arg1, arg2)
+
+char fmt[ARB] #I printf format string
+char arg1[ARB] #I string argument
+int arg2 #I integer argument
+
+pointer sp, buf
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_LINE, TY_CHAR)
+
+ call sprintf (Memc[buf], SZ_LINE, fmt)
+ call pargstr (arg1)
+ call pargi (arg2)
+
+ call xvv_error (Memc[buf])
+ call sfree (sp)
+end
+
+
+# XVV_ERROR -- Take an error action, given an error message string as the
+# sole argument.
+
+procedure xvv_error (errmsg)
+
+char errmsg[ARB] #I error message
+
+begin
+ call error (1, errmsg)
+end
+
+
+# XVV_CHTYPE -- Change the datatype of an operand. The input and output
+# operands may be the same.
+
+procedure xvv_chtype (o1, o2, dtype)
+
+pointer o1 #I input operand
+pointer o2 #I output operand
+int dtype #I new datatype
+
+short v_s
+int v_i
+long v_l
+real v_r
+double v_d
+pointer vp, ip, op
+bool float, freeval
+int old_type, nelem, ch
+
+pointer coerce()
+int sizeof(), btoi(), gctod()
+string s_badtype "chtype: invalid operand type"
+
+begin
+ old_type = O_TYPE(o1)
+ nelem = O_LEN(o1)
+
+ # No type conversion needed?
+ if (old_type == dtype) {
+ if (o1 != o2) {
+ if (nelem <= 0)
+ YYMOVE (o1, o2)
+ else {
+ call xvv_initop (o2, nelem, old_type)
+ call amovc (O_VALC(o1), O_VALC(o2), nelem * sizeof(dtype))
+ }
+ }
+ return
+ }
+
+ if (nelem <= 0) {
+ # Scalar input operand.
+
+ O_TYPE(o2) = dtype
+ O_LEN(o2) = 0
+ float = false
+
+ # Read the old value into a local variable of type long or double.
+ switch (old_type) {
+ case TY_BOOL:
+ v_l = O_VALI(o1)
+ case TY_CHAR:
+ v_l = 0 # null string?
+ $for (sil)
+ case TY_PIXEL:
+ v_l = O_VAL$T(o1)
+ $endfor
+ $for (rd)
+ case TY_PIXEL:
+ v_d = O_VAL$T(o1)
+ float = true
+ $endfor
+ default:
+ call xvv_error (s_badtype)
+ }
+
+ # Set the value of the output operand.
+ switch (dtype) {
+ case TY_BOOL:
+ if (float)
+ O_VALI(o2) = btoi (v_d != 0)
+ else
+ O_VALI(o2) = btoi (v_l != 0)
+ case TY_CHAR:
+ call xvv_initop (o2, MAX_DIGITS, TY_CHAR)
+ if (float) {
+ call sprintf (O_VALC(o2), MAX_DIGITS, "%g")
+ call pargd (v_d)
+ } else {
+ call sprintf (O_VALC(o2), MAX_DIGITS, "%d")
+ call pargl (v_l)
+ }
+ $for (sil)
+ case TY_PIXEL:
+ if (float)
+ O_VAL$T(o2) = v_d
+ else
+ O_VAL$T(o2) = v_l
+ $endfor
+ $for (rd)
+ case TY_PIXEL:
+ if (float)
+ O_VAL$T(o2) = v_d
+ else
+ O_VAL$T(o2) = v_l
+ $endfor
+ default:
+ call xvv_error (s_badtype)
+ }
+
+ } else {
+ # Vector input operand.
+
+ # Save a pointer to the input operand data vector, to avoid it
+ # getting clobbered if O1 and O2 are the same operand.
+
+ vp = O_VALP(o1)
+
+ # If we have a char string input operand the output numeric
+ # operand can only be a scalar. If we have a char string output
+ # operand nelem is the length of the string.
+
+ if (old_type == TY_CHAR)
+ nelem = 0
+ else if (dtype == TY_CHAR)
+ nelem = MAX_DIGITS
+
+ # Initialize the output operand O2. The freeval flag is cleared
+ # cleared to keep the initop from freeing the input operand array,
+ # inherited when the input operand is copied (or when the input
+ # and output operands are the same). We free the old operand
+ # array manually below.
+
+ if (o1 != o2)
+ YYMOVE (o1, o2)
+ freeval = (and (O_FLAGS(o1), O_FREEVAL) != 0)
+ O_FLAGS(o2) = and (O_FLAGS(o2), not(O_FREEVAL))
+ call xvv_initop (o2, nelem, dtype)
+
+ # Write output value.
+ switch (dtype) {
+ case TY_BOOL:
+ if (old_type == TY_CHAR) {
+ ch = Memc[vp]
+ O_VALI(o2) = btoi (ch == 'y' || ch == 'Y')
+ } else {
+ switch (old_type) {
+ $for (silrd)
+ case TY_PIXEL:
+ v_$t = 0$f
+ call abnek$t (Mem$t[vp], v_$t, Memi[O_VALP(o2)], nelem)
+ $endfor
+ default:
+ call xvv_error (s_badtype)
+ }
+ }
+
+ case TY_CHAR:
+ call xvv_error (s_badtype)
+
+ case TY_SHORT, TY_INT, TY_LONG, TY_REAL, TY_DOUBLE:
+ switch (old_type) {
+ case TY_BOOL:
+ op = coerce (O_VALP(o2), O_TYPE(o2), TY_CHAR)
+ call achti (Memi[vp], Memc[op], nelem, dtype)
+ case TY_CHAR:
+ ip = vp
+ if (gctod (Memc, ip, v_d) <= 0)
+ v_d = 0
+ switch (dtype) {
+ $for (silrd)
+ case TY_PIXEL:
+ O_VAL$T(o2) = v_d
+ $endfor
+ }
+ $for (silrd)
+ case TY_PIXEL:
+ op = coerce (O_VALP(o2), O_TYPE(o2), TY_CHAR)
+ call acht$t (Mem$t[vp], Memc[op], nelem, dtype)
+ $endfor
+ default:
+ call xvv_error (s_badtype)
+ }
+ default:
+ call xvv_error (s_badtype)
+ }
+
+ # Free old operand value.
+ if (freeval)
+ call mfree (vp, old_type)
+ }
+end
+
+
+# XVV_INITOP -- Initialize an operand, providing storage for an operand value
+# of the given size and type.
+
+procedure xvv_initop (o, o_len, o_type)
+
+pointer o #I pointer to operand structure
+int o_len #I length of operand (zero if scalar)
+int o_type #I datatype of operand
+
+begin
+ O_LEN(o) = 0
+ call xvv_makeop (o, o_len, o_type)
+end
+
+
+# XVV_MAKEOP -- Set up the operand structure. If the operand structure has
+# already been initialized and array storage allocated, free the old array.
+
+procedure xvv_makeop (o, o_len, o_type)
+
+pointer o #I pointer to operand structure
+int o_len #I length of operand (zero if scalar)
+int o_type #I datatype of operand
+
+errchk malloc
+
+begin
+ # Free old array storage if any.
+ if (O_TYPE(o) != 0 && O_LEN(o) > 0)
+ if (and (O_FLAGS(o), O_FREEVAL) != 0) {
+ if (O_TYPE(o) == TY_BOOL)
+ call mfree (O_VALP(o), TY_INT)
+ else
+ call mfree (O_VALP(o), O_TYPE(o))
+ O_LEN(o) = 0
+ }
+
+ # Set new operand type.
+ O_TYPE(o) = o_type
+
+ # Allocate array storage if nonscalar operand.
+ if (o_len > 0) {
+ if (o_type == TY_BOOL)
+ call malloc (O_VALP(o), o_len, TY_INT)
+ else
+ call malloc (O_VALP(o), o_len, o_type)
+ O_LEN(o) = o_len
+ }
+
+ O_FLAGS(o) = O_FREEVAL
+end
+
+
+# XVV_FREEOP -- Reinitialize an operand structure, i.e., free any associated
+# array storage and clear the operand datatype field, but do not free the
+# operand structure itself (which may be only a segment of an array and not
+# a separately allocated structure).
+
+procedure xvv_freeop (o)
+
+pointer o #I pointer to operand structure
+
+begin
+ # Free old array storage if any.
+ if (O_TYPE(o) != 0 && O_LEN(o) > 0)
+ if (and (O_FLAGS(o), O_FREEVAL) != 0) {
+ if (O_TYPE(o) == TY_BOOL)
+ call mfree (O_VALP(o), TY_INT)
+ else
+ call mfree (O_VALP(o), O_TYPE(o))
+ O_LEN(o) = 0
+ }
+
+ # Either free operand struct or clear the operand type to mark
+ # operand invalid.
+
+ if (and (O_FLAGS(o), O_FREEOP) != 0)
+ call mfree (o, TY_STRUCT)
+ else
+ O_TYPE(o) = 0
+end
+
+
+# XVV_LOADSYMBOLS -- Load a list of symbol names into a symbol table. Each
+# symbol is tagged with an integer code corresponding to its sequence number
+# in the symbol list.
+
+pointer procedure xvv_loadsymbols (s)
+
+char s[ARB] #I symbol list "|sym1|sym2|...|"
+
+int delim, symnum, ip
+pointer sp, symname, st, sym, op
+pointer stopen(), stenter()
+
+begin
+ call smark (sp)
+ call salloc (symname, SZ_FNAME, TY_CHAR)
+
+ st = stopen ("evvexpr", LEN_INDEX, LEN_STAB, LEN_SBUF)
+ delim = s[1]
+ symnum = 0
+
+ for (ip=2; s[ip] != EOS; ip=ip+1) {
+ op = symname
+ while (s[ip] != delim && s[ip] != EOS) {
+ Memc[op] = s[ip]
+ op = op + 1
+ ip = ip + 1
+ }
+ Memc[op] = EOS
+ symnum = symnum + 1
+
+ if (op > symname && IS_ALPHA(Memc[symname])) {
+ sym = stenter (st, Memc[symname], LEN_SYM)
+ SYM_CODE(sym) = symnum
+ }
+ }
+
+ call sfree (sp)
+ return (st)
+end
+
+
+# XVV_NULL -- Return a null value to be used when a computation cannot be
+# performed and range checking is enabled. Perhaps we should permit a user
+# specified value here, however this doesn't really work in an expression
+# evaluator since the value generated may be used in subsequent calculations
+# and hence may change. If more careful treatment of out of range values
+# is needed a conditional expression can be used in which case the value
+# we return here is ignored (but still needed to avoid a hardware exception
+# when computing a vector).
+
+$for (silrd)
+PIXEL procedure xvv_null$t (ignore)
+PIXEL ignore #I ignored
+begin
+ return (0$f)
+end
+$endfor
diff --git a/sys/fmtio/evvexpr.x b/sys/fmtio/evvexpr.x
new file mode 100644
index 00000000..19bc4790
--- /dev/null
+++ b/sys/fmtio/evvexpr.x
@@ -0,0 +1,5050 @@
+
+# line 2 "evvexpr.y"
+include <lexnum.h>
+include <ctype.h>
+include <mach.h>
+include <math.h>
+include <evvexpr.h>
+
+.help evvexpr
+.nf --------------------------------------------------------------------------
+EVVEXPR.GY -- Generic XYacc source for a general vector expression evaluator.
+
+ o = evvexpr (expr, getop, getop_data, ufcn, ufcn_data, flags)
+ evvfree (o)
+
+Client callbacks:
+
+ getop (client_data, opname, out)
+ ufcn (client_data, fcn, args, nargs, out)
+
+here "out" is the output operand returned to EVVEXPR. Client_data is any
+arbitrary integer or pointer value passed in to EVVEXPR when by the client
+when the callback was registered. "args" is an array of operand structs,
+the arguments for the user function being called. If the operand or
+function call cannot be completed normally an error exit may be made (call
+error) or an invalid operand may be returned (O_TYPE set to 0). The client
+should not free the "args" input operands, this will be handled by EVVEXPR.
+
+Operand struct (lib$evvexpr.h):
+
+ struct operand {
+ int O_TYPE # operand type (bcsilrd)
+ int O_LEN # operand length (0=scalar)
+ int O_FLAGS # O_FREEVAL, O_FREEOP
+ union {
+ char* O_VALC # string
+ short O_VALS
+ int O_VALI # int or bool
+ long O_VALL
+ real O_VALR
+ double O_VALD
+ pointer O_VALP # vector data
+ }
+ }
+
+The macro O_VALC references the string value of a TY_CHAR operand. The
+flags are O_FREEVAL and O_FREEOP, which tell EVVEXPR and EVVFREE whether or
+not to free any vector operand array or the operand struct when the operand
+is freed. The client should set these flags on operands returned to EVVEXPR
+if it wants EVVEXPR to free any operand storage.
+
+Supported types are bool, char (string), and SILRD. Bool is indicated as
+TY_BOOL in the O_TYPE field of the operand struct, but is stored internally
+as an integer and the value field of a boolean operand is given by O_VALI.
+
+Operands may be either scalars or vectors. A vector is indicated by a O_LEN
+value greater than zero. For vector operands O_VALP points to the data array.
+A special case is TY_CHAR (string), in which case O_LEN is the allocated
+length of the EOS-terminated string. A string is logically a scalar value
+even though it is physically stored in the operand as a character vector.
+
+The trig functions operate upon angles in units of radians. The intrinsic
+functions RAD and DEG are available for converting between radians and
+degrees. A string can be coerced to a binary value and vice versa, using
+the INT, STR, etc. intrinsic functions.
+
+This is a generalization of the older EVEXPR routine, adding additional
+datatypes, support for vector operands, and numerous minor enhancements.
+.endhelp ---------------------------------------------------------------------
+
+define YYMAXDEPTH 64 # parser stack length
+define MAX_ARGS 17 # max args in a function call
+define yyparse xvv_parse
+
+# Arglist structure.
+define LEN_ARGSTRUCT (1+MAX_ARGS+(MAX_ARGS*LEN_OPERAND))
+define A_NARGS Memi[$1] # number of arguments
+define A_ARGP Memi[$1+$2] # array of pointers to operand structs
+define A_OPS ($1+MAX_ARGS+1) # offset to operand storage area
+
+# Intrinsic functions.
+
+define LEN_STAB 300 # for symbol table
+define LEN_SBUF 256
+define LEN_INDEX 97
+
+define LEN_SYM 1 # symbol data
+define SYM_CODE Memi[$1]
+
+define KEYWORDS "|abs|acos|asin|atan|atan2|bool|cos|cosh|deg|double|\
+ |exp|hiv|int|len|log|log10|long|lov|max|mean|median|\
+ |min|mod|nint|rad|real|repl|stddev|shift|short|sin|\
+ |sinh|sort|sqrt|str|sum|tan|tanh|"
+
+define F_ABS 01 # function codes
+define F_ACOS 02
+define F_ASIN 03
+define F_ATAN 04
+define F_ATAN2 05
+define F_BOOL 06
+define F_COS 07
+define F_COSH 08
+define F_DEG 09 # radians to degrees
+define F_DOUBLE 10
+ # newline 11
+define F_EXP 12
+define F_HIV 13 # high value
+define F_INT 14
+define F_LEN 15 # vector length
+define F_LOG 16
+define F_LOG10 17
+define F_LONG 18
+define F_LOV 19 # low value
+define F_MAX 20
+define F_MEAN 21
+define F_MEDIAN 22
+ # newline 23
+define F_MIN 24
+define F_MOD 25
+define F_NINT 26
+define F_RAD 27 # degrees to radians
+define F_REAL 28
+define F_REPL 29 # replicate
+define F_STDDEV 30 # standard deviation
+define F_SHIFT 31
+define F_SHORT 32
+define F_SIN 33
+ # newline 34
+define F_SINH 35
+define F_SORT 36 # sort
+define F_SQRT 37 # square root
+define F_STR 38
+define F_SUM 39
+define F_TAN 40
+define F_TANH 41
+
+define T_B TY_BOOL
+define T_C TY_CHAR
+define T_S TY_SHORT
+define T_I TY_INT
+define T_L TY_LONG
+define T_R TY_REAL
+define T_D TY_DOUBLE
+
+
+# EVVEXPR -- Evaluate a general mixed type vector expression. Input consists
+# of the expression to be evaluated (a string) and, optionally, user
+# procedures for fetching external operands and executing external functions.
+# Output is a pointer to an operand structure containing the computed value of
+# the expression. The output operand structure is dynamically allocated by
+# EVVEXPR and must be freed by the user.
+#
+# NOTE: this is not intended to be an especially efficient procedure. Rather,
+# this is a high level, easy to use procedure, intended to provide greater
+# flexibility in the parameterization of applications programs. The main
+# inefficiency is that, since compilation and execution are not broken out as
+# separate steps, when the routine is repeatedly called to evaluate the same
+# expression with different data, all the compile time computation (parsing
+# etc.) has to be repeated.
+
+pointer procedure evvexpr (expr, getop, getop_data, ufcn, ufcn_data, flags)
+
+char expr[ARB] #I expression to be evaluated
+int getop #I user supplied get operand procedure
+int getop_data #I client data for above function
+int ufcn #I user supplied function call procedure
+int ufcn_data #I client data for above function
+int flags #I flag bits
+
+int junk
+pointer sp, ip
+bool debug, first_time
+int strlen(), xvv_parse()
+pointer xvv_loadsymbols()
+extern xvv_gettok()
+
+errchk xvv_parse, calloc
+include "evvexpr.com"
+data debug /false/
+data first_time /true/
+
+begin
+ call smark (sp)
+
+ if (first_time) {
+ # This creates data which remains for the life of the process.
+ ev_st = xvv_loadsymbols (KEYWORDS)
+ first_time = false
+ }
+
+ # Set user function entry point addresses.
+ ev_getop = getop
+ ev_getop_data = getop_data
+ ev_ufcn = ufcn
+ ev_ufcn_data = ufcn_data
+ ev_flags = flags
+
+ # Allocate an operand struct for the expression value.
+ call calloc (ev_oval, LEN_OPERAND, TY_STRUCT)
+
+ # Make a local copy of the input string.
+ call salloc (ip, strlen(expr), TY_CHAR)
+ call strcpy (expr, Memc[ip], ARB)
+
+ # Evaluate the expression. The expression value is copied into the
+ # output operand structure by XVV_PARSE, given the operand pointer
+ # passed in common. A common must be used since the standard parser
+ # subroutine has a fixed calling sequence.
+
+ junk = xvv_parse (ip, debug, xvv_gettok)
+ O_FLAGS(ev_oval) = or (O_FLAGS(ev_oval), O_FREEOP)
+
+ call sfree (sp)
+ return (ev_oval)
+end
+
+
+# EVVFREE -- Free an operand struct such as is returned by EVVEXPR.
+
+procedure evvfree (o)
+
+pointer o # operand struct
+
+begin
+ call xvv_freeop (o)
+end
+
+define CONSTANT 257
+define IDENTIFIER 258
+define NEWLINE 259
+define YYEOS 260
+define PLUS 261
+define MINUS 262
+define STAR 263
+define SLASH 264
+define EXPON 265
+define CONCAT 266
+define QUEST 267
+define COLON 268
+define LT 269
+define GT 270
+define LE 271
+define EQ 272
+define NE 273
+define SE 274
+define LAND 275
+define LOR 276
+define LNOT 277
+define BAND 278
+define BOR 279
+define BXOR 280
+define BNOT 281
+define AT 282
+define GE 283
+define UMINUS 284
+define yyclearin yychar = -1
+define yyerrok yyerrflag = 0
+define YYMOVE call amovi (Memi[$1], Memi[$2], YYOPLEN)
+define YYERRCODE 256
+
+
+# End generic preprocessor escape.
+
+
+
+# XVV_UNOP -- Unary operation. Perform the indicated unary operation on the
+# input operand, returning the result as the output operand.
+
+procedure xvv_unop (opcode, in, out)
+
+int opcode #I operation to be performed
+pointer in #I input operand
+pointer out #I output operand
+
+short val_s
+long val_l
+int val_i, nelem
+errchk xvv_error, xvv_initop
+string s_badswitch "unop: bad switch"
+
+begin
+ nelem = O_LEN(in)
+
+ switch (opcode) {
+ case MINUS:
+ # Unary negation.
+ call xvv_initop (out, nelem, O_TYPE(in))
+ switch (O_TYPE(in)) {
+ case TY_BOOL, TY_CHAR:
+ call xvv_error ("negation of a nonarithmetic operand")
+
+ case TY_SHORT:
+ if (nelem > 0)
+ call anegs (Mems[O_VALP(in)], Mems[O_VALP(out)], nelem)
+ else
+ O_VALS(out) = -O_VALS(in)
+
+ case TY_INT:
+ if (nelem > 0)
+ call anegi (Memi[O_VALP(in)], Memi[O_VALP(out)], nelem)
+ else
+ O_VALI(out) = -O_VALI(in)
+
+ case TY_LONG:
+ if (nelem > 0)
+ call anegl (Meml[O_VALP(in)], Meml[O_VALP(out)], nelem)
+ else
+ O_VALL(out) = -O_VALL(in)
+
+ case TY_REAL:
+ if (nelem > 0)
+ call anegr (Memr[O_VALP(in)], Memr[O_VALP(out)], nelem)
+ else
+ O_VALR(out) = -O_VALR(in)
+
+ case TY_DOUBLE:
+ if (nelem > 0)
+ call anegd (Memd[O_VALP(in)], Memd[O_VALP(out)], nelem)
+ else
+ O_VALD(out) = -O_VALD(in)
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ case LNOT:
+ # Logical NOT.
+
+ call xvv_initop (out, nelem, TY_BOOL)
+ switch (O_TYPE(in)) {
+ case TY_BOOL:
+ if (nelem > 0)
+ call abeqki (Memi[O_VALP(in)], NO, Memi[O_VALP(out)], nelem)
+ else {
+ if (O_VALI(in) == NO)
+ O_VALI(out) = YES
+ else
+ O_VALI(out) = NO
+ }
+
+ case TY_SHORT:
+ if (nelem > 0) {
+ val_s = NO
+ call abeqks (Mems[O_VALP(in)], val_s, Memi[O_VALP(out)],
+ nelem)
+ } else {
+ if (O_VALS(in) == NO)
+ O_VALS(out) = YES
+ else
+ O_VALS(out) = NO
+ }
+
+ case TY_INT:
+ if (nelem > 0) {
+ val_i = NO
+ call abeqki (Memi[O_VALP(in)], val_i, Memi[O_VALP(out)],
+ nelem)
+ } else {
+ if (O_VALI(in) == NO)
+ O_VALI(out) = YES
+ else
+ O_VALI(out) = NO
+ }
+
+ case TY_LONG:
+ if (nelem > 0) {
+ val_l = NO
+ call abeqkl (Meml[O_VALP(in)], val_l, Memi[O_VALP(out)],
+ nelem)
+ } else {
+ if (O_VALL(in) == NO)
+ O_VALL(out) = YES
+ else
+ O_VALL(out) = NO
+ }
+
+ case TY_CHAR, TY_REAL, TY_DOUBLE:
+ call xvv_error ("not of a nonlogical")
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ case BNOT:
+ # Bitwise boolean NOT.
+
+ call xvv_initop (out, nelem, O_TYPE(in))
+ switch (O_TYPE(in)) {
+ case TY_BOOL, TY_CHAR, TY_REAL, TY_DOUBLE:
+ call xvv_error ("boolean not of a noninteger operand")
+
+ case TY_SHORT:
+ if (nelem > 0)
+ call anots (Mems[O_VALP(in)], Mems[O_VALP(out)], nelem)
+ else
+ O_VALS(out) = not(O_VALS(in))
+
+ case TY_INT:
+ if (nelem > 0)
+ call anoti (Memi[O_VALP(in)], Memi[O_VALP(out)], nelem)
+ else
+ O_VALI(out) = not(O_VALI(in))
+
+ case TY_LONG:
+ if (nelem > 0)
+ call anotl (Meml[O_VALP(in)], Meml[O_VALP(out)], nelem)
+ else
+ O_VALL(out) = not(O_VALL(in))
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ call xvv_freeop (in)
+end
+
+
+# XVV_BINOP -- Binary operation. Perform the indicated arithmetic binary
+# operation on the two input operands, returning the result as the output
+# operand.
+
+procedure xvv_binop (opcode, in1, in2, out)
+
+int opcode #I operation to be performed
+pointer in1, in2 #I input operands
+pointer out #I output operand
+
+
+short v_s
+short xvv_nulls()
+extern xvv_nulls()
+
+int v_i
+int xvv_nulli()
+extern xvv_nulli()
+
+long v_l
+long xvv_nulll()
+extern xvv_nulll()
+
+real v_r
+real xvv_nullr()
+extern xvv_nullr()
+
+double v_d
+double xvv_nulld()
+extern xvv_nulld()
+
+pointer sp, otemp, p1, p2, po
+int dtype, nelem, len1, len2
+include "evvexpr.com"
+
+int xvv_newtype(), strlen()
+errchk xvv_newtype, xvv_initop, xvv_chtype, xvv_error
+string s_badswitch "binop: bad case in switch"
+string s_boolop "binop: bitwise boolean operands must be an integer type"
+define done_ 91
+
+begin
+ # Set the datatype of the output operand, taking an error action if
+ # the operands have incompatible datatypes.
+
+ dtype = xvv_newtype (O_TYPE(in1), O_TYPE(in2))
+
+ # Compute the size of the output operand. If both input operands are
+ # vectors the length of the output vector is the shorter of the two.
+
+ switch (dtype) {
+ case TY_BOOL:
+ call xvv_error ("binop: operation illegal for boolean operands")
+ case TY_CHAR:
+ nelem = strlen (O_VALC(in1)) + strlen (O_VALC(in2))
+ default:
+ if (opcode == CONCAT)
+ nelem = max (1, O_LEN(in1)) + max (1, O_LEN(in2))
+ else {
+ if (O_LEN(in1) > 0 && O_LEN(in2) > 0)
+ nelem = min (O_LEN(in1), O_LEN(in2))
+ else if (O_LEN(in1) > 0)
+ nelem = O_LEN(in1)
+ else if (O_LEN(in2) > 0)
+ nelem = O_LEN(in2)
+ else
+ nelem = 0
+ }
+ }
+
+ # Convert input operands to desired type.
+ if (O_TYPE(in1) != dtype)
+ call xvv_chtype (in1, in1, dtype)
+ if (O_TYPE(in2) != dtype)
+ call xvv_chtype (in2, in2, dtype)
+
+ # If this is a scalar/vector operation make sure the vector is the
+ # first operand.
+
+ len1 = O_LEN(in1)
+ len2 = O_LEN(in2)
+
+ if (len1 == 0 && len2 > 0) {
+ switch (opcode) {
+ case PLUS:
+ # Swap operands.
+ call smark (sp)
+ call salloc (otemp, LEN_OPERAND, TY_STRUCT)
+ YYMOVE (in1, otemp)
+ YYMOVE (in2, in1)
+ YYMOVE (otemp, in2)
+ call sfree (sp)
+
+ case CONCAT:
+ ; # Do nothing
+
+ default:
+ # Promote operand to a constant vector. Inefficient, but
+ # better than aborting.
+
+ switch (dtype) {
+
+ case TY_SHORT:
+ v_s = O_VALS(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovks (v_s, Mems[O_VALP(in1)], nelem)
+
+ case TY_INT:
+ v_i = O_VALI(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovki (v_i, Memi[O_VALP(in1)], nelem)
+
+ case TY_LONG:
+ v_l = O_VALL(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovkl (v_l, Meml[O_VALP(in1)], nelem)
+
+ case TY_REAL:
+ v_r = O_VALR(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovkr (v_r, Memr[O_VALP(in1)], nelem)
+
+ case TY_DOUBLE:
+ v_d = O_VALD(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovkd (v_d, Memd[O_VALP(in1)], nelem)
+
+ }
+ }
+
+ len1 = O_LEN(in1)
+ len2 = O_LEN(in2)
+ }
+
+ # Initialize the output operand.
+ call xvv_initop (out, nelem, dtype)
+
+ p1 = O_VALP(in1)
+ p2 = O_VALP(in2)
+ po = O_VALP(out)
+
+ # The bitwise boolean binary operators a special case since only the
+ # integer datatypes are permitted. Otherwise the bitwise booleans
+ # are just like arithmetic booleans.
+
+ if (opcode == BAND || opcode == BOR || opcode == BXOR) {
+ switch (dtype) {
+
+ case TY_SHORT:
+ switch (opcode) {
+ case BAND:
+ if (len1 <= 0) {
+ O_VALS(out) = and (O_VALS(in1), O_VALS(in2))
+ } else if (len2 <= 0) {
+ call aandks (Mems[p1], O_VALS(in2),
+ Mems[po], nelem)
+ } else {
+ call aands (Mems[p1], Mems[p2],
+ Mems[po], nelem)
+ }
+ case BOR:
+ if (len1 <= 0) {
+ O_VALS(out) = or (O_VALS(in1), O_VALS(in2))
+ } else if (len2 <= 0) {
+ call aborks (Mems[p1], O_VALS(in2),
+ Mems[po], nelem)
+ } else {
+ call abors (Mems[p1], Mems[p2],
+ Mems[po], nelem)
+ }
+ case BXOR:
+ if (len1 <= 0) {
+ O_VALS(out) = xor (O_VALS(in1), O_VALS(in2))
+ } else if (len2 <= 0) {
+ call axorks (Mems[p1], O_VALS(in2),
+ Mems[po], nelem)
+ } else {
+ call axors (Mems[p1], Mems[p2],
+ Mems[po], nelem)
+ }
+ }
+
+ case TY_INT:
+ switch (opcode) {
+ case BAND:
+ if (len1 <= 0) {
+ O_VALI(out) = and (O_VALI(in1), O_VALI(in2))
+ } else if (len2 <= 0) {
+ call aandki (Memi[p1], O_VALI(in2),
+ Memi[po], nelem)
+ } else {
+ call aandi (Memi[p1], Memi[p2],
+ Memi[po], nelem)
+ }
+ case BOR:
+ if (len1 <= 0) {
+ O_VALI(out) = or (O_VALI(in1), O_VALI(in2))
+ } else if (len2 <= 0) {
+ call aborki (Memi[p1], O_VALI(in2),
+ Memi[po], nelem)
+ } else {
+ call abori (Memi[p1], Memi[p2],
+ Memi[po], nelem)
+ }
+ case BXOR:
+ if (len1 <= 0) {
+ O_VALI(out) = xor (O_VALI(in1), O_VALI(in2))
+ } else if (len2 <= 0) {
+ call axorki (Memi[p1], O_VALI(in2),
+ Memi[po], nelem)
+ } else {
+ call axori (Memi[p1], Memi[p2],
+ Memi[po], nelem)
+ }
+ }
+
+ case TY_LONG:
+ switch (opcode) {
+ case BAND:
+ if (len1 <= 0) {
+ O_VALL(out) = and (O_VALL(in1), O_VALL(in2))
+ } else if (len2 <= 0) {
+ call aandkl (Meml[p1], O_VALL(in2),
+ Meml[po], nelem)
+ } else {
+ call aandl (Meml[p1], Meml[p2],
+ Meml[po], nelem)
+ }
+ case BOR:
+ if (len1 <= 0) {
+ O_VALL(out) = or (O_VALL(in1), O_VALL(in2))
+ } else if (len2 <= 0) {
+ call aborkl (Meml[p1], O_VALL(in2),
+ Meml[po], nelem)
+ } else {
+ call aborl (Meml[p1], Meml[p2],
+ Meml[po], nelem)
+ }
+ case BXOR:
+ if (len1 <= 0) {
+ O_VALL(out) = xor (O_VALL(in1), O_VALL(in2))
+ } else if (len2 <= 0) {
+ call axorkl (Meml[p1], O_VALL(in2),
+ Meml[po], nelem)
+ } else {
+ call axorl (Meml[p1], Meml[p2],
+ Meml[po], nelem)
+ }
+ }
+
+ default:
+ call xvv_error (s_boolop)
+ }
+
+ goto done_
+ }
+
+ # Perform an arithmetic binary operation.
+ switch (dtype) {
+ case TY_CHAR:
+ switch (opcode) {
+ case CONCAT:
+ call strcpy (O_VALC(in1), O_VALC(out), ARB)
+ call strcat (O_VALC(in2), O_VALC(out), ARB)
+ default:
+ call xvv_error ("binop: operation illegal for string operands")
+ }
+
+ case TY_SHORT:
+ switch (opcode) {
+ case PLUS:
+ if (len1 <= 0) {
+ O_VALS(out) = O_VALS(in1) + O_VALS(in2)
+ } else if (len2 <= 0) {
+ call aaddks (Mems[p1], O_VALS(in2),
+ Mems[po], nelem)
+ } else {
+ call aadds (Mems[p1], Mems[p2],
+ Mems[po], nelem)
+ }
+ case MINUS:
+ if (len1 <= 0)
+ O_VALS(out) = O_VALS(in1) - O_VALS(in2)
+ else if (len2 <= 0)
+ call asubks (Mems[p1], O_VALS(in2), Mems[po], nelem)
+ else
+ call asubs (Mems[p1], Mems[p2], Mems[po], nelem)
+
+ case STAR:
+ if (len1 <= 0)
+ O_VALS(out) = O_VALS(in1) * O_VALS(in2)
+ else if (len2 <= 0)
+ call amulks (Mems[p1], O_VALS(in2), Mems[po], nelem)
+ else
+ call amuls (Mems[p1], Mems[p2], Mems[po], nelem)
+
+ case SLASH:
+ if (and (ev_flags, EV_RNGCHK) == 0) {
+ # No range checking.
+ if (len1 <= 0)
+ O_VALS(out) = O_VALS(in1) / O_VALS(in2)
+ else if (len2 <= 0)
+ call adivks (Mems[p1], O_VALS(in2), Mems[po], nelem)
+ else
+ call adivs (Mems[p1], Mems[p2], Mems[po], nelem)
+ } else {
+ # Check for divide by zero.
+ if (len1 <= 0) {
+ if (O_VALS(in2) == 0)
+ O_VALS(out) = xvv_nulls(0)
+ else
+ O_VALS(out) = O_VALS(in1) / O_VALS(in2)
+ } else if (len2 <= 0) {
+ if (O_VALS(in2) == 0)
+ call amovks (xvv_nulls(0), Mems[po], nelem)
+ else {
+ call adivks (Mems[p1], O_VALS(in2), Mems[po],
+ nelem)
+ }
+ } else {
+ call advzs (Mems[p1], Mems[p2], Mems[po], nelem,
+ xvv_nulls)
+ }
+ }
+ case EXPON:
+ if (len1 <= 0)
+ O_VALS(out) = O_VALS(in1) ** O_VALS(in2)
+ else if (len2 <= 0)
+ call aexpks (Mems[p1], O_VALS(in2), Mems[po], nelem)
+ else
+ call aexps (Mems[p1], Mems[p2], Mems[po], nelem)
+
+ case CONCAT:
+ # Concatenate two numeric operands.
+ if (len1 <= 0) {
+ Mems[po] = O_VALS(in1)
+ po = po + 1
+ } else {
+ call amovs (Mems[p1], Mems[po], len1)
+ po = po + len1
+ }
+ if (len2 <= 0)
+ Mems[po] = O_VALS(in2)
+ else
+ call amovs (Mems[p2], Mems[po], len2)
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ case TY_INT:
+ switch (opcode) {
+ case PLUS:
+ if (len1 <= 0) {
+ O_VALI(out) = O_VALI(in1) + O_VALI(in2)
+ } else if (len2 <= 0) {
+ call aaddki (Memi[p1], O_VALI(in2),
+ Memi[po], nelem)
+ } else {
+ call aaddi (Memi[p1], Memi[p2],
+ Memi[po], nelem)
+ }
+ case MINUS:
+ if (len1 <= 0)
+ O_VALI(out) = O_VALI(in1) - O_VALI(in2)
+ else if (len2 <= 0)
+ call asubki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ else
+ call asubi (Memi[p1], Memi[p2], Memi[po], nelem)
+
+ case STAR:
+ if (len1 <= 0)
+ O_VALI(out) = O_VALI(in1) * O_VALI(in2)
+ else if (len2 <= 0)
+ call amulki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ else
+ call amuli (Memi[p1], Memi[p2], Memi[po], nelem)
+
+ case SLASH:
+ if (and (ev_flags, EV_RNGCHK) == 0) {
+ # No range checking.
+ if (len1 <= 0)
+ O_VALI(out) = O_VALI(in1) / O_VALI(in2)
+ else if (len2 <= 0)
+ call adivki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ else
+ call adivi (Memi[p1], Memi[p2], Memi[po], nelem)
+ } else {
+ # Check for divide by zero.
+ if (len1 <= 0) {
+ if (O_VALI(in2) == 0)
+ O_VALI(out) = xvv_nulli(0)
+ else
+ O_VALI(out) = O_VALI(in1) / O_VALI(in2)
+ } else if (len2 <= 0) {
+ if (O_VALI(in2) == 0)
+ call amovki (xvv_nulli(0), Memi[po], nelem)
+ else {
+ call adivki (Memi[p1], O_VALI(in2), Memi[po],
+ nelem)
+ }
+ } else {
+ call advzi (Memi[p1], Memi[p2], Memi[po], nelem,
+ xvv_nulli)
+ }
+ }
+ case EXPON:
+ if (len1 <= 0)
+ O_VALI(out) = O_VALI(in1) ** O_VALI(in2)
+ else if (len2 <= 0)
+ call aexpki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ else
+ call aexpi (Memi[p1], Memi[p2], Memi[po], nelem)
+
+ case CONCAT:
+ # Concatenate two numeric operands.
+ if (len1 <= 0) {
+ Memi[po] = O_VALI(in1)
+ po = po + 1
+ } else {
+ call amovi (Memi[p1], Memi[po], len1)
+ po = po + len1
+ }
+ if (len2 <= 0)
+ Memi[po] = O_VALI(in2)
+ else
+ call amovi (Memi[p2], Memi[po], len2)
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ case TY_LONG:
+ switch (opcode) {
+ case PLUS:
+ if (len1 <= 0) {
+ O_VALL(out) = O_VALL(in1) + O_VALL(in2)
+ } else if (len2 <= 0) {
+ call aaddkl (Meml[p1], O_VALL(in2),
+ Meml[po], nelem)
+ } else {
+ call aaddl (Meml[p1], Meml[p2],
+ Meml[po], nelem)
+ }
+ case MINUS:
+ if (len1 <= 0)
+ O_VALL(out) = O_VALL(in1) - O_VALL(in2)
+ else if (len2 <= 0)
+ call asubkl (Meml[p1], O_VALL(in2), Meml[po], nelem)
+ else
+ call asubl (Meml[p1], Meml[p2], Meml[po], nelem)
+
+ case STAR:
+ if (len1 <= 0)
+ O_VALL(out) = O_VALL(in1) * O_VALL(in2)
+ else if (len2 <= 0)
+ call amulkl (Meml[p1], O_VALL(in2), Meml[po], nelem)
+ else
+ call amull (Meml[p1], Meml[p2], Meml[po], nelem)
+
+ case SLASH:
+ if (and (ev_flags, EV_RNGCHK) == 0) {
+ # No range checking.
+ if (len1 <= 0)
+ O_VALL(out) = O_VALL(in1) / O_VALL(in2)
+ else if (len2 <= 0)
+ call adivkl (Meml[p1], O_VALL(in2), Meml[po], nelem)
+ else
+ call adivl (Meml[p1], Meml[p2], Meml[po], nelem)
+ } else {
+ # Check for divide by zero.
+ if (len1 <= 0) {
+ if (O_VALL(in2) == 0)
+ O_VALL(out) = xvv_nulll(0)
+ else
+ O_VALL(out) = O_VALL(in1) / O_VALL(in2)
+ } else if (len2 <= 0) {
+ if (O_VALL(in2) == 0)
+ call amovkl (xvv_nulll(0), Meml[po], nelem)
+ else {
+ call adivkl (Meml[p1], O_VALL(in2), Meml[po],
+ nelem)
+ }
+ } else {
+ call advzl (Meml[p1], Meml[p2], Meml[po], nelem,
+ xvv_nulll)
+ }
+ }
+ case EXPON:
+ if (len1 <= 0)
+ O_VALL(out) = O_VALL(in1) ** O_VALL(in2)
+ else if (len2 <= 0)
+ call aexpkl (Meml[p1], O_VALL(in2), Meml[po], nelem)
+ else
+ call aexpl (Meml[p1], Meml[p2], Meml[po], nelem)
+
+ case CONCAT:
+ # Concatenate two numeric operands.
+ if (len1 <= 0) {
+ Meml[po] = O_VALL(in1)
+ po = po + 1
+ } else {
+ call amovl (Meml[p1], Meml[po], len1)
+ po = po + len1
+ }
+ if (len2 <= 0)
+ Meml[po] = O_VALL(in2)
+ else
+ call amovl (Meml[p2], Meml[po], len2)
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ case TY_REAL:
+ switch (opcode) {
+ case PLUS:
+ if (len1 <= 0) {
+ O_VALR(out) = O_VALR(in1) + O_VALR(in2)
+ } else if (len2 <= 0) {
+ call aaddkr (Memr[p1], O_VALR(in2),
+ Memr[po], nelem)
+ } else {
+ call aaddr (Memr[p1], Memr[p2],
+ Memr[po], nelem)
+ }
+ case MINUS:
+ if (len1 <= 0)
+ O_VALR(out) = O_VALR(in1) - O_VALR(in2)
+ else if (len2 <= 0)
+ call asubkr (Memr[p1], O_VALR(in2), Memr[po], nelem)
+ else
+ call asubr (Memr[p1], Memr[p2], Memr[po], nelem)
+
+ case STAR:
+ if (len1 <= 0)
+ O_VALR(out) = O_VALR(in1) * O_VALR(in2)
+ else if (len2 <= 0)
+ call amulkr (Memr[p1], O_VALR(in2), Memr[po], nelem)
+ else
+ call amulr (Memr[p1], Memr[p2], Memr[po], nelem)
+
+ case SLASH:
+ if (and (ev_flags, EV_RNGCHK) == 0) {
+ # No range checking.
+ if (len1 <= 0)
+ O_VALR(out) = O_VALR(in1) / O_VALR(in2)
+ else if (len2 <= 0)
+ call adivkr (Memr[p1], O_VALR(in2), Memr[po], nelem)
+ else
+ call adivr (Memr[p1], Memr[p2], Memr[po], nelem)
+ } else {
+ # Check for divide by zero.
+ if (len1 <= 0) {
+ if (O_VALR(in2) == 0.0)
+ O_VALR(out) = xvv_nullr(0.0)
+ else
+ O_VALR(out) = O_VALR(in1) / O_VALR(in2)
+ } else if (len2 <= 0) {
+ if (O_VALR(in2) == 0.0)
+ call amovkr (xvv_nullr(0.0), Memr[po], nelem)
+ else {
+ call adivkr (Memr[p1], O_VALR(in2), Memr[po],
+ nelem)
+ }
+ } else {
+ call advzr (Memr[p1], Memr[p2], Memr[po], nelem,
+ xvv_nullr)
+ }
+ }
+ case EXPON:
+ if (len1 <= 0)
+ O_VALR(out) = O_VALR(in1) ** O_VALR(in2)
+ else if (len2 <= 0)
+ call aexpkr (Memr[p1], O_VALR(in2), Memr[po], nelem)
+ else
+ call aexpr (Memr[p1], Memr[p2], Memr[po], nelem)
+
+ case CONCAT:
+ # Concatenate two numeric operands.
+ if (len1 <= 0) {
+ Memr[po] = O_VALR(in1)
+ po = po + 1
+ } else {
+ call amovr (Memr[p1], Memr[po], len1)
+ po = po + len1
+ }
+ if (len2 <= 0)
+ Memr[po] = O_VALR(in2)
+ else
+ call amovr (Memr[p2], Memr[po], len2)
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ case TY_DOUBLE:
+ switch (opcode) {
+ case PLUS:
+ if (len1 <= 0) {
+ O_VALD(out) = O_VALD(in1) + O_VALD(in2)
+ } else if (len2 <= 0) {
+ call aaddkd (Memd[p1], O_VALD(in2),
+ Memd[po], nelem)
+ } else {
+ call aaddd (Memd[p1], Memd[p2],
+ Memd[po], nelem)
+ }
+ case MINUS:
+ if (len1 <= 0)
+ O_VALD(out) = O_VALD(in1) - O_VALD(in2)
+ else if (len2 <= 0)
+ call asubkd (Memd[p1], O_VALD(in2), Memd[po], nelem)
+ else
+ call asubd (Memd[p1], Memd[p2], Memd[po], nelem)
+
+ case STAR:
+ if (len1 <= 0)
+ O_VALD(out) = O_VALD(in1) * O_VALD(in2)
+ else if (len2 <= 0)
+ call amulkd (Memd[p1], O_VALD(in2), Memd[po], nelem)
+ else
+ call amuld (Memd[p1], Memd[p2], Memd[po], nelem)
+
+ case SLASH:
+ if (and (ev_flags, EV_RNGCHK) == 0) {
+ # No range checking.
+ if (len1 <= 0)
+ O_VALD(out) = O_VALD(in1) / O_VALD(in2)
+ else if (len2 <= 0)
+ call adivkd (Memd[p1], O_VALD(in2), Memd[po], nelem)
+ else
+ call adivd (Memd[p1], Memd[p2], Memd[po], nelem)
+ } else {
+ # Check for divide by zero.
+ if (len1 <= 0) {
+ if (O_VALD(in2) == 0.0D0)
+ O_VALD(out) = xvv_nulld(0.0D0)
+ else
+ O_VALD(out) = O_VALD(in1) / O_VALD(in2)
+ } else if (len2 <= 0) {
+ if (O_VALD(in2) == 0.0D0)
+ call amovkd (xvv_nulld(0.0D0), Memd[po], nelem)
+ else {
+ call adivkd (Memd[p1], O_VALD(in2), Memd[po],
+ nelem)
+ }
+ } else {
+ call advzd (Memd[p1], Memd[p2], Memd[po], nelem,
+ xvv_nulld)
+ }
+ }
+ case EXPON:
+ if (len1 <= 0)
+ O_VALD(out) = O_VALD(in1) ** O_VALD(in2)
+ else if (len2 <= 0)
+ call aexpkd (Memd[p1], O_VALD(in2), Memd[po], nelem)
+ else
+ call aexpd (Memd[p1], Memd[p2], Memd[po], nelem)
+
+ case CONCAT:
+ # Concatenate two numeric operands.
+ if (len1 <= 0) {
+ Memd[po] = O_VALD(in1)
+ po = po + 1
+ } else {
+ call amovd (Memd[p1], Memd[po], len1)
+ po = po + len1
+ }
+ if (len2 <= 0)
+ Memd[po] = O_VALD(in2)
+ else
+ call amovd (Memd[p2], Memd[po], len2)
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+done_
+ # Free any storage in input operands.
+ call xvv_freeop (in1)
+ call xvv_freeop (in2)
+end
+
+
+# XVV_BOOLOP -- Boolean (actually logical) binary operations. Perform the
+# indicated logical operation on the two input operands, returning the result
+# as the output operand. The opcodes implemented by this routine are
+# characterized by the fact that they all return a logical result (YES or NO
+# physically expressed as an integer).
+
+procedure xvv_boolop (opcode, in1, in2, out)
+
+int opcode #I operation to be performed
+pointer in1, in2 #I input operands
+pointer out #I output operand
+
+
+short v_s
+
+int v_i
+
+long v_l
+
+real v_r
+
+double v_d
+
+pointer sp, otemp, p1, p2, po
+int dtype, nelem, len1, len2
+int xvv_newtype(), xvv_patmatch(), strncmp(), btoi()
+errchk xvv_newtype, xvv_initop, xvv_chtype, xvv_error
+string s_badop "boolop: illegal operation"
+string s_badswitch "boolop: illegal switch"
+
+begin
+ # Boolean operands are treated as integer within this routine.
+ if (O_TYPE(in1) == TY_BOOL)
+ O_TYPE(in1) = TY_INT
+ if (O_TYPE(in2) == TY_BOOL)
+ O_TYPE(in2) = TY_INT
+
+ # Determine the computation type for the operation, i.e., the type
+ # both input operands must have. This is not the same as the type
+ # of the output operand, which is always boolean for the operations
+ # implemented by this routine.
+
+ dtype = xvv_newtype (O_TYPE(in1), O_TYPE(in2))
+
+ # Compute the size of the output operand. If both input operands are
+ # vectors the length of the output vector is the shorter of the two.
+
+ if (dtype == TY_CHAR)
+ nelem = 0
+ else {
+ if (O_LEN(in1) > 0 && O_LEN(in2) > 0)
+ nelem = min (O_LEN(in1), O_LEN(in2))
+ else if (O_LEN(in1) > 0)
+ nelem = O_LEN(in1)
+ else if (O_LEN(in2) > 0)
+ nelem = O_LEN(in2)
+ else
+ nelem = 0
+ }
+
+ # Convert input operands to desired computation type.
+ if (O_TYPE(in1) != dtype)
+ call xvv_chtype (in1, in1, dtype)
+ if (O_TYPE(in2) != dtype)
+ call xvv_chtype (in2, in2, dtype)
+
+ # If this is a scalar/vector operation make sure the vector is the
+ # first operand.
+
+ len1 = O_LEN(in1)
+ len2 = O_LEN(in2)
+
+ if (len1 == 0 && len2 > 0) {
+ switch (opcode) {
+ case EQ, NE:
+ call smark (sp)
+ call salloc (otemp, LEN_OPERAND, TY_STRUCT)
+ YYMOVE (in1, otemp)
+ YYMOVE (in2, in1)
+ YYMOVE (otemp, in2)
+ call sfree (sp)
+ default:
+ # Promote operand to a constant vector. Inefficient, but
+ # better than aborting.
+
+ switch (dtype) {
+
+ case TY_SHORT:
+ v_s = O_VALS(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovks (v_s, Mems[O_VALP(in1)], nelem)
+
+ case TY_INT:
+ v_i = O_VALI(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovki (v_i, Memi[O_VALP(in1)], nelem)
+
+ case TY_LONG:
+ v_l = O_VALL(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovkl (v_l, Meml[O_VALP(in1)], nelem)
+
+ case TY_REAL:
+ v_r = O_VALR(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovkr (v_r, Memr[O_VALP(in1)], nelem)
+
+ case TY_DOUBLE:
+ v_d = O_VALD(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovkd (v_d, Memd[O_VALP(in1)], nelem)
+
+ }
+ }
+
+ len1 = O_LEN(in1)
+ len2 = O_LEN(in2)
+ }
+
+ # Initialize the output operand.
+ call xvv_initop (out, nelem, TY_BOOL)
+
+ p1 = O_VALP(in1)
+ p2 = O_VALP(in2)
+ po = O_VALP(out)
+
+ # Perform the operation.
+ if (dtype == TY_CHAR) {
+ # Character data is a special case.
+
+ switch (opcode) {
+ case SE:
+ O_VALI(out) = btoi(xvv_patmatch (O_VALC(in1), O_VALC(in2)) > 0)
+ case LT:
+ O_VALI(out) = btoi(strncmp (O_VALC(in1), O_VALC(in2), ARB) < 0)
+ case LE:
+ O_VALI(out) = btoi(strncmp (O_VALC(in1), O_VALC(in2), ARB) <= 0)
+ case GT:
+ O_VALI(out) = btoi(strncmp (O_VALC(in1), O_VALC(in2), ARB) > 0)
+ case GE:
+ O_VALI(out) = btoi(strncmp (O_VALC(in1), O_VALC(in2), ARB) >= 0)
+ case EQ:
+ O_VALI(out) = btoi(strncmp (O_VALC(in1), O_VALC(in2), ARB) == 0)
+ case NE:
+ O_VALI(out) = btoi(strncmp (O_VALC(in1), O_VALC(in2), ARB) != 0)
+ default:
+ call xvv_error (s_badop)
+ }
+
+ } else if (opcode == LAND || opcode == LOR) {
+ # Operations supporting only the integer types.
+
+ switch (dtype) {
+
+ case TY_SHORT:
+ switch (opcode) {
+ case LAND:
+ if (len1 <= 0) {
+ O_VALI(out) =
+ btoi (O_VALS(in1) != 0 && O_VALS(in2) != 0)
+ } else if (len2 <= 0) {
+ call alanks (Mems[p1], O_VALS(in2), Memi[po], nelem)
+ } else
+ call alans (Mems[p1], Mems[p2], Memi[po], nelem)
+ case LOR:
+ if (len1 <= 0) {
+ O_VALI(out) =
+ btoi (O_VALS(in1) != 0 || O_VALS(in2) != 0)
+ } else if (len2 <= 0) {
+ call alorks (Mems[p1], O_VALS(in2), Memi[po], nelem)
+ } else
+ call alors (Mems[p1], Mems[p2], Memi[po], nelem)
+ default:
+ call xvv_error (s_badop)
+ }
+
+ case TY_INT:
+ switch (opcode) {
+ case LAND:
+ if (len1 <= 0) {
+ O_VALI(out) =
+ btoi (O_VALI(in1) != 0 && O_VALI(in2) != 0)
+ } else if (len2 <= 0) {
+ call alanki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ } else
+ call alani (Memi[p1], Memi[p2], Memi[po], nelem)
+ case LOR:
+ if (len1 <= 0) {
+ O_VALI(out) =
+ btoi (O_VALI(in1) != 0 || O_VALI(in2) != 0)
+ } else if (len2 <= 0) {
+ call alorki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ } else
+ call alori (Memi[p1], Memi[p2], Memi[po], nelem)
+ default:
+ call xvv_error (s_badop)
+ }
+
+ case TY_LONG:
+ switch (opcode) {
+ case LAND:
+ if (len1 <= 0) {
+ O_VALI(out) =
+ btoi (O_VALL(in1) != 0 && O_VALL(in2) != 0)
+ } else if (len2 <= 0) {
+ call alankl (Meml[p1], O_VALL(in2), Memi[po], nelem)
+ } else
+ call alanl (Meml[p1], Meml[p2], Memi[po], nelem)
+ case LOR:
+ if (len1 <= 0) {
+ O_VALI(out) =
+ btoi (O_VALL(in1) != 0 || O_VALL(in2) != 0)
+ } else if (len2 <= 0) {
+ call alorkl (Meml[p1], O_VALL(in2), Memi[po], nelem)
+ } else
+ call alorl (Meml[p1], Meml[p2], Memi[po], nelem)
+ default:
+ call xvv_error (s_badop)
+ }
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+ } else {
+ # Operations supporting any arithmetic type.
+
+ switch (dtype) {
+
+ case TY_SHORT:
+ switch (opcode) {
+ case LT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALS(in1) < O_VALS(in2))
+ else if (len2 <= 0)
+ call abltks (Mems[p1], O_VALS(in2), Memi[po], nelem)
+ else
+ call ablts (Mems[p1], Mems[p2], Memi[po], nelem)
+
+ case LE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALS(in1) <= O_VALS(in2))
+ else if (len2 <= 0)
+ call ableks (Mems[p1], O_VALS(in2), Memi[po], nelem)
+ else
+ call ables (Mems[p1], Mems[p2], Memi[po], nelem)
+
+ case GT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALS(in1) > O_VALS(in2))
+ else if (len2 <= 0)
+ call abgtks (Mems[p1], O_VALS(in2), Memi[po], nelem)
+ else
+ call abgts (Mems[p1], Mems[p2], Memi[po], nelem)
+
+ case GE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALS(in1) >= O_VALS(in2))
+ else if (len2 <= 0)
+ call abgeks (Mems[p1], O_VALS(in2), Memi[po], nelem)
+ else
+ call abges (Mems[p1], Mems[p2], Memi[po], nelem)
+
+ case EQ:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALS(in1) == O_VALS(in2))
+ else if (len2 <= 0)
+ call abeqks (Mems[p1], O_VALS(in2), Memi[po], nelem)
+ else
+ call abeqs (Mems[p1], Mems[p2], Memi[po], nelem)
+
+ case NE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALS(in1) != O_VALS(in2))
+ else if (len2 <= 0)
+ call abneks (Mems[p1], O_VALS(in2), Memi[po], nelem)
+ else
+ call abnes (Mems[p1], Mems[p2], Memi[po], nelem)
+
+ default:
+ call xvv_error (s_badop)
+ }
+
+ case TY_INT:
+ switch (opcode) {
+ case LT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALI(in1) < O_VALI(in2))
+ else if (len2 <= 0)
+ call abltki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ else
+ call ablti (Memi[p1], Memi[p2], Memi[po], nelem)
+
+ case LE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALI(in1) <= O_VALI(in2))
+ else if (len2 <= 0)
+ call ableki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ else
+ call ablei (Memi[p1], Memi[p2], Memi[po], nelem)
+
+ case GT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALI(in1) > O_VALI(in2))
+ else if (len2 <= 0)
+ call abgtki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ else
+ call abgti (Memi[p1], Memi[p2], Memi[po], nelem)
+
+ case GE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALI(in1) >= O_VALI(in2))
+ else if (len2 <= 0)
+ call abgeki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ else
+ call abgei (Memi[p1], Memi[p2], Memi[po], nelem)
+
+ case EQ:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALI(in1) == O_VALI(in2))
+ else if (len2 <= 0)
+ call abeqki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ else
+ call abeqi (Memi[p1], Memi[p2], Memi[po], nelem)
+
+ case NE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALI(in1) != O_VALI(in2))
+ else if (len2 <= 0)
+ call abneki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ else
+ call abnei (Memi[p1], Memi[p2], Memi[po], nelem)
+
+ default:
+ call xvv_error (s_badop)
+ }
+
+ case TY_LONG:
+ switch (opcode) {
+ case LT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALL(in1) < O_VALL(in2))
+ else if (len2 <= 0)
+ call abltkl (Meml[p1], O_VALL(in2), Memi[po], nelem)
+ else
+ call abltl (Meml[p1], Meml[p2], Memi[po], nelem)
+
+ case LE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALL(in1) <= O_VALL(in2))
+ else if (len2 <= 0)
+ call ablekl (Meml[p1], O_VALL(in2), Memi[po], nelem)
+ else
+ call ablel (Meml[p1], Meml[p2], Memi[po], nelem)
+
+ case GT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALL(in1) > O_VALL(in2))
+ else if (len2 <= 0)
+ call abgtkl (Meml[p1], O_VALL(in2), Memi[po], nelem)
+ else
+ call abgtl (Meml[p1], Meml[p2], Memi[po], nelem)
+
+ case GE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALL(in1) >= O_VALL(in2))
+ else if (len2 <= 0)
+ call abgekl (Meml[p1], O_VALL(in2), Memi[po], nelem)
+ else
+ call abgel (Meml[p1], Meml[p2], Memi[po], nelem)
+
+ case EQ:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALL(in1) == O_VALL(in2))
+ else if (len2 <= 0)
+ call abeqkl (Meml[p1], O_VALL(in2), Memi[po], nelem)
+ else
+ call abeql (Meml[p1], Meml[p2], Memi[po], nelem)
+
+ case NE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALL(in1) != O_VALL(in2))
+ else if (len2 <= 0)
+ call abnekl (Meml[p1], O_VALL(in2), Memi[po], nelem)
+ else
+ call abnel (Meml[p1], Meml[p2], Memi[po], nelem)
+
+ default:
+ call xvv_error (s_badop)
+ }
+
+ case TY_REAL:
+ switch (opcode) {
+ case LT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALR(in1) < O_VALR(in2))
+ else if (len2 <= 0)
+ call abltkr (Memr[p1], O_VALR(in2), Memi[po], nelem)
+ else
+ call abltr (Memr[p1], Memr[p2], Memi[po], nelem)
+
+ case LE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALR(in1) <= O_VALR(in2))
+ else if (len2 <= 0)
+ call ablekr (Memr[p1], O_VALR(in2), Memi[po], nelem)
+ else
+ call abler (Memr[p1], Memr[p2], Memi[po], nelem)
+
+ case GT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALR(in1) > O_VALR(in2))
+ else if (len2 <= 0)
+ call abgtkr (Memr[p1], O_VALR(in2), Memi[po], nelem)
+ else
+ call abgtr (Memr[p1], Memr[p2], Memi[po], nelem)
+
+ case GE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALR(in1) >= O_VALR(in2))
+ else if (len2 <= 0)
+ call abgekr (Memr[p1], O_VALR(in2), Memi[po], nelem)
+ else
+ call abger (Memr[p1], Memr[p2], Memi[po], nelem)
+
+ case EQ:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALR(in1) == O_VALR(in2))
+ else if (len2 <= 0)
+ call abeqkr (Memr[p1], O_VALR(in2), Memi[po], nelem)
+ else
+ call abeqr (Memr[p1], Memr[p2], Memi[po], nelem)
+
+ case NE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALR(in1) != O_VALR(in2))
+ else if (len2 <= 0)
+ call abnekr (Memr[p1], O_VALR(in2), Memi[po], nelem)
+ else
+ call abner (Memr[p1], Memr[p2], Memi[po], nelem)
+
+ default:
+ call xvv_error (s_badop)
+ }
+
+ case TY_DOUBLE:
+ switch (opcode) {
+ case LT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALD(in1) < O_VALD(in2))
+ else if (len2 <= 0)
+ call abltkd (Memd[p1], O_VALD(in2), Memi[po], nelem)
+ else
+ call abltd (Memd[p1], Memd[p2], Memi[po], nelem)
+
+ case LE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALD(in1) <= O_VALD(in2))
+ else if (len2 <= 0)
+ call ablekd (Memd[p1], O_VALD(in2), Memi[po], nelem)
+ else
+ call abled (Memd[p1], Memd[p2], Memi[po], nelem)
+
+ case GT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALD(in1) > O_VALD(in2))
+ else if (len2 <= 0)
+ call abgtkd (Memd[p1], O_VALD(in2), Memi[po], nelem)
+ else
+ call abgtd (Memd[p1], Memd[p2], Memi[po], nelem)
+
+ case GE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALD(in1) >= O_VALD(in2))
+ else if (len2 <= 0)
+ call abgekd (Memd[p1], O_VALD(in2), Memi[po], nelem)
+ else
+ call abged (Memd[p1], Memd[p2], Memi[po], nelem)
+
+ case EQ:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALD(in1) == O_VALD(in2))
+ else if (len2 <= 0)
+ call abeqkd (Memd[p1], O_VALD(in2), Memi[po], nelem)
+ else
+ call abeqd (Memd[p1], Memd[p2], Memi[po], nelem)
+
+ case NE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALD(in1) != O_VALD(in2))
+ else if (len2 <= 0)
+ call abnekd (Memd[p1], O_VALD(in2), Memi[po], nelem)
+ else
+ call abned (Memd[p1], Memd[p2], Memi[po], nelem)
+
+ default:
+ call xvv_error (s_badop)
+ }
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+ }
+
+ # Free any storage in input operands.
+ call xvv_freeop (in1)
+ call xvv_freeop (in2)
+end
+
+
+# XVV_PATMATCH -- Match a string against a pattern, returning the patmatch
+# index if the string matches. The pattern may contain any of the conventional
+# pattern matching metacharacters. Closure (i.e., "*") is mapped to "?*".
+
+int procedure xvv_patmatch (str, pat)
+
+char str[ARB] #I operand string
+char pat[ARB] #I pattern
+
+int junk, ip, index
+pointer sp, patstr, patbuf, op
+int patmake(), patmatch()
+
+begin
+ call smark (sp)
+ call salloc (patstr, SZ_FNAME, TY_CHAR)
+ call salloc (patbuf, SZ_LINE, TY_CHAR)
+ call aclrc (Memc[patstr], SZ_FNAME)
+ call aclrc (Memc[patbuf], SZ_LINE)
+
+ # Map pattern, changing '*' into '?*'.
+ op = patstr
+ for (ip=1; pat[ip] != EOS; ip=ip+1) {
+ if (pat[ip] == '*') {
+ Memc[op] = '?'
+ op = op + 1
+ }
+ Memc[op] = pat[ip]
+ op = op + 1
+ }
+
+ # Encode pattern.
+ junk = patmake (Memc[patstr], Memc[patbuf], SZ_LINE)
+
+ # Perform the pattern matching operation.
+ index = patmatch (str, Memc[patbuf])
+
+ call sfree (sp)
+ return (index)
+end
+
+
+# XVV_NEWTYPE -- Get the datatype of a binary operation, given the datatypes
+# of the two input operands. An error action is taken if the datatypes are
+# incompatible, e.g., boolean and anything else or string and anything else.
+
+int procedure xvv_newtype (type1, type2)
+
+int type1 #I datatype of first operand
+int type2 #I datatype of second operand
+
+int newtype, p, q, i
+int tyindex[NTYPES], ttbl[NTYPES*NTYPES]
+data tyindex /T_B, T_C, T_S, T_I, T_L, T_R, T_D/
+
+data (ttbl(i),i= 1, 7) /T_B, 0, 0, 0, 0, 0, 0/
+data (ttbl(i),i= 8,14) / 0, T_C, 0, 0, 0, 0, 0/
+data (ttbl(i),i=15,21) / 0, 0, T_S, T_I, T_L, T_R, T_D/
+data (ttbl(i),i=22,28) / 0, 0, T_I, T_I, T_L, T_R, T_D/
+data (ttbl(i),i=29,35) / 0, 0, T_L, T_L, T_L, T_R, T_D/
+data (ttbl(i),i=36,42) / 0, 0, T_R, T_R, T_R, T_R, T_D/
+data (ttbl(i),i=43,49) / 0, 0, T_D, T_D, T_D, T_D, T_D/
+
+begin
+ do i = 1, NTYPES {
+ if (tyindex[i] == type1)
+ p = i
+ if (tyindex[i] == type2)
+ q = i
+ }
+
+ newtype = ttbl[(p-1)*NTYPES+q]
+ if (newtype == 0)
+ call xvv_error ("operands have incompatible types")
+ else
+ return (newtype)
+end
+
+
+# XVV_QUEST -- Conditional expression. If the condition operand is true
+# return the first (true) operand, else return the second (false) operand.
+
+procedure xvv_quest (cond, in1, in2, out)
+
+pointer cond #I pointer to condition operand
+pointer in1, in2 #I pointer to true,false operands
+pointer out #I pointer to output operand
+
+int dtype, nelem, i
+pointer sp, otemp, ip1, ip2, op, sel
+errchk xvv_error, xvv_newtype, xvv_initop, xvv_chtype
+int xvv_newtype(), btoi()
+
+begin
+ switch (O_TYPE(cond)) {
+ case TY_BOOL, TY_INT:
+ ;
+ case TY_SHORT, TY_LONG:
+ call xvv_chtype (cond, cond, TY_BOOL)
+ default:
+ call xvv_error ("evvexpr: nonboolean condition operand")
+ }
+
+ if (O_LEN(cond) <= 0 &&
+ (O_LEN(in1) <= 0 || O_TYPE(in1) == TY_CHAR) &&
+ (O_LEN(in2) <= 0 || O_TYPE(in2) == TY_CHAR)) {
+
+ # Both operands and the conditional are scalars; the expression
+ # type is the type of the selected operand.
+
+ if (O_VALI(cond) != 0) {
+ YYMOVE (in1, out)
+ call xvv_freeop (in2)
+ } else {
+ YYMOVE (in2, out)
+ call xvv_freeop (in1)
+ }
+
+ } else if (O_TYPE(in1) == TY_CHAR || O_TYPE(in2) == TY_CHAR) {
+ # This combination is not legal.
+ call xvv_error ("evvexpr: character and vector in cond expr")
+
+ } else {
+ # Vector/scalar or vector/vector operation. Both operands must
+ # be of the same type.
+
+ dtype = xvv_newtype (O_TYPE(in1), O_TYPE(in2))
+
+ # Compute the size of the output operand. If both input operands
+ # are vectors the length of the output vector is the shorter of
+ # the two. The condition operand contributes to the dimension of
+ # the expression result, although not to the datatype.
+
+ nelem = 0
+ if (O_LEN(in1) > 0 && O_LEN(in2) > 0)
+ nelem = min (O_LEN(in1), O_LEN(in2))
+ else if (O_LEN(in1) > 0)
+ nelem = O_LEN(in1)
+ else if (O_LEN(in2) > 0)
+ nelem = O_LEN(in2)
+
+ if (O_LEN(cond) > 0 && nelem > 0)
+ nelem = min (O_LEN(cond), nelem)
+ else if (O_LEN(cond) > 0)
+ nelem = O_LEN(cond)
+
+ # If this is a scalar/vector operation make sure the vector is the
+ # first operand.
+
+ if (O_LEN(in1) == 0 && O_LEN(in2) > 0) {
+ call smark (sp)
+ call salloc (otemp, LEN_OPERAND, TY_STRUCT)
+ YYMOVE (in1, otemp)
+ YYMOVE (in2, in1)
+ YYMOVE (otemp, in2)
+ call sfree (sp)
+
+ # Since we are swapping arguments we need to negate the cond.
+ if (O_LEN(cond) <= 0)
+ O_VALI(cond) = btoi (O_VALI(cond) == 0)
+ else {
+ call abeqki (Memi[O_VALP(cond)], NO, Memi[O_VALP(cond)],
+ nelem)
+ }
+ }
+
+ # Initialize the output operand.
+ call xvv_initop (out, nelem, dtype)
+
+ # Convert input operands to desired computation type.
+ if (O_TYPE(in1) != dtype)
+ call xvv_chtype (in1, in1, dtype)
+ if (O_TYPE(in2) != dtype)
+ call xvv_chtype (in2, in2, dtype)
+
+ ip1 = O_VALP(in1)
+ ip2 = O_VALP(in2)
+ op = O_VALP(out)
+ sel = O_VALP(cond)
+
+ # Perform the operation.
+ switch (dtype) {
+
+ case TY_SHORT:
+ if (O_LEN(in1) <= 0 && O_LEN(in2) <= 0) {
+ # Vector conditional, both operands are scalars.
+ do i = 1, nelem
+ if (Memi[sel+i-1] != 0)
+ Mems[op+i-1] = O_VALS(in1)
+ else
+ Mems[op+i-1] = O_VALS(in2)
+
+ } else if (O_LEN(in2) <= 0) {
+ # Operand 1 is a vector, operand 2 is a scalar.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amovs (Mems[ip1], Mems[op], nelem)
+ else
+ call amovks (O_VALS(in2), Mems[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call aselks (Mems[ip1], O_VALS(in2), Mems[op],
+ Memi[sel], nelem)
+ }
+ } else {
+ # Both operands are vectors.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amovs (Mems[ip1], Mems[op], nelem)
+ else
+ call amovs (Mems[ip2], Mems[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call asels (Mems[ip1], Mems[ip2], Mems[op],
+ Memi[sel], nelem)
+ }
+ }
+
+ case TY_INT:
+ if (O_LEN(in1) <= 0 && O_LEN(in2) <= 0) {
+ # Vector conditional, both operands are scalars.
+ do i = 1, nelem
+ if (Memi[sel+i-1] != 0)
+ Memi[op+i-1] = O_VALI(in1)
+ else
+ Memi[op+i-1] = O_VALI(in2)
+
+ } else if (O_LEN(in2) <= 0) {
+ # Operand 1 is a vector, operand 2 is a scalar.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amovi (Memi[ip1], Memi[op], nelem)
+ else
+ call amovki (O_VALI(in2), Memi[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call aselki (Memi[ip1], O_VALI(in2), Memi[op],
+ Memi[sel], nelem)
+ }
+ } else {
+ # Both operands are vectors.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amovi (Memi[ip1], Memi[op], nelem)
+ else
+ call amovi (Memi[ip2], Memi[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call aseli (Memi[ip1], Memi[ip2], Memi[op],
+ Memi[sel], nelem)
+ }
+ }
+
+ case TY_LONG:
+ if (O_LEN(in1) <= 0 && O_LEN(in2) <= 0) {
+ # Vector conditional, both operands are scalars.
+ do i = 1, nelem
+ if (Memi[sel+i-1] != 0)
+ Meml[op+i-1] = O_VALL(in1)
+ else
+ Meml[op+i-1] = O_VALL(in2)
+
+ } else if (O_LEN(in2) <= 0) {
+ # Operand 1 is a vector, operand 2 is a scalar.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amovl (Meml[ip1], Meml[op], nelem)
+ else
+ call amovkl (O_VALL(in2), Meml[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call aselkl (Meml[ip1], O_VALL(in2), Meml[op],
+ Memi[sel], nelem)
+ }
+ } else {
+ # Both operands are vectors.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amovl (Meml[ip1], Meml[op], nelem)
+ else
+ call amovl (Meml[ip2], Meml[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call asell (Meml[ip1], Meml[ip2], Meml[op],
+ Memi[sel], nelem)
+ }
+ }
+
+ case TY_REAL:
+ if (O_LEN(in1) <= 0 && O_LEN(in2) <= 0) {
+ # Vector conditional, both operands are scalars.
+ do i = 1, nelem
+ if (Memi[sel+i-1] != 0)
+ Memr[op+i-1] = O_VALR(in1)
+ else
+ Memr[op+i-1] = O_VALR(in2)
+
+ } else if (O_LEN(in2) <= 0) {
+ # Operand 1 is a vector, operand 2 is a scalar.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amovr (Memr[ip1], Memr[op], nelem)
+ else
+ call amovkr (O_VALR(in2), Memr[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call aselkr (Memr[ip1], O_VALR(in2), Memr[op],
+ Memi[sel], nelem)
+ }
+ } else {
+ # Both operands are vectors.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amovr (Memr[ip1], Memr[op], nelem)
+ else
+ call amovr (Memr[ip2], Memr[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call aselr (Memr[ip1], Memr[ip2], Memr[op],
+ Memi[sel], nelem)
+ }
+ }
+
+ case TY_DOUBLE:
+ if (O_LEN(in1) <= 0 && O_LEN(in2) <= 0) {
+ # Vector conditional, both operands are scalars.
+ do i = 1, nelem
+ if (Memi[sel+i-1] != 0)
+ Memd[op+i-1] = O_VALD(in1)
+ else
+ Memd[op+i-1] = O_VALD(in2)
+
+ } else if (O_LEN(in2) <= 0) {
+ # Operand 1 is a vector, operand 2 is a scalar.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amovd (Memd[ip1], Memd[op], nelem)
+ else
+ call amovkd (O_VALD(in2), Memd[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call aselkd (Memd[ip1], O_VALD(in2), Memd[op],
+ Memi[sel], nelem)
+ }
+ } else {
+ # Both operands are vectors.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amovd (Memd[ip1], Memd[op], nelem)
+ else
+ call amovd (Memd[ip2], Memd[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call aseld (Memd[ip1], Memd[ip2], Memd[op],
+ Memi[sel], nelem)
+ }
+ }
+
+ default:
+ call xvv_error ("evvexpr: bad datatype in cond expr")
+ }
+
+ call xvv_freeop (in1)
+ call xvv_freeop (in2)
+ }
+
+ call xvv_freeop (cond)
+end
+
+
+# XVV_CALLFCN -- Call an intrinsic function. If the function named is not
+# one of the standard intrinsic functions, call an external user function
+# if a function call procedure was supplied.
+
+procedure xvv_callfcn (fcn, args, nargs, out)
+
+char fcn[ARB] #I function to be called
+pointer args[ARB] #I pointer to arglist descriptor
+int nargs #I number of arguments
+pointer out #I output operand (function value)
+
+
+short v_s
+short ahivs(), alovs()
+short ameds()
+int aravs()
+
+int v_i
+int ahivi(), alovi()
+int amedi()
+int aravi()
+
+long v_l
+long ahivl(), alovl()
+long amedl()
+int aravl()
+
+real v_r
+real ahivr(), alovr()
+real amedr()
+int aravr()
+
+double v_d
+double ahivd(), alovd()
+double amedd()
+int aravd()
+
+
+real mean_r, sigma_r
+double mean_d, sigma_d
+real asums(), asumi(), asumr()
+double asuml(), asumd()
+
+bool rangecheck
+int optype, opcode
+int chunk, repl, nelem, v_nargs, ch, shift, i, j
+pointer sp, sym, buf, ap, ip, op, in1, in2
+include "evvexpr.com"
+
+pointer stfind()
+int xvv_newtype(), strlen(), gctod(), btoi()
+errchk xvv_chtype, xvv_initop, xvv_newtype, xvv_error1, xvv_error2
+errchk zcall5, malloc
+
+string s_badtype "%s: illegal operand type"
+define free_ 91
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_FNAME, TY_CHAR)
+
+ # Lookup the function name in the symbol table.
+ sym = stfind (ev_st, fcn)
+ if (sym != NULL)
+ opcode = SYM_CODE(sym)
+ else
+ opcode = 0
+
+ # If the function named is not a standard one and the user has supplied
+ # the entry point of an external function evaluation procedure, call
+ # the user procedure to evaluate the function, otherwise abort.
+
+ if (opcode <= 0)
+ if (ev_ufcn != NULL) {
+ call zcall5 (ev_ufcn, ev_ufcn_data, fcn, args, nargs, out)
+ if (O_TYPE(out) <= 0)
+ call xvv_error1 ("unrecognized macro or function `%s'", fcn)
+ goto free_
+ } else
+ call xvv_error1 ("unknown function `%s' called", fcn)
+
+ # Range checking on functions that need it?
+ rangecheck = (and (ev_flags, EV_RNGCHK) != 0)
+
+ # Verify correct number of arguments.
+ switch (opcode) {
+ case F_MOD, F_REPL, F_SHIFT:
+ v_nargs = 2
+ case F_MAX, F_MIN, F_ATAN, F_ATAN2, F_MEAN, F_STDDEV, F_MEDIAN:
+ v_nargs = -1
+ default:
+ v_nargs = 1
+ }
+ if (v_nargs > 0 && nargs != v_nargs)
+ call xvv_error2 ("function `%s' requires %d arguments",
+ fcn, v_nargs)
+ else if (v_nargs < 0 && nargs < abs(v_nargs))
+ call xvv_error2 ("function `%s' requires at least %d arguments",
+ fcn, abs(v_nargs))
+
+ # Some functions require that the input operand be a certain type,
+ # e.g. floating. Handle the simple cases, converting input operands
+ # to the desired type.
+
+ switch (opcode) {
+ case F_ACOS, F_ASIN, F_ATAN, F_ATAN2, F_COS, F_COSH, F_DEG, F_EXP,
+ F_LOG, F_LOG10, F_RAD, F_SIN, F_SINH, F_SQRT, F_TAN, F_TANH:
+
+ # These functions want a floating point input operand.
+ optype = TY_REAL
+ do i = 1, nargs {
+ if (O_TYPE(args[i]) == TY_DOUBLE || O_TYPE(args[i]) == TY_LONG)
+ optype = TY_DOUBLE
+ }
+ do i = 1, nargs {
+ if (O_TYPE(args[i]) != optype)
+ call xvv_chtype (args[i], args[i], optype)
+ }
+ call xvv_initop (out, O_LEN(args[1]), optype)
+
+ case F_MOD, F_MIN, F_MAX, F_MEDIAN:
+ # These functions may have multiple arguments, all of which
+ # should be the same type.
+
+ optype = O_TYPE(args[1])
+ nelem = O_LEN(args[1])
+ do i = 2, nargs {
+ optype = xvv_newtype (optype, O_TYPE(args[i]))
+ if (O_LEN(args[i]) > 0)
+ if (nelem > 0)
+ nelem = min (nelem, O_LEN(args[i]))
+ else if (nelem == 0)
+ nelem = O_LEN(args[i])
+ }
+
+ do i = 1, nargs
+ if (O_TYPE(args[i]) != optype)
+ call xvv_chtype (args[i], args[i], optype)
+
+ if (nargs == 1 && opcode == F_MEDIAN)
+ nelem = 0
+ call xvv_initop (out, nelem, optype)
+
+ case F_LEN:
+ # This function always returns an integer scalar value.
+ nelem = 0
+ optype = TY_INT
+ call xvv_initop (out, nelem, optype)
+
+ case F_HIV, F_LOV:
+ # These functions return a scalar value.
+ nelem = 0
+ optype = O_TYPE(args[1])
+ if (optype == TY_BOOL)
+ optype = TY_INT
+ call xvv_initop (out, nelem, optype)
+
+ case F_SUM, F_MEAN, F_STDDEV:
+ # These functions require a vector argument and return a scalar
+ # value.
+
+ nelem = 0
+ optype = O_TYPE(args[1])
+ if (optype == TY_BOOL)
+ optype = TY_INT
+
+ if (optype == TY_DOUBLE)
+ call xvv_initop (out, nelem, TY_DOUBLE)
+ else
+ call xvv_initop (out, nelem, TY_REAL)
+
+ case F_SORT, F_SHIFT:
+ # Vector to vector, no type conversions.
+ nelem = O_LEN(args[1])
+ optype = O_TYPE(args[1])
+ call xvv_initop (out, nelem, optype)
+
+ default:
+ optype = 0
+ }
+
+ # Evaluate the function.
+ ap = args[1]
+
+ switch (opcode) {
+ case F_ABS:
+ call xvv_initop (out, O_LEN(ap), O_TYPE(ap))
+ switch (O_TYPE(ap)) {
+
+ case TY_SHORT:
+ if (O_LEN(ap) > 0) {
+ call aabss (Mems[O_VALP(ap)], Mems[O_VALP(out)],
+ O_LEN(ap))
+ } else
+ O_VALS(out) = abs(O_VALS(ap))
+
+ case TY_INT:
+ if (O_LEN(ap) > 0) {
+ call aabsi (Memi[O_VALP(ap)], Memi[O_VALP(out)],
+ O_LEN(ap))
+ } else
+ O_VALI(out) = abs(O_VALI(ap))
+
+ case TY_LONG:
+ if (O_LEN(ap) > 0) {
+ call aabsl (Meml[O_VALP(ap)], Meml[O_VALP(out)],
+ O_LEN(ap))
+ } else
+ O_VALL(out) = abs(O_VALL(ap))
+
+ case TY_REAL:
+ if (O_LEN(ap) > 0) {
+ call aabsr (Memr[O_VALP(ap)], Memr[O_VALP(out)],
+ O_LEN(ap))
+ } else
+ O_VALR(out) = abs(O_VALR(ap))
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) > 0) {
+ call aabsd (Memd[O_VALP(ap)], Memd[O_VALP(out)],
+ O_LEN(ap))
+ } else
+ O_VALD(out) = abs(O_VALD(ap))
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_ACOS:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = acos (Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = acos (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = acos (Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = acos (O_VALD(ap))
+
+ case F_ASIN:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = asin (Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = asin (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = asin (Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = asin (O_VALD(ap))
+
+ case F_COS:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = cos (Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = cos (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = cos (Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = cos (O_VALD(ap))
+
+ case F_COSH:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = cosh (Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = cosh (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = cosh (Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = cosh (O_VALD(ap))
+
+ case F_DEG:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = RADTODEG(Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = RADTODEG (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = RADTODEG(Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = RADTODEG (O_VALD(ap))
+
+ case F_EXP:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = exp (Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = exp (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = exp (Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = exp (O_VALD(ap))
+
+ case F_LOG:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ op = O_VALP(out)
+ do i = 1, O_LEN(ap) {
+ v_r = Memr[O_VALP(ap)+i-1]
+ if (rangecheck && v_r <= 0)
+ Memr[op] = 0
+ else
+ Memr[op] = log (v_r)
+ op = op + 1
+ }
+ } else {
+ v_r = O_VALR(ap)
+ if (rangecheck && v_r <= 0)
+ O_VALR(out) = 0
+ else
+ O_VALR(out) = log (v_r)
+ }
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ op = O_VALP(out)
+ do i = 1, O_LEN(ap) {
+ v_d = Memd[O_VALP(ap)+i-1]
+ if (rangecheck && v_d <= 0)
+ Memd[op] = 0
+ else
+ Memd[op] = log (v_d)
+ op = op + 1
+ }
+ } else {
+ v_d = O_VALD(ap)
+ if (rangecheck && v_d <= 0)
+ O_VALD(out) = 0
+ else
+ O_VALD(out) = log (v_d)
+ }
+
+ case F_LOG10:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ op = O_VALP(out)
+ do i = 1, O_LEN(ap) {
+ v_r = Memr[O_VALP(ap)+i-1]
+ if (rangecheck && v_r <= 0)
+ Memr[op] = 0
+ else
+ Memr[op] = log10 (v_r)
+ op = op + 1
+ }
+ } else {
+ v_r = O_VALR(ap)
+ if (rangecheck && v_r <= 0)
+ O_VALR(out) = 0
+ else
+ O_VALR(out) = log10 (v_r)
+ }
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ op = O_VALP(out)
+ do i = 1, O_LEN(ap) {
+ v_d = Memd[O_VALP(ap)+i-1]
+ if (rangecheck && v_d <= 0)
+ Memd[op] = 0
+ else
+ Memd[op] = log10 (v_d)
+ op = op + 1
+ }
+ } else {
+ v_d = O_VALD(ap)
+ if (rangecheck && v_d <= 0)
+ O_VALD(out) = 0
+ else
+ O_VALD(out) = log10 (v_d)
+ }
+
+ case F_RAD:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = DEGTORAD(Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = DEGTORAD (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = DEGTORAD(Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = DEGTORAD (O_VALD(ap))
+
+ case F_SIN:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = sin (Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = sin (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = sin (Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = sin (O_VALD(ap))
+
+ case F_SINH:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = sinh (Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = sinh (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = sinh (Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = sinh (O_VALD(ap))
+
+ case F_SQRT:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ op = O_VALP(out)
+ do i = 1, O_LEN(ap) {
+ v_r = Memr[O_VALP(ap)+i-1]
+ if (rangecheck && v_r < 0)
+ Memr[op] = 0
+ else
+ Memr[op] = sqrt (v_r)
+ op = op + 1
+ }
+ } else {
+ v_r = O_VALR(ap)
+ if (rangecheck && v_r <= 0)
+ O_VALR(out) = 0
+ else
+ O_VALR(out) = sqrt (v_r)
+ }
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ op = O_VALP(out)
+ do i = 1, O_LEN(ap) {
+ v_d = Memd[O_VALP(ap)+i-1]
+ if (rangecheck && v_d < 0)
+ Memd[op] = 0
+ else
+ Memd[op] = sqrt (v_d)
+ op = op + 1
+ }
+ } else {
+ v_d = O_VALD(ap)
+ if (rangecheck && v_d <= 0)
+ O_VALD(out) = 0
+ else
+ O_VALD(out) = sqrt (v_d)
+ }
+
+ case F_TAN:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = tan (Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = tan (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = tan (Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = tan (O_VALD(ap))
+
+ case F_TANH:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = tanh (Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = tanh (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = tanh (Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = tanh (O_VALD(ap))
+
+
+ case F_LEN:
+ # Vector length.
+ O_VALI(out) = O_LEN(ap)
+
+ case F_HIV:
+ # High value.
+ switch (optype) {
+
+ case TY_SHORT:
+ if (O_LEN(ap) > 0)
+ O_VALS(out) = ahivs (Mems[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VALS(out) = O_VALS(ap)
+
+ case TY_INT:
+ if (O_LEN(ap) > 0)
+ O_VALI(out) = ahivi (Memi[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VALI(out) = O_VALI(ap)
+
+ case TY_LONG:
+ if (O_LEN(ap) > 0)
+ O_VALL(out) = ahivl (Meml[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VALL(out) = O_VALL(ap)
+
+ case TY_REAL:
+ if (O_LEN(ap) > 0)
+ O_VALR(out) = ahivr (Memr[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VALR(out) = O_VALR(ap)
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) > 0)
+ O_VALD(out) = ahivd (Memd[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VALD(out) = O_VALD(ap)
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+ case F_LOV:
+ # Low value.
+ switch (optype) {
+
+ case TY_SHORT:
+ if (O_LEN(ap) > 0)
+ O_VALS(out) = alovs (Mems[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VALS(out) = O_VALS(ap)
+
+ case TY_INT:
+ if (O_LEN(ap) > 0)
+ O_VALI(out) = alovi (Memi[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VALI(out) = O_VALI(ap)
+
+ case TY_LONG:
+ if (O_LEN(ap) > 0)
+ O_VALL(out) = alovl (Meml[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VALL(out) = O_VALL(ap)
+
+ case TY_REAL:
+ if (O_LEN(ap) > 0)
+ O_VALR(out) = alovr (Memr[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VALR(out) = O_VALR(ap)
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) > 0)
+ O_VALD(out) = alovd (Memd[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VALD(out) = O_VALD(ap)
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_SUM:
+ # Vector sum.
+ switch (optype) {
+
+ case TY_SHORT:
+ if (O_LEN(ap) > 0)
+ v_r = asums (Mems[O_VALP(ap)], O_LEN(ap))
+ else
+ v_r = O_VALS(ap)
+
+ case TY_INT:
+ if (O_LEN(ap) > 0)
+ v_r = asumi (Memi[O_VALP(ap)], O_LEN(ap))
+ else
+ v_r = O_VALI(ap)
+
+ case TY_LONG:
+ if (O_LEN(ap) > 0)
+ v_r = asuml (Meml[O_VALP(ap)], O_LEN(ap))
+ else
+ v_r = O_VALL(ap)
+
+ case TY_REAL:
+ if (O_LEN(ap) > 0)
+ v_r = asumr (Memr[O_VALP(ap)], O_LEN(ap))
+ else
+ v_r = O_VALR(ap)
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) > 0)
+ v_d = asumd (Memd[O_VALP(ap)], O_LEN(ap))
+ else
+ v_d = O_VALD(ap)
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ if (optype == TY_DOUBLE)
+ O_VALD(out) = v_d
+ else
+ O_VALR(out) = v_r
+
+ case F_MEAN, F_STDDEV:
+ # Compute the mean or standard deviation of a vector. An optional
+ # second argument may be supplied to compute a K-sigma rejection
+ # mean and sigma.
+
+ if (nargs == 2) {
+ if (O_LEN(args[2]) > 0)
+ call xvv_error1 ("%s: ksigma arg must be a scalar" , fcn)
+
+ switch (O_TYPE(args[2])) {
+ case TY_REAL:
+ v_r = O_VALR(args[2])
+ v_d = v_r
+ case TY_DOUBLE:
+ v_d = O_VALD(args[2])
+ v_r = v_d
+ default:
+ call xvv_chtype (args[2], args[2], TY_REAL)
+ v_r = O_VALR(args[2])
+ v_d = v_r
+ }
+ } else {
+ v_r = 0.0
+ v_d = 0.0
+ }
+
+ switch (optype) {
+
+ case TY_SHORT:
+ v_i = aravs (Mems[O_VALP(ap)], O_LEN(ap), mean_r,sigma_r,v_r)
+
+ case TY_INT:
+ v_i = aravi (Memi[O_VALP(ap)], O_LEN(ap), mean_r,sigma_r,v_r)
+
+ case TY_REAL:
+ v_i = aravr (Memr[O_VALP(ap)], O_LEN(ap), mean_r,sigma_r,v_r)
+
+
+ case TY_LONG:
+ v_i = aravl (Meml[O_VALP(ap)], O_LEN(ap), mean_d,sigma_d,v_d)
+
+ case TY_DOUBLE:
+ v_i = aravd (Memd[O_VALP(ap)], O_LEN(ap), mean_d,sigma_d,v_d)
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ if (opcode == F_MEAN) {
+ if (O_TYPE(out) == TY_REAL)
+ O_VALR(out) = mean_r
+ else
+ O_VALD(out) = mean_d
+ } else {
+ if (O_TYPE(out) == TY_REAL)
+ O_VALR(out) = sigma_r
+ else
+ O_VALD(out) = sigma_d
+ }
+
+ case F_MEDIAN:
+ # Compute the median value of a vector, or the vector median
+ # of 3 or more vectors.
+
+ switch (nargs) {
+ case 1:
+ switch (optype) {
+
+ case TY_SHORT:
+ O_VALS(out) = ameds (Mems[O_VALP(ap)], O_LEN(ap))
+
+ case TY_INT:
+ O_VALI(out) = amedi (Memi[O_VALP(ap)], O_LEN(ap))
+
+ case TY_LONG:
+ O_VALL(out) = amedl (Meml[O_VALP(ap)], O_LEN(ap))
+
+ case TY_REAL:
+ O_VALR(out) = amedr (Memr[O_VALP(ap)], O_LEN(ap))
+
+ case TY_DOUBLE:
+ O_VALD(out) = amedd (Memd[O_VALP(ap)], O_LEN(ap))
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+ case 3:
+ switch (optype) {
+
+ case TY_SHORT:
+ call amed3s (Mems[O_VALP(args[1])],
+ Mems[O_VALP(args[2])],
+ Mems[O_VALP(args[3])],
+ Mems[O_VALP(out)], nelem)
+
+ case TY_INT:
+ call amed3i (Memi[O_VALP(args[1])],
+ Memi[O_VALP(args[2])],
+ Memi[O_VALP(args[3])],
+ Memi[O_VALP(out)], nelem)
+
+ case TY_LONG:
+ call amed3l (Meml[O_VALP(args[1])],
+ Meml[O_VALP(args[2])],
+ Meml[O_VALP(args[3])],
+ Meml[O_VALP(out)], nelem)
+
+ case TY_REAL:
+ call amed3r (Memr[O_VALP(args[1])],
+ Memr[O_VALP(args[2])],
+ Memr[O_VALP(args[3])],
+ Memr[O_VALP(out)], nelem)
+
+ case TY_DOUBLE:
+ call amed3d (Memd[O_VALP(args[1])],
+ Memd[O_VALP(args[2])],
+ Memd[O_VALP(args[3])],
+ Memd[O_VALP(out)], nelem)
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+ case 4:
+ switch (optype) {
+
+ case TY_SHORT:
+ call amed4s (Mems[O_VALP(args[1])],
+ Mems[O_VALP(args[2])],
+ Mems[O_VALP(args[3])],
+ Mems[O_VALP(args[4])],
+ Mems[O_VALP(out)], nelem)
+
+ case TY_INT:
+ call amed4i (Memi[O_VALP(args[1])],
+ Memi[O_VALP(args[2])],
+ Memi[O_VALP(args[3])],
+ Memi[O_VALP(args[4])],
+ Memi[O_VALP(out)], nelem)
+
+ case TY_LONG:
+ call amed4l (Meml[O_VALP(args[1])],
+ Meml[O_VALP(args[2])],
+ Meml[O_VALP(args[3])],
+ Meml[O_VALP(args[4])],
+ Meml[O_VALP(out)], nelem)
+
+ case TY_REAL:
+ call amed4r (Memr[O_VALP(args[1])],
+ Memr[O_VALP(args[2])],
+ Memr[O_VALP(args[3])],
+ Memr[O_VALP(args[4])],
+ Memr[O_VALP(out)], nelem)
+
+ case TY_DOUBLE:
+ call amed4d (Memd[O_VALP(args[1])],
+ Memd[O_VALP(args[2])],
+ Memd[O_VALP(args[3])],
+ Memd[O_VALP(args[4])],
+ Memd[O_VALP(out)], nelem)
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+ case 5:
+ switch (optype) {
+
+ case TY_SHORT:
+ call amed5s (Mems[O_VALP(args[1])],
+ Mems[O_VALP(args[2])],
+ Mems[O_VALP(args[3])],
+ Mems[O_VALP(args[4])],
+ Mems[O_VALP(args[5])],
+ Mems[O_VALP(out)], nelem)
+
+ case TY_INT:
+ call amed5i (Memi[O_VALP(args[1])],
+ Memi[O_VALP(args[2])],
+ Memi[O_VALP(args[3])],
+ Memi[O_VALP(args[4])],
+ Memi[O_VALP(args[5])],
+ Memi[O_VALP(out)], nelem)
+
+ case TY_LONG:
+ call amed5l (Meml[O_VALP(args[1])],
+ Meml[O_VALP(args[2])],
+ Meml[O_VALP(args[3])],
+ Meml[O_VALP(args[4])],
+ Meml[O_VALP(args[5])],
+ Meml[O_VALP(out)], nelem)
+
+ case TY_REAL:
+ call amed5r (Memr[O_VALP(args[1])],
+ Memr[O_VALP(args[2])],
+ Memr[O_VALP(args[3])],
+ Memr[O_VALP(args[4])],
+ Memr[O_VALP(args[5])],
+ Memr[O_VALP(out)], nelem)
+
+ case TY_DOUBLE:
+ call amed5d (Memd[O_VALP(args[1])],
+ Memd[O_VALP(args[2])],
+ Memd[O_VALP(args[3])],
+ Memd[O_VALP(args[4])],
+ Memd[O_VALP(args[5])],
+ Memd[O_VALP(out)], nelem)
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+ default:
+ call xvv_error1 ("%s: wrong number of arguments", fcn)
+ }
+
+ case F_REPL:
+ # Replicate an item to make a longer vector.
+
+ chunk = O_LEN(ap)
+ optype = O_TYPE(ap)
+ if (optype == TY_BOOL)
+ optype = TY_INT
+
+ if (O_LEN(args[2]) > 0)
+ call xvv_error1 ("%s: replication factor must be a scalar", fcn)
+ if (O_TYPE(args[2]) != TY_INT)
+ call xvv_chtype (args[2], args[2], TY_INT)
+ repl = max (1, O_VALI(args[2]))
+
+ if (chunk <= 0)
+ nelem = repl
+ else
+ nelem = chunk * repl
+ call xvv_initop (out, nelem, optype)
+
+ switch (optype) {
+
+ case TY_SHORT:
+ if (chunk > 0) {
+ ip = O_VALP(ap)
+ op = O_VALP(out)
+ do i = 1, repl {
+ call amovs (Mems[ip], Mems[op], chunk)
+ op = op + chunk
+ }
+ } else
+ call amovks (O_VALS(ap), Mems[O_VALP(out)], nelem)
+
+ case TY_INT:
+ if (chunk > 0) {
+ ip = O_VALP(ap)
+ op = O_VALP(out)
+ do i = 1, repl {
+ call amovi (Memi[ip], Memi[op], chunk)
+ op = op + chunk
+ }
+ } else
+ call amovki (O_VALI(ap), Memi[O_VALP(out)], nelem)
+
+ case TY_LONG:
+ if (chunk > 0) {
+ ip = O_VALP(ap)
+ op = O_VALP(out)
+ do i = 1, repl {
+ call amovl (Meml[ip], Meml[op], chunk)
+ op = op + chunk
+ }
+ } else
+ call amovkl (O_VALL(ap), Meml[O_VALP(out)], nelem)
+
+ case TY_REAL:
+ if (chunk > 0) {
+ ip = O_VALP(ap)
+ op = O_VALP(out)
+ do i = 1, repl {
+ call amovr (Memr[ip], Memr[op], chunk)
+ op = op + chunk
+ }
+ } else
+ call amovkr (O_VALR(ap), Memr[O_VALP(out)], nelem)
+
+ case TY_DOUBLE:
+ if (chunk > 0) {
+ ip = O_VALP(ap)
+ op = O_VALP(out)
+ do i = 1, repl {
+ call amovd (Memd[ip], Memd[op], chunk)
+ op = op + chunk
+ }
+ } else
+ call amovkd (O_VALD(ap), Memd[O_VALP(out)], nelem)
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_SHIFT:
+ # Vector shift.
+ if (O_LEN(args[2]) > 0)
+ call xvv_error1 ("%s: shift arg must be a scalar" , fcn)
+ if (O_TYPE(args[2]) != TY_INT)
+ call xvv_chtype (args[2], args[2], TY_INT)
+ shift = O_VALI(args[2])
+
+ if (abs(shift) > nelem) {
+ if (shift > 0)
+ shift = nelem
+ else
+ shift = -nelem
+ }
+
+ switch (optype) {
+
+ case TY_SHORT:
+ if (nelem > 0) {
+ do i = 1, nelem {
+ j = i - shift
+ if (j < 1)
+ j = j + nelem
+ else if (j > nelem)
+ j = j - nelem
+ Mems[O_VALP(out)+i-1] = Mems[O_VALP(ap)+j-1]
+ }
+ } else
+ O_VALS(out) = (O_VALS(ap))
+
+ case TY_INT:
+ if (nelem > 0) {
+ do i = 1, nelem {
+ j = i - shift
+ if (j < 1)
+ j = j + nelem
+ else if (j > nelem)
+ j = j - nelem
+ Memi[O_VALP(out)+i-1] = Memi[O_VALP(ap)+j-1]
+ }
+ } else
+ O_VALI(out) = (O_VALI(ap))
+
+ case TY_LONG:
+ if (nelem > 0) {
+ do i = 1, nelem {
+ j = i - shift
+ if (j < 1)
+ j = j + nelem
+ else if (j > nelem)
+ j = j - nelem
+ Meml[O_VALP(out)+i-1] = Meml[O_VALP(ap)+j-1]
+ }
+ } else
+ O_VALL(out) = (O_VALL(ap))
+
+ case TY_REAL:
+ if (nelem > 0) {
+ do i = 1, nelem {
+ j = i - shift
+ if (j < 1)
+ j = j + nelem
+ else if (j > nelem)
+ j = j - nelem
+ Memr[O_VALP(out)+i-1] = Memr[O_VALP(ap)+j-1]
+ }
+ } else
+ O_VALR(out) = (O_VALR(ap))
+
+ case TY_DOUBLE:
+ if (nelem > 0) {
+ do i = 1, nelem {
+ j = i - shift
+ if (j < 1)
+ j = j + nelem
+ else if (j > nelem)
+ j = j - nelem
+ Memd[O_VALP(out)+i-1] = Memd[O_VALP(ap)+j-1]
+ }
+ } else
+ O_VALD(out) = (O_VALD(ap))
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_SORT:
+ # Sort a vector.
+ switch (optype) {
+
+ case TY_SHORT:
+ if (nelem > 0)
+ call asrts (Mems[O_VALP(ap)], Mems[O_VALP(out)], nelem)
+ else
+ O_VALS(out) = (O_VALS(ap))
+
+ case TY_INT:
+ if (nelem > 0)
+ call asrti (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+ else
+ O_VALI(out) = (O_VALI(ap))
+
+ case TY_LONG:
+ if (nelem > 0)
+ call asrtl (Meml[O_VALP(ap)], Meml[O_VALP(out)], nelem)
+ else
+ O_VALL(out) = (O_VALL(ap))
+
+ case TY_REAL:
+ if (nelem > 0)
+ call asrtr (Memr[O_VALP(ap)], Memr[O_VALP(out)], nelem)
+ else
+ O_VALR(out) = (O_VALR(ap))
+
+ case TY_DOUBLE:
+ if (nelem > 0)
+ call asrtd (Memd[O_VALP(ap)], Memd[O_VALP(out)], nelem)
+ else
+ O_VALD(out) = (O_VALD(ap))
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_ATAN, F_ATAN2:
+
+ if (optype == TY_REAL) {
+ if (nargs == 1) {
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] =
+ atan (Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = atan (O_VALR(ap))
+ } else {
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] =
+ atan2 (Memr[O_VALP(args[1])+i-1],
+ Memr[O_VALP(args[2])+i-1])
+ } else
+ O_VALR(out) = atan2(O_VALR(args[1]), O_VALR(args[2]))
+ }
+ }
+
+ if (optype == TY_DOUBLE) {
+ if (nargs == 1) {
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] =
+ atan (Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = atan (O_VALD(ap))
+ } else {
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] =
+ atan2 (Memd[O_VALP(args[1])+i-1],
+ Memd[O_VALP(args[2])+i-1])
+ } else
+ O_VALD(out) = atan2(O_VALD(args[1]), O_VALD(args[2]))
+ }
+ }
+
+
+ case F_MOD:
+ in1 = args[1]
+ in2 = args[2]
+
+ switch (optype) {
+
+ case TY_SHORT:
+ if (O_LEN(in1) <= 0) {
+ O_VALS(out) = mod (O_VALS(in1), O_VALS(in2))
+ } else if (O_LEN(in2) <= 0) {
+ call amodks (Mems[O_VALP(in1)], O_VALS(in2),
+ Mems[O_VALP(out)], nelem)
+ } else {
+ call amods (Mems[O_VALP(in1)], Mems[O_VALP(in2)],
+ Mems[O_VALP(out)], nelem)
+ }
+
+ case TY_INT:
+ if (O_LEN(in1) <= 0) {
+ O_VALI(out) = mod (O_VALI(in1), O_VALI(in2))
+ } else if (O_LEN(in2) <= 0) {
+ call amodki (Memi[O_VALP(in1)], O_VALI(in2),
+ Memi[O_VALP(out)], nelem)
+ } else {
+ call amodi (Memi[O_VALP(in1)], Memi[O_VALP(in2)],
+ Memi[O_VALP(out)], nelem)
+ }
+
+ case TY_LONG:
+ if (O_LEN(in1) <= 0) {
+ O_VALL(out) = mod (O_VALL(in1), O_VALL(in2))
+ } else if (O_LEN(in2) <= 0) {
+ call amodkl (Meml[O_VALP(in1)], O_VALL(in2),
+ Meml[O_VALP(out)], nelem)
+ } else {
+ call amodl (Meml[O_VALP(in1)], Meml[O_VALP(in2)],
+ Meml[O_VALP(out)], nelem)
+ }
+
+ case TY_REAL:
+ if (O_LEN(in1) <= 0) {
+ O_VALR(out) = mod (O_VALR(in1), O_VALR(in2))
+ } else if (O_LEN(in2) <= 0) {
+ call amodkr (Memr[O_VALP(in1)], O_VALR(in2),
+ Memr[O_VALP(out)], nelem)
+ } else {
+ call amodr (Memr[O_VALP(in1)], Memr[O_VALP(in2)],
+ Memr[O_VALP(out)], nelem)
+ }
+
+ case TY_DOUBLE:
+ if (O_LEN(in1) <= 0) {
+ O_VALD(out) = mod (O_VALD(in1), O_VALD(in2))
+ } else if (O_LEN(in2) <= 0) {
+ call amodkd (Memd[O_VALP(in1)], O_VALD(in2),
+ Memd[O_VALP(out)], nelem)
+ } else {
+ call amodd (Memd[O_VALP(in1)], Memd[O_VALP(in2)],
+ Memd[O_VALP(out)], nelem)
+ }
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_MAX:
+ switch (optype) {
+
+ case TY_SHORT:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovks (O_VALS(ap), Mems[O_VALP(out)], nelem)
+ else
+ O_VALS(out) = O_VALS(ap)
+ } else
+ call amovs (Mems[O_VALP(ap)], Mems[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VALS(out) = max (O_VALS(ap), O_VALS(out))
+ else {
+ call amaxks (Mems[O_VALP(out)], O_VALS(ap),
+ Mems[O_VALP(out)], nelem)
+ }
+ } else {
+ call amaxs (Mems[O_VALP(out)], Mems[O_VALP(ap)],
+ Mems[O_VALP(out)], nelem)
+ }
+ }
+
+ case TY_INT:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovki (O_VALI(ap), Memi[O_VALP(out)], nelem)
+ else
+ O_VALI(out) = O_VALI(ap)
+ } else
+ call amovi (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VALI(out) = max (O_VALI(ap), O_VALI(out))
+ else {
+ call amaxki (Memi[O_VALP(out)], O_VALI(ap),
+ Memi[O_VALP(out)], nelem)
+ }
+ } else {
+ call amaxi (Memi[O_VALP(out)], Memi[O_VALP(ap)],
+ Memi[O_VALP(out)], nelem)
+ }
+ }
+
+ case TY_LONG:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovkl (O_VALL(ap), Meml[O_VALP(out)], nelem)
+ else
+ O_VALL(out) = O_VALL(ap)
+ } else
+ call amovl (Meml[O_VALP(ap)], Meml[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VALL(out) = max (O_VALL(ap), O_VALL(out))
+ else {
+ call amaxkl (Meml[O_VALP(out)], O_VALL(ap),
+ Meml[O_VALP(out)], nelem)
+ }
+ } else {
+ call amaxl (Meml[O_VALP(out)], Meml[O_VALP(ap)],
+ Meml[O_VALP(out)], nelem)
+ }
+ }
+
+ case TY_REAL:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovkr (O_VALR(ap), Memr[O_VALP(out)], nelem)
+ else
+ O_VALR(out) = O_VALR(ap)
+ } else
+ call amovr (Memr[O_VALP(ap)], Memr[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VALR(out) = max (O_VALR(ap), O_VALR(out))
+ else {
+ call amaxkr (Memr[O_VALP(out)], O_VALR(ap),
+ Memr[O_VALP(out)], nelem)
+ }
+ } else {
+ call amaxr (Memr[O_VALP(out)], Memr[O_VALP(ap)],
+ Memr[O_VALP(out)], nelem)
+ }
+ }
+
+ case TY_DOUBLE:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovkd (O_VALD(ap), Memd[O_VALP(out)], nelem)
+ else
+ O_VALD(out) = O_VALD(ap)
+ } else
+ call amovd (Memd[O_VALP(ap)], Memd[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VALD(out) = max (O_VALD(ap), O_VALD(out))
+ else {
+ call amaxkd (Memd[O_VALP(out)], O_VALD(ap),
+ Memd[O_VALP(out)], nelem)
+ }
+ } else {
+ call amaxd (Memd[O_VALP(out)], Memd[O_VALP(ap)],
+ Memd[O_VALP(out)], nelem)
+ }
+ }
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_MIN:
+ switch (optype) {
+
+ case TY_SHORT:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovks (O_VALS(ap), Mems[O_VALP(out)], nelem)
+ else
+ O_VALS(out) = O_VALS(ap)
+ } else
+ call amovs (Mems[O_VALP(ap)], Mems[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VALS(out) = min (O_VALS(ap), O_VALS(out))
+ else {
+ call aminks (Mems[O_VALP(out)], O_VALS(ap),
+ Mems[O_VALP(out)], nelem)
+ }
+ } else {
+ call amins (Mems[O_VALP(out)], Mems[O_VALP(ap)],
+ Mems[O_VALP(out)], nelem)
+ }
+ }
+
+ case TY_INT:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovki (O_VALI(ap), Memi[O_VALP(out)], nelem)
+ else
+ O_VALI(out) = O_VALI(ap)
+ } else
+ call amovi (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VALI(out) = min (O_VALI(ap), O_VALI(out))
+ else {
+ call aminki (Memi[O_VALP(out)], O_VALI(ap),
+ Memi[O_VALP(out)], nelem)
+ }
+ } else {
+ call amini (Memi[O_VALP(out)], Memi[O_VALP(ap)],
+ Memi[O_VALP(out)], nelem)
+ }
+ }
+
+ case TY_LONG:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovkl (O_VALL(ap), Meml[O_VALP(out)], nelem)
+ else
+ O_VALL(out) = O_VALL(ap)
+ } else
+ call amovl (Meml[O_VALP(ap)], Meml[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VALL(out) = min (O_VALL(ap), O_VALL(out))
+ else {
+ call aminkl (Meml[O_VALP(out)], O_VALL(ap),
+ Meml[O_VALP(out)], nelem)
+ }
+ } else {
+ call aminl (Meml[O_VALP(out)], Meml[O_VALP(ap)],
+ Meml[O_VALP(out)], nelem)
+ }
+ }
+
+ case TY_REAL:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovkr (O_VALR(ap), Memr[O_VALP(out)], nelem)
+ else
+ O_VALR(out) = O_VALR(ap)
+ } else
+ call amovr (Memr[O_VALP(ap)], Memr[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VALR(out) = min (O_VALR(ap), O_VALR(out))
+ else {
+ call aminkr (Memr[O_VALP(out)], O_VALR(ap),
+ Memr[O_VALP(out)], nelem)
+ }
+ } else {
+ call aminr (Memr[O_VALP(out)], Memr[O_VALP(ap)],
+ Memr[O_VALP(out)], nelem)
+ }
+ }
+
+ case TY_DOUBLE:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovkd (O_VALD(ap), Memd[O_VALP(out)], nelem)
+ else
+ O_VALD(out) = O_VALD(ap)
+ } else
+ call amovd (Memd[O_VALP(ap)], Memd[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VALD(out) = min (O_VALD(ap), O_VALD(out))
+ else {
+ call aminkd (Memd[O_VALP(out)], O_VALD(ap),
+ Memd[O_VALP(out)], nelem)
+ }
+ } else {
+ call amind (Memd[O_VALP(out)], Memd[O_VALP(ap)],
+ Memd[O_VALP(out)], nelem)
+ }
+ }
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_BOOL:
+ nelem = 0
+ if (O_LEN(ap) > 0 && O_TYPE(ap) != TY_CHAR)
+ nelem = O_LEN(ap)
+ call xvv_initop (out, nelem, TY_BOOL)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALI(ap)
+ else
+ call amovi (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_CHAR:
+ ch = O_VALC(ap)
+ O_VALI(out) = btoi (ch == 'y' || ch == 'Y')
+
+
+ case TY_SHORT:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = btoi (O_VALS(ap) != 0)
+ else {
+ v_s = 0
+ call abneks (Mems[O_VALP(ap)], v_s, Memi[O_VALP(out)],
+ nelem)
+ }
+
+ case TY_INT:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = btoi (O_VALI(ap) != 0)
+ else {
+ v_i = 0
+ call abneki (Memi[O_VALP(ap)], v_i, Memi[O_VALP(out)],
+ nelem)
+ }
+
+ case TY_LONG:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = btoi (O_VALL(ap) != 0)
+ else {
+ v_l = 0
+ call abnekl (Meml[O_VALP(ap)], v_l, Memi[O_VALP(out)],
+ nelem)
+ }
+
+ case TY_REAL:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = btoi (O_VALR(ap) != 0.0)
+ else {
+ v_r = 0.0
+ call abnekr (Memr[O_VALP(ap)], v_r, Memi[O_VALP(out)],
+ nelem)
+ }
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = btoi (O_VALD(ap) != 0.0D0)
+ else {
+ v_d = 0.0D0
+ call abnekd (Memd[O_VALP(ap)], v_d, Memi[O_VALP(out)],
+ nelem)
+ }
+
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_SHORT:
+ nelem = 0
+ if (O_LEN(ap) > 0 && O_TYPE(ap) != TY_CHAR)
+ nelem = O_LEN(ap)
+ call xvv_initop (out, nelem, TY_SHORT)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_LEN(ap) <= 0)
+ O_VALS(out) = O_VALI(ap)
+ else
+ call achtis (Memi[O_VALP(ap)], Mems[O_VALP(out)], nelem)
+
+ case TY_CHAR:
+ ip = O_VALP(ap)
+ if (gctod (Memc, ip, v_d) <= 0)
+ O_VALS(out) = 0
+ else
+ O_VALS(out) = v_d
+
+
+ case TY_SHORT:
+ if (O_LEN(ap) <= 0)
+ O_VALS(out) = O_VALS(ap)
+ else
+ call achtss (Mems[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_INT:
+ if (O_LEN(ap) <= 0)
+ O_VALS(out) = O_VALI(ap)
+ else
+ call achtis (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_LONG:
+ if (O_LEN(ap) <= 0)
+ O_VALS(out) = O_VALL(ap)
+ else
+ call achtls (Meml[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_REAL:
+ if (O_LEN(ap) <= 0)
+ O_VALS(out) = O_VALR(ap)
+ else
+ call achtrs (Memr[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) <= 0)
+ O_VALS(out) = O_VALD(ap)
+ else
+ call achtds (Memd[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_INT:
+ nelem = 0
+ if (O_LEN(ap) > 0 && O_TYPE(ap) != TY_CHAR)
+ nelem = O_LEN(ap)
+ call xvv_initop (out, nelem, TY_INT)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALI(ap)
+ else
+ call amovi (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_CHAR:
+ ip = O_VALP(ap)
+ if (gctod (Memc, ip, v_d) <= 0)
+ O_VALI(out) = 0
+ else
+ O_VALI(out) = v_d
+
+
+ case TY_SHORT:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALS(ap)
+ else
+ call achtsi (Mems[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_INT:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALI(ap)
+ else
+ call achtii (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_LONG:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALL(ap)
+ else
+ call achtli (Meml[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_REAL:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALR(ap)
+ else
+ call achtri (Memr[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALD(ap)
+ else
+ call achtdi (Memd[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_LONG:
+ nelem = 0
+ if (O_LEN(ap) > 0 && O_TYPE(ap) != TY_CHAR)
+ nelem = O_LEN(ap)
+ call xvv_initop (out, nelem, TY_LONG)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_LEN(ap) <= 0)
+ O_VALL(out) = O_VALI(ap)
+ else
+ call amovi (Memi[O_VALP(ap)], Meml[O_VALP(out)], nelem)
+
+ case TY_CHAR:
+ ip = O_VALP(ap)
+ if (gctod (Memc, ip, v_d) <= 0)
+ O_VALL(out) = 0
+ else
+ O_VALL(out) = v_d
+
+
+ case TY_SHORT:
+ if (O_LEN(ap) <= 0)
+ O_VALL(out) = O_VALS(ap)
+ else
+ call achtsl (Mems[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_INT:
+ if (O_LEN(ap) <= 0)
+ O_VALL(out) = O_VALI(ap)
+ else
+ call achtil (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_LONG:
+ if (O_LEN(ap) <= 0)
+ O_VALL(out) = O_VALL(ap)
+ else
+ call achtll (Meml[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_REAL:
+ if (O_LEN(ap) <= 0)
+ O_VALL(out) = O_VALR(ap)
+ else
+ call achtrl (Memr[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) <= 0)
+ O_VALL(out) = O_VALD(ap)
+ else
+ call achtdl (Memd[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_NINT:
+ nelem = 0
+ if (O_LEN(ap) > 0 && O_TYPE(ap) != TY_CHAR)
+ nelem = O_LEN(ap)
+ call xvv_initop (out, nelem, TY_INT)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALI(ap)
+ else
+ call amovi (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_CHAR:
+ ip = O_VALP(ap)
+ if (gctod (Memc, ip, v_d) <= 0)
+ O_VALI(out) = 0
+ else
+ O_VALI(out) = nint (v_d)
+
+
+ case TY_SHORT:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALS(ap)
+ else
+ call achtsi (Mems[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_INT:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALI(ap)
+ else
+ call achtii (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_LONG:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALL(ap)
+ else
+ call achtli (Meml[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+
+
+ case TY_REAL:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = nint (O_VALR(ap))
+ else {
+ do i = 1, nelem
+ Memi[O_VALP(out)+i-1] = nint (Memr[O_VALP(ap)+i-1])
+ }
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = nint (O_VALD(ap))
+ else {
+ do i = 1, nelem
+ Memi[O_VALP(out)+i-1] = nint (Memd[O_VALP(ap)+i-1])
+ }
+
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_REAL:
+ nelem = 0
+ if (O_LEN(ap) > 0 && O_TYPE(ap) != TY_CHAR)
+ nelem = O_LEN(ap)
+ call xvv_initop (out, nelem, TY_REAL)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_LEN(ap) <= 0)
+ O_VALR(out) = O_VALI(ap)
+ else
+ call achtir (Memi[O_VALP(ap)], Memr[O_VALP(out)], nelem)
+
+ case TY_CHAR:
+ ip = O_VALP(ap)
+ if (gctod (Memc, ip, v_d) <= 0)
+ O_VALR(out) = 0
+ else
+ O_VALR(out) = v_d
+
+
+ case TY_SHORT:
+ if (O_LEN(ap) <= 0)
+ O_VALR(out) = O_VALS(ap)
+ else
+ call achtsr (Mems[O_VALP(ap)], Memr[O_VALP(out)], nelem)
+
+ case TY_INT:
+ if (O_LEN(ap) <= 0)
+ O_VALR(out) = O_VALI(ap)
+ else
+ call achtir (Memi[O_VALP(ap)], Memr[O_VALP(out)], nelem)
+
+ case TY_LONG:
+ if (O_LEN(ap) <= 0)
+ O_VALR(out) = O_VALL(ap)
+ else
+ call achtlr (Meml[O_VALP(ap)], Memr[O_VALP(out)], nelem)
+
+ case TY_REAL:
+ if (O_LEN(ap) <= 0)
+ O_VALR(out) = O_VALR(ap)
+ else
+ call achtrr (Memr[O_VALP(ap)], Memr[O_VALP(out)], nelem)
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) <= 0)
+ O_VALR(out) = O_VALD(ap)
+ else
+ call achtdr (Memd[O_VALP(ap)], Memr[O_VALP(out)], nelem)
+
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_DOUBLE:
+ nelem = 0
+ if (O_LEN(ap) > 0 && O_TYPE(ap) != TY_CHAR)
+ nelem = O_LEN(ap)
+ call xvv_initop (out, nelem, TY_DOUBLE)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_LEN(ap) <= 0)
+ O_VALD(out) = O_VALI(ap)
+ else
+ call achtid (Memi[O_VALP(ap)], Memd[O_VALP(out)], nelem)
+
+ case TY_CHAR:
+ ip = O_VALP(ap)
+ if (gctod (Memc, ip, v_d) <= 0)
+ O_VALD(out) = 0
+ else
+ O_VALD(out) = v_d
+
+
+ case TY_SHORT:
+ if (O_LEN(ap) <= 0)
+ O_VALD(out) = O_VALS(ap)
+ else
+ call achtsd (Mems[O_VALP(ap)], Memd[O_VALP(out)], nelem)
+
+ case TY_INT:
+ if (O_LEN(ap) <= 0)
+ O_VALD(out) = O_VALI(ap)
+ else
+ call achtid (Memi[O_VALP(ap)], Memd[O_VALP(out)], nelem)
+
+ case TY_LONG:
+ if (O_LEN(ap) <= 0)
+ O_VALD(out) = O_VALL(ap)
+ else
+ call achtld (Meml[O_VALP(ap)], Memd[O_VALP(out)], nelem)
+
+ case TY_REAL:
+ if (O_LEN(ap) <= 0)
+ O_VALD(out) = O_VALR(ap)
+ else
+ call achtrd (Memr[O_VALP(ap)], Memd[O_VALP(out)], nelem)
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) <= 0)
+ O_VALD(out) = O_VALD(ap)
+ else
+ call achtdd (Memd[O_VALP(ap)], Memd[O_VALP(out)], nelem)
+
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_STR:
+ optype = TY_CHAR
+ if (O_TYPE(ap) == TY_CHAR)
+ nelem = strlen (O_VALC(ap))
+ else
+ nelem = MAX_DIGITS
+ call xvv_initop (out, nelem, TY_CHAR)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ call sprintf (O_VALC(out), nelem, "%b")
+ call pargi (O_VALI(ap))
+ case TY_CHAR:
+ call sprintf (O_VALC(out), nelem, "%s")
+ call pargstr (O_VALC(ap))
+
+ case TY_SHORT:
+ call sprintf (O_VALC(out), nelem, "%d")
+ call pargs (O_VALS(ap))
+
+ case TY_INT:
+ call sprintf (O_VALC(out), nelem, "%d")
+ call pargi (O_VALI(ap))
+
+ case TY_LONG:
+ call sprintf (O_VALC(out), nelem, "%d")
+ call pargl (O_VALL(ap))
+
+
+ case TY_REAL:
+ call sprintf (O_VALC(out), nelem, "%g")
+ call pargr (O_VALR(ap))
+
+ case TY_DOUBLE:
+ call sprintf (O_VALC(out), nelem, "%g")
+ call pargd (O_VALD(ap))
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ default:
+ call xvv_error ("callfcn: unknown function type")
+ }
+
+free_
+ # Free any storage used by the argument list operands.
+ do i = 1, nargs
+ call xvv_freeop (args[i])
+
+ call sfree (sp)
+end
+
+
+# XVV_STARTARGLIST -- Allocate an argument list descriptor to receive
+# arguments as a function call is parsed. We are called with either
+# zero or one arguments. The argument list descriptor is pointed to by
+# a ficticious operand. The descriptor itself contains a count of the
+# number of arguments, an array of pointers to the operand structures,
+# as well as storage for the operand structures. The operands must be
+# stored locally since the parser will discard its copy of the operand
+# structure for each argument as the associated grammar rule is reduced.
+
+procedure xvv_startarglist (arg, out)
+
+pointer arg #I pointer to first argument, or NULL
+pointer out #I output operand pointing to arg descriptor
+
+pointer ap
+errchk xvv_initop
+
+begin
+ call xvv_initop (out, LEN_ARGSTRUCT, TY_STRUCT)
+ ap = O_VALP(out)
+
+ if (arg == NULL)
+ A_NARGS(ap) = 0
+ else {
+ A_NARGS(ap) = 1
+ A_ARGP(ap,1) = A_OPS(ap)
+ YYMOVE (arg, A_OPS(ap))
+ }
+end
+
+
+# XVV_ADDARG -- Add an argument to the argument list for a function call.
+
+procedure xvv_addarg (arg, arglist, out)
+
+pointer arg #I pointer to argument to be added
+pointer arglist #I pointer to operand pointing to arglist
+pointer out #I output operand pointing to arg descriptor
+
+pointer ap, o
+int nargs
+
+begin
+ ap = O_VALP(arglist)
+
+ nargs = A_NARGS(ap) + 1
+ A_NARGS(ap) = nargs
+ if (nargs > MAX_ARGS)
+ call xvv_error ("too many function arguments")
+
+ o = A_OPS(ap) + ((nargs - 1) * LEN_OPERAND)
+ A_ARGP(ap,nargs) = o
+ YYMOVE (arg, o)
+
+ YYMOVE (arglist, out)
+end
+
+
+# XVV_ERROR1 -- Take an error action, formatting an error message with one
+# format string plus one string argument.
+
+procedure xvv_error1 (fmt, arg)
+
+char fmt[ARB] #I printf format string
+char arg[ARB] #I string argument
+
+pointer sp, buf
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_LINE, TY_CHAR)
+
+ call sprintf (Memc[buf], SZ_LINE, fmt)
+ call pargstr (arg)
+
+ call xvv_error (Memc[buf])
+ call sfree (sp)
+end
+
+
+# XVV_ERROR2 -- Take an error action, formatting an error message with one
+# format string plus one string argument and one integer argument.
+
+procedure xvv_error2 (fmt, arg1, arg2)
+
+char fmt[ARB] #I printf format string
+char arg1[ARB] #I string argument
+int arg2 #I integer argument
+
+pointer sp, buf
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_LINE, TY_CHAR)
+
+ call sprintf (Memc[buf], SZ_LINE, fmt)
+ call pargstr (arg1)
+ call pargi (arg2)
+
+ call xvv_error (Memc[buf])
+ call sfree (sp)
+end
+
+
+# XVV_ERROR -- Take an error action, given an error message string as the
+# sole argument.
+
+procedure xvv_error (errmsg)
+
+char errmsg[ARB] #I error message
+
+begin
+ call error (1, errmsg)
+end
+
+
+# XVV_CHTYPE -- Change the datatype of an operand. The input and output
+# operands may be the same.
+
+procedure xvv_chtype (o1, o2, dtype)
+
+pointer o1 #I input operand
+pointer o2 #I output operand
+int dtype #I new datatype
+
+short v_s
+int v_i
+long v_l
+real v_r
+double v_d
+pointer vp, ip, op
+bool float, freeval
+int old_type, nelem, ch
+
+pointer coerce()
+int sizeof(), btoi(), gctod()
+string s_badtype "chtype: invalid operand type"
+
+begin
+ old_type = O_TYPE(o1)
+ nelem = O_LEN(o1)
+
+ # No type conversion needed?
+ if (old_type == dtype) {
+ if (o1 != o2) {
+ if (nelem <= 0)
+ YYMOVE (o1, o2)
+ else {
+ call xvv_initop (o2, nelem, old_type)
+ call amovc (O_VALC(o1), O_VALC(o2), nelem * sizeof(dtype))
+ }
+ }
+ return
+ }
+
+ if (nelem <= 0) {
+ # Scalar input operand.
+
+ O_TYPE(o2) = dtype
+ O_LEN(o2) = 0
+ float = false
+
+ # Read the old value into a local variable of type long or double.
+ switch (old_type) {
+ case TY_BOOL:
+ v_l = O_VALI(o1)
+ case TY_CHAR:
+ v_l = 0 # null string?
+
+ case TY_SHORT:
+ v_l = O_VALS(o1)
+
+ case TY_INT:
+ v_l = O_VALI(o1)
+
+ case TY_LONG:
+ v_l = O_VALL(o1)
+
+
+ case TY_REAL:
+ v_d = O_VALR(o1)
+ float = true
+
+ case TY_DOUBLE:
+ v_d = O_VALD(o1)
+ float = true
+
+ default:
+ call xvv_error (s_badtype)
+ }
+
+ # Set the value of the output operand.
+ switch (dtype) {
+ case TY_BOOL:
+ if (float)
+ O_VALI(o2) = btoi (v_d != 0)
+ else
+ O_VALI(o2) = btoi (v_l != 0)
+ case TY_CHAR:
+ call xvv_initop (o2, MAX_DIGITS, TY_CHAR)
+ if (float) {
+ call sprintf (O_VALC(o2), MAX_DIGITS, "%g")
+ call pargd (v_d)
+ } else {
+ call sprintf (O_VALC(o2), MAX_DIGITS, "%d")
+ call pargl (v_l)
+ }
+
+ case TY_SHORT:
+ if (float)
+ O_VALS(o2) = v_d
+ else
+ O_VALS(o2) = v_l
+
+ case TY_INT:
+ if (float)
+ O_VALI(o2) = v_d
+ else
+ O_VALI(o2) = v_l
+
+ case TY_LONG:
+ if (float)
+ O_VALL(o2) = v_d
+ else
+ O_VALL(o2) = v_l
+
+
+ case TY_REAL:
+ if (float)
+ O_VALR(o2) = v_d
+ else
+ O_VALR(o2) = v_l
+
+ case TY_DOUBLE:
+ if (float)
+ O_VALD(o2) = v_d
+ else
+ O_VALD(o2) = v_l
+
+ default:
+ call xvv_error (s_badtype)
+ }
+
+ } else {
+ # Vector input operand.
+
+ # Save a pointer to the input operand data vector, to avoid it
+ # getting clobbered if O1 and O2 are the same operand.
+
+ vp = O_VALP(o1)
+
+ # If we have a char string input operand the output numeric
+ # operand can only be a scalar. If we have a char string output
+ # operand nelem is the length of the string.
+
+ if (old_type == TY_CHAR)
+ nelem = 0
+ else if (dtype == TY_CHAR)
+ nelem = MAX_DIGITS
+
+ # Initialize the output operand O2. The freeval flag is cleared
+ # cleared to keep the initop from freeing the input operand array,
+ # inherited when the input operand is copied (or when the input
+ # and output operands are the same). We free the old operand
+ # array manually below.
+
+ if (o1 != o2)
+ YYMOVE (o1, o2)
+ freeval = (and (O_FLAGS(o1), O_FREEVAL) != 0)
+ O_FLAGS(o2) = and (O_FLAGS(o2), not(O_FREEVAL))
+ call xvv_initop (o2, nelem, dtype)
+
+ # Write output value.
+ switch (dtype) {
+ case TY_BOOL:
+ if (old_type == TY_CHAR) {
+ ch = Memc[vp]
+ O_VALI(o2) = btoi (ch == 'y' || ch == 'Y')
+ } else {
+ switch (old_type) {
+
+ case TY_SHORT:
+ v_s = 0
+ call abneks (Mems[vp], v_s, Memi[O_VALP(o2)], nelem)
+
+ case TY_INT:
+ v_i = 0
+ call abneki (Memi[vp], v_i, Memi[O_VALP(o2)], nelem)
+
+ case TY_LONG:
+ v_l = 0
+ call abnekl (Meml[vp], v_l, Memi[O_VALP(o2)], nelem)
+
+ case TY_REAL:
+ v_r = 0.0
+ call abnekr (Memr[vp], v_r, Memi[O_VALP(o2)], nelem)
+
+ case TY_DOUBLE:
+ v_d = 0.0D0
+ call abnekd (Memd[vp], v_d, Memi[O_VALP(o2)], nelem)
+
+ default:
+ call xvv_error (s_badtype)
+ }
+ }
+
+ case TY_CHAR:
+ call xvv_error (s_badtype)
+
+ case TY_SHORT, TY_INT, TY_LONG, TY_REAL, TY_DOUBLE:
+ switch (old_type) {
+ case TY_BOOL:
+ op = coerce (O_VALP(o2), O_TYPE(o2), TY_CHAR)
+ call achti (Memi[vp], Memc[op], nelem, dtype)
+ case TY_CHAR:
+ ip = vp
+ if (gctod (Memc, ip, v_d) <= 0)
+ v_d = 0
+ switch (dtype) {
+
+ case TY_SHORT:
+ O_VALS(o2) = v_d
+
+ case TY_INT:
+ O_VALI(o2) = v_d
+
+ case TY_LONG:
+ O_VALL(o2) = v_d
+
+ case TY_REAL:
+ O_VALR(o2) = v_d
+
+ case TY_DOUBLE:
+ O_VALD(o2) = v_d
+
+ }
+
+ case TY_SHORT:
+ op = coerce (O_VALP(o2), O_TYPE(o2), TY_CHAR)
+ call achts (Mems[vp], Memc[op], nelem, dtype)
+
+ case TY_INT:
+ op = coerce (O_VALP(o2), O_TYPE(o2), TY_CHAR)
+ call achti (Memi[vp], Memc[op], nelem, dtype)
+
+ case TY_LONG:
+ op = coerce (O_VALP(o2), O_TYPE(o2), TY_CHAR)
+ call achtl (Meml[vp], Memc[op], nelem, dtype)
+
+ case TY_REAL:
+ op = coerce (O_VALP(o2), O_TYPE(o2), TY_CHAR)
+ call achtr (Memr[vp], Memc[op], nelem, dtype)
+
+ case TY_DOUBLE:
+ op = coerce (O_VALP(o2), O_TYPE(o2), TY_CHAR)
+ call achtd (Memd[vp], Memc[op], nelem, dtype)
+
+ default:
+ call xvv_error (s_badtype)
+ }
+ default:
+ call xvv_error (s_badtype)
+ }
+
+ # Free old operand value.
+ if (freeval)
+ call mfree (vp, old_type)
+ }
+end
+
+
+# XVV_INITOP -- Initialize an operand, providing storage for an operand value
+# of the given size and type.
+
+procedure xvv_initop (o, o_len, o_type)
+
+pointer o #I pointer to operand structure
+int o_len #I length of operand (zero if scalar)
+int o_type #I datatype of operand
+
+begin
+ O_LEN(o) = 0
+ call xvv_makeop (o, o_len, o_type)
+end
+
+
+# XVV_MAKEOP -- Set up the operand structure. If the operand structure has
+# already been initialized and array storage allocated, free the old array.
+
+procedure xvv_makeop (o, o_len, o_type)
+
+pointer o #I pointer to operand structure
+int o_len #I length of operand (zero if scalar)
+int o_type #I datatype of operand
+
+errchk malloc
+
+begin
+ # Free old array storage if any.
+ if (O_TYPE(o) != 0 && O_LEN(o) > 0)
+ if (and (O_FLAGS(o), O_FREEVAL) != 0) {
+ if (O_TYPE(o) == TY_BOOL)
+ call mfree (O_VALP(o), TY_INT)
+ else
+ call mfree (O_VALP(o), O_TYPE(o))
+ O_LEN(o) = 0
+ }
+
+ # Set new operand type.
+ O_TYPE(o) = o_type
+
+ # Allocate array storage if nonscalar operand.
+ if (o_len > 0) {
+ if (o_type == TY_BOOL)
+ call malloc (O_VALP(o), o_len, TY_INT)
+ else
+ call malloc (O_VALP(o), o_len, o_type)
+ O_LEN(o) = o_len
+ }
+
+ O_FLAGS(o) = O_FREEVAL
+end
+
+
+# XVV_FREEOP -- Reinitialize an operand structure, i.e., free any associated
+# array storage and clear the operand datatype field, but do not free the
+# operand structure itself (which may be only a segment of an array and not
+# a separately allocated structure).
+
+procedure xvv_freeop (o)
+
+pointer o #I pointer to operand structure
+
+begin
+ # Free old array storage if any.
+ if (O_TYPE(o) != 0 && O_LEN(o) > 0)
+ if (and (O_FLAGS(o), O_FREEVAL) != 0) {
+ if (O_TYPE(o) == TY_BOOL)
+ call mfree (O_VALP(o), TY_INT)
+ else
+ call mfree (O_VALP(o), O_TYPE(o))
+ O_LEN(o) = 0
+ }
+
+ # Either free operand struct or clear the operand type to mark
+ # operand invalid.
+
+ if (and (O_FLAGS(o), O_FREEOP) != 0)
+ call mfree (o, TY_STRUCT)
+ else
+ O_TYPE(o) = 0
+end
+
+
+# XVV_LOADSYMBOLS -- Load a list of symbol names into a symbol table. Each
+# symbol is tagged with an integer code corresponding to its sequence number
+# in the symbol list.
+
+pointer procedure xvv_loadsymbols (s)
+
+char s[ARB] #I symbol list "|sym1|sym2|...|"
+
+int delim, symnum, ip
+pointer sp, symname, st, sym, op
+pointer stopen(), stenter()
+
+begin
+ call smark (sp)
+ call salloc (symname, SZ_FNAME, TY_CHAR)
+
+ st = stopen ("evvexpr", LEN_INDEX, LEN_STAB, LEN_SBUF)
+ delim = s[1]
+ symnum = 0
+
+ for (ip=2; s[ip] != EOS; ip=ip+1) {
+ op = symname
+ while (s[ip] != delim && s[ip] != EOS) {
+ Memc[op] = s[ip]
+ op = op + 1
+ ip = ip + 1
+ }
+ Memc[op] = EOS
+ symnum = symnum + 1
+
+ if (op > symname && IS_ALPHA(Memc[symname])) {
+ sym = stenter (st, Memc[symname], LEN_SYM)
+ SYM_CODE(sym) = symnum
+ }
+ }
+
+ call sfree (sp)
+ return (st)
+end
+
+
+# XVV_NULL -- Return a null value to be used when a computation cannot be
+# performed and range checking is enabled. Perhaps we should permit a user
+# specified value here, however this doesn't really work in an expression
+# evaluator since the value generated may be used in subsequent calculations
+# and hence may change. If more careful treatment of out of range values
+# is needed a conditional expression can be used in which case the value
+# we return here is ignored (but still needed to avoid a hardware exception
+# when computing a vector).
+
+
+short procedure xvv_nulls (ignore)
+short ignore #I ignored
+begin
+ return (0)
+end
+
+int procedure xvv_nulli (ignore)
+int ignore #I ignored
+begin
+ return (0)
+end
+
+long procedure xvv_nulll (ignore)
+long ignore #I ignored
+begin
+ return (0)
+end
+
+real procedure xvv_nullr (ignore)
+real ignore #I ignored
+begin
+ return (0.0)
+end
+
+double procedure xvv_nulld (ignore)
+double ignore #I ignored
+begin
+ return (0.0D0)
+end
+
+define YYNPROD 39
+define YYLAST 303
+# line 1 "/iraf/iraf/lib/yaccpar.x"
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# Parser for yacc output, translated to the IRAF SPP language. The contents
+# of this file form the bulk of the source of the parser produced by Yacc.
+# Yacc recognizes several macros in the yaccpar input source and replaces
+# them as follows:
+# A user suppled "global" definitions and declarations
+# B parser tables
+# C user supplied actions (reductions)
+# The remainder of the yaccpar code is not changed.
+
+define yystack_ 10 # statement labels for gotos
+define yynewstate_ 20
+define yydefault_ 30
+define yyerrlab_ 40
+define yyabort_ 50
+
+define YYFLAG (-1000) # defs used in user actions
+define YYERROR goto yyerrlab_
+define YYACCEPT return (OK)
+define YYABORT return (ERR)
+
+
+# YYPARSE -- Parse the input stream, returning OK if the source is
+# syntactically acceptable (i.e., if compilation is successful),
+# otherwise ERR. The parameters YYMAXDEPTH and YYOPLEN must be
+# supplied by the caller in the %{ ... %} section of the Yacc source.
+# The token value stack is a dynamically allocated array of operand
+# structures, with the length and makeup of the operand structure being
+# application dependent.
+
+int procedure yyparse (fd, yydebug, yylex)
+
+int fd # stream to be parsed
+bool yydebug # print debugging information?
+int yylex() # user-supplied lexical input function
+extern yylex()
+
+short yys[YYMAXDEPTH] # parser stack -- stacks tokens
+pointer yyv # pointer to token value stack
+pointer yyval # value returned by action
+pointer yylval # value of token
+int yyps # token stack pointer
+pointer yypv # value stack pointer
+int yychar # current input token number
+int yyerrflag # error recovery flag
+int yynerrs # number of errors
+
+short yyj, yym # internal variables
+pointer yysp, yypvt
+short yystate, yyn
+int yyxi, i
+errchk salloc, yylex
+
+
+# XVV_PARSE -- SPP/Yacc parser for the evaluation of an expression passed as
+# a text string. Expression evaluation is carried out as the expression is
+# parsed, rather than being broken into separate compile and execute stages.
+# There is only one statement in this grammar, the expression. Our function
+# is to reduce an expression to a single value of type bool, string, int,
+# or real.
+
+pointer ap
+bool streq()
+errchk zcall3, xvv_error1, xvv_unop, xvv_binop, xvv_boolop
+errchk xvv_quest, xvv_callfcn, xvv_addarg
+include "evvexpr.com"
+
+short yyexca[96]
+data (yyexca(i),i= 1, 8) / -1, 1, 0, -1, -2, 0, -1, 5/
+data (yyexca(i),i= 9, 16) / 40, 33, -2, 5, -1, 6, 40, 32/
+data (yyexca(i),i= 17, 24) / -2, 6, -1, 76, 269, 0, 270, 0/
+data (yyexca(i),i= 25, 32) / 271, 0, 283, 0, -2, 22, -1, 77/
+data (yyexca(i),i= 33, 40) / 269, 0, 270, 0, 271, 0, 283, 0/
+data (yyexca(i),i= 41, 48) / -2, 23, -1, 78, 269, 0, 270, 0/
+data (yyexca(i),i= 49, 56) / 271, 0, 283, 0, -2, 24, -1, 79/
+data (yyexca(i),i= 57, 64) / 269, 0, 270, 0, 271, 0, 283, 0/
+data (yyexca(i),i= 65, 72) / -2, 25, -1, 80, 272, 0, 273, 0/
+data (yyexca(i),i= 73, 80) / 274, 0, -2, 26, -1, 81, 272, 0/
+data (yyexca(i),i= 81, 88) / 273, 0, 274, 0, -2, 27, -1, 82/
+data (yyexca(i),i= 89, 96) / 272, 0, 273, 0, 274, 0, -2, 28/
+short yyact[303]
+data (yyact(i),i= 1, 8) / 15, 16, 17, 18, 19, 20, 33, 86/
+data (yyact(i),i= 9, 16) / 26, 27, 28, 30, 32, 31, 21, 22/
+data (yyact(i),i= 17, 24) / 62, 23, 24, 25, 19, 34, 29, 15/
+data (yyact(i),i= 25, 32) / 16, 17, 18, 19, 20, 33, 38, 26/
+data (yyact(i),i= 33, 40) / 27, 28, 30, 32, 31, 21, 22, 60/
+data (yyact(i),i= 41, 48) / 23, 24, 25, 12, 11, 29, 15, 16/
+data (yyact(i),i= 49, 56) / 17, 18, 19, 20, 12, 2, 26, 27/
+data (yyact(i),i= 57, 64) / 28, 30, 32, 31, 12, 1, 0, 23/
+data (yyact(i),i= 65, 72) / 24, 25, 0, 14, 29, 15, 16, 17/
+data (yyact(i),i= 73, 80) / 18, 19, 20, 0, 0, 26, 27, 28/
+data (yyact(i),i= 81, 88) / 30, 32, 31, 0, 15, 16, 17, 18/
+data (yyact(i),i= 89, 96) / 19, 20, 0, 29, 26, 27, 28, 15/
+data (yyact(i),i= 97,104) / 16, 17, 18, 19, 20, 15, 16, 17/
+data (yyact(i),i=105,112) / 18, 19, 29, 17, 18, 19, 4, 0/
+data (yyact(i),i=113,120) / 84, 0, 40, 85, 0, 0, 0, 35/
+data (yyact(i),i=121,128) / 36, 37, 0, 39, 0, 0, 0, 0/
+data (yyact(i),i=129,136) / 0, 0, 41, 42, 43, 44, 45, 46/
+data (yyact(i),i=137,144) / 47, 48, 49, 50, 51, 52, 53, 54/
+data (yyact(i),i=145,152) / 55, 56, 57, 58, 59, 61, 0, 63/
+data (yyact(i),i=153,160) / 65, 66, 67, 68, 69, 70, 71, 72/
+data (yyact(i),i=161,168) / 73, 74, 75, 76, 77, 78, 79, 80/
+data (yyact(i),i=169,176) / 81, 82, 83, 0, 0, 0, 0, 0/
+data (yyact(i),i=177,184) / 0, 0, 0, 0, 0, 0, 0, 0/
+data (yyact(i),i=185,192) / 0, 0, 0, 0, 0, 0, 0, 0/
+data (yyact(i),i=193,200) / 0, 0, 0, 0, 0, 0, 89, 90/
+data (yyact(i),i=201,208) / 87, 88, 0, 0, 0, 0, 0, 0/
+data (yyact(i),i=209,216) / 0, 0, 0, 0, 0, 0, 0, 0/
+data (yyact(i),i=217,224) / 0, 0, 0, 0, 0, 0, 0, 0/
+data (yyact(i),i=225,232) / 0, 0, 0, 0, 0, 0, 0, 0/
+data (yyact(i),i=233,240) / 0, 0, 0, 0, 15, 16, 17, 18/
+data (yyact(i),i=241,248) / 19, 20, 33, 0, 26, 27, 28, 30/
+data (yyact(i),i=249,256) / 32, 31, 21, 22, 0, 23, 24, 25/
+data (yyact(i),i=257,264) / 0, 0, 29, 0, 5, 6, 64, 0/
+data (yyact(i),i=265,272) / 0, 8, 0, 0, 3, 5, 6, 0/
+data (yyact(i),i=273,280) / 0, 0, 8, 0, 0, 5, 6, 0/
+data (yyact(i),i=281,288) / 9, 0, 8, 13, 10, 7, 0, 0/
+data (yyact(i),i=289,296) / 0, 9, 0, 0, 0, 10, 7, 0/
+data (yyact(i),i=297,303) / 0, 9, 0, 0, 0, 10, 7/
+short yypact[91]
+data (yypact(i),i= 1, 8) / 12,-1000, 23,-1000,-238,-1000,-1000,-236/
+data (yypact(i),i= 9, 16) / 20, 20, 20, -10, 20,-1000,-1000,-1000/
+data (yypact(i),i= 17, 24) /-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000/
+data (yypact(i),i= 25, 32) /-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000/
+data (yypact(i),i= 33, 40) /-1000,-1000,-1000,-245,-245,-245, 20, -25/
+data (yypact(i),i= 41, 48) / 3, 3, 3, 3, 3, 3, 3, 3/
+data (yypact(i),i= 49, 56) / 3, 3, 3, 3, 3, 3, 3, 3/
+data (yypact(i),i= 57, 64) / 3, 3, 3, 3, 71,-238,-1000,-238/
+data (yypact(i),i= 65, 72) /-1000,-156,-156,-245,-245,-1000,-160,-215/
+data (yypact(i),i= 73, 80) /-215,-192,-192,-192,-166,-166,-166,-166/
+data (yypact(i),i= 81, 88) /-177,-177,-177,-261,-1000,-1000,-1000, 3/
+data (yypact(i),i= 89, 91) / 3,-238,-238/
+short yypgo[7]
+data (yypgo(i),i= 1, 7) / 0, 61, 53, 110, 114, 44, 39/
+short yyr1[39]
+data (yyr1(i),i= 1, 8) / 0, 1, 1, 2, 2, 3, 3, 3/
+data (yyr1(i),i= 9, 16) / 3, 3, 3, 3, 3, 3, 3, 3/
+data (yyr1(i),i= 17, 24) / 3, 3, 3, 3, 3, 3, 3, 3/
+data (yyr1(i),i= 25, 32) / 3, 3, 3, 3, 3, 3, 3, 3/
+data (yyr1(i),i= 33, 39) / 5, 5, 6, 6, 6, 4, 4/
+short yyr2[39]
+data (yyr2(i),i= 1, 8) / 0, 2, 1, 1, 4, 1, 1, 2/
+data (yyr2(i),i= 9, 16) / 2, 2, 2, 4, 4, 4, 4, 4/
+data (yyr2(i),i= 17, 24) / 4, 4, 4, 4, 4, 4, 4, 4/
+data (yyr2(i),i= 25, 32) / 4, 4, 4, 4, 4, 7, 4, 3/
+data (yyr2(i),i= 33, 39) / 1, 1, 0, 1, 4, 0, 2/
+short yychk[91]
+data (yychk(i),i= 1, 8) /-1000, -1, -2, 256, -3, 257, 258, 282/
+data (yychk(i),i= 9, 16) / 262, 277, 281, -5, 40, 260, 44, 261/
+data (yychk(i),i= 17, 24) / 262, 263, 264, 265, 266, 275, 276, 278/
+data (yychk(i),i= 25, 32) / 279, 280, 269, 270, 271, 283, 272, 274/
+data (yychk(i),i= 33, 40) / 273, 267, 257, -3, -3, -3, 40, -3/
+data (yychk(i),i= 41, 48) / -4, -4, -4, -4, -4, -4, -4, -4/
+data (yychk(i),i= 49, 56) / -4, -4, -4, -4, -4, -4, -4, -4/
+data (yychk(i),i= 57, 64) / -4, -4, -4, -4, -6, -3, 41, -3/
+data (yychk(i),i= 65, 72) / 259, -3, -3, -3, -3, -3, -3, -3/
+data (yychk(i),i= 73, 80) / -3, -3, -3, -3, -3, -3, -3, -3/
+data (yychk(i),i= 81, 88) / -3, -3, -3, -3, 41, 44, 268, -4/
+data (yychk(i),i= 89, 91) / -4, -3, -3/
+short yydef[91]
+data (yydef(i),i= 1, 8) / 0, -2, 0, 2, 3, -2, -2, 0/
+data (yydef(i),i= 9, 16) / 0, 0, 0, 0, 0, 1, 37, 37/
+data (yydef(i),i= 17, 24) / 37, 37, 37, 37, 37, 37, 37, 37/
+data (yydef(i),i= 25, 32) / 37, 37, 37, 37, 37, 37, 37, 37/
+data (yydef(i),i= 33, 40) / 37, 37, 7, 8, 9, 10, 34, 0/
+data (yydef(i),i= 41, 48) / 0, 0, 0, 0, 0, 0, 0, 0/
+data (yydef(i),i= 49, 56) / 0, 0, 0, 0, 0, 0, 0, 0/
+data (yydef(i),i= 57, 64) / 0, 0, 0, 0, 0, 35, 31, 4/
+data (yydef(i),i= 65, 72) / 38, 11, 12, 13, 14, 15, 16, 17/
+data (yydef(i),i= 73, 80) / 18, 19, 20, 21, -2, -2, -2, -2/
+data (yydef(i),i= 81, 88) / -2, -2, -2, 0, 30, 37, 37, 0/
+data (yydef(i),i= 89, 91) / 0, 36, 29/
+
+begin
+ call smark (yysp)
+ call salloc (yyv, (YYMAXDEPTH+2) * YYOPLEN, TY_STRUCT)
+
+ # Initialization. The first element of the dynamically allocated
+ # token value stack (yyv) is used for yyval, the second for yylval,
+ # and the actual stack starts with the third element.
+
+ yystate = 0
+ yychar = -1
+ yynerrs = 0
+ yyerrflag = 0
+ yyps = 0
+ yyval = yyv
+ yylval = yyv + YYOPLEN
+ yypv = yylval
+
+yystack_
+ # SHIFT -- Put a state and value onto the stack. The token and
+ # value stacks are logically the same stack, implemented as two
+ # separate arrays.
+
+ if (yydebug) {
+ call printf ("state %d, char 0%o\n")
+ call pargs (yystate)
+ call pargi (yychar)
+ }
+ yyps = yyps + 1
+ yypv = yypv + YYOPLEN
+ if (yyps > YYMAXDEPTH) {
+ call sfree (yysp)
+ call eprintf ("yacc stack overflow\n")
+ return (ERR)
+ }
+ yys[yyps] = yystate
+ YYMOVE (yyval, yypv)
+
+yynewstate_
+ # Process the new state.
+ yyn = yypact[yystate+1]
+
+ if (yyn <= YYFLAG)
+ goto yydefault_ # simple state
+
+ # The variable "yychar" is the lookahead token.
+ if (yychar < 0) {
+ yychar = yylex (fd, yylval)
+ if (yychar < 0)
+ yychar = 0
+ }
+ yyn = yyn + yychar
+ if (yyn < 0 || yyn >= YYLAST)
+ goto yydefault_
+
+ yyn = yyact[yyn+1]
+ if (yychk[yyn+1] == yychar) { # valid shift
+ yychar = -1
+ YYMOVE (yylval, yyval)
+ yystate = yyn
+ if (yyerrflag > 0)
+ yyerrflag = yyerrflag - 1
+ goto yystack_
+ }
+
+yydefault_
+ # Default state action.
+
+ yyn = yydef[yystate+1]
+ if (yyn == -2) {
+ if (yychar < 0) {
+ yychar = yylex (fd, yylval)
+ if (yychar < 0)
+ yychar = 0
+ }
+
+ # Look through exception table.
+ yyxi = 1
+ while ((yyexca[yyxi] != (-1)) || (yyexca[yyxi+1] != yystate))
+ yyxi = yyxi + 2
+ for (yyxi=yyxi+2; yyexca[yyxi] >= 0; yyxi=yyxi+2) {
+ if (yyexca[yyxi] == yychar)
+ break
+ }
+
+ yyn = yyexca[yyxi+1]
+ if (yyn < 0) {
+ call sfree (yysp)
+ return (OK) # ACCEPT -- all done
+ }
+ }
+
+
+ # SYNTAX ERROR -- resume parsing if possible.
+
+ if (yyn == 0) {
+ switch (yyerrflag) {
+ case 0, 1, 2:
+ if (yyerrflag == 0) { # brand new error
+ call eprintf ("syntax error\n")
+yyerrlab_
+ yynerrs = yynerrs + 1
+ # fall through...
+ }
+
+ # case 1:
+ # case 2: incompletely recovered error ... try again
+ yyerrflag = 3
+
+ # Find a state where "error" is a legal shift action.
+ while (yyps >= 1) {
+ yyn = yypact[yys[yyps]+1] + YYERRCODE
+ if ((yyn >= 0) && (yyn < YYLAST) &&
+ (yychk[yyact[yyn+1]+1] == YYERRCODE)) {
+ # Simulate a shift of "error".
+ yystate = yyact[yyn+1]
+ goto yystack_
+ }
+ yyn = yypact[yys[yyps]+1]
+
+ # The current yyps has no shift on "error", pop stack.
+ if (yydebug) {
+ call printf ("error recovery pops state %d, ")
+ call pargs (yys[yyps])
+ call printf ("uncovers %d\n")
+ call pargs (yys[yyps-1])
+ }
+ yyps = yyps - 1
+ yypv = yypv - YYOPLEN
+ }
+
+ # ABORT -- There is no state on the stack with an error shift.
+yyabort_
+ call sfree (yysp)
+ return (ERR)
+
+
+ case 3: # No shift yet; clobber input char.
+
+ if (yydebug) {
+ call printf ("error recovery discards char %d\n")
+ call pargi (yychar)
+ }
+
+ if (yychar == 0)
+ goto yyabort_ # don't discard EOF, quit
+ yychar = -1
+ goto yynewstate_ # try again in the same state
+ }
+ }
+
+
+ # REDUCE -- Reduction by production yyn.
+
+ if (yydebug) {
+ call printf ("reduce %d\n")
+ call pargs (yyn)
+ }
+ yyps = yyps - yyr2[yyn+1]
+ yypvt = yypv
+ yypv = yypv - yyr2[yyn+1] * YYOPLEN
+ YYMOVE (yypv + YYOPLEN, yyval)
+ yym = yyn
+
+ # Consult goto table to find next state.
+ yyn = yyr1[yyn+1]
+ yyj = yypgo[yyn+1] + yys[yyps] + 1
+ if (yyj >= YYLAST)
+ yystate = yyact[yypgo[yyn+1]+1]
+ else {
+ yystate = yyact[yyj+1]
+ if (yychk[yystate+1] != -yyn)
+ yystate = yyact[yypgo[yyn+1]+1]
+ }
+
+ # Perform action associated with the grammar rule, if any.
+ switch (yym) {
+
+case 1:
+# line 266 "evvexpr.y"
+{
+ # Normal exit. Move the final expression value operand
+ # into the operand structure pointed to by the global
+ # variable ev_oval.
+
+ YYMOVE (yypvt-YYOPLEN, ev_oval)
+ call sfree (yysp)
+ return (OK)
+ }
+case 2:
+# line 275 "evvexpr.y"
+{
+ call error (1, "syntax error")
+ }
+case 3:
+# line 280 "evvexpr.y"
+{
+ YYMOVE (yypvt, yyval)
+ }
+case 4:
+# line 283 "evvexpr.y"
+{
+ YYMOVE (yypvt, yyval)
+ call xvv_freeop (yypvt-3*YYOPLEN)
+ }
+case 5:
+# line 289 "evvexpr.y"
+{
+ # Numeric constant.
+ YYMOVE (yypvt, yyval)
+ }
+case 6:
+# line 293 "evvexpr.y"
+{
+ # The boolean constants "yes" and "no" are implemented
+ # as reserved operands.
+
+ call xvv_initop (yyval, 0, TY_BOOL)
+ if (streq (O_VALC(yypvt), "yes")) {
+ O_VALI(yyval) = YES
+ } else if (streq (O_VALC(yypvt), "no")) {
+ O_VALI(yyval) = NO
+ } else if (ev_getop != NULL) {
+ call zcall3 (ev_getop,ev_getop_data, O_VALC(yypvt), yyval)
+ if (O_TYPE(yyval) <= 0)
+ call xvv_error1 ("unknown operand `%s'",
+ O_VALC(yypvt))
+ } else
+ call xvv_error1 ("illegal operand `%s'", O_VALC(yypvt))
+ call xvv_freeop (yypvt)
+ }
+case 7:
+# line 311 "evvexpr.y"
+{
+ # e.g., @"param"
+ if (ev_getop != NULL) {
+ call zcall3 (ev_getop,ev_getop_data, O_VALC(yypvt), yyval)
+ if (O_TYPE(yyval) <= 0)
+ call xvv_error1 ("unknown operand `%s'",
+ O_VALC(yypvt-YYOPLEN))
+ } else
+ call xvv_error1 ("illegal operand `%s'", O_VALC(yypvt))
+ call xvv_freeop (yypvt)
+ }
+case 8:
+# line 322 "evvexpr.y"
+{
+ # Unary arithmetic minus.
+ call xvv_unop (MINUS, yypvt, yyval)
+ }
+case 9:
+# line 326 "evvexpr.y"
+{
+ # Logical not.
+ call xvv_unop (LNOT, yypvt, yyval)
+ }
+case 10:
+# line 330 "evvexpr.y"
+{
+ # Boolean not.
+ call xvv_unop (BNOT, yypvt, yyval)
+ }
+case 11:
+# line 334 "evvexpr.y"
+{
+ # Addition.
+ call xvv_binop (PLUS, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 12:
+# line 338 "evvexpr.y"
+{
+ # Subtraction.
+ call xvv_binop (MINUS, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 13:
+# line 342 "evvexpr.y"
+{
+ # Multiplication.
+ call xvv_binop (STAR, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 14:
+# line 346 "evvexpr.y"
+{
+ # Division.
+ call xvv_binop (SLASH, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 15:
+# line 350 "evvexpr.y"
+{
+ # Exponentiation.
+ call xvv_binop (EXPON, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 16:
+# line 354 "evvexpr.y"
+{
+ # Concatenate two operands.
+ call xvv_binop (CONCAT, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 17:
+# line 358 "evvexpr.y"
+{
+ # Logical and.
+ call xvv_boolop (LAND, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 18:
+# line 362 "evvexpr.y"
+{
+ # Logical or.
+ call xvv_boolop (LOR, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 19:
+# line 366 "evvexpr.y"
+{
+ # Boolean and.
+ call xvv_binop (BAND, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 20:
+# line 370 "evvexpr.y"
+{
+ # Boolean or.
+ call xvv_binop (BOR, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 21:
+# line 374 "evvexpr.y"
+{
+ # Boolean xor.
+ call xvv_binop (BXOR, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 22:
+# line 378 "evvexpr.y"
+{
+ # Boolean less than.
+ call xvv_boolop (LT, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 23:
+# line 382 "evvexpr.y"
+{
+ # Boolean greater than.
+ call xvv_boolop (GT, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 24:
+# line 386 "evvexpr.y"
+{
+ # Boolean less than or equal.
+ call xvv_boolop (LE, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 25:
+# line 390 "evvexpr.y"
+{
+ # Boolean greater than or equal.
+ call xvv_boolop (GE, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 26:
+# line 394 "evvexpr.y"
+{
+ # Boolean equal.
+ call xvv_boolop (EQ, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 27:
+# line 398 "evvexpr.y"
+{
+ # String pattern-equal.
+ call xvv_boolop (SE, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 28:
+# line 402 "evvexpr.y"
+{
+ # Boolean not equal.
+ call xvv_boolop (NE, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 29:
+# line 406 "evvexpr.y"
+{
+ # Conditional expression.
+ call xvv_quest (yypvt-6*YYOPLEN, yypvt-3*YYOPLEN, yypvt, yyval)
+ }
+case 30:
+# line 410 "evvexpr.y"
+{
+ # Call an intrinsic or external function.
+ ap = O_VALP(yypvt-YYOPLEN)
+ call xvv_callfcn (O_VALC(yypvt-3*YYOPLEN),
+ A_ARGP(ap,1), A_NARGS(ap), yyval)
+ call xvv_freeop (yypvt-3*YYOPLEN)
+ call xvv_freeop (yypvt-YYOPLEN)
+ }
+case 31:
+# line 418 "evvexpr.y"
+{
+ YYMOVE (yypvt-YYOPLEN, yyval)
+ }
+case 32:
+# line 424 "evvexpr.y"
+{
+ YYMOVE (yypvt, yyval)
+ }
+case 33:
+# line 427 "evvexpr.y"
+{
+ if (O_TYPE(yypvt) != TY_CHAR)
+ call error (1, "illegal function name")
+ YYMOVE (yypvt, yyval)
+ }
+case 34:
+# line 435 "evvexpr.y"
+{
+ # Empty.
+ call xvv_startarglist (NULL, yyval)
+ }
+case 35:
+# line 439 "evvexpr.y"
+{
+ # First arg; start a nonnull list.
+ call xvv_startarglist (yypvt, yyval)
+ }
+case 36:
+# line 443 "evvexpr.y"
+{
+ # Add an argument to an existing list.
+ call xvv_addarg (yypvt, yypvt-3*YYOPLEN, yyval)
+ } }
+
+ goto yystack_ # stack new state and value
+end
diff --git a/sys/fmtio/evvexpr.y b/sys/fmtio/evvexpr.y
new file mode 100644
index 00000000..d6efa629
--- /dev/null
+++ b/sys/fmtio/evvexpr.y
@@ -0,0 +1,4644 @@
+%{
+include <lexnum.h>
+include <ctype.h>
+include <mach.h>
+include <math.h>
+include <evvexpr.h>
+
+.help evvexpr
+.nf --------------------------------------------------------------------------
+EVVEXPR.GY -- Generic XYacc source for a general vector expression evaluator.
+
+ o = evvexpr (expr, getop, getop_data, ufcn, ufcn_data, flags)
+ evvfree (o)
+
+Client callbacks:
+
+ getop (client_data, opname, out)
+ ufcn (client_data, fcn, args, nargs, out)
+
+here "out" is the output operand returned to EVVEXPR. Client_data is any
+arbitrary integer or pointer value passed in to EVVEXPR when by the client
+when the callback was registered. "args" is an array of operand structs,
+the arguments for the user function being called. If the operand or
+function call cannot be completed normally an error exit may be made (call
+error) or an invalid operand may be returned (O_TYPE set to 0). The client
+should not free the "args" input operands, this will be handled by EVVEXPR.
+
+Operand struct (lib$evvexpr.h):
+
+ struct operand {
+ int O_TYPE # operand type (bcsilrd)
+ int O_LEN # operand length (0=scalar)
+ int O_FLAGS # O_FREEVAL, O_FREEOP
+ union {
+ char* O_VALC # string
+ short O_VALS
+ int O_VALI # int or bool
+ long O_VALL
+ real O_VALR
+ double O_VALD
+ pointer O_VALP # vector data
+ }
+ }
+
+The macro O_VALC references the string value of a TY_CHAR operand. The
+flags are O_FREEVAL and O_FREEOP, which tell EVVEXPR and EVVFREE whether or
+not to free any vector operand array or the operand struct when the operand
+is freed. The client should set these flags on operands returned to EVVEXPR
+if it wants EVVEXPR to free any operand storage.
+
+Supported types are bool, char (string), and SILRD. Bool is indicated as
+TY_BOOL in the O_TYPE field of the operand struct, but is stored internally
+as an integer and the value field of a boolean operand is given by O_VALI.
+
+Operands may be either scalars or vectors. A vector is indicated by a O_LEN
+value greater than zero. For vector operands O_VALP points to the data array.
+A special case is TY_CHAR (string), in which case O_LEN is the allocated
+length of the EOS-terminated string. A string is logically a scalar value
+even though it is physically stored in the operand as a character vector.
+
+The trig functions operate upon angles in units of radians. The intrinsic
+functions RAD and DEG are available for converting between radians and
+degrees. A string can be coerced to a binary value and vice versa, using
+the INT, STR, etc. intrinsic functions.
+
+This is a generalization of the older EVEXPR routine, adding additional
+datatypes, support for vector operands, and numerous minor enhancements.
+.endhelp ---------------------------------------------------------------------
+
+define YYMAXDEPTH 64 # parser stack length
+define MAX_ARGS 17 # max args in a function call
+define yyparse xvv_parse
+
+# Arglist structure.
+define LEN_ARGSTRUCT (1+MAX_ARGS+(MAX_ARGS*LEN_OPERAND))
+define A_NARGS Memi[$1] # number of arguments
+define A_ARGP Memi[$1+$2] # array of pointers to operand structs
+define A_OPS ($1+MAX_ARGS+1) # offset to operand storage area
+
+# Intrinsic functions.
+
+define LEN_STAB 300 # for symbol table
+define LEN_SBUF 256
+define LEN_INDEX 97
+
+define LEN_SYM 1 # symbol data
+define SYM_CODE Memi[$1]
+
+define KEYWORDS "|abs|acos|asin|atan|atan2|bool|cos|cosh|deg|double|\
+ |exp|hiv|int|len|log|log10|long|lov|max|mean|median|\
+ |min|mod|nint|rad|real|repl|stddev|shift|short|sin|\
+ |sinh|sort|sqrt|str|sum|tan|tanh|"
+
+define F_ABS 01 # function codes
+define F_ACOS 02
+define F_ASIN 03
+define F_ATAN 04
+define F_ATAN2 05
+define F_BOOL 06
+define F_COS 07
+define F_COSH 08
+define F_DEG 09 # radians to degrees
+define F_DOUBLE 10
+ # newline 11
+define F_EXP 12
+define F_HIV 13 # high value
+define F_INT 14
+define F_LEN 15 # vector length
+define F_LOG 16
+define F_LOG10 17
+define F_LONG 18
+define F_LOV 19 # low value
+define F_MAX 20
+define F_MEAN 21
+define F_MEDIAN 22
+ # newline 23
+define F_MIN 24
+define F_MOD 25
+define F_NINT 26
+define F_RAD 27 # degrees to radians
+define F_REAL 28
+define F_REPL 29 # replicate
+define F_STDDEV 30 # standard deviation
+define F_SHIFT 31
+define F_SHORT 32
+define F_SIN 33
+ # newline 34
+define F_SINH 35
+define F_SORT 36 # sort
+define F_SQRT 37 # square root
+define F_STR 38
+define F_SUM 39
+define F_TAN 40
+define F_TANH 41
+
+define T_B TY_BOOL
+define T_C TY_CHAR
+define T_S TY_SHORT
+define T_I TY_INT
+define T_L TY_LONG
+define T_R TY_REAL
+define T_D TY_DOUBLE
+
+
+# EVVEXPR -- Evaluate a general mixed type vector expression. Input consists
+# of the expression to be evaluated (a string) and, optionally, user
+# procedures for fetching external operands and executing external functions.
+# Output is a pointer to an operand structure containing the computed value of
+# the expression. The output operand structure is dynamically allocated by
+# EVVEXPR and must be freed by the user.
+#
+# NOTE: this is not intended to be an especially efficient procedure. Rather,
+# this is a high level, easy to use procedure, intended to provide greater
+# flexibility in the parameterization of applications programs. The main
+# inefficiency is that, since compilation and execution are not broken out as
+# separate steps, when the routine is repeatedly called to evaluate the same
+# expression with different data, all the compile time computation (parsing
+# etc.) has to be repeated.
+
+pointer procedure evvexpr (expr, getop, getop_data, ufcn, ufcn_data, flags)
+
+char expr[ARB] #I expression to be evaluated
+int getop #I user supplied get operand procedure
+int getop_data #I client data for above function
+int ufcn #I user supplied function call procedure
+int ufcn_data #I client data for above function
+int flags #I flag bits
+
+int junk
+pointer sp, ip
+bool debug, first_time
+int strlen(), xvv_parse()
+pointer xvv_loadsymbols()
+extern xvv_gettok()
+
+errchk xvv_parse, calloc
+include "evvexpr.com"
+data debug /false/
+data first_time /true/
+
+begin
+ call smark (sp)
+
+ if (first_time) {
+ # This creates data which remains for the life of the process.
+ ev_st = xvv_loadsymbols (KEYWORDS)
+ first_time = false
+ }
+
+ # Set user function entry point addresses.
+ ev_getop = getop
+ ev_getop_data = getop_data
+ ev_ufcn = ufcn
+ ev_ufcn_data = ufcn_data
+ ev_flags = flags
+
+ # Allocate an operand struct for the expression value.
+ call calloc (ev_oval, LEN_OPERAND, TY_STRUCT)
+
+ # Make a local copy of the input string.
+ call salloc (ip, strlen(expr), TY_CHAR)
+ call strcpy (expr, Memc[ip], ARB)
+
+ # Evaluate the expression. The expression value is copied into the
+ # output operand structure by XVV_PARSE, given the operand pointer
+ # passed in common. A common must be used since the standard parser
+ # subroutine has a fixed calling sequence.
+
+ junk = xvv_parse (ip, debug, xvv_gettok)
+ O_FLAGS(ev_oval) = or (O_FLAGS(ev_oval), O_FREEOP)
+
+ call sfree (sp)
+ return (ev_oval)
+end
+
+
+# EVVFREE -- Free an operand struct such as is returned by EVVEXPR.
+
+procedure evvfree (o)
+
+pointer o # operand struct
+
+begin
+ call xvv_freeop (o)
+end
+
+%L
+# XVV_PARSE -- SPP/Yacc parser for the evaluation of an expression passed as
+# a text string. Expression evaluation is carried out as the expression is
+# parsed, rather than being broken into separate compile and execute stages.
+# There is only one statement in this grammar, the expression. Our function
+# is to reduce an expression to a single value of type bool, string, int,
+# or real.
+
+pointer ap
+bool streq()
+errchk zcall3, xvv_error1, xvv_unop, xvv_binop, xvv_boolop
+errchk xvv_quest, xvv_callfcn, xvv_addarg
+include "evvexpr.com"
+
+%}
+
+# The $/ following causes the generic preprocessor to pass this block of code
+# through unchanged.
+
+
+
+%token CONSTANT IDENTIFIER NEWLINE YYEOS
+%token PLUS MINUS STAR SLASH EXPON CONCAT QUEST COLON
+%token LT GT LE GT EQ NE SE LAND LOR LNOT BAND BOR BXOR BNOT AT
+
+%nonassoc QUEST
+%left LAND LOR
+%left BAND BOR BXOR
+%nonassoc EQ NE SE
+%nonassoc LT GT LE GE
+%left CONCAT
+%left PLUS MINUS
+%left STAR SLASH
+%right UMINUS LNOT BNOT
+%left EXPON
+%right AT
+
+%%
+
+stmt : exprlist YYEOS {
+ # Normal exit. Move the final expression value operand
+ # into the operand structure pointed to by the global
+ # variable ev_oval.
+
+ YYMOVE ($1, ev_oval)
+ call sfree (yysp)
+ return (OK)
+ }
+ | error {
+ call error (1, "syntax error")
+ }
+ ;
+
+exprlist: expr {
+ YYMOVE ($1, $$)
+ }
+ | exprlist ',' opnl expr {
+ YYMOVE ($4, $$)
+ call xvv_freeop ($1)
+ }
+
+
+expr : CONSTANT {
+ # Numeric constant.
+ YYMOVE ($1, $$)
+ }
+ | IDENTIFIER {
+ # The boolean constants "yes" and "no" are implemented
+ # as reserved operands.
+
+ call xvv_initop ($$, 0, TY_BOOL)
+ if (streq (O_VALC($1), "yes")) {
+ O_VALI($$) = YES
+ } else if (streq (O_VALC($1), "no")) {
+ O_VALI($$) = NO
+ } else if (ev_getop != NULL) {
+ call zcall3 (ev_getop,ev_getop_data, O_VALC($1), $$)
+ if (O_TYPE($$) <= 0)
+ call xvv_error1 ("unknown operand `%s'",
+ O_VALC($1))
+ } else
+ call xvv_error1 ("illegal operand `%s'", O_VALC($1))
+ call xvv_freeop ($1)
+ }
+ | AT CONSTANT {
+ # e.g., @"param"
+ if (ev_getop != NULL) {
+ call zcall3 (ev_getop,ev_getop_data, O_VALC($2), $$)
+ if (O_TYPE($$) <= 0)
+ call xvv_error1 ("unknown operand `%s'",
+ O_VALC($1))
+ } else
+ call xvv_error1 ("illegal operand `%s'", O_VALC($2))
+ call xvv_freeop ($2)
+ }
+ | MINUS expr %prec UMINUS {
+ # Unary arithmetic minus.
+ call xvv_unop (MINUS, $2, $$)
+ }
+ | LNOT expr {
+ # Logical not.
+ call xvv_unop (LNOT, $2, $$)
+ }
+ | BNOT expr {
+ # Boolean not.
+ call xvv_unop (BNOT, $2, $$)
+ }
+ | expr PLUS opnl expr {
+ # Addition.
+ call xvv_binop (PLUS, $1, $4, $$)
+ }
+ | expr MINUS opnl expr {
+ # Subtraction.
+ call xvv_binop (MINUS, $1, $4, $$)
+ }
+ | expr STAR opnl expr {
+ # Multiplication.
+ call xvv_binop (STAR, $1, $4, $$)
+ }
+ | expr SLASH opnl expr {
+ # Division.
+ call xvv_binop (SLASH, $1, $4, $$)
+ }
+ | expr EXPON opnl expr {
+ # Exponentiation.
+ call xvv_binop (EXPON, $1, $4, $$)
+ }
+ | expr CONCAT opnl expr {
+ # Concatenate two operands.
+ call xvv_binop (CONCAT, $1, $4, $$)
+ }
+ | expr LAND opnl expr {
+ # Logical and.
+ call xvv_boolop (LAND, $1, $4, $$)
+ }
+ | expr LOR opnl expr {
+ # Logical or.
+ call xvv_boolop (LOR, $1, $4, $$)
+ }
+ | expr BAND opnl expr {
+ # Boolean and.
+ call xvv_binop (BAND, $1, $4, $$)
+ }
+ | expr BOR opnl expr {
+ # Boolean or.
+ call xvv_binop (BOR, $1, $4, $$)
+ }
+ | expr BXOR opnl expr {
+ # Boolean xor.
+ call xvv_binop (BXOR, $1, $4, $$)
+ }
+ | expr LT opnl expr {
+ # Boolean less than.
+ call xvv_boolop (LT, $1, $4, $$)
+ }
+ | expr GT opnl expr {
+ # Boolean greater than.
+ call xvv_boolop (GT, $1, $4, $$)
+ }
+ | expr LE opnl expr {
+ # Boolean less than or equal.
+ call xvv_boolop (LE, $1, $4, $$)
+ }
+ | expr GE opnl expr {
+ # Boolean greater than or equal.
+ call xvv_boolop (GE, $1, $4, $$)
+ }
+ | expr EQ opnl expr {
+ # Boolean equal.
+ call xvv_boolop (EQ, $1, $4, $$)
+ }
+ | expr SE opnl expr {
+ # String pattern-equal.
+ call xvv_boolop (SE, $1, $4, $$)
+ }
+ | expr NE opnl expr {
+ # Boolean not equal.
+ call xvv_boolop (NE, $1, $4, $$)
+ }
+ | expr QUEST opnl expr COLON opnl expr {
+ # Conditional expression.
+ call xvv_quest ($1, $4, $7, $$)
+ }
+ | funct '(' arglist ')' {
+ # Call an intrinsic or external function.
+ ap = O_VALP($3)
+ call xvv_callfcn (O_VALC($1),
+ A_ARGP(ap,1), A_NARGS(ap), $$)
+ call xvv_freeop ($1)
+ call xvv_freeop ($3)
+ }
+ | '(' expr ')' {
+ YYMOVE ($2, $$)
+ }
+ ;
+
+
+funct : IDENTIFIER {
+ YYMOVE ($1, $$)
+ }
+ | CONSTANT {
+ if (O_TYPE($1) != TY_CHAR)
+ call error (1, "illegal function name")
+ YYMOVE ($1, $$)
+ }
+ ;
+
+
+arglist : {
+ # Empty.
+ call xvv_startarglist (NULL, $$)
+ }
+ | expr {
+ # First arg; start a nonnull list.
+ call xvv_startarglist ($1, $$)
+ }
+ | arglist ',' opnl expr {
+ # Add an argument to an existing list.
+ call xvv_addarg ($4, $1, $$)
+ }
+ ;
+
+
+opnl : # Empty.
+ | opnl NEWLINE
+ ;
+
+%%
+
+# End generic preprocessor escape.
+
+
+
+# XVV_UNOP -- Unary operation. Perform the indicated unary operation on the
+# input operand, returning the result as the output operand.
+
+procedure xvv_unop (opcode, in, out)
+
+int opcode #I operation to be performed
+pointer in #I input operand
+pointer out #I output operand
+
+short val_s
+long val_l
+int val_i, nelem
+errchk xvv_error, xvv_initop
+string s_badswitch "unop: bad switch"
+
+begin
+ nelem = O_LEN(in)
+
+ switch (opcode) {
+ case MINUS:
+ # Unary negation.
+ call xvv_initop (out, nelem, O_TYPE(in))
+ switch (O_TYPE(in)) {
+ case TY_BOOL, TY_CHAR:
+ call xvv_error ("negation of a nonarithmetic operand")
+
+ case TY_SHORT:
+ if (nelem > 0)
+ call anegs (Mems[O_VALP(in)], Mems[O_VALP(out)], nelem)
+ else
+ O_VALS(out) = -O_VALS(in)
+
+ case TY_INT:
+ if (nelem > 0)
+ call anegi (Memi[O_VALP(in)], Memi[O_VALP(out)], nelem)
+ else
+ O_VALI(out) = -O_VALI(in)
+
+ case TY_LONG:
+ if (nelem > 0)
+ call anegl (Meml[O_VALP(in)], Meml[O_VALP(out)], nelem)
+ else
+ O_VALL(out) = -O_VALL(in)
+
+ case TY_REAL:
+ if (nelem > 0)
+ call anegr (Memr[O_VALP(in)], Memr[O_VALP(out)], nelem)
+ else
+ O_VALR(out) = -O_VALR(in)
+
+ case TY_DOUBLE:
+ if (nelem > 0)
+ call anegd (Memd[O_VALP(in)], Memd[O_VALP(out)], nelem)
+ else
+ O_VALD(out) = -O_VALD(in)
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ case LNOT:
+ # Logical NOT.
+
+ call xvv_initop (out, nelem, TY_BOOL)
+ switch (O_TYPE(in)) {
+ case TY_BOOL:
+ if (nelem > 0)
+ call abeqki (Memi[O_VALP(in)], NO, Memi[O_VALP(out)], nelem)
+ else {
+ if (O_VALI(in) == NO)
+ O_VALI(out) = YES
+ else
+ O_VALI(out) = NO
+ }
+
+ case TY_SHORT:
+ if (nelem > 0) {
+ val_s = NO
+ call abeqks (Mems[O_VALP(in)], val_s, Memi[O_VALP(out)],
+ nelem)
+ } else {
+ if (O_VALS(in) == NO)
+ O_VALS(out) = YES
+ else
+ O_VALS(out) = NO
+ }
+
+ case TY_INT:
+ if (nelem > 0) {
+ val_i = NO
+ call abeqki (Memi[O_VALP(in)], val_i, Memi[O_VALP(out)],
+ nelem)
+ } else {
+ if (O_VALI(in) == NO)
+ O_VALI(out) = YES
+ else
+ O_VALI(out) = NO
+ }
+
+ case TY_LONG:
+ if (nelem > 0) {
+ val_l = NO
+ call abeqkl (Meml[O_VALP(in)], val_l, Memi[O_VALP(out)],
+ nelem)
+ } else {
+ if (O_VALL(in) == NO)
+ O_VALL(out) = YES
+ else
+ O_VALL(out) = NO
+ }
+
+ case TY_CHAR, TY_REAL, TY_DOUBLE:
+ call xvv_error ("not of a nonlogical")
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ case BNOT:
+ # Bitwise boolean NOT.
+
+ call xvv_initop (out, nelem, O_TYPE(in))
+ switch (O_TYPE(in)) {
+ case TY_BOOL, TY_CHAR, TY_REAL, TY_DOUBLE:
+ call xvv_error ("boolean not of a noninteger operand")
+
+ case TY_SHORT:
+ if (nelem > 0)
+ call anots (Mems[O_VALP(in)], Mems[O_VALP(out)], nelem)
+ else
+ O_VALS(out) = not(O_VALS(in))
+
+ case TY_INT:
+ if (nelem > 0)
+ call anoti (Memi[O_VALP(in)], Memi[O_VALP(out)], nelem)
+ else
+ O_VALI(out) = not(O_VALI(in))
+
+ case TY_LONG:
+ if (nelem > 0)
+ call anotl (Meml[O_VALP(in)], Meml[O_VALP(out)], nelem)
+ else
+ O_VALL(out) = not(O_VALL(in))
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ call xvv_freeop (in)
+end
+
+
+# XVV_BINOP -- Binary operation. Perform the indicated arithmetic binary
+# operation on the two input operands, returning the result as the output
+# operand.
+
+procedure xvv_binop (opcode, in1, in2, out)
+
+int opcode #I operation to be performed
+pointer in1, in2 #I input operands
+pointer out #I output operand
+
+
+short v_s
+short xvv_nulls()
+extern xvv_nulls()
+
+int v_i
+int xvv_nulli()
+extern xvv_nulli()
+
+long v_l
+long xvv_nulll()
+extern xvv_nulll()
+
+real v_r
+real xvv_nullr()
+extern xvv_nullr()
+
+double v_d
+double xvv_nulld()
+extern xvv_nulld()
+
+pointer sp, otemp, p1, p2, po
+int dtype, nelem, len1, len2
+include "evvexpr.com"
+
+int xvv_newtype(), strlen()
+errchk xvv_newtype, xvv_initop, xvv_chtype, xvv_error
+string s_badswitch "binop: bad case in switch"
+string s_boolop "binop: bitwise boolean operands must be an integer type"
+define done_ 91
+
+begin
+ # Set the datatype of the output operand, taking an error action if
+ # the operands have incompatible datatypes.
+
+ dtype = xvv_newtype (O_TYPE(in1), O_TYPE(in2))
+
+ # Compute the size of the output operand. If both input operands are
+ # vectors the length of the output vector is the shorter of the two.
+
+ switch (dtype) {
+ case TY_BOOL:
+ call xvv_error ("binop: operation illegal for boolean operands")
+ case TY_CHAR:
+ nelem = strlen (O_VALC(in1)) + strlen (O_VALC(in2))
+ default:
+ if (opcode == CONCAT)
+ nelem = max (1, O_LEN(in1)) + max (1, O_LEN(in2))
+ else {
+ if (O_LEN(in1) > 0 && O_LEN(in2) > 0)
+ nelem = min (O_LEN(in1), O_LEN(in2))
+ else if (O_LEN(in1) > 0)
+ nelem = O_LEN(in1)
+ else if (O_LEN(in2) > 0)
+ nelem = O_LEN(in2)
+ else
+ nelem = 0
+ }
+ }
+
+ # Convert input operands to desired type.
+ if (O_TYPE(in1) != dtype)
+ call xvv_chtype (in1, in1, dtype)
+ if (O_TYPE(in2) != dtype)
+ call xvv_chtype (in2, in2, dtype)
+
+ # If this is a scalar/vector operation make sure the vector is the
+ # first operand.
+
+ len1 = O_LEN(in1)
+ len2 = O_LEN(in2)
+
+ if (len1 == 0 && len2 > 0) {
+ switch (opcode) {
+ case PLUS:
+ # Swap operands.
+ call smark (sp)
+ call salloc (otemp, LEN_OPERAND, TY_STRUCT)
+ YYMOVE (in1, otemp)
+ YYMOVE (in2, in1)
+ YYMOVE (otemp, in2)
+ call sfree (sp)
+
+ case CONCAT:
+ ; # Do nothing
+
+ default:
+ # Promote operand to a constant vector. Inefficient, but
+ # better than aborting.
+
+ switch (dtype) {
+
+ case TY_SHORT:
+ v_s = O_VALS(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovks (v_s, Mems[O_VALP(in1)], nelem)
+
+ case TY_INT:
+ v_i = O_VALI(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovki (v_i, Memi[O_VALP(in1)], nelem)
+
+ case TY_LONG:
+ v_l = O_VALL(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovkl (v_l, Meml[O_VALP(in1)], nelem)
+
+ case TY_REAL:
+ v_r = O_VALR(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovkr (v_r, Memr[O_VALP(in1)], nelem)
+
+ case TY_DOUBLE:
+ v_d = O_VALD(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovkd (v_d, Memd[O_VALP(in1)], nelem)
+
+ }
+ }
+
+ len1 = O_LEN(in1)
+ len2 = O_LEN(in2)
+ }
+
+ # Initialize the output operand.
+ call xvv_initop (out, nelem, dtype)
+
+ p1 = O_VALP(in1)
+ p2 = O_VALP(in2)
+ po = O_VALP(out)
+
+ # The bitwise boolean binary operators a special case since only the
+ # integer datatypes are permitted. Otherwise the bitwise booleans
+ # are just like arithmetic booleans.
+
+ if (opcode == BAND || opcode == BOR || opcode == BXOR) {
+ switch (dtype) {
+
+ case TY_SHORT:
+ switch (opcode) {
+ case BAND:
+ if (len1 <= 0) {
+ O_VALS(out) = and (O_VALS(in1), O_VALS(in2))
+ } else if (len2 <= 0) {
+ call aandks (Mems[p1], O_VALS(in2),
+ Mems[po], nelem)
+ } else {
+ call aands (Mems[p1], Mems[p2],
+ Mems[po], nelem)
+ }
+ case BOR:
+ if (len1 <= 0) {
+ O_VALS(out) = or (O_VALS(in1), O_VALS(in2))
+ } else if (len2 <= 0) {
+ call aborks (Mems[p1], O_VALS(in2),
+ Mems[po], nelem)
+ } else {
+ call abors (Mems[p1], Mems[p2],
+ Mems[po], nelem)
+ }
+ case BXOR:
+ if (len1 <= 0) {
+ O_VALS(out) = xor (O_VALS(in1), O_VALS(in2))
+ } else if (len2 <= 0) {
+ call axorks (Mems[p1], O_VALS(in2),
+ Mems[po], nelem)
+ } else {
+ call axors (Mems[p1], Mems[p2],
+ Mems[po], nelem)
+ }
+ }
+
+ case TY_INT:
+ switch (opcode) {
+ case BAND:
+ if (len1 <= 0) {
+ O_VALI(out) = and (O_VALI(in1), O_VALI(in2))
+ } else if (len2 <= 0) {
+ call aandki (Memi[p1], O_VALI(in2),
+ Memi[po], nelem)
+ } else {
+ call aandi (Memi[p1], Memi[p2],
+ Memi[po], nelem)
+ }
+ case BOR:
+ if (len1 <= 0) {
+ O_VALI(out) = or (O_VALI(in1), O_VALI(in2))
+ } else if (len2 <= 0) {
+ call aborki (Memi[p1], O_VALI(in2),
+ Memi[po], nelem)
+ } else {
+ call abori (Memi[p1], Memi[p2],
+ Memi[po], nelem)
+ }
+ case BXOR:
+ if (len1 <= 0) {
+ O_VALI(out) = xor (O_VALI(in1), O_VALI(in2))
+ } else if (len2 <= 0) {
+ call axorki (Memi[p1], O_VALI(in2),
+ Memi[po], nelem)
+ } else {
+ call axori (Memi[p1], Memi[p2],
+ Memi[po], nelem)
+ }
+ }
+
+ case TY_LONG:
+ switch (opcode) {
+ case BAND:
+ if (len1 <= 0) {
+ O_VALL(out) = and (O_VALL(in1), O_VALL(in2))
+ } else if (len2 <= 0) {
+ call aandkl (Meml[p1], O_VALL(in2),
+ Meml[po], nelem)
+ } else {
+ call aandl (Meml[p1], Meml[p2],
+ Meml[po], nelem)
+ }
+ case BOR:
+ if (len1 <= 0) {
+ O_VALL(out) = or (O_VALL(in1), O_VALL(in2))
+ } else if (len2 <= 0) {
+ call aborkl (Meml[p1], O_VALL(in2),
+ Meml[po], nelem)
+ } else {
+ call aborl (Meml[p1], Meml[p2],
+ Meml[po], nelem)
+ }
+ case BXOR:
+ if (len1 <= 0) {
+ O_VALL(out) = xor (O_VALL(in1), O_VALL(in2))
+ } else if (len2 <= 0) {
+ call axorkl (Meml[p1], O_VALL(in2),
+ Meml[po], nelem)
+ } else {
+ call axorl (Meml[p1], Meml[p2],
+ Meml[po], nelem)
+ }
+ }
+
+ default:
+ call xvv_error (s_boolop)
+ }
+
+ goto done_
+ }
+
+ # Perform an arithmetic binary operation.
+ switch (dtype) {
+ case TY_CHAR:
+ switch (opcode) {
+ case CONCAT:
+ call strcpy (O_VALC(in1), O_VALC(out), ARB)
+ call strcat (O_VALC(in2), O_VALC(out), ARB)
+ default:
+ call xvv_error ("binop: operation illegal for string operands")
+ }
+
+ case TY_SHORT:
+ switch (opcode) {
+ case PLUS:
+ if (len1 <= 0) {
+ O_VALS(out) = O_VALS(in1) + O_VALS(in2)
+ } else if (len2 <= 0) {
+ call aaddks (Mems[p1], O_VALS(in2),
+ Mems[po], nelem)
+ } else {
+ call aadds (Mems[p1], Mems[p2],
+ Mems[po], nelem)
+ }
+ case MINUS:
+ if (len1 <= 0)
+ O_VALS(out) = O_VALS(in1) - O_VALS(in2)
+ else if (len2 <= 0)
+ call asubks (Mems[p1], O_VALS(in2), Mems[po], nelem)
+ else
+ call asubs (Mems[p1], Mems[p2], Mems[po], nelem)
+
+ case STAR:
+ if (len1 <= 0)
+ O_VALS(out) = O_VALS(in1) * O_VALS(in2)
+ else if (len2 <= 0)
+ call amulks (Mems[p1], O_VALS(in2), Mems[po], nelem)
+ else
+ call amuls (Mems[p1], Mems[p2], Mems[po], nelem)
+
+ case SLASH:
+ if (and (ev_flags, EV_RNGCHK) == 0) {
+ # No range checking.
+ if (len1 <= 0)
+ O_VALS(out) = O_VALS(in1) / O_VALS(in2)
+ else if (len2 <= 0)
+ call adivks (Mems[p1], O_VALS(in2), Mems[po], nelem)
+ else
+ call adivs (Mems[p1], Mems[p2], Mems[po], nelem)
+ } else {
+ # Check for divide by zero.
+ if (len1 <= 0) {
+ if (O_VALS(in2) == 0)
+ O_VALS(out) = xvv_nulls(0)
+ else
+ O_VALS(out) = O_VALS(in1) / O_VALS(in2)
+ } else if (len2 <= 0) {
+ if (O_VALS(in2) == 0)
+ call amovks (xvv_nulls(0), Mems[po], nelem)
+ else {
+ call adivks (Mems[p1], O_VALS(in2), Mems[po],
+ nelem)
+ }
+ } else {
+ call advzs (Mems[p1], Mems[p2], Mems[po], nelem,
+ xvv_nulls)
+ }
+ }
+ case EXPON:
+ if (len1 <= 0)
+ O_VALS(out) = O_VALS(in1) ** O_VALS(in2)
+ else if (len2 <= 0)
+ call aexpks (Mems[p1], O_VALS(in2), Mems[po], nelem)
+ else
+ call aexps (Mems[p1], Mems[p2], Mems[po], nelem)
+
+ case CONCAT:
+ # Concatenate two numeric operands.
+ if (len1 <= 0) {
+ Mems[po] = O_VALS(in1)
+ po = po + 1
+ } else {
+ call amovs (Mems[p1], Mems[po], len1)
+ po = po + len1
+ }
+ if (len2 <= 0)
+ Mems[po] = O_VALS(in2)
+ else
+ call amovs (Mems[p2], Mems[po], len2)
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ case TY_INT:
+ switch (opcode) {
+ case PLUS:
+ if (len1 <= 0) {
+ O_VALI(out) = O_VALI(in1) + O_VALI(in2)
+ } else if (len2 <= 0) {
+ call aaddki (Memi[p1], O_VALI(in2),
+ Memi[po], nelem)
+ } else {
+ call aaddi (Memi[p1], Memi[p2],
+ Memi[po], nelem)
+ }
+ case MINUS:
+ if (len1 <= 0)
+ O_VALI(out) = O_VALI(in1) - O_VALI(in2)
+ else if (len2 <= 0)
+ call asubki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ else
+ call asubi (Memi[p1], Memi[p2], Memi[po], nelem)
+
+ case STAR:
+ if (len1 <= 0)
+ O_VALI(out) = O_VALI(in1) * O_VALI(in2)
+ else if (len2 <= 0)
+ call amulki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ else
+ call amuli (Memi[p1], Memi[p2], Memi[po], nelem)
+
+ case SLASH:
+ if (and (ev_flags, EV_RNGCHK) == 0) {
+ # No range checking.
+ if (len1 <= 0)
+ O_VALI(out) = O_VALI(in1) / O_VALI(in2)
+ else if (len2 <= 0)
+ call adivki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ else
+ call adivi (Memi[p1], Memi[p2], Memi[po], nelem)
+ } else {
+ # Check for divide by zero.
+ if (len1 <= 0) {
+ if (O_VALI(in2) == 0)
+ O_VALI(out) = xvv_nulli(0)
+ else
+ O_VALI(out) = O_VALI(in1) / O_VALI(in2)
+ } else if (len2 <= 0) {
+ if (O_VALI(in2) == 0)
+ call amovki (xvv_nulli(0), Memi[po], nelem)
+ else {
+ call adivki (Memi[p1], O_VALI(in2), Memi[po],
+ nelem)
+ }
+ } else {
+ call advzi (Memi[p1], Memi[p2], Memi[po], nelem,
+ xvv_nulli)
+ }
+ }
+ case EXPON:
+ if (len1 <= 0)
+ O_VALI(out) = O_VALI(in1) ** O_VALI(in2)
+ else if (len2 <= 0)
+ call aexpki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ else
+ call aexpi (Memi[p1], Memi[p2], Memi[po], nelem)
+
+ case CONCAT:
+ # Concatenate two numeric operands.
+ if (len1 <= 0) {
+ Memi[po] = O_VALI(in1)
+ po = po + 1
+ } else {
+ call amovi (Memi[p1], Memi[po], len1)
+ po = po + len1
+ }
+ if (len2 <= 0)
+ Memi[po] = O_VALI(in2)
+ else
+ call amovi (Memi[p2], Memi[po], len2)
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ case TY_LONG:
+ switch (opcode) {
+ case PLUS:
+ if (len1 <= 0) {
+ O_VALL(out) = O_VALL(in1) + O_VALL(in2)
+ } else if (len2 <= 0) {
+ call aaddkl (Meml[p1], O_VALL(in2),
+ Meml[po], nelem)
+ } else {
+ call aaddl (Meml[p1], Meml[p2],
+ Meml[po], nelem)
+ }
+ case MINUS:
+ if (len1 <= 0)
+ O_VALL(out) = O_VALL(in1) - O_VALL(in2)
+ else if (len2 <= 0)
+ call asubkl (Meml[p1], O_VALL(in2), Meml[po], nelem)
+ else
+ call asubl (Meml[p1], Meml[p2], Meml[po], nelem)
+
+ case STAR:
+ if (len1 <= 0)
+ O_VALL(out) = O_VALL(in1) * O_VALL(in2)
+ else if (len2 <= 0)
+ call amulkl (Meml[p1], O_VALL(in2), Meml[po], nelem)
+ else
+ call amull (Meml[p1], Meml[p2], Meml[po], nelem)
+
+ case SLASH:
+ if (and (ev_flags, EV_RNGCHK) == 0) {
+ # No range checking.
+ if (len1 <= 0)
+ O_VALL(out) = O_VALL(in1) / O_VALL(in2)
+ else if (len2 <= 0)
+ call adivkl (Meml[p1], O_VALL(in2), Meml[po], nelem)
+ else
+ call adivl (Meml[p1], Meml[p2], Meml[po], nelem)
+ } else {
+ # Check for divide by zero.
+ if (len1 <= 0) {
+ if (O_VALL(in2) == 0)
+ O_VALL(out) = xvv_nulll(0)
+ else
+ O_VALL(out) = O_VALL(in1) / O_VALL(in2)
+ } else if (len2 <= 0) {
+ if (O_VALL(in2) == 0)
+ call amovkl (xvv_nulll(0), Meml[po], nelem)
+ else {
+ call adivkl (Meml[p1], O_VALL(in2), Meml[po],
+ nelem)
+ }
+ } else {
+ call advzl (Meml[p1], Meml[p2], Meml[po], nelem,
+ xvv_nulll)
+ }
+ }
+ case EXPON:
+ if (len1 <= 0)
+ O_VALL(out) = O_VALL(in1) ** O_VALL(in2)
+ else if (len2 <= 0)
+ call aexpkl (Meml[p1], O_VALL(in2), Meml[po], nelem)
+ else
+ call aexpl (Meml[p1], Meml[p2], Meml[po], nelem)
+
+ case CONCAT:
+ # Concatenate two numeric operands.
+ if (len1 <= 0) {
+ Meml[po] = O_VALL(in1)
+ po = po + 1
+ } else {
+ call amovl (Meml[p1], Meml[po], len1)
+ po = po + len1
+ }
+ if (len2 <= 0)
+ Meml[po] = O_VALL(in2)
+ else
+ call amovl (Meml[p2], Meml[po], len2)
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ case TY_REAL:
+ switch (opcode) {
+ case PLUS:
+ if (len1 <= 0) {
+ O_VALR(out) = O_VALR(in1) + O_VALR(in2)
+ } else if (len2 <= 0) {
+ call aaddkr (Memr[p1], O_VALR(in2),
+ Memr[po], nelem)
+ } else {
+ call aaddr (Memr[p1], Memr[p2],
+ Memr[po], nelem)
+ }
+ case MINUS:
+ if (len1 <= 0)
+ O_VALR(out) = O_VALR(in1) - O_VALR(in2)
+ else if (len2 <= 0)
+ call asubkr (Memr[p1], O_VALR(in2), Memr[po], nelem)
+ else
+ call asubr (Memr[p1], Memr[p2], Memr[po], nelem)
+
+ case STAR:
+ if (len1 <= 0)
+ O_VALR(out) = O_VALR(in1) * O_VALR(in2)
+ else if (len2 <= 0)
+ call amulkr (Memr[p1], O_VALR(in2), Memr[po], nelem)
+ else
+ call amulr (Memr[p1], Memr[p2], Memr[po], nelem)
+
+ case SLASH:
+ if (and (ev_flags, EV_RNGCHK) == 0) {
+ # No range checking.
+ if (len1 <= 0)
+ O_VALR(out) = O_VALR(in1) / O_VALR(in2)
+ else if (len2 <= 0)
+ call adivkr (Memr[p1], O_VALR(in2), Memr[po], nelem)
+ else
+ call adivr (Memr[p1], Memr[p2], Memr[po], nelem)
+ } else {
+ # Check for divide by zero.
+ if (len1 <= 0) {
+ if (O_VALR(in2) == 0.0)
+ O_VALR(out) = xvv_nullr(0.0)
+ else
+ O_VALR(out) = O_VALR(in1) / O_VALR(in2)
+ } else if (len2 <= 0) {
+ if (O_VALR(in2) == 0.0)
+ call amovkr (xvv_nullr(0.0), Memr[po], nelem)
+ else {
+ call adivkr (Memr[p1], O_VALR(in2), Memr[po],
+ nelem)
+ }
+ } else {
+ call advzr (Memr[p1], Memr[p2], Memr[po], nelem,
+ xvv_nullr)
+ }
+ }
+ case EXPON:
+ if (len1 <= 0)
+ O_VALR(out) = O_VALR(in1) ** O_VALR(in2)
+ else if (len2 <= 0)
+ call aexpkr (Memr[p1], O_VALR(in2), Memr[po], nelem)
+ else
+ call aexpr (Memr[p1], Memr[p2], Memr[po], nelem)
+
+ case CONCAT:
+ # Concatenate two numeric operands.
+ if (len1 <= 0) {
+ Memr[po] = O_VALR(in1)
+ po = po + 1
+ } else {
+ call amovr (Memr[p1], Memr[po], len1)
+ po = po + len1
+ }
+ if (len2 <= 0)
+ Memr[po] = O_VALR(in2)
+ else
+ call amovr (Memr[p2], Memr[po], len2)
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ case TY_DOUBLE:
+ switch (opcode) {
+ case PLUS:
+ if (len1 <= 0) {
+ O_VALD(out) = O_VALD(in1) + O_VALD(in2)
+ } else if (len2 <= 0) {
+ call aaddkd (Memd[p1], O_VALD(in2),
+ Memd[po], nelem)
+ } else {
+ call aaddd (Memd[p1], Memd[p2],
+ Memd[po], nelem)
+ }
+ case MINUS:
+ if (len1 <= 0)
+ O_VALD(out) = O_VALD(in1) - O_VALD(in2)
+ else if (len2 <= 0)
+ call asubkd (Memd[p1], O_VALD(in2), Memd[po], nelem)
+ else
+ call asubd (Memd[p1], Memd[p2], Memd[po], nelem)
+
+ case STAR:
+ if (len1 <= 0)
+ O_VALD(out) = O_VALD(in1) * O_VALD(in2)
+ else if (len2 <= 0)
+ call amulkd (Memd[p1], O_VALD(in2), Memd[po], nelem)
+ else
+ call amuld (Memd[p1], Memd[p2], Memd[po], nelem)
+
+ case SLASH:
+ if (and (ev_flags, EV_RNGCHK) == 0) {
+ # No range checking.
+ if (len1 <= 0)
+ O_VALD(out) = O_VALD(in1) / O_VALD(in2)
+ else if (len2 <= 0)
+ call adivkd (Memd[p1], O_VALD(in2), Memd[po], nelem)
+ else
+ call adivd (Memd[p1], Memd[p2], Memd[po], nelem)
+ } else {
+ # Check for divide by zero.
+ if (len1 <= 0) {
+ if (O_VALD(in2) == 0.0D0)
+ O_VALD(out) = xvv_nulld(0.0D0)
+ else
+ O_VALD(out) = O_VALD(in1) / O_VALD(in2)
+ } else if (len2 <= 0) {
+ if (O_VALD(in2) == 0.0D0)
+ call amovkd (xvv_nulld(0.0D0), Memd[po], nelem)
+ else {
+ call adivkd (Memd[p1], O_VALD(in2), Memd[po],
+ nelem)
+ }
+ } else {
+ call advzd (Memd[p1], Memd[p2], Memd[po], nelem,
+ xvv_nulld)
+ }
+ }
+ case EXPON:
+ if (len1 <= 0)
+ O_VALD(out) = O_VALD(in1) ** O_VALD(in2)
+ else if (len2 <= 0)
+ call aexpkd (Memd[p1], O_VALD(in2), Memd[po], nelem)
+ else
+ call aexpd (Memd[p1], Memd[p2], Memd[po], nelem)
+
+ case CONCAT:
+ # Concatenate two numeric operands.
+ if (len1 <= 0) {
+ Memd[po] = O_VALD(in1)
+ po = po + 1
+ } else {
+ call amovd (Memd[p1], Memd[po], len1)
+ po = po + len1
+ }
+ if (len2 <= 0)
+ Memd[po] = O_VALD(in2)
+ else
+ call amovd (Memd[p2], Memd[po], len2)
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+done_
+ # Free any storage in input operands.
+ call xvv_freeop (in1)
+ call xvv_freeop (in2)
+end
+
+
+# XVV_BOOLOP -- Boolean (actually logical) binary operations. Perform the
+# indicated logical operation on the two input operands, returning the result
+# as the output operand. The opcodes implemented by this routine are
+# characterized by the fact that they all return a logical result (YES or NO
+# physically expressed as an integer).
+
+procedure xvv_boolop (opcode, in1, in2, out)
+
+int opcode #I operation to be performed
+pointer in1, in2 #I input operands
+pointer out #I output operand
+
+
+short v_s
+
+int v_i
+
+long v_l
+
+real v_r
+
+double v_d
+
+pointer sp, otemp, p1, p2, po
+int dtype, nelem, len1, len2
+int xvv_newtype(), xvv_patmatch(), strncmp(), btoi()
+errchk xvv_newtype, xvv_initop, xvv_chtype, xvv_error
+string s_badop "boolop: illegal operation"
+string s_badswitch "boolop: illegal switch"
+
+begin
+ # Boolean operands are treated as integer within this routine.
+ if (O_TYPE(in1) == TY_BOOL)
+ O_TYPE(in1) = TY_INT
+ if (O_TYPE(in2) == TY_BOOL)
+ O_TYPE(in2) = TY_INT
+
+ # Determine the computation type for the operation, i.e., the type
+ # both input operands must have. This is not the same as the type
+ # of the output operand, which is always boolean for the operations
+ # implemented by this routine.
+
+ dtype = xvv_newtype (O_TYPE(in1), O_TYPE(in2))
+
+ # Compute the size of the output operand. If both input operands are
+ # vectors the length of the output vector is the shorter of the two.
+
+ if (dtype == TY_CHAR)
+ nelem = 0
+ else {
+ if (O_LEN(in1) > 0 && O_LEN(in2) > 0)
+ nelem = min (O_LEN(in1), O_LEN(in2))
+ else if (O_LEN(in1) > 0)
+ nelem = O_LEN(in1)
+ else if (O_LEN(in2) > 0)
+ nelem = O_LEN(in2)
+ else
+ nelem = 0
+ }
+
+ # Convert input operands to desired computation type.
+ if (O_TYPE(in1) != dtype)
+ call xvv_chtype (in1, in1, dtype)
+ if (O_TYPE(in2) != dtype)
+ call xvv_chtype (in2, in2, dtype)
+
+ # If this is a scalar/vector operation make sure the vector is the
+ # first operand.
+
+ len1 = O_LEN(in1)
+ len2 = O_LEN(in2)
+
+ if (len1 == 0 && len2 > 0) {
+ switch (opcode) {
+ case EQ, NE:
+ call smark (sp)
+ call salloc (otemp, LEN_OPERAND, TY_STRUCT)
+ YYMOVE (in1, otemp)
+ YYMOVE (in2, in1)
+ YYMOVE (otemp, in2)
+ call sfree (sp)
+ default:
+ # Promote operand to a constant vector. Inefficient, but
+ # better than aborting.
+
+ switch (dtype) {
+
+ case TY_SHORT:
+ v_s = O_VALS(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovks (v_s, Mems[O_VALP(in1)], nelem)
+
+ case TY_INT:
+ v_i = O_VALI(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovki (v_i, Memi[O_VALP(in1)], nelem)
+
+ case TY_LONG:
+ v_l = O_VALL(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovkl (v_l, Meml[O_VALP(in1)], nelem)
+
+ case TY_REAL:
+ v_r = O_VALR(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovkr (v_r, Memr[O_VALP(in1)], nelem)
+
+ case TY_DOUBLE:
+ v_d = O_VALD(in1)
+ call xvv_initop (in1, nelem, dtype)
+ call amovkd (v_d, Memd[O_VALP(in1)], nelem)
+
+ }
+ }
+
+ len1 = O_LEN(in1)
+ len2 = O_LEN(in2)
+ }
+
+ # Initialize the output operand.
+ call xvv_initop (out, nelem, TY_BOOL)
+
+ p1 = O_VALP(in1)
+ p2 = O_VALP(in2)
+ po = O_VALP(out)
+
+ # Perform the operation.
+ if (dtype == TY_CHAR) {
+ # Character data is a special case.
+
+ switch (opcode) {
+ case SE:
+ O_VALI(out) = btoi(xvv_patmatch (O_VALC(in1), O_VALC(in2)) > 0)
+ case LT:
+ O_VALI(out) = btoi(strncmp (O_VALC(in1), O_VALC(in2), ARB) < 0)
+ case LE:
+ O_VALI(out) = btoi(strncmp (O_VALC(in1), O_VALC(in2), ARB) <= 0)
+ case GT:
+ O_VALI(out) = btoi(strncmp (O_VALC(in1), O_VALC(in2), ARB) > 0)
+ case GE:
+ O_VALI(out) = btoi(strncmp (O_VALC(in1), O_VALC(in2), ARB) >= 0)
+ case EQ:
+ O_VALI(out) = btoi(strncmp (O_VALC(in1), O_VALC(in2), ARB) == 0)
+ case NE:
+ O_VALI(out) = btoi(strncmp (O_VALC(in1), O_VALC(in2), ARB) != 0)
+ default:
+ call xvv_error (s_badop)
+ }
+
+ } else if (opcode == LAND || opcode == LOR) {
+ # Operations supporting only the integer types.
+
+ switch (dtype) {
+
+ case TY_SHORT:
+ switch (opcode) {
+ case LAND:
+ if (len1 <= 0) {
+ O_VALI(out) =
+ btoi (O_VALS(in1) != 0 && O_VALS(in2) != 0)
+ } else if (len2 <= 0) {
+ call alanks (Mems[p1], O_VALS(in2), Memi[po], nelem)
+ } else
+ call alans (Mems[p1], Mems[p2], Memi[po], nelem)
+ case LOR:
+ if (len1 <= 0) {
+ O_VALI(out) =
+ btoi (O_VALS(in1) != 0 || O_VALS(in2) != 0)
+ } else if (len2 <= 0) {
+ call alorks (Mems[p1], O_VALS(in2), Memi[po], nelem)
+ } else
+ call alors (Mems[p1], Mems[p2], Memi[po], nelem)
+ default:
+ call xvv_error (s_badop)
+ }
+
+ case TY_INT:
+ switch (opcode) {
+ case LAND:
+ if (len1 <= 0) {
+ O_VALI(out) =
+ btoi (O_VALI(in1) != 0 && O_VALI(in2) != 0)
+ } else if (len2 <= 0) {
+ call alanki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ } else
+ call alani (Memi[p1], Memi[p2], Memi[po], nelem)
+ case LOR:
+ if (len1 <= 0) {
+ O_VALI(out) =
+ btoi (O_VALI(in1) != 0 || O_VALI(in2) != 0)
+ } else if (len2 <= 0) {
+ call alorki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ } else
+ call alori (Memi[p1], Memi[p2], Memi[po], nelem)
+ default:
+ call xvv_error (s_badop)
+ }
+
+ case TY_LONG:
+ switch (opcode) {
+ case LAND:
+ if (len1 <= 0) {
+ O_VALI(out) =
+ btoi (O_VALL(in1) != 0 && O_VALL(in2) != 0)
+ } else if (len2 <= 0) {
+ call alankl (Meml[p1], O_VALL(in2), Memi[po], nelem)
+ } else
+ call alanl (Meml[p1], Meml[p2], Memi[po], nelem)
+ case LOR:
+ if (len1 <= 0) {
+ O_VALI(out) =
+ btoi (O_VALL(in1) != 0 || O_VALL(in2) != 0)
+ } else if (len2 <= 0) {
+ call alorkl (Meml[p1], O_VALL(in2), Memi[po], nelem)
+ } else
+ call alorl (Meml[p1], Meml[p2], Memi[po], nelem)
+ default:
+ call xvv_error (s_badop)
+ }
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+ } else {
+ # Operations supporting any arithmetic type.
+
+ switch (dtype) {
+
+ case TY_SHORT:
+ switch (opcode) {
+ case LT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALS(in1) < O_VALS(in2))
+ else if (len2 <= 0)
+ call abltks (Mems[p1], O_VALS(in2), Memi[po], nelem)
+ else
+ call ablts (Mems[p1], Mems[p2], Memi[po], nelem)
+
+ case LE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALS(in1) <= O_VALS(in2))
+ else if (len2 <= 0)
+ call ableks (Mems[p1], O_VALS(in2), Memi[po], nelem)
+ else
+ call ables (Mems[p1], Mems[p2], Memi[po], nelem)
+
+ case GT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALS(in1) > O_VALS(in2))
+ else if (len2 <= 0)
+ call abgtks (Mems[p1], O_VALS(in2), Memi[po], nelem)
+ else
+ call abgts (Mems[p1], Mems[p2], Memi[po], nelem)
+
+ case GE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALS(in1) >= O_VALS(in2))
+ else if (len2 <= 0)
+ call abgeks (Mems[p1], O_VALS(in2), Memi[po], nelem)
+ else
+ call abges (Mems[p1], Mems[p2], Memi[po], nelem)
+
+ case EQ:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALS(in1) == O_VALS(in2))
+ else if (len2 <= 0)
+ call abeqks (Mems[p1], O_VALS(in2), Memi[po], nelem)
+ else
+ call abeqs (Mems[p1], Mems[p2], Memi[po], nelem)
+
+ case NE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALS(in1) != O_VALS(in2))
+ else if (len2 <= 0)
+ call abneks (Mems[p1], O_VALS(in2), Memi[po], nelem)
+ else
+ call abnes (Mems[p1], Mems[p2], Memi[po], nelem)
+
+ default:
+ call xvv_error (s_badop)
+ }
+
+ case TY_INT:
+ switch (opcode) {
+ case LT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALI(in1) < O_VALI(in2))
+ else if (len2 <= 0)
+ call abltki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ else
+ call ablti (Memi[p1], Memi[p2], Memi[po], nelem)
+
+ case LE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALI(in1) <= O_VALI(in2))
+ else if (len2 <= 0)
+ call ableki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ else
+ call ablei (Memi[p1], Memi[p2], Memi[po], nelem)
+
+ case GT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALI(in1) > O_VALI(in2))
+ else if (len2 <= 0)
+ call abgtki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ else
+ call abgti (Memi[p1], Memi[p2], Memi[po], nelem)
+
+ case GE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALI(in1) >= O_VALI(in2))
+ else if (len2 <= 0)
+ call abgeki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ else
+ call abgei (Memi[p1], Memi[p2], Memi[po], nelem)
+
+ case EQ:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALI(in1) == O_VALI(in2))
+ else if (len2 <= 0)
+ call abeqki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ else
+ call abeqi (Memi[p1], Memi[p2], Memi[po], nelem)
+
+ case NE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALI(in1) != O_VALI(in2))
+ else if (len2 <= 0)
+ call abneki (Memi[p1], O_VALI(in2), Memi[po], nelem)
+ else
+ call abnei (Memi[p1], Memi[p2], Memi[po], nelem)
+
+ default:
+ call xvv_error (s_badop)
+ }
+
+ case TY_LONG:
+ switch (opcode) {
+ case LT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALL(in1) < O_VALL(in2))
+ else if (len2 <= 0)
+ call abltkl (Meml[p1], O_VALL(in2), Memi[po], nelem)
+ else
+ call abltl (Meml[p1], Meml[p2], Memi[po], nelem)
+
+ case LE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALL(in1) <= O_VALL(in2))
+ else if (len2 <= 0)
+ call ablekl (Meml[p1], O_VALL(in2), Memi[po], nelem)
+ else
+ call ablel (Meml[p1], Meml[p2], Memi[po], nelem)
+
+ case GT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALL(in1) > O_VALL(in2))
+ else if (len2 <= 0)
+ call abgtkl (Meml[p1], O_VALL(in2), Memi[po], nelem)
+ else
+ call abgtl (Meml[p1], Meml[p2], Memi[po], nelem)
+
+ case GE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALL(in1) >= O_VALL(in2))
+ else if (len2 <= 0)
+ call abgekl (Meml[p1], O_VALL(in2), Memi[po], nelem)
+ else
+ call abgel (Meml[p1], Meml[p2], Memi[po], nelem)
+
+ case EQ:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALL(in1) == O_VALL(in2))
+ else if (len2 <= 0)
+ call abeqkl (Meml[p1], O_VALL(in2), Memi[po], nelem)
+ else
+ call abeql (Meml[p1], Meml[p2], Memi[po], nelem)
+
+ case NE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALL(in1) != O_VALL(in2))
+ else if (len2 <= 0)
+ call abnekl (Meml[p1], O_VALL(in2), Memi[po], nelem)
+ else
+ call abnel (Meml[p1], Meml[p2], Memi[po], nelem)
+
+ default:
+ call xvv_error (s_badop)
+ }
+
+ case TY_REAL:
+ switch (opcode) {
+ case LT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALR(in1) < O_VALR(in2))
+ else if (len2 <= 0)
+ call abltkr (Memr[p1], O_VALR(in2), Memi[po], nelem)
+ else
+ call abltr (Memr[p1], Memr[p2], Memi[po], nelem)
+
+ case LE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALR(in1) <= O_VALR(in2))
+ else if (len2 <= 0)
+ call ablekr (Memr[p1], O_VALR(in2), Memi[po], nelem)
+ else
+ call abler (Memr[p1], Memr[p2], Memi[po], nelem)
+
+ case GT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALR(in1) > O_VALR(in2))
+ else if (len2 <= 0)
+ call abgtkr (Memr[p1], O_VALR(in2), Memi[po], nelem)
+ else
+ call abgtr (Memr[p1], Memr[p2], Memi[po], nelem)
+
+ case GE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALR(in1) >= O_VALR(in2))
+ else if (len2 <= 0)
+ call abgekr (Memr[p1], O_VALR(in2), Memi[po], nelem)
+ else
+ call abger (Memr[p1], Memr[p2], Memi[po], nelem)
+
+ case EQ:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALR(in1) == O_VALR(in2))
+ else if (len2 <= 0)
+ call abeqkr (Memr[p1], O_VALR(in2), Memi[po], nelem)
+ else
+ call abeqr (Memr[p1], Memr[p2], Memi[po], nelem)
+
+ case NE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALR(in1) != O_VALR(in2))
+ else if (len2 <= 0)
+ call abnekr (Memr[p1], O_VALR(in2), Memi[po], nelem)
+ else
+ call abner (Memr[p1], Memr[p2], Memi[po], nelem)
+
+ default:
+ call xvv_error (s_badop)
+ }
+
+ case TY_DOUBLE:
+ switch (opcode) {
+ case LT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALD(in1) < O_VALD(in2))
+ else if (len2 <= 0)
+ call abltkd (Memd[p1], O_VALD(in2), Memi[po], nelem)
+ else
+ call abltd (Memd[p1], Memd[p2], Memi[po], nelem)
+
+ case LE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALD(in1) <= O_VALD(in2))
+ else if (len2 <= 0)
+ call ablekd (Memd[p1], O_VALD(in2), Memi[po], nelem)
+ else
+ call abled (Memd[p1], Memd[p2], Memi[po], nelem)
+
+ case GT:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALD(in1) > O_VALD(in2))
+ else if (len2 <= 0)
+ call abgtkd (Memd[p1], O_VALD(in2), Memi[po], nelem)
+ else
+ call abgtd (Memd[p1], Memd[p2], Memi[po], nelem)
+
+ case GE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALD(in1) >= O_VALD(in2))
+ else if (len2 <= 0)
+ call abgekd (Memd[p1], O_VALD(in2), Memi[po], nelem)
+ else
+ call abged (Memd[p1], Memd[p2], Memi[po], nelem)
+
+ case EQ:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALD(in1) == O_VALD(in2))
+ else if (len2 <= 0)
+ call abeqkd (Memd[p1], O_VALD(in2), Memi[po], nelem)
+ else
+ call abeqd (Memd[p1], Memd[p2], Memi[po], nelem)
+
+ case NE:
+ if (len1 <= 0)
+ O_VALI(out) = btoi (O_VALD(in1) != O_VALD(in2))
+ else if (len2 <= 0)
+ call abnekd (Memd[p1], O_VALD(in2), Memi[po], nelem)
+ else
+ call abned (Memd[p1], Memd[p2], Memi[po], nelem)
+
+ default:
+ call xvv_error (s_badop)
+ }
+
+ default:
+ call xvv_error (s_badswitch)
+ }
+ }
+
+ # Free any storage in input operands.
+ call xvv_freeop (in1)
+ call xvv_freeop (in2)
+end
+
+
+# XVV_PATMATCH -- Match a string against a pattern, returning the patmatch
+# index if the string matches. The pattern may contain any of the conventional
+# pattern matching metacharacters. Closure (i.e., "*") is mapped to "?*".
+
+int procedure xvv_patmatch (str, pat)
+
+char str[ARB] #I operand string
+char pat[ARB] #I pattern
+
+int junk, ip, index
+pointer sp, patstr, patbuf, op
+int patmake(), patmatch()
+
+begin
+ call smark (sp)
+ call salloc (patstr, SZ_FNAME, TY_CHAR)
+ call salloc (patbuf, SZ_LINE, TY_CHAR)
+ call aclrc (Memc[patstr], SZ_FNAME)
+ call aclrc (Memc[patbuf], SZ_LINE)
+
+ # Map pattern, changing '*' into '?*'.
+ op = patstr
+ for (ip=1; pat[ip] != EOS; ip=ip+1) {
+ if (pat[ip] == '*') {
+ Memc[op] = '?'
+ op = op + 1
+ }
+ Memc[op] = pat[ip]
+ op = op + 1
+ }
+
+ # Encode pattern.
+ junk = patmake (Memc[patstr], Memc[patbuf], SZ_LINE)
+
+ # Perform the pattern matching operation.
+ index = patmatch (str, Memc[patbuf])
+
+ call sfree (sp)
+ return (index)
+end
+
+
+# XVV_NEWTYPE -- Get the datatype of a binary operation, given the datatypes
+# of the two input operands. An error action is taken if the datatypes are
+# incompatible, e.g., boolean and anything else or string and anything else.
+
+int procedure xvv_newtype (type1, type2)
+
+int type1 #I datatype of first operand
+int type2 #I datatype of second operand
+
+int newtype, p, q, i
+int tyindex[NTYPES], ttbl[NTYPES*NTYPES]
+data tyindex /T_B, T_C, T_S, T_I, T_L, T_R, T_D/
+
+data (ttbl(i),i= 1, 7) /T_B, 0, 0, 0, 0, 0, 0/
+data (ttbl(i),i= 8,14) / 0, T_C, 0, 0, 0, 0, 0/
+data (ttbl(i),i=15,21) / 0, 0, T_S, T_I, T_L, T_R, T_D/
+data (ttbl(i),i=22,28) / 0, 0, T_I, T_I, T_L, T_R, T_D/
+data (ttbl(i),i=29,35) / 0, 0, T_L, T_L, T_L, T_R, T_D/
+data (ttbl(i),i=36,42) / 0, 0, T_R, T_R, T_R, T_R, T_D/
+data (ttbl(i),i=43,49) / 0, 0, T_D, T_D, T_D, T_D, T_D/
+
+begin
+ do i = 1, NTYPES {
+ if (tyindex[i] == type1)
+ p = i
+ if (tyindex[i] == type2)
+ q = i
+ }
+
+ newtype = ttbl[(p-1)*NTYPES+q]
+ if (newtype == 0)
+ call xvv_error ("operands have incompatible types")
+ else
+ return (newtype)
+end
+
+
+# XVV_QUEST -- Conditional expression. If the condition operand is true
+# return the first (true) operand, else return the second (false) operand.
+
+procedure xvv_quest (cond, in1, in2, out)
+
+pointer cond #I pointer to condition operand
+pointer in1, in2 #I pointer to true,false operands
+pointer out #I pointer to output operand
+
+int dtype, nelem, i
+pointer sp, otemp, ip1, ip2, op, sel
+errchk xvv_error, xvv_newtype, xvv_initop, xvv_chtype
+int xvv_newtype(), btoi()
+
+begin
+ switch (O_TYPE(cond)) {
+ case TY_BOOL, TY_INT:
+ ;
+ case TY_SHORT, TY_LONG:
+ call xvv_chtype (cond, cond, TY_BOOL)
+ default:
+ call xvv_error ("evvexpr: nonboolean condition operand")
+ }
+
+ if (O_LEN(cond) <= 0 &&
+ (O_LEN(in1) <= 0 || O_TYPE(in1) == TY_CHAR) &&
+ (O_LEN(in2) <= 0 || O_TYPE(in2) == TY_CHAR)) {
+
+ # Both operands and the conditional are scalars; the expression
+ # type is the type of the selected operand.
+
+ if (O_VALI(cond) != 0) {
+ YYMOVE (in1, out)
+ call xvv_freeop (in2)
+ } else {
+ YYMOVE (in2, out)
+ call xvv_freeop (in1)
+ }
+
+ } else if (O_TYPE(in1) == TY_CHAR || O_TYPE(in2) == TY_CHAR) {
+ # This combination is not legal.
+ call xvv_error ("evvexpr: character and vector in cond expr")
+
+ } else {
+ # Vector/scalar or vector/vector operation. Both operands must
+ # be of the same type.
+
+ dtype = xvv_newtype (O_TYPE(in1), O_TYPE(in2))
+
+ # Compute the size of the output operand. If both input operands
+ # are vectors the length of the output vector is the shorter of
+ # the two. The condition operand contributes to the dimension of
+ # the expression result, although not to the datatype.
+
+ nelem = 0
+ if (O_LEN(in1) > 0 && O_LEN(in2) > 0)
+ nelem = min (O_LEN(in1), O_LEN(in2))
+ else if (O_LEN(in1) > 0)
+ nelem = O_LEN(in1)
+ else if (O_LEN(in2) > 0)
+ nelem = O_LEN(in2)
+
+ if (O_LEN(cond) > 0 && nelem > 0)
+ nelem = min (O_LEN(cond), nelem)
+ else if (O_LEN(cond) > 0)
+ nelem = O_LEN(cond)
+
+ # If this is a scalar/vector operation make sure the vector is the
+ # first operand.
+
+ if (O_LEN(in1) == 0 && O_LEN(in2) > 0) {
+ call smark (sp)
+ call salloc (otemp, LEN_OPERAND, TY_STRUCT)
+ YYMOVE (in1, otemp)
+ YYMOVE (in2, in1)
+ YYMOVE (otemp, in2)
+ call sfree (sp)
+
+ # Since we are swapping arguments we need to negate the cond.
+ if (O_LEN(cond) <= 0)
+ O_VALI(cond) = btoi (O_VALI(cond) == 0)
+ else {
+ call abeqki (Memi[O_VALP(cond)], NO, Memi[O_VALP(cond)],
+ nelem)
+ }
+ }
+
+ # Initialize the output operand.
+ call xvv_initop (out, nelem, dtype)
+
+ # Convert input operands to desired computation type.
+ if (O_TYPE(in1) != dtype)
+ call xvv_chtype (in1, in1, dtype)
+ if (O_TYPE(in2) != dtype)
+ call xvv_chtype (in2, in2, dtype)
+
+ ip1 = O_VALP(in1)
+ ip2 = O_VALP(in2)
+ op = O_VALP(out)
+ sel = O_VALP(cond)
+
+ # Perform the operation.
+ switch (dtype) {
+
+ case TY_SHORT:
+ if (O_LEN(in1) <= 0 && O_LEN(in2) <= 0) {
+ # Vector conditional, both operands are scalars.
+ do i = 1, nelem
+ if (Memi[sel+i-1] != 0)
+ Mems[op+i-1] = O_VALS(in1)
+ else
+ Mems[op+i-1] = O_VALS(in2)
+
+ } else if (O_LEN(in2) <= 0) {
+ # Operand 1 is a vector, operand 2 is a scalar.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amovs (Mems[ip1], Mems[op], nelem)
+ else
+ call amovks (O_VALS(in2), Mems[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call aselks (Mems[ip1], O_VALS(in2), Mems[op],
+ Memi[sel], nelem)
+ }
+ } else {
+ # Both operands are vectors.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amovs (Mems[ip1], Mems[op], nelem)
+ else
+ call amovs (Mems[ip2], Mems[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call asels (Mems[ip1], Mems[ip2], Mems[op],
+ Memi[sel], nelem)
+ }
+ }
+
+ case TY_INT:
+ if (O_LEN(in1) <= 0 && O_LEN(in2) <= 0) {
+ # Vector conditional, both operands are scalars.
+ do i = 1, nelem
+ if (Memi[sel+i-1] != 0)
+ Memi[op+i-1] = O_VALI(in1)
+ else
+ Memi[op+i-1] = O_VALI(in2)
+
+ } else if (O_LEN(in2) <= 0) {
+ # Operand 1 is a vector, operand 2 is a scalar.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amovi (Memi[ip1], Memi[op], nelem)
+ else
+ call amovki (O_VALI(in2), Memi[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call aselki (Memi[ip1], O_VALI(in2), Memi[op],
+ Memi[sel], nelem)
+ }
+ } else {
+ # Both operands are vectors.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amovi (Memi[ip1], Memi[op], nelem)
+ else
+ call amovi (Memi[ip2], Memi[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call aseli (Memi[ip1], Memi[ip2], Memi[op],
+ Memi[sel], nelem)
+ }
+ }
+
+ case TY_LONG:
+ if (O_LEN(in1) <= 0 && O_LEN(in2) <= 0) {
+ # Vector conditional, both operands are scalars.
+ do i = 1, nelem
+ if (Memi[sel+i-1] != 0)
+ Meml[op+i-1] = O_VALL(in1)
+ else
+ Meml[op+i-1] = O_VALL(in2)
+
+ } else if (O_LEN(in2) <= 0) {
+ # Operand 1 is a vector, operand 2 is a scalar.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amovl (Meml[ip1], Meml[op], nelem)
+ else
+ call amovkl (O_VALL(in2), Meml[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call aselkl (Meml[ip1], O_VALL(in2), Meml[op],
+ Memi[sel], nelem)
+ }
+ } else {
+ # Both operands are vectors.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amovl (Meml[ip1], Meml[op], nelem)
+ else
+ call amovl (Meml[ip2], Meml[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call asell (Meml[ip1], Meml[ip2], Meml[op],
+ Memi[sel], nelem)
+ }
+ }
+
+ case TY_REAL:
+ if (O_LEN(in1) <= 0 && O_LEN(in2) <= 0) {
+ # Vector conditional, both operands are scalars.
+ do i = 1, nelem
+ if (Memi[sel+i-1] != 0)
+ Memr[op+i-1] = O_VALR(in1)
+ else
+ Memr[op+i-1] = O_VALR(in2)
+
+ } else if (O_LEN(in2) <= 0) {
+ # Operand 1 is a vector, operand 2 is a scalar.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amovr (Memr[ip1], Memr[op], nelem)
+ else
+ call amovkr (O_VALR(in2), Memr[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call aselkr (Memr[ip1], O_VALR(in2), Memr[op],
+ Memi[sel], nelem)
+ }
+ } else {
+ # Both operands are vectors.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amovr (Memr[ip1], Memr[op], nelem)
+ else
+ call amovr (Memr[ip2], Memr[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call aselr (Memr[ip1], Memr[ip2], Memr[op],
+ Memi[sel], nelem)
+ }
+ }
+
+ case TY_DOUBLE:
+ if (O_LEN(in1) <= 0 && O_LEN(in2) <= 0) {
+ # Vector conditional, both operands are scalars.
+ do i = 1, nelem
+ if (Memi[sel+i-1] != 0)
+ Memd[op+i-1] = O_VALD(in1)
+ else
+ Memd[op+i-1] = O_VALD(in2)
+
+ } else if (O_LEN(in2) <= 0) {
+ # Operand 1 is a vector, operand 2 is a scalar.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amovd (Memd[ip1], Memd[op], nelem)
+ else
+ call amovkd (O_VALD(in2), Memd[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call aselkd (Memd[ip1], O_VALD(in2), Memd[op],
+ Memi[sel], nelem)
+ }
+ } else {
+ # Both operands are vectors.
+ if (O_LEN(cond) <= 0) {
+ # Conditional is a scalar.
+ if (O_VALI(cond) != 0)
+ call amovd (Memd[ip1], Memd[op], nelem)
+ else
+ call amovd (Memd[ip2], Memd[op], nelem)
+ } else {
+ # Conditional is a vector.
+ call aseld (Memd[ip1], Memd[ip2], Memd[op],
+ Memi[sel], nelem)
+ }
+ }
+
+ default:
+ call xvv_error ("evvexpr: bad datatype in cond expr")
+ }
+
+ call xvv_freeop (in1)
+ call xvv_freeop (in2)
+ }
+
+ call xvv_freeop (cond)
+end
+
+
+# XVV_CALLFCN -- Call an intrinsic function. If the function named is not
+# one of the standard intrinsic functions, call an external user function
+# if a function call procedure was supplied.
+
+procedure xvv_callfcn (fcn, args, nargs, out)
+
+char fcn[ARB] #I function to be called
+pointer args[ARB] #I pointer to arglist descriptor
+int nargs #I number of arguments
+pointer out #I output operand (function value)
+
+
+short v_s
+short ahivs(), alovs()
+short ameds()
+int aravs()
+
+int v_i
+int ahivi(), alovi()
+int amedi()
+int aravi()
+
+long v_l
+long ahivl(), alovl()
+long amedl()
+int aravl()
+
+real v_r
+real ahivr(), alovr()
+real amedr()
+int aravr()
+
+double v_d
+double ahivd(), alovd()
+double amedd()
+int aravd()
+
+
+real mean_r, sigma_r
+double mean_d, sigma_d
+real asums(), asumi(), asumr()
+double asuml(), asumd()
+
+bool rangecheck
+int optype, opcode
+int chunk, repl, nelem, v_nargs, ch, shift, i, j
+pointer sp, sym, buf, ap, ip, op, in1, in2
+include "evvexpr.com"
+
+pointer stfind()
+int xvv_newtype(), strlen(), gctod(), btoi()
+errchk xvv_chtype, xvv_initop, xvv_newtype, xvv_error1, xvv_error2
+errchk zcall5, malloc
+
+string s_badtype "%s: illegal operand type"
+define free_ 91
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_FNAME, TY_CHAR)
+
+ # Lookup the function name in the symbol table.
+ sym = stfind (ev_st, fcn)
+ if (sym != NULL)
+ opcode = SYM_CODE(sym)
+ else
+ opcode = 0
+
+ # If the function named is not a standard one and the user has supplied
+ # the entry point of an external function evaluation procedure, call
+ # the user procedure to evaluate the function, otherwise abort.
+
+ if (opcode <= 0)
+ if (ev_ufcn != NULL) {
+ call zcall5 (ev_ufcn, ev_ufcn_data, fcn, args, nargs, out)
+ if (O_TYPE(out) <= 0)
+ call xvv_error1 ("unrecognized macro or function `%s'", fcn)
+ goto free_
+ } else
+ call xvv_error1 ("unknown function `%s' called", fcn)
+
+ # Range checking on functions that need it?
+ rangecheck = (and (ev_flags, EV_RNGCHK) != 0)
+
+ # Verify correct number of arguments.
+ switch (opcode) {
+ case F_MOD, F_REPL, F_SHIFT:
+ v_nargs = 2
+ case F_MAX, F_MIN, F_ATAN, F_ATAN2, F_MEAN, F_STDDEV, F_MEDIAN:
+ v_nargs = -1
+ default:
+ v_nargs = 1
+ }
+ if (v_nargs > 0 && nargs != v_nargs)
+ call xvv_error2 ("function `%s' requires %d arguments",
+ fcn, v_nargs)
+ else if (v_nargs < 0 && nargs < abs(v_nargs))
+ call xvv_error2 ("function `%s' requires at least %d arguments",
+ fcn, abs(v_nargs))
+
+ # Some functions require that the input operand be a certain type,
+ # e.g. floating. Handle the simple cases, converting input operands
+ # to the desired type.
+
+ switch (opcode) {
+ case F_ACOS, F_ASIN, F_ATAN, F_ATAN2, F_COS, F_COSH, F_DEG, F_EXP,
+ F_LOG, F_LOG10, F_RAD, F_SIN, F_SINH, F_SQRT, F_TAN, F_TANH:
+
+ # These functions want a floating point input operand.
+ optype = TY_REAL
+ do i = 1, nargs {
+ if (O_TYPE(args[i]) == TY_DOUBLE || O_TYPE(args[i]) == TY_LONG)
+ optype = TY_DOUBLE
+ }
+ do i = 1, nargs {
+ if (O_TYPE(args[i]) != optype)
+ call xvv_chtype (args[i], args[i], optype)
+ }
+ call xvv_initop (out, O_LEN(args[1]), optype)
+
+ case F_MOD, F_MIN, F_MAX, F_MEDIAN:
+ # These functions may have multiple arguments, all of which
+ # should be the same type.
+
+ optype = O_TYPE(args[1])
+ nelem = O_LEN(args[1])
+ do i = 2, nargs {
+ optype = xvv_newtype (optype, O_TYPE(args[i]))
+ if (O_LEN(args[i]) > 0)
+ if (nelem > 0)
+ nelem = min (nelem, O_LEN(args[i]))
+ else if (nelem == 0)
+ nelem = O_LEN(args[i])
+ }
+
+ do i = 1, nargs
+ if (O_TYPE(args[i]) != optype)
+ call xvv_chtype (args[i], args[i], optype)
+
+ if (nargs == 1 && opcode == F_MEDIAN)
+ nelem = 0
+ call xvv_initop (out, nelem, optype)
+
+ case F_LEN:
+ # This function always returns an integer scalar value.
+ nelem = 0
+ optype = TY_INT
+ call xvv_initop (out, nelem, optype)
+
+ case F_HIV, F_LOV:
+ # These functions return a scalar value.
+ nelem = 0
+ optype = O_TYPE(args[1])
+ if (optype == TY_BOOL)
+ optype = TY_INT
+ call xvv_initop (out, nelem, optype)
+
+ case F_SUM, F_MEAN, F_STDDEV:
+ # These functions require a vector argument and return a scalar
+ # value.
+
+ nelem = 0
+ optype = O_TYPE(args[1])
+ if (optype == TY_BOOL)
+ optype = TY_INT
+
+ if (optype == TY_DOUBLE)
+ call xvv_initop (out, nelem, TY_DOUBLE)
+ else
+ call xvv_initop (out, nelem, TY_REAL)
+
+ case F_SORT, F_SHIFT:
+ # Vector to vector, no type conversions.
+ nelem = O_LEN(args[1])
+ optype = O_TYPE(args[1])
+ call xvv_initop (out, nelem, optype)
+
+ default:
+ optype = 0
+ }
+
+ # Evaluate the function.
+ ap = args[1]
+
+ switch (opcode) {
+ case F_ABS:
+ call xvv_initop (out, O_LEN(ap), O_TYPE(ap))
+ switch (O_TYPE(ap)) {
+
+ case TY_SHORT:
+ if (O_LEN(ap) > 0) {
+ call aabss (Mems[O_VALP(ap)], Mems[O_VALP(out)],
+ O_LEN(ap))
+ } else
+ O_VALS(out) = abs(O_VALS(ap))
+
+ case TY_INT:
+ if (O_LEN(ap) > 0) {
+ call aabsi (Memi[O_VALP(ap)], Memi[O_VALP(out)],
+ O_LEN(ap))
+ } else
+ O_VALI(out) = abs(O_VALI(ap))
+
+ case TY_LONG:
+ if (O_LEN(ap) > 0) {
+ call aabsl (Meml[O_VALP(ap)], Meml[O_VALP(out)],
+ O_LEN(ap))
+ } else
+ O_VALL(out) = abs(O_VALL(ap))
+
+ case TY_REAL:
+ if (O_LEN(ap) > 0) {
+ call aabsr (Memr[O_VALP(ap)], Memr[O_VALP(out)],
+ O_LEN(ap))
+ } else
+ O_VALR(out) = abs(O_VALR(ap))
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) > 0) {
+ call aabsd (Memd[O_VALP(ap)], Memd[O_VALP(out)],
+ O_LEN(ap))
+ } else
+ O_VALD(out) = abs(O_VALD(ap))
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_ACOS:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = acos (Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = acos (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = acos (Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = acos (O_VALD(ap))
+
+ case F_ASIN:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = asin (Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = asin (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = asin (Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = asin (O_VALD(ap))
+
+ case F_COS:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = cos (Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = cos (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = cos (Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = cos (O_VALD(ap))
+
+ case F_COSH:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = cosh (Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = cosh (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = cosh (Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = cosh (O_VALD(ap))
+
+ case F_DEG:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = RADTODEG(Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = RADTODEG (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = RADTODEG(Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = RADTODEG (O_VALD(ap))
+
+ case F_EXP:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = exp (Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = exp (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = exp (Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = exp (O_VALD(ap))
+
+ case F_LOG:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ op = O_VALP(out)
+ do i = 1, O_LEN(ap) {
+ v_r = Memr[O_VALP(ap)+i-1]
+ if (rangecheck && v_r <= 0)
+ Memr[op] = 0
+ else
+ Memr[op] = log (v_r)
+ op = op + 1
+ }
+ } else {
+ v_r = O_VALR(ap)
+ if (rangecheck && v_r <= 0)
+ O_VALR(out) = 0
+ else
+ O_VALR(out) = log (v_r)
+ }
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ op = O_VALP(out)
+ do i = 1, O_LEN(ap) {
+ v_d = Memd[O_VALP(ap)+i-1]
+ if (rangecheck && v_d <= 0)
+ Memd[op] = 0
+ else
+ Memd[op] = log (v_d)
+ op = op + 1
+ }
+ } else {
+ v_d = O_VALD(ap)
+ if (rangecheck && v_d <= 0)
+ O_VALD(out) = 0
+ else
+ O_VALD(out) = log (v_d)
+ }
+
+ case F_LOG10:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ op = O_VALP(out)
+ do i = 1, O_LEN(ap) {
+ v_r = Memr[O_VALP(ap)+i-1]
+ if (rangecheck && v_r <= 0)
+ Memr[op] = 0
+ else
+ Memr[op] = log10 (v_r)
+ op = op + 1
+ }
+ } else {
+ v_r = O_VALR(ap)
+ if (rangecheck && v_r <= 0)
+ O_VALR(out) = 0
+ else
+ O_VALR(out) = log10 (v_r)
+ }
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ op = O_VALP(out)
+ do i = 1, O_LEN(ap) {
+ v_d = Memd[O_VALP(ap)+i-1]
+ if (rangecheck && v_d <= 0)
+ Memd[op] = 0
+ else
+ Memd[op] = log10 (v_d)
+ op = op + 1
+ }
+ } else {
+ v_d = O_VALD(ap)
+ if (rangecheck && v_d <= 0)
+ O_VALD(out) = 0
+ else
+ O_VALD(out) = log10 (v_d)
+ }
+
+ case F_RAD:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = DEGTORAD(Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = DEGTORAD (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = DEGTORAD(Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = DEGTORAD (O_VALD(ap))
+
+ case F_SIN:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = sin (Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = sin (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = sin (Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = sin (O_VALD(ap))
+
+ case F_SINH:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = sinh (Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = sinh (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = sinh (Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = sinh (O_VALD(ap))
+
+ case F_SQRT:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ op = O_VALP(out)
+ do i = 1, O_LEN(ap) {
+ v_r = Memr[O_VALP(ap)+i-1]
+ if (rangecheck && v_r < 0)
+ Memr[op] = 0
+ else
+ Memr[op] = sqrt (v_r)
+ op = op + 1
+ }
+ } else {
+ v_r = O_VALR(ap)
+ if (rangecheck && v_r <= 0)
+ O_VALR(out) = 0
+ else
+ O_VALR(out) = sqrt (v_r)
+ }
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ op = O_VALP(out)
+ do i = 1, O_LEN(ap) {
+ v_d = Memd[O_VALP(ap)+i-1]
+ if (rangecheck && v_d < 0)
+ Memd[op] = 0
+ else
+ Memd[op] = sqrt (v_d)
+ op = op + 1
+ }
+ } else {
+ v_d = O_VALD(ap)
+ if (rangecheck && v_d <= 0)
+ O_VALD(out) = 0
+ else
+ O_VALD(out) = sqrt (v_d)
+ }
+
+ case F_TAN:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = tan (Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = tan (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = tan (Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = tan (O_VALD(ap))
+
+ case F_TANH:
+
+ if (optype == TY_REAL)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] = tanh (Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = tanh (O_VALR(ap))
+
+ if (optype == TY_DOUBLE)
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] = tanh (Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = tanh (O_VALD(ap))
+
+
+ case F_LEN:
+ # Vector length.
+ O_VALI(out) = O_LEN(ap)
+
+ case F_HIV:
+ # High value.
+ switch (optype) {
+
+ case TY_SHORT:
+ if (O_LEN(ap) > 0)
+ O_VALS(out) = ahivs (Mems[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VALS(out) = O_VALS(ap)
+
+ case TY_INT:
+ if (O_LEN(ap) > 0)
+ O_VALI(out) = ahivi (Memi[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VALI(out) = O_VALI(ap)
+
+ case TY_LONG:
+ if (O_LEN(ap) > 0)
+ O_VALL(out) = ahivl (Meml[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VALL(out) = O_VALL(ap)
+
+ case TY_REAL:
+ if (O_LEN(ap) > 0)
+ O_VALR(out) = ahivr (Memr[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VALR(out) = O_VALR(ap)
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) > 0)
+ O_VALD(out) = ahivd (Memd[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VALD(out) = O_VALD(ap)
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+ case F_LOV:
+ # Low value.
+ switch (optype) {
+
+ case TY_SHORT:
+ if (O_LEN(ap) > 0)
+ O_VALS(out) = alovs (Mems[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VALS(out) = O_VALS(ap)
+
+ case TY_INT:
+ if (O_LEN(ap) > 0)
+ O_VALI(out) = alovi (Memi[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VALI(out) = O_VALI(ap)
+
+ case TY_LONG:
+ if (O_LEN(ap) > 0)
+ O_VALL(out) = alovl (Meml[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VALL(out) = O_VALL(ap)
+
+ case TY_REAL:
+ if (O_LEN(ap) > 0)
+ O_VALR(out) = alovr (Memr[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VALR(out) = O_VALR(ap)
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) > 0)
+ O_VALD(out) = alovd (Memd[O_VALP(ap)], O_LEN(ap))
+ else
+ O_VALD(out) = O_VALD(ap)
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_SUM:
+ # Vector sum.
+ switch (optype) {
+
+ case TY_SHORT:
+ if (O_LEN(ap) > 0)
+ v_r = asums (Mems[O_VALP(ap)], O_LEN(ap))
+ else
+ v_r = O_VALS(ap)
+
+ case TY_INT:
+ if (O_LEN(ap) > 0)
+ v_r = asumi (Memi[O_VALP(ap)], O_LEN(ap))
+ else
+ v_r = O_VALI(ap)
+
+ case TY_LONG:
+ if (O_LEN(ap) > 0)
+ v_r = asuml (Meml[O_VALP(ap)], O_LEN(ap))
+ else
+ v_r = O_VALL(ap)
+
+ case TY_REAL:
+ if (O_LEN(ap) > 0)
+ v_r = asumr (Memr[O_VALP(ap)], O_LEN(ap))
+ else
+ v_r = O_VALR(ap)
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) > 0)
+ v_d = asumd (Memd[O_VALP(ap)], O_LEN(ap))
+ else
+ v_d = O_VALD(ap)
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ if (optype == TY_DOUBLE)
+ O_VALD(out) = v_d
+ else
+ O_VALR(out) = v_r
+
+ case F_MEAN, F_STDDEV:
+ # Compute the mean or standard deviation of a vector. An optional
+ # second argument may be supplied to compute a K-sigma rejection
+ # mean and sigma.
+
+ if (nargs == 2) {
+ if (O_LEN(args[2]) > 0)
+ call xvv_error1 ("%s: ksigma arg must be a scalar" , fcn)
+
+ switch (O_TYPE(args[2])) {
+ case TY_REAL:
+ v_r = O_VALR(args[2])
+ v_d = v_r
+ case TY_DOUBLE:
+ v_d = O_VALD(args[2])
+ v_r = v_d
+ default:
+ call xvv_chtype (args[2], args[2], TY_REAL)
+ v_r = O_VALR(args[2])
+ v_d = v_r
+ }
+ } else {
+ v_r = 0.0
+ v_d = 0.0
+ }
+
+ switch (optype) {
+
+ case TY_SHORT:
+ v_i = aravs (Mems[O_VALP(ap)], O_LEN(ap), mean_r,sigma_r,v_r)
+
+ case TY_INT:
+ v_i = aravi (Memi[O_VALP(ap)], O_LEN(ap), mean_r,sigma_r,v_r)
+
+ case TY_REAL:
+ v_i = aravr (Memr[O_VALP(ap)], O_LEN(ap), mean_r,sigma_r,v_r)
+
+
+ case TY_LONG:
+ v_i = aravl (Meml[O_VALP(ap)], O_LEN(ap), mean_d,sigma_d,v_d)
+
+ case TY_DOUBLE:
+ v_i = aravd (Memd[O_VALP(ap)], O_LEN(ap), mean_d,sigma_d,v_d)
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ if (opcode == F_MEAN) {
+ if (O_TYPE(out) == TY_REAL)
+ O_VALR(out) = mean_r
+ else
+ O_VALD(out) = mean_d
+ } else {
+ if (O_TYPE(out) == TY_REAL)
+ O_VALR(out) = sigma_r
+ else
+ O_VALD(out) = sigma_d
+ }
+
+ case F_MEDIAN:
+ # Compute the median value of a vector, or the vector median
+ # of 3 or more vectors.
+
+ switch (nargs) {
+ case 1:
+ switch (optype) {
+
+ case TY_SHORT:
+ O_VALS(out) = ameds (Mems[O_VALP(ap)], O_LEN(ap))
+
+ case TY_INT:
+ O_VALI(out) = amedi (Memi[O_VALP(ap)], O_LEN(ap))
+
+ case TY_LONG:
+ O_VALL(out) = amedl (Meml[O_VALP(ap)], O_LEN(ap))
+
+ case TY_REAL:
+ O_VALR(out) = amedr (Memr[O_VALP(ap)], O_LEN(ap))
+
+ case TY_DOUBLE:
+ O_VALD(out) = amedd (Memd[O_VALP(ap)], O_LEN(ap))
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+ case 3:
+ switch (optype) {
+
+ case TY_SHORT:
+ call amed3s (Mems[O_VALP(args[1])],
+ Mems[O_VALP(args[2])],
+ Mems[O_VALP(args[3])],
+ Mems[O_VALP(out)], nelem)
+
+ case TY_INT:
+ call amed3i (Memi[O_VALP(args[1])],
+ Memi[O_VALP(args[2])],
+ Memi[O_VALP(args[3])],
+ Memi[O_VALP(out)], nelem)
+
+ case TY_LONG:
+ call amed3l (Meml[O_VALP(args[1])],
+ Meml[O_VALP(args[2])],
+ Meml[O_VALP(args[3])],
+ Meml[O_VALP(out)], nelem)
+
+ case TY_REAL:
+ call amed3r (Memr[O_VALP(args[1])],
+ Memr[O_VALP(args[2])],
+ Memr[O_VALP(args[3])],
+ Memr[O_VALP(out)], nelem)
+
+ case TY_DOUBLE:
+ call amed3d (Memd[O_VALP(args[1])],
+ Memd[O_VALP(args[2])],
+ Memd[O_VALP(args[3])],
+ Memd[O_VALP(out)], nelem)
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+ case 4:
+ switch (optype) {
+
+ case TY_SHORT:
+ call amed4s (Mems[O_VALP(args[1])],
+ Mems[O_VALP(args[2])],
+ Mems[O_VALP(args[3])],
+ Mems[O_VALP(args[4])],
+ Mems[O_VALP(out)], nelem)
+
+ case TY_INT:
+ call amed4i (Memi[O_VALP(args[1])],
+ Memi[O_VALP(args[2])],
+ Memi[O_VALP(args[3])],
+ Memi[O_VALP(args[4])],
+ Memi[O_VALP(out)], nelem)
+
+ case TY_LONG:
+ call amed4l (Meml[O_VALP(args[1])],
+ Meml[O_VALP(args[2])],
+ Meml[O_VALP(args[3])],
+ Meml[O_VALP(args[4])],
+ Meml[O_VALP(out)], nelem)
+
+ case TY_REAL:
+ call amed4r (Memr[O_VALP(args[1])],
+ Memr[O_VALP(args[2])],
+ Memr[O_VALP(args[3])],
+ Memr[O_VALP(args[4])],
+ Memr[O_VALP(out)], nelem)
+
+ case TY_DOUBLE:
+ call amed4d (Memd[O_VALP(args[1])],
+ Memd[O_VALP(args[2])],
+ Memd[O_VALP(args[3])],
+ Memd[O_VALP(args[4])],
+ Memd[O_VALP(out)], nelem)
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+ case 5:
+ switch (optype) {
+
+ case TY_SHORT:
+ call amed5s (Mems[O_VALP(args[1])],
+ Mems[O_VALP(args[2])],
+ Mems[O_VALP(args[3])],
+ Mems[O_VALP(args[4])],
+ Mems[O_VALP(args[5])],
+ Mems[O_VALP(out)], nelem)
+
+ case TY_INT:
+ call amed5i (Memi[O_VALP(args[1])],
+ Memi[O_VALP(args[2])],
+ Memi[O_VALP(args[3])],
+ Memi[O_VALP(args[4])],
+ Memi[O_VALP(args[5])],
+ Memi[O_VALP(out)], nelem)
+
+ case TY_LONG:
+ call amed5l (Meml[O_VALP(args[1])],
+ Meml[O_VALP(args[2])],
+ Meml[O_VALP(args[3])],
+ Meml[O_VALP(args[4])],
+ Meml[O_VALP(args[5])],
+ Meml[O_VALP(out)], nelem)
+
+ case TY_REAL:
+ call amed5r (Memr[O_VALP(args[1])],
+ Memr[O_VALP(args[2])],
+ Memr[O_VALP(args[3])],
+ Memr[O_VALP(args[4])],
+ Memr[O_VALP(args[5])],
+ Memr[O_VALP(out)], nelem)
+
+ case TY_DOUBLE:
+ call amed5d (Memd[O_VALP(args[1])],
+ Memd[O_VALP(args[2])],
+ Memd[O_VALP(args[3])],
+ Memd[O_VALP(args[4])],
+ Memd[O_VALP(args[5])],
+ Memd[O_VALP(out)], nelem)
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+ default:
+ call xvv_error1 ("%s: wrong number of arguments", fcn)
+ }
+
+ case F_REPL:
+ # Replicate an item to make a longer vector.
+
+ chunk = O_LEN(ap)
+ optype = O_TYPE(ap)
+ if (optype == TY_BOOL)
+ optype = TY_INT
+
+ if (O_LEN(args[2]) > 0)
+ call xvv_error1 ("%s: replication factor must be a scalar", fcn)
+ if (O_TYPE(args[2]) != TY_INT)
+ call xvv_chtype (args[2], args[2], TY_INT)
+ repl = max (1, O_VALI(args[2]))
+
+ if (chunk <= 0)
+ nelem = repl
+ else
+ nelem = chunk * repl
+ call xvv_initop (out, nelem, optype)
+
+ switch (optype) {
+
+ case TY_SHORT:
+ if (chunk > 0) {
+ ip = O_VALP(ap)
+ op = O_VALP(out)
+ do i = 1, repl {
+ call amovs (Mems[ip], Mems[op], chunk)
+ op = op + chunk
+ }
+ } else
+ call amovks (O_VALS(ap), Mems[O_VALP(out)], nelem)
+
+ case TY_INT:
+ if (chunk > 0) {
+ ip = O_VALP(ap)
+ op = O_VALP(out)
+ do i = 1, repl {
+ call amovi (Memi[ip], Memi[op], chunk)
+ op = op + chunk
+ }
+ } else
+ call amovki (O_VALI(ap), Memi[O_VALP(out)], nelem)
+
+ case TY_LONG:
+ if (chunk > 0) {
+ ip = O_VALP(ap)
+ op = O_VALP(out)
+ do i = 1, repl {
+ call amovl (Meml[ip], Meml[op], chunk)
+ op = op + chunk
+ }
+ } else
+ call amovkl (O_VALL(ap), Meml[O_VALP(out)], nelem)
+
+ case TY_REAL:
+ if (chunk > 0) {
+ ip = O_VALP(ap)
+ op = O_VALP(out)
+ do i = 1, repl {
+ call amovr (Memr[ip], Memr[op], chunk)
+ op = op + chunk
+ }
+ } else
+ call amovkr (O_VALR(ap), Memr[O_VALP(out)], nelem)
+
+ case TY_DOUBLE:
+ if (chunk > 0) {
+ ip = O_VALP(ap)
+ op = O_VALP(out)
+ do i = 1, repl {
+ call amovd (Memd[ip], Memd[op], chunk)
+ op = op + chunk
+ }
+ } else
+ call amovkd (O_VALD(ap), Memd[O_VALP(out)], nelem)
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_SHIFT:
+ # Vector shift.
+ if (O_LEN(args[2]) > 0)
+ call xvv_error1 ("%s: shift arg must be a scalar" , fcn)
+ if (O_TYPE(args[2]) != TY_INT)
+ call xvv_chtype (args[2], args[2], TY_INT)
+ shift = O_VALI(args[2])
+
+ if (abs(shift) > nelem) {
+ if (shift > 0)
+ shift = nelem
+ else
+ shift = -nelem
+ }
+
+ switch (optype) {
+
+ case TY_SHORT:
+ if (nelem > 0) {
+ do i = 1, nelem {
+ j = i - shift
+ if (j < 1)
+ j = j + nelem
+ else if (j > nelem)
+ j = j - nelem
+ Mems[O_VALP(out)+i-1] = Mems[O_VALP(ap)+j-1]
+ }
+ } else
+ O_VALS(out) = (O_VALS(ap))
+
+ case TY_INT:
+ if (nelem > 0) {
+ do i = 1, nelem {
+ j = i - shift
+ if (j < 1)
+ j = j + nelem
+ else if (j > nelem)
+ j = j - nelem
+ Memi[O_VALP(out)+i-1] = Memi[O_VALP(ap)+j-1]
+ }
+ } else
+ O_VALI(out) = (O_VALI(ap))
+
+ case TY_LONG:
+ if (nelem > 0) {
+ do i = 1, nelem {
+ j = i - shift
+ if (j < 1)
+ j = j + nelem
+ else if (j > nelem)
+ j = j - nelem
+ Meml[O_VALP(out)+i-1] = Meml[O_VALP(ap)+j-1]
+ }
+ } else
+ O_VALL(out) = (O_VALL(ap))
+
+ case TY_REAL:
+ if (nelem > 0) {
+ do i = 1, nelem {
+ j = i - shift
+ if (j < 1)
+ j = j + nelem
+ else if (j > nelem)
+ j = j - nelem
+ Memr[O_VALP(out)+i-1] = Memr[O_VALP(ap)+j-1]
+ }
+ } else
+ O_VALR(out) = (O_VALR(ap))
+
+ case TY_DOUBLE:
+ if (nelem > 0) {
+ do i = 1, nelem {
+ j = i - shift
+ if (j < 1)
+ j = j + nelem
+ else if (j > nelem)
+ j = j - nelem
+ Memd[O_VALP(out)+i-1] = Memd[O_VALP(ap)+j-1]
+ }
+ } else
+ O_VALD(out) = (O_VALD(ap))
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_SORT:
+ # Sort a vector.
+ switch (optype) {
+
+ case TY_SHORT:
+ if (nelem > 0)
+ call asrts (Mems[O_VALP(ap)], Mems[O_VALP(out)], nelem)
+ else
+ O_VALS(out) = (O_VALS(ap))
+
+ case TY_INT:
+ if (nelem > 0)
+ call asrti (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+ else
+ O_VALI(out) = (O_VALI(ap))
+
+ case TY_LONG:
+ if (nelem > 0)
+ call asrtl (Meml[O_VALP(ap)], Meml[O_VALP(out)], nelem)
+ else
+ O_VALL(out) = (O_VALL(ap))
+
+ case TY_REAL:
+ if (nelem > 0)
+ call asrtr (Memr[O_VALP(ap)], Memr[O_VALP(out)], nelem)
+ else
+ O_VALR(out) = (O_VALR(ap))
+
+ case TY_DOUBLE:
+ if (nelem > 0)
+ call asrtd (Memd[O_VALP(ap)], Memd[O_VALP(out)], nelem)
+ else
+ O_VALD(out) = (O_VALD(ap))
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_ATAN, F_ATAN2:
+
+ if (optype == TY_REAL) {
+ if (nargs == 1) {
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] =
+ atan (Memr[O_VALP(ap)+i-1])
+ } else
+ O_VALR(out) = atan (O_VALR(ap))
+ } else {
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memr[O_VALP(out)+i-1] =
+ atan2 (Memr[O_VALP(args[1])+i-1],
+ Memr[O_VALP(args[2])+i-1])
+ } else
+ O_VALR(out) = atan2(O_VALR(args[1]), O_VALR(args[2]))
+ }
+ }
+
+ if (optype == TY_DOUBLE) {
+ if (nargs == 1) {
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] =
+ atan (Memd[O_VALP(ap)+i-1])
+ } else
+ O_VALD(out) = atan (O_VALD(ap))
+ } else {
+ if (O_LEN(ap) > 0) {
+ do i = 1, O_LEN(ap)
+ Memd[O_VALP(out)+i-1] =
+ atan2 (Memd[O_VALP(args[1])+i-1],
+ Memd[O_VALP(args[2])+i-1])
+ } else
+ O_VALD(out) = atan2(O_VALD(args[1]), O_VALD(args[2]))
+ }
+ }
+
+
+ case F_MOD:
+ in1 = args[1]
+ in2 = args[2]
+
+ switch (optype) {
+
+ case TY_SHORT:
+ if (O_LEN(in1) <= 0) {
+ O_VALS(out) = mod (O_VALS(in1), O_VALS(in2))
+ } else if (O_LEN(in2) <= 0) {
+ call amodks (Mems[O_VALP(in1)], O_VALS(in2),
+ Mems[O_VALP(out)], nelem)
+ } else {
+ call amods (Mems[O_VALP(in1)], Mems[O_VALP(in2)],
+ Mems[O_VALP(out)], nelem)
+ }
+
+ case TY_INT:
+ if (O_LEN(in1) <= 0) {
+ O_VALI(out) = mod (O_VALI(in1), O_VALI(in2))
+ } else if (O_LEN(in2) <= 0) {
+ call amodki (Memi[O_VALP(in1)], O_VALI(in2),
+ Memi[O_VALP(out)], nelem)
+ } else {
+ call amodi (Memi[O_VALP(in1)], Memi[O_VALP(in2)],
+ Memi[O_VALP(out)], nelem)
+ }
+
+ case TY_LONG:
+ if (O_LEN(in1) <= 0) {
+ O_VALL(out) = mod (O_VALL(in1), O_VALL(in2))
+ } else if (O_LEN(in2) <= 0) {
+ call amodkl (Meml[O_VALP(in1)], O_VALL(in2),
+ Meml[O_VALP(out)], nelem)
+ } else {
+ call amodl (Meml[O_VALP(in1)], Meml[O_VALP(in2)],
+ Meml[O_VALP(out)], nelem)
+ }
+
+ case TY_REAL:
+ if (O_LEN(in1) <= 0) {
+ O_VALR(out) = mod (O_VALR(in1), O_VALR(in2))
+ } else if (O_LEN(in2) <= 0) {
+ call amodkr (Memr[O_VALP(in1)], O_VALR(in2),
+ Memr[O_VALP(out)], nelem)
+ } else {
+ call amodr (Memr[O_VALP(in1)], Memr[O_VALP(in2)],
+ Memr[O_VALP(out)], nelem)
+ }
+
+ case TY_DOUBLE:
+ if (O_LEN(in1) <= 0) {
+ O_VALD(out) = mod (O_VALD(in1), O_VALD(in2))
+ } else if (O_LEN(in2) <= 0) {
+ call amodkd (Memd[O_VALP(in1)], O_VALD(in2),
+ Memd[O_VALP(out)], nelem)
+ } else {
+ call amodd (Memd[O_VALP(in1)], Memd[O_VALP(in2)],
+ Memd[O_VALP(out)], nelem)
+ }
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_MAX:
+ switch (optype) {
+
+ case TY_SHORT:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovks (O_VALS(ap), Mems[O_VALP(out)], nelem)
+ else
+ O_VALS(out) = O_VALS(ap)
+ } else
+ call amovs (Mems[O_VALP(ap)], Mems[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VALS(out) = max (O_VALS(ap), O_VALS(out))
+ else {
+ call amaxks (Mems[O_VALP(out)], O_VALS(ap),
+ Mems[O_VALP(out)], nelem)
+ }
+ } else {
+ call amaxs (Mems[O_VALP(out)], Mems[O_VALP(ap)],
+ Mems[O_VALP(out)], nelem)
+ }
+ }
+
+ case TY_INT:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovki (O_VALI(ap), Memi[O_VALP(out)], nelem)
+ else
+ O_VALI(out) = O_VALI(ap)
+ } else
+ call amovi (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VALI(out) = max (O_VALI(ap), O_VALI(out))
+ else {
+ call amaxki (Memi[O_VALP(out)], O_VALI(ap),
+ Memi[O_VALP(out)], nelem)
+ }
+ } else {
+ call amaxi (Memi[O_VALP(out)], Memi[O_VALP(ap)],
+ Memi[O_VALP(out)], nelem)
+ }
+ }
+
+ case TY_LONG:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovkl (O_VALL(ap), Meml[O_VALP(out)], nelem)
+ else
+ O_VALL(out) = O_VALL(ap)
+ } else
+ call amovl (Meml[O_VALP(ap)], Meml[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VALL(out) = max (O_VALL(ap), O_VALL(out))
+ else {
+ call amaxkl (Meml[O_VALP(out)], O_VALL(ap),
+ Meml[O_VALP(out)], nelem)
+ }
+ } else {
+ call amaxl (Meml[O_VALP(out)], Meml[O_VALP(ap)],
+ Meml[O_VALP(out)], nelem)
+ }
+ }
+
+ case TY_REAL:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovkr (O_VALR(ap), Memr[O_VALP(out)], nelem)
+ else
+ O_VALR(out) = O_VALR(ap)
+ } else
+ call amovr (Memr[O_VALP(ap)], Memr[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VALR(out) = max (O_VALR(ap), O_VALR(out))
+ else {
+ call amaxkr (Memr[O_VALP(out)], O_VALR(ap),
+ Memr[O_VALP(out)], nelem)
+ }
+ } else {
+ call amaxr (Memr[O_VALP(out)], Memr[O_VALP(ap)],
+ Memr[O_VALP(out)], nelem)
+ }
+ }
+
+ case TY_DOUBLE:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovkd (O_VALD(ap), Memd[O_VALP(out)], nelem)
+ else
+ O_VALD(out) = O_VALD(ap)
+ } else
+ call amovd (Memd[O_VALP(ap)], Memd[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VALD(out) = max (O_VALD(ap), O_VALD(out))
+ else {
+ call amaxkd (Memd[O_VALP(out)], O_VALD(ap),
+ Memd[O_VALP(out)], nelem)
+ }
+ } else {
+ call amaxd (Memd[O_VALP(out)], Memd[O_VALP(ap)],
+ Memd[O_VALP(out)], nelem)
+ }
+ }
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_MIN:
+ switch (optype) {
+
+ case TY_SHORT:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovks (O_VALS(ap), Mems[O_VALP(out)], nelem)
+ else
+ O_VALS(out) = O_VALS(ap)
+ } else
+ call amovs (Mems[O_VALP(ap)], Mems[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VALS(out) = min (O_VALS(ap), O_VALS(out))
+ else {
+ call aminks (Mems[O_VALP(out)], O_VALS(ap),
+ Mems[O_VALP(out)], nelem)
+ }
+ } else {
+ call amins (Mems[O_VALP(out)], Mems[O_VALP(ap)],
+ Mems[O_VALP(out)], nelem)
+ }
+ }
+
+ case TY_INT:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovki (O_VALI(ap), Memi[O_VALP(out)], nelem)
+ else
+ O_VALI(out) = O_VALI(ap)
+ } else
+ call amovi (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VALI(out) = min (O_VALI(ap), O_VALI(out))
+ else {
+ call aminki (Memi[O_VALP(out)], O_VALI(ap),
+ Memi[O_VALP(out)], nelem)
+ }
+ } else {
+ call amini (Memi[O_VALP(out)], Memi[O_VALP(ap)],
+ Memi[O_VALP(out)], nelem)
+ }
+ }
+
+ case TY_LONG:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovkl (O_VALL(ap), Meml[O_VALP(out)], nelem)
+ else
+ O_VALL(out) = O_VALL(ap)
+ } else
+ call amovl (Meml[O_VALP(ap)], Meml[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VALL(out) = min (O_VALL(ap), O_VALL(out))
+ else {
+ call aminkl (Meml[O_VALP(out)], O_VALL(ap),
+ Meml[O_VALP(out)], nelem)
+ }
+ } else {
+ call aminl (Meml[O_VALP(out)], Meml[O_VALP(ap)],
+ Meml[O_VALP(out)], nelem)
+ }
+ }
+
+ case TY_REAL:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovkr (O_VALR(ap), Memr[O_VALP(out)], nelem)
+ else
+ O_VALR(out) = O_VALR(ap)
+ } else
+ call amovr (Memr[O_VALP(ap)], Memr[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VALR(out) = min (O_VALR(ap), O_VALR(out))
+ else {
+ call aminkr (Memr[O_VALP(out)], O_VALR(ap),
+ Memr[O_VALP(out)], nelem)
+ }
+ } else {
+ call aminr (Memr[O_VALP(out)], Memr[O_VALP(ap)],
+ Memr[O_VALP(out)], nelem)
+ }
+ }
+
+ case TY_DOUBLE:
+ # Copy the first argument.
+ ap = args[1]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) > 0)
+ call amovkd (O_VALD(ap), Memd[O_VALP(out)], nelem)
+ else
+ O_VALD(out) = O_VALD(ap)
+ } else
+ call amovd (Memd[O_VALP(ap)], Memd[O_VALP(out)], nelem)
+
+ # Process the second and remaining arguments.
+ do i = 2, nargs {
+ ap = args[i]
+ if (O_LEN(ap) <= 0) {
+ if (O_LEN(out) <= 0)
+ O_VALD(out) = min (O_VALD(ap), O_VALD(out))
+ else {
+ call aminkd (Memd[O_VALP(out)], O_VALD(ap),
+ Memd[O_VALP(out)], nelem)
+ }
+ } else {
+ call amind (Memd[O_VALP(out)], Memd[O_VALP(ap)],
+ Memd[O_VALP(out)], nelem)
+ }
+ }
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_BOOL:
+ nelem = 0
+ if (O_LEN(ap) > 0 && O_TYPE(ap) != TY_CHAR)
+ nelem = O_LEN(ap)
+ call xvv_initop (out, nelem, TY_BOOL)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALI(ap)
+ else
+ call amovi (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_CHAR:
+ ch = O_VALC(ap)
+ O_VALI(out) = btoi (ch == 'y' || ch == 'Y')
+
+
+ case TY_SHORT:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = btoi (O_VALS(ap) != 0)
+ else {
+ v_s = 0
+ call abneks (Mems[O_VALP(ap)], v_s, Memi[O_VALP(out)],
+ nelem)
+ }
+
+ case TY_INT:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = btoi (O_VALI(ap) != 0)
+ else {
+ v_i = 0
+ call abneki (Memi[O_VALP(ap)], v_i, Memi[O_VALP(out)],
+ nelem)
+ }
+
+ case TY_LONG:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = btoi (O_VALL(ap) != 0)
+ else {
+ v_l = 0
+ call abnekl (Meml[O_VALP(ap)], v_l, Memi[O_VALP(out)],
+ nelem)
+ }
+
+ case TY_REAL:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = btoi (O_VALR(ap) != 0.0)
+ else {
+ v_r = 0.0
+ call abnekr (Memr[O_VALP(ap)], v_r, Memi[O_VALP(out)],
+ nelem)
+ }
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = btoi (O_VALD(ap) != 0.0D0)
+ else {
+ v_d = 0.0D0
+ call abnekd (Memd[O_VALP(ap)], v_d, Memi[O_VALP(out)],
+ nelem)
+ }
+
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_SHORT:
+ nelem = 0
+ if (O_LEN(ap) > 0 && O_TYPE(ap) != TY_CHAR)
+ nelem = O_LEN(ap)
+ call xvv_initop (out, nelem, TY_SHORT)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_LEN(ap) <= 0)
+ O_VALS(out) = O_VALI(ap)
+ else
+ call achtis (Memi[O_VALP(ap)], Mems[O_VALP(out)], nelem)
+
+ case TY_CHAR:
+ ip = O_VALP(ap)
+ if (gctod (Memc, ip, v_d) <= 0)
+ O_VALS(out) = 0
+ else
+ O_VALS(out) = v_d
+
+
+ case TY_SHORT:
+ if (O_LEN(ap) <= 0)
+ O_VALS(out) = O_VALS(ap)
+ else
+ call achtss (Mems[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_INT:
+ if (O_LEN(ap) <= 0)
+ O_VALS(out) = O_VALI(ap)
+ else
+ call achtis (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_LONG:
+ if (O_LEN(ap) <= 0)
+ O_VALS(out) = O_VALL(ap)
+ else
+ call achtls (Meml[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_REAL:
+ if (O_LEN(ap) <= 0)
+ O_VALS(out) = O_VALR(ap)
+ else
+ call achtrs (Memr[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) <= 0)
+ O_VALS(out) = O_VALD(ap)
+ else
+ call achtds (Memd[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_INT:
+ nelem = 0
+ if (O_LEN(ap) > 0 && O_TYPE(ap) != TY_CHAR)
+ nelem = O_LEN(ap)
+ call xvv_initop (out, nelem, TY_INT)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALI(ap)
+ else
+ call amovi (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_CHAR:
+ ip = O_VALP(ap)
+ if (gctod (Memc, ip, v_d) <= 0)
+ O_VALI(out) = 0
+ else
+ O_VALI(out) = v_d
+
+
+ case TY_SHORT:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALS(ap)
+ else
+ call achtsi (Mems[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_INT:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALI(ap)
+ else
+ call achtii (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_LONG:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALL(ap)
+ else
+ call achtli (Meml[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_REAL:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALR(ap)
+ else
+ call achtri (Memr[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALD(ap)
+ else
+ call achtdi (Memd[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_LONG:
+ nelem = 0
+ if (O_LEN(ap) > 0 && O_TYPE(ap) != TY_CHAR)
+ nelem = O_LEN(ap)
+ call xvv_initop (out, nelem, TY_LONG)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_LEN(ap) <= 0)
+ O_VALL(out) = O_VALI(ap)
+ else
+ call amovi (Memi[O_VALP(ap)], Meml[O_VALP(out)], nelem)
+
+ case TY_CHAR:
+ ip = O_VALP(ap)
+ if (gctod (Memc, ip, v_d) <= 0)
+ O_VALL(out) = 0
+ else
+ O_VALL(out) = v_d
+
+
+ case TY_SHORT:
+ if (O_LEN(ap) <= 0)
+ O_VALL(out) = O_VALS(ap)
+ else
+ call achtsl (Mems[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_INT:
+ if (O_LEN(ap) <= 0)
+ O_VALL(out) = O_VALI(ap)
+ else
+ call achtil (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_LONG:
+ if (O_LEN(ap) <= 0)
+ O_VALL(out) = O_VALL(ap)
+ else
+ call achtll (Meml[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_REAL:
+ if (O_LEN(ap) <= 0)
+ O_VALL(out) = O_VALR(ap)
+ else
+ call achtrl (Memr[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) <= 0)
+ O_VALL(out) = O_VALD(ap)
+ else
+ call achtdl (Memd[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_NINT:
+ nelem = 0
+ if (O_LEN(ap) > 0 && O_TYPE(ap) != TY_CHAR)
+ nelem = O_LEN(ap)
+ call xvv_initop (out, nelem, TY_INT)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALI(ap)
+ else
+ call amovi (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_CHAR:
+ ip = O_VALP(ap)
+ if (gctod (Memc, ip, v_d) <= 0)
+ O_VALI(out) = 0
+ else
+ O_VALI(out) = nint (v_d)
+
+
+ case TY_SHORT:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALS(ap)
+ else
+ call achtsi (Mems[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_INT:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALI(ap)
+ else
+ call achtii (Memi[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+ case TY_LONG:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = O_VALL(ap)
+ else
+ call achtli (Meml[O_VALP(ap)], Memi[O_VALP(out)], nelem)
+
+
+
+ case TY_REAL:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = nint (O_VALR(ap))
+ else {
+ do i = 1, nelem
+ Memi[O_VALP(out)+i-1] = nint (Memr[O_VALP(ap)+i-1])
+ }
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) <= 0)
+ O_VALI(out) = nint (O_VALD(ap))
+ else {
+ do i = 1, nelem
+ Memi[O_VALP(out)+i-1] = nint (Memd[O_VALP(ap)+i-1])
+ }
+
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_REAL:
+ nelem = 0
+ if (O_LEN(ap) > 0 && O_TYPE(ap) != TY_CHAR)
+ nelem = O_LEN(ap)
+ call xvv_initop (out, nelem, TY_REAL)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_LEN(ap) <= 0)
+ O_VALR(out) = O_VALI(ap)
+ else
+ call achtir (Memi[O_VALP(ap)], Memr[O_VALP(out)], nelem)
+
+ case TY_CHAR:
+ ip = O_VALP(ap)
+ if (gctod (Memc, ip, v_d) <= 0)
+ O_VALR(out) = 0
+ else
+ O_VALR(out) = v_d
+
+
+ case TY_SHORT:
+ if (O_LEN(ap) <= 0)
+ O_VALR(out) = O_VALS(ap)
+ else
+ call achtsr (Mems[O_VALP(ap)], Memr[O_VALP(out)], nelem)
+
+ case TY_INT:
+ if (O_LEN(ap) <= 0)
+ O_VALR(out) = O_VALI(ap)
+ else
+ call achtir (Memi[O_VALP(ap)], Memr[O_VALP(out)], nelem)
+
+ case TY_LONG:
+ if (O_LEN(ap) <= 0)
+ O_VALR(out) = O_VALL(ap)
+ else
+ call achtlr (Meml[O_VALP(ap)], Memr[O_VALP(out)], nelem)
+
+ case TY_REAL:
+ if (O_LEN(ap) <= 0)
+ O_VALR(out) = O_VALR(ap)
+ else
+ call achtrr (Memr[O_VALP(ap)], Memr[O_VALP(out)], nelem)
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) <= 0)
+ O_VALR(out) = O_VALD(ap)
+ else
+ call achtdr (Memd[O_VALP(ap)], Memr[O_VALP(out)], nelem)
+
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_DOUBLE:
+ nelem = 0
+ if (O_LEN(ap) > 0 && O_TYPE(ap) != TY_CHAR)
+ nelem = O_LEN(ap)
+ call xvv_initop (out, nelem, TY_DOUBLE)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ if (O_LEN(ap) <= 0)
+ O_VALD(out) = O_VALI(ap)
+ else
+ call achtid (Memi[O_VALP(ap)], Memd[O_VALP(out)], nelem)
+
+ case TY_CHAR:
+ ip = O_VALP(ap)
+ if (gctod (Memc, ip, v_d) <= 0)
+ O_VALD(out) = 0
+ else
+ O_VALD(out) = v_d
+
+
+ case TY_SHORT:
+ if (O_LEN(ap) <= 0)
+ O_VALD(out) = O_VALS(ap)
+ else
+ call achtsd (Mems[O_VALP(ap)], Memd[O_VALP(out)], nelem)
+
+ case TY_INT:
+ if (O_LEN(ap) <= 0)
+ O_VALD(out) = O_VALI(ap)
+ else
+ call achtid (Memi[O_VALP(ap)], Memd[O_VALP(out)], nelem)
+
+ case TY_LONG:
+ if (O_LEN(ap) <= 0)
+ O_VALD(out) = O_VALL(ap)
+ else
+ call achtld (Meml[O_VALP(ap)], Memd[O_VALP(out)], nelem)
+
+ case TY_REAL:
+ if (O_LEN(ap) <= 0)
+ O_VALD(out) = O_VALR(ap)
+ else
+ call achtrd (Memr[O_VALP(ap)], Memd[O_VALP(out)], nelem)
+
+ case TY_DOUBLE:
+ if (O_LEN(ap) <= 0)
+ O_VALD(out) = O_VALD(ap)
+ else
+ call achtdd (Memd[O_VALP(ap)], Memd[O_VALP(out)], nelem)
+
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ case F_STR:
+ optype = TY_CHAR
+ if (O_TYPE(ap) == TY_CHAR)
+ nelem = strlen (O_VALC(ap))
+ else
+ nelem = MAX_DIGITS
+ call xvv_initop (out, nelem, TY_CHAR)
+
+ switch (O_TYPE(ap)) {
+ case TY_BOOL:
+ call sprintf (O_VALC(out), nelem, "%b")
+ call pargi (O_VALI(ap))
+ case TY_CHAR:
+ call sprintf (O_VALC(out), nelem, "%s")
+ call pargstr (O_VALC(ap))
+
+ case TY_SHORT:
+ call sprintf (O_VALC(out), nelem, "%d")
+ call pargs (O_VALS(ap))
+
+ case TY_INT:
+ call sprintf (O_VALC(out), nelem, "%d")
+ call pargi (O_VALI(ap))
+
+ case TY_LONG:
+ call sprintf (O_VALC(out), nelem, "%d")
+ call pargl (O_VALL(ap))
+
+
+ case TY_REAL:
+ call sprintf (O_VALC(out), nelem, "%g")
+ call pargr (O_VALR(ap))
+
+ case TY_DOUBLE:
+ call sprintf (O_VALC(out), nelem, "%g")
+ call pargd (O_VALD(ap))
+
+ default:
+ call xvv_error1 (s_badtype, fcn)
+ }
+
+ default:
+ call xvv_error ("callfcn: unknown function type")
+ }
+
+free_
+ # Free any storage used by the argument list operands.
+ do i = 1, nargs
+ call xvv_freeop (args[i])
+
+ call sfree (sp)
+end
+
+
+# XVV_STARTARGLIST -- Allocate an argument list descriptor to receive
+# arguments as a function call is parsed. We are called with either
+# zero or one arguments. The argument list descriptor is pointed to by
+# a ficticious operand. The descriptor itself contains a count of the
+# number of arguments, an array of pointers to the operand structures,
+# as well as storage for the operand structures. The operands must be
+# stored locally since the parser will discard its copy of the operand
+# structure for each argument as the associated grammar rule is reduced.
+
+procedure xvv_startarglist (arg, out)
+
+pointer arg #I pointer to first argument, or NULL
+pointer out #I output operand pointing to arg descriptor
+
+pointer ap
+errchk xvv_initop
+
+begin
+ call xvv_initop (out, LEN_ARGSTRUCT, TY_STRUCT)
+ ap = O_VALP(out)
+
+ if (arg == NULL)
+ A_NARGS(ap) = 0
+ else {
+ A_NARGS(ap) = 1
+ A_ARGP(ap,1) = A_OPS(ap)
+ YYMOVE (arg, A_OPS(ap))
+ }
+end
+
+
+# XVV_ADDARG -- Add an argument to the argument list for a function call.
+
+procedure xvv_addarg (arg, arglist, out)
+
+pointer arg #I pointer to argument to be added
+pointer arglist #I pointer to operand pointing to arglist
+pointer out #I output operand pointing to arg descriptor
+
+pointer ap, o
+int nargs
+
+begin
+ ap = O_VALP(arglist)
+
+ nargs = A_NARGS(ap) + 1
+ A_NARGS(ap) = nargs
+ if (nargs > MAX_ARGS)
+ call xvv_error ("too many function arguments")
+
+ o = A_OPS(ap) + ((nargs - 1) * LEN_OPERAND)
+ A_ARGP(ap,nargs) = o
+ YYMOVE (arg, o)
+
+ YYMOVE (arglist, out)
+end
+
+
+# XVV_ERROR1 -- Take an error action, formatting an error message with one
+# format string plus one string argument.
+
+procedure xvv_error1 (fmt, arg)
+
+char fmt[ARB] #I printf format string
+char arg[ARB] #I string argument
+
+pointer sp, buf
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_LINE, TY_CHAR)
+
+ call sprintf (Memc[buf], SZ_LINE, fmt)
+ call pargstr (arg)
+
+ call xvv_error (Memc[buf])
+ call sfree (sp)
+end
+
+
+# XVV_ERROR2 -- Take an error action, formatting an error message with one
+# format string plus one string argument and one integer argument.
+
+procedure xvv_error2 (fmt, arg1, arg2)
+
+char fmt[ARB] #I printf format string
+char arg1[ARB] #I string argument
+int arg2 #I integer argument
+
+pointer sp, buf
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_LINE, TY_CHAR)
+
+ call sprintf (Memc[buf], SZ_LINE, fmt)
+ call pargstr (arg1)
+ call pargi (arg2)
+
+ call xvv_error (Memc[buf])
+ call sfree (sp)
+end
+
+
+# XVV_ERROR -- Take an error action, given an error message string as the
+# sole argument.
+
+procedure xvv_error (errmsg)
+
+char errmsg[ARB] #I error message
+
+begin
+ call error (1, errmsg)
+end
+
+
+# XVV_CHTYPE -- Change the datatype of an operand. The input and output
+# operands may be the same.
+
+procedure xvv_chtype (o1, o2, dtype)
+
+pointer o1 #I input operand
+pointer o2 #I output operand
+int dtype #I new datatype
+
+short v_s
+int v_i
+long v_l
+real v_r
+double v_d
+pointer vp, ip, op
+bool float, freeval
+int old_type, nelem, ch
+
+pointer coerce()
+int sizeof(), btoi(), gctod()
+string s_badtype "chtype: invalid operand type"
+
+begin
+ old_type = O_TYPE(o1)
+ nelem = O_LEN(o1)
+
+ # No type conversion needed?
+ if (old_type == dtype) {
+ if (o1 != o2) {
+ if (nelem <= 0)
+ YYMOVE (o1, o2)
+ else {
+ call xvv_initop (o2, nelem, old_type)
+ call amovc (O_VALC(o1), O_VALC(o2), nelem * sizeof(dtype))
+ }
+ }
+ return
+ }
+
+ if (nelem <= 0) {
+ # Scalar input operand.
+
+ O_TYPE(o2) = dtype
+ O_LEN(o2) = 0
+ float = false
+
+ # Read the old value into a local variable of type long or double.
+ switch (old_type) {
+ case TY_BOOL:
+ v_l = O_VALI(o1)
+ case TY_CHAR:
+ v_l = 0 # null string?
+
+ case TY_SHORT:
+ v_l = O_VALS(o1)
+
+ case TY_INT:
+ v_l = O_VALI(o1)
+
+ case TY_LONG:
+ v_l = O_VALL(o1)
+
+
+ case TY_REAL:
+ v_d = O_VALR(o1)
+ float = true
+
+ case TY_DOUBLE:
+ v_d = O_VALD(o1)
+ float = true
+
+ default:
+ call xvv_error (s_badtype)
+ }
+
+ # Set the value of the output operand.
+ switch (dtype) {
+ case TY_BOOL:
+ if (float)
+ O_VALI(o2) = btoi (v_d != 0)
+ else
+ O_VALI(o2) = btoi (v_l != 0)
+ case TY_CHAR:
+ call xvv_initop (o2, MAX_DIGITS, TY_CHAR)
+ if (float) {
+ call sprintf (O_VALC(o2), MAX_DIGITS, "%g")
+ call pargd (v_d)
+ } else {
+ call sprintf (O_VALC(o2), MAX_DIGITS, "%d")
+ call pargl (v_l)
+ }
+
+ case TY_SHORT:
+ if (float)
+ O_VALS(o2) = v_d
+ else
+ O_VALS(o2) = v_l
+
+ case TY_INT:
+ if (float)
+ O_VALI(o2) = v_d
+ else
+ O_VALI(o2) = v_l
+
+ case TY_LONG:
+ if (float)
+ O_VALL(o2) = v_d
+ else
+ O_VALL(o2) = v_l
+
+
+ case TY_REAL:
+ if (float)
+ O_VALR(o2) = v_d
+ else
+ O_VALR(o2) = v_l
+
+ case TY_DOUBLE:
+ if (float)
+ O_VALD(o2) = v_d
+ else
+ O_VALD(o2) = v_l
+
+ default:
+ call xvv_error (s_badtype)
+ }
+
+ } else {
+ # Vector input operand.
+
+ # Save a pointer to the input operand data vector, to avoid it
+ # getting clobbered if O1 and O2 are the same operand.
+
+ vp = O_VALP(o1)
+
+ # If we have a char string input operand the output numeric
+ # operand can only be a scalar. If we have a char string output
+ # operand nelem is the length of the string.
+
+ if (old_type == TY_CHAR)
+ nelem = 0
+ else if (dtype == TY_CHAR)
+ nelem = MAX_DIGITS
+
+ # Initialize the output operand O2. The freeval flag is cleared
+ # cleared to keep the initop from freeing the input operand array,
+ # inherited when the input operand is copied (or when the input
+ # and output operands are the same). We free the old operand
+ # array manually below.
+
+ if (o1 != o2)
+ YYMOVE (o1, o2)
+ freeval = (and (O_FLAGS(o1), O_FREEVAL) != 0)
+ O_FLAGS(o2) = and (O_FLAGS(o2), not(O_FREEVAL))
+ call xvv_initop (o2, nelem, dtype)
+
+ # Write output value.
+ switch (dtype) {
+ case TY_BOOL:
+ if (old_type == TY_CHAR) {
+ ch = Memc[vp]
+ O_VALI(o2) = btoi (ch == 'y' || ch == 'Y')
+ } else {
+ switch (old_type) {
+
+ case TY_SHORT:
+ v_s = 0
+ call abneks (Mems[vp], v_s, Memi[O_VALP(o2)], nelem)
+
+ case TY_INT:
+ v_i = 0
+ call abneki (Memi[vp], v_i, Memi[O_VALP(o2)], nelem)
+
+ case TY_LONG:
+ v_l = 0
+ call abnekl (Meml[vp], v_l, Memi[O_VALP(o2)], nelem)
+
+ case TY_REAL:
+ v_r = 0.0
+ call abnekr (Memr[vp], v_r, Memi[O_VALP(o2)], nelem)
+
+ case TY_DOUBLE:
+ v_d = 0.0D0
+ call abnekd (Memd[vp], v_d, Memi[O_VALP(o2)], nelem)
+
+ default:
+ call xvv_error (s_badtype)
+ }
+ }
+
+ case TY_CHAR:
+ call xvv_error (s_badtype)
+
+ case TY_SHORT, TY_INT, TY_LONG, TY_REAL, TY_DOUBLE:
+ switch (old_type) {
+ case TY_BOOL:
+ op = coerce (O_VALP(o2), O_TYPE(o2), TY_CHAR)
+ call achti (Memi[vp], Memc[op], nelem, dtype)
+ case TY_CHAR:
+ ip = vp
+ if (gctod (Memc, ip, v_d) <= 0)
+ v_d = 0
+ switch (dtype) {
+
+ case TY_SHORT:
+ O_VALS(o2) = v_d
+
+ case TY_INT:
+ O_VALI(o2) = v_d
+
+ case TY_LONG:
+ O_VALL(o2) = v_d
+
+ case TY_REAL:
+ O_VALR(o2) = v_d
+
+ case TY_DOUBLE:
+ O_VALD(o2) = v_d
+
+ }
+
+ case TY_SHORT:
+ op = coerce (O_VALP(o2), O_TYPE(o2), TY_CHAR)
+ call achts (Mems[vp], Memc[op], nelem, dtype)
+
+ case TY_INT:
+ op = coerce (O_VALP(o2), O_TYPE(o2), TY_CHAR)
+ call achti (Memi[vp], Memc[op], nelem, dtype)
+
+ case TY_LONG:
+ op = coerce (O_VALP(o2), O_TYPE(o2), TY_CHAR)
+ call achtl (Meml[vp], Memc[op], nelem, dtype)
+
+ case TY_REAL:
+ op = coerce (O_VALP(o2), O_TYPE(o2), TY_CHAR)
+ call achtr (Memr[vp], Memc[op], nelem, dtype)
+
+ case TY_DOUBLE:
+ op = coerce (O_VALP(o2), O_TYPE(o2), TY_CHAR)
+ call achtd (Memd[vp], Memc[op], nelem, dtype)
+
+ default:
+ call xvv_error (s_badtype)
+ }
+ default:
+ call xvv_error (s_badtype)
+ }
+
+ # Free old operand value.
+ if (freeval)
+ call mfree (vp, old_type)
+ }
+end
+
+
+# XVV_INITOP -- Initialize an operand, providing storage for an operand value
+# of the given size and type.
+
+procedure xvv_initop (o, o_len, o_type)
+
+pointer o #I pointer to operand structure
+int o_len #I length of operand (zero if scalar)
+int o_type #I datatype of operand
+
+begin
+ O_LEN(o) = 0
+ call xvv_makeop (o, o_len, o_type)
+end
+
+
+# XVV_MAKEOP -- Set up the operand structure. If the operand structure has
+# already been initialized and array storage allocated, free the old array.
+
+procedure xvv_makeop (o, o_len, o_type)
+
+pointer o #I pointer to operand structure
+int o_len #I length of operand (zero if scalar)
+int o_type #I datatype of operand
+
+errchk malloc
+
+begin
+ # Free old array storage if any.
+ if (O_TYPE(o) != 0 && O_LEN(o) > 0)
+ if (and (O_FLAGS(o), O_FREEVAL) != 0) {
+ if (O_TYPE(o) == TY_BOOL)
+ call mfree (O_VALP(o), TY_INT)
+ else
+ call mfree (O_VALP(o), O_TYPE(o))
+ O_LEN(o) = 0
+ }
+
+ # Set new operand type.
+ O_TYPE(o) = o_type
+
+ # Allocate array storage if nonscalar operand.
+ if (o_len > 0) {
+ if (o_type == TY_BOOL)
+ call malloc (O_VALP(o), o_len, TY_INT)
+ else
+ call malloc (O_VALP(o), o_len, o_type)
+ O_LEN(o) = o_len
+ }
+
+ O_FLAGS(o) = O_FREEVAL
+end
+
+
+# XVV_FREEOP -- Reinitialize an operand structure, i.e., free any associated
+# array storage and clear the operand datatype field, but do not free the
+# operand structure itself (which may be only a segment of an array and not
+# a separately allocated structure).
+
+procedure xvv_freeop (o)
+
+pointer o #I pointer to operand structure
+
+begin
+ # Free old array storage if any.
+ if (O_TYPE(o) != 0 && O_LEN(o) > 0)
+ if (and (O_FLAGS(o), O_FREEVAL) != 0) {
+ if (O_TYPE(o) == TY_BOOL)
+ call mfree (O_VALP(o), TY_INT)
+ else
+ call mfree (O_VALP(o), O_TYPE(o))
+ O_LEN(o) = 0
+ }
+
+ # Either free operand struct or clear the operand type to mark
+ # operand invalid.
+
+ if (and (O_FLAGS(o), O_FREEOP) != 0)
+ call mfree (o, TY_STRUCT)
+ else
+ O_TYPE(o) = 0
+end
+
+
+# XVV_LOADSYMBOLS -- Load a list of symbol names into a symbol table. Each
+# symbol is tagged with an integer code corresponding to its sequence number
+# in the symbol list.
+
+pointer procedure xvv_loadsymbols (s)
+
+char s[ARB] #I symbol list "|sym1|sym2|...|"
+
+int delim, symnum, ip
+pointer sp, symname, st, sym, op
+pointer stopen(), stenter()
+
+begin
+ call smark (sp)
+ call salloc (symname, SZ_FNAME, TY_CHAR)
+
+ st = stopen ("evvexpr", LEN_INDEX, LEN_STAB, LEN_SBUF)
+ delim = s[1]
+ symnum = 0
+
+ for (ip=2; s[ip] != EOS; ip=ip+1) {
+ op = symname
+ while (s[ip] != delim && s[ip] != EOS) {
+ Memc[op] = s[ip]
+ op = op + 1
+ ip = ip + 1
+ }
+ Memc[op] = EOS
+ symnum = symnum + 1
+
+ if (op > symname && IS_ALPHA(Memc[symname])) {
+ sym = stenter (st, Memc[symname], LEN_SYM)
+ SYM_CODE(sym) = symnum
+ }
+ }
+
+ call sfree (sp)
+ return (st)
+end
+
+
+# XVV_NULL -- Return a null value to be used when a computation cannot be
+# performed and range checking is enabled. Perhaps we should permit a user
+# specified value here, however this doesn't really work in an expression
+# evaluator since the value generated may be used in subsequent calculations
+# and hence may change. If more careful treatment of out of range values
+# is needed a conditional expression can be used in which case the value
+# we return here is ignored (but still needed to avoid a hardware exception
+# when computing a vector).
+
+
+short procedure xvv_nulls (ignore)
+short ignore #I ignored
+begin
+ return (0)
+end
+
+int procedure xvv_nulli (ignore)
+int ignore #I ignored
+begin
+ return (0)
+end
+
+long procedure xvv_nulll (ignore)
+long ignore #I ignored
+begin
+ return (0)
+end
+
+real procedure xvv_nullr (ignore)
+real ignore #I ignored
+begin
+ return (0.0)
+end
+
+double procedure xvv_nulld (ignore)
+double ignore #I ignored
+begin
+ return (0.0D0)
+end
+
diff --git a/sys/fmtio/fmt.com b/sys/fmtio/fmt.com
new file mode 100644
index 00000000..3f6d2525
--- /dev/null
+++ b/sys/fmtio/fmt.com
@@ -0,0 +1,17 @@
+# Printf common block.
+
+int fd # output file
+int ip # pointer to next char in format string
+int width, decpl # field width, number of decimal places
+int col # output column
+int left_justify # left or right justify output in field
+int radix # output radix
+int fmt_state # current state of FPRFMT (gets a format)
+int ofile_type # type of output file
+int format_char # format type character (bcdefghmorstuxz#*)
+char fill_char # filler char for rt. justification
+char format[SZ_OBUF] # format string
+char obuf[SZ_OBUF] # for formatting output
+
+common /fmtcom/ fd,ip,width,decpl,col,left_justify,radix,fmt_state,
+ ofile_type,format_char,fill_char,format,obuf
diff --git a/sys/fmtio/fmterr.x b/sys/fmtio/fmterr.x
new file mode 100644
index 00000000..7a341764
--- /dev/null
+++ b/sys/fmtio/fmterr.x
@@ -0,0 +1,25 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# FMT_ERR -- Print the format string on the standard error output, marking
+# the position within the string to which the error refers.
+
+procedure fmt_err (preamble, format, index)
+
+char preamble[ARB], format[ARB]
+int index, ip
+
+begin
+ call putline (STDERR, "(")
+ call putline (STDERR, preamble)
+ call putline (STDERR, "format = \"")
+
+ for (ip=1; ip < index && format[ip] != EOS; ip=ip+1)
+ call putcc (STDERR, format[ip])
+
+ if (format[ip] != EOS) { # mark position of error
+ call putline (STDERR, "<>")
+ for (; format[ip] != EOS; ip=ip+1)
+ call putcc (STDERR, format[ip])
+ }
+ call putline (STDERR, "\")\n")
+end
diff --git a/sys/fmtio/fmtinit.x b/sys/fmtio/fmtinit.x
new file mode 100644
index 00000000..0d8ea547
--- /dev/null
+++ b/sys/fmtio/fmtinit.x
@@ -0,0 +1,23 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <printf.h>
+
+# FMT_INIT -- The following is called by SPRINTF and CLPRINTF to set a flag to
+# close the mem file or finish the CL command, respectively, when
+# the end of the format string is reached. This entry is also called
+# by the iraf main at startup time to initialize the printf common.
+
+procedure fmt_init (ftype)
+
+int ftype
+include "fmt.com"
+
+begin
+ if (ftype == FMT_INITIALIZE) {
+ ip = 1
+ format[ip] = EOS
+ ofile_type = REGULAR_FILE # fpradv
+ fmt_state = FMT_START # fprfmt
+ } else
+ ofile_type = ftype
+end
diff --git a/sys/fmtio/fmtread.x b/sys/fmtio/fmtread.x
new file mode 100644
index 00000000..e7506ab9
--- /dev/null
+++ b/sys/fmtio/fmtread.x
@@ -0,0 +1,23 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <printf.h>
+
+# FMT_READ -- Read and interpret a format specification. Called in
+# circumstances where a NOT_DONE_YET return from FPRFMT is certain to
+# indicate a missing PARGI type argument. If this happens, print warning
+# message, and exhaust format string so that default formats will be used.
+
+procedure fmt_read()
+
+int fprfmt()
+include "fmt.com"
+
+begin
+ while (fprfmt(0) != ALL_DONE) { # read format
+ call putline (STDERR, "Warning: Missing argument to printf\n")
+ call fmt_err ("", format, ip)
+ while (format[ip] != EOS) # discard rest of format
+ ip = ip + 1
+ fmt_state = FMT_START # set defaults
+ }
+end
diff --git a/sys/fmtio/fmtsetcol.x b/sys/fmtio/fmtsetcol.x
new file mode 100644
index 00000000..a8d06855
--- /dev/null
+++ b/sys/fmtio/fmtsetcol.x
@@ -0,0 +1,28 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+include <printf.h>
+
+# FMT_SETCOL -- Called when a control character is output, to keep track of
+# the column index during output, for the "%nt" (tabulate) format. Columns
+# are indexed from the start of the printf, rather than in absolute units on
+# the output, unless a \r or \n is output during the print.
+
+procedure fmt_setcol (ch, col)
+
+char ch
+int col
+
+begin
+ switch (ch) {
+ case '\t': # next tab stop
+ col = ((col + TABSTOP-1) / TABSTOP) * TABSTOP + 1
+ case '\n', '\r', '\f':
+ col = 1
+ case '\b':
+ col = col - 1
+ default:
+ if (IS_PRINT (ch))
+ col = col + 1
+ }
+end
diff --git a/sys/fmtio/fmtstr.x b/sys/fmtio/fmtstr.x
new file mode 100644
index 00000000..197ea2bd
--- /dev/null
+++ b/sys/fmtio/fmtstr.x
@@ -0,0 +1,49 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+
+# FMTSTR -- Place a string in a field of the given width, left or right
+# justifying as indicated, and output to the named file. The length of
+# the text string may exceed the field width, in which case there is no
+# filling.
+
+procedure fmtstr (fd, str, col, fill_char, left_justify, maxch, width)
+
+int fd # output file
+char str[ARB] # string to be output
+int col # column: both input and output parameter
+char fill_char # fill character, if right justify
+int left_justify # YES or NO
+int maxch # maximum string chars to output
+int width # field width
+int nchars, nfill, ip
+int strlen()
+errchk putc, putci
+
+begin
+ if (fd <= 0)
+ return
+
+ if (width > 0) {
+ nchars = min (maxch, strlen(str))
+ nfill = max (0, width - nchars)
+ } else {
+ nchars = maxch
+ nfill = 0 # free format
+ }
+
+ if (left_justify == NO) # fill at left
+ for (col=col+nfill; nfill > 0; nfill=nfill-1)
+ call putc (fd, fill_char)
+
+ for (ip=1; str[ip] != EOS && ip <= nchars; ip=ip+1) { # put string
+ call putc (fd, str[ip])
+ if (IS_PRINT (str[ip]))
+ col = col + 1
+ else
+ call fmt_setcol (str[ip], col)
+ }
+
+ for (col=col+nfill; nfill > 0; nfill=nfill-1) # fill at right
+ call putci (fd, ' ')
+end
diff --git a/sys/fmtio/fpradv.x b/sys/fmtio/fpradv.x
new file mode 100644
index 00000000..942c29aa
--- /dev/null
+++ b/sys/fmtio/fpradv.x
@@ -0,0 +1,76 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+include <chars.h>
+include <printf.h>
+
+# FPRADV -- Copy format chars to output until next "%w.dC" format sequence is
+# encountered, or until EOS is encountered on format string. When EOS is
+# encountered, return buffer containing format string, and if mem_flag is set,
+# close the output file (a string) as well. If a format string contains no
+# regular format sequences, and hence requires no PARG_ calls, we are all done.
+
+procedure fpradv()
+
+int i, junk, ival, ch
+char cch
+int ip_save
+int ctoi(), cctoc()
+include "fmt.com"
+errchk putci
+
+begin
+ for (ch = format[ip]; ch != EOS; ch = format[ip]) {
+ cch = ch
+ if (ch == ESCAPE) {
+ junk = cctoc (format, ip, cch)
+
+ } else if (ch == START_OF_FORMAT) {
+ if (format[ip+1] == START_OF_FORMAT) # '%%' --> '%'
+ ip = ip + 2
+
+ else if (IS_DIGIT (format[ip+1])) { # %Nw or %Nt
+ ip_save = ip # ip_save --> '%'
+ ip = ip + 1
+
+ junk = ctoi (format, ip, ival)
+
+ switch (format[ip]) {
+ case FMT_WHITESPACE: # output blanks
+ do i = 1, ival
+ call putci (fd, BLANK)
+ col = col + ival
+ case FMT_TOCOLUMN: # advance to column
+ for (; col < ival; col=col+1)
+ call putci (fd, BLANK)
+ default:
+ ip = ip_save # regular format spec
+ return
+ }
+
+ ip = ip + 1 # eat "t" or "w"
+ next
+
+ } else
+ return # regular format spec
+
+ } else
+ ip = ip + 1
+
+ call putc (fd, cch) # output ordinary chars
+ if (IS_PRINT (cch)) # keep track of column
+ col = col + 1
+ else
+ call fmt_setcol (cch, col)
+ }
+
+ switch (ofile_type) { # EOS of format reached
+ case STRING_FILE:
+ call close (fd)
+ case CL_PARAM:
+ call putline (CLOUT, "\"\n")
+ }
+
+ ofile_type = REGULAR_FILE # restore default
+ fd = NULL
+end
diff --git a/sys/fmtio/fprfmt.x b/sys/fmtio/fprfmt.x
new file mode 100644
index 00000000..d5e68fb6
--- /dev/null
+++ b/sys/fmtio/fprfmt.x
@@ -0,0 +1,180 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+include <printf.h>
+
+.help
+.nf _________________________________________________________________________
+Process a format descriptor, setting the variables "decpl", "fill_char",
+"format_char", and "width" in the fmtio common. Called from PARG_
+to determine the format specification for printing a variable.
+
+Format: "%[w[.d]]C[n]", where W is the field width, D the number of decimal
+places or precision, C the format type character, and N the radix numeral,
+for format type FMT_RADIX only. A negative field width signifies left
+justification. A leading zero in the W field sets the fill character to the
+numeral zero (when right justifying). Default values will be supplied if
+any of the fields are omitted. The minimum format is "%C".
+
+If any of the fields (wdCn) have the value GET_FIELD (= "*") the value of
+the field will be taken from the next PARG_ call, rather than from the
+format string. This makes it easy to vary the format specification at run
+time. For example, "%10.*g" would print a number in G-floating format,
+with a constant field width of 10, and with the number of digits of precision
+being given by a PARGI call at execution time (followed by a PARG_ call to
+pass the value to be printed).
+.endhelp ____________________________________________________________________
+
+# The following macro marks the position in the FPRFMT procedure (saves the
+# code for the needed field), and returns the not done status to PARG_.
+# A subsequent call to a PARG_ (with the value of the field we are waiting for
+# as argument) causes FPRFMT to be reentered at the point where we left off.
+
+define (waitfor, if (ival_already_used) { fmt_state = $1; return (NOT_DONE_YET) } ; $1 ival_already_used = true)
+
+#define (waitfor, if (ival_already_used) {
+# fmt_state = $1
+# return (NOT_DONE_YET)
+# }
+# $1 ival_already_used = true)
+
+# FPRFMT -- Process a %W.Dn format specification. ALL_DONE is returned when
+# the format specification has been fully processed, else NOT_DONE_YET is
+# returned, indicating that an additional PARG call is required to complete
+# the format (which therefore contained one or more "*" specifiers).
+
+int procedure fprfmt (ival)
+
+int ival # argument value (from parg_)
+bool ival_already_used # wait for next parg
+int ctoi(), stridx()
+char ch, chrlwr()
+include "fmt.com"
+
+begin
+ # This routine functions as a coroutine. If one of the fields in
+ # the format spec is to be given in a pargi call, an early return
+ # is taken. The routine is later reentered with the value of the
+ # needed field, and execution continues at the point it left off.
+ # (Sorry, I could not think of a simpler way to do it...)
+
+ switch (fmt_state) { # return from "waitfor"
+ case FMT_START: # initial state
+ ival_already_used = false
+ case GET_WIDTH_1: # "%*.dC"
+ goto GET_WIDTH_1
+ case GET_WIDTH_2: # "%-0*.dC"
+ goto GET_WIDTH_2
+ case GET_DECPL: # "%w.*C"
+ goto GET_DECPL
+ case GET_FMTCHAR: # "%w.d*"
+ goto GET_FMTCHAR
+ case GET_RADIX: # "%w.dr*"
+ goto GET_RADIX
+ case GET_OPERAND: # used ival for format
+ goto GET_OPERAND
+ }
+
+ # It is not an error if there is no format string.
+ if (format[ip] == EOS || format[ip] != START_OF_FORMAT) {
+ width = USE_DEFAULT
+ decpl = USE_DEFAULT
+ format_char = USE_DEFAULT
+ fill_char = ' '
+ left_justify = NO
+ fmt_state = FMT_START
+ return (ALL_DONE)
+ } else
+ ip = ip + 1 # eat the "%"
+
+ if (format[ip] == GET_FIELD) { # "%*.dC"
+ ip = ip + 1
+ waitfor (GET_WIDTH_1) # go get field width...
+ if (ival < 0) # ...and come back here
+ left_justify = YES
+ else
+ left_justify = NO
+
+ fill_char = ' '
+ width = abs (ival)
+
+ } else { # "%-0*.dC"
+ if (format[ip] == '-') { # left or right justify
+ left_justify = YES
+ ip = ip + 1
+ } else
+ left_justify = NO
+
+ fill_char = ' ' # zero or blank fill
+ if (format[ip] == '0') {
+ if (IS_DIGIT (format[ip+1]) || format[ip+1] == GET_FIELD) {
+ fill_char = '0'
+ ip = ip + 1
+ } else
+ fill_char = ' '
+ }
+
+ if (format[ip] == GET_FIELD) {
+ ip = ip + 1
+ waitfor (GET_WIDTH_2) # go get field width...
+ if (ival < 0) # ... and come back here
+ left_justify = YES
+ else
+ left_justify = NO
+ width = abs (ival)
+
+ } else if (ctoi (format, ip, width) <= 0) # "%N.dC"
+ width = USE_DEFAULT
+ }
+
+ if (width == 0) # make as big as needed
+ width = USE_DEFAULT
+
+ if (format[ip] == '.') { # get decpl field
+ ip = ip + 1
+ if (format[ip] == GET_FIELD) { # "%w.*C"
+ ip = ip + 1
+ waitfor (GET_DECPL)
+ decpl = ival
+ } else if (ctoi (format, ip, decpl) <= 0) # "%w.NC"
+ decpl = USE_DEFAULT
+ } else
+ decpl = USE_DEFAULT
+
+ if (format[ip] == GET_FIELD) { # "%w.d*"
+ ip = ip + 1
+ waitfor (GET_FMTCHAR)
+ format_char = ival
+ } else {
+ format_char = format[ip] # "%w.dC"
+ ip = ip + 1
+ }
+
+ ch = format_char
+ if (stridx (ch, "bcdefghHmMorstuwxz") <= 0) {
+ call putline (STDERR, "Warning: Unknown format type char\n")
+ call fmt_err ("", format, ip-1)
+ format_char = USE_DEFAULT
+
+ } else if (format_char == FMT_RADIX) { # get radix
+ ch = chrlwr (format[ip])
+ ip = ip + 1
+ if (ch == GET_FIELD) { # "%w.dr*"
+ waitfor (GET_RADIX)
+ radix = ival
+ } else if (IS_DIGIT (ch)) {
+ radix = TO_INTEG (ch)
+ } else if (IS_LOWER (ch)) {
+ radix = ch - 'a' + 10
+ } else {
+ radix = DECIMAL
+ ip = ip - 1
+ }
+
+ } else if (format_char == FMT_WHITESPACE || format_char == FMT_TOCOLUMN)
+ ival_already_used = false # no operand
+
+ waitfor (GET_OPERAND) # used ival for format,
+ fmt_state = FMT_START # need to get another
+ return (ALL_DONE)
+end
diff --git a/sys/fmtio/fprintf.x b/sys/fmtio/fprintf.x
new file mode 100644
index 00000000..dd5304d2
--- /dev/null
+++ b/sys/fmtio/fprintf.x
@@ -0,0 +1,14 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <printf.h>
+
+# FPRINTF -- Format output to a file.
+
+procedure fprintf (fd, format_string)
+
+int fd
+char format_string[ARB]
+
+begin
+ call fprntf (fd, format_string, REGULAR_FILE)
+end
diff --git a/sys/fmtio/fprntf.x b/sys/fmtio/fprntf.x
new file mode 100644
index 00000000..095b57b2
--- /dev/null
+++ b/sys/fmtio/fprntf.x
@@ -0,0 +1,40 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <printf.h>
+
+# FPRNTF -- Initiate a formatted print. Called by FPRINTF, SPRINTF, etc.
+# Check that the previous print has completed, initialize the current
+# print, and advance to the first format specification (if any).
+
+procedure fprntf (new_fd, format_string, file_type)
+
+int new_fd, file_type
+char format_string[ARB]
+include "fmt.com"
+
+begin
+ # Printf is not reentrant. An expression in a PARG_ call must not
+ # directly or indirectly call any of the printf entry points. There
+ # must be a PARG_ for each "%w.dC" format specification in the format
+ # string. Errors result in lost output, but are otherwise harmless,
+ # and are diagnosed below.
+
+ if (format[ip] != EOS) {
+ call putline (STDERR, "Warning: Incomplete or reentrant printf\n")
+ call fmt_err ("Old ", format, ip)
+ call fmt_err ("New ", format_string, ARB)
+
+ while (format[ip] != EOS) # discard rest of format string
+ ip = ip + 1
+ call fpradv() # possibly close mem file
+ }
+
+ fd = new_fd # normal initialization
+ ip = 1
+ col = 1
+ fmt_state = FMT_START # initialize FPRFMT state
+ ofile_type = file_type
+
+ call strcpy (format_string, format, SZ_OBUF)
+ call fpradv()
+end
diff --git a/sys/fmtio/fscan.x b/sys/fmtio/fscan.x
new file mode 100644
index 00000000..4a76c772
--- /dev/null
+++ b/sys/fmtio/fscan.x
@@ -0,0 +1,30 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# FSCAN -- Begin scanning a line from a file.
+
+int procedure fscan (fd)
+
+int fd
+int getlline()
+include "scan.com"
+errchk getlline
+
+begin
+ if (getlline (fd, sc_scanbuf, SZ_SCANBUF) == EOF)
+ return (EOF)
+ else {
+ call reset_scan()
+ return (OK)
+ }
+end
+
+
+# SCAN -- Scan the standard input.
+
+int procedure scan()
+
+int fscan()
+
+begin
+ return (fscan (STDIN))
+end
diff --git a/sys/fmtio/gargb.x b/sys/fmtio/gargb.x
new file mode 100644
index 00000000..82d9e0cb
--- /dev/null
+++ b/sys/fmtio/gargb.x
@@ -0,0 +1,33 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+
+# GARGB -- Interpret the next token in the input as a boolean quantity
+# (token "y...." or "n....").
+
+procedure gargb (bval)
+
+bool bval
+include "scan.com"
+
+begin
+ if (sc_stopscan)
+ return
+
+ while (IS_WHITE (sc_scanbuf[sc_ip]))
+ sc_ip = sc_ip + 1
+
+ switch (sc_scanbuf[sc_ip]) {
+ case 'Y','y':
+ bval = true
+ case 'N','n':
+ bval = false
+ default:
+ sc_stopscan = true
+ return
+ }
+
+ while (IS_ALPHA(sc_scanbuf[sc_ip]) || sc_scanbuf[sc_ip] == '_')
+ sc_ip = sc_ip + 1
+ sc_ntokens = sc_ntokens + 1
+end
diff --git a/sys/fmtio/gargc.x b/sys/fmtio/gargc.x
new file mode 100644
index 00000000..e6ce6996
--- /dev/null
+++ b/sys/fmtio/gargc.x
@@ -0,0 +1,19 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# GARGC -- Interpret the next input token as a character constant.
+
+procedure gargc (cval)
+
+char cval
+int cctoc()
+include "scan.com"
+
+begin
+ if (sc_stopscan)
+ return
+
+ if (cctoc (sc_scanbuf, sc_ip, cval) > 0)
+ sc_ntokens = sc_ntokens + 1
+ else
+ sc_stopscan = true
+end
diff --git a/sys/fmtio/gargd.x b/sys/fmtio/gargd.x
new file mode 100644
index 00000000..cb8c4561
--- /dev/null
+++ b/sys/fmtio/gargd.x
@@ -0,0 +1,20 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# GARGD -- Interpret the next input token as a double precision floating
+# number.
+
+procedure gargd (dval)
+
+double dval
+int gctod()
+include "scan.com"
+
+begin
+ if (sc_stopscan)
+ return
+
+ if (gctod (sc_scanbuf, sc_ip, dval) > 0)
+ sc_ntokens = sc_ntokens + 1
+ else
+ sc_stopscan = true
+end
diff --git a/sys/fmtio/gargi.x b/sys/fmtio/gargi.x
new file mode 100644
index 00000000..5b53bba8
--- /dev/null
+++ b/sys/fmtio/gargi.x
@@ -0,0 +1,20 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+
+# GARGI -- Interpret the next input token as an integer quantity.
+
+procedure gargi (ival)
+
+int ival
+double dval
+
+begin
+ call gargd (dval)
+ if (IS_INDEFD (dval))
+ ival = INDEFI
+ else if (abs(dval) > MAX_INT)
+ ival = INDEFI
+ else
+ ival = dval
+end
diff --git a/sys/fmtio/gargl.x b/sys/fmtio/gargl.x
new file mode 100644
index 00000000..142ac0e1
--- /dev/null
+++ b/sys/fmtio/gargl.x
@@ -0,0 +1,20 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+
+# GARGL -- Interpret the next input token as an integer quantity.
+
+procedure gargl (lval)
+
+long lval
+double dval
+
+begin
+ call gargd (dval)
+ if (IS_INDEFD (dval))
+ lval = INDEFL
+ else if (abs(dval) > MAX_LONG)
+ lval = INDEFL
+ else
+ lval = dval
+end
diff --git a/sys/fmtio/gargr.x b/sys/fmtio/gargr.x
new file mode 100644
index 00000000..4f25d717
--- /dev/null
+++ b/sys/fmtio/gargr.x
@@ -0,0 +1,17 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# GARGR -- Interpret the next input token as a single precision floating
+# quantity.
+
+procedure gargr (rval)
+
+real rval
+double dval
+
+begin
+ call gargd (dval)
+ if (IS_INDEFD (dval))
+ rval = INDEFR
+ else
+ rval = dval
+end
diff --git a/sys/fmtio/gargrad.x b/sys/fmtio/gargrad.x
new file mode 100644
index 00000000..8ff78bf8
--- /dev/null
+++ b/sys/fmtio/gargrad.x
@@ -0,0 +1,20 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# GARGRAD -- Convert the next number using the radix given as the second
+# argument.
+
+procedure gargrad (lval, radix)
+
+long lval
+int radix, gctol()
+include "scan.com"
+
+begin
+ if (sc_stopscan)
+ return
+
+ if (gctol (sc_scanbuf, sc_ip, lval, radix) > 0)
+ sc_ntokens = sc_ntokens + 1
+ else
+ sc_stopscan = true
+end
diff --git a/sys/fmtio/gargs.x b/sys/fmtio/gargs.x
new file mode 100644
index 00000000..193a725f
--- /dev/null
+++ b/sys/fmtio/gargs.x
@@ -0,0 +1,20 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+
+# GARGS -- Interpret the next input token as an integer quantity.
+
+procedure gargs (sval)
+
+short sval
+double dval
+
+begin
+ call gargd (dval)
+ if (IS_INDEFD (dval))
+ sval = INDEFS
+ else if (abs(dval) > MAX_SHORT)
+ sval = INDEFS
+ else
+ sval = dval
+end
diff --git a/sys/fmtio/gargstr.x b/sys/fmtio/gargstr.x
new file mode 100644
index 00000000..9ae30462
--- /dev/null
+++ b/sys/fmtio/gargstr.x
@@ -0,0 +1,24 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# GARGSTR -- Return the remainder of the scanned input line as a string.
+
+procedure gargstr (outstr, maxch)
+
+char outstr[ARB]
+int maxch, op
+include "scan.com"
+
+begin
+ if (sc_stopscan)
+ return
+
+ for (op=1; op <= maxch && sc_scanbuf[sc_ip] != EOS; op=op+1) {
+ if (sc_scanbuf[sc_ip] == '\n')
+ break # don't keep newlines
+ outstr[op] = sc_scanbuf[sc_ip]
+ sc_ip = sc_ip + 1
+ }
+
+ outstr[op] = EOS
+ sc_ntokens = sc_ntokens + 1 # null strings are ok
+end
diff --git a/sys/fmtio/gargtok.x b/sys/fmtio/gargtok.x
new file mode 100644
index 00000000..b775c7ea
--- /dev/null
+++ b/sys/fmtio/gargtok.x
@@ -0,0 +1,18 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# GARGTOK -- Return the next token from the scanned input line.
+
+procedure gargtok (token, outstr, maxch)
+
+int token
+char outstr[ARB]
+int maxch, ctotok()
+include "scan.com"
+
+begin
+ if (sc_stopscan)
+ return
+
+ sc_ntokens = sc_ntokens + 1 # Newline, EOS are legal tokens
+ token = ctotok (sc_scanbuf, sc_ip, outstr, maxch)
+end
diff --git a/sys/fmtio/gargwrd.x b/sys/fmtio/gargwrd.x
new file mode 100644
index 00000000..cc8aa695
--- /dev/null
+++ b/sys/fmtio/gargwrd.x
@@ -0,0 +1,22 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# GARGWRD -- Return the next whitespace delimited token or quoted string from
+# the scan buffer.
+
+procedure gargwrd (outstr, maxch)
+
+char outstr[ARB]
+int maxch, ctowrd()
+include "scan.com"
+
+begin
+ if (sc_stopscan) {
+ outstr[1] = EOS
+ return
+ }
+
+ if (ctowrd (sc_scanbuf, sc_ip, outstr, maxch) > 0)
+ sc_ntokens = sc_ntokens + 1
+ else
+ sc_stopscan = true
+end
diff --git a/sys/fmtio/gargx.x b/sys/fmtio/gargx.x
new file mode 100644
index 00000000..2a1be607
--- /dev/null
+++ b/sys/fmtio/gargx.x
@@ -0,0 +1,19 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# GARGX -- Interpret the next input token as a complex number.
+
+procedure gargx (xval)
+
+complex xval
+int gctox()
+include "scan.com"
+
+begin
+ if (sc_stopscan)
+ return
+
+ if (gctox (sc_scanbuf, sc_ip, xval) > 0)
+ sc_ntokens = sc_ntokens + 1
+ else
+ sc_stopscan = true
+end
diff --git a/sys/fmtio/gctod.x b/sys/fmtio/gctod.x
new file mode 100644
index 00000000..ff58555b
--- /dev/null
+++ b/sys/fmtio/gctod.x
@@ -0,0 +1,81 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+include <chars.h>
+include <lexnum.h>
+
+define OCTAL 8
+define DECIMAL 10
+define HEX 16
+
+
+# GCTOD -- General character string to double precision real. Any legal
+# number, e.g., integer, floating point, complex, or character constant,
+# is decoded and returned as a double.
+
+int procedure gctod (str, ip, odval)
+
+char str[ARB] # input string
+int ip # pointer into input string
+double odval # output double
+
+char ch
+double dval
+complex xval
+long lval
+int ip_save, radix, nchars, vtype
+int ctox(), cctoc(), ctod(), gctol(), lexnum()
+
+begin
+ vtype = TY_DOUBLE # val to be returned
+ while (IS_WHITE (str[ip]))
+ ip = ip + 1
+
+ ip_save = ip
+ ch = str[ip] # first nonwhite
+
+ if (ch == '(') { # complex number?
+ if (ctox (str, ip, xval) <= 0)
+ return (0) # not a number
+ else
+ vtype = TY_COMPLEX
+
+ } else if (ch == SQUOTE || ch == ESCAPE) {
+ if (cctoc (str, ip, ch) <= 0) # character constant?
+ return (0)
+ else
+ dval = ch
+
+ } else { # determine type of number
+ switch (lexnum (str, ip, nchars)) {
+ case LEX_OCTAL:
+ radix = OCTAL
+ case LEX_DECIMAL:
+ radix = DECIMAL
+ case LEX_HEX:
+ radix = HEX
+ case LEX_REAL:
+ radix = TY_REAL
+ default:
+ return (0)
+ }
+
+ if (radix == TY_REAL) # perform the conversion
+ nchars = ctod (str, ip, dval)
+ else {
+ nchars = gctol (str, ip, lval, radix)
+ dval = lval
+ if (IS_INDEFL (lval))
+ dval = INDEFD
+ }
+ }
+
+ if (vtype == TY_COMPLEX) {
+ odval = xval
+ if (IS_INDEFX (xval))
+ odval = INDEFD
+ } else
+ odval = dval
+
+ return (ip - ip_save)
+end
diff --git a/sys/fmtio/gctol.x b/sys/fmtio/gctol.x
new file mode 100644
index 00000000..f1478ff0
--- /dev/null
+++ b/sys/fmtio/gctol.x
@@ -0,0 +1,78 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+
+define OCTAL 8
+define DECIMAL 10
+define HEX 16
+
+.help gctol
+.nf _______________________________________________________________________
+GCTOL -- Convert string to long integer (any radix). The long integer
+value is returned in LVAL, and the pointer IP is left pointing at the
+first character following the number. IP must be set to the index of
+the character at which conversion is to start before calling GCTOL.
+
+If the conversion radix is octal (hex), and the number is immediately
+followed by the suffix "b|B" ("x|X"), IP will be advanced past the suffix
+character, which is considered to be part of the number.
+.endhelp __________________________________________________________________
+
+
+int procedure gctol (str, ip, lval, radix)
+
+char str[ARB] # string to be decoded
+int ip # pointer within string
+int radix # radix of number
+long lval # output variable
+
+int digit, base, ip_save, first_char
+char ch
+bool neg
+
+begin
+ while (IS_WHITE (str[ip]))
+ ip = ip + 1
+ ip_save = ip
+
+ neg = (str[ip] == '-')
+ if (neg || str[ip] == '+') # eat the +/-
+ ip = ip + 1
+
+ first_char = ip
+ base = abs (radix)
+
+ # The first character (following than the sign character) must be
+ # a digit, regardless of the radix.
+
+ for (lval=0; str[ip] != EOS; ip=ip+1) {
+ ch = str[ip]
+
+ if (IS_DIGIT (ch)) # cvt char to binary
+ digit = TO_INTEG (ch)
+ else if (base > DECIMAL) {
+ if (IS_UPPER (ch))
+ ch = TO_LOWER (ch)
+ else if (! IS_LOWER (ch))
+ break
+ digit = ch - 'a' + 10 # for radices > 10
+ } else
+ break
+
+ if (digit < 0 || digit >= base)
+ break
+ lval = lval * base + digit
+ }
+
+ if (neg)
+ lval = -lval
+
+ if (ip == first_char) # not a number ?
+ ip = ip_save # restore pointer
+ else if (radix == OCTAL && ch == 'b' || ch == 'B')
+ ip = ip + 1
+ else if (radix == HEX && ch == 'x' || ch == 'X')
+ ip = ip + 1 # eat suffix char
+
+ return (ip - first_char)
+end
diff --git a/sys/fmtio/gctox.x b/sys/fmtio/gctox.x
new file mode 100644
index 00000000..2a23a917
--- /dev/null
+++ b/sys/fmtio/gctox.x
@@ -0,0 +1,81 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+include <chars.h>
+include <lexnum.h>
+
+define OCTAL 8
+define DECIMAL 10
+define HEX 16
+
+
+# GCTOX -- General character string to complex. Any legal number, e.g.,
+# integer, floating point, complex, or character constant, is decoded and
+# returned as a complex.
+
+int procedure gctox (str, ip, oxval)
+
+char str[ARB] # input string
+int ip # pointer into input string
+complex oxval # output complex
+
+char ch
+double dval
+complex xval
+long lval
+int ip_save, radix, nchars, vtype
+int ctox(), cctoc(), ctod(), gctol(), lexnum()
+
+begin
+ vtype = TY_DOUBLE # val to be returned
+ while (IS_WHITE (str[ip]))
+ ip = ip + 1
+
+ ip_save = ip
+ ch = str[ip] # first nonwhite
+
+ if (ch == '(') { # complex number?
+ if (ctox (str, ip, xval) <= 0)
+ return (0) # not a number
+ else
+ vtype = TY_COMPLEX
+
+ } else if (ch == SQUOTE || ch == ESCAPE) {
+ if (cctoc (str, ip, ch) <= 0) # character constant?
+ return (0)
+ else
+ dval = ch
+
+ } else { # determine type of number
+ switch (lexnum (str, ip, nchars)) {
+ case LEX_OCTAL:
+ radix = OCTAL
+ case LEX_DECIMAL:
+ radix = DECIMAL
+ case LEX_HEX:
+ radix = HEX
+ case LEX_REAL:
+ radix = TY_REAL
+ default:
+ return (0)
+ }
+
+ if (radix == TY_REAL) # perform the conversion
+ nchars = ctod (str, ip, dval)
+ else {
+ nchars = gctol (str, ip, lval, radix)
+ dval = lval
+ if (IS_INDEFL (lval))
+ dval = INDEFD
+ }
+ }
+
+ if (vtype == TY_DOUBLE) {
+ oxval = dval
+ if (IS_INDEFD (dval))
+ oxval = INDEFX
+ } else
+ oxval = xval
+
+ return (ip - ip_save)
+end
diff --git a/sys/fmtio/gltoc.x b/sys/fmtio/gltoc.x
new file mode 100644
index 00000000..eaf47405
--- /dev/null
+++ b/sys/fmtio/gltoc.x
@@ -0,0 +1,82 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include <ctype.h>
+
+define OCTAL 8
+define DECIMAL 10
+define HEX 16
+define MAX_RADIX 'Z' - 'A' + 11
+
+# GLTOC -- Convert long integer to any radix string. Returns the
+# number of characters generated.
+
+int procedure gltoc (lval, outstr, maxch, base)
+
+long lval # long integer to be encoded
+char outstr[maxch] # output buffer
+int maxch, base # numeric base (2..16)
+
+int carry, d, op, radix, n, size, nchars, gstrcpy()
+long andl(), orl()
+bool unsigned
+
+begin
+ if (IS_INDEFL(lval) && base > 0)
+ return (gstrcpy ("INDEF", outstr, maxch))
+ size = maxch
+
+ # Digit string is generated backwards, then reversed. Unsigned
+ # conversion used if radix negative.
+
+ radix = max(2, min(MAX_RADIX, abs(base)))
+
+ unsigned = (base < 0) # get raw number
+ if (unsigned) {
+ n = andl (lval, MAX_LONG) / 2
+ if (lval < 0)
+ n = orl (n, (MAX_LONG / 2 + 1))
+ carry = andl (lval, 1) # get initial carry
+ } else
+ n = lval
+
+ op = 0
+ repeat {
+ d = abs (mod (n, radix)) # generate next digit
+ if (unsigned) {
+ d = 2 * d + carry # get actual digit value
+ if (d >= radix) { # check for generated carry
+ d = d - radix
+ carry = 1
+ } else
+ carry = 0
+ }
+ op = op + 1
+ if (d < 10) # convert to char and store
+ outstr[op] = TO_DIGIT (d)
+ else
+ outstr[op] = d - 10 + 'A'
+ n = n / radix
+ } until (n == 0 || op >= size)
+
+ if (unsigned) {
+ if (carry != 0 && op < size) { # check for final carry
+ op = op + 1
+ outstr[op] = '1'
+ }
+ } else if (lval < 0 && op < size) { # add sign if needed
+ op = op + 1
+ outstr[op] = '-'
+ }
+ nchars = op # return length of string
+
+ for (d=1; d < op; d=d+1) { # reverse digits
+ carry = outstr[d]
+ outstr[d] = outstr[op]
+ outstr[op] = carry
+ op = op - 1
+ }
+
+ outstr[nchars+1] = EOS
+ return (nchars)
+end
diff --git a/sys/fmtio/gstrcat.x b/sys/fmtio/gstrcat.x
new file mode 100644
index 00000000..d3f3fa94
--- /dev/null
+++ b/sys/fmtio/gstrcat.x
@@ -0,0 +1,26 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# GSTRCAT -- String concatenation. String STR is appended to OUTSTR.
+
+int procedure gstrcat (str, outstr, maxch)
+
+char str[ARB], outstr[ARB]
+int maxch
+
+int ip, op, n
+
+begin
+ do op = 0, maxch-1
+ if (outstr[op+1] == EOS)
+ break
+
+ n = maxch - op
+ do ip = 1, n {
+ outstr[op+ip] = str[ip]
+ if (str[ip] == EOS)
+ return (op + ip-1)
+ }
+
+ outstr[maxch+1] = EOS
+ return (maxch)
+end
diff --git a/sys/fmtio/gstrcpy.x b/sys/fmtio/gstrcpy.x
new file mode 100644
index 00000000..e2d6e7b1
--- /dev/null
+++ b/sys/fmtio/gstrcpy.x
@@ -0,0 +1,19 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# GSTRCPY -- Copy string s1 to s2, return the number of characters copied.
+
+int procedure gstrcpy (s1, s2, maxch)
+
+char s1[ARB], s2[ARB]
+int maxch, i
+
+begin
+ do i = 1, maxch {
+ s2[i] = s1[i]
+ if (s2[i] == EOS)
+ return (i - 1)
+ }
+
+ s2[maxch+1] = EOS
+ return (maxch)
+end
diff --git a/sys/fmtio/itoc.x b/sys/fmtio/itoc.x
new file mode 100644
index 00000000..726fbc40
--- /dev/null
+++ b/sys/fmtio/itoc.x
@@ -0,0 +1,53 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include <ctype.h>
+
+# ITOC -- Integer to character string. We do not resolve this into a call
+# to GLTOC for reasons of efficiency.
+
+int procedure itoc (ival, str, maxch)
+
+int ival, maxch
+char str[ARB]
+
+char buf[MAX_DIGITS]
+int b_op, s_op, num, temp
+int gstrcpy()
+
+begin
+ s_op = 1
+
+ if (IS_INDEFI (ival)) {
+ return (gstrcpy ("INDEF", str, maxch))
+ } else if (ival < 0) {
+ str[1] = '-'
+ s_op = 2
+ num = -ival
+ } else
+ num = ival
+
+ # Encode nonnegative number in BUF, least significant digits first.
+
+ b_op = 0
+ repeat {
+ temp = num / 10
+ b_op = b_op + 1
+ buf[b_op] = TO_DIGIT (num - temp * 10)
+ num = temp
+ } until (num == 0)
+
+ # Copy encoded number to output string, reversing the order of the
+ # digits so that the most significant digits are first.
+
+ while (b_op > 0) {
+ if (s_op > maxch)
+ return (gstrcpy ("**********", str, maxch))
+ str[s_op] = buf[b_op]
+ s_op = s_op + 1
+ b_op = b_op - 1
+ }
+
+ str[s_op] = EOS
+ return (s_op - 1)
+end
diff --git a/sys/fmtio/lexdata.inc b/sys/fmtio/lexdata.inc
new file mode 100644
index 00000000..1a1bf3e4
--- /dev/null
+++ b/sys/fmtio/lexdata.inc
@@ -0,0 +1,28 @@
+# Actions of the LEXNUM finite state automaton.
+
+define Acc ACCEPT # special actions
+define Rvt REVERT
+
+define o_o LEX_OCTAL # reductions
+define d_d LEX_DECIMAL
+define x_x LEX_HEX
+define r_r LEX_REAL
+define n__ LEX_NONNUM # (other actions are new states)
+
+# cc: +- 0-7 8-9 ACF ED : . X B other
+
+data action /UNM, ODH, DHR, n__, n__, QRN, QRF, n__, n__, n__, # start
+ Rvt, ODH, DHR, Rvt, Rvt, Rvt, QRF, Rvt, Rvt, Rvt, # UNM
+ d_d, Acc, DHR, HEX, QHX, QRN, RFR, x_x, OHN, d_d, # ODH
+ d_d, Acc, Acc, HEX, QHX, QRN, RFR, x_x, HEX, d_d, # DHR
+ Rvt, RFR, RFR, Rvt, Rvt, Rvt, Rvt, Rvt, Rvt, Rvt, # QRF
+ Rvt, Acc, Acc, Acc, Acc, Rvt, Rvt, x_x, Acc, Rvt, # HEX
+ QRX, HRX, HRX, HEX, HEX, Rvt, Rvt, x_x, HEX, Rvt, # QHX
+ Rvt, RNM, RNM, Rvt, Rvt, Acc, Rvt, Rvt, Rvt, Rvt, # QRN
+ o_o, HEX, HEX, HEX, HEX, o_o, o_o, x_x, HEX, o_o, # OHN
+ r_r, Acc, Acc, r_r, RRX, r_r, r_r, r_r, r_r, r_r, # RFR
+ QRX, REX, REX, Rvt, Rvt, Rvt, Rvt, Rvt, Rvt, Rvt, # RRX
+ Rvt, REX, REX, Rvt, Rvt, Rvt, Rvt, Rvt, Rvt, Rvt, # QRX
+ r_r, Acc, Acc, HEX, HEX, r_r, r_r, x_x, r_r, r_r, # HRX
+ r_r, Acc, Acc, r_r, QRX, Acc, QRF, r_r, r_r, r_r, # RNM
+ r_r, Acc, Acc, r_r, r_r, r_r, r_r, r_r, r_r, r_r/ # REX
diff --git a/sys/fmtio/lexnum.x b/sys/fmtio/lexnum.x
new file mode 100644
index 00000000..f62b0ed2
--- /dev/null
+++ b/sys/fmtio/lexnum.x
@@ -0,0 +1,190 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include <ctype.h>
+include <lexnum.h>
+
+# LEXNUM -- Lexically analyse a character string, determine if string is
+# a number, and if so, the type of number, and the number of characters
+# in the number. The ip_start argument is left pointing at the first char
+# of the number (or other token), and the number of chars in the number is
+# returned as the third argument (0 if not a number).
+#
+# NOTE - See .doc/lexnum.hlp for a description of the states of the automaton.
+
+define SZ_STACK 15
+
+# Lexical actions. "Reduce" means exit, returning code identifying lexical
+# type of token. "Shift" means switch to a new state in the automaton.
+# "Revert" means reduce class "other" in the previous state.
+
+define ACCEPT -6 # remain in same state
+define REVERT -5 # revert to earlier state
+
+
+# Character classes
+
+define SIGNCHAR 1 # +-
+define OCTDIG 2 # 0-7
+define DECDIG 3 # 8-9
+define HEXDIG 4 # a-fA-F
+define REALEXP 5 # eEdD
+define SEXAG 6 # :
+define FRACTION 7 # .
+define HEXSUFFIX 8 # xX
+define OCTSUFFIX 9 # bB
+define OTHER 10 # invalid character
+define NCC 10
+
+
+# States of the automaton
+
+define START 1 # initial state
+define UNM 2 # unop or number
+define ODH 3 # octal, decimal, hex, or real
+define DHR 4 # decimal, hex, or real
+define QRF 5 # maybe real fraction
+define HEX 6 # hex
+define QHX 7 # maybe hex or real exponent
+define QRN 8 # maybe real number
+define OHN 9 # octal or hex number
+define RFR 10 # real fraction
+define RRX 11 # real or real exponent
+define QRX 12 # maybe real exponent
+define HRX 13 # hex or real exponent
+define RNM 14 # real number
+define REX 15 # real exponent
+define NSTATES 15
+
+
+# LEXNUM -- Determine if the next sequence of characters in the string STR
+# can be interpreted as a number. Return the numeric type as the function
+# value or LEX_NONNUM if the string is not a number.
+
+int procedure lexnum (str, ip_start, nchars)
+
+char str[ARB] # string to be decoded
+int ip_start # starting index in string
+int nchars # receives nchars in next token
+
+char ch
+int stk_ip[SZ_STACK]
+int ip, sp, cc, state, ip_save, toktype, act
+short stk_state[SZ_STACK], action[NCC,NSTATES]
+int strncmp()
+include "lexdata.inc"
+
+begin
+ while (IS_WHITE (str[ip_start]))
+ ip_start = ip_start + 1
+ ip = ip_start
+
+ # INDEF is a legal number and is best dealt with as a special case.
+ if (str[ip] == 'I')
+ if (strncmp (str[ip], "INDEF", 5) == 0) {
+ nchars = 5
+ return (LEX_REAL)
+ }
+
+ state = START # initialization
+ ip_save = ip
+ sp = 0
+
+ repeat {
+ ch = str[ip]
+
+ repeat { # determine character class
+ switch (ch) {
+ case '+','-':
+ cc = SIGNCHAR
+ break
+ case '0','1','2','3','4','5','6','7':
+ cc = OCTDIG
+ break
+ case '8','9':
+ cc = DECDIG
+ break
+ case 'B':
+ cc = OCTSUFFIX
+ break
+ case 'D','E':
+ cc = REALEXP
+ break
+ case 'A','C','F':
+ cc = HEXDIG
+ break
+ case ':':
+ cc = SEXAG
+ break
+ case '.':
+ cc = FRACTION
+ break
+ default:
+ if (IS_LOWER (ch))
+ ch = TO_UPPER (ch) # and repeat
+ else if (ch == 'X') {
+ cc = HEXSUFFIX
+ break
+ } else {
+ cc = OTHER
+ break
+ }
+ }
+ }
+
+#call eprintf ("ip=%2d, sp=%2d, ch=%c, cc=%d, state=%d, action=%d\n")
+#call pargi(ip); call pargi(sp)
+#call pargc(ch); call pargi(cc); call pargi(state)
+#call pargs(action[cc,state])
+
+ # Perform the action indicated by the action table when this
+ # class of character is encountered in the current state.
+
+ act = action[cc,state]
+ if (act == ACCEPT) {
+ ip = ip + 1 # a simple optimization
+ next
+ }
+
+ switch (act) {
+ case REVERT:
+ repeat {
+ ip = stk_ip[sp]
+ state = stk_state[sp]
+ toktype = action[OTHER,state]
+ sp = sp - 1
+ } until (toktype != REVERT || sp <= 0)
+
+ break
+
+ case LEX_OCTAL, LEX_DECIMAL, LEX_HEX, LEX_REAL, LEX_NONNUM:
+ toktype = action[cc,state]
+ if (toktype == LEX_OCTAL && cc == OCTSUFFIX)
+ ip = ip + 1 # discard suffix char
+ else if (toktype == LEX_HEX && cc == HEXSUFFIX)
+ ip = ip + 1
+ break
+
+ default: # shift to new state
+ sp = sp + 1
+ if (sp > SZ_STACK) {
+ toktype = LEX_NONNUM
+ break
+ }
+ stk_ip[sp] = ip
+ stk_state[sp] = state
+
+ ip = ip + 1
+ state = action[cc,state]
+ if (state < 1 || state > NSTATES)
+ call error (0, "In LEXNUM: cannot happen")
+ }
+ }
+
+ if (toktype == LEX_NONNUM)
+ nchars = 0
+ else
+ nchars = ip - ip_save
+
+ return (toktype)
+end
diff --git a/sys/fmtio/ltoc.x b/sys/fmtio/ltoc.x
new file mode 100644
index 00000000..d2105433
--- /dev/null
+++ b/sys/fmtio/ltoc.x
@@ -0,0 +1,17 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+define DECIMAL 10
+
+# LTOC -- Convert long integer to decimal string.
+# Returns the number of characters generated.
+
+int procedure ltoc (lval, outstr, maxch)
+
+long lval # long integer to be encoded
+char outstr[ARB] # output buffer
+int maxch # size of output buffer
+int gltoc()
+
+begin
+ return (gltoc (lval, outstr, maxch, DECIMAL))
+end
diff --git a/sys/fmtio/mkpkg b/sys/fmtio/mkpkg
new file mode 100644
index 00000000..b27d6a5f
--- /dev/null
+++ b/sys/fmtio/mkpkg
@@ -0,0 +1,125 @@
+# Formatted i/o (FMTIO) portion of the system library.
+
+$checkout libsys.a lib$
+$update libsys.a
+$checkin libsys.a lib$
+$exit
+
+tfiles:
+ $ifnewer (evvexpr.gy, evvexpr.y)
+ $generic -k evvexpr.gy -o evvexpr.y
+ $endif
+
+ $ifnewer (evvexpr.y, evvexpr.x)
+ $ifeq (HOSTID, unix)
+ $echo "fmtio/evvexpr.x is out of date; rebuilding with XYACC:"
+ !(xyacc evvexpr.y; mv -f ytab.x evvexpr.x)
+ $else
+ $echo "fmtio/evvexpr.x is out of date; rebuild with XYACC"
+ $endif
+ $endif
+
+ $ifnewer (evexpr.y, evexpr.x)
+ $ifeq (HOSTID, unix)
+ $echo "fmtio/evexpr.x is out of date; rebuilding with XYACC:"
+ !(xyacc evexpr.y; mv -f ytab.x evexpr.x)
+ $else
+ $echo "fmtio/evexpr.x is out of date; rebuild with XYACC"
+ $endif
+ $endif
+ ;
+
+libsys.a:
+ $ifeq (USE_GENERIC, yes) $call tfiles $endif
+
+ cctoc.x escchars.inc <chars.h> <ctype.h>
+ chdeposit.x
+ chfetch.x
+ chrlwr.x <ctype.h>
+ chrupr.x <ctype.h>
+ clprintf.x <printf.h>
+ clscan.x scan.com
+ ctocc.x escchars.inc <ctype.h>
+ ctod.x <ctype.h> <mach.h>
+ ctoi.x <ctype.h>
+ ctol.x <ctype.h>
+ ctor.x <mach.h>
+ ctotok.x tokdata.inc <chars.h> <lexnum.h> <ctotok.h> <ctype.h>
+ ctowrd.x <chars.h> <ctype.h>
+ ctox.x <ctype.h>
+ dtcscl.x
+ dtoc.x <ctype.h> <printf.h>
+ dtoc3.x <ctype.h> <mach.h> <printf.h>
+ eprintf.x <printf.h>
+ evexpr.x evexpr.com <ctype.h> <evexpr.h> <lexnum.h> <mach.h>
+ evvexpr.x evvexpr.com <ctype.h> <evvexpr.h> <lexnum.h>\
+ <mach.h> <math.h>
+ fmterr.x
+ fmtinit.x fmt.com <printf.h>
+ fmtread.x fmt.com <printf.h>
+ fmtsetcol.x <ctype.h> <printf.h>
+ fmtstr.x <ctype.h>
+ fpradv.x <chars.h> <ctype.h> <printf.h> fmt.com
+ fprfmt.x <ctype.h> <printf.h> fmt.com
+ fprintf.x <printf.h>
+ fprntf.x <printf.h> fmt.com
+ fscan.x scan.com
+ gargb.x scan.com <ctype.h>
+ gargc.x scan.com
+ gargd.x scan.com
+ gargi.x <mach.h>
+ gargl.x <mach.h>
+ gargr.x
+ gargrad.x scan.com
+ gargs.x <mach.h>
+ gargstr.x scan.com
+ gargtok.x scan.com
+ gargwrd.x scan.com
+ gargx.x scan.com
+ gctod.x <chars.h> <ctype.h> <lexnum.h>
+ gctol.x <ctype.h>
+ gctox.x <chars.h> <ctype.h> <lexnum.h>
+ gltoc.x <ctype.h> <mach.h>
+ gstrcat.x
+ gstrcpy.x
+ itoc.x <ctype.h> <mach.h>
+ lexnum.x lexdata.inc <ctype.h> <lexnum.h> <mach.h>
+ ltoc.x
+ nscan.x scan.com
+ parg.x <ctype.h> <mach.h> <printf.h> fmt.com
+ pargb.x <printf.h>
+ pargstr.x <printf.h> fmt.com
+ pargx.x <ctype.h> <mach.h> <printf.h> fmt.com
+ patmatch.x <chars.h> <ctype.h> <pattern.h>
+ printf.x <printf.h>
+ resetscan.x scan.com
+ scanc.x scan.com
+ sprintf.x <printf.h>
+ sscan.x scan.com
+ strcat.x
+ strcmp.x
+ strcpy.x
+ strdic.x <ctype.h>
+ streq.x
+ strge.x
+ strgt.x
+ stridx.x
+ stridxs.x
+ strldx.x
+ strldxs.x
+ strle.x
+ strlen.x
+ strlt.x
+ strlwr.x <ctype.h>
+ strmac.x <ctype.h>
+ strmatch.x <ctype.h> <pattern.h>
+ strncmp.x
+ strne.x
+ strsearch.x
+ strsrt.x
+ strtbl.x
+ strupr.x <ctype.h>
+ xevgettok.x <lexnum.h> <ctype.h> <evexpr.h>
+ xvvgettok.x <lexnum.h> <ctype.h> <mach.h> <math.h> <evexpr.h>
+ xtoc.x
+ ;
diff --git a/sys/fmtio/nscan.x b/sys/fmtio/nscan.x
new file mode 100644
index 00000000..c9069927
--- /dev/null
+++ b/sys/fmtio/nscan.x
@@ -0,0 +1,12 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# NSCAN -- Return the number of tokens successfully converted in the most
+# recent scan.
+
+int procedure nscan()
+
+include "scan.com"
+
+begin
+ return (sc_ntokens)
+end
diff --git a/sys/fmtio/parg.x b/sys/fmtio/parg.x
new file mode 100644
index 00000000..d65bd3ce
--- /dev/null
+++ b/sys/fmtio/parg.x
@@ -0,0 +1,283 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include <ctype.h>
+include <printf.h>
+
+.help parg
+.nf ___________________________________________________________________________
+PARG[CSILRDX] -- Pass a numeric argument to printf. Get the format spec and
+format the number on the output file. Try to provide reasonable automatic
+type conversions. Avoid any type coercion of indefinites.
+We try to make the operand fit in the specified field width, decreasing the
+precision if necessary, but if it cannot be made to fit we increase the field
+width until it does. We feel that it is more important to output a readable
+number than to keep the output columns justified.
+.endhelp ______________________________________________________________________
+
+
+# PARGD -- Pass a double.
+
+procedure pargd (dval)
+
+double dval
+
+begin
+ call pargg (dval, TY_DOUBLE)
+end
+
+
+# PARGC -- Pass a char.
+
+procedure pargc (cval)
+
+char cval
+double dval
+
+begin
+ dval = cval
+ call pargg (dval, TY_CHAR)
+end
+
+
+# PARGS -- Pass a short.
+
+procedure pargs (sval)
+
+short sval
+double dval
+
+begin
+ dval = sval
+ if (IS_INDEFS (sval))
+ dval = INDEFD
+
+ call pargg (dval, TY_SHORT)
+end
+
+
+# PARGI -- Pass an int.
+
+procedure pargi (ival)
+
+int ival
+double dval
+
+begin
+ dval = ival
+ if (IS_INDEFI (ival))
+ dval = INDEFD
+
+ call pargg (dval, TY_INT)
+end
+
+
+# PARGL -- Pass a long.
+
+procedure pargl (lval)
+
+long lval
+double dval
+
+begin
+ dval = lval
+ if (IS_INDEFL (lval))
+ dval = INDEFD
+
+ call pargg (dval, TY_LONG)
+end
+
+
+# PARGR -- Pass a real.
+
+procedure pargr (rval)
+
+real rval
+double dval
+
+begin
+ dval = rval
+ if (IS_INDEFR (rval))
+ dval = INDEFD
+
+ call pargg (dval, TY_REAL)
+end
+
+
+# PARGG -- Generic put argument. Encode a value of a specific datatype passed
+# as a double precision value.
+
+procedure pargg (value, dtype)
+
+double value
+int dtype
+
+char ch
+long lnum
+complex xnum
+int n, precision, i, junk, ival, nchars, nbits, fmt
+int ctocc(), gltoc(), dtoc(), xtoc(), fprfmt()
+errchk putci, fmtstr, fpradv
+include "fmt.com"
+
+begin
+ # Read format. If format spec contains "*" fields, VALUE is a part of
+ # the format, rather than a true operand. In that case we return,
+ # and the next call again checks to see if the format spec is complete.
+ # Note that if VALUE is not part of the format but is instead a floating
+ # point value to be printed, it may have an exponent large enough to
+ # cause integer overflow in an INT(VALUE) operation, hence we must
+ # guard against this. This is easy since only PARGI will be used to
+ # pass format information.
+
+ if (dtype == TY_REAL || dtype == TY_DOUBLE)
+ ival = 0
+ else if (IS_INDEFD (value))
+ ival = INDEFI
+ else
+ ival = nint (value)
+
+ if (fprfmt (ival) == NOT_DONE_YET)
+ return
+
+ if (format_char == USE_DEFAULT || format_char == FMT_STRING)
+ switch (dtype) {
+ case TY_CHAR:
+ format_char = FMT_CHAR
+ case TY_INT:
+ format_char = FMT_DECIMAL
+ default:
+ format_char = FMT_GENERAL
+ }
+
+ if (dtype == TY_DOUBLE) # supply def. precision
+ precision = NDIGITS_DP
+ else
+ precision = NDIGITS_RP
+
+ if (width == USE_DEFAULT) # make as big as needed
+ width = SZ_OBUF
+
+ # Convert number from binary into character form in OBUF, applying
+ # defaults as needed.
+
+ # Ignore case in testing format type.
+ fmt = format_char
+ if (IS_UPPER (fmt))
+ fmt = TO_LOWER(fmt)
+
+ switch (fmt) {
+ case FMT_BOOL:
+ if (IS_INDEFD (value))
+ call strcpy ("INDEF", obuf, SZ_OBUF)
+ else if (int (value) == 0)
+ call strcpy ("NO", obuf, SZ_OBUF)
+ else
+ call strcpy ("YES", obuf, SZ_OBUF)
+
+ case FMT_CHAR:
+ if (IS_INDEFD (value))
+ call strcpy ("INDEF", obuf, SZ_OBUF)
+ else {
+ ch = nint (value)
+ junk = ctocc (ch, obuf, SZ_OBUF)
+ }
+
+ case FMT_DECIMAL, FMT_OCTAL, FMT_HEX, FMT_RADIX, FMT_UNSIGNED:
+ switch (fmt) {
+ case FMT_DECIMAL:
+ radix = DECIMAL # signed decimal
+ case FMT_OCTAL:
+ radix = -OCTAL # unsigned octal
+ case FMT_HEX:
+ radix = -HEX # unsigned hex
+ case FMT_UNSIGNED:
+ radix = -DECIMAL # unsigned decimal
+ default:
+ radix = -abs(radix) # unsigned radix
+ }
+
+ if (IS_INDEFD (value)) {
+ lnum = INDEFL
+ nchars = gltoc (lnum, obuf, SZ_OBUF, radix)
+
+ } else {
+ lnum = long (value)
+ nchars = gltoc (lnum, obuf, SZ_OBUF, radix)
+
+ # Limit sign extension if negative number, hex or octal.
+ if (lnum < 0 && (dtype == TY_SHORT || dtype == TY_CHAR)) {
+ nbits = SZB_CHAR * NBITS_BYTE
+ if (dtype == TY_SHORT)
+ nbits = nbits * SZ_SHORT
+ if (fmt == FMT_OCTAL) {
+ n = nchars - (nbits + 2) / 3
+ if (n > 0) {
+ call strcpy (obuf[n+2], obuf[2], SZ_OBUF)
+ obuf[1] = '1'
+ }
+ } else if (fmt == FMT_HEX) {
+ n = nchars - (nbits + 3) / 4
+ if (n > 0)
+ call strcpy (obuf[n+1], obuf[1], SZ_OBUF)
+ }
+ }
+ }
+
+ case FMT_EXPON, FMT_FIXED, FMT_GENERAL, FMT_HMS, FMT_MINSEC:
+ if (decpl == USE_DEFAULT || decpl == 0)
+ switch (fmt) {
+ case FMT_EXPON, FMT_GENERAL:
+ decpl = precision
+ case FMT_HMS, FMT_MINSEC:
+ if (decpl == USE_DEFAULT)
+ decpl = 1
+ default:
+ if (decpl == USE_DEFAULT)
+ decpl = precision
+ }
+ repeat {
+ # Need the case sensitive format char here.
+ n = dtoc (value, obuf, SZ_OBUF, decpl, format_char, width+1)
+ decpl = decpl - 1
+ } until (n <= width || decpl <= 0)
+
+ case FMT_TOCOLUMN: # advance to column
+ for (i=int(value); col < i; col=col+1)
+ call putci (fd, ' ')
+ call fpradv()
+ return
+
+ case FMT_WHITESPACE: # output whitespace
+ for (i=0; i < int(value); i=i+1)
+ call putci (fd, ' ')
+ col = col + i
+ call fpradv()
+ return
+
+ case FMT_COMPLEX:
+ if (decpl == USE_DEFAULT) # set defaults
+ decpl = precision
+ else
+ decpl = abs (decpl)
+
+ if (IS_INDEFD (value))
+ xnum = INDEFX
+ else
+ xnum = complex (value)
+
+ # Convert, decrease precision until it fits in given field width.
+ repeat {
+ n = xtoc (xnum, obuf, SZ_OBUF, decpl, 'e', SZ_OBUF)
+ decpl = decpl - 1
+ } until (n <= width || decpl <= 0)
+ }
+
+ # Move the string in OBUF to the output file, left or right justifying
+ # as specified. Advance to the next format spec (or finish up).
+
+ if (width == SZ_OBUF) # free format?
+ width = 0
+ call fmtstr (fd, obuf, col, fill_char, left_justify, SZ_OBUF, width)
+ call fpradv ()
+end
diff --git a/sys/fmtio/pargb.x b/sys/fmtio/pargb.x
new file mode 100644
index 00000000..bc3e6eb0
--- /dev/null
+++ b/sys/fmtio/pargb.x
@@ -0,0 +1,16 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <printf.h>
+
+# PARGB -- Print a boolean operand (as a string).
+
+procedure pargb (bval)
+
+bool bval
+
+begin
+ if (bval)
+ call pargstr ("yes")
+ else
+ call pargstr ("no")
+end
diff --git a/sys/fmtio/pargstr.x b/sys/fmtio/pargstr.x
new file mode 100644
index 00000000..59fc7433
--- /dev/null
+++ b/sys/fmtio/pargstr.x
@@ -0,0 +1,26 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <printf.h>
+
+# PARGSTR -- Pass a string type operand to printf.
+
+procedure pargstr (str)
+
+char str[ARB]
+int maxch
+include "fmt.com"
+
+begin
+ call fmt_read() # get format
+
+ if (decpl == USE_DEFAULT)
+ maxch = SZ_OBUF
+ else
+ maxch = abs (decpl)
+
+ if (width == USE_DEFAULT)
+ width = 0
+
+ call fmtstr (fd, str, col, fill_char, left_justify, maxch, width)
+ call fpradv ()
+end
diff --git a/sys/fmtio/pargx.x b/sys/fmtio/pargx.x
new file mode 100644
index 00000000..d399da29
--- /dev/null
+++ b/sys/fmtio/pargx.x
@@ -0,0 +1,57 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include <ctype.h>
+include <printf.h>
+
+# PARGX -- Pass a numeric argument of type complex to printf. Get the format
+# spec and format the number on the output file. Try to provide reasonable
+# automatic type conversions. Avoid any type coercion of indefinites.
+
+procedure pargx (xval)
+
+complex xval # complex value to be encoded
+double value
+int n, xtoc()
+include "fmt.com"
+
+begin
+ call fmt_read() # read format
+
+ if (format_char == FMT_COMPLEX || format_char == USE_DEFAULT) {
+ if (width == USE_DEFAULT) # print as (r,r)
+ width = SZ_OBUF
+
+ if (decpl == USE_DEFAULT || decpl == 0)
+ decpl = NDIGITS_RP
+ else
+ decpl = abs (decpl)
+
+ # Encode number in the available field width, decreasing the
+ # precision until the number fits.
+
+ repeat {
+ n = xtoc (xval, obuf, SZ_OBUF, decpl, FMT_EXPON, SZ_OBUF)
+ decpl = decpl - 1
+ } until (n <= width || decpl <= 0)
+
+ # Move the string in OBUF to the output file, left or right
+ # justifying as specified. Advance to the next format spec
+ # (or finish up).
+
+ if (width == SZ_OBUF) # free format?
+ width = 0
+ call fmtstr (fd, obuf, col, fill_char, left_justify, SZ_OBUF, width)
+ call fpradv ()
+
+ } else {
+ # Print real part of complex number in some format other than
+ # complex.
+
+ value = real (xval)
+ if (IS_INDEFR (real(xval)))
+ value = INDEFD
+
+ call pargg (value, TY_REAL)
+ }
+end
diff --git a/sys/fmtio/patmatch.x b/sys/fmtio/patmatch.x
new file mode 100644
index 00000000..9972060f
--- /dev/null
+++ b/sys/fmtio/patmatch.x
@@ -0,0 +1,568 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <pattern.h>
+include <ctype.h>
+include <chars.h>
+
+# PATMATCH.X -- Routines for matching regular expressions (general pattern
+# matching). Adapted from Software Tools.
+#
+# patsize = patmake (patstr, patbuf, sz_patbuf)
+# next_char = patmatch (str, patbuf)
+# next_char = gpatmatch (str, patbuf, first_char, last_char)
+# ip = patindex (patbuf, index_number)
+#
+# The pattern string must be encoded with PATMAKE before use. See also
+# STRMATCH, STRNCMP, etc.
+
+# Pattern codes (for encoded patterns).
+
+define EOP -1 # end of encoded pattern
+define CHAR -2 # match char
+define UCHAR -3 # match either case
+define LCHAR -4 # match either case
+define BOL -5 # match at beginning of line
+define EOL -6 # match at end of line
+define ANY -7 # "?"
+define WHITESPACE -8 # "#"
+define CCL -9 # [...
+define NCCL -10 # [^...
+define CLOSURE -11 # "*"
+define INDEX -12 # % (mark index of ^ in pattern)
+
+define CH_INDEX '%' # move to <chars.h> after a while
+
+# Definitions for the closure structure.
+
+define CLOSIZE 4 # size of closure structure
+define COUNT 1 # repeat count for matches
+define PREVCL 2 # index of previous closure in pat
+define START 3 # index in str where match starts
+
+
+# PATMATCH -- Match pattern anywhere on line. Returns the index of the
+# first character AFTER the match, or zero if no match.
+
+int procedure patmatch (str, pat)
+
+char str[ARB] # string to be scanned
+char pat[ARB] # encoded pattern
+
+int first_char, last_char
+int gpatmatch()
+
+begin
+ return (gpatmatch (str, pat, first_char, last_char))
+end
+
+
+# GPATMATCH -- Generalized pattern match. Matches pattern anywhere on
+# line (the first such pattern matched terminates the search). Function
+# return same as for PATMATCH, but also returns indices of the first and
+# last characters in the matched substring.
+
+int procedure gpatmatch (str, pat, first_char, last_char)
+
+char str[ARB] # string to be scanned
+char pat[ARB] # encoded pattern
+int first_char # index of first char matched (output)
+int last_char # index of last char matched (output)
+
+int ip, nchars_matched
+int pat_amatch() # anchored match
+
+begin
+ nchars_matched = 0
+
+ if (pat[1] == BOL) {
+ ip = 1
+ nchars_matched = pat_amatch (str, ip, pat)
+ } else {
+ for (ip=1; str[ip] != EOS; ip=ip+1) {
+ nchars_matched = pat_amatch (str, ip, pat)
+ if (nchars_matched > 0)
+ break
+ }
+ }
+
+ if (nchars_matched > 0) {
+ first_char = ip
+ last_char = ip + nchars_matched - 1
+ return (last_char + 1)
+ } else
+ return (0)
+end
+
+
+# PATINDEX -- Return the index of a marked position in the pattern. Inclusion
+# of the character % in the pattern causes the index of the character following
+# the % to be saved in the encoded pattern at patmatch time. We are called
+# after a patmatch operation to scan the pattern and recall the Nth saved index.
+# Zero is returned if N is larger than the number of saved index points.
+
+int procedure patindex (pat, n)
+
+char pat[ARB] # encoded pattern
+int n # number of index to be returned
+
+int pp, ix
+int pat_gsize()
+
+begin
+ ix = 1
+ for (pp=1; pat[pp] != EOP; pp=pp+pat_gsize(pat,pp))
+ if (pat[pp] == INDEX)
+ if (ix >= n)
+ return (pat[pp+1])
+ else
+ ix = ix + 1
+
+ return (0)
+end
+
+
+# PAT_AMATCH -- Anchored match. Look for match starting at the given
+# offset. Return the number of characters matched.
+
+int procedure pat_amatch (str, from, pat)
+
+char str[ARB] # string to be matched
+int from # starting at this index
+char pat[ARB] # encoded pattern
+
+int ip, pp, offset, stack
+int pat_omatch(), pat_gsize()
+
+begin
+ stack = 0
+ offset = from # next unexamined input char
+
+ for (pp=1; pat[pp] != EOP; pp = pp + pat_gsize(pat,pp)) {
+ if (pat[pp] == CLOSURE) { # a closure entry
+ stack = pp
+ pp = pp + CLOSIZE
+ # Match as many characters as possible, save results
+ for (ip=offset; str[ip] != EOS; )
+ if (pat_omatch (str, ip, pat, pp) == NO)
+ break
+ pat[stack+COUNT] = ip - offset
+ pat[stack+START] = offset
+ offset = ip # character that made us fail
+
+ } else if (pat_omatch (str, offset, pat, pp) == NO) {
+ for (; stack > 0; stack = pat[stack+PREVCL])
+ if (pat[stack+COUNT] > 0)
+ break
+ if (stack <= 0) # stack is empty
+ return (0) # return failure
+
+ pat[stack+COUNT] = pat[stack+COUNT] - 1
+ pp = stack + CLOSIZE
+ offset = pat[stack+START] + pat[stack+COUNT]
+ }
+ }
+
+ return (offset-from) # successful match
+end
+
+
+# PAT_GSIZE -- Returns size of pattern entry at pat[n].
+
+int procedure pat_gsize (pat, n)
+
+char pat[ARB] # encoded pattern
+int n # pointer into pattern
+int pattern_size
+
+begin
+ switch (pat[n]) {
+ case CHAR, UCHAR, LCHAR, INDEX:
+ pattern_size = 2
+ case BOL, EOL, ANY, WHITESPACE:
+ pattern_size = 1
+ case CCL, NCCL:
+ pattern_size = pat[n+1] + 2
+ case CLOSURE: # not used
+ pattern_size = CLOSIZE
+ default:
+ call error (0, "In patsize: can't happen.")
+ }
+
+ return (pattern_size)
+end
+
+
+# PAT_OMATCH -- Try to match a single pattern at pat[pp]. If match, bump IP
+# to point to the next unmatched character. Return OK if match.
+
+int procedure pat_omatch (str, ip, pat, pp)
+
+char str[ARB] # string to be scanned
+int ip # starting index in string (may be changed)
+char pat[ARB] # encoded pattern
+int pp # pointer to next pattern element
+
+char str_ch
+int bump, pat_locate()
+
+begin
+ if (str[ip] == EOS)
+ if (pat[pp] == INDEX) {
+ pat[pp+1] = ip
+ return (YES)
+ } else if (pat[pp] == EOL) {
+ return (YES)
+ } else
+ return (NO)
+
+ # Treat CHAR (simple character match) as a special case to speed
+ # things up a bit.
+
+ if (pat[pp] == CHAR)
+ if (str[ip] == pat[pp+1]) {
+ ip = ip + 1
+ return (YES)
+ } else
+ return (NO)
+
+ # Compare as indicated by encoded pattern opcode.
+ bump = -1
+
+ switch (pat[pp]) {
+ case UCHAR: # match either case
+ str_ch = str[ip]
+ if (IS_LOWER (str_ch))
+ str_ch = TO_UPPER (str_ch)
+ if (str_ch == pat[pp+1])
+ bump = 1
+ case LCHAR: # match either case
+ str_ch = str[ip]
+ if (IS_UPPER (str_ch))
+ str_ch = TO_LOWER (str_ch)
+ if (str_ch == pat[pp+1])
+ bump = 1
+ case BOL: # beg. of line
+ if (ip == 1)
+ bump = 0
+ case EOL: # end of line
+ if (str[ip] == '\n')
+ bump = 0
+ case ANY: # match any char
+ if (str[ip] != '\n')
+ bump = 1
+ case WHITESPACE:
+ for (bump=0; IS_WHITE (str[ip+bump]); bump=bump+1)
+ ;
+ case CCL: # char class
+ if (pat_locate (str[ip], pat, pp + 1) == YES)
+ bump = 1
+ case NCCL: # not in char class
+ if (str[ip] != '\n' && pat_locate (str[ip], pat, pp + 1) == NO)
+ bump = 1
+ case INDEX:
+ pat[pp+1] = ip
+ bump = 0
+ default:
+ call error (0, "In omatch: can't happen.")
+ }
+
+ if (bump >= 0) {
+ ip = ip + bump
+ return (YES)
+ } else
+ return (NO)
+end
+
+
+# PAT_LOCATE -- Look for c in char class at pat[offset].
+
+int procedure pat_locate (ch, pat, offset)
+
+char ch # char to search for
+char pat[ARB] # encoded pattern
+int offset # offset of character class in pattern
+
+int nchars, i
+
+begin
+ # Size of class is at pat[offset], characters follow.
+ nchars = pat[offset]
+ do i = 1, nchars
+ if (ch == pat[offset+i])
+ return (YES)
+
+ return (NO)
+end
+
+
+# PATMAKE -- Encode pattern specification string. Returns the size of
+# the encoded pattern string.
+
+int procedure patmake (str, pat, sz_pat)
+
+char str[ARB] # pattern to be encoded
+char pat[ARB] # encoded pattern (output)
+int sz_pat # max size of the pattern string
+int gpatmake()
+
+begin
+ return (gpatmake (str, 1, EOS, pat, sz_pat))
+end
+
+
+# GPATMAKE -- Make pattern from str[from], terminate at delim.
+
+int procedure gpatmake (patstr, from, delim, patbuf, sz_pat)
+
+char patstr[ARB] # pattern to be encoded
+int from # starting index
+int delim # delimiter character
+char patbuf[ARB] # put encoded pattern here
+int sz_pat # max chars in encoded pattern
+
+int ip, op, last_closure, last_op, l_op
+char cval
+bool ignore_case
+int cctoc(), pat_getccl(), pat_stclos()
+
+begin
+ op = 1 # pat index
+ last_op = 1
+ last_closure = 0
+ ignore_case = false
+
+ for (ip=from; patstr[ip] != delim && patstr[ip] != EOS; ip=ip+1) {
+ l_op = op
+
+ # If CVAL gets set to nonzero it will be deposited in the output
+ # buffer at end of switch.
+
+ cval = 0
+
+ switch (patstr[ip]) {
+ case CH_ANY:
+ cval = ANY
+ case CH_WHITESPACE:
+ cval = WHITESPACE
+
+ case CH_BOL:
+ if (ip == from)
+ cval = BOL
+ else {
+ cval = CHAR
+ call chdeposit (cval, patbuf, sz_pat, op)
+ cval = CH_BOL
+ }
+
+ case CH_EOL:
+ if (patstr[ip+1] == delim)
+ cval = EOL
+ else {
+ cval = CHAR
+ call chdeposit (cval, patbuf, sz_pat, op)
+ cval = CH_EOL
+ }
+
+ case CH_IGNORECASE:
+ ignore_case = true
+ case CH_MATCHCASE:
+ ignore_case = false
+
+ case CH_CCL:
+ if (pat_getccl (patstr, patbuf, sz_pat, ip, op) == ERR)
+ return (ERR)
+
+ case CH_CLOSURE:
+ # The "closure" of a pattern, e.g., "..*".
+
+ l_op = last_op
+ # Convert a pattern such as "*..." into "?*...".
+ if (ip == from) # closure of nothing
+ cval = ANY
+ else {
+ switch (patbuf[l_op]) {
+ case BOL, EOL, CLOSURE:
+ cval = ANY
+ }
+ }
+
+ if (cval != 0)
+ call chdeposit (cval, patbuf, sz_pat, op)
+ cval = 0
+
+ last_closure = pat_stclos (patbuf, sz_pat, op, last_op,
+ last_closure)
+
+ case CH_INDEX:
+ # This metacharacter does not match anything, but rather is
+ # used to record the index of the marked position in the
+ # matched pattern. The index is recorded in the pattern
+ # buffer at match time, to be later recovered with patindex.
+
+ cval = INDEX
+ call chdeposit (cval, patbuf, sz_pat, op)
+ cval = 0
+ call chdeposit (cval, patbuf, sz_pat, op)
+
+ default:
+ # Ordinary character.
+
+ # Deposit command code.
+ if (ignore_case) {
+ if (IS_UPPER (patstr[ip]))
+ cval = UCHAR
+ else
+ cval = LCHAR
+ } else
+ cval = CHAR
+ call chdeposit (cval, patbuf, sz_pat, op)
+
+ # Set CVAL to actual character value.
+ if (patstr[ip] == CH_ESCAPE) {
+ if (cctoc (patstr, ip, cval) == 1)
+ cval = patstr[ip]
+ else
+ ip = ip - 1
+ } else
+ cval = patstr[ip]
+ }
+
+ # Deposit the character left in CVAL by the code above.
+ if (cval != 0)
+ call chdeposit (cval, patbuf, sz_pat, op)
+
+ last_op = l_op
+ }
+
+ # Terminate the pattern.
+ cval = EOP
+ call chdeposit (cval, patbuf, sz_pat, op)
+
+ if (patstr[ip] != delim || op >= sz_pat)
+ return (ERR)
+ else
+ return (op - 1) # return size patbuf
+end
+
+
+# PAT_GETCCL -- Expand character class at patstr[i] into patbuf[op].
+
+int procedure pat_getccl (patstr, patbuf, sz_pat, ip, op)
+
+char patstr[ARB], patbuf[ARB]
+int sz_pat, ip, op
+char cval
+int op_start
+
+begin
+ ip = ip + 1 # skip over [
+ if (patstr[ip] == CH_NOT) {
+ cval = NCCL
+ ip = ip + 1
+ } else
+ cval = CCL
+
+ call chdeposit (cval, patbuf, sz_pat, op)
+
+ op_start = op
+ cval = 0
+ call chdeposit (cval, patbuf, sz_pat, op) # leave room for count
+ call pat_filset (CH_CCLEND, patstr, ip, patbuf, sz_pat, op)
+ patbuf[op_start] = op - op_start - 1 # fix up count
+
+ if (patstr[ip] == CH_CCLEND)
+ return (OK)
+ else
+ return (ERR)
+end
+
+
+# PAT_STCLOS -- Insert closure entry at patbuf[op].
+
+int procedure pat_stclos (patbuf, sz_pat, op, last_op, last_closure)
+
+char patbuf[ARB]
+int sz_pat
+int op
+int last_op
+int last_closure
+
+char cvals[4]
+int next_closure, jp, jt, i
+
+begin
+ for (jp=op-1; jp >= last_op; jp=jp-1) { # make a hole
+ jt = min (sz_pat, jp + CLOSIZE)
+ patbuf[jt] = patbuf[jp]
+ }
+
+ op = op + CLOSIZE
+ next_closure = last_op
+
+ cvals[1] = CLOSURE
+ cvals[2] = 0 # COUNT
+ cvals[3] = last_closure # PREVCL
+ cvals[4] = 0 # START
+
+ do i = 1, 4
+ call chdeposit (cvals[i], patbuf, sz_pat, last_op)
+
+ return (next_closure)
+end
+
+
+# PAT_FILSET -- Process a character class into a simple list of characters.
+
+procedure pat_filset (delim, patstr, ip, patbuf, sz_pat, op)
+
+int delim # character class delimiter character
+char patstr[ARB] # character class characters
+int ip # index where they start
+char patbuf[ARB] # encode character class in this string
+int sz_pat # max chars out
+int op # offset into patbuf
+
+char ch, ch1, ch2
+int cctoc()
+
+begin
+ for (; patstr[ip] != delim && patstr[ip] != EOS; ip=ip+1) {
+ if (patstr[ip] == ESCAPE) { # escape seq.
+ if (cctoc (patstr, ip, ch) == 1)
+ ch = patstr[ip]
+ else
+ ip = ip - 1
+ call chdeposit (ch, patbuf, sz_pat, op)
+
+ } else if (patstr[ip] != CH_RANGE) {
+ call chdeposit (patstr[ip], patbuf, sz_pat, op)
+
+ } else if (op <= 1 || patstr[ip+1] == EOS) { # literal "-"
+ ch = CH_RANGE
+ call chdeposit (ch, patbuf, sz_pat, op)
+
+ } else {
+ # Here if char is CH_RANGE, denoting a range of characters to be
+ # included in the character class. Range is valid only if limit
+ # chars are both digits, both lower case, or both upper case.
+
+ ch1 = patbuf[op-1] # not same as patstr[ip-1]
+ ch2 = patstr[ip+1]
+
+ if ((IS_DIGIT (ch1) && IS_DIGIT (ch2)) ||
+ (IS_LOWER (ch1) && IS_LOWER (ch2)) ||
+ (IS_UPPER (ch1) && IS_UPPER (ch2))) {
+ if (ch1 <= ch2)
+ for (ch=ch1+1; ch <= ch2; ch=ch+1)
+ call chdeposit (ch, patbuf, sz_pat, op)
+ else
+ for (ch=ch1-1; ch >= ch2; ch=ch-1)
+ call chdeposit (ch, patbuf, sz_pat, op)
+ ip = ip + 1
+ } else {
+ ch = CH_RANGE
+ call chdeposit (ch, patbuf, sz_pat, op)
+ }
+ }
+ }
+end
diff --git a/sys/fmtio/printf.x b/sys/fmtio/printf.x
new file mode 100644
index 00000000..e0662d90
--- /dev/null
+++ b/sys/fmtio/printf.x
@@ -0,0 +1,13 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <printf.h>
+
+# PRINTF -- Format output to the standard output.
+
+procedure printf (format_string)
+
+char format_string[ARB]
+
+begin
+ call fprntf (STDOUT, format_string, REGULAR_FILE)
+end
diff --git a/sys/fmtio/resetscan.x b/sys/fmtio/resetscan.x
new file mode 100644
index 00000000..bde3a8f7
--- /dev/null
+++ b/sys/fmtio/resetscan.x
@@ -0,0 +1,14 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# RESET_SCAN -- Initialize the scan common at the start of a scan. May also
+# be called by the user to rescan a line, following a conversion failure.
+
+procedure reset_scan()
+
+include "scan.com"
+
+begin
+ sc_ip = 1
+ sc_ntokens = 0
+ sc_stopscan = false
+end
diff --git a/sys/fmtio/scan.com b/sys/fmtio/scan.com
new file mode 100644
index 00000000..cb60824c
--- /dev/null
+++ b/sys/fmtio/scan.com
@@ -0,0 +1,10 @@
+# SCAN.COM -- Global common for the scan family of routines.
+
+define SZ_SCANBUF 4096
+
+int sc_ip # char pointer into lbuf
+int sc_ntokens # keep track of successful conversions
+bool sc_stopscan # set if conversion is unsuccessful
+char sc_scanbuf[SZ_SCANBUF] # line buffer for scan procedures
+
+common /scncom/ sc_ip, sc_ntokens, sc_stopscan, sc_scanbuf
diff --git a/sys/fmtio/scanc.x b/sys/fmtio/scanc.x
new file mode 100644
index 00000000..1578af42
--- /dev/null
+++ b/sys/fmtio/scanc.x
@@ -0,0 +1,14 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# SCANC -- Return the next character from the scanned input.
+
+procedure scanc (cval)
+
+char cval
+include "scan.com"
+
+begin
+ cval = sc_scanbuf[sc_ip]
+ if (cval != EOS)
+ sc_ip = sc_ip + 1
+end
diff --git a/sys/fmtio/sprintf.x b/sys/fmtio/sprintf.x
new file mode 100644
index 00000000..c247d868
--- /dev/null
+++ b/sys/fmtio/sprintf.x
@@ -0,0 +1,19 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <printf.h>
+
+# SPRINTF: Open string as a file, call fprntf. When the last argument is
+# passed, and EOS is reached, the string will be closed (by fpradv).
+
+procedure sprintf (outstr, maxch, format_string)
+
+char outstr[maxch]
+int maxch
+char format_string[ARB]
+int mem_fd, stropen()
+errchk stropen, fprntf
+
+begin
+ mem_fd = stropen (outstr, maxch, WRITE_ONLY)
+ call fprntf (mem_fd, format_string, STRING_FILE)
+end
diff --git a/sys/fmtio/sscan.x b/sys/fmtio/sscan.x
new file mode 100644
index 00000000..6c877dc2
--- /dev/null
+++ b/sys/fmtio/sscan.x
@@ -0,0 +1,24 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# SSCAN -- Begin a scan from a string. Only the first newline terminated
+# line in the string buffer will be scanned. If a string buffer containing
+# more than a single line must be scanned, MEMOPEN and FSCAN may be used.
+
+procedure sscan (str)
+
+char str[ARB]
+int ip, op
+include "scan.com"
+
+begin
+ op = 1
+ for (ip=1; str[ip] != EOS && str[ip] != '\n'; ip=ip+1) {
+ sc_scanbuf[op] = str[ip]
+ op = op + 1
+ if (op >= SZ_SCANBUF)
+ break
+ }
+
+ sc_scanbuf[op] = EOS
+ call reset_scan() # initialize scan
+end
diff --git a/sys/fmtio/strcat.x b/sys/fmtio/strcat.x
new file mode 100644
index 00000000..777174a3
--- /dev/null
+++ b/sys/fmtio/strcat.x
@@ -0,0 +1,12 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# STRCAT -- String concatenation. String STR is appended to OUTSTR.
+
+procedure strcat (str, outstr, maxch)
+
+char str[ARB], outstr[ARB]
+int maxch, junk, gstrcat()
+
+begin
+ junk = gstrcat (str, outstr, maxch)
+end
diff --git a/sys/fmtio/strcmp.x b/sys/fmtio/strcmp.x
new file mode 100644
index 00000000..67dbc2ba
--- /dev/null
+++ b/sys/fmtio/strcmp.x
@@ -0,0 +1,17 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# STRCMP -- Compare two strings. -N is returned if S1 < S2, 0 if S1 == S2,
+# and +N if S1 > S2.
+
+int procedure strcmp (s1, s2)
+
+char s1[ARB], s2[ARB] # strings to be compared
+int i
+
+begin
+ do i = 1, ARB
+ if (s1[i] != s2[i])
+ return (s1[i] - s2[i])
+ else if (s1[i] == EOS)
+ return (0)
+end
diff --git a/sys/fmtio/strcpy.x b/sys/fmtio/strcpy.x
new file mode 100644
index 00000000..8892e89d
--- /dev/null
+++ b/sys/fmtio/strcpy.x
@@ -0,0 +1,18 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# STRCPY -- Copy string s1 to s2.
+
+procedure strcpy (s1, s2, maxch)
+
+char s1[ARB], s2[ARB]
+int maxch, i
+
+begin
+ do i = 1, maxch {
+ s2[i] = s1[i]
+ if (s2[i] == EOS)
+ return
+ }
+
+ s2[maxch+1] = EOS
+end
diff --git a/sys/fmtio/strdic.x b/sys/fmtio/strdic.x
new file mode 100644
index 00000000..3da4a71b
--- /dev/null
+++ b/sys/fmtio/strdic.x
@@ -0,0 +1,73 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+
+# STRDIC -- Search a dictionary string for a match with an input string.
+# The input string may be an abbreviation of a dictionary entry, however,
+# it is an error if the abbreviation is not unique. The entries in the
+# dictionary string are separated by a delimiter character which is the first
+# character of the dictionary string. The full name of the matched dictionary
+# entry found is returned in out_str; the function value is the word index of
+# the dictionary entry. The output string may be the same as the input string.
+
+int procedure strdic (in_str, out_str, maxchars, dict)
+
+char in_str[ARB] # Input string, always lower case
+char out_str[ARB] # Output string as found in dictionary
+int maxchars # Maximum length of output string
+char dict[ARB] # Dictionary string
+
+char ch, fch
+int start, len, ip, i, match, entry
+int strlen(), strncmp()
+
+begin
+ if (dict[1] == EOS)
+ return (0)
+
+ for (i=1; IS_WHITE (in_str[i]); i=i+1)
+ ;
+
+ start = i
+ match = 0
+ ip = 2
+ len = strlen (in_str[start])
+ fch = in_str[start]
+
+ # Search the dictionary string. If the input string matches a
+ # dictionary entry it is either an exact match (len = dictionary
+ # entry length) or a legal abbreviation. If an abbreviation
+ # matches two entries it is ambiguous and an error.
+
+ for (entry=1; dict[ip] != EOS; entry=entry+1) {
+ if (dict[ip] == fch) {
+ if (strncmp (dict[ip], in_str[start], len) == 0) {
+ for (i=1; i <= maxchars; i=i+1) {
+ ch = dict[ip+i-1]
+ if ((ch == dict[1]) || (ch == EOS))
+ break
+ out_str[i] = ch
+ }
+ out_str[i] = EOS
+
+ if ((dict[ip+len] == dict[1]) || (dict[ip+len] == EOS))
+ return (entry) # exact match
+ else {
+ # If we already have a match and the new match is not
+ # exact, then the abbreviation is ambiguous.
+
+ if (match != 0)
+ return (0)
+ else
+ match = entry
+ }
+ }
+ }
+
+ repeat {
+ ip = ip + 1
+ } until (dict[ip-1] == dict[1] || dict[ip] == EOS)
+ }
+
+ return (match)
+end
diff --git a/sys/fmtio/streq.x b/sys/fmtio/streq.x
new file mode 100644
index 00000000..0f394d6d
--- /dev/null
+++ b/sys/fmtio/streq.x
@@ -0,0 +1,16 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# STREQ -- Compare two strings for equality.
+
+bool procedure streq (s1, s2)
+
+char s1[ARB], s2[ARB]
+int ip
+
+begin
+ do ip = 1, ARB
+ if (s1[ip] != s2[ip])
+ return (false)
+ else if (s1[ip] == EOS)
+ return (s2[ip] == EOS)
+end
diff --git a/sys/fmtio/strge.x b/sys/fmtio/strge.x
new file mode 100644
index 00000000..4fc24c59
--- /dev/null
+++ b/sys/fmtio/strge.x
@@ -0,0 +1,16 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# STRGE -- Is S1 >= S2.
+
+bool procedure strge (s1, s2)
+
+char s1[ARB], s2[ARB]
+int ip
+
+begin
+ do ip = 1, ARB
+ if (s2[ip] == EOS)
+ return (true)
+ else if (s1[ip] != s2[ip])
+ return (s1[ip] > s2[ip])
+end
diff --git a/sys/fmtio/strgt.x b/sys/fmtio/strgt.x
new file mode 100644
index 00000000..720a1397
--- /dev/null
+++ b/sys/fmtio/strgt.x
@@ -0,0 +1,16 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# STRGT -- Is S1 > S2.
+
+bool procedure strgt (s1, s2)
+
+char s1[ARB], s2[ARB]
+int ip
+
+begin
+ do ip = 1, ARB
+ if (s1[ip] == EOS)
+ return (false)
+ else if (s1[ip] != s2[ip])
+ return (s1[ip] > s2[ip])
+end
diff --git a/sys/fmtio/stridx.x b/sys/fmtio/stridx.x
new file mode 100644
index 00000000..dda74f5a
--- /dev/null
+++ b/sys/fmtio/stridx.x
@@ -0,0 +1,17 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# STRIDX -- Return the index of the first occurrence of a character in a
+# string.
+
+int procedure stridx (ch, str)
+
+char ch, str[ARB]
+int ip
+
+begin
+ do ip = 1, ARB
+ if (str[ip] == EOS)
+ return (0)
+ else if (str[ip] == ch)
+ return (ip)
+end
diff --git a/sys/fmtio/stridxs.x b/sys/fmtio/stridxs.x
new file mode 100644
index 00000000..fef58806
--- /dev/null
+++ b/sys/fmtio/stridxs.x
@@ -0,0 +1,43 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+define BIGSET 10
+define SZ_ASCII 128
+
+# STRIDXS -- Return the index of the first occurrence of any of a set of
+# characters in a string.
+
+int procedure stridxs (set, str)
+
+char set[ARB] # set of characters to be searched for
+char str[ARB] # string to be searched
+
+int setlen, ip, i
+char ch, lut[SZ_ASCII]
+int strlen()
+
+begin
+ setlen = strlen (set)
+
+ if (setlen > BIGSET) {
+ # Encode the set in a lookup table.
+ call aclrc (lut, SZ_ASCII)
+ do i = 1, setlen
+ lut[set[i]] = 1
+
+ # Search the string.
+ for (ip=1; str[ip] != EOS; ip=ip+1)
+ if (lut[str[ip]] != 0)
+ return (ip)
+
+ } else {
+ # Set is too small to be worth using a lookup table.
+ for (ip=1; str[ip] != EOS; ip=ip+1) {
+ ch = str[ip]
+ do i = 1, setlen
+ if (ch == set[i])
+ return (ip)
+ }
+ }
+
+ return (0)
+end
diff --git a/sys/fmtio/strldx.x b/sys/fmtio/strldx.x
new file mode 100644
index 00000000..0087a208
--- /dev/null
+++ b/sys/fmtio/strldx.x
@@ -0,0 +1,20 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# STRLDX -- Return the index of the last occurrence of a character in a
+# string.
+
+int procedure strldx (ch, str)
+
+char ch, str[ARB]
+int ip, offset
+
+begin
+ offset = 0
+ do ip = 1, ARB
+ if (str[ip] == EOS)
+ break
+ else if (str[ip] == ch)
+ offset = ip
+
+ return (offset)
+end
diff --git a/sys/fmtio/strldxs.x b/sys/fmtio/strldxs.x
new file mode 100644
index 00000000..1c583d0f
--- /dev/null
+++ b/sys/fmtio/strldxs.x
@@ -0,0 +1,46 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+define BIGSET 10
+define SZ_ASCII 128
+
+# STRLDXS -- Return the index of the last occurrence of any of a set of
+# characters in a string.
+
+int procedure strldxs (set, str)
+
+char set[ARB] # set of characters to be searched for
+char str[ARB] # string to be searched
+
+int setlen, ip, i, last_member
+char ch, lut[SZ_ASCII]
+int strlen()
+
+begin
+ setlen = strlen (set)
+ last_member = 0
+
+ if (setlen > BIGSET) {
+ # Encode the set in a lookup table.
+ call aclrc (lut, SZ_ASCII)
+ do i = 1, setlen
+ lut[set[i]] = 1
+
+ # Search the string.
+ for (ip=1; str[ip] != EOS; ip=ip+1)
+ if (lut[str[ip]] != 0)
+ last_member = ip
+
+ } else {
+ # Set is too small to be worth using a lookup table.
+ for (ip=1; str[ip] != EOS; ip=ip+1) {
+ ch = str[ip]
+ do i = 1, setlen
+ if (ch == set[i]) {
+ last_member = ip
+ break
+ }
+ }
+ }
+
+ return (last_member)
+end
diff --git a/sys/fmtio/strle.x b/sys/fmtio/strle.x
new file mode 100644
index 00000000..34ad7870
--- /dev/null
+++ b/sys/fmtio/strle.x
@@ -0,0 +1,16 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# STRLE -- Is S1 <= S2.
+
+bool procedure strle (s1, s2)
+
+char s1[ARB], s2[ARB]
+int ip
+
+begin
+ do ip = 1, ARB
+ if (s1[ip] == EOS)
+ return (true)
+ else if (s1[ip] != s2[ip])
+ return (s1[ip] < s2[ip])
+end
diff --git a/sys/fmtio/strlen.x b/sys/fmtio/strlen.x
new file mode 100644
index 00000000..4c3e7364
--- /dev/null
+++ b/sys/fmtio/strlen.x
@@ -0,0 +1,14 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# STRLEN -- Return length of string (EOS not included).
+
+int procedure strlen (str)
+
+char str[ARB]
+int ip
+
+begin
+ do ip = 1, ARB
+ if (str[ip] == EOS)
+ return (ip - 1)
+end
diff --git a/sys/fmtio/strlt.x b/sys/fmtio/strlt.x
new file mode 100644
index 00000000..0a530d4d
--- /dev/null
+++ b/sys/fmtio/strlt.x
@@ -0,0 +1,16 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# STRLT -- Is S1 < S2.
+
+bool procedure strlt (s1, s2)
+
+char s1[ARB], s2[ARB]
+int ip
+
+begin
+ do ip = 1, ARB
+ if (s2[ip] == EOS)
+ return (false)
+ else if (s1[ip] != s2[ip])
+ return (s1[ip] < s2[ip])
+end
diff --git a/sys/fmtio/strlwr.x b/sys/fmtio/strlwr.x
new file mode 100644
index 00000000..318366ac
--- /dev/null
+++ b/sys/fmtio/strlwr.x
@@ -0,0 +1,18 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+
+# STRLWR -- Convert string to lower case.
+
+procedure strlwr (a)
+
+char a[ARB]
+int ip
+
+begin
+ do ip = 1, ARB
+ if (a[ip] == EOS)
+ break
+ else if (IS_UPPER(a[ip]))
+ a[ip] = TO_LOWER (a[ip])
+end
diff --git a/sys/fmtio/strmac.x b/sys/fmtio/strmac.x
new file mode 100644
index 00000000..1f3f3e98
--- /dev/null
+++ b/sys/fmtio/strmac.x
@@ -0,0 +1,86 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+
+define MAX_ARGS 9
+
+.help strmac
+.nf ___________________________________________________________________________
+STRMAC -- Expand a macro (nonrecursively) by string substitution.
+The macro string may contain zero or more occurrences of the sequences
+"$1" through "$9". The substitution strings are passed in the string
+buffer "argstr", wherein successive strings are delimited by the EOS marker.
+A double EOS marks the end of the list.
+
+Macros $1-$9 are replaced by the substitution string. The sequence $$ is
+replaced by a single $. If any other character follows the $, both the $
+and the following character are passed to the output unchanged. An error
+action is taken if there are insufficient arguments or if the output buffer
+overflows. Bugs: null substitution strings don't work.
+.endhelp ______________________________________________________________________
+
+int procedure strmac (macro, argstr, outstr, maxch)
+
+char macro[ARB] # substitution string
+char argstr[ARB] # argument strings, if any
+char outstr[maxch] # output string
+int maxch
+
+short offset[MAX_ARGS]
+char ch
+int i, ip, op, arg, nargs, nchars
+int strlen()
+
+begin
+ # Determine the offsets of the argument strings.
+ ip = 1
+ for (nargs=1; nargs <= MAX_ARGS; nargs=nargs+1) {
+ nchars = strlen (argstr[ip])
+ if (nchars > 0) {
+ offset[nargs] = ip
+ ip = ip + nchars + 1
+ } else
+ break
+ }
+ nargs = nargs - 1
+
+ # Expand the macro.
+ op = 1
+ for (ip=1; macro[ip] != EOS; ip=ip+1) {
+ ch = macro[ip]
+
+ if (ch == '$') { # Process $ arg sequence.
+ ip = ip + 1
+ ch = macro[ip]
+ if (ch >= '1' && ch <= '9') {
+ arg = TO_INTEG(ch)
+ if (arg > nargs)
+ call error (1, "Strmac: too few substitution arguments")
+ for (i = offset[arg]; argstr[i] != EOS; i=i+1) {
+ outstr[op] = argstr[i]
+ op = op + 1
+ }
+
+ } else if (ch == '$') { # "$$" --> "$"
+ outstr[op] = '$'
+ op = op + 1
+
+ } else { # "$?" --> "$?"
+ outstr[op] = '$'
+ op = op + 1
+ outstr[op] = ch
+ op = op + 1
+ }
+
+ } else { # ordinary character
+ outstr[op] = ch
+ op = op + 1
+ }
+
+ if (op > maxch)
+ call error (2, "Strmac: output buffer overflow")
+ }
+
+ outstr[op] = EOS
+ return (op - 1)
+end
diff --git a/sys/fmtio/strmatch.x b/sys/fmtio/strmatch.x
new file mode 100644
index 00000000..ad16bef8
--- /dev/null
+++ b/sys/fmtio/strmatch.x
@@ -0,0 +1,136 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+include <pattern.h>
+
+.help strmatch, gstrmatch
+.nf ________________________________________________________________________
+STRMATCH -- Find the first occurrence of the string A in the string B.
+If not found, return zero, else return the index of the first character
+following the matched substring.
+
+GSTRMATCH -- More general version of strmatch. The indices of the
+first and last characters matched are returned as arguments. The function
+value is the same as for STRMATCH.
+
+STRMATCH recognizes the metacharacters BOL, EOL, ANY, WHITESPACE, IGNORECASE,
+and MATCHCASE (BOL and EOL are special only as the first and last chars
+in the pattern). The null pattern matches any string. Metacharacters
+can be escaped.
+.endhelp ___________________________________________________________________
+
+
+# STRMATCH -- Search string STR for pattern PAT. Return the index of the
+# next character following the matched substring, or 0.
+
+int procedure strmatch (str, pat)
+
+char pat[ARB], str[ARB]
+int first_char, last_char
+int gstrmatch()
+
+begin
+ return (gstrmatch (str, pat, first_char, last_char))
+end
+
+
+# GSTRMATCH -- Generalized string match. Returns the indices of the first and
+# last characters in the matched substring if a match occurs.
+
+int procedure gstrmatch (str, pat, first_char, last_char)
+
+char pat[ARB], str[ARB]
+int first_char, last_char
+bool ignore_case, bolflag
+char ch, pch
+int i, ip, initial_pp, pp
+
+begin
+ ignore_case = false
+ bolflag = false
+ first_char = 1
+ initial_pp = 1
+
+ if (pat[1] == CH_BOL) { # match at beginning of line?
+ bolflag = true
+ initial_pp = 2
+ }
+
+ # Try to match pattern starting at each character offset in string.
+ do ip = 1, ARB {
+ if (str[ip] == EOS)
+ break
+ i = ip
+
+ # Compare pattern to string str[ip].
+ for (pp=initial_pp; pat[pp] != EOS; pp=pp+1) {
+ switch (pat[pp]) {
+ case CH_WHITESPACE:
+ while (IS_WHITE (str[i]))
+ i = i + 1
+ case CH_ANY:
+ if (str[i] != '\n')
+ i = i + 1
+ case CH_IGNORECASE:
+ ignore_case = true
+ case CH_MATCHCASE:
+ ignore_case = false
+
+ default:
+ pch = pat[pp]
+ if (pch == CH_ESCAPE && pat[pp+1] != EOS) {
+ pp = pp + 1
+ pch = pat[pp]
+ } else if (pch == CH_EOL)
+ if (pat[pp+1] == EOS && (str[i]=='\n' || str[i]==EOS)) {
+ first_char = ip
+ last_char = i
+ if (str[i] == EOS)
+ last_char = last_char - 1
+ return (last_char + 1)
+ }
+
+ ch = str[i]
+ i = i + 1
+
+ # Compare ordinary characters. The comparison is trivial
+ # unless case insensitivity is required.
+
+ if (ignore_case) {
+ if (IS_UPPER (ch)) {
+ if (IS_UPPER (pch)) {
+ if (pch != ch)
+ break
+ } else if (pch != TO_LOWER (ch))
+ break
+ } else if (IS_LOWER (ch)) {
+ if (IS_LOWER (pch)) {
+ if (pch != ch)
+ break
+ } else if (pch != TO_UPPER (ch))
+ break
+ } else {
+ if (pch != ch)
+ break
+ }
+ } else {
+ if (pch != ch)
+ break
+ }
+ }
+ }
+
+ # If the above loop was exited before the end of the pattern
+ # was reached, the pattern did not match.
+
+ if (pat[pp] == EOS) {
+ first_char = ip
+ last_char = i-1
+ return (i)
+
+ } else if (bolflag || str[i] == EOS)
+ break
+ }
+
+ return (0) # no match
+end
diff --git a/sys/fmtio/strncmp.x b/sys/fmtio/strncmp.x
new file mode 100644
index 00000000..515aeaaf
--- /dev/null
+++ b/sys/fmtio/strncmp.x
@@ -0,0 +1,20 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# STRNCMP -- Compare the first N characters of two strings. A negative value
+# is returned if S1 < S2, 0 if S1 == S2, and a positive value if S1 > S2.
+
+int procedure strncmp (s1, s2, n)
+
+char s1[ARB], s2[ARB] # strings to be compared
+int n # number of chars to compare
+int i
+
+begin
+ do i = 1, n
+ if (s1[i] != s2[i])
+ return (s1[i] - s2[i])
+ else if (s1[i] == EOS)
+ return (0)
+
+ return (0)
+end
diff --git a/sys/fmtio/strne.x b/sys/fmtio/strne.x
new file mode 100644
index 00000000..ee95d3fe
--- /dev/null
+++ b/sys/fmtio/strne.x
@@ -0,0 +1,16 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# STRNE -- Compare two strings for inequality.
+
+bool procedure strne (s1, s2)
+
+char s1[ARB], s2[ARB]
+int ip
+
+begin
+ do ip = 1, ARB
+ if (s1[ip] == EOS)
+ return (s2[ip] != EOS)
+ else if (s1[ip] != s2[ip])
+ return (true)
+end
diff --git a/sys/fmtio/strsearch.x b/sys/fmtio/strsearch.x
new file mode 100644
index 00000000..e3006ed8
--- /dev/null
+++ b/sys/fmtio/strsearch.x
@@ -0,0 +1,55 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# STRSEARCH -- Search a string for a substring. This is the simplest and
+# fastest member of the pattern matching family. A significant increase in
+# efficiency will result if this procedure is used to search for substrings
+# that do not use any metacharacters.
+
+int procedure strsearch (str, patstr)
+
+char str[ARB] # string to be searched
+char patstr[ARB] # substring to search for
+
+int first_char, ch
+int ip, patlen
+bool strse1()
+
+begin
+ # The null pattern matches any string.
+ if (patstr[1] == EOS)
+ return (1)
+
+ first_char = patstr[1]
+
+ do ip = 1, ARB {
+ ch = str[ip]
+ if (ch == EOS)
+ break
+ if (ch == first_char)
+ if (strse1 (str[ip], patstr, patlen))
+ return (ip + patlen)
+ }
+
+ return (0)
+end
+
+
+# STRSE1 -- Internal routine which compares a substring of the first string
+# with the pattern string. STREQ cannot be used because it does not give a
+# match unless the two strings have the same length.
+
+bool procedure strse1 (str, patstr, patlen)
+
+char str[ARB]
+char patstr[ARB]
+int patlen
+int ip
+
+begin
+ do ip = 1, ARB
+ if (patstr[ip] == EOS || str[ip] != patstr[ip])
+ break
+
+ patlen = ip - 1
+ return (patstr[ip] == EOS)
+end
diff --git a/sys/fmtio/strsrt.x b/sys/fmtio/strsrt.x
new file mode 100644
index 00000000..67318f01
--- /dev/null
+++ b/sys/fmtio/strsrt.x
@@ -0,0 +1,73 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+define LOGPTR 20 # log2(maxpts) (1e6)
+
+# STRSRT -- Sort a list of strings, given an array of indices pointing into a
+# string buffer (e.g., sbuf=1 is Memc). The sort is performed by permutation
+# of the index array.
+
+procedure strsrt (x, sb, nstr)
+
+int x[ARB] # array of string pointers or indices
+char sb[ARB] # string buffer
+int nstr # number of strings
+
+int i, j, k, p, temp
+int lv[LOGPTR], uv[LOGPTR], pivot
+define swap {temp=$1;$1=$2;$2=temp}
+int strcmp()
+
+begin
+ lv[1] = 1
+ uv[1] = nstr
+ p = 1
+
+ while (p > 0) {
+ if (lv[p] >= uv[p]) # only one elem in this subset
+ p = p - 1 # pop stack
+ else {
+ # Dummy do-loop to trigger optimizer.
+ do p = p, ARB {
+ i = lv[p] - 1
+ j = uv[p]
+
+ # Select as the pivot the element at the middle of the
+ # subfile; move it to the end of the range so that the
+ # for loops below do not have to skip over it. Selecting
+ # a pivot near the center of the subfile helps prevent
+ # quadratic behavior when sorting an already sorted array.
+
+ k = (lv[p] + uv[p]) / 2
+ swap (x[j], x[k])
+ pivot = x[j]
+
+ while (i < j) {
+ for (i=i+1; strcmp (sb[x[i]], sb[pivot]) < 0; i=i+1)
+ ;
+ for (j=j-1; j > i; j=j-1)
+ if (strcmp (sb[x[j]], sb[pivot]) <= 0)
+ break
+ if (i < j) # out of order pair
+ swap (x[i], x[j]) # interchange elements
+ }
+
+ j = uv[p] # move pivot to position i
+ swap (x[i], x[j]) # interchange elements
+
+ if (i-lv[p] < uv[p] - i) { # stack so shorter done first
+ lv[p+1] = lv[p]
+ uv[p+1] = i - 1
+ lv[p] = i + 1
+ } else {
+ lv[p+1] = i + 1
+ uv[p+1] = uv[p]
+ uv[p] = i - 1
+ }
+
+ break
+ }
+
+ p = p + 1 # push onto stack
+ }
+ }
+end
diff --git a/sys/fmtio/strtbl.x b/sys/fmtio/strtbl.x
new file mode 100644
index 00000000..7ec0205d
--- /dev/null
+++ b/sys/fmtio/strtbl.x
@@ -0,0 +1,81 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# STRTBL -- Print a list of strings on the named file. If NCOL is zero,
+# the maximum number of columns is calculated based on the maximum
+# string length. If NCOL is nonzero, it is taken to be the maximum
+# number of columns (the actual number may be less, depending on the
+# maximum string length). FIRST_COL and LAST_COL define where on the
+# page the table will be placed.
+
+procedure strtbl (fd, buf, strp, nstr, first_col, last_col, maxch, ncol)
+
+int fd # output file
+char buf[ARB] # buffer containing the strings
+int strp[ARB] # array of string pointers
+int nstr # number of strings
+int first_col, last_col # where to place table on a line
+int maxch # maximum chars to print from a string
+int ncol # desired number of columns (0 to autoscale)
+
+pointer sp, obuf, op
+int row, i, j, p, nspaces, maxlen, colwidth, numcol, numrow, str
+int strlen()
+
+begin
+ call smark (sp)
+ call salloc (obuf, last_col + 1, TY_CHAR)
+
+ maxlen = 0
+ do i = 1, nstr
+ maxlen = max (maxlen, strlen(buf[strp[i]]))
+ if (maxch > 0)
+ maxlen = min (maxch, maxlen)
+ numcol = max (1, (last_col - first_col + 1) / (maxlen + 2))
+
+ if (ncol > 0)
+ numcol = min (numcol, ncol)
+ colwidth = (last_col - first_col + 1) / numcol
+ numrow = (nstr + numcol-1) / numcol
+
+ # For each row in the table:
+ do row = 1, numrow {
+ op = obuf
+
+ # Space to the first column.
+ do i = 2, first_col {
+ Memc[op] = ' '
+ op = op + 1
+ }
+
+ # For each string in the row:
+ do i = 1, numcol {
+ str = row + (i-1) * numrow
+ if (str > nstr)
+ next
+ p = strp[str]
+
+ # Output the string.
+ for (j=0; buf[p+j] != EOS && j < maxlen; j=j+1) {
+ Memc[op] = buf[p+j]
+ op = op + 1
+ }
+
+ # Advance to the next column.
+ if (i < numcol) {
+ nspaces = max (2, colwidth - j)
+ for (j=1; j <= nspaces; j=j+1) {
+ Memc[op] = ' '
+ op = op + 1
+ }
+ }
+ }
+
+ # Terminate this row of the table.
+ Memc[op] = '\n'
+ op = op + 1
+ Memc[op] = EOS
+ call putline (fd, Memc[obuf])
+ }
+
+ call sfree (sp)
+end
diff --git a/sys/fmtio/strupr.x b/sys/fmtio/strupr.x
new file mode 100644
index 00000000..6e80108c
--- /dev/null
+++ b/sys/fmtio/strupr.x
@@ -0,0 +1,18 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+
+# STRUPR -- Convert string to upper case.
+
+procedure strupr (str)
+
+char str[ARB]
+int ip
+
+begin
+ do ip = 1, ARB
+ if (str[ip] == EOS)
+ return
+ else if (IS_LOWER (str[ip]))
+ str[ip] = TO_UPPER (str[ip])
+end
diff --git a/sys/fmtio/tokdata.inc b/sys/fmtio/tokdata.inc
new file mode 100644
index 00000000..ceb7c090
--- /dev/null
+++ b/sys/fmtio/tokdata.inc
@@ -0,0 +1,32 @@
+# TOKDATA.INC -- Character classes for ctotok.x.
+# identifier=1, number=2, operator=3, punctuation=4, string=5, unknown=8
+
+# ! " # $ % & ' ( ) *
+data (class(i),i= 1,10) / 3, 5, 3, 1, 3, 3, 5, 4, 4, 3/
+
+# + , - . / 0 1 2 3 4
+data (class(i),i=11,20) / 3, 4, 3, 1, 3, 1, 1, 1, 1, 1/
+
+# 5 6 7 8 9 : ; < = >
+data (class(i),i=21,30) / 1, 1, 1, 1, 1, 4, 4, 3, 3, 3/
+
+# ? @ A B C D E F G H
+data (class(i),i=31,40) / 3, 3, 1, 1, 1, 1, 1, 1, 1, 1/
+
+# I J K L M N O P Q R
+data (class(i),i=41,50) / 1, 1, 1, 1, 1, 1, 1, 1, 1, 1/
+
+# S T U V W X Y Z [ \
+data (class(i),i=51,60) / 1, 1, 1, 1, 1, 1, 1, 1, 4, 4/
+
+# ] ^ _ ` a b c d e f
+data (class(i),i=61,70) / 4, 3, 1, 3, 1, 1, 1, 1, 1, 1/
+
+# g h i j k l m n o p
+data (class(i),i=71,80) / 1, 1, 1, 1, 1, 1, 1, 1, 1, 1/
+
+# q r s t u v w x y z
+data (class(i),i=81,90) / 1, 1, 1, 1, 1, 1, 1, 1, 1, 1/
+
+# { | } ~ DEL
+data (class(i),i=91,96) / 4, 3, 4, 3, 8, EOS/
diff --git a/sys/fmtio/xevgettok.x b/sys/fmtio/xevgettok.x
new file mode 100644
index 00000000..34e6c37e
--- /dev/null
+++ b/sys/fmtio/xevgettok.x
@@ -0,0 +1,208 @@
+include <lexnum.h>
+include <ctype.h>
+include <evexpr.h>
+
+
+
+# Parse definitions.
+define CONSTANT 257
+define IDENTIFIER 258
+define NEWLINE 259
+define YYEOS 260
+define PLUS 261
+define MINUS 262
+define STAR 263
+define SLASH 264
+define EXPON 265
+define CONCAT 266
+define QUEST 267
+define COLON 268
+define LT 269
+define GT 270
+define LE 271
+define EQ 272
+define NE 273
+define SE 274
+define AND 275
+define OR 276
+define NOT 277
+define AT 278
+define GE 279
+define UMINUS 280
+
+
+# XEV_GETTOK -- Lexical analyzer for EVEXPR. Returns the token code as the
+# function value. If the token is an operand (identifier or constant) the
+# operand value is returned in OUT.
+
+int procedure xev_gettok (ip, out)
+
+pointer ip # pointer into input string (expression)
+pointer out # pointer to yacc YYLVAL token value operand
+
+char ch
+long lval
+double dval
+pointer ip_start
+int nchars, token, junk
+int stridx(), lexnum(), gctod(), gctol()
+define ident_ 91
+
+begin
+ while (IS_WHITE(Memc[ip]))
+ ip = ip + 1
+
+ ch = Memc[ip]
+ switch (ch) {
+ case 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M',
+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z':
+
+ # Return an identifier.
+ident_
+ ip_start = ip
+ while (IS_ALNUM(ch) || stridx (ch, "_.$@#%&;[]\\^{}~") > 0) {
+ ip = ip + 1
+ ch = Memc[ip]
+ }
+
+ nchars = ip - ip_start
+ call xev_initop (out, nchars, TY_CHAR)
+ call strcpy (Memc[ip_start], O_VALC(out), nchars)
+
+ return (IDENTIFIER)
+
+ case 'I', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ # Return a numeric constant. The character I vectors here so
+ # that we can check for INDEF, a legal number.
+
+ token = lexnum (Memc, ip, nchars)
+ switch (token) {
+ case LEX_OCTAL:
+ junk = gctol (Memc, ip, lval, 8)
+ call xev_initop (out, 0, TY_INT)
+ O_VALI(out) = lval
+ case LEX_DECIMAL:
+ junk = gctol (Memc, ip, lval, 10)
+ call xev_initop (out, 0, TY_INT)
+ O_VALI(out) = lval
+ case LEX_HEX:
+ junk = gctol (Memc, ip, lval, 16)
+ call xev_initop (out, 0, TY_INT)
+ O_VALI(out) = lval
+ case LEX_REAL:
+ junk = gctod (Memc, ip, dval)
+ call xev_initop (out, 0, TY_REAL)
+ if (IS_INDEFD (dval))
+ O_VALR(out) = INDEFR
+ else
+ O_VALR(out) = dval
+ default:
+ goto ident_
+ }
+
+ return (CONSTANT)
+
+ case '\'', '"':
+ # Return a string constant.
+
+ ip_start = ip + 1
+ for (ip=ip+1; Memc[ip] != ch && Memc[ip] != EOS; ip=ip+1)
+ ;
+
+ nchars = ip - ip_start
+ if (Memc[ip] == EOS)
+ call xev_error ("missing closing quote in string constant")
+ else
+ ip = ip + 1
+
+ call xev_initop (out, nchars, TY_CHAR)
+ call strcpy (Memc[ip_start], O_VALC(out), nchars)
+
+ return (CONSTANT)
+
+ case '+':
+ token = PLUS
+ case '-':
+ token = MINUS
+ case '*':
+ if (Memc[ip+1] == '*') {
+ ip = ip + 1
+ token = EXPON
+ } else
+ token = STAR
+ case '/':
+ if (Memc[ip+1] == '/') {
+ ip = ip + 1
+ token = CONCAT
+ } else
+ token = SLASH
+
+ case '?':
+ if (Memc[ip+1] == '=') {
+ ip = ip + 1
+ token = SE
+ } else
+ token = QUEST
+
+ case ':':
+ token = COLON
+
+ case '@':
+ token = AT
+
+ case '<':
+ if (Memc[ip+1] == '=') {
+ ip = ip + 1
+ token = LE
+ } else
+ token = LT
+ case '>':
+ if (Memc[ip+1] == '=') {
+ ip = ip + 1
+ token = GE
+ } else
+ token = GT
+ case '!':
+ if (Memc[ip+1] == '=') {
+ ip = ip + 1
+ token = NE
+ } else
+ token = NOT
+ case '=':
+ if (Memc[ip+1] == '=') {
+ ip = ip + 1
+ token = EQ
+ } else
+ token = EQ
+ case '&':
+ if (Memc[ip+1] == '&') {
+ ip = ip + 1
+ token = AND
+ } else
+ token = AND
+ case '|':
+ if (Memc[ip+1] == '|') {
+ ip = ip + 1
+ token = OR
+ } else
+ token = OR
+
+ case '(', ')', ',':
+ token = ch
+
+ default:
+ if (ch == '\n')
+ token = NEWLINE
+ else if (ch == EOS)
+ token = YYEOS
+ else {
+ # Anything we don't understand is assumed to be an identifier.
+ goto ident_
+ }
+ }
+
+ ip = ip + 1
+ return (token)
+end
diff --git a/sys/fmtio/xtoc.x b/sys/fmtio/xtoc.x
new file mode 100644
index 00000000..8315b4cd
--- /dev/null
+++ b/sys/fmtio/xtoc.x
@@ -0,0 +1,39 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# XTOC -- Encode a complex number as a character string in a field width of
+# at most WIDTH characters.
+
+int procedure xtoc (xval, outstr, maxch, decpl, fmt, width)
+
+complex xval # value to be formatted
+char outstr[ARB] # output string
+int fmt # format encoding (f,e,etc.)
+int maxch # max chars out
+int decpl # precision
+int width # field width
+
+int op, dtoc()
+double real_part, imag_part
+define output {outstr[op]=$1;op=op+1;if(op>maxch)goto overflow_}
+define overflow_ 91
+
+begin
+ if (IS_INDEFX (xval)) {
+ real_part = INDEFD
+ imag_part = INDEFD
+ } else {
+ real_part = real (xval)
+ imag_part = aimag (xval)
+ }
+
+ op = 1
+ output ('(')
+ op = op + dtoc (real_part, outstr[op], maxch-op+1, decpl, fmt, width)
+ output (',')
+ op = op + dtoc (imag_part, outstr[op], maxch-op+1, decpl, fmt, width)
+ output (')')
+
+overflow_
+ outstr[op] = EOS
+ return (op-1)
+end
diff --git a/sys/fmtio/xvvgettok.x b/sys/fmtio/xvvgettok.x
new file mode 100644
index 00000000..f2a05977
--- /dev/null
+++ b/sys/fmtio/xvvgettok.x
@@ -0,0 +1,234 @@
+include <lexnum.h>
+include <ctype.h>
+include <mach.h>
+include <math.h>
+include <evvexpr.h>
+
+
+# Parser definitions.
+define CONSTANT 257
+define IDENTIFIER 258
+define NEWLINE 259
+define YYEOS 260
+define PLUS 261
+define MINUS 262
+define STAR 263
+define SLASH 264
+define EXPON 265
+define CONCAT 266
+define QUEST 267
+define COLON 268
+define LT 269
+define GT 270
+define LE 271
+define EQ 272
+define NE 273
+define SE 274
+define LAND 275
+define LOR 276
+define LNOT 277
+define BAND 278
+define BOR 279
+define BXOR 280
+define BNOT 281
+define AT 282
+define GE 283
+define UMINUS 284
+
+
+# XVV_GETTOK -- Lexical analyzer for EVVEXPR. Returns the token code as the
+# function value. If the token is an operand (identifier or constant) the
+# operand value is returned in OUT.
+
+int procedure xvv_gettok (ip, out)
+
+pointer ip #I pointer into input string (expression)
+pointer out #I pointer to yacc YYLVAL token value operand
+
+char ch
+long lval
+double dval
+pointer ip_start
+char numbuf[MAX_DIGITS]
+int nchars, token, junk, dtype
+int stridx(), stridxs(), lexnum(), gctod(), gctol()
+define ident_ 91
+
+begin
+ while (IS_WHITE(Memc[ip]))
+ ip = ip + 1
+
+ ch = Memc[ip]
+ switch (ch) {
+ case 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M',
+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z':
+
+ # Return an identifier.
+ident_
+ ip_start = ip
+ while (IS_ALNUM(ch) || stridx (ch, "_.$@#%&;[]\\^{}~") > 0) {
+ ip = ip + 1
+ ch = Memc[ip]
+ }
+
+ nchars = ip - ip_start
+ call xvv_initop (out, nchars, TY_CHAR)
+ call strcpy (Memc[ip_start], O_VALC(out), nchars)
+
+ return (IDENTIFIER)
+
+ case 'I', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ # Return a numeric constant. The character I vectors here so
+ # that we can check for INDEF, a legal number.
+
+ token = lexnum (Memc, ip, nchars)
+ switch (token) {
+ case LEX_OCTAL:
+ junk = gctol (Memc, ip, lval, 8)
+ call xvv_initop (out, 0, TY_INT)
+ O_VALI(out) = lval
+ case LEX_DECIMAL:
+ junk = gctol (Memc, ip, lval, 10)
+ call xvv_initop (out, 0, TY_INT)
+ O_VALI(out) = lval
+ case LEX_HEX:
+ junk = gctol (Memc, ip, lval, 16)
+ call xvv_initop (out, 0, TY_INT)
+ O_VALI(out) = lval
+
+ case LEX_REAL:
+ ip_start = ip
+ nchars = gctod (Memc, ip, dval)
+ call strcpy (Memc[ip], numbuf, min(nchars,MAX_DIGITS))
+
+ dtype = TY_REAL
+ if (stridxs ("dD", numbuf) > 0 || nchars > NDIGITS_RP+3)
+ dtype = TY_DOUBLE
+
+ call xvv_initop (out, 0, dtype)
+ if (dtype == TY_REAL) {
+ if (IS_INDEFD (dval))
+ O_VALR(out) = INDEFR
+ else
+ O_VALR(out) = dval
+ } else {
+ if (IS_INDEFD (dval))
+ O_VALD(out) = INDEFD
+ else
+ O_VALD(out) = dval
+ }
+ default:
+ goto ident_
+ }
+
+ return (CONSTANT)
+
+ case '\'', '"':
+ # Return a string constant.
+
+ ip_start = ip + 1
+ for (ip=ip+1; Memc[ip] != ch && Memc[ip] != EOS; ip=ip+1)
+ ;
+
+ nchars = ip - ip_start
+ if (Memc[ip] == EOS)
+ call xvv_error ("missing closing quote in string constant")
+ else
+ ip = ip + 1
+
+ call xvv_initop (out, nchars, TY_CHAR)
+ call strcpy (Memc[ip_start], O_VALC(out), nchars)
+
+ return (CONSTANT)
+
+ case '+':
+ token = PLUS
+ case '-':
+ token = MINUS
+ case '*':
+ if (Memc[ip+1] == '*') {
+ ip = ip + 1
+ token = EXPON
+ } else
+ token = STAR
+ case '/':
+ if (Memc[ip+1] == '/') {
+ ip = ip + 1
+ token = CONCAT
+ } else
+ token = SLASH
+
+ case '?':
+ if (Memc[ip+1] == '=') {
+ ip = ip + 1
+ token = SE
+ } else
+ token = QUEST
+
+ case ':':
+ token = COLON
+
+ case '@':
+ token = AT
+
+ case '<':
+ if (Memc[ip+1] == '=') {
+ ip = ip + 1
+ token = LE
+ } else
+ token = LT
+ case '>':
+ if (Memc[ip+1] == '=') {
+ ip = ip + 1
+ token = GE
+ } else
+ token = GT
+ case '!':
+ if (Memc[ip+1] == '=') {
+ ip = ip + 1
+ token = NE
+ } else
+ token = LNOT
+ case '=':
+ if (Memc[ip+1] == '=') {
+ ip = ip + 1
+ token = EQ
+ } else
+ token = EQ
+ case '&':
+ if (Memc[ip+1] == '&') {
+ ip = ip + 1
+ token = LAND
+ } else
+ token = BAND
+ case '|':
+ if (Memc[ip+1] == '|') {
+ ip = ip + 1
+ token = LOR
+ } else
+ token = BOR
+
+ case '^':
+ token = BXOR
+ case '~':
+ token = BNOT
+
+ case '(', ')', ',':
+ token = ch
+
+ default:
+ if (ch == '\n')
+ token = NEWLINE
+ else if (ch == EOS)
+ token = YYEOS
+ else {
+ # Anything we don't understand is assumed to be an identifier.
+ goto ident_
+ }
+ }
+
+ ip = ip + 1
+ return (token)
+end
diff --git a/sys/fmtio/zzdebug.x b/sys/fmtio/zzdebug.x
new file mode 100644
index 00000000..3729a1ff
--- /dev/null
+++ b/sys/fmtio/zzdebug.x
@@ -0,0 +1,319 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <evexpr.h>
+include <lexnum.h>
+
+task ev = t_ev,
+ lex = t_lex,
+ eq = t_eq,
+ ne = t_ne,
+ lt = t_lt,
+ le = t_le,
+ gt = t_gt,
+ ge = t_ge,
+ cmp = t_cmp,
+ ncmp = t_ncmp,
+ mat = t_mat,
+ srch = t_srch,
+ ctowrd = t_ctowrd
+
+
+# EV -- Text EVEXPR.
+
+procedure t_ev
+
+char expr[SZ_LINE]
+pointer o, evexpr()
+int clglstr()
+
+begin
+ while (clglstr ("expr", expr, SZ_LINE) != EOF) {
+ o = evexpr (expr, 0, 0)
+
+ switch (O_TYPE(o)) {
+ case TY_BOOL:
+ call printf ("%b = %s\n")
+ call pargb (O_VALB(o))
+ call pargstr (expr)
+
+ case TY_CHAR:
+ call printf ("%s = %s\n")
+ call pargstr (O_VALC(o))
+ call pargstr (expr)
+
+ case TY_INT:
+ call printf ("%d = %s\n")
+ call pargi (O_VALI(o))
+ call pargstr (expr)
+
+ case TY_REAL:
+ call printf ("%g = %s\n")
+ call pargr (O_VALR(o))
+ call pargstr (expr)
+
+ default:
+ call error (1, "expression datatype unknown")
+ }
+ }
+
+ call printf ("\n")
+end
+
+
+# LEX -- Test LEXNUM.
+
+procedure t_lex()
+
+int ip, nchars, toktype
+char token[SZ_FNAME]
+int lexnum(), strlen()
+
+begin
+ repeat {
+ call clgstr ("token", token, SZ_FNAME)
+ if (strlen (token) == 0)
+ break
+
+ ip = 1
+ toktype = lexnum (token, ip, nchars)
+
+ call printf ("tokchars=%d, type = %s\n")
+ call pargi (nchars)
+
+ switch (toktype) {
+ case LEX_OCTAL:
+ call pargstr ("octal")
+ case LEX_DECIMAL:
+ call pargstr ("decimal")
+ case LEX_HEX:
+ call pargstr ("hex")
+ case LEX_REAL:
+ call pargstr ("real")
+ case LEX_NONNUM:
+ call pargstr ("nonnumeric")
+ default:
+ call pargstr ("unknown")
+ }
+ }
+end
+
+
+# EQ -- Test string equals.
+
+procedure t_eq()
+
+char s1[SZ_FNAME], s2[SZ_FNAME]
+bool streq()
+
+begin
+ repeat {
+ call clgstr ("s1", s1, SZ_FNAME)
+ call clgstr ("s2", s2, SZ_FNAME)
+ call printf ("%s == %s: %b\n")
+ call pargstr (s1)
+ call pargstr (s2)
+ call pargb (streq (s1, s2))
+ call flush (STDOUT)
+ }
+end
+
+
+# NE -- Test string not equals.
+
+procedure t_ne()
+
+char s1[SZ_FNAME], s2[SZ_FNAME]
+bool strne()
+
+begin
+ repeat {
+ call clgstr ("s1", s1, SZ_FNAME)
+ call clgstr ("s2", s2, SZ_FNAME)
+ call printf ("%s != %s: %b\n")
+ call pargstr (s1)
+ call pargstr (s2)
+ call pargb (strne (s1, s2))
+ call flush (STDOUT)
+ }
+end
+
+
+# LT -- Test string less than.
+
+procedure t_lt()
+
+char s1[SZ_FNAME], s2[SZ_FNAME]
+bool strlt()
+
+begin
+ repeat {
+ call clgstr ("s1", s1, SZ_FNAME)
+ call clgstr ("s2", s2, SZ_FNAME)
+ call printf ("%s < %s: %b\n")
+ call pargstr (s1)
+ call pargstr (s2)
+ call pargb (strlt (s1, s2))
+ call flush (STDOUT)
+ }
+end
+
+
+# LE -- Test string less than or equals.
+
+procedure t_le()
+
+char s1[SZ_FNAME], s2[SZ_FNAME]
+bool strle()
+
+begin
+ repeat {
+ call clgstr ("s1", s1, SZ_FNAME)
+ call clgstr ("s2", s2, SZ_FNAME)
+ call printf ("%s <= %s: %b\n")
+ call pargstr (s1)
+ call pargstr (s2)
+ call pargb (strle (s1, s2))
+ call flush (STDOUT)
+ }
+end
+
+
+# GT -- Test string greater than.
+
+procedure t_gt()
+
+char s1[SZ_FNAME], s2[SZ_FNAME]
+bool strgt()
+
+begin
+ repeat {
+ call clgstr ("s1", s1, SZ_FNAME)
+ call clgstr ("s2", s2, SZ_FNAME)
+ call printf ("%s > %s: %b\n")
+ call pargstr (s1)
+ call pargstr (s2)
+ call pargb (strgt (s1, s2))
+ call flush (STDOUT)
+ }
+end
+
+
+# GE -- Test string greater than or equals.
+
+procedure t_ge()
+
+char s1[SZ_FNAME], s2[SZ_FNAME]
+bool strge()
+
+begin
+ repeat {
+ call clgstr ("s1", s1, SZ_FNAME)
+ call clgstr ("s2", s2, SZ_FNAME)
+ call printf ("%s >= %s: %b\n")
+ call pargstr (s1)
+ call pargstr (s2)
+ call pargb (strge (s1, s2))
+ call flush (STDOUT)
+ }
+end
+
+
+# CMP -- Test string compare.
+
+procedure t_cmp()
+
+char s1[SZ_FNAME], s2[SZ_FNAME]
+int strcmp()
+
+begin
+ repeat {
+ call clgstr ("s1", s1, SZ_FNAME)
+ call clgstr ("s2", s2, SZ_FNAME)
+ call printf ("compare %s, %s: %d\n")
+ call pargstr (s1)
+ call pargstr (s2)
+ call pargi (strcmp (s1, s2))
+ call flush (STDOUT)
+ }
+end
+
+
+# NCMP -- Test counted string compare.
+
+procedure t_ncmp()
+
+char s1[SZ_FNAME], s2[SZ_FNAME]
+int strncmp(), clgeti()
+
+begin
+ repeat {
+ call clgstr ("s1", s1, SZ_FNAME)
+ call clgstr ("s2", s2, SZ_FNAME)
+ call printf ("compare %s, %s: %d\n")
+ call pargstr (s1)
+ call pargstr (s2)
+ call pargi (strncmp (s1, s2, clgeti("nchars")))
+ call flush (STDOUT)
+ }
+end
+
+
+# MAT -- Test string match.
+
+procedure t_mat()
+
+char s1[SZ_FNAME], pat[SZ_FNAME]
+int strmatch()
+
+begin
+ repeat {
+ call clgstr ("s1", s1, SZ_FNAME)
+ call clgstr ("pat", pat, SZ_FNAME)
+ call printf ("match %s, pat=%s: %d\n")
+ call pargstr (s1)
+ call pargstr (pat)
+ call pargi (strmatch (s1, pat))
+ call flush (STDOUT)
+ }
+end
+
+
+# SRCH -- Test string search.
+
+procedure t_srch()
+
+char s1[SZ_FNAME], pat[SZ_FNAME]
+int strsearch()
+
+begin
+ repeat {
+ call clgstr ("s1", s1, SZ_FNAME)
+ call clgstr ("pat", pat, SZ_FNAME)
+ call printf ("search %s, pat=%s: %d\n")
+ call pargstr (s1)
+ call pargstr (pat)
+ call pargi (strsearch (s1, pat))
+ call flush (STDOUT)
+ }
+end
+
+
+# CTOWRD -- Test ctowrd.
+
+procedure t_ctowrd()
+
+char buf1[SZ_LINE], buf2[SZ_LINE]
+int n, ip, ctowrd(), getline()
+
+begin
+ while (getline (STDIN, buf1) != EOF) {
+ ip = 1
+ repeat {
+ buf2[1] = EOS
+ n = ctowrd (buf1, ip, buf2, SZ_LINE)
+ call printf ("n=%d, token=%s\n")
+ call pargi (n)
+ call pargstr (buf2)
+ } until (n <= 0)
+ }
+end