home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #27 / NN_1992_27.iso / spool / alt / sources / 2532 < prev    next >
Encoding:
Text File  |  1992-11-16  |  61.0 KB  |  2,869 lines

  1. Newsgroups: alt.sources
  2. Path: sparky!uunet!snorkelwacker.mit.edu!bloom-picayune.mit.edu!news
  3. From: scs@adam.mit.edu (Steve Summit)
  4. Subject: med -- expression evaluator and stream-based math processor
  5. Message-ID: <1992Nov16.160145.7707@athena.mit.edu>
  6. Keywords: expression parse evaluate
  7. Sender: news@athena.mit.edu (News system)
  8. Nntp-Posting-Host: adam.mit.edu
  9. Organization: none, at the moment
  10. Date: Mon, 16 Nov 1992 16:01:45 GMT
  11. Lines: 2856
  12.  
  13. This package is really in two parts.  The first part (and the one
  14. people usually ask for) is an expression parser and evaluator.
  15. Because it seemed like a good idea at the time, the parser
  16. produces an intermediate representation (a simple RPN-like
  17. program) which is later executed.
  18.  
  19. The expression evaluation code was originally written with
  20. plotting of user-defined functions in mind; it would still be
  21. well-suited for that job.
  22.  
  23. The second, and better documented, and more immediately useful
  24. part of the package is "med," a stream-based math processor built
  25. upon the aforementioned expression evaluator.  med isn't really
  26. an editor, but it acts a bit like sed in that it is often
  27. inserted in pipelines.
  28.  
  29. See the README file for more information.
  30.  
  31. The code in this package is copyrighted but may be freely
  32. redistributed.
  33.  
  34.                     Steve Summit
  35.                     scs@adam.mit.edu
  36.  
  37. # feed everything below this line through /bin/sh
  38. echo extracting README
  39. cat > README <<\%
  40. This package is really in two parts.  The first part (and the one
  41. people usually ask for) is an expression parser and evaluator.
  42. Because it seemed like a good idea at the time, the parser
  43. produces an intermediate representation (a simple RPN-like
  44. notation) which is later executed.  The obvious incentive for
  45. this strategy is allowing more efficient execution (if the
  46. expression is to be executed many times), although I confess I
  47. have never made any measurements to verify how much faster the
  48. two-stage process is (if at all), as opposed to a simpler
  49. evaluate-while-parsing.
  50.  
  51. The expression evaluation code was originally written with
  52. plotting of user-defined functions in mind; it would still be
  53. well-suited for that job.  The two main tasks involved in
  54. re-using this code in some other project would be (1) overcoming
  55. the lack of low-level documentation and (2) arranging that data
  56. values emanating from the rest of the project (i.e. the x
  57. coordinate, for a plotting package) can be accessed in
  58. expressions somehow.  An example of a clumsy but workable kludge
  59. for doing so can be found in the med code.
  60.  
  61. The second, and better documented, and more immediately useful
  62. part of the package is "med," a stream-based math processor.
  63. med isn't really an editor, but it acts a bit like sed in that it
  64. is often inserted in pipelines.  Besides being a good exerciser
  65. for the expression evaluator package, med is a useful program in
  66. its own right.  (It is remarkable how often the "obvious" test
  67. jig for a subroutine package turns into a useful program.)
  68.  
  69. It will be noted that med is an awful lot like awk, much less
  70. flexible in general, but optimized for performing arithmetic on
  71. columns of numbers.  Purists will claim that med is superfluous
  72. and unnecessary, because it can't do anything awk can't do; they
  73. are in fact correct.  (med was originally a throwaway, I brought
  74. it to its current, mostly "finished" state only because I was
  75. doing a lot of manipulation of data files on some MS-DOS machines
  76. lacking awk.)
  77.  
  78. A note on the code: it uses a sort of a mix of "K&R" C and ANSI
  79. C.  It contains no function prototypes or other new-style syntax,
  80. but it does make use of a few new header files and library
  81. subroutines.  I've included my implementations of these in the
  82. include and lib subdirectories, if you need them.
  83.  
  84. Steve Summit
  85. 10/21/92
  86. %
  87. chmod 664 README
  88. if test `wc -c < README` -ne 2337; then
  89.     echo "error extracting README" 1>&2
  90. fi
  91. echo extracting med.man
  92. cat > med.man <<\%
  93. MED(1)              UNIX Programmer's Manual               MED(1)
  94.  
  95. NAME
  96.      med - math editor
  97.  
  98. SYNOPSIS
  99.      med [ -bn ] [ -cc c ] [ -ef expressionfile ] [ -if inputfile
  100.      ] expressions [ files ]
  101.  
  102. DESCRIPTION
  103.      _M_e_d is a filter which reads a stream consisting of columns
  104.      of numbers, performing mathematical operations on them, and
  105.      writing out a stream of columns of (presumably different)
  106.      numbers.  One column of output is generated for each expres-
  107.      sion present on the command line.  The input is taken from
  108.      the named files, the file named by -if, or (if neither of
  109.      these appear) the standard input.
  110.  
  111.      The expression syntax is based on Fortran.  The +, -, *, /,
  112.      and ** (exponentiation) operators are supported (with the
  113.      customary associativity and precedence), as well as unary -,
  114.      and parentheses for grouping.  The following built-in func-
  115.      tions are supported:
  116.  
  117.                abs                     floor
  118.                acos                    ln    (natural log)
  119.                asin                    log10 (common log)
  120.                atan                    sin
  121.                atan2 (two arguments)   sinh
  122.                ceil                    sqrt
  123.                cos                     tan
  124.                cosh                    tanh
  125.  
  126.      Access to input data, as well as a few useful constants, is
  127.      through identifiers (``variables'').  The following identif-
  128.      iers may be present in expressions:
  129.  
  130.                pi    3.141592654
  131.                e     2.718281828
  132.                n     input line number
  133.                c1    data from input column 1 (similarly c2, c3, ...)
  134.  
  135.      An input column can be passed through to the output (essen-
  136.      tialy) unchanged with a trivial expression such as ``c2''.
  137.      (As a special case, the input data in a column which is used
  138.      only with such an expression is not required to be numeric,
  139.      and is passed through even if it is alphabetic.)  The input
  140.      may contain more columns than are called for by the expres-
  141.      sions; unused input columns are silently discarded.  Blank
  142.      lines, as well as those beginning with a comment character
  143.      (by default, `#', but settable with -cc) are passed through
  144.      to the output unchanged.
  145.  
  146.      Normally, _m_e_d reads one line, evaluates the requested
  147.      expressions, prints one line containing the results, and
  148.      continues on to the next line.  However, a few special-
  149.      purpose function-like operators modify this behavior.  If
  150.      any of the following ``functions:''
  151.  
  152.                max
  153.                mean
  154.                min
  155.                product
  156.                stdev
  157.                sum
  158.  
  159.      appears, a value is accumulated, which is only printed out
  160.      after all input lines have been read.  Line-by-line output
  161.      is suppressed.
  162.  
  163.      The operands of these ``summarizing'' operators can be arbi-
  164.      trarily complex expressions, and the results can be further
  165.      operated upon before printing.
  166.  
  167.      The -b (``bunch tallies'') option indicates that the summar-
  168.      izing operators should generate output after each group of
  169.      numbers in the input stream.  Groups of numbers are
  170.      separated by one or more blank lines.  After printing the
  171.      result from each group, all accumulated counts are zeroed
  172.      before processing the next group.
  173.  
  174.      The -cc (``comment character'') option indicates that the
  175.      next argument is to be taken as the (single) character
  176.      introducing comments in data and expression files.  By
  177.      default, the comment character is `#'.
  178.  
  179.      The -ef (``expression file'') option indicates that the next
  180.      argument is the name of a file out of which expressions are
  181.      to be read.  The file is assumed to contain one expression
  182.      per line.  (Expressions may also appear as arguments on the
  183.      command line; neither command line expressions nor expres-
  184.      sion file expressions preclude the other.)  Comments may
  185.      appear in the expression file on lines beginning with `#'
  186.      (or the comment character set with -cc).  Expressions within
  187.      expression files are immune from any unwanted interactions
  188.      or restrictions imposed by the shell, and may therefore con-
  189.      tain whitespace and `*' (which is presumably to be a multi-
  190.      plication operator, rather than a wildcard) with impunity.
  191.  
  192.      The -if (``input file'') option indicates that the next
  193.      argument is the name of a file from which input data will be
  194.      read.  (Input filenames may also appear as arguments on the
  195.      command line.  If neither -if nor standalone input file
  196.      arguments appear, input is read from the standard input.)
  197.  
  198.      The -n (``annotate output'') option indicates that, when
  199.      per-file summaries are being generated and multiple files
  200.      are being read, each output line should be preceded by the
  201.      originating file name.
  202.  
  203.      A brief summary of the invocation syntax and accepted
  204.      options may be requested with -help.
  205.  
  206. EXAMPLES
  207.      1.  From a stream of two columns, print four columns con-
  208.      sisting of the sum, difference, product, and quotient of the
  209.      input:
  210.  
  211.                med c1+c2 c1-c2 c1*c2 c1/c2
  212.  
  213.      With this invocation, the input
  214.  
  215.                1       2
  216.                3       4
  217.                5       6
  218.                7       5
  219.                3       1
  220.  
  221.      would produce
  222.  
  223.                3       -1      2       0.5
  224.                7       -1      12      0.75
  225.                11      -1      30      0.833333
  226.                12      2       35      1.4
  227.                4       2       3       3
  228.  
  229.      2.  Print the mean and standard deviation of a series of
  230.      numbers:
  231.  
  232.                med mean(c1) stdev(c1)
  233.  
  234.      The input
  235.  
  236.                1
  237.                2
  238.                3
  239.                4
  240.  
  241.      would produce the single line
  242.  
  243.                2.5     1.29099
  244.  
  245.      3.  Compute the mean and standard deviation of a column of
  246.      numbers the ``hard way'':
  247.  
  248.                med sum(c1)/n sqrt((sum(c1**2)-sum(c1)**2/n)/(n-1))
  249.  
  250.      This example will generate the same output as the previous
  251.      one.  (In fact, the built-in mean and stdev functions are
  252.      implemented internally with exactly these latter expres-
  253.      sions.)
  254.  
  255. BUGS
  256.      Not all possible floating-point errors are handled grace-
  257.      fully.
  258.  
  259.      The output is always printed with printf %g format (i.e.
  260.      with six significant digits); there is no control over out-
  261.      put precision.
  262.  
  263.      C and Fortran differ in the naming of logarithmic functions.
  264.      This program uses ln for natural log, and log10 for ``com-
  265.      mon'' or base-10 log.  (Plain log is also accepted, and
  266.      implements common log, following Fortran.  In C, log() is a
  267.      natural log.)
  268.  
  269.      There should be better control over the ``summary'' behavior
  270.      currently requested with the special functions min, sum,
  271.      mean, etc.  Versions of these functions which operate on
  272.      multiple arguments, within one line, and which do not there-
  273.      fore trigger summary behavior, should be provided.
  274.  
  275.      Since the input is read only once, an expression like
  276.  
  277.                c1/max(c1)
  278.  
  279.      which attempts to apply a summarizing function to each line
  280.      of input, will not work.
  281.  
  282.      The standard deviation function, since it is implemented in
  283.      terms of running totals of x and x**2 (rather than directly
  284.      from the definition), can be unstable.
  285.  
  286.      The input is limited to 20 columns.  There is also a res-
  287.      triction on the number of embedded constants in expressions
  288.      (7-10 distinct values).
  289.  
  290.      The command-line syntax is awkward.  Individual expressions
  291.      must be devoid of whitespace, and operators such as '*' can
  292.      cause difficulties with shell wildcarding unless the expres-
  293.      sion is quoted.  (Quoting also permits whitespace in expres-
  294.      sions.)
  295.  
  296.      The textual distinction between option flags (-b and -n),
  297.      expressions, and input filenames is subtle if not downright
  298.      ambiguous.  It is safer to use -if, or shell input redirec-
  299.      tion (using <), rather than presenting the input filename as
  300.      an individual argument.  (The single argument will defin-
  301.      itely _n_o_t work if the file has a numeric name, or otherwise
  302.      looks like an expression.)
  303.  
  304.      There are no conditionals; there are no user-definable func-
  305.      tions.
  306.  
  307.      It is not clear that this program is sufficiently more use-
  308.      ful than awk(1) to warrant its existence.
  309.  
  310. SEE ALSO
  311.      awk(1)
  312.  
  313. AUTHOR
  314.      Steve Summit
  315. %
  316. chmod 664 med.man
  317. if test `wc -c < med.man` -ne 8509; then
  318.     echo "error extracting med.man" 1>&2
  319. fi
  320. echo extracting Makefile
  321. cat > Makefile <<\%
  322. CFLAGS =
  323.  
  324. LINK = cc
  325. LINKFLAGS =
  326.  
  327. LIBC2 =
  328.  
  329. EOBJS = eval.o getexpr.o geterm.o getfact.o getpri.o \
  330.     getop.o codep.o misc.o alloc.o
  331.  
  332. MEDOBJS = med.o $(EOBJS) getline.o getargs.o error.o
  333.  
  334. LIBS = $(LIBC2) -lm
  335.  
  336. med: $(MEDOBJS)
  337.     $(LINK) $(LINKFLAGS) -o $@ $(MEDOBJS) $(LIBS)
  338.  
  339. ETEST = etest.o $(EOBJS) getline.o
  340.  
  341. etest: $(ETEST)
  342.     $(LINK) $(LINKFLAGS) -o $@ $(ETEST) $(LIBS)
  343.  
  344. alloc.o: alloc.c defs.h
  345.  
  346. etest.o: etest.c expr.h defs.h
  347.  
  348. eval.o: eval.c expr.h
  349.  
  350. geterm.o: geterm.c expr.h defs.h
  351.  
  352. getexpr.o: getexpr.c expr.h defs.h
  353.  
  354. getfact.o: getfact.c expr.h defs.h
  355.  
  356. getop.o: getop.c expr.h
  357.  
  358. getpri.o: getpri.c expr.h defs.h
  359.  
  360. med.o: med.c expr.h defs.h
  361.  
  362. misc.o: misc.c defs.h
  363. %
  364. chmod 664 Makefile
  365. if test `wc -c < Makefile` -ne 662; then
  366.     echo "error extracting Makefile" 1>&2
  367. fi
  368. echo extracting med.1
  369. cat > med.1 <<\%
  370. .\" Copyright 1991 by Steve Summit.
  371. .\" This program may be freely redistributed so long as the
  372. .\" author's name, and this notice, are retained.
  373. .TH MED 1
  374. .SH NAME
  375. med \- math editor
  376. .SH SYNOPSIS
  377. .B med
  378. [
  379. .B \-bn
  380. ] [
  381. .B \-cc
  382. c
  383. ] [
  384. .B \-ef
  385. expressionfile
  386. ] [
  387. .B \-if
  388. inputfile
  389. ]
  390. expressions
  391. [ files ]
  392. .br
  393. .SH DESCRIPTION
  394. .PP
  395. .I Med
  396. is a filter which
  397. reads a stream consisting of columns of numbers,
  398. performing mathematical operations on them,
  399. and writing out a stream of columns of
  400. (presumably different) numbers.
  401. One column of output is generated for each expression present on
  402. the command line.
  403. The input is taken from the named files,
  404. the file named by
  405. .BR \-if ,
  406. or (if neither of these appear)
  407. the standard input.
  408. .PP
  409. The expression syntax is based on Fortran.
  410. The +, \-, *, /, and ** (exponentiation) operators are supported
  411. (with the customary associativity and precedence),
  412. as well as unary \-, and parentheses for grouping.
  413. The following built-in functions are supported:
  414. .LP
  415. .nf
  416. .ta 1i +\w'floor'u+1m +\w'(two arguments)'u+3m +\w'floor'u+1m
  417.     abs        floor
  418.     acos        ln    (natural log)
  419.     asin        log10    (common log)
  420.     atan        sin
  421.     atan2    (two arguments)    sinh
  422.     ceil        sqrt
  423.     cos        tan
  424.     cosh        tanh
  425. .fi
  426. .PP
  427. Access to input data, as well as a few useful constants, is
  428. through identifiers (``variables'').
  429. The following identifiers may be present in expressions:
  430. .LP
  431. .nf
  432.     pi    3.141592654
  433.     e    2.718281828
  434.     n    input line number
  435.     c1    data from input column 1 (similarly c2, c3, ...)
  436. .fi
  437. .PP
  438. An input column can be passed through to the output
  439. (essentialy) unchanged
  440. with a trivial expression such as ``c2''.
  441. (As a special case,
  442. the input data in a column which is used only with such an expression
  443. is not required to be numeric,
  444. and is passed through even if it is alphabetic.)  The
  445. input may contain more columns
  446. than are called for by the expressions;
  447. unused input columns are silently discarded.
  448. Blank lines,
  449. as well as those beginning with a comment character
  450. (by default, `#', but settable with
  451. .BR \-cc )
  452. are passed through to the output unchanged.
  453. .PP
  454. Normally,
  455. .I med
  456. reads one line,
  457. evaluates the requested expressions,
  458. prints one line containing the results,
  459. and continues on to the next line.
  460. However, a few special-purpose function-like operators
  461. modify this behavior.
  462. If any of the following ``functions:''
  463. .LP
  464. .nf
  465.     max
  466.     mean
  467.     min
  468.     product
  469.     stdev
  470.     sum
  471. .fi
  472. .LP
  473. appears, a value is accumulated,
  474. which is only printed out after all input lines have been read.
  475. Line-by-line output is suppressed.
  476. .PP
  477. The operands of these ``summarizing'' operators can be
  478. arbitrarily complex expressions,
  479. and the results can be further operated upon before printing.
  480. .PP
  481. The
  482. .B \-b
  483. (``bunch tallies'') option indicates that the summarizing
  484. operators should generate output after each group of numbers in
  485. the input stream.
  486. Groups of numbers are separated by one or more blank lines.
  487. After printing the result from each group, all accumulated counts
  488. are zeroed before processing the next group.
  489. .PP
  490. The
  491. .B \-cc
  492. (``comment character'') option indicates that the next
  493. argument is to be taken as the (single) character introducing
  494. comments in data and expression files.
  495. By default, the comment character is `#'.
  496. .PP
  497. The
  498. .B \-ef
  499. (``expression file'') option indicates that the next
  500. argument is the name of a file out of which expressions are to be read.
  501. The file is assumed to contain one expression per line.
  502. (Expressions may also appear as arguments on the command line;
  503. neither command line expressions nor expression file expressions
  504. preclude the other.)  Comments
  505. may appear in the expression file on lines beginning
  506. with `#' (or the comment character set with
  507. .BR \-cc ).
  508. Expressions within expression files are immune from any unwanted
  509. interactions or restrictions imposed by the shell,
  510. and may therefore contain whitespace and `*'
  511. (which is presumably to be a multiplication operator,
  512. rather than a wildcard)
  513. with impunity.
  514. .PP
  515. The
  516. .B \-if
  517. (``input file'') option indicates that the next
  518. argument is the name of a file from which input data will be read.
  519. (Input filenames may also appear as arguments on the command line.
  520. If neither
  521. .B \-if
  522. nor standalone input file arguments appear,
  523. input is read from the standard input.)
  524. .PP
  525. The
  526. .B \-n
  527. (``annotate output'') option indicates that,
  528. when per-file summaries are being generated and multiple files
  529. are being read,
  530. each output line should be preceded by the originating file name.
  531. .PP
  532. A brief summary of the invocation syntax and accepted options may
  533. be requested with
  534. .BR \-help .
  535. .SH EXAMPLES
  536. .PP
  537. 1.
  538. From a stream of two columns,
  539. print four columns consisting of the sum, difference, product,
  540. and quotient of the input:
  541. .LP
  542. .ft C
  543.     med c1+c2 c1-c2 c1*c2 c1/c2
  544. .ft
  545. .LP
  546. With this invocation, the input
  547. .LP
  548. .nf
  549. .ft C
  550. .ta 1i +8u*\w' 'u +8u*\w' 'u +8u*\w' 'u +8u*\w' 'u
  551.     1    2
  552.     3    4
  553.     5    6
  554.     7    5
  555.     3    1
  556. .ft
  557. .LP
  558. would produce
  559. .LP
  560. .ft C
  561.     3    -1    2    0.5
  562.     7    -1    12    0.75
  563.     11    -1    30    0.833333
  564.     12    2    35    1.4
  565.     4    2    3    3
  566. .ft
  567. .fi
  568. .PP
  569. 2.
  570. Print the mean and standard deviation of a series of numbers:
  571. .LP
  572. .ft C
  573.     med mean(c1) stdev(c1)
  574. .ft
  575. .LP
  576. The input
  577. .LP
  578. .nf
  579. .ft C
  580.     1
  581.     2
  582.     3
  583.     4
  584. .ft
  585. .LP
  586. would produce the single line
  587. .LP
  588. .ft C
  589.     2.5    1.29099
  590. .ft
  591. .fi
  592. .PP
  593. 3.
  594. Compute the mean and standard deviation of a column of numbers
  595. the ``hard way'':
  596. .LP
  597. .ft C
  598.     med sum(c1)/n sqrt((sum(c1**2)-sum(c1)**2/n)/(n-1))
  599. .ft
  600. .LP
  601. This example will generate the same output as the previous one.
  602. (In fact, the built-in mean and stdev functions are implemented
  603. internally with exactly these latter expressions.)
  604. .SH BUGS
  605. .PP
  606. Not all possible floating-point errors are handled gracefully.
  607. .PP
  608. The output is always printed with printf %g format
  609. (i.e. with six significant digits);
  610. there is no control over output precision.
  611. .PP
  612. C and Fortran differ in the naming of logarithmic functions.
  613. This program uses ln for natural log,
  614. and log10 for ``common'' or base-10 log.
  615. (Plain log is also accepted,
  616. and implements common log,
  617. following Fortran.
  618. In C, log() is a natural log.)
  619. .PP
  620. There should be better control over the ``summary'' behavior
  621. currently requested with the special functions min, sum, mean, etc.
  622. Versions of these functions which operate on multiple arguments,
  623. within one line,
  624. and which do not therefore trigger summary behavior,
  625. should be provided.
  626. .PP
  627. Since the input is read only once,
  628. an expression like
  629. .LP
  630.     c1/max(c1)
  631. .LP
  632. which attempts to apply a summarizing function to each line of input,
  633. will not work.
  634. .PP
  635. The standard deviation function,
  636. since it is implemented in terms
  637. of running totals of x and x**2
  638. (rather than directly from the definition),
  639. can be unstable.
  640. .PP
  641. The input is limited to 20 columns.
  642. There is also a restriction on the number of embedded constants
  643. in expressions (7-10 distinct values).
  644. .PP
  645. The command-line syntax is awkward.
  646. Individual expressions must be devoid of whitespace,
  647. and operators such as '*' can cause difficulties with shell
  648. wildcarding unless the expression is quoted.
  649. (Quoting also permits whitespace in expressions.)
  650. .PP
  651. The textual distinction between option flags
  652. .RB ( \-b
  653. and
  654. .BR \-n ),
  655. expressions, and input filenames is subtle if not downright
  656. ambiguous.
  657. It is safer to use
  658. .BR \-if ,
  659. or shell input redirection (using <),
  660. rather than presenting the input filename as an individual
  661. argument.
  662. (The single argument will definitely
  663. .I not
  664. work if the file has a numeric name,
  665. or otherwise looks like an expression.)
  666. .PP
  667. There are no conditionals;
  668. there are no user-definable functions.
  669. .PP
  670. It is not clear that this program is sufficiently more useful
  671. than awk(1) to warrant its existence.
  672. .SH "SEE ALSO"
  673. awk(1)
  674. .SH AUTHOR
  675. .PP
  676. Steve Summit
  677. %
  678. chmod 644 med.1
  679. if test `wc -c < med.1` -ne 7565; then
  680.     echo "error extracting med.1" 1>&2
  681. fi
  682. echo extracting med.c
  683. cat > med.c <<\%
  684. /*
  685.  *  math editor
  686.  *
  687.  *  A lot like awk, but optimized for mathematical
  688.  *  transformations on columns of numbers.
  689.  *
  690.  *  Each command-line expression requests an output column, which
  691.  *  will be the result of applying the expression to each line
  692.  *  (in turn) of the input data.
  693.  *
  694.  *  Copyright 1987-1990 by Steve Summit.
  695.  *  This code may be freely redistributed so long as the author's
  696.  *  name, and this notice, are retained.
  697.  */
  698.  
  699. #include <stdio.h>
  700. #include <ctype.h>
  701. #include "expr.h"
  702. #include "defs.h"
  703. #include "alloc.h"
  704.  
  705. #define MAXLINE 512
  706. #define MAXCOLS 20    /* must be comfortably less than 26
  707.                     so var encoding doesn't overflow */
  708.  
  709. struct code **exprs = NULL;
  710. int nexprs = 0;
  711.  
  712. int tallies = FALSE;
  713. int bunchtallies = FALSE;
  714. int annotate = FALSE;
  715.  
  716. int keepblanks = TRUE;        /* pass blank lines through to output */
  717. int keepcomments = TRUE;    /* pass comment lines through to output */
  718. int keepgarbage = TRUE;        /* pass garbage lines through to output */
  719.                 /* as yet, no way to set */
  720.  
  721. int ignoregarbage = FALSE;    /* if true, treat non-numeric columns as 0 */
  722.  
  723. enum {ZERO, PREV, GARBAGE} missingaction = GARBAGE;
  724.                 /* what to do when requested column missing */
  725.  
  726. FILE *ofd = stdout;
  727.  
  728. double consts[10];
  729. int nconsts = 0;
  730. char *vars[26];
  731. double vals[26];
  732. int nvars = 0;
  733. int basenvars;
  734.  
  735. char *ofmt = "%g";
  736.  
  737. char *progname = "med";
  738.  
  739. int commentchar = '#';
  740.  
  741. char usage[] = "usage: %s [-b] [-n] [-?] expression(s) [-f] [file(s)]\n";
  742.  
  743. extern double eval();
  744.  
  745. extern double strtod();
  746.  
  747. static char line[MAXLINE];    /* buffer for reading data lines, */
  748.                 /* and also expression file (-ef) */
  749.                 /* (would be local to main() and med(), */
  750.                 /* but putting it here saves stack space) */
  751.  
  752. main(argc, argv)
  753. int argc;
  754. char *argv[];
  755. {
  756. int r;
  757. int argi;
  758. int i;
  759. int errs = 0;
  760.  
  761. consts[0] = 0.;
  762. consts[1] = 1.;
  763. consts[2] = 2.;
  764.  
  765. nconsts = 3;
  766.  
  767. vars[nvars] = "pi";
  768. vals[nvars++] = 3.141592654;
  769.  
  770. vars[nvars] = "e";
  771. vals[nvars++] = 2.718281828;
  772.  
  773. vars[nvars++] = "n";
  774. /* val filled in for each line */
  775.  
  776. basenvars = nvars;
  777.  
  778. /*
  779.  *  Standard option file parse is rife with ambiguity:
  780.  *  -b is a legal expression, except for the unknown variable.
  781.  */
  782.  
  783. for(argi = 1; argi < argc; argi++)
  784.     {
  785.     if(strcmp(argv[argi], "-b") == 0)
  786.         bunchtallies = TRUE;
  787.     else if(strcmp(argv[argi], "-cc") == 0)
  788.         commentchar = argv[++argi][0];
  789.     else if(strcmp(argv[argi], "-ef") == 0 || strcmp(argv[argi], "-f") == 0)
  790.         {
  791.         FILE *fd;
  792.  
  793.         if((fd = fopen(argv[++argi], "r")) == NULL)
  794.             {
  795.             fprintf(stderr, "%s: can't open %s: ",
  796.                             progname, argv[argi]);
  797.             perror("");
  798.             continue;
  799.             }
  800.  
  801.         while(getline(fd, line, MAXLINE) != EOF)
  802.             {
  803.             char *p = line;
  804.             while(Iswhite(*p))
  805.                 p++;
  806.             if(*p == '\0')
  807.                 continue;
  808.             if(*p == commentchar)
  809.                 continue;
  810.             addexpr(p);
  811.             }
  812.         }
  813.     else if(strcmp(argv[argi], "-n") == 0)
  814.         annotate = TRUE;
  815.     else if(strcmp(argv[argi], "-?") == 0 ||
  816.                     stricmp(argv[argi], "-help") == 0)
  817.         {
  818.         fprintf(stderr, usage, progname);
  819.         fprintf(stderr, "-b\tbunch tallies at blank lines\n");
  820.         fprintf(stderr, "-cc x\tset data comment character\n");
  821.         fprintf(stderr, "-n\tannotate tallies with input filename\n");
  822.         fprintf(stderr, "-ef\tnext argument file of expressions\n");
  823.         fprintf(stderr, "-if\tnext argument is input filename\n");
  824.         exit(0);    /* oughta continue */
  825.         }
  826.     else    break;
  827.     }
  828.  
  829. for(; argi < argc; argi++)
  830.     {
  831.     int prevnvars = nvars;
  832.  
  833.     if(strcmp(argv[argi], "-if") == 0)    /* means next arg is file */
  834.         {
  835.         argi++;
  836.         break;
  837.         }
  838.  
  839.     r = addexpr(argv[argi]);
  840.  
  841.     if(r != OK)
  842.         {
  843.         nvars = prevnvars;    /* kludge */
  844.         nexprs--;        /* ditto */
  845.  
  846.         /* maybe it's a file */
  847.         /* (handy getexpr() seems not to print any error messages) */
  848.         /* probably stuck at least one unknown variable in, though. */
  849.         /* and what about files named c1? */
  850.  
  851.         if(access(argv[argi], 0) == 0)
  852.             break;
  853.  
  854.         errs++;
  855.         }
  856.     }
  857.  
  858. if(nexprs == 0)
  859.     {
  860.     fprintf(stderr, usage, progname);
  861.     exit(1);
  862.     }
  863.  
  864. if(errs > 0)
  865.     exit(errs);
  866.  
  867. for(i = basenvars; i < nvars; i++)
  868.     {
  869.     int ii;
  870.  
  871.     if(sscanf(vars[i], "c%d", &ii) != 1)
  872.         {
  873.         fprintf(stderr, "%s: unknown variable %s\n",
  874.                         progname, vars[i]);
  875.         }
  876.     }
  877.  
  878. if(tallies)
  879.     keepblanks = keepcomments = keepgarbage = FALSE;
  880.  
  881. if(argi < argc)
  882.     {
  883.     for(; argi < argc; argi++)
  884.         {
  885.         FILE *fd = fopen(argv[argi], "r");
  886.         if(fd == NULL)
  887.             {
  888.             fprintf(stderr, "%s: can't open %s: ",
  889.                             progname, argv[argi]);
  890.             perror("");
  891.             continue;
  892.             }
  893.  
  894.         med(fd, argv[argi]);
  895.  
  896.         fclose(fd);
  897.  
  898.         cleartallies();
  899.         }
  900.     }
  901. else    med(stdin, "stdin");
  902.  
  903. exit(0);
  904. }
  905.  
  906. addexpr(string)
  907. char *string;
  908. {
  909. char *p;
  910. int r;
  911.  
  912. nexprs++;
  913. exprs = Srealloc(exprs, nexprs, struct code *);
  914. exprs[nexprs - 1] = codealloc(0);
  915.  
  916. r = getexpr(string, &p, exprs[nexprs - 1]);
  917.  
  918. if(*p != '\0')
  919.     r = SYNTAX;
  920.  
  921. if(r == OK)
  922.     {
  923.     emit('\0', exprs[nexprs - 1]);
  924.  
  925.     if(exprs[nexprs - 1]->c_nsubsid > 0)
  926.         tallies = TRUE;
  927.     }
  928.  
  929. return r;
  930. }
  931.  
  932. med(fd, filename)
  933. FILE *fd;
  934. char *filename;
  935. {
  936.                 /* primary line buffer is file static */
  937.                 /* (shared with main) */
  938.  
  939. static char line2[MAXLINE];    /* copy of line, to be broken up by getargs */
  940.                 /* (static to save stack space) */
  941. char *av[MAXCOLS];
  942. char *p;
  943. int r;
  944. int i, j;
  945. long int n;
  946.  
  947. n = 1;
  948.  
  949. while((r = getline(fd, line, MAXLINE)) != EOF)
  950.     {
  951.     if(r == 0)        /* blank line */
  952.         {
  953.         if(keepblanks)
  954.             fprintf(ofd, "%s\n", line);
  955.  
  956.         if(tallies && bunchtallies && n > 1)
  957.             {
  958.             dotallies(filename);
  959.             cleartallies();
  960.             n = 1;
  961.             }
  962.  
  963.         continue;
  964.         }
  965.  
  966.     if(*line == commentchar)
  967.         {
  968.         if(keepcomments)
  969.             fprintf(ofd, "%s\n", line);
  970.  
  971.         continue;
  972.         }
  973.  
  974.     (void)strcpy(line2, line);
  975.  
  976.     if((r = getargs(av, line2, MAXCOLS)) == 0)
  977.         {
  978.         if(keepblanks)
  979.             fprintf(ofd, "%s\n", line);
  980.  
  981.         continue;
  982.         }
  983.  
  984.     for(i = basenvars; i < nvars; i++)
  985.         {
  986.         int ii;
  987.  
  988.         if(sscanf(vars[i], "c%d", &ii) != 1)
  989.             {
  990. #ifdef notdef
  991.             fprintf(stderr, "%s: unknown variable %s\n",
  992.                             progname, vars[i]);
  993. #endif
  994.             continue;
  995.             }
  996.  
  997.         if(ii > r)
  998.             {
  999.             fprintf(stderr, "%s: only %d columns (asked %s)\n",
  1000.                         progname, r, vars[i]);
  1001.  
  1002.             if(missingaction == PREV)
  1003.                 {
  1004.                 /* value is retained from previous line */
  1005.                 continue;
  1006.                 }
  1007.             if(missingaction == ZERO)
  1008.                 {
  1009.                 vals[i] = 0;
  1010.                 av[ii-1] = "";    /* in case passed through */
  1011.                 }
  1012.             else    break;    /* treat as garbage */
  1013.  
  1014.             continue;
  1015.             }
  1016.  
  1017.         ii--;    /* av is 0-based */
  1018.  
  1019.         /*
  1020.          *  It's easy for strtod to get this case wrong, and
  1021.          *  Microsoft's does.  (Related to leading whitespace case.)
  1022.          *  (That is, strtod seems to succeed for inputs like
  1023.          *  " ", "-", and ".")
  1024.          */
  1025.  
  1026.         if(av[ii][0] == '-' && !isdigit(av[ii][1]) && av[ii][1] != '.')
  1027.             {
  1028.             if(ignoregarbage || noarith(i))
  1029.                 vals[i] = 0;
  1030.             else    {
  1031.                 fprintf(stderr,
  1032.                     "%s: column %d nonnumeric (\"%s\")\n",
  1033.                         progname, ii+1, av[ii]);
  1034.                 break;
  1035.                 }
  1036.             }
  1037.         else    {
  1038.             vals[i] = strtod(av[ii], &p);
  1039.  
  1040.             if(*p != '\0')        /* non-numeric */
  1041.                 {
  1042.                 if(ignoregarbage || noarith(i))
  1043.                     vals[i] = 0;
  1044.                 else    {
  1045.                     fprintf(stderr,
  1046.                      "%s: column %d nonnumeric (\"%s\")\n",
  1047.                         progname, ii+1, av[ii]);
  1048.                     break;
  1049.                     }
  1050.                 }
  1051.             }
  1052.         }
  1053.  
  1054.     if(i < nvars)    /* means some columns not found and/or nonnumeric */
  1055.         {
  1056.         if(keepgarbage)
  1057.             fprintf(ofd, "%s\n", line);
  1058.  
  1059.         continue;
  1060.         }
  1061.  
  1062.     /*
  1063.      *  Subtle ordering dependency here -- although n starts out 1,
  1064.      *  it is plugged in to vals[] before it is incremented, so it's
  1065.      *  correct during the postprocessing tallies.  However, it must
  1066.      *  be filled in after any of the loop short-circuits above.
  1067.      */
  1068.  
  1069.     vals[basenvars - 1] = n;
  1070.  
  1071.     if(tallies)
  1072.         {
  1073.         for(i = 0; i < nexprs; i++)
  1074.             {
  1075.             for(j = 0; j < exprs[i]->c_nsubsid; j++)
  1076.                 {
  1077.                 struct subsid *sp = &exprs[i]->c_subsid[j];
  1078.                 double val = eval(sp->s_code->c_base);
  1079.  
  1080.                 switch(sp->s_type)
  1081.                     {
  1082.                     case SUM:
  1083.                     case MEAN:
  1084.                         sp->s_tally += val;
  1085.                         break;
  1086.  
  1087.                     case PROD:
  1088.                         sp->s_tally *= val;
  1089.                         break;
  1090.  
  1091.                     case MAX:
  1092.                         if(n == 1 || val > sp->s_tally)
  1093.                             sp->s_tally = val;
  1094.                         break;
  1095.  
  1096.                     case MIN:
  1097.                         if(n == 1 || val < sp->s_tally)
  1098.                             sp->s_tally = val;
  1099.                         break;
  1100.  
  1101.                     default:
  1102.                         panic("bad subsid %d",
  1103.                                 sp->s_type);
  1104.                     }
  1105.                 }
  1106.             }
  1107.         }
  1108.     else    {
  1109.         if(annotate)
  1110.             fprintf(ofd, "%s\t", filename);
  1111.  
  1112.         for(i = 0; i < nexprs; i++)
  1113.             {
  1114.             int ii;
  1115.  
  1116.             if((ii = singlecol(exprs[i]->c_base)) >= 0)
  1117.                 fprintf(ofd, "%s", av[ii-1]);
  1118.             else    fprintf(ofd, ofmt, eval(exprs[i]->c_base));
  1119.  
  1120.             putc(i == nexprs - 1 ? '\n' : '\t', ofd);
  1121.             }
  1122.         }
  1123.     n++;
  1124.     }
  1125.  
  1126. if(tallies && (!bunchtallies || n > 1))
  1127.     dotallies(filename);
  1128. }
  1129.  
  1130. dotallies(filename)
  1131. char *filename;
  1132. {
  1133. int i, j;
  1134.  
  1135. if(annotate)
  1136.     fprintf(ofd, "%s\t", filename);
  1137.  
  1138. for(i = 0; i < nexprs; i++)
  1139.     {
  1140.     for(j = 0; j < exprs[i]->c_nsubsid; j++)
  1141.         vals[basenvars + j] = exprs[i]->c_subsid[j].s_tally;
  1142.  
  1143.     fprintf(ofd, ofmt, eval(exprs[i]->c_base));
  1144.     putc(i == nexprs - 1 ? '\n' : '\t', ofd);
  1145.     }
  1146. }
  1147.  
  1148. cleartallies()
  1149. {
  1150. int i, j;
  1151.  
  1152. for(i = 0; i < nexprs; i++)
  1153.     {
  1154.     for(j = 0; j < exprs[i]->c_nsubsid; j++)
  1155.         {
  1156.         struct subsid *sp = &exprs[i]->c_subsid[j];
  1157.  
  1158.         if(sp->s_type == PROD)
  1159.             sp->s_tally = 1.;
  1160.         else    sp->s_tally = 0.;
  1161.         }
  1162.     }
  1163. }
  1164.  
  1165. /*
  1166.  *  The following kludges have to do with detecting trivial expressions
  1167.  *  which effectively pass input columns through to the output.
  1168.  */
  1169.  
  1170. static checkcode();
  1171. static check2();
  1172.  
  1173. /*
  1174.  *  noarith(n) returns TRUE if variable n has no arithmetic
  1175.  *  performed on it, i.e. appears standalone in expressions like
  1176.  *  "c1" and can therefore be passed directly through to the
  1177.  *  output without calling atof and the like.
  1178.  */
  1179.  
  1180. noarith(n)
  1181. int n;
  1182. {
  1183. static int beenhere = FALSE;
  1184. static int usedarray[26];        /* initially 0 (FALSE) */
  1185.  
  1186. if(!beenhere)
  1187.     {
  1188.     register int i;
  1189.  
  1190.     for(i = 0; i < nexprs; i++)
  1191.         checkcode(exprs[i], usedarray, FALSE);
  1192.     }
  1193.  
  1194. return !usedarray[n];
  1195. }
  1196.  
  1197. static
  1198. checkcode(codep, usedarray, subsidflag)
  1199. struct code *codep;
  1200. int usedarray[];
  1201. int subsidflag;
  1202. {
  1203. if(codep->c_nsubsid == 0)
  1204.     check2(codep->c_base, usedarray, subsidflag);
  1205. else    {
  1206.     register int i;
  1207.     for(i = 0; i < codep->c_nsubsid; i++)
  1208.         checkcode(codep->c_subsid[i].s_code, usedarray, TRUE);
  1209.     }
  1210. }
  1211.  
  1212. static
  1213. check2(code, usedarray, subsidflag)
  1214. char *code;
  1215. int usedarray[];
  1216. int subsidflag;
  1217. {
  1218. char *p;
  1219. for(p = code; *p != '\0'; p++)
  1220.     {
  1221.     if(islower(*p))        /* var fetch */
  1222.         {
  1223.         if(*(p + 1) != '\0' || subsidflag)
  1224.             usedarray[*p - 'a'] = TRUE;
  1225.         }
  1226.     }
  1227. }
  1228.  
  1229. /*
  1230.  *  If code requests only that a single column variable be fetched,
  1231.  *  return that column number.  (This means that the input column
  1232.  *  can be passed through to the output without calling atof.)
  1233.  *  Otherwise, return -1.
  1234.  */
  1235.  
  1236. singlecol(code)
  1237. char *code;
  1238. {
  1239. if(islower(code[0]) && code[1] == '\0')
  1240.     {
  1241.     int i = code[0] - 'a';
  1242.     if(vars[i] != NULL && (vars[i][0] == 'c' || vars[i][0] == 'C'))
  1243.         return atoi(&vars[i][1]);
  1244.     }
  1245.  
  1246. return -1;
  1247. }
  1248. %
  1249. chmod 644 med.c
  1250. if test `wc -c < med.c` -ne 10553; then
  1251.     echo "error extracting med.c" 1>&2
  1252. fi
  1253. echo extracting alloc.c
  1254. cat > alloc.c <<\%
  1255. #include <stdlib.h>
  1256. #include "alloc.h"
  1257.  
  1258. char *
  1259. alloc(size)
  1260. int size;
  1261. {
  1262. char *ret;
  1263.  
  1264. ret = malloc((unsigned)size);
  1265.  
  1266. if(ret == NULL)
  1267.     {
  1268.     error("out of memory");
  1269.     exit(1);
  1270.     }
  1271.  
  1272. return(ret);
  1273. }
  1274.  
  1275. char *
  1276. crealloc(oldptr, newsize)
  1277. char *oldptr;
  1278. int newsize;
  1279. {
  1280. char *newptr;
  1281.  
  1282. newptr = realloc(oldptr, (unsigned)newsize);
  1283.  
  1284. if(newptr == NULL)
  1285.     {
  1286.     error("out of memory");
  1287.     exit(1);
  1288.     }
  1289.  
  1290. return(newptr);
  1291. }
  1292.  
  1293. %
  1294. chmod 644 alloc.c
  1295. if test `wc -c < alloc.c` -ne 391; then
  1296.     echo "error extracting alloc.c" 1>&2
  1297. fi
  1298. echo extracting alloc.h
  1299. cat > alloc.h <<\%
  1300. #ifndef ALLOC_H
  1301. #define ALLOC_H
  1302.  
  1303. #define Salloc(type) (type *)alloc(sizeof(type))
  1304. #define Srealloc(oldptr, n, type) \
  1305.             (type *)crealloc((char *)oldptr, n * sizeof(type))
  1306.  
  1307. extern char *alloc();
  1308. extern char *crealloc();
  1309.  
  1310. #ifndef NULL
  1311. #define NULL 0
  1312. #endif
  1313.  
  1314. #endif
  1315. %
  1316. chmod 644 alloc.h
  1317. if test `wc -c < alloc.h` -ne 264; then
  1318.     echo "error extracting alloc.h" 1>&2
  1319. fi
  1320. echo extracting codep.c
  1321. cat > codep.c <<\%
  1322. #include "expr.h"
  1323. #include "alloc.h"
  1324. #include "defs.h"
  1325.  
  1326. struct code *
  1327. codealloc(size)
  1328. int size;
  1329. {
  1330. register struct code *cp;
  1331.  
  1332. if(size == 0)
  1333.     size = 512;
  1334.  
  1335. cp = Salloc(struct code);
  1336.  
  1337. cp->c_bufsize = size;
  1338. cp->c_base = cp->c_ptr = alloc(cp->c_bufsize);
  1339.  
  1340. cp->c_nsubsid = 0;
  1341. cp->c_subsid = NULL;
  1342.  
  1343. return cp;
  1344. }
  1345.  
  1346.  
  1347. %
  1348. chmod 644 codep.c
  1349. if test `wc -c < codep.c` -ne 306; then
  1350.     echo "error extracting codep.c" 1>&2
  1351. fi
  1352. echo extracting defs.h
  1353. cat > defs.h <<\%
  1354. #ifndef DEFS_H
  1355. #define DEFS_H
  1356.  
  1357. #define TRUE 1
  1358. #define FALSE 0
  1359.  
  1360. #ifndef NULL
  1361. #define NULL 0
  1362. #endif
  1363.  
  1364. extern char *alloc();
  1365.  
  1366. #endif
  1367. %
  1368. chmod 644 defs.h
  1369. if test `wc -c < defs.h` -ne 129; then
  1370.     echo "error extracting defs.h" 1>&2
  1371. fi
  1372. echo extracting error.c
  1373. cat > error.c <<\%
  1374. #include <stdio.h>
  1375. #include <stdarg.h>
  1376.  
  1377. extern char *progname;
  1378.  
  1379. error(fmt)
  1380. char *fmt;
  1381. {
  1382. va_list arg_ptr;
  1383.  
  1384. va_start(arg_ptr, fmt);
  1385.  
  1386. fprintf(stderr, "%s: ", progname);
  1387.  
  1388. vfprintf(stderr, fmt, arg_ptr);
  1389.  
  1390. putc('\n', stderr);
  1391.  
  1392. va_end(arg_ptr);
  1393. }
  1394.  
  1395. panic(fmt)
  1396. char *fmt;
  1397. {
  1398. va_list arg_ptr;
  1399.  
  1400. va_start(arg_ptr, fmt);
  1401.  
  1402. fprintf(stderr, "%s: panic: ", progname);
  1403.  
  1404. vfprintf(stderr, fmt, arg_ptr);
  1405.  
  1406. putc('\n', stderr);
  1407.  
  1408. va_end(arg_ptr);
  1409.  
  1410. exit(1);
  1411. }
  1412. %
  1413. chmod 644 error.c
  1414. if test `wc -c < error.c` -ne 434; then
  1415.     echo "error extracting error.c" 1>&2
  1416. fi
  1417. echo extracting eval.c
  1418. cat > eval.c <<\%
  1419. /*
  1420.  *  expression evaluator
  1421.  *
  1422.  *  Implements a simple stack-based machine which "runs" the
  1423.  *  "programs" "compiled" by the expression parser.
  1424.  *
  1425.  *  Copyright 1987-1990 by Steve Summit.
  1426.  *  This code may be freely redistributed so long as the author's
  1427.  *  name, and this notice, are retained.
  1428.  */
  1429.  
  1430. #include <stdio.h>
  1431. #include <math.h>
  1432. #include <ctype.h>
  1433. #include "expr.h"
  1434.  
  1435. #define MAXSTACK 15
  1436.  
  1437. extern struct biftab bitab[];
  1438. extern int nbitab;
  1439.  
  1440. double
  1441. eval(code)
  1442. register char *code;
  1443. {
  1444. double stack[MAXSTACK];
  1445. register double *stackp;
  1446. #define Push(v)        (*++stackp = (v))
  1447. #define Pop()        (*stackp--)
  1448. #define Peek()        (*stackp)
  1449. #define Cheatpush(v)    (*stackp = (v))
  1450. double l, r;
  1451.  
  1452. stackp = &stack[-1];
  1453.  
  1454. while(*code != '\0')
  1455.     {
  1456.     switch(*code)
  1457.         {
  1458.         case '0':
  1459.             Push(0.);
  1460.             break;
  1461.  
  1462.         case '1':
  1463.             Push(1.);
  1464.             break;
  1465.  
  1466.         case '2':
  1467.             Push(2.);
  1468.             break;
  1469.  
  1470.         case PLUS:
  1471.             r = Pop();
  1472.             l = Peek();
  1473.             Cheatpush(l + r);
  1474.             break;
  1475.  
  1476.         case MINUS:
  1477.             r = Pop();
  1478.             l = Peek();
  1479.             Cheatpush(l - r);
  1480.             break;
  1481.  
  1482.         case UMINUS:
  1483.             Cheatpush(-Peek());
  1484.             break;
  1485.  
  1486.         case TIMES:
  1487.             r = Pop();
  1488.             l = Peek();
  1489.             Cheatpush(l * r);
  1490.             break;
  1491.  
  1492.         case DIVIDE:
  1493.             r = Pop();
  1494.             l = Peek();
  1495.             if(r != 0.)
  1496.                 Cheatpush(l / r);
  1497.             else    {
  1498.                 error("divide by 0");
  1499.                 Cheatpush(0.);
  1500.                 }
  1501.             break;
  1502.  
  1503.         case EXPONENT:
  1504.             r = Pop();
  1505.             l = Peek();
  1506.             Cheatpush(pow(l, r));
  1507.             break;
  1508.  
  1509.         default:
  1510.             if(isdigit(*code))
  1511.                 Push(consts[*code - '0']);
  1512.             else if(islower(*code))
  1513.                 Push(vals[*code - 'a']);
  1514.             else if(isupper(*code))
  1515.                 {
  1516.                 register struct biftab *bipt;
  1517.  
  1518.                 /* gross: linear search lookup */
  1519.  
  1520.                 for(bipt = bitab; bipt < &bitab[nbitab]; bipt++)
  1521.                     {
  1522.                     if(*code == bipt->bi_token)
  1523.                         break;
  1524.                     }
  1525.  
  1526.                 if(bipt >= &bitab[nbitab])
  1527.                     {
  1528.                     error("bad function %c", *code);
  1529.                                 /* panic? */
  1530.                     break;
  1531.                     }
  1532.  
  1533.                 if(bipt->bi_nargs == 1)
  1534.                     {
  1535.                     Cheatpush((*bipt->bi_function)(Peek()));
  1536.                     }
  1537.                 else    {
  1538.                     double arg1 = Pop();
  1539.                     Cheatpush((*bipt->bi_function)(arg1,
  1540.                                 Peek()));
  1541.                     }
  1542.                 }
  1543.             else    error("bad code %c", *code);    /* panic? */
  1544.         }
  1545.     code++;
  1546.     }
  1547.  
  1548. return(Peek());
  1549. }
  1550.  
  1551. double
  1552. xsqrt(x)
  1553. double x;
  1554. {
  1555. if(x < 0.)
  1556.     {
  1557.     error("sqrt of negative number");
  1558.     return 0.;
  1559.     }
  1560.  
  1561. return sqrt(x);
  1562. }
  1563. %
  1564. chmod 644 eval.c
  1565. if test `wc -c < eval.c` -ne 2209; then
  1566.     echo "error extracting eval.c" 1>&2
  1567. fi
  1568. echo extracting expr.h
  1569. cat > expr.h <<\%
  1570. #ifndef EXPR_H
  1571. #define EXPR_H
  1572.  
  1573. #define PLUS        '+'
  1574. #define MINUS        '-'
  1575. #define TIMES        '*'
  1576. #define DIVIDE        '/'
  1577. #define OPENPAREN    '('
  1578. #define CLOSEPAREN    ')'
  1579. #define UMINUS        '_'
  1580. #define EXPONENT    '^'
  1581. #define OPENBRACK    '['
  1582. #define CLOSEBRACK    ']'
  1583. #define COMMA        ','
  1584.  
  1585. #define ABS        'A'
  1586. #define ACOS        'B'
  1587. #define ASIN        'D'
  1588. #define ATAN        'E'
  1589. #define ATAN2        'M'
  1590. #define CEIL        'G'
  1591. #define COS        'C'
  1592. #define COSH        'H'
  1593. #define FLOOR        'F'
  1594. #define LN        'N'
  1595. #define LOG10        'L'
  1596. #define SIN        'S'
  1597. #define SINH        'J'
  1598. #define SQRT        'Q'
  1599. #define TAN        'T'
  1600. #define TANH        'K'
  1601.  
  1602. struct biftab            /* "built in function" table */
  1603.     {
  1604.     char *bi_syntax;
  1605.     int bi_token;
  1606.     int bi_nargs;
  1607.     int bi_flags;
  1608.     double (*bi_function)();
  1609.     };
  1610.  
  1611. /* bi_flags values: */
  1612.  
  1613. #define SPEC    1
  1614.  
  1615. /* special function codes: */
  1616.  
  1617. #define SUM    1
  1618. #define MEAN    2
  1619. #define STDEV    3
  1620. #define PROD    4
  1621. #define MAX    6
  1622. #define MIN    5
  1623.  
  1624. /* expression evaluation return codes: */
  1625.  
  1626. #define OK        1
  1627. #define ERR        0
  1628. #define SYNTAX        -1
  1629.  
  1630. #define Iswhite(c) ((c) == ' ' || (c) == '\t')
  1631.  
  1632. struct code
  1633.     {
  1634.     char *c_base;
  1635.     int c_bufsize;
  1636.     char *c_ptr;
  1637.     int c_nsubsid;
  1638.     struct subsid *c_subsid;
  1639.     };
  1640.  
  1641. struct subsid
  1642.     {
  1643.     struct code *s_code;
  1644.     int s_type;
  1645.     double s_tally;
  1646.     };
  1647.  
  1648. struct code *codealloc();
  1649.  
  1650. #define emit(c, codep) (*codep->c_ptr++ = (c))
  1651.  
  1652. union retval
  1653.     {
  1654.     char *string;
  1655.     double number;
  1656.     };
  1657.  
  1658. extern double consts[];
  1659. extern int nconsts;
  1660. extern char *vars[];
  1661. extern int nvars;
  1662. extern double vals[];
  1663.  
  1664. #endif
  1665. %
  1666. chmod 644 expr.h
  1667. if test `wc -c < expr.h` -ne 1410; then
  1668.     echo "error extracting expr.h" 1>&2
  1669. fi
  1670. echo extracting getargs.c
  1671. cat > getargs.c <<\%
  1672. /*
  1673.  *  takes a string (line) and builds an array of pointers to each word in it.
  1674.  *  words are separated by spaces or any control characters.  At most maxargs
  1675.  *  pointers are calculated.  \0's are inserted in line, so that each word
  1676.  *  becomes a string in its own right.  The number of pointers (argc) is
  1677.  *  returned.
  1678.  */
  1679.  
  1680. #include <stdio.h>
  1681.  
  1682. #define iswhite(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
  1683.  
  1684. getargs(argv, line, maxargs)
  1685. register char *argv[];
  1686. register char *line;
  1687. int maxargs;
  1688. {
  1689. register int nargs;
  1690.  
  1691. nargs = 0;
  1692.  
  1693. while(1)
  1694.         {
  1695.         while(iswhite(*line))
  1696.                 line++;
  1697.  
  1698.         if(*line == '\0')
  1699.                 {
  1700.                 if(nargs < maxargs) *argv = NULL;
  1701.                 return(nargs);
  1702.                 }
  1703.  
  1704.         *argv++ = line;
  1705.         nargs++;
  1706.  
  1707.         while(!iswhite(*line) && *line != '\0')
  1708.                 line++;
  1709.  
  1710.         if(*line == '\0')
  1711.                 {
  1712.                 if(nargs < maxargs) *argv = NULL;
  1713.                 return(nargs);
  1714.                 }
  1715.         *line++ = '\0';
  1716.         if(nargs == maxargs) return(nargs);
  1717.         }
  1718. }
  1719. %
  1720. chmod 644 getargs.c
  1721. if test `wc -c < getargs.c` -ne 1086; then
  1722.     echo "error extracting getargs.c" 1>&2
  1723. fi
  1724. echo extracting geterm.c
  1725. cat > geterm.c <<\%
  1726. /*
  1727.  *  expression parser
  1728.  *
  1729.  *  Second stage: get "terms" involving multiplicative operators.
  1730.  *  "Compiles" parsed expressions to simple stack-based
  1731.  *  calculator language.
  1732.  *
  1733.  *  Copyright 1987 by Steve Summit.
  1734.  *  This code may be freely redistributed so long as the author's
  1735.  *  name, and this notice, are retained.
  1736.  */
  1737.  
  1738. #include <stdio.h>
  1739. #include "expr.h"
  1740. #include "defs.h"
  1741.  
  1742. geterm(buf, leftover, codep)
  1743. char *buf;
  1744. char **leftover;
  1745. struct code *codep;
  1746. {
  1747. char *p, *p1, *p2;
  1748. int op;
  1749. int r;
  1750.  
  1751. r = getfact(buf, &p, codep);
  1752.  
  1753. if(r != OK) return(r);
  1754.  
  1755. while(TRUE)
  1756.         {
  1757.         r = getop(p, &p1, &op);
  1758.         if(r != OK || (op != TIMES && op != DIVIDE))
  1759.                 {
  1760.                 *leftover = p;
  1761.                 return(OK);
  1762.                 }
  1763.  
  1764.         r = getfact(p1, &p2, codep);
  1765.         if(r != OK) return(r);
  1766.         emit(op, codep);
  1767.  
  1768.         p = p2;
  1769.         }
  1770. }
  1771. %
  1772. chmod 644 geterm.c
  1773. if test `wc -c < geterm.c` -ne 875; then
  1774.     echo "error extracting geterm.c" 1>&2
  1775. fi
  1776. echo extracting getexpr.c
  1777. cat > getexpr.c <<\%
  1778. /*
  1779.  *  expression parser
  1780.  *
  1781.  *  First stage (lowest precedence): get expressions involving
  1782.  *  additive operators (separating higher-precedence "terms").
  1783.  *  "Compiles" parsed expressions to simple stack-based
  1784.  *  calculator language.
  1785.  *
  1786.  *  Copyright 1987 by Steve Summit.
  1787.  *  This code may be freely redistributed so long as the author's
  1788.  *  name, and this notice, are retained.
  1789.  */
  1790.  
  1791. #include <stdio.h>
  1792. #include "expr.h"
  1793. #include "defs.h"
  1794.  
  1795. getexpr(buf, leftover, codep)
  1796. char *buf;
  1797. char **leftover;
  1798. struct code *codep;
  1799. {
  1800. char *p, *p1, *p2;
  1801. int op;
  1802. int r;
  1803.  
  1804. r = geterm(buf, &p, codep);
  1805.  
  1806. if(r != OK) return(r);
  1807.  
  1808. while(TRUE)
  1809.         {
  1810.         r = getop(p, &p1, &op);
  1811.  
  1812.         if(r != OK || (op != PLUS && op != MINUS))
  1813.                 {
  1814.                 *leftover = p;
  1815.                 return(OK);
  1816.                 }
  1817.  
  1818.         r = geterm(p1, &p2, codep);
  1819.  
  1820.         if(r != OK)
  1821.                 return(r);
  1822.  
  1823.         emit(op, codep);
  1824.  
  1825.         p = p2;
  1826.         }
  1827. }
  1828. %
  1829. chmod 644 getexpr.c
  1830. if test `wc -c < getexpr.c` -ne 951; then
  1831.     echo "error extracting getexpr.c" 1>&2
  1832. fi
  1833. echo extracting getfact.c
  1834. cat > getfact.c <<\%
  1835. /*
  1836.  *  expression parser
  1837.  *
  1838.  *  Third stage: get "factors" involving exponentiation operator
  1839.  *  and/or unary minus.
  1840.  *  "Compiles" parsed expressions to simple stack-based
  1841.  *  calculator language.
  1842.  *
  1843.  *  Copyright 1987 by Steve Summit.
  1844.  *  This code may be freely redistributed so long as the author's
  1845.  *  name, and this notice, are retained.
  1846.  */
  1847.  
  1848. #include <stdio.h>
  1849. #include "expr.h"
  1850. #include "defs.h"
  1851.  
  1852. getfact(buf, leftover, codep)
  1853. char *buf;
  1854. char **leftover;
  1855. struct code *codep;
  1856. {
  1857. char *p, *p1, *p2, *p3;
  1858. int op;
  1859. int r;
  1860.  
  1861. r = getop(buf, &p, &op);
  1862.  
  1863. if(r != OK || op != MINUS)
  1864.         {
  1865.         r = getpri(buf, &p, codep);
  1866.         if(r != OK) return(r);
  1867.         p1 = p;
  1868.         }
  1869. else    {
  1870.         r = getfact(p, &p1, codep);
  1871.  
  1872.         if(r != OK)
  1873.                 return(r);
  1874.  
  1875.         emit(UMINUS, codep);
  1876.         }
  1877.  
  1878. r = getop(p1, &p2, &op);
  1879.  
  1880. if(r == OK && op == EXPONENT)
  1881.         {
  1882.         r = getfact(p2, &p3, codep);
  1883.                 /* calling getfact again arranges right-associativity */
  1884.  
  1885.         emit(EXPONENT, codep);
  1886.         p1 = p3;
  1887.         }
  1888.  
  1889. *leftover = p1;
  1890.  
  1891. return(OK);
  1892. }
  1893. %
  1894. chmod 644 getfact.c
  1895. if test `wc -c < getfact.c` -ne 1083; then
  1896.     echo "error extracting getfact.c" 1>&2
  1897. fi
  1898. echo extracting getline.c
  1899. cat > getline.c <<\%
  1900. /*
  1901.  * reads from fi until newline or EOF.  Puts at most max characters (but never
  1902.  * the newline) into string and appends \0.  Returns number of characters in
  1903.  * string, exclusive of \0.  (i.e. returns 0 for blank line terminated by
  1904.  * newline.)  Reads properly a string terminated with an EOF, but returns EOF
  1905.  * on a blank line with no newline, terminated with an EOF.
  1906.  */
  1907.  
  1908. #include <stdio.h>
  1909.  
  1910. getline(fi, string, max)
  1911. FILE *fi;
  1912. char string[];
  1913. register int max;
  1914. {
  1915. register int i;
  1916. register int c;
  1917. i = 0;
  1918. while((c = getc(fi)) != EOF && c != '\n') if(i < max) string[i++] = c;
  1919. string[i] = '\0';
  1920. return(c == EOF && i == 0 ? EOF : i);
  1921. }
  1922. %
  1923. chmod 644 getline.c
  1924. if test `wc -c < getline.c` -ne 634; then
  1925.     echo "error extracting getline.c" 1>&2
  1926. fi
  1927. echo extracting getop.c
  1928. cat > getop.c <<\%
  1929. /*
  1930.  *  expression parser
  1931.  *
  1932.  *  Lexical analysis: return tokens.
  1933.  *
  1934.  *  Copyright 1987 by Steve Summit.
  1935.  *  This code may be freely redistributed so long as the author's
  1936.  *  name, and this notice, are retained.
  1937.  */
  1938.  
  1939. #include <stdio.h>
  1940. #include "expr.h"
  1941.  
  1942. struct optab
  1943.     {
  1944.     char *ot_syntax;
  1945.     int ot_token;
  1946.     } optab[] =
  1947.         {
  1948.         "(",    OPENPAREN,
  1949.         ")",    CLOSEPAREN,
  1950.         "**",    EXPONENT,
  1951.         "*",    TIMES,
  1952.         "+",    PLUS,
  1953.         "-",    MINUS,
  1954.         "/",    DIVIDE,
  1955.         "[",    OPENBRACK,
  1956.         "]",    CLOSEBRACK,
  1957.         ",",    COMMA,
  1958.         };
  1959.  
  1960. getop(buf, leftover, retval)
  1961. char *buf;
  1962. char **leftover;
  1963. int *retval;
  1964. {
  1965. char *p, *p1, *p2;
  1966. int i;
  1967.  
  1968. p = buf;
  1969.  
  1970. while(Iswhite(*p))
  1971.     p++;
  1972.  
  1973. for(i = 0; i < sizeof(optab)/sizeof(struct optab); i++)
  1974.     {
  1975.     for(p1 = p, p2 = optab[i].ot_syntax; *p2 != '\0'; p1++, p2++)
  1976.         if(*p1 != *p2) break;
  1977.  
  1978.     if(*p2 == '\0')
  1979.         {
  1980.         *retval = optab[i].ot_token;
  1981.         *leftover = p1;
  1982.         return(OK);
  1983.         }
  1984.     }
  1985.  
  1986. /*fprintf(stderr, "getop: missing operator near \"%.10s\"\n", buf);*/
  1987. *leftover = buf;
  1988. return(SYNTAX);
  1989. }
  1990. %
  1991. chmod 644 getop.c
  1992. if test `wc -c < getop.c` -ne 967; then
  1993.     echo "error extracting getop.c" 1>&2
  1994. fi
  1995. echo extracting getpri.c
  1996. cat > getpri.c <<\%
  1997. /*
  1998.  *  expression parser
  1999.  *
  2000.  *  Last stage (highest precedence): parse primaries
  2001.  *  (identifiers, functions, constants, and parentheses)
  2002.  *  "Compiles" parsed expressions to simple stack-based
  2003.  *  calculator language
  2004.  *
  2005.  *  Copyright 1987-1990 by Steve Summit.
  2006.  *  This code may be freely redistributed so long as the author's
  2007.  *  name, and this notice, are retained.
  2008.  */
  2009.  
  2010. #include <stdio.h>
  2011. #include <ctype.h>
  2012. #include <math.h>
  2013. #include "expr.h"
  2014. #include "defs.h"
  2015. #include "alloc.h"
  2016.  
  2017. extern double xsqrt();    /* checks its argument */
  2018.  
  2019. struct biftab bitab[] =
  2020.     {
  2021.     "abs",        ABS,    1,    0,    fabs,
  2022.     "acos",        ACOS,    1,    0,    acos,
  2023.     "asin",        ASIN,    1,    0,    asin,
  2024.     "atan",        ATAN,    1,    0,    atan,
  2025.     "atan2",    ATAN2,    2,    0,    atan2,
  2026.     "average",    MEAN,    1,    SPEC,    NULL,
  2027.     "ceil",        CEIL,    1,    0,    ceil,
  2028.     "cos",        COS,    1,    0,    cos,
  2029.     "cosh",        COSH,    1,    0,    cosh,
  2030.     "floor",    FLOOR,    1,    0,    floor,
  2031.     "ln",        LN,    1,    0,    log,
  2032.     "log",        LOG10,    1,    0,    log10,    /* which is */
  2033.     "log10",    LOG10,    1,    0,    log10,    /* preferable? */
  2034.     "max",        MAX,    1,    SPEC,    NULL,    /* of args or column? */
  2035.     "mean",        MEAN,    1,    SPEC,    NULL,
  2036.     "min",        MIN,    1,    SPEC,    NULL,    /* ditto */
  2037.     "product",    PROD,    1,    SPEC,    NULL,
  2038.     "sin",        SIN,    1,    0,    sin,
  2039.     "sinh",        SINH,    1,    0,    sinh,
  2040.     "sqrt",        SQRT,    1,    0,    xsqrt,
  2041.     "stdev",    STDEV,    1,    SPEC,    NULL,
  2042.     "sum",        SUM,    1,    SPEC,    NULL,
  2043.     "tan",        TAN,    1,    0,    tan,
  2044.     "tanh",        TANH,    1,    0,    tanh,
  2045.     };
  2046.  
  2047. int nbitab = sizeof(bitab) / sizeof(bitab[0]);
  2048.  
  2049. extern int basenvars;
  2050.  
  2051. extern double atof();
  2052. extern char *strncpy();
  2053.  
  2054. getpri(buf, leftover, codep)
  2055. char *buf;
  2056. char **leftover;
  2057. struct code *codep;
  2058. {
  2059. char *p, *p1, *p2, *p3, *p4;
  2060. char c;
  2061. int r;
  2062. int len;
  2063. double val;
  2064. int op;
  2065. int i;
  2066.  
  2067. p = buf;
  2068.  
  2069. while(Iswhite(*p))
  2070.     p++;
  2071.  
  2072. if(isalpha(*p))
  2073.     {
  2074.     p1 = p;
  2075.     p1++;
  2076.     len = 1;
  2077.     while(isalnum(*p1))
  2078.         {
  2079.         p1++;
  2080.         len++;
  2081.         }
  2082.  
  2083.     for(i = 0; i < nbitab; i++)
  2084.         if(sstrneq(bitab[i].bi_syntax, p, len))
  2085.             break;
  2086.  
  2087.     if(i < nbitab)
  2088.         {
  2089.         int ii;
  2090.  
  2091.         r = getop(p1, &p2, &op);
  2092.         if(r != OK || op != OPENPAREN)
  2093.             {
  2094.             error("missing '('");
  2095.             return(ERR);
  2096.             }
  2097.  
  2098.         for(ii = 0; ii < bitab[i].bi_nargs; ii++)
  2099.             {
  2100.             if((bitab[i].bi_flags & SPEC) &&
  2101.                         bitab[i].bi_token == STDEV)
  2102.                 {
  2103.                 /*
  2104.                  *  Starts two subsidiary tallies, to sum
  2105.                  *  the given quantity and the square of
  2106.                  *  the given quantity, and plugs it into
  2107.                  *  the formula 
  2108.                  *
  2109.                  *    sqrt((sum(x**2)-sum(x)**2/n)/(n-1))
  2110.                  *
  2111.                  *  (The implementation is rather
  2112.                  *  distressingly special-cased and
  2113.                  *  hardwired.  It would be nice to use
  2114.                  *  a more general, and user-accessible,
  2115.                  *  function notation one day.)
  2116.                  */
  2117.  
  2118.                 struct subsid *sp1, *sp2;
  2119.                 int si;
  2120.  
  2121.                 si = codep->c_nsubsid;
  2122.  
  2123.                 codep->c_nsubsid += 2;
  2124.                 codep->c_subsid = Srealloc(codep->c_subsid,
  2125.                     codep->c_nsubsid, struct subsid);
  2126.  
  2127.                 sp1 = &codep->c_subsid[si];
  2128.                 sp1->s_code = codealloc(0);
  2129.                 sp1->s_type = SUM;
  2130.                 sp1->s_tally = 0.;
  2131.  
  2132.                 sp2 = sp1 + 1;
  2133.                 sp2->s_code = codealloc(0);
  2134.                 sp2->s_type = SUM;
  2135.                 sp2->s_tally = 0.;
  2136.  
  2137.                 r = getexpr(p2, &p3, sp1->s_code);
  2138.                 emit('\0', sp1->s_code);
  2139.  
  2140.                 /* Yuck.  Re-do, but square */
  2141.  
  2142.                 r = getexpr(p2, &p3, sp2->s_code);
  2143.                 emit('2', sp2->s_code);
  2144.                 emit('^', sp2->s_code);
  2145.                 emit('\0', sp2->s_code);
  2146.  
  2147.                 /* compute sqrt((sum(x**2)-sum(x)**2/n)/(n-1)) */
  2148.                 /* this is quite gross: */
  2149.                 emit('a' + basenvars + si + 1, codep); /* Σ(x**2) */
  2150.                 emit('a' + basenvars + si, codep);    /* Σx */
  2151.                 emit('2', codep);
  2152.                 emit('^', codep);
  2153.                 emitvar("n", 1, codep);
  2154.                 emit('/', codep);
  2155.                 emit('-', codep);
  2156.                 emitvar("n", 1, codep);
  2157.                 emit('1', codep);
  2158.                 emit('-', codep);
  2159.                 emit('/', codep);
  2160.                 emit(SQRT, codep);
  2161.                 }
  2162.             else if(bitab[i].bi_flags & SPEC)
  2163.                 {
  2164.                 struct subsid *sp;
  2165.                 int si;
  2166.  
  2167.                 si = codep->c_nsubsid;
  2168.  
  2169.                 codep->c_nsubsid++;
  2170.                 codep->c_subsid = Srealloc(codep->c_subsid,
  2171.                     codep->c_nsubsid, struct subsid);
  2172.                 sp = &codep->c_subsid[si];
  2173.                 sp->s_code = codealloc(0);
  2174.                 sp->s_type = bitab[i].bi_token;
  2175.                 if(sp->s_type == PROD)
  2176.                     sp->s_tally = 1.;
  2177.                 else    sp->s_tally = 0.;
  2178.  
  2179.                 r = getexpr(p2, &p3, sp->s_code);
  2180.  
  2181.                 /*
  2182.                  * to sum square of something, do:
  2183.                  *
  2184.                  *    emit('2', sp->s_code);
  2185.                  *    emit('^', sp->s_code);
  2186.                  */
  2187.  
  2188.                 emit('\0', sp->s_code);
  2189.  
  2190.                 /* this is pretty gross: */
  2191.                 emit('a' + basenvars + si, codep);
  2192.  
  2193.                 if(bitab[i].bi_token == MEAN)
  2194.                     {
  2195.                     emitvar("n", 1, codep);
  2196.                     emit('/', codep);
  2197.                     }
  2198.                 }
  2199.             else    r = getexpr(p2, &p3, codep);
  2200.  
  2201.             if(r != OK)
  2202.                 return(r);
  2203.             r = getop(p3, &p4, &op);
  2204.             if(r != OK)
  2205.                 return(r);
  2206.             if(ii < bitab[i].bi_nargs - 1)
  2207.                 {
  2208.                 if(op != COMMA)
  2209.                     {
  2210.                     error("missing ','");
  2211.                     return(ERR);
  2212.                     }
  2213.                 }
  2214.             else    {
  2215.                 if(op != CLOSEPAREN)
  2216.                     {
  2217.                     error("missing ')'");
  2218.                     return(ERR);
  2219.                     }
  2220.                 }
  2221.  
  2222.             p2 = p4;
  2223.             }
  2224.  
  2225.         if(!(bitab[i].bi_flags & SPEC))
  2226.             emit(bitab[i].bi_token, codep);
  2227.  
  2228.         p1 = p4;
  2229.         }
  2230.     else    {
  2231.         r = emitvar(p, len, codep);
  2232.         if(r != OK)
  2233.             return r;
  2234.         }
  2235.     }
  2236. else if(isdigit(*p) || *p == '.')
  2237.     {
  2238.     p1 = p;
  2239.     while(isdigit(*p1))
  2240.         p1++;
  2241.     if(*p1 == '.')
  2242.         {
  2243.         p1++;
  2244.         while(isdigit(*p1))
  2245.             p1++;
  2246.         }
  2247.     if(*p1 == 'e' || *p1 == 'E')
  2248.         {
  2249.         p1++;
  2250.         if(*p1 == '+' || *p1 == '-')
  2251.             p1++;
  2252.         while(isdigit(*p1))
  2253.             p1++;
  2254.         }
  2255.     c = *p1;
  2256.     *p1 = '\0';
  2257.  
  2258.     val = atof(p);
  2259.  
  2260.     *p1 = c;
  2261.  
  2262.     for(i = 0; i < nconsts; i++)
  2263.         if(val == consts[i])
  2264.             break;
  2265.  
  2266.     if(i == nconsts)
  2267.         {
  2268.         if(nconsts == 10)
  2269.             {
  2270.             error("Too many constants");
  2271.             return(ERR);
  2272.             }
  2273.  
  2274.         consts[nconsts++] = val;
  2275.         }
  2276.  
  2277.     emit(i + '0', codep);
  2278.     }
  2279. else    {
  2280.     r = getop(p, &p1, &op);
  2281.  
  2282.     if(r != OK || op != OPENPAREN)
  2283.         {
  2284.         error("missing primary expression");
  2285.         return(SYNTAX);
  2286.         }
  2287.  
  2288.     r = getexpr(p1, &p2, codep);
  2289.  
  2290.     if(r != OK) return(r);
  2291.  
  2292.     r = getop(p2, &p3, &op);
  2293.  
  2294.     if(r != OK || op != CLOSEPAREN)
  2295.         {
  2296.         error("missing ')'");
  2297.         return(SYNTAX);
  2298.         }
  2299.  
  2300.     p1 = p3;
  2301.     }
  2302.  
  2303. r = getop(p1, &p2, &op);
  2304. if(r != OK)
  2305.     {
  2306.     *leftover = p1;
  2307.     return(OK);
  2308.     }
  2309.  
  2310. switch(op)
  2311.     {
  2312.     case OPENBRACK:
  2313.         error("can't handle arrays yet");
  2314.         return(SYNTAX);
  2315.         break;
  2316.  
  2317.     case OPENPAREN:
  2318.         error("can't handle functions yet");
  2319. /*        r = getarglist(p2, &p3, &args);*/
  2320.         if(r != OK)
  2321.             return(r);
  2322.         r = getop(p3, &p4, &op);
  2323.         if(r != OK && op != CLOSEPAREN)
  2324.             {
  2325.             error("missing )");
  2326.             return(SYNTAX);
  2327.             }
  2328.  
  2329.         /* emit fcn call */
  2330.  
  2331.         *leftover = p4;
  2332.         return(OK);
  2333.  
  2334.     default:
  2335.         *leftover = p1;
  2336.         return(OK);
  2337.     }
  2338.  
  2339. return(OK);
  2340. }
  2341.  
  2342. emitvar(name, len, codep)
  2343. char *name;
  2344. int len;
  2345. struct code *codep;
  2346. {
  2347. int i;
  2348.  
  2349. for(i = 0; i < nvars; i++)
  2350.     if(sstrneq(vars[i], name, len))
  2351.         break;
  2352.  
  2353. if(i == nvars)
  2354.     {
  2355.     if(nvars == 26)
  2356.         {
  2357.         error("Too many variables");
  2358.         return(ERR);
  2359.         }
  2360.  
  2361.     vars[nvars] = strncpy(alloc(len + 1), name, len);
  2362.     vars[nvars++][len] = '\0';
  2363.     }
  2364.  
  2365. emit(i + 'a', codep);        /* assumes ASCII */
  2366.  
  2367. return OK;
  2368. }
  2369. %
  2370. chmod 644 getpri.c
  2371. if test `wc -c < getpri.c` -ne 6596; then
  2372.     echo "error extracting getpri.c" 1>&2
  2373. fi
  2374. echo extracting makefile.dos
  2375. cat > makefile.dos <<\%
  2376. CFLAGS = /Zi -nologo
  2377.  
  2378. EOBJS = eval.obj getexpr.obj geterm.obj getfact.obj getpri.obj \
  2379.     getop.obj codep.obj misc.obj alloc.obj
  2380.  
  2381. LINK = link
  2382. LINKFLAGS = /CO /BATCH /NOE
  2383.  
  2384. MSCLIB = c:\msc\lib
  2385.  
  2386. SETARGV = $(MSCLIB)\setargv.obj
  2387.  
  2388. MEDOBJS =  med.obj $(EOBJS) getline.obj getargs.obj error.obj
  2389.  
  2390. med.exe: $(MEDOBJS) med.opt
  2391.         $(LINK) $(LINKFLAGS) @med.opt $(SETARGV),$@;
  2392.  
  2393. med.opt: Makefile 
  2394.     $(ECHO) $%XARGS($(MEDOBJS),,,,, " +", "56") > $@
  2395.  
  2396. ETEST = etest.obj $(EOBJS) getline.obj
  2397.  
  2398. etest.exe: $(ETEST) etest.opt
  2399.         $(LINK) $(LINKFLAGS) @etest.opt,$@;
  2400.  
  2401. alloc.obj: alloc.c defs.h
  2402.  
  2403. etest.obj: etest.c expr.h defs.h
  2404.  
  2405. eval.obj: eval.c expr.h
  2406.  
  2407. geterm.obj: geterm.c expr.h defs.h
  2408.  
  2409. getexpr.obj: getexpr.c expr.h defs.h
  2410.  
  2411. getfact.obj: getfact.c expr.h defs.h
  2412.  
  2413. getop.obj: getop.c expr.h
  2414.  
  2415. getpri.obj: getpri.c expr.h defs.h
  2416.  
  2417. med.obj: med.c expr.h defs.h
  2418.  
  2419. misc.obj: misc.c defs.h
  2420. %
  2421. chmod 644 makefile.dos
  2422. if test `wc -c < makefile.dos` -ne 868; then
  2423.     echo "error extracting makefile.dos" 1>&2
  2424. fi
  2425. echo extracting misc.c
  2426. cat > misc.c <<\%
  2427. #include "defs.h"
  2428.  
  2429. sstrneq(s1, s2, len)
  2430. char *s1, *s2;
  2431. int len;
  2432. {
  2433. while(*s1 != '\0')
  2434.     {
  2435.     if(len <= 0)
  2436.         return(FALSE);
  2437.     if(*s1++ != *s2++)
  2438.         return(FALSE);
  2439.     len--;
  2440.     }
  2441.  
  2442. if(len != 0)
  2443.     return(FALSE);
  2444.  
  2445. return(TRUE);
  2446. }
  2447. %
  2448. chmod 644 misc.c
  2449. if test `wc -c < misc.c` -ne 214; then
  2450.     echo "error extracting misc.c" 1>&2
  2451. fi
  2452. if test ! -d include
  2453. then    echo "making directory include"
  2454.     mkdir include
  2455. fi
  2456. echo extracting include/stdarg.h
  2457. cat > include/stdarg.h <<\%
  2458. #ifndef __STDARG_H
  2459. #define __STDARG_H
  2460.  
  2461. /*
  2462.  *  stdarg implementation for "conventional" architectures with
  2463.  *  downward-growing stacks.  The stack is assumed to be aligned
  2464.  *  to word, i.e. int, sized boundaries, with smaller arguments
  2465.  *  widened when passed and larger arguments equal to (or rounded
  2466.  *  up to) a multiple of the word size.
  2467.  *
  2468.  *  The stack word size can be changed by adjusting the
  2469.  *  __stacktype typedef.
  2470.  *
  2471.  *  The va_arg macro looks inefficient, with its embedded
  2472.  *  conditional, and lint may complain about "constant in
  2473.  *  conditional context," but most optimizers I have tried it
  2474.  *  with successfully remove the "dead" case (the controlling
  2475.  *  expression is a compile-time constant), and the remaining
  2476.  *  postincrement int pointer expression (the more common case)
  2477.  *  is considerably nicer in space and time (although the va_list
  2478.  *  pointer may have to be in a register for it to make any
  2479.  *  difference).
  2480.  *
  2481.  *  Unfortunately, some compilers can't always handle the admittedly
  2482.  *  obfuscated va_arg code.  (Ritchie's original pdp11 compiler,
  2483.  *  for instance, complains if the second argument indicates a
  2484.  *  structure type, since structs are evidently not allowed as
  2485.  *  arguments to ?:, even though optimization could remove the
  2486.  *  dead case and preclude the impossible use of r0 for an
  2487.  *  intermediate result.)
  2488.  *
  2489.  *  When the complicated ?: implementation can't be used, it can
  2490.  *  be replaced with the nonoptimized case, which appears inside
  2491.  *  #ifdef pdp11 below.  The simpler version then generally yields
  2492.  *  smaller code when the argp type is also changed to a char pointer.
  2493.  *
  2494.  *  Another possibility is to move the indirection, so that ?: is
  2495.  *  operating on pointers, instead of objects, as in
  2496.  *
  2497.  *    #define va_arg(vaptr, type) (*(type *)(__argsize(type) == 1 ? \
  2498.  *        ((vaptr)++) : \
  2499.  *            (((vaptr) += __argsize(type)) - __argsize(type))))
  2500.  *
  2501.  *  although this seems to make it harder for the code generator
  2502.  *  to recognize the possibility of using a postincrement
  2503.  *  addressing mode, which was the whole point of the exercise.
  2504.  *  (pcc on the vax figures it out in either case.  For the '11,
  2505.  *  you can get postincrements by using the gory code below, as
  2506.  *  long as you never access structs, in which case you need
  2507.  *  either this code, which doesn't then do postincrement, and
  2508.  *  ends up larger, or else the pedestrian alternative below using
  2509.  *  char *argp and no ?:.)
  2510.  *
  2511.  *  Note that this code (and indeed, any simple macro-based
  2512.  *  approach) cannot possibly work on architectures which pass
  2513.  *  arguments in registers.
  2514.  *
  2515.  *  Another problem is that it doesn't (and can't, without help
  2516.  *  from the compiler) know that float arguments are widened to double
  2517.  *  when passed.  Don't, therefore, call va_arg(..., float).
  2518.  *
  2519.  *  Steve Summit  4/15/89
  2520.  *
  2521.  *  This code is Copyright 1989 by Steve Summit.
  2522.  *  It may, however, be redistributed and used without restriction.
  2523.  */
  2524.  
  2525. typedef int __stacktype;
  2526. #ifdef pdp11
  2527. typedef char __argptype;
  2528. #else
  2529. typedef int __argptype;
  2530. #endif
  2531. typedef __argptype *va_list;
  2532.  
  2533. #define __argsize(thing) ((sizeof(thing) + (sizeof(__stacktype) - 1)) \
  2534.     / sizeof(__stacktype) * (sizeof(__stacktype) / sizeof(__argptype)))
  2535.  
  2536. #define va_start(vaptr, lastarg) \
  2537.             vaptr = (va_list)&lastarg + __argsize(lastarg)
  2538.  
  2539. #ifdef pdp11
  2540.  
  2541. #define va_arg(vaptr, type) \
  2542.         (*(type *)(((vaptr) += __argsize(type)) - __argsize(type)))
  2543.  
  2544. #else
  2545.  
  2546. #define va_arg(vaptr, type) (__argsize(type) == 1 ? \
  2547.     *(type *)((vaptr)++) : \
  2548.         *(type *)(((vaptr) += __argsize(type)) - __argsize(type)))
  2549.  
  2550. #endif
  2551.  
  2552. #define va_end(vaptr)
  2553.  
  2554. #endif
  2555. %
  2556. chmod 664 include/stdarg.h
  2557. if test `wc -c < include/stdarg.h` -ne 3583; then
  2558.     echo "error extracting include/stdarg.h" 1>&2
  2559. fi
  2560. echo extracting include/stddef.h
  2561. cat > include/stddef.h <<\%
  2562. #ifndef _PTRDIFF_T_DEFINED
  2563. #define _PTRDIFF_T_DEFINED
  2564. typedef long int ptrdiff_t;
  2565. #endif
  2566.  
  2567. #ifndef _SIZE_T_DEFINED
  2568. #define _SIZE_T_DEFINED
  2569. typedef unsigned int size_t;
  2570. #endif
  2571.  
  2572. #ifndef _WCHAR_T_DEFINED
  2573. #define _WCHAR_T_DEFINED
  2574. typedef char wchar_t;
  2575. #endif
  2576.  
  2577. #ifndef NULL
  2578. #define NULL 0
  2579. #endif
  2580.  
  2581. #ifndef offsetof
  2582. #define offsetof(type, member) ((size_t)(char *)&((type *)0)->member)
  2583. #endif
  2584. %
  2585. chmod 664 include/stddef.h
  2586. if test `wc -c < include/stddef.h` -ne 385; then
  2587.     echo "error extracting include/stddef.h" 1>&2
  2588. fi
  2589. echo extracting include/stdlib.h
  2590. cat > include/stdlib.h <<\%
  2591. #ifndef _STDLIB_H
  2592. #define _STDLIB_H
  2593.  
  2594. #ifndef _SIZE_T_DEFINED
  2595. #define _SIZE_T_DEFINED
  2596. typedef unsigned int size_t;                    /* 4.1.5 */
  2597. #endif
  2598.  
  2599. #ifndef _WCHAR_T_DEFINED
  2600. #define _WCHAR_T_DEFINED
  2601. typedef char wchar_t;                        /* 4.1.5 */
  2602. #endif
  2603.  
  2604. typedef struct                            /* 4.10.6.2 */
  2605.     {
  2606.     int quot;
  2607.     int rem;
  2608.     } div_t;
  2609.  
  2610. typedef struct                            /* 4.10.6.4 */
  2611.     {
  2612.     long int quot;
  2613.     long int rem;
  2614.     } ldiv_t;
  2615.  
  2616. #ifndef NULL
  2617. #define NULL 0                            /* 4.1.5 */
  2618. #endif
  2619.  
  2620. #define EXIT_FAILURE 1                        /* 4.10.4.3 */
  2621. #define EXIT_SUCCESS 0
  2622.  
  2623. #ifdef pdp11        /* imperfect; also other 16-bit machines */
  2624. #define RAND_MAX 32767
  2625. #else                                /* 4.10.2.1 */
  2626. #define RAND_MAX 2147483647
  2627. #endif
  2628.  
  2629. #define MB_CUR_MAX 1
  2630.  
  2631. #ifdef __STDC__
  2632.  
  2633. extern double atof(const char *);                /* 4.10.1.1 */
  2634. extern int atoi(const char *);                    /* 4.10.1.2 */
  2635. extern long int atol(const char *);                /* 4.10.1.3 */
  2636. extern double strtod(const char *, char **);            /* 4.10.1.4 */
  2637. extern long int strtol(const char *, char **, int);        /* 4.10.1.5 */
  2638. extern unsigned long int strtoul(const char *, char **, int);    /* 4.10.1.6 */
  2639. extern int rand(void);                        /* 4.10.2.1 */
  2640. extern void srand(unsigned int);                /* 4.10.2.2 */
  2641. extern void *calloc(size_t, size_t);                /* 4.10.3.1 */
  2642. extern void free(void *);                    /* 4.10.3.2 */
  2643. extern void *malloc(size_t);                    /* 4.10.3.3 */
  2644. extern void *realloc(void *, size_t);                /* 4.10.3.4 */
  2645. extern void abort(void);                    /* 4.10.4.1 */
  2646. extern int atexit(void (*)(void));                /* 4.10.4.2 */
  2647. extern void exit(int);                        /* 4.10.4.3 */
  2648. extern char *getenv(const char *);                /* 4.10.4.4 */
  2649. extern int system(const char *);                /* 4.10.4.5 */
  2650. extern void *bsearch(const void *, const void *, size_t, size_t,
  2651.     int (*)(const void *, const void *));            /* 4.10.5.1 */
  2652. extern void qsort(void *, size_t, size_t,
  2653.     int (*)(const void *, const void *));            /* 4.10.5.2 */
  2654. extern int abs(int);                        /* 4.10.6.1 */
  2655. extern div_t div(int, int);                    /* 4.10.6.2 */
  2656. extern long int labs(long int);                    /* 4.10.6.3 */
  2657. extern ldiv_t ldiv(long int, long int);                /* 4.10.6.4 */
  2658. extern int mblen(const char *, size_t);                /* 4.10.7.1 */
  2659. extern int mbtowc(wchar_t *, const char *, size_t);        /* 4.10.7.2 */
  2660. extern int wctomb(char *, wchar_t);                /* 4.10.7.3 */
  2661. extern size_t mbstowcs(wchar_t *, const char *, size_t);    /* 4.10.8.1 */
  2662. extern size_t wcstombs(char *, const wchar_t *, size_t);    /* 4.10.8.2 */
  2663.  
  2664. #endif
  2665.  
  2666. extern double atof();
  2667. extern long int atol();
  2668. extern double strtod();
  2669. extern long int strtol();
  2670. extern unsigned long int strtoul();
  2671. extern void srand();
  2672. #ifdef __STDC__
  2673. extern void *calloc();
  2674. extern void *malloc();
  2675. extern void *realloc();
  2676. #else
  2677. extern char *calloc();
  2678. extern char *malloc();
  2679. extern char *realloc();
  2680. #endif
  2681. extern void free();
  2682. extern void abort();
  2683. extern void exit();
  2684. extern char *getenv();
  2685. #ifdef __STDC__
  2686. extern void *bsearch();
  2687. #else
  2688. extern char *bsearch();
  2689. #endif
  2690. extern void qsort();
  2691. extern div_t div();
  2692. extern long int labs();
  2693. extern ldiv_t ldiv();
  2694. extern size_t mbstowcs();
  2695. extern size_t wcstombs();
  2696.  
  2697. #endif
  2698. %
  2699. chmod 664 include/stdlib.h
  2700. if test `wc -c < include/stdlib.h` -ne 2977; then
  2701.     echo "error extracting include/stdlib.h" 1>&2
  2702. fi
  2703. if test ! -d lib
  2704. then    echo "making directory lib"
  2705.     mkdir lib
  2706. fi
  2707. echo extracting lib/stricmp.c
  2708. cat > lib/stricmp.c <<\%
  2709. #include <ctype.h>
  2710.  
  2711. stricmp(s1, s2)
  2712. register char *s1, *s2;
  2713. {
  2714. char c1, c2;
  2715.  
  2716. do    {
  2717.     c1 = *s1++;
  2718.     if(isupper(c1))
  2719.         c1 = tolower(c1);
  2720.  
  2721.     c2 = *s2++;
  2722.     if(isupper(c2))
  2723.         c2 = tolower(c2);
  2724.  
  2725.     if(c1 != c2)
  2726.         return c1 - c2;
  2727.  
  2728.     } while(c1 != '\0');
  2729.  
  2730. return 0;
  2731. }
  2732. %
  2733. chmod 664 lib/stricmp.c
  2734. if test `wc -c < lib/stricmp.c` -ne 251; then
  2735.     echo "error extracting lib/stricmp.c" 1>&2
  2736. fi
  2737. echo extracting lib/strtod.c
  2738. cat > lib/strtod.c <<\%
  2739. #include <stddef.h>
  2740. #include <ctype.h>
  2741.  
  2742. #define Ctod(c)  ((c) - '0')
  2743.  
  2744. #define NEXP 7
  2745.  
  2746. /* precomputed table handles up to 1e100 more-or-less directly */
  2747. /* (VAX D_floating only handles 1e38 or so!) */
  2748.  
  2749. static double facs[2][NEXP+1] =
  2750.     {
  2751.     {1e0,  1e1,  1e2,  1e4,  1e8,  1e16,  1e32,  1e64,},
  2752.     {1e-0, 1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32, 1e-64,},
  2753.     };
  2754.  
  2755. double
  2756. strtod(str, endp)
  2757. char *str;
  2758. char **endp;
  2759. {
  2760. register char *p;
  2761. register long int mant = 0;
  2762. int fracd = 0;
  2763. int exponent = 0;
  2764. int sign = 1;
  2765. double ret;
  2766.  
  2767. if(endp != NULL)
  2768.     *endp = str;
  2769.  
  2770. while(isspace(*str))
  2771.     str++;
  2772.  
  2773. if(*str == '-')
  2774.     {
  2775.     str++;
  2776.     sign = -1;
  2777.     }
  2778.  
  2779. p = str;
  2780.  
  2781. while(isdigit(*p))
  2782.     mant = 10 * mant + Ctod(*p++);
  2783.  
  2784. if(*p == '.')
  2785.     {
  2786.     p++;
  2787.  
  2788.     while(isdigit(*p))
  2789.         {
  2790.         mant = 10 * mant + Ctod(*p++);
  2791.         exponent--;
  2792.         }
  2793.     }
  2794.  
  2795. if(*p == 'e' || *p == 'E')
  2796.     {
  2797.     int expexp = 0;        /* "explicit exponent", if you like */
  2798.     int expsign = 1;
  2799.  
  2800.     p++;
  2801.  
  2802.     if(*p == '-')
  2803.         {
  2804.         p++;
  2805.         expsign = -1;
  2806.         }
  2807.  
  2808.     while(isdigit(*p))
  2809.         expexp = 10 * expexp + Ctod(*p++);
  2810.  
  2811.     exponent += expsign * expexp;
  2812.     }
  2813.  
  2814. if(endp != NULL && p > str)
  2815.     *endp = p;
  2816.  
  2817. ret = mant;
  2818.  
  2819. if(exponent != 0)
  2820.     {
  2821.     int which = 0;
  2822.     register int i;
  2823.  
  2824.     if(exponent < 0)
  2825.         {
  2826.         which = 1;
  2827.         exponent = -exponent;
  2828.         }
  2829.             
  2830.     for(i = 1; i < NEXP && exponent > 0; i++)
  2831.         {
  2832.         if(exponent & 01)
  2833.             ret *= facs[which][i];
  2834.  
  2835.         exponent /= 2;
  2836.         }
  2837.  
  2838.     while(exponent > 0)
  2839.         {
  2840.         ret *= facs[which][NEXP];
  2841.         exponent--;
  2842.         }
  2843.     }
  2844.  
  2845. return ret;
  2846. }
  2847. %
  2848. chmod 664 lib/strtod.c
  2849. if test `wc -c < lib/strtod.c` -ne 1411; then
  2850.     echo "error extracting lib/strtod.c" 1>&2
  2851. fi
  2852. echo extracting lib/vfprintf.c
  2853. cat > lib/vfprintf.c <<\%
  2854. #include <stdio.h>
  2855.  
  2856. vfprintf(fd, fmt, argp)
  2857. FILE *fd;
  2858. char *fmt;
  2859. int *argp;
  2860. {
  2861. return _doprnt(fmt, argp, fd);
  2862. }
  2863. %
  2864. chmod 664 lib/vfprintf.c
  2865. if test `wc -c < lib/vfprintf.c` -ne 111; then
  2866.     echo "error extracting lib/vfprintf.c" 1>&2
  2867. fi
  2868. exit
  2869.