[mod.sources] v08i081: Graph+, A Graph Plotting Program, Part01/03

sources-request@munnari.UUCP (02/28/87)

Submitted by: Alan Kent <ajk@goanna.oz.au>
Mod.sources: Volume 8, Issue 81
Archive-name: graph+/Part01

#! /bin/sh
# This is a shell archive, meaning:
# 1.  Remove everything above the #! /bin/sh line.
# 2.  Save the resulting test in a file
# 3.  Execute the file with /bin/sh (not csh) to create the files:
#
#		README
#		graph+.1
#		Makefile
#		bar_graph
#		best_fit
#		graph.h
#		yacc.y
#
# Created by ajk on Wed Feb 25 09:23:09 EST 1987
#
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
echo extracting "'README'"
sed 's/^X//' >README <<'SHAR_EOF'
XA Graph Plot Program:
X
XI have found this program extremely useful for generating line plots from
Xtables of data. It provides quite powerful operators to maniuplate and
Xjoin different tables of numeric data. It does not do bar graphs, pie charts
Xetc., although it could be modified to do so (and no, I am not volunteering!)
X
XIt can also be used as a desk calculator or to print tables of results
Xon the screen rather than as a graph. I personally prefer it to using
Xdc and bc quite often (but then I am probably biased). Just type graph+
Xthen 'print 1/log(3);' etc. I also use it interactively to check data
Xfiles, find how many entries have a column with values in a certain range
Xand so on. For example
X	data = read "datafile"
X	print count ( data where $1 > 0 && $1 < 10)
X
XThe program here is not interactive like GNUPLOT but an alternative which
Xprovides greater flexability in the preparation of data before it is plot
X(I think). Since it can be used in a non-interactive fashion, it can
Xbe used in makefiles (I use Makefiles to keep documents which include
Xgraphs up to date).
X
XThis program requires lex and yacc, and at present has calls to ctime()
Xand getenv() (although these last two calls are not critical). It also
Xrequires the standard unix plot(3X) library. Output is in plot(5) format
Xwhich must be fed through a filter program before it can be displayed.
XThe maths library is used extensively to provide the same functions in
Xgraph+, but the actual code only really needs log() and pow().
X
XI hope it is of some use to someone.
X
XAt the end of this file are instructions for patching this program
Xfor your system.
X
XBrief History
X=============
X
XAfter needing some graphs plotted and finding that the only really available
Xcommand I could find was graph(1), I decided to write my own. I also found
Xthat awk was too slow for my liking when trying to extract data from lots
Xof files to be reformatted in some way. Thus graph+ was born. Graph+ requires
Xa program (much like a shell script) to drive it. It allows files of data
Xto be loaded as two dimensional arrays and then allows many commands to
Xmanipulate the data. For example
X
X    data = read "data.file"
X    graph data where $1 > 0 [ $2 , $3 + $4 ] dotted line label "data"
X
Xwill plot a graph of the data in the file data.file but will only plot
Xpoints where the first column in the file has values greater than zero,
Xand the data to plot will be column 2 (x) vs the sum of the values in
Xcolumn 3 and 4 (y).
X
XWhen plotting the graph, different types of lines can be used (solid,
Xdotted, dot dashed, short dashed, long dashed) or each point can be
Xseparately marked (with triangles, squares, circles, crosses).
XAll the scaling and numbering of the axis is done automatically
Xalthough most values can be over-ridden.
X
XThe program also allows functions to be defined with sufficent power
Xto do a least-squares best fit algorithm.
X
XThe software was not written for efficency (it can be memory hungry
Xas all the table operations tend to make copies when performing the
Xoperations) but I have not found memory to be a problem yet (well,
Xnot on our vax anyway!)
X
XAll output is in standard plot(1) format as it uses the -lplot libraries.
X
XThis software should be fairly portable across UNIX systems although
Xa little work may be required for other systems (like IBM PCs).
X
XCOMMON PROBLEMS
X===============
X
XThe automatic scaling mechanism still does not always work correctly.
XIt is actually very difficult to do properly. If strange things appear
Xto be happening, try specifiying from and to on the xaxis and yaxis
Xstatements (xaxis from 0 to 1000). Also, there can only be one xaxis
Xand yaxis command per plot - multiple commands will ignore preceeding
Xcommands.
X
XMost of the automatic features can be overriden manually.
X
X
XPossible Patching Problems:
X
Xo   The file main.c contains a path name that needs to be patched.
X    This is because the program looks in some special directories for
X    predefined funtions (if you wish to install any). (See GLOB_LIB)
X    The path can be defined in the Makefile instead.
X
Xo   The last function in main.c may also need to be re-written for your machine
X    as it tries to determine what caused a floating exception. It will
X    probably work without any alterations. It is patched for our VAX 11/750
X    running BSD 4.2 at the moment.
X
Xo   The file main.c uses getenv() and the file yacc.y uses time(),
X    ctime() and system() (for the shell statement).
X
Xo   The include statement may not work as it involves fiddling with yacc.
X    At present it chews one token more than I really want it to.
X
Xo   If your library does not contain relevant maths functions, either
X    just delete the code, insert a dummy function, or whatever. Note
X    that the code assumes all math functions have extern double func();
X    in <math.h> so if the function is not in the library, you will 
X    probably have to add your own extern's where necessary.
X
Xo   dumpgrph.c needs to know the width of a character!! This of course
X    changes per output device. If the macro LASER is defined, it seems
X    to work ok with our apple laser printer (helvetica), otherwise
X    it works for a tektronics emulator on a Sun we have here. The size
X    of a character must be known so that centering and right justification
X    can be done (for titles, numbers along axis etc).
X
Xo   -lplot should be used in the Makefile for the most general form
X    of the program. Output can then be fed into plot(1) which re-maps
X    it for differnt terminal types. If however there is only one terminal
X    you are ever going to use it on (eg: a Tektronics 4014) then there
X    may be a plot library available on your machine so you can use
X    -l4014 and not have to type graph+ ... | plot ... all the time.
X    On the Sun for example there is a tektronics emulator which I use
X    for previewing graphs (they NEVER come out quite right the first time).
X
XAlan Kent,
XDept Computer Science,
XRoyal Melbourne Institute of Technology,
XMelbourne, Australia.
X
XACSNet: ajk@goanna.oz
XUUCP: {seismo,hplabs,mcvax,ukc,nttlab}!munnari!goanna.oz!ajk
XARPA: munnari!goanna.oz!ajk@SEISMO.ARPA
SHAR_EOF
if test 6129 -ne "`wc -c < 'README'`"
then
	echo shar: error transmitting "'README'" '(should have been 6129 characters)'
fi
fi
if test -f 'graph+.1'
then
	echo shar: will not over-write existing file "'graph+.1'"
