JUEXPR(3f)
Version 3.0.0:Use at your own risk.
SYNOPSIS
The JUEXPR(3f) library evaluates
CHARACTER strings containing FORTRAN-like expressions and returns
numeric and string values.
It supports named variables and most ANSI FORTRAN 77 functions.
- juexpr.f is the library code
- simple.f is a very simple example that calls the convenience function RNUM0(3f)
- mycalc.f is an example that calls the main routine JUEXPR(3f)
f77 juexpr.f mycalc.f -o mycalc
Using the JUEXPR(3f) interface it is easy to make free-format and
order-independent input interfaces where values can be expressions
and variable names instead of simple numbers.
It has been used in lieu of NAMELIST in many
programs; and as a basic building block in creating language-like
input formats.
There are much more powerful and modern expression parsers available
now-adays, but this routine has proven to be very portable and
simple to maintain; and is still useful.
The library is basically written in FORTRAN 77; it uses no recursion and no
dynamically allocated variables and is very portable. It does
not support user-defined arrays, handling of complex numbers,
or interpreted user-defined functions.
The parser is not intended to be used to generate a large number
(hundreds of thousands) of calculations. It was written to be
portable, not optimal. The library has been used on many operating
systems, including NOS, NOS/VE, VMS, COS, Aegis, UNICOS, SunOS,
HP-UX, IRIX64, AIX, Solaris, Ultrix, Tru64, OpenBSD, Linux, CygWin,
...
Perhaps the simplest example is a loop that reads input lines
and calls the calculator using the convenience function RNUM0(3f):
PROGRAM simple
CHARACTER*255 line
REAL*8 rnum0
WRITE(*,*)'Enter numeric expressions'
1 CONTINUE
READ(*,'(A)',ERR=1,IOSTAT=ios,END=999) line
WRITE(*,*)rnum0(line)
GOTO 1
999 CONTINUE
END
A run of the example program might look like this:
300+4/3-1
300.33334
sin(d2r(90))
1.
f2c(210)
98.888885
a=10
10.
b=20
20.
a/b
0.5
?+a+b
30.5
DESCRIPTION
A summary of the syntax rules for the expressions follows:
- The hierarchy of operations is the same as that of
FORTRAN 77 except that adjacent exponents are done from left
to right, not right to left [i.e. in FORTRAN
3**2**4=3**(2**4), in JUEXPR 3**2**4=(3**2)**4]; and +-
strings are resolved to a single sign (that is, 3+ -4 is
acceptable instead of 3+(-4)).
- The JUEXPR routine provides almost all the ANSI
mathematical functions available in FORTRAN 77, as well as
access to common extensions and custom routines.
- Embedded non-quoted blanks are ignored during the processing of a calculation.
- All numeric values are treated as FORTRAN type REAL*8 variables.
- Input lines should not be longer than 255 characters.
- There are three ways to store results to be used in future calculations:
- Variable names
- The current-value
- The x and y arrays
Each of the types will be discussed separately.
VARIABLE NAMES
Names must be 1 to 20 characters long, and are case-sensitive. Up
to 2345 names are permitted. Numeric variable names should be
composed of the letters a-z and underscores and numbers. String
variables are similar but start with a dollar sign($). Names must
not end in a "digit-E" combination. For example:
A=sin(3.1416/2)
big=200.333E200
$name="Thomas Jefferson"
Variables may be defined by equating them to an expression.
To define or redefine a variable called FRED, simply enter:
> FRED=300*4/500
The last value assigned to a variable will be used to
evaluate the expression on the left of the equals sign when this
expression redefines the variable. For example:
> A=2
2
> A
2
> A=A+A
4
> A=A+A
8
To allow FORTRAN-type E-format numeric entry and yet not
cause the calculator routine to do an excessive amount of checking,
a variable name ending in the letter E must not have a digit
(012345789) in front of that ending E. Attempting to define such a
variable name will produce an error. This limitation prevents the
calculator from becoming confused by whether 12E+3 is a variable
called 12E plus 3 or the exponential number 12E3=12000.
CURRENT VALUE
The variable name '?' is automatically set by the program to
contain the last calculated value. This current-value
register may be used like any variable or number. It is 0 at
program initialization. Example:
> (300+500)
800
> (1/4)*?
200
> ?+?
400
THE X AND Y ARRAYS
Two arrays called X and Y are available that can contain up
to 3333 values each. The arrays are originally initialized
to all zeros. To set values in the arrays, use the xstore (or
ystore) command. The format of the commands is
xstore(start,ex1,ex2,ex3)
ystore(start,ex1,ex2,ex3)
where start=array address to start storing at
and ex(i) are expressions.
The current value is assigned the last value stored.
In addition there are similar string arrays and functions
that can hold up to 50 255-character strings:
- $xstore(), $ystore()
- $x() ,$y()
For example, to store into the locations 10,11,and 12
the values 1/10,2/10, and 3/10, the following could be
entered:
xstore( 10 , 1/10 , 2/20 , 3/10 )
^
!
*-------Start storing evaluated expressions sequentially,
beginning at x(10).
REFERENCING AN ARRAY VALUE
The values stored into the arrays may be referenced by
subscript. For example:
> xstore(1,10,20,30)
30
> fred=x(1)+x(2)+x(3)
60
NOTES:
- x and y array values cannot be used on the left of
equal signs.
x(10)=5 # IS ILLEGAL
- The current value is set to the value of the last
expression by the xstore and ystore commands
AVAILABLE FUNCTIONS
conversion functions
- r2d(arg) - converts from radians to degrees
- d2r(arg) - converts from degrees to radians
- f2c() - convert Fahrenheit to Celcius
- c2f() - convert Celcius to Fahrenheit
logical functions
- ge(val1,val2) - return TRUE if VAL1 is greater than or equal to VAL2, else return FALSE
- gt(val1,val2) - return TRUE if VAL1 is greater than to VAL2, else return FALSE
- eq(val1,val2) - return TRUE if VAL1 is equal to VAL2, else return FALSE
- le(val1,val2) - return TRUE if VAL1 is less than or equal to VAL2, else return FALSE
- lt(val1,val2) - return TRUE if VAL1 is less than VAL2, else return FALSE
- ne(val1,val2) - return TRUE if VAL1 is not equal to VAL2, else return FALSE
- if(expression,val1,val2) - If expression is TRUE, return VAL1 else return VAL2
For example:
a=if(ge(b,c),a,d)
means return a if b is greater than or equal to c else return d.
lexical logical functions
- lge($str1,$str2) - return TRUE if $STR1 is lexically greater than or equal to $STR2, else return FALSE
- lgt($str1,$str2) - return TRUE if $STR1 is lexically greater than to $STR2, else return FALSE
- leq($str1,$strN) - return TRUE if $STR1 is lexically equal to any of the other strings, else return FALSE
- lle($str1,$str2) - return TRUE if $STR1 is lexically less than or equal to $STR2, else return FALSE
- llt($str1,$str2) - return TRUE if $STR1 is lexically less than $STR2, else return FALSE
- lne($str1,$strN) - return TRUE if $STR1 is not equal to all following strings.
- $if(expression,$str1,$str2) - If expression is TRUE, return $STR1 else return $STR2
miscellaneous functions
- ceil(val1) - ceil() returns the least integral value greater than or equal to VAL1.
- floor(val1) - floor() returns the greatest integral value less than or equal to VAL1.
- in(val1,val2,val3) - returns TRUE if VAL1 is between VAL2 and VAL3 else returns FALSE
String-related
- $char(v1,v2,....) - return characters indicated by numeric ADE (ASCII decimal equivalent) values passed.
- $(ex,ex,ex,...) or $str(ex,ex,ex,...) - generate a string from a series of strings and values
- str(ex,ex,ex,...) - same as $str() but convert resulting string to a number IF the string is a simple numeric value
- $substr(string,i,j) - return a string that is columns i thru j of the input string (first character is called column 1).
- ichar($char) - return the ADE (ASCII Decimal Equivalent) value of a letter
- index($str1,$str2) - return column number where $str2 begins in $str1 or zero(0).
- len($str1) - return the length of the string
ANSI FORTRAN INTRINSIC MATH FUNCTIONS
The common FORTRAN 77 intrinsic functions are supported.
FORTRAN Functions
abs | acos | aint | anint | asin |
atan | atan2 | cos | cosh | dim |
exp | frac | idnint | int | log |
log10 | max | min | mod | nint |
real | sign | sin | sinh | sqrt |
tan | tanh |
A full description of the ANSI FORTRAN Functions is available in The
The calculator routine supports many ANSI-standard functions.
The following material briefly describes each of these routines.
For exact details on the bounds values may assume and for other
details relating to these routines, a machine-specific FORTRAN
manual is advised as reference. These descriptions are placed
here as a guide.
The intrinsic functions are listed in alphabetical order along
with a brief description. For specific functions, the argument
type is shown in parentheses.
ANSI FORTRAN FUNCTIONS ALPHABETIZED
- ABS - absolute value
- ACOS - arccosine in radians
- AINT - truncation
- ANINT - nearest whole number
- ASIN - arcsine in radians
- ATAN - arctangent in radians
- ATAN2 - arctangent in radians. Both arguments must not be 0
- COS - cosine in radians
- COSH - hyperbolic cosine
- DIM - positive difference
- =arg1-arg2 if arg1 > arg2
- =0 if arg1 <= arg2
- EXP - exponential
- INT - type conversion (value returned in REAL storage)
- LOG - natural logarithm
- LOG10 - common logarithm
- MAX - largest value (50 instead of 500 parameters allowed)
- MIN - smallest value (50 instead of 500 parameters allowed)
- MOD - remainder of arg1/arg2 =arg1-(INT(arg1/arg2)*arg2) if arg2=0 result is undefined
- NINT - nearest integer (value returned in REAL storage)
- REAL - type conversion (real)
- SIGN - sign transfer
- = |arg1| if arg2 >=0
- = -|arg1| if arg2 < 0
- SIN - sine in radians
- SINH - hyperbolic sine in radians
- SQRT - square root
- TAN - tangent in radians
- TANH - hyperbolic arctangent in radians
ANSI FORTRAN FUNCTIONS GROUPED BY FUNCTIONS
- Arc or anti-trigonometric functions
-
- ACOS - arccosine in radians
- ASIN - arcsine in radians
- ATAN - arctangent in radians
- ATAN2 - arctangent in radians
- Trigonometric functions
-
- COS - cosine in radians
- SIN - sine in radians
- TAN - tangent in radians
- Hyperbolic trigonometric functions
-
- COSH - hyperbolic cosine
- SINH - hyperbolic sine in radians
- TANH - hyperbolic arctangent in radians
- Powers and logarithms
-
- EXP - exponential
- LOG - natural logarithm
- LOG10 - common logarithm
- SQRT - square root
- Maximum/Minimum
-
- MAX - largest value (50 instead of 500 parameters allowed)
- MIN - smallest value (50 instead of 500 parameters allowed)
- Directly effecting sign of value
-
- ABS - absolute value
- SIGN - sign transfer
- Converting to a whole number
-
- INT - type conversion (value returned in REAL storage)
- AINT - truncation
- ANINT - nearest whole number
- NINT - nearest integer (value returned in REAL storage)
- Miscellaneous
-
- DIM - positive difference
- MOD - remainder of arg1/arg2
- REAL - type conversion (real)
- ABS
- ABS(arg) is a generic function that returns the absolute
value of its argument. The result of ABS(real-arg) is
real.
- ACOS
- ACOS(arg) is a generic function that returns the arccosine
of its argument in radians. The result of ACOS(real-arg) is
real.
- AINT
- AINT(arg) is a generic function that returns a whole number
after truncation of its argument. The result of AINT(real-arg)
is real. The result is 0 if |arg| < 1. The result is the
largest integer with the same sign as arg that does not exceed
the magnitude of arg if |arg| >= 1.
- ANINT
- ANINT(arg) is a generic function that returns the nearest
whole number of its argument. The result of ANINT(real-arg) is
real.
- ASIN
- ASIN(arg) is a generic function that returns the arcsine of
its argument in radians. The result of ASIN(real-arg) is
real.
- ATAN
- ATAN(arg) is a generic function that returns the arctangent
of its argument in radians. The result of ATAN(real-arg) is
real.
- ATAN2
- ATAN2(arg1, arg2) is a generic function that returns the
arctangent of its argument in radians. The result of
ATAN2(real-arg1, real-arg2) is real. The arguments must not
both be 0.
- COS
- COS(arg) is a generic function that returns the cosine of
its argument in radians. The result of COS(real-arg) is
real.
- COSH
- COSH(arg) is a generic function that returns the hyperbolic
cosine of its argument. The result of COSH(real-arg) is
real.
- DIM
- DIM(arg1, arg2) is a generic function that returns the
positive difference of its arguments. The result of
DIM(real-arg1, real-arg2) is real. The result is arg1-arg2 if
arg1 > arg2, and the result is 0 if arg1 <= arg2.
- EXP
- EXP(arg) is a generic function that returns the exponential
of its argument. The result of EXP(real-arg) is real.
- INT
- INT(arg) is a generic function that converts its argument
to integer type. The result of INT(real-arg) is zero if
|real-arg| < 1. The result is the largest integer with the
same sign as real-arg that does not exceed the magnitude of
real-arg if |real-arg| >= 1.
- LOG
- LOG(arg) is a generic function that returns the natural
logarithm (base e) of its argument. The result of LOG(real-arg)
is real.
- LOG10
- LOG10(arg) is a generic function that returns the common
logarithm (base 10) of its argument. The result of
LOG10(real-arg) is real.
- MAX
- MAX(arg1, arg2 [,..., arg500]) is a generic function that
returns the largest value in its argument list. The result of
MAX(real-arg1, real-arg2 [,..., real-arg500]) is real.
- MIN
- MIN(arg1, arg2 [,..., arg500]) is a generic function that
returns the smallest value in its argument list. is integer.
The result of MIN(real-arg1, real-arg2 [,..., real-arg500]) is
real.
- MOD
- MOD(arg1, arg2) is a generic function that returns the
remainder of arg1 divided by arg2. The result of MOD(real-arg1,
real-arg2) is real. The result is arg1 - (INT(arg1/arg2)*arg2).
If arg2 = 0, the result is undefined. Arg1 and arg2 must not
exceed 2**48-1.
- NINT
- NINT(arg) is a generic function that returns the integer
that is nearest to its argument. The result of NINT(real-arg)
is integer. If arg >= 0, the result is (INT(arg+.5)). If arg
< 0, the result is (INT(arg-.5)).
- REAL
- REAL(arg) is a generic function that performs type
conversion on its argument. The result of REAL(real-arg) is
real.
- SIGN
- SIGN(arg1, arg2) is a generic function that returns a value
after a sign transfer. The result of SIGN(real-arg1, real-arg2)
is real. The result is |arg1| if arg2 >= 0. The result is
-|arg1| if arg2 < 0.
- SIN
- SIN(arg) is a generic function that returns the sine of its
argument in radians. The result of SIN(real-arg) is real.
- SINH
- SINH(arg) is a generic function that returns the hyperbolic
sine of its argument in radians. The result of SINH(real-arg)
is real.
- SQRT
- SQRT(arg) is a generic function that returns the principal
square root of its argument. The result of SQRT(real-arg) is
real.
- TAN
- TAN(arg) is a generic function that returns the tangent of
its argument in radians.
- TANH
- TANH(arg) is a generic function that returns the hyperbolic
tangent of its argument in radians.
MISCELLANEOUS COMMANDS
-
Displaying variable values: dump
- The current value and all defined variable names are
displayed via the dump command.
- Listing Available Functions:
funcs
- A display of all available functions can be obtained
when executing JUEXPR by entering the command 'funcs'. No
descriptions are provided.
Known bugs and limitations
- There is no type-checking to make sure that numeric values are not used where strings are required and vice-versa. That is,
sin("hello there")
can cause problems.
- If you are looking for another type of parser, some useful keywords would be "Shunting yard algorithm" for converting infix notation to postfix notation; "bytecode interpreters" which can be used to rapidly repeat a series of operations on a vector;...
Programmer Notes
- Changing the number of variable names.
- Adding a new function directly to the calculator code.
- Points to ponder
- Using C or Fortran 90 it would be easier to make a version
that uses recursion and allocatable arrays to handle array
syntax and to run more efficiently by avoiding almost all
string manipulations
The calculator library is composed of the following routines:
-
JUEXPR is the main calculator
interface. All other routines are internal routines that
the user does not (generally) call directly or routines
that simplify making the most common forms of calls to
JUEXPR.
Internal routines:
- JUPARS crack out the parenthesis and pass each subexpression to JFUNS or JUARGS
- JUFUNS processes function call strings of form "fun(a1,a2,a3,...pi)"
- STUFFTOK stores a string being returned by a function into the string dictionary
- JUARGS separates a list of expressions that do not contain parenthesis and passes them to JUCALS
- JUCALS resolve a single expression into a value and restring it
- JUPOWS process power expressions "a**b" in expressions from JUCALS
- JUFACS process multiplies and divides in expressions from JUCALS
- JUATOR return a REAL value from a numeric string
- JURTOA return a numeric string from a REAL value
- JUSQES removes spaces and tokenizes strings before the calculator starts to crack the input expression.
- JUBOUS find where a new variable name should be stored or where an old name and it's associated value can be found
- JUADDR Add a new variable name and it's value to the dictionary.
- JUADDS add a new string variable and it's value to the string dictionary
- JUATOA given a string variable name return it's value
- JUFIND given a function name find the line JUFUNS() should go to
- STUFF directly store a value into calculator variable name table. This routine can be directly called when need warrants.
- STUFFA directly store a string into calculator variable name table This routine can be directly called when need warrants.
- BLOCKDATA JUINIT The BLOCKDATA defines the commons
used to hold the X,Y,$X $Y, ... arrays and the
dictionaries of variable names and string names and
their associated values.
- EXPRESSION returns the results of evaluating an expression
but in addition it displays error messages and other
information. It calls JUEXPR to evaluate the
expression.
The number of variable names permitted is
controlled by the line
parameter(ic=2345)
Change all occurrences in juexpr.f to increase this value.
The number of values in the X() and Y() arrays is similarly
controlled by
parameter(ixy=3333)
and the number of strings in the $X() and $Y() arrays is controlled by
parameter(ixy=50)
To add a new function
directly to the calculator go into JUFUNS() and increase the
number of branches in the large computed GOTO. For example, to
add the new functions NEW and $NEW make a change like this:
BEFORE:
if(i.gt.0.and.i.lt.35)then
goto (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,
& 21,22,23,24,25,26,27,28,29,30,31,32,33,34) i
elseif(i.gt.0.and.i.lt.70)then
goto (35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,
& 54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69) i-34
endif
AFTER:
if(i.gt.0.and.i.lt.35)then
goto (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,
& 21,22,23,24,25,26,27,28,29,30,31,32,33,34) i
elseif(i.gt.0.and.i.lt.70)then
goto (35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,
& 54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69) i-34
elseif(i.gt.0.and.i.lt.72)then
goto (70,71)i-69
endif
Then go and add the new line numbers:
!=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
70 continue ! NEW(value) function
! numeric parameters are in args(n)
! set fval and goto 5000 to set the returning numeric value
fval=new(args(1))
goto 5000 ! go to 5000 to return a numeric value
!=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
71 continue ! $NEW(string) function
ii=args(1) ! assume argument 1 is a string, then this is
! pointer to where the string is
ctmp=values(ii) ! get the string
iend=valuer(ii) ! get the length of string
! set ctmp and goto 5002 to return a string value
ctmp=newchar(ctmp(:iend)) ! set returned value
iend=julen(ctmp) ! set significant length of returned value
! do something based on string
goto 5002 ! go to 5002 to return a string value
!=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Exactly how to extract and return character strings may seem
a little confusing. Remember there are over 70 examples in JUFUNS()
In function JUFIND() increase ICF by 2 and add new lines to
record the names of the functions, what line number to goto in
JUFUNCS(), and how many parameters the functions have. Use a -1
to suppress checking for a specific number of parameters.
do NOT alter the line that begins ixn(icf)=
BEFORE:
parameter(icf=71) ! the number of commands
:
:
! !function name !goto !number of parameters
ixn( 1)='!!!!!!!!!!!!!!!!!!!!0000000000-111111111'
ixn( 2)='acos 1 1 '
ixn( 3)='asin 2 1 '
:
:
ixn( 68)='$x 67 -1 ' !
ixn( 69)='egg 68 -1 ' !
ixn( 70)='nog 69 -1 ' !
ixn(icf)='~~~~~~~~~~~~~~~~~~~~0000000000-111111111'
AFTER:
parameter(icf=73) ! the number of commands
:
:
! !function name !goto !number of parameters
ixn( 1)='!!!!!!!!!!!!!!!!!!!!0000000000-111111111'
ixn( 2)='acos 1 1 '
ixn( 3)='asin 2 1 '
:
:
ixn( 68)='$x 67 -1 ' !
ixn( 69)='egg 68 -1 ' !
ixn( 70)='nog 69 -1 ' !
ixn( 71)='new 70 1 ' !
ixn( 72)='$new 71 1 ' !
ixn(icf)='~~~~~~~~~~~~~~~~~~~~0000000000-111111111'