home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 September
/
Simtel20_Sept92.cdr
/
msdos
/
turbopas
/
spoc88.arc
/
DCG.ARC
/
MATHEXP.PRO
< prev
next >
Wrap
Text File
|
1988-06-17
|
6KB
|
200 lines
/* Mathematical Expression parser
Barbara Clinger, 1988
This program parses a mathematical expression and returns the
value of the expression. It allows the use of ^ for exponation,
grouping using parentheses, evaluation of functions (sine,
cosine, ...). Decimals in the range from -1 to +1 must be entered
with a leading zero (i.e., 0.25). A warning is issued if negative
numbers are raised to fractional powers; the indeterminant zero
raised to the zero power stops execution of the program.
sample input: 2^3 + ( sin(2*pi/3) + 1 )^2 - ln(0.123)
*/
domains
toklist = string*
predicates
reader(string,toklist)
give_result(real,toklist,toklist)
append(toklist,toklist,toklist)
do
if_can_do(real,real,real)
is_odd_int(real)
is_even_int(real)
/* the grammar */
expr(real,toklist,toklist)
term(real,toklist,toklist)
power(real,toklist,toklist)
group(real,toklist,toklist)
number(real,toklist,toklist)
/* goal
do. */
clauses
do :-
write("When entering numbers between -1 and +1 enter"),nl,
write("a leading zero. For example 0.15"),nl,nl,
nl,write("Enter an expression: "),nl, write(">"),
readln(S),nl,nl, /* get the expression */
reader(S,List_in), /* process for expr */
expr(Info_out,List_in,Rest),!, /* parse expression */
give_result(Info_out,List_in,Rest). /* print results */
give_result(N,_,T) :-
T = [],
write("The value of the expression is ", N),nl.
give_result(_,_,T) :-
write("Cannot evaluate the expression."),nl,
write("Unevaluated remainder list is:"),nl,nl,
write(T),nl,nl.
/* THE GRAMMAR */
/* An expression takes the form of
an expression plus a term,
or an express minus a term,
or a term
*/
expr(X,L1,L2) :-
append(Left,["+"|Right],L1),
expr(V1,Left,L2),
term(V2,Right,L2),
X = V1 + V2. /* returns left value plus right value */
expr(X,L1,L2) :-
append(Left,["-"|Right],L1),
expr(V1,Left,L2),
term(V2,Right,L2),
X = V1 - V2. /* returns left value minus right value */
expr(X,L1,L2) :- term(X,L1,L2).
/* A term takes the form of
a term times a power
or a term divided by a power
or a power
*/
term(X,L1,L2) :-
append(Left,["*"|Right],L1),
term(V1,Left,L2),
power(V2,Right,L2),
X = V1 * V2. /* returns left value times right value */
term(X,L1,L2) :-
append(Left,["/"|Right],L1),
term(V1,Left,L2),
power(V2,Right,L2),
X = V1 / V2. /* returns left value divided by right */
term(X,L1,L2) :- power(X,L1,L2).
/* A power takes the form of
a group raised to a power
or a group
Not all expressions of the form X ^ Y are possible. The clause
if_can_do allows the obvious cases to be evaluated.
*/
power(X,L1,L2) :-
append(Left,["^"|Right],L1),
group(V1,Left,L2),
power(V2,Right,L2),
if_can_do(X,V1,V2). /* check for acceptable cases */
power(X,L1,L2) :- group(X,L1,L2).
/* a group takes the form of
an expression enclosed in parentheses
or a number
*/
group(X,["("|L1],L2) :-
append(Sub_expr,[")"],L1),
expr(V,Sub_expr,L2),!,
X = V. /* return the value inside the parentheses */
group(X,L1,L2) :- number(X,L1,L2).
/* a number takes the form of
a plus sign followed by a an unsigned number N
or a minus sign followed by a an unsigned number N
or sin(x), cos(x), ... , ln(x), or the number pi
or an unsigned number N
*/
number(X,["+"|T],L2) :- /* + N is the same as N */
number(X,T,L2).
number(X,["-"|T],L2) :- /* return negative of unsigned N */
number(X1,T,L2),
X = -X1.
number(X,["sin"|L1],L2) :- /* use of the sine function, must */
group(V,L1,L2), /* be of the form sin(arg) */
X = sin(V),!.
number(X,["cos"|L1],L2) :-
group(V,L1,L2),X = cos(V),!.
number(X,["tan"|L1],L2) :-
group(V,L1,L2), X = tan(V),!.
/* secant definition */
number(X,["sec"|L1],L2) :-
group(V,L1,L2),
cos(V) <> 0,
X = 1/cos(V),!.
number(_,["sec"|L1],L2) :-
group(V,L1,L2),
cos(V) = 0,
write("error in secant argument"),nl,nl,!,fail.
number(X,["arctan"|L1],L2) :-
group(V,L1,L2),X = arctan(V),!.
number(X,["exp"|L1],L2) :-
group(V,L1,L2), X = exp(V),!.
number(X,["ln"|L1],L2) :-
group(V,L1,L2), X = ln(V),!.
number(X,["pi"|T],T) :-
X = 4 * arctan(1),!.
/* the angle whose tangent is 1 is pi/4 */
Number(Num,[H|T],T) :-
str_real(H,Num),!. /* convert string to unsigned number */
reader("",[]) :- !.
reader(Str,[Tok|Rest]) :-
fronttoken(Str,Tok,Str1),
reader(Str1,Rest),!.
append([],List,List).
append([H|T],L,[H|T2]) :-
append(T,L,T2).
/* The clause if_can_do tests some cases for the evaluation of
expressions of the form V1 ^ V2
*/
if_can_do(X,V1,V2) :-
V1 > 0,!, /* positive base, all ok */
X = exp(V2 * ln(V1)).
if_can_do(X,V1,V2) :- /* 0 raised to 0 is indeterminant */
V1 = 0,V2 = 0,!,
write("*************** ERROR **************"),nl,
write("expression contains indeterminant form 0 ^ 0"),nl,
write("*************************************"),nl,nl,
X = ln(V1). /* automatic stop of program */
if_can_do(X,V1,_) :-
V1 = 0, /* 0 raised to nonzero power is 0 */
X = 0.
if_can_do(X,_,V2) :-
V2 = 0, /* any number except 0 raised to */
X = 1. /* the 0 power is 1 */
if_can_do(X,V1,V2) :- /* negative number to an odd */
is_odd_int(V2), /* integer is ok */
X = -exp(V2 * ln(abs(V1))).
if_can_do(X,V1,V2) :- /* negative number to an even */
is_even_int(V2), /* integer is ok */
X = exp(V2 * ln(abs(V1))).
/* negative number to a fractional power can swing right or wrong.
For example:
(-32)^ 0.2 (the fifth root of -32) is -2
(-1024)^0.1 (the 10th root of -1024) does not exist.*/
if_can_do(X,V1,V2) :-
X = exp(V2 * ln(abs(V1))),
write("*************** WARNING **************"),nl,
write("expression contains (",V1,") ^ ",V2),nl,
write("had to use (abs(",V1,")) ^ ",V2),nl,nl.
is_odd_int(X) :- X = round(X), (round(X) mod 2) = 1.
is_even_int(X) :- X = round(X).