else
echo extracting "'graph+.1'"
sed 's/^X//' >graph+.1 <<'SHAR_EOF'
X.\" Nroff -man
X.de SY
X.sp
X.SH SYNTAX
X.PP
X.nf
X..
X.de ES
X.SH EXAMPLES
X.PP
X.nf
X..
X.de EE
X.fi
X.SH DESCRIPTION
X..
X.TH graph+ ajk "31 January 1984"
X.SH NAME
Xgraph+ \- powerful graph generator program
X.SH SYNOPSIS
X.B graph+
X[ commandfile | \- ]
X[ parameter ... ]
X.SH DESCRIPTION
X.PP
X.I Graph+
Xis a utility program for producing graphs.
XIts main advantage is that
Xit allows easy manipulation of data using dynamic tables
Xin memory using a relational style language (project etc.)
Xrather than requiring a number of separate programs piped together.
X.I Graph+
Xalso allows functions to be defined returning values or tables.
X.PP
XBy placing the line
X.PP
X.in +4
X #! /usr/rmit/graph+
X.in -4
X.PP
Xat the beginning of a
X.I graph+
Xcommand file, the file can then be executed
Xdirectly like a shell script, assuming that the binary file is in /usr/rmit.
XOutput is in the format for input to the
X.IR plot (1)
Xfilters.
X.PP
XThe parameters can be referenced inside a plot program. The first parameter
Xis given the number 1. This allows file names and graph titles to be
Xpassed to a graph+ program. (See strings). If parameters are to be
Xsuppied, but the actual program is on standard input, then \- must
Xbe specified for the program file name. 'parms' is set to the number
Xof parameters. Note that numeric parameters can be handled by using
Xthe val() function.
X.PP
XIn
X.IR graph+ ,
Xa
X.I table
Xis a simple two dimensional array of floating point numbers.
XTables can have any number of rows and columns.
XData to be plotted is usually kept in files in a multicolumn format
Xwith each line representing one piece of related data (say results
Xfrom one experiment). An 
X.I awk
Xprogram can be used to extract data from a complexly structured data
Xfile or from files containing text and reformat into columns of data
Xif necessary.
X.I Graph+
Xdoes not allow text to
Xbe read \- only floating point data.
X.PP
XAnywhere a
X.I string
Xcan appear, a string expression is allowed.
XA string expression can consist of a list of concatenated strings
Xsepearated by + symbols.
XLiteral strings
Xare enclosed in double quotes (").
XTo embed a double quote in the string, place two double quotes
Ximmediately adjacent to each other.
XAlternatives for literal strings include the word
X.I parameter
Xfollowed by the parameter number (the first parameter is number 1).
XThe reserved word
X.I date
Xreturns a string which consists of the
Xcurrent time and date (UNIX format).
XThe function
X.I val(string)
Xreturns the numeric value of a string.
XThe function
X.I str(expr)
Xreturns a string containing the result of printf("%g",expr).
XThe function
X.I str(format,expr)
Xallows your own printf format to be used.
X.PP
XA
X.I graph+
Xprogram consists of a number of statements.
X.SH STATEMENTS
X.PP
XThe ';' is treated as a null statement. It can be used at between other
Xstatements if desired. When graph+ is used interactively (ie. type
Xgraph+ with no parameters) commands can be entered but due to the
Xlook-ahead parser, the next statement must be entered before the
Xexisting one is processed. Adding a ; to the end of the command
Xthen causes the parser to realise that the end of a statement has
Xbeen reached. Note also that due to problems with yacc(1), the
Xinclude statement requires a ; after it (see below).
X.SY
Xassume <expr> <string>
X.ES
Xassume parms=3 "3 parameters expected: "+str(parms)+" parameters given"
Xassume count(data) > 0 "data table empty"
X.EE
X.PP
X.I Assume
Xaborts a graph+ program if the expression evaluates to false.
XThis allows simple checks for table sizes, numbers of parameters
Xetc. to be inserted in a program and an error message to be
Xprinted.
X.SY
X<tab_ident> = <table>
X.ES
Xdata = read "data.file"
X.EE
X.PP
XDefines the identifier 
X.I tab_ident
Xto have the specified table of values.
XTable identifiers may be referred to in
X.I table
Xexpressions. In the declaration,
X.I tab_ident
Xmust not have been already
Xdefined unless it has already been defined as a table.
X.SY
X<var_ident> = <expr>
X.ES
Xpi = 3.1415
X.EE
X.PP
XSimilar to a table declaration, except that the identifier has a single
Xnumeric value assigned to it instead of a complete table.
X.SY 
X<ident> ( <dec_parm_list> ) = <expr>
X<ident> ( <dec_parm_list> ) = <table>
X.ES
X# this function returns the ratio of two numbers
X
Xa = 0
Xb = 0
Xratio(a,b) = a/b
X
X# this function limit returns a table of values where column
X# 1 is less than the specified limit
X
Xlimit_val = 0     # declare parameters
Xlimit_tab = {{0}}  # declares a table
Xlimit ( limit_tab , limit_val ) = limit_tab where $1 < limit_val
X.EE
X.PP
XDeclares a function which returns 
Xa simple value or a table of values.
XFunction identifiers can never be redefined.
XThe parameter lists consist of a comma separated list of
X.I tab_ident
Xor
X.I var_ident
Xidentifers
X.B "which must have been previously defined"
X(see above example).
XThese identifiers will act as parameters to the functions in the following
Xway. When the function is called, the identifiers are redeclared with
Xthe parameter values. Care must be taken for nested function calls as if they
Xuse the same identifier names for parameters, the parameters to function
XA might be changed after returning from a call to function B.
XIt is common to declare special variables for parameters to functions
Ximmediately before the actual function to be certain of the parameter type.
X.SY
Xinclude <string> ;
X.ES
Xinclude "bar_chart.g" ;
X.EE
X.PP
XThe
X.I include
Xstatement allows another file of
X.I graph+
Xcommands to be included. This is useful for including function declarations.
XThe ; is needed only because of the current implementation of the parser.
XWhen an include is done, one token after the string is accidently lost.
XThis may not be consistant across implementations of yacc and lex.
X.SY
Xshell <string>
X.ES
Xshell "awk -f complex.awk prelimfile > finalfile"
Xdata = read "finalfile"
X.EE
X.PP
XThe
X.I shell
Xcommand allows a command to be passed to the shell 'sh'.
XThis can be used
Xto generate a file to be read by including an output redirection.
X.SY
Xprint <table>|<expr>|<string>
Xprint > <string> <table>|<expr>|<string>
Xprint >> <string> <table>|<expr>|<string>
X.ES
Xprint "saving data to file copy.of.data"  # print message on screen
Xprint > "copy.of.data" data  # save data on a file
Xprint >> "copy.of.data" more_data  # append some more data
X.EE
X.PP
XThe
X.I print
Xcommand can have its output redirected (overwrite or append) to a
Xfile called
X.I <string>
Xand print out a table, the result of an expression or a simple string.
XIf two prints to the same file are used, make sure the second is and
Xappend redirect (>>).
X.SY
Xsave <table> as <string>
X.ES
Xdata = read "text.data"
Xsave data as "data.binary"
Xprint > "text.data" load "data.binary"  # converts a binary file back to text
X.EE
X.PP
XThe
X.I save
Xcommand saves the table of data in binary format for faster loading
Xwith the
X.I load
Xcommand. This is useful when a large datafile is to be read a number
Xof times by different plot programs as the binary file can be loaded
Xmuch more quickly than textual versions. It is also possible that
Xthe binary file will require less disk space. The format of the
Xbinary file is simply two integers (number of columns and number
Xof rows) followed by all the data (doubles).
XThe data is written out column by column so the first double is
Xthe first value in the first column, the second value is the
Xsecond value in the first column and so on.
XBinary files may not be transportable across machines.
X.SY
Xgraph <table> [ <option> ... ]
X<option> = no|solid|dotted|shortdashed|longdashed|dotdashed line
X<option> = no|triangle|circle|square|cross|plus points
X<option> = label <string>
X<option> = legend <string>
X.ES
Xgraph data
Xgraph data2 dotted line
Xgraph data3 dotdashed line label "data3"
Xgraph data no line cross points circle points # put a circle and cross
Xgraph {{10,20}} label "text at 10,20"
X.EE
X.PP
XThe
X.I graph table
Xcommand specifies details of an actual graph to plot. The table specified
Xmust have exactly two columns, the first column being for x coordinatates and
Xthe second for y coordinates. Multiple
X.I graph
Xcommands will cause multiple graphs to be drawn on the same axis.
X.PP
XThe graph options allow the type of line to draw the graph to
Xbe specified (default is solid) and for a label to be printed on the
Xfinal graph after the last point of the data. Note that text can be
Xpositioned anywhere on a graph by using a constant table and a label
X(as in the example above).
X.PP
XThe
X.I legend
Xoption can be used instead of the label option if a separate legend
Xis wanted. The label option often fails when more than one graph end
Xat the same point causing the labels to be overwritten making them
Xunreadable. The
X.I put
X.I legend
X.I at
Xstatement can be used to position the legend on the graph (the legend
Xcannot be put below the graph).
X.PP
XThe point information (circle, triangle etc) is drawn per point in the
Xgraph. The
X.I "no line"
Xoption must be used if only the points are to be
Xmarked. The default points value is 
X.I "no points."
XNote that multiple
Xpoints options on the same graph statement will cause the different
Xtypes of points to be superimposed. This means a crossed circle can
Xbe done using 
X.I "crossed points circle points."
XNote that only one line type can be specified however.
X.SY
Xgraph label <string>
X.ES
Xgraph label "Statistics for the year 1986  " + date
X.EE
X.PP
XAllows a label to be placed at the top of the graph.
XIf this command is not specified, no label
Xis printed. Note that the string (as like all other strings)
Xcan be a concatenation of literal strings and other
Xbuilt in operators (like
X.IR date ).
X.SY
Xxaxis [ <axis_option> ... ]
Xyaxis [ <axis_option> ... ]
X<axis_option> can be one of
X    label <string>	(default none)
X    format <string>	(default depends on range of data)
X    auto|no scale	(default auto scale)
X    frame|grid|outline|no axis	(default grid axis)
X    logarithmic|linear	(default linear)
X    from <expr> to <expr>
X    tick size <expr> [ power <expr> ]
X.ES
X# xaxis will have all values marked and vertical lines drawn over the
X# whole graph per tick value
Xxaxis label "Year" format "%4f"
X    from 1980 to 1986 tick size 1
X# yaxis will have only the tick values drawn (no horizontal lines)
X# The data will be made logarthmic and the label will be spilt over
X# two lines: "Population" and "per year"
Xyaxis frame axis logarithmic label "Population per~year"
X
X# another example with ticks
Xyaxis tick size 2 power 3  # ticks are really of size 2000 (2 x 10^3)
X.EE
X.PP
XThe
X.I xaxis
Xand
X.I yaxis
Xstatements allow details to be specified on how the x and y axis lines
Xfor the graph frame should be drawn.
X.PP
XThe
X.I label
Xoption is used to define what label to place on the axis.
XOn the Y axis, the words (separated by blanks) are placed on separate lines
Xone above the other. To place two words on the same line
X(for example if there are two short words) use a '~' instead
Xof a space. When the label is printed, the '~' will be changed
Xinto a blank.
XThe best way to write the label would be by rotating the text
Xby 90 degrees, but the plot(1) format does not allow this.
X.PP
XThe
X.I format
Xoption allows a printf format string for a long floating point number
X('double' in the 'C' programming language)
Xfor printing the numbers on the ticks along the axis. No checking of
Xthis format string is made. The default format is generally acceptable.
X.PP
XThe
X.I scale
Xoption allows the values along the axis to be automatically scaled by
Xlabeling the axis as 'x 10^n' where n is the power. For example,
Xfor values from 0 to 10000 may be shown on the ticks as from 0 to 10.0
Xwith a factor of 10^3. The default is to
X.I auto
X.I scale
Xbut the
X.I no
X.I scale
Xoption can inhibit this.
X.PP
XThe
X.I axis
Xoption allows different forms of axis lines to be drawn.
XNormally both axis will be given the same axis type, but this
Xis not necessary.
XThe
X.I frame
Xoption simply draws a single line along which ticks are place with
Xnumeric values.
XThe
X.I grid
Xoption draws a grid over the whole graph.
XThe
X.I outline
Xoption is very similar to the 
X.I frame
Xoption but a second line is drawn on the far side of the graph.
XIf this option is used on both axis or with a grid on the other axis,
Xthe graph will appear to be enclosed in a box.
XIf no axis (and no numbering) is desired, use the
X.I no
Xoption.
XThe best way to find out what these do exactly is to experiment.
X.PP
XThe
X.I linear
Xand
X.I logarithmic
Xoptions allow a simple linear (default) or logarithmic graph to be
Xplotted.
X.PP
XThe
X.I from
Xclause allows the range of values for the axis to be specified.
XBy default this is calculated from the graphs to be plotted by
Xdetermining the minimum and maximum values.
X.PP
XThe
X.I tick
X.I size
Xclause allows the tick sizes to be specified. Normally this option would
Xnot be used as graph+ tries to automatically calculate a nice looking
Xvalue for the tick size and power (based on how much will fit on the output).
XFractions for tick sizes (eg 1.33333) do not work well and due to rounding
Xproblems may cause tick marks to be skipped along the axis. Round numbers
Xshould be safe.
XIf
X.I power
Xis not specified, then it is defaulted to 0. If a value of say 3 is used,
Xthen the tick values will really be the tick size specified by 10^3 and
Xthe power will be printed below the axis label in the form 'x 10^3'.
X.SY
Xput legend at top|middle|bottom left|center|right
X.ES
Xgraph data1 dotted line legend "graph 1"
Xgraph data2 solid line legend "graph 2"
Xput legend at bottom left
Xplot
X.EE
X.PP
XThe
X.I put
X.I legend
X.I at
Xstatement is used to position the legend on the graph. The legend
X(at present) is drawn on top of the actual graph (underneath may
Xbe better, but is harder due to the way the plot(1) functions work).
XThe top/middle/bottom option selects the verical positioning and
Xthe left/center/right option selects the horizontal positioning.
XThe default position is the top right corner of the graph.
X.SY
Xplot
X.ES
Xgraph data1 solid line
Xgraph data2 dotted line
Xplot
Xgraph data3 dotted line
X.EE
X.PP
XThe
X.I plot
Xcommand generates a plot of all the graph statements done so far.
XAny following graph commands will appear on the next plot.
XA plot command is automatically performed at the very end of
Xa command file if any graph statements have been found.
X.SH TABLES
X.PP
XA <table> can be an expression made up from any of the following
Xsub-expressions. These usually appear directly in
X.I graph
Xstatements, or when assigned to a table variable.
X.SY
Xread <file_name_string>
Xread <cols_expr> by <rows_expr> <file_name_string>
X.ES
Xdata = read "data.file"
Xgraph read 2 by 200 "data.file" # I know the file is 2 colums by 200 lines
Xdata = read parameter 2 + ".dat"
X.EE
X.PP
XThe
X.I read
Xcommand reads a table from a file. Blank lines
Xand all characters from # to the end of line are ignore (allowing
Xcomments to be put in data files). The number
Xof columns in the table is determined by reading the first non-blank
Xline in the file. The number of rows is determined by counting
Xthe number of non-blank lines in the file. To read a file, two passes
Xare made \- the first to determine the table size so a table can be
Xallocated enough memory, and a second to read the actual values.
X.PP
XThe second form of the
X.I read
Xcommand allows the dimensions of the table to be prespecified so that
Xonly one pass of the file is needed. This makes the command slightly
Xfaster but much less flexible to changes in the data file. The first
X.I expr
Xspecifies the number of columns (which must be correct)
Xand the second specifies the number of rows (which can be too large \-
Xthe table is simply truncated although enough memory is allocated for
Xthe full table.
X.PP
XThe ability to create strings from concatenations of sub-strings
Xis very useful when specifying a filename. In particular, the
Xability to extract parameters to the command allows the same
Xplot program to be used with several data files.
X.SY
Xload <string>
X.ES
Xdata = load "data.file"
X.EE
X.PP
X.I load
Xis very similar to
X.I read
Xexcept that it loads a binary file saved with the
X.I save
Xcommand. The binary file format is described under the
X.I save
Xcommand.
X.SY
X{ { <expr> , <expr> , ... } { <expr> , <expr> , ... } ... }
X.ES
Xconst_table = { {0,0} {1,2} {3,10} {4,20} } # 2 cols, 4 rows
Xcalc_table = { {0,log(0)} {10,log(10)} {100,log(100)} }
Xgraph {{10,20}} label "label at 10,20 on graph"
X.EE
X.PP
XBraces allow a table of expressions to be specified.
XOne set of braces surround
Xthe whole table, each row in the table being specified by a comma
Xseparated list of expressions surrounded in one set of braces per row.
X.SY
Xgenerate from <expr> to <expr> <interval>
X<interval> = with <expr> intervals
X<interval> = interval size <expr>
X.ES
Xtab_size = 10
Xlog_table = generate from 1 to tab_size interval size 1 [ $1 , log($1) ]
Xsingle_col_table = generate from 1 to 100 with 11 intervals
X.EE
X.PP
XThe
X.I generate
Xcommand generates a single column table of values in the specified from\-to
Xrange using the specified interval details.
X.SY
X<table> append <table>
X.ES
Xtotal_dat = dat1 append dat2
X.EE
X.PP
XThe
X.I append
Xcommand appends the second
X.I table
Xto the end of the first
X.I table.
XBoth tables must have the same number of columns.
X.SY
X<table> adjacent <table>
X.ES
Xwide_tale = dat1 adjacent dat2
X.EE
X.PP
XThe
X.I adjacent
Xcommand is similar to the
X.I append
Xcommand, but it joins tables horizontally instead of vertically.
XBoth tables can have different numbers of columns, but must have the
Xsame number of rows.
X.SY
X<table> [ <expr> , <expr> , ... ]
X.ES
Xgraph all_data [ $1 , $3 ] # plot columns 1 vs 3
Xgraph all_data [ $0 , ($1+$3)/2 ] # plot the average of column 1 and 3
X.EE
X.PP
XThe projection command (square brackets) allows data to be extracted
Xfrom a table, or new columns of data to be formed from expressions based
Xon existing columns of data. See the
X.I expr
Xfor exact details of how to extract columns of data from a table.
XThe number of rows in the new table is the same as the number of
Xrows in the old table, but the number of columns is dependent on the
Xnumber of expressions in the comma separated list of expressions inside
Xthe square brackets.
X.SY
X<table> where <expr>
X.ES
Xsome_data = data where $0 <= 100   # select first 100 entries in table
Xsome_data = data where $1 >= $2/2   # select lines where first column is
X   # greather than or equal to half the second column
X.EE
X.PP
XThe
X.I where
X(or selection) command forms a new table with the same number of columns
Xas the old table, but only containing rows that satisfy the condition
X.I expr.
XThe expression normally uses relational operators such as <, >= etc.
Xbut any expression can be used. A return value of 0.0 is considered as
Xfalse, any other value being true (as in the 'C' programming language).
X.SY
Xsort <table> by <attr>
X.ES
Xdata = sort data by $2
X
X# sort table of two columns by the sum of the two columns
Xsorted_data = sort ( data [ $1 , $2 , $1+$2 ] ) by $3 [ $1 , $2 ]
X.EE
X.PP
XThe
X.I sort
Xcommand allows a table to be sorted by a column in the table specified by
X.I attr.
XThe column specified cannot be column zero (which has a special use).
XAn
X.I attr
Xwas used to sort on instead of any expression for efficency reasons.
XTo sort by an expression, simply project an extra column into the table
Xbefore sorting, and remove it (with another project) later.
XThe sort algorithm used is a version of quicksort.
X.SY
Xjoin <table1> , <table2> by <attr1> , <attr2>
X.ES
Xdata = join dat1 , dat2 by $1 , $1
X.EE
X.PP
XThe
X.I join
Xcommand performs an equi-join on the two tables of data.
X<attr1> and <attr2> specify the fields on which the join is
Xto be performed. If the data in the two tables is not found
Xto be sorted on the join fields, the tables are sorted automatically.
XThe join then creates a new table which contains all the columns
Xof <table1> followed by all the columns of <table2> (the join
Xfield is NOT removed and so will appear in the resultant table twice).
X.SY
Xcumulate <table> by <attr>
X.ES
Xgraph cumulate cost_per_unit by $2
X.EE
X.PP
XThe
X.I cumulate
Xcommand takes a column in a table specified by
X.I attr
Xand replaces each entry with the sum of the present value and all the
Xprevious values. This is useful for plotting cumulative graphs.
X.SY
Xgroup <table> [ from <expr> to <expr> ] <interval> performing <function>
X.ES
Xgraph group sort data by $1 with 10 intervals performing max [ ($1+$2)/2 , $3 ]
Xdata = group data from 10 to 20 with interval size 5 performing average
X.EE
X.PP
XThe
X.I group
Xcommand is fairly complex but allows graphs to be split into a number
Xof intervals with the specified function
X.RI ( fvar_ident )
Xbeing called for each interval.
XThe function must return a simple value and have a single parameter of
Xa table (such as the predefined 
X.IR sum ,
X.IR min ,
X.IR max ,
X.I count
Xand
X.I average
Xfunctions.
X.PP
XThe group table must have exactly two colums, the first column
Xbeing used for grouping by and the second containing the values to pass
Xto the function. The table must have already been sorted on the first column.
XProjects are often made to tables to get them into the
Xcorrect form for a grouping.
XThe optional range specification (if no range is specified, the minimum and
Xmaximum values are calculated from the table) defines the upper and lower
Xbound of values to be considered in the group.
XThe interval specification is used to determine the number of and size of
Xthe group intervals. Beware of intervals have no values in them. These
Xwill cause the function to be called with an empty table.
X.PP
XThe result of a group has extactly three columns. The first two columns
Xcontain the lower and upper bound values of the interval and the third
Xcolumn contains the result of the function when applied to all of the
Xvalues within that interval. From this, it is fairly simple to produce
Xdifferent forms of graphs (eg bar graphs, line graphs).
X.SY
X<function_ident> ( <expr> , <expr> , ... )
X.ES
Xprint min ( data_table )
Xprint average ( data_table[$3] )
X
Xtab = {{0}}
Xftab(tab) = count ( tab[$1] ) / average ( tab[$2] )
Xprint ftab({{1,1}{4,4}})
X.EE
X.PP
X.I <function_ident>
Xis a table function name which is called with the specified parameter list.
X.PP
XTable identifiers return the value of the table as last declared
Xwith "=" or when declared as a parameter.
X.SY
X( <table> )
X.ES
Xsort ( data[ $1 , $3 ] ) by $2
X.EE
X.PP
XSimple parenthisis allow the priority of evaluation to be forced.
XDefault priority of table operators listed from highest priority to lowest is
Xselect and project, adjacent, append, sort, group and cumulate.
X.SH EXPRESSIONS
X.PP
XIn the following, a true value is considered to be any non-zero value,
Xand false is considered to be the value 0.
XThe operators are listed here from lowest to highest priority.
X.SY
X<expr> "?" <expr> ":" <expr>
X.ES
Xdata = data [ $1 , $2<=0 ? 0 : log($2) ]
X.EE
X.PP
XThe
X.I '?'
Xoperator returns the value of the second
Xexpression if the first expression is true; otherwise the value of the third
Xexpression is returned.
X.SY
X<expr> || <expr>
X.ES
Xdata = data where $1<10 || $1>20
X.EE
X.PP
XThe logical OR operator ('||') returns true if either expression returns
Xtrue, false otherwise.
X.SY
X<expr> && <expr>
X.ES
Xdata = dat where $1>10 && $2<20
X.EE
X.PP
XThe logical AND operator ('&&') returns true if both expressions return true,
Xfalse otherwise.
X.SY
X<expr> =|==|<>|!=|<=|<|>|>= <expr>
X.ES
Xdata = data where $1>10 | $2 = 0
X.EE
X.PP
XThese relational operators return true or false based on the operator
Xand the results of the two expressions. Note that equivalence can be
Xspecified with either '=' or '==' and inequivalence can be specified
Xwith either '<>' or '!='.
X.SY
X<expr> +|\- <expr>
X.ES
Xprint data[ $1+$2 , $1\-$2 ]
X.EE
X.PP
XThese are the simple arithmetic addition and subtraction operators.
X.SY
X<expr> /|*|% <expr>
X.ES
Xpi = 3.1415
Xprint pi / 3
Xprint pi * 3
Xprint 100 % 3
X.EE
X.PP
XDivide, multiply and modulus operators.
X.SY
X<primary> =
X    \- <primary>
X    ! <primary>
X    ( <expr> )
X    <attr>
X    <number>
X    <func_var_ident> ( <expr>|<table> , <expr>|<table> , ... )
X    <var_ident>
X    parms
X.ES
Xprint -3
Xprint !3
Xprint data[ $2 ]
Xpi = 3.1415
Xprint pi
Xtab={{0}}
Xsizeof ( tab ) = count ( tab )
Xprint "table size should be 2"
Xprint sizeof ( {{1},{2}} )
X.EE
X.PP
X.I Primary
Xexpressions are separated in the syntax as an attribute specification
X(see 
X.IR attr )
Xcan only be a
X.I primary
Xexpression, typically a simple number.
X.PP
XThe minus unary operator is for negation.
X.PP
XThe NOT ("!") operator returns the oposite boolean value of the expression.
X.PP
XParenthises allow the default operator precidence to be overridden.
X.PP
X.I Numbers
Xare any floating point number.
X.PP
X.IR <fvar_ident> s
Xare variable function names, the parameters being listed inside
Xthe parenthises.
X.PP
X.IR <var_ident> s
Xare variables defined with the "=" operator or by parameter passing.
X.PP
X.I parms
Xreturns the number of command line parameters given to the program.
XThis is really only of any use in an
X.I assume
Xstatement (assume parms = 3 "3 parameters expected").
X.SY
X<attr> = $ <primary>
X.ES
Xgraph data[ $1 , $2 ]
Xsumcol = 3
Xgraph data[ $1 , $sumcol ]
Xgraph data[ $1 , $($1<100 ? 2 : 3 ) ]  # choose column based on $1
Xprint > "savefile" data[ $0 , $1 , $2 ]
X.EE
X.PP
X.IR <attr> 's
Xspecify which column of a table to use.
XColumn numbers start from 1 and count upwards.
XIn an expression, the value
Xof that column is returned if used in a project or select type command
Xfor which the expression is re-evaluated for every row of a table.
XIn expressions, column zero may be specified which returns the row number
X(starting from 1)
Xof the row instead of data from the table.
X.SH "PREDEFINED FUNCTIONS"
X.PP
XThere are a number of predefined funtions, all of which at present return
Xvalues.
X.TP
X.BI str (expr)
X.TP
X.BI str (format,expr)
XReturn a string containing the value of the given expression.
XIf no format is specified, "%g" is used. The format is passed straight
Xto sprintf().
X.TP
X.BI min (table)
XReturn the minimum value in the first column of
X.IR table .
XIf the table is empty, a warning is printed on stderr and a value of 0.0 is
Xreturned.
X.TP
X.BI max (table)
XReturn the maximum value in the first column of
X.IR table .
XIf the table is empty, a warning is printed on stderr and a value of 0.0 is
Xreturned.
X.TP
X.BI sum (table)
XReturn the total of all the values in the first column of
X.IR table .
XIf the table is empty, a warning is printed on stderr and a value of 0.0 is
Xreturned.
X.TP
X.BI count (table)
XReturn the number of rows in
X.IR table .
X.TP
X.BI average (table)
XReturn the average value in the first column of
X.IR table .
XIf the table is empty, a warning is printed on stderr and a value of 0.0 is
Xreturned. This is like doing a
X.I sum
Xdivided by a
X.I count.
X.PP
XThe following mathmatical functions are straight from the math library
Xand suffer from all their constraints (no checking is done). See section
X3(M) in the Unix Programmers Manual.
X.TP
X.BI sqrt (expr)
XReturn the square root of the given expression.
X.TP
X.BI log (expr)
XReturn log10 of the given expression.
X.TP
X.BI ln (expr)
XReturn the natural log of the given expression.
X.TP
X.BI exp (expr)
XReturn e to the power of expr.
X.TP
X.BI pow (expr1,expr2)
XReturn 
X.I expr1
Xto the power of
X.I expr2.
X.TP
X.BI floor (expr)
XReturn the largest integer not greater than
X.I expr.
X.TP
X.BI ceil (expr)
XReturn the smallest integer not less than
X.I expr.
X.TP
X.BI abs (expr)
XReturn the absolute value of
X.I expr.
X.TP
X.BI sin (expr)
XReturn sine of
X.I expr.
X.TP
X.BI cos (expr)
XReturn cosine of
X.I expr.
X.TP
X.BI asin (expr)
XReturn arc-sine of
X.I expr
Xin the range from -pi/2 to +pi/2.
X.TP
X.BI acos (expr)
XReturn arc-cos of
X.I expr
Xin the range 0 to pi.
X.TP
X.BI atan (expr)
XReturn arc-tangent of
X.I expr
Xin the range -pi/2 to +pi/2.
X.TP
X.BI atan2 (expr1,expr2)
XReturn arc-tangent of
X.I expr1/expr2
Xin the range -pi to +pi.
X.TP
X.BI sinh (expr)
XReturn hyperbolic sine of
X.I expr.
X.TP
X.BI cosh (expr)
XReturn hyperbolic cosine of
X.I expr.
X.TP
X.BI tanh (expr)
XReturn hyperbolic tangent of
X.I expr.
X.TP
X.BI hypot (expr1,expr2)
XReturns the square root of the sum of the squares of
X.I expr1
Xand
X.I expr2.
X.TP
X.BI j0 (expr)
X.TP
X.BI j1 (expr)
X.TP
X.BI jn (expr1,expr2)
X.TP
X.BI y0 (expr)
X.TP
X.BI y1 (expr)
X.TP
X.BI yn (expr1,expr2)
XBessel functions.
X.TP
X.BI val (string)
XReturn the numeric value of the specified string.
XThis is particularly useful for getting numeric parameters from the
Xcommand line.
X.SH EXAMPLE
X.PP
XThe following are some simple examples to try and help show how to actually
Xuse all these commands.
X.PP
X #! /usr/rmit/graph+
X  
X # some constant tables
X  
X file1={ { 0, 1 } { 1, 20 } { 2, 30 } { 3, 16 } { 4, 19 } { 5, 29 } }
X file2={ { 0, 3 } { 1, 15 } { 2, 34 } { 3, 12 } { 4, 12 } { 5, 24 } }
X
X num = 6
X  
X final = sort (file1 append file2 )  by $1
X  
X # the output of graph can not be directly printed.
X # turn a graph table into a list of lines through
X # the middle of each interval by forming a table
X # with the first column being the middle of the group
X # interval (the average of the first two columns) and
X # the other column being the actual data
X  
X lineg_tab={{0}} # dummy declaration for function parameter
X lineg(lineg_tab) = lineg_tab[ ($1+$2)/2 , $3 ]
X  
X graph lineg( group final with num intervals performing max )
X graph lineg( group final with 10 intervals performing min )
X graph file1 label "file"
X graph cumulate(file1) label "cumulative"
X  
X xaxis label "This is the x-axis"
X yaxis label "This is~the y-axis"
X # will appear
X #   This
X #  is the
X #  y-axis
X.PP
XA more typical example is
X.PP
X data1 = read "results.1"
X data2 = read "results.2"
X graph data1[ $1 , $2 ] dotted line label "first results"
X graph data2[ $1 , $2 ] shortdashed line label "second results"
X graph ( data1[$1,$2] adjacent data2[$2] ) [ $1 , ($2+$3)/2 ]
X    solid line label "average"
X.SH DIAGNOSTICS
X.PP
XError recovery is poor. Syntax error can mean almost anything, the
Xparse being a simple yacc program. Note that undefined identifiers
X(due to typing errors) can also come up as syntax errors.
X.SH BUGS
X.PP
XAlmost infinite. But the program in general does work. Its just that
XI personally find the graphs are often not quite what I want.
XIt is however MUCH better than the standard unix graph(1) command
Xand allows good manipulation of data much faster than awk(1).
X.PP
XOne problem that can occur is running out of memory.
XAll tables are held in memory - never on disk.
XThe program works be continually building new tables from
Xthe contents of old tables and then (if possible) freeing the
Xold tables. Parameters after being called do not free the space
Xconsumed by the parameter declaration, so large data files can
Xcause a lot of memory to be held by the graph+ program.
X.PP
XThere would need to be several thousand more options to be able to
Xprint all forms of graphs to keep everyone happy.
X.PP
XIf you have any problems, do not mail to ajk@goanna.oz ;-)
X.SH AUTHOR
XAlan Kent (ajk@goanna.oz)
X.SH SEE ALSO
Xleplot(ajk), awk(1), graph(1), plot(1), plot(3x).
SHAR_EOF
if test 30797 -ne "`wc -c < 'graph+.1'`"
then
	echo shar: error transmitting "'graph+.1'" '(should have been 30797 characters)'
fi
fi
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
echo extracting "'Makefile'"
sed 's/^X//' >Makefile <<'SHAR_EOF'
X
XCFLAGS= -O -DGLOB_LIB=\"/r/pmr/ajk/lib/graph+rc\"
X#CFLAGS=	-O -DGLOB_LIB=\"/r/pmr/ajk/lib/graph+rc\" -DLASER
X
XLIBS=	-lm -ll -lplot
X#LIBS=	-lm -ll -l4014
X
XYFLAGS=	-d
XPROG=	graph+
XOBJS=	yacc.o lex.o \
X	abort.o adjacent.o appendtb.o attrnode.o average.o count.o \
X	cpyentry.o cpytable.o cumulate.o dumpgrph.o eval.o evaltab.o \
X	freetab.o fundeclr.o generate.o global.o group.o include.o \
X	join.o main.o max.o min.o new.o newtable.o numcols.o parmnode.o \
X	printtab.o project.o readtab.o reset.o saveload.o select.o sort.o \
X	sum.o tabdeclr.o tabnode.o vardeclr.o
X
XAll : $(PROG)
X
X$(PROG) : $(OBJS)
X	cc -o $(PROG) $(OBJS) $(LIBS)
X
Xshar :
X	shar README graph+.1 Makefile bar_graph best_fit graph.h yacc.y > g1.shar
X	shar [a-f]*.c > g2.shar
X	shar [g-z]*.c lex.l > g3.shar
X
Xclean :
X	/bin/rm $(OBJS)
X
Xlink :
X	cc -pg -o $(PROG) $(OBJS) $(LIBS)
X
X# following line needed if numbering of tokens change
X# but otherwise just causes lots more compiles.
X
Xcumulate.o lex.o fundeclr.o dumpgrph.o eval.o evaltab.o \
Xreset.o group.o main.o parmnode.o: y.tab.h 
X
X$(OBJS): graph.h
SHAR_EOF
if test 1060 -ne "`wc -c < 'Makefile'`"
then
	echo shar: error transmitting "'Makefile'" '(should have been 1060 characters)'
fi
fi
if test -f 'bar_graph'
then
	echo shar: will not over-write existing file "'bar_graph'"
else
echo extracting "'bar_graph'"
sed 's/^X//' >bar_graph <<'SHAR_EOF'
X
XPARM = {{0}}
X
Xstep_graph ( PARM ) =
X	( sort (PARM [ $0*2+1 , $1 , $3 ]
X		    append
X		PARM [ $0*2+1 , $2 , $3 ] )
X	by $1 ) [ $2 , $3 ]
X
Xbar_graph ( PARM ) =
X	( sort (PARM [ $0*4+1 , $1 , 0 ]
X		    append
X		PARM [ $0*4+2 , $1 , $3 ]
X		    append
X		PARM [ $0*4+3 , $2 , $3 ]
X		    append
X		PARM [ $0*4+4 , $2 , 0 ] )
X	by $1 ) [ $2 , $3 ]
X
SHAR_EOF
if test 338 -ne "`wc -c < 'bar_graph'`"
then
	echo shar: error transmitting "'bar_graph'" '(should have been 338 characters)'
fi
fi
if test -f 'best_fit'
then
	echo shar: will not over-write existing file "'best_fit'"
else
echo extracting "'best_fit'"
sed 's/^X//' >best_fit <<'SHAR_EOF'
X
XSxy_table = {{0}}
XSxy( Sxy_table ) =
X	sum ( Sxy_table[ $1*$2 ] ) - ( 1 / count ( Sxy_table ) )
X	* sum ( Sxy_table[ $1 ] ) * sum ( Sxy_table [ $2 ] )
X
Xbeta_table = {{0}}
Xbeta_func ( beta_table ) = 
X	Sxy ( beta_table ) / Sxy ( beta_table[ $1 , $1 ] )
X
Xalpha_table = {{0}}
Xalpha_func ( alpha_table ) =
X	average ( alpha_table[ $2 ] )
X	- beta_func ( alpha_table ) * average ( alpha_table[ $1 ] )
X
Xfit_Min = 0
Xfit_Max = 0
Xfit_Alpha = 0
Xfit_Beta = 0
Xline_fit2 ( fit_Min , fit_Max , fit_Alpha , fit_Beta ) =
X	{{ fit_Min , fit_Alpha + fit_Beta * fit_Min }
X	{  fit_Max , fit_Alpha + fit_Beta * fit_Max }}
X
Xline_fit_table = {{0}}
Xline_fit ( line_fit_table ) =
X	line_fit2 ( min ( line_fit_table[ $1 ] ) , max ( line_fit_table[ $1 ] ),
X	alpha_func ( line_fit_table ) , beta_func ( line_fit_table ) )
X
SHAR_EOF
if test 789 -ne "`wc -c < 'best_fit'`"
then
	echo shar: error transmitting "'best_fit'" '(should have been 789 characters)'
fi
fi
if test -f 'graph.h'
then
	echo shar: will not over-write existing file "'graph.h'"
else
echo extracting "'graph.h'"
sed 's/^X//' >graph.h <<'SHAR_EOF'
X/*
X * Copyright (C) 1986   Alan Kent
X *
X * Permission is granted to freely distribute part or
X * all of this code as long as it is not for profit
X * and this message is retained in the code.
X *
X * No resposibility is taken for any damage or incorect
X * results this program generates.
X * 
X */
X
X
X#define MAX_GRAPHS 30
X
Xtypedef struct table_st {
X    int size;
X    double *data;
X    struct table_st *next;
X} table_st;
X
Xtypedef struct tnode_st {
X    int operator;
X    struct tnode_st *left , *right;
X    struct table_st *table;
X    struct expr_list_st *expr_list;
X    struct attr_st *expr;
X    char *ident;
X    double value;
X    struct range_st *range;
X    struct int_st *interval;
X    int func;
X    struct parm_st *parm_list;
X    struct trow_st *const_table;
X} tnode_st;
X
Xtypedef struct attr_st {
X    double value;
X    int node_type;
X    struct attr_st *left , *right;
X    char *ident;
X    struct parm_st *parm_list;
X} attr_st;
X
Xtypedef struct expr_list_st {
X    struct attr_st *expr;
X    struct expr_list_st *next;
X} expr_list_st;
X
Xtypedef struct range_st {
X    double min , max;
X} range_st;
X
Xtypedef struct int_st {
X#define ISIZE	0
X#define INUMINT	1
X    int int_type;
X    double value;
X} int_st;
X
Xtypedef struct graph_st {
X    int line_type;	/* NO,DOTTED,SOLID,LONGDASHED,SHORTDASHED,DOTDASHED */
X    int point_type;	/* A mask MSK_TRIANGLE etc */
X#define MSK_CIRCLE	0x01
X#define MSK_TRIANGLE	0x02
X#define MSK_SQUARE	0x04
X#define MSK_CROSS	0x08
X#define MSK_PLUS	0x10
X    int cumulative;	/* true/false */
X    char *label;
X    char *legend;	/* NULL means dont put in legend */
X    table_st *table;
X} graph_st;
X
Xtypedef struct axis_st {
X    char *format;	/* printf format for numbering axis */
X    char *user_format;	/* user defined format */
X    char *label;
X    int scale;		/* AUTO,NO */
X    int frame;		/* FRAME,GRID,OUTLINE,NO */
X    int linear;		/* LOG,LINEAR */
X    int auto_tick_size;	/* true/false */
X    range_st range;
X    int_st *interval;
X    double tick_size;
X    double power_10;
X} axis_st;
X
Xtypedef struct parm_st {
X    int parm_type;
X    char *ident;
X    tnode_st *tab_expr;
X    attr_st *expr;
X    struct parm_st *next;
X} parm_st;
X
Xtypedef struct tcol_st {
X    struct attr_st *expr;
X    struct tcol_st *next;
X} tcol_st;
X
Xtypedef struct trow_st {
X    struct tcol_st *cols;
X    struct trow_st *next;
X} trow_st;
X
SHAR_EOF
if test 2321 -ne "`wc -c < 'graph.h'`"
then
	echo shar: error transmitting "'graph.h'" '(should have been 2321 characters)'
fi
fi
if test -f 'yacc.y'
then
	echo shar: will not over-write existing file "'yacc.y'"
else
echo extracting "'yacc.y'"
sed 's/^X//' >yacc.y <<'SHAR_EOF'
X/*
X * Copyright (C) 1986   Alan Kent
X *
X * Permission is granted to freely distribute part or
X * all of this code as long as it is not for profit
X * and this message is retained in the code.
X *
X * No resposibility is taken for any damage or incorect
X * results this program generates.
X * 
X */
X
X
X/* Declaration section */
X
X%{
X
X#include <stdio.h>
X#include <math.h>
X#include "graph.h"
X
X#define LARGE_INT	16000
X
Xextern char *new ();
Xextern double floor ();
Xextern table_st *read_table ();
Xextern table_st *load_table ();
Xextern attr_st *attr_node ();
Xextern double eval ();
Xextern table_st *eval_tab ();
Xextern tnode_st *tab_node ();
Xextern parm_st *parm_node ();
Xextern char *ctime ();
X
Xextern int gargc;
Xextern char **gargv;
Xextern axis_st xaxis;
Xextern axis_st yaxis;
Xextern graph_st graph[];
Xextern int num_graphs;
Xextern char *graph_label;
Xextern int horiz_legend;
Xextern int vert_legend;
X
X
Xstatic axis_st *paxis;
X
X
X%}
X
X%union {
X    attr_st *attr_expr;
X    table_st *table;
X    tnode_st *tnode;
X    double number;
X    char *string;
X    expr_list_st *expr_list;
X    range_st *range;
X    int_st *interval;
X    int integer;
X    parm_st *parm;
X    tcol_st *tcol;
X    trow_st *trow;
X}
X
X%left JOIN SORT GROUP CUMULATE
X%left BY
X%left APPEND
X%left ADJACENT
X%left '[' WHERE
X%left <string> STRING IDENT TAB_IDENT VAR_IDENT FTAB_IDENT FVAR_IDENT
X%left <number> NUMBER 
X
X%type <string> string_expr string_sub_expr
X%type <trow> table_line_list
X%type <tcol> table_line
X%type <number> constant
X%type <attr_expr> expr or_op and_op rel_op add_op mul_op primary attr
X%type <expr_list> expr_list
X%type <range> range
X%type <interval> interval
X%type <string> group_function 
X%type <tnode> table
X%type <parm> dec_parm_list dec_parm call_parm_list call_parm
X
X%start program
X
X%token READ SHELL PRINT TABLE XAXIS YAXIS PLOT GRAPH VALUE INCLUDE
X%token APPEND SORT JOIN GROUP BY WHERE INTO INTERVAL SIZE FROM TO WITH
X%token PERFORMING ADJACENT DATE PARAMETER SAVE LOAD AS
X%token LBRACKET RBRACKET LPAREN RPAREN
X%token EQ NE GE GT LE LT PLUS MINUS DIVIDE MULTIPLY AND OR NOT NEG
X%token IDENT TAB_IDENT VAR_IDENT STRING ATTR NUMBER FTAB_IDENT FVAR_IDENT
X%token TICK LABEL FORMAT AUTO NO SCALE FRAME GRID OUTLINE AXIS 
X%token LOGRITHMIC LINEAR DOTTED SOLID LONGDASHED SHORTDASHED DOTDASHED LINE
X%token QUEST COLON MODULUS TAB_CONST PROJECT CUMULATE GENERATE
X%token TRIANGLE CIRCLE SQUARE CROSS POINTS POWER
X%token PUT LEGEND AT TOP MIDDLE BOTTOM LEFT CENTER RIGHT
X%token STR VAL PARMS ASSUME
X
X
X
X%%
X
X/* Rules section */
X
X
Xprogram:	statement_list
X			{
X			    if ( num_graphs > 0 ) {
X				dump_graphs ();
X				reset_graphs ();
X			    }
X			}
X	;
X
Xstatement_list:	
X	|	statement statement_list
X	;
X
X
Xstatement:	';'
X	|	IDENT EQ table
X			{ tab_declare ( $1 , eval_tab ( $3 ) ); }
X	|	TAB_IDENT EQ table
X			{ tab_declare ( $1 , eval_tab ( $3 ) ); }
X	|	IDENT EQ expr
X			{ var_declare ( $1 , eval ( NULL , 0 , $3 ) ); }
X	|	VAR_IDENT EQ expr
X			{ var_declare ( $1 , eval ( NULL , 0 , $3 ) ); }
X	|	IDENT '(' dec_parm_list ')' EQ expr
X			{ fun_declare ( $1 , $3 , $6 , NULL ); }
X	|	IDENT '(' dec_parm_list ')' EQ table
X			{ fun_declare ( $1 , $3 , NULL , $6 ); }
X	|	INCLUDE string_expr
X			{ include ( $2 ); release ( $2 ); }
X	|	SHELL string_expr
X			{ system ( $2 ); /* use >file */ release ( $2 ); }
X	|	SAVE table AS string_expr
X			{ save_table ( $4 , eval_tab ( $2 ) ); release ( $4 ); }
X	|	PRINT GT GT string_expr table
X			{ print_table ( eval_tab ( $5 ) , $4 , "a" ); release ( $4 ); }
X	|	PRINT GT string_expr table
X			{ print_table ( eval_tab ( $4 ) , $3 , "w" ); release ( $3 ); }
X	|	PRINT table
X			{ print_table ( eval_tab ( $2 ) , NULL , NULL ); }
X	|	PRINT GT GT string_expr expr
X			{ print_expr ( eval ( NULL , 0 , $5 ) , $4 , "a" ); release ( $4 ); }
X	|	PRINT GT string_expr expr
X			{ print_expr ( eval ( NULL , 0 , $4 ) , $3 , "w" ); release ( $3 ); }
X	|	PRINT expr
X			{ print_expr ( eval ( NULL , 0 , $2 ) , NULL , NULL ); }
X	|	PRINT GT GT string_expr string_expr
X			{ print_string ( $5 , $4 , "a" ); release ( $4 ); release ( $5 ); }
X	|	PRINT GT string_expr string_expr
X			{ print_string ( $4 , $3 , "w" ); release ( $3 ); release ( $4 ); }
X	|	PRINT string_expr
X			{ print_string ( $2 , NULL , NULL ); release ( $2 ); }
X	|	graph
X	|	XAXIS
X			{ paxis = &xaxis; }
X		axis
X	|	YAXIS
X			{ paxis = &yaxis; }
X		axis
X	|	PUT LEGEND AT pos_list
X	|	PLOT
X			{ dump_graphs (); reset_graphs (); }
X	|	ASSUME expr string_expr
X			{
X			    if ( eval ( NULL , 0 , $2 ) == 0.0 ) {
X				fprintf ( stderr , "%s\n" , $3 );
X				exit ( 1 );
X			    }
X			    release ( $3 );
X			}
X	;
X
Xpos_list:	/* empty */
X	|	pos pos_list
X	;
X
Xpos:		LEFT
X			{ horiz_legend = LEFT; }
X	|	CENTER
X			{ horiz_legend = CENTER; }
X	|	RIGHT
X			{ horiz_legend = RIGHT; }
X	|	TOP
X			{ vert_legend = TOP; }
X	|	MIDDLE
X			{ vert_legend = MIDDLE; }
X	|	BOTTOM
X			{ vert_legend = BOTTOM; }
X	;
X
Xtable	:	READ string_expr
X			{
X			    $$ = tab_node ( TAB_CONST );
X			    $$->table = read_table ( $2 , -1 , -1 );
X			    release ( $2 );
X			}
X	|	READ expr BY expr string_expr
X			{
X			    $$ = tab_node ( TAB_CONST );
X			    $$->table = read_table ( $5 ,
X				    (int)eval ( NULL , 0 , $2 ) ,
X				    (int)eval ( NULL , 0 , $4 ) );
X			    release ( $5 );
X			}
X	|	LOAD string_expr
X			{
X			    $$ = tab_node ( TAB_CONST );
X			    $$->table = load_table ( $2 );
X			    release ( $2 );
X			}
X	|	'{' table_line_list '}'
X			{
X			    $$ = tab_node ( TABLE );
X			    $$->const_table = $2;
X			}
X	|	GENERATE range interval
X			{
X			    $$ = tab_node ( GENERATE );
X			    $$->range = $2;
X			    $$->interval = $3;
X			}
X	|	table APPEND table
X			{
X			    $$ = tab_node ( APPEND );
X			    $$->left = $1;
X			    $$->right = $3;
X			}
X	|	table ADJACENT table
X			{
X			    $$ = tab_node ( ADJACENT );
X			    $$->left = $1;
X			    $$->right = $3;
X			}
X	|	table '[' expr_list ']'
X			{
X			    $$ = tab_node ( PROJECT );
X			    $$->left = $1;
X			    $$->expr_list = $3;
X			}
X	|	table WHERE expr
X			{
X			    $$ = tab_node ( WHERE );
X			    $$->left = $1;
X			    $$->expr = $3;
X			}
X	|	SORT table BY attr
X			{
X			    $$ = tab_node ( SORT );
X			    $$->left = $2;
X			    $$->expr = $4;
X			}
X	|	JOIN table ',' table BY attr ',' attr
X			{
X			    $$ = tab_node ( JOIN );
X			    $$->left = $2;
X			    $$->right = $4;
X			    $$->expr = attr_node ( JOIN , (double)0.0 , $6 , $8 );
X			}
X	|	CUMULATE table BY attr
X			{
X			    $$ = tab_node ( CUMULATE );
X			    $$->left = $2;
X			    $$->expr = $4;
X			}
X	|	GROUP table range interval PERFORMING group_function
X			{
X			    $$ = tab_node ( GROUP );
X			    $$->left = $2;
X			    $$->range = $3;
X			    $$->interval = $4;
X			    $$->ident = $6;
X			}
X	|	'(' table ')'
X			{ $$ = $2; }
X	|	IDENT
X			{ abort ( "Undefined identifier" ); }
X	|	FTAB_IDENT '(' call_parm_list ')'
X			{
X			    $$ = tab_node ( FTAB_IDENT );
X			    $$->ident = $1;
X			    $$->parm_list = $3;
X			}
X	|	TAB_IDENT
X			{ $$ = tab_node ( TAB_IDENT ); $$->ident = $1; }
X	;
X
Xgroup_function:
X		FVAR_IDENT
X			{ $$ = $1; check_group_fun ( $1 ); }
X	;
X
Xtable_line_list:	
X			{ $$ = NULL; }
X	|	'{' table_line '}' table_line_list
X			{
X			    $$ = (trow_st *) new ( sizeof ( trow_st ) );
X			    $$->cols = $2;
X			    $$->next = $4;
X			}
X	;
X
Xtable_line:	expr
X			{
X			    $$ = (tcol_st *) new ( sizeof ( tcol_st ) );
X			    $$->expr = $1;
X			    $$->next = NULL;
X			}
X	|
X		expr ',' table_line
X			{
X			    $$ = (tcol_st *) new ( sizeof ( tcol_st ) );
X			    $$->expr = $1;
X			    $$->next = $3;
X			}
X	;
X
Xattr	:	'$' primary
X			{ $$ = $2; }
X	;
X
Xexpr	:	or_op '?' or_op ':' or_op
X			{ $$ = attr_node ( QUEST , (double)0.0 , $1 ,
X			    attr_node ( COLON , (double)0.0 , $3 , $5 ) ); }
X	|	or_op
X			{ $$ = $1; }
X	;
X
Xor_op	:	and_op '|' or_op
X			{ $$ = attr_node ( OR , (double)0.0 , $1 , $3 ); }
X	|	and_op
X			{ $$ = $1; }
X	;
X
Xand_op	:	rel_op '&' and_op
X			{ $$ = attr_node ( AND , (double)0.0 , $1 , $3 ); }
X	|	rel_op
X			{ $$ = $1; }
X	;
X
Xrel_op	:	add_op EQ add_op
X			{ $$ = attr_node ( EQ , (double)0.0 , $1 , $3 ); }
X	|	add_op NE add_op
X			{ $$ = attr_node ( NE , (double)0.0 , $1 , $3 ); }
X	|	add_op LE add_op
X			{ $$ = attr_node ( LE , (double)0.0 , $1 , $3 ); }
X	|	add_op LT add_op
X			{ $$ = attr_node ( LT , (double)0.0 , $1 , $3 ); }
X	|	add_op GE add_op
X			{ $$ = attr_node ( GE , (double)0.0 , $1 , $3 ); }
X	|	add_op GT add_op
X			{ $$ = attr_node ( GT , (double)0.0 , $1 , $3 ); }
X	|	add_op
X			{ $$ = $1; }
X	;
X
Xadd_op	:	mul_op '+' add_op
X			{ $$ = attr_node ( PLUS , (double)0.0 , $1 , $3 ); }
X	|	mul_op '-' add_op
X			{ $$ = attr_node ( MINUS , (double)0.0 , $1 , $3 ); }
X	|	mul_op
X			{ $$ = $1; }
X	;
X
Xmul_op	:	primary '/' mul_op
X			{ $$ = attr_node ( DIVIDE , (double)0.0 , $1 , $3 ); }
X	|	primary '*' mul_op
X			{ $$ = attr_node ( MULTIPLY , (double)0.0 , $1 , $3 ); }
X	|	primary '%' mul_op
X			{ $$ = attr_node ( MODULUS , (double)0.0 , $1 , $3 ); }
X	|	primary
X			{ $$ = $1; }
X	;
X
Xprimary	:	'-' primary
X			{ $$ = attr_node ( NEG , (double)0.0 , $2 , NULL ); }
X	|	'!' primary
X			{ $$ = attr_node ( NOT , (double)0.0 , $2 , NULL ); }
X	|	'(' expr ')'
X			{ $$ = $2; }
X	|	attr
X			{ $$ = attr_node ( ATTR , (double)0.0 , $1 , NULL ); }
X	|	constant
X			{ $$ = attr_node ( NUMBER , $1 , NULL , NULL ); }
X	|	FVAR_IDENT '(' call_parm_list ')'
X			{
X			    $$ = attr_node ( FVAR_IDENT , (double)0.0 , NULL , NULL );
X			    $$->ident = $1;
X			    $$->parm_list = $3;
X			}
X	|	VAR_IDENT
X			{
X			    $$ = attr_node ( VAR_IDENT , (double)0.0 , NULL , NULL );
X			    $$->ident = $1;
X			}
X	;
X
Xexpr_list:	
X			{ $$ = NULL; }
X	|	expr
X			{
X			    $$ = (expr_list_st *) new ( sizeof ( expr_list_st ) );
X			    $$->expr = $1;
X			    $$->next = NULL;
X			}
X	|	expr ',' expr_list
X			{
X			    $$ = (expr_list_st *) new ( sizeof ( expr_list_st ) );
X			    $$->expr = $1;
X			    $$->next = $3;
X			}
X	;
X
Xinterval:	WITH expr INTERVAL 
X			{
X			    $$ = (int_st *) new ( sizeof ( int_st ) );
X			    $$->int_type = INUMINT;
X			    $$->value = eval ( NULL , 0 , $2 );
X			}
X	|	INTERVAL SIZE expr 
X			{
X			    $$ = (int_st *) new ( sizeof ( int_st ) );
X			    $$->int_type = ISIZE;
X			    $$->value = eval ( NULL , 0 , $3 );
X			}
X	;
X
Xrange	:	/* default min to max */
X			{ $$ = NULL; }
X	|	FROM expr TO expr
X			{
X			    $$ = (range_st *) new ( sizeof ( range_st ) );
X			    $$->min = eval ( NULL , 0 , $2 );
X			    $$->max = eval ( NULL , 0 , $4 );
X			}
X	;
X
Xconstant:	NUMBER
X			{ $$ = $1; }
X	|	PARMS
X			{ $$ = gargc - 2; }
X	|	VAL '(' string_expr ')'
X			{
X			    double num;
X
X			    if ( scan_value ( $3 , &num ) == NULL ) {
X				warn ( "Illegal number in VAL()" );
X				num = 0.0;
X			    }
X			    release ( $3 );
X			    $$ = num;
X			}
X	;
X
Xaxis	:	axis_option_list
X	;
X
Xaxis_option_list:
X	|	axis_option axis_option_list
X	;
X
Xaxis_option:
X	/*
X	|	TICK range
X	|	AUTO TICK
X	*/
X		LABEL string_expr
X			{ paxis->label = $2; }
X	|	FORMAT string_expr
X			{ paxis->user_format = $2; }
X	|	AUTO SCALE
X			{ paxis->scale = AUTO; }
X	|	NO SCALE
X			{ paxis->scale = NO; }
X	|	TICK SIZE expr POWER expr
X			{
X			    paxis->auto_tick_size = 0;
X			    paxis->tick_size = eval ( NULL , 0 , $3 );
X			    paxis->power_10 = eval ( NULL , 0 , $5 );
X			}
X	|	TICK SIZE expr
X			{
X			    paxis->auto_tick_size = 0;
X			    paxis->tick_size = eval ( NULL , 0 , $3 );
X			    paxis->power_10 = 0.0;	/* not good */
X			}
X	|	FRAME AXIS
X			{ paxis->frame = FRAME; }
X	|	GRID AXIS
X			{ paxis->frame = GRID; }
X	|	OUTLINE AXIS
X			{ paxis->frame = OUTLINE; }
X	|	NO AXIS
X			{ paxis->frame = NO; }
X	|	LOGRITHMIC
X			{ paxis->linear = LOGRITHMIC; }
X	|	LINEAR
X			{ paxis->linear = LINEAR; }
X	|	FROM expr TO expr
X			{
X			    paxis->range.min = eval ( NULL , 0 , $2 );
X			    paxis->range.max = eval ( NULL , 0 , $4 );
X			}
X	;
X
Xgraph	:	GRAPH table
X			{
X			    if ( num_graphs >= MAX_GRAPHS )
X				abort ( "too many graphs" );
X			    graph[ num_graphs ].line_type = SOLID;
X			    graph[ num_graphs ].point_type = 0;
X			    graph[ num_graphs ].cumulative = 0;
X			    graph[ num_graphs ].label = NULL;
X			    graph[ num_graphs ].legend = NULL;
X			}
X		graph_option_list
X			{
X			    table_st *p;
X
X			    p = eval_tab ( $2 );
X			    if ( p == NULL || p->next == NULL )
X				abort ( "GRAPH requires two column tables to plot" );
X			    if ( p->size < 1 )
X				warn ( "No data in graph to plot" );
X			    else
X				graph[ num_graphs++ ].table = p;
X			}
X	|	GRAPH LABEL string_expr
X			{
X			    graph_label = $3;
X			}
X	;
X
Xgraph_option_list:
X	|	graph_option_list graph_option
X	;
X
Xgraph_option:	DOTTED LINE
X			{ graph[ num_graphs ].line_type = DOTTED; }
X	|	SOLID LINE
X			{ graph[ num_graphs ].line_type = SOLID; }
X	|	SHORTDASHED LINE
X			{ graph[ num_graphs ].line_type = SHORTDASHED; }
X	|	LONGDASHED LINE
X			{ graph[ num_graphs ].line_type = LONGDASHED; }
X	|	DOTDASHED LINE
X			{ graph[ num_graphs ].line_type = DOTDASHED; }
X	|	NO LINE
X			{ graph[ num_graphs ].line_type = NO; }
X	|	NO POINTS
X			{ graph[ num_graphs ].point_type = 0; }
X	|	TRIANGLE POINTS
X			{ graph[ num_graphs ].point_type |= MSK_TRIANGLE; }
X	|	CIRCLE POINTS
X			{ graph[ num_graphs ].point_type |= MSK_CIRCLE; }
X	|	SQUARE POINTS
X			{ graph[ num_graphs ].point_type |= MSK_SQUARE; }
X	|	CROSS POINTS
X			{ graph[ num_graphs ].point_type |= MSK_CROSS; }
X	|	PLUS POINTS
X			{ graph[ num_graphs ].point_type |= MSK_PLUS; }
X	|	LABEL string_expr
X			{ graph[ num_graphs ].label = $2; }
X	|	LEGEND string_expr
X			{ graph[ num_graphs ].legend = $2; }
X	;
X
Xdec_parm_list:	
X			{ $$ = NULL; }
X	|
X		dec_parm
X			{ $$ = $1; $$->next = NULL; }
X	|	dec_parm ',' dec_parm_list
X			{ $$ = $1; $$->next = $3; }
X	;
X
Xdec_parm:	VAR_IDENT
X			{ $$ = parm_node ( $1 , VALUE ); }
X	|	TAB_IDENT
X			{ $$ = parm_node ( $1 , TABLE ); }
X	;
X
Xcall_parm_list:	
X			{ $$ = NULL; }
X	|
X		call_parm
X			{ $$ = $1; $$->next = NULL; }
X	|	call_parm ',' call_parm_list
X			{ $$ = $1; $$->next = $3; }
X	;
X
Xcall_parm:	expr
X			{ $$ = parm_node ( NULL , VALUE ); $$->expr = $1; }
X	|	table
X			{ $$ = parm_node ( NULL , TABLE ); $$->tab_expr = $1; }
X	;
X
Xstring_expr:	string_sub_expr
X			{
X			    $$ = $1;
X			}
X	|	string_sub_expr '+' string_expr
X			{
X			    char *p;
X
X			    p = new ( strlen ( $1 ) + strlen ( $3 ) + 1 );
X			    strcpy ( p , $1 );
X			    strcat ( p , $3 );
X			    release ( $1 );
X			    release ( $3 );
X			    $$ = p;
X			}
X	;
X
Xstring_sub_expr: STRING
X			{
X			    char *p;
X
X			    p = new ( strlen ( $1 ) + 1 );
X			    strcpy ( p , $1 );
X			    $$ = p;
X			}
X	|	PARAMETER constant
X			{
X			    char *p;
X			    int i;
X
X			    i = $2;
X			    if ( i > gargc - 2  ||  i < 1 )
X				abort ( "undefined parameter" );
X			    p = new ( strlen ( gargv[ i + 1 ] ) + 1 );
X			    strcpy ( p , gargv[ i + 1 ] );
X			    $$ = p;
X			}
X	|	STR '(' string_expr ',' expr ')'
X			{
X			    char *p;
X			    char buf[ 100 ];
X
X			    sprintf ( buf , $3 , eval ( NULL , 0 , $5 ) );
X			    p = new ( strlen ( buf ) + 1 );
X			    strcpy ( p , buf );
X			    release ( $3 );
X			    $$ = p;
X			}
X	|	STR '(' expr ')'
X			{
X			    char *p;
X			    char buf[ 30 ];
X
X			    sprintf ( buf , "%g" , eval ( NULL , 0 , $3 ) );
X			    p = new ( strlen ( buf ) + 1 );
X			    strcpy ( p , buf );
X			    $$ = p;
X			}
X	|	DATE
X			{
X			    long clock;
X			    char *p , *str;
X			    int i;
X
X			    p = new ( 32 );
X			    time ( &clock );
X			    str = ctime ( &clock );
X			    for ( i = 0; str[i] != '\n' && str[i] != '\0'; i++ )
X				p[i] = str[i];
X			    p[i] = '\0';
X			    $$ = p;
X			}
X	;
X
X%%
X
X/* Subroutine section */
X
SHAR_EOF
if test 15119 -ne "`wc -c < 'yacc.y'`"
then
	echo shar: error transmitting "'yacc.y'" '(should have been 15119 characters)'
fi
fi
# end of shell archive
exit 0