Program Calculator;

var expression : String[255];
var ans : Real;
var TextFile : Text;
var plik : String[255];
var i : Integer;


type pListItem=^ListItem;

ListItem=record
    expr : String[255];
    inv : Boolean;
    next : pListItem;
end;


procedure RemoveSpaces(var expr : String);
var i,j : Integer;
begin
    j:=1;
    for i:=1 to length(expr) do
    begin
	if (expr[i]<>' ') then
	begin
	    expr[j]:=expr[i];
	    inc(j);
	end
    end;
    expr:=copy(expr,1,j-1);
end;

procedure RemoveParentheses(var expr : String);
var i, level : Integer;
var b, c : Boolean;
begin
    b:=False;
    c:=False;
    level:=-1;
    if (expr[1]='(') and (expr[length(expr)]=')') then
    begin
	for i:=1 to length(expr) do
	begin
	    if (expr[i]='(') and (c=True) then b:=True;
	    if expr[i]=')' then c:=True;
	end;
	if b=False then expr:=copy(expr,2,length(expr)-2);
    end;
end;

procedure DisposeList(pItem : pListItem);

var CurItem, temp : pListItem;

begin
    CurItem:=pItem;
    while CurItem<>nil do
    begin
	temp:=CurItem^.next;
	Dispose(CurItem);
	CurItem:=temp;
    end;
end;

function eval(expr : String) : Real;

var i, level, signs, prevsign : Integer;
pFirstItem, pCurrentItem : pListItem;
result : Real;
err : Integer;
negative : Boolean;

begin
    level:=0;
    signs:=0;
    prevsign:=1;
    pFirstItem:=nil;
    pCurrentItem:=nil;
    result:=0;
    negative:=False;
    RemoveParentheses(expr);
    if (expr[1]='-') then
    begin
	expr:='0'+expr;
    end;
    for i:=1 to (length(expr)+1) do {adding, subtracting}
    begin
	if i<= length(expr) then
	begin
	    if expr[i]='(' then inc(level);
	    if expr[i]=')' then dec(level);
	end;
	if (((expr[i]='+') or (expr[i]='-') or ((i=Length(expr)+1) and (prevsign<>1))) and (level=0)) then
	begin
	    if pFirstItem=nil then
	    begin
		new(pFirstItem);
		pCurrentItem:=pFirstItem
	    end
	    else
	    begin
		new (pCurrentItem^.next);
		pCurrentItem:=pCurrentItem^.next
	    end;
	    pCurrentItem^.expr:=copy(expr,prevsign,i-prevsign);
	    if negative=True then pCurrentItem^.inv:=True else pCurrentItem^.inv:=False;
	    if expr[i]='-' then negative:=True else negative:=False;
	    prevsign:=i+1;
	    pCurrentItem^.next:=nil;
	end;	
    end;
    if pFirstItem<>nil then
    begin
        pCurrentItem:=pFirstItem;
        while pCurrentItem<>nil do
        begin
	    if pCurrentItem^.inv=True then result:=result-eval(pCurrentItem^.expr) else result:=result+eval(pCurrentItem^.expr);
    	    pCurrentItem:=pCurrentItem^.next;
        end;
	DisposeList(pFirstItem);
	eval:=result
    end
    else {multiplying, dividing}
    begin
	level:=0;
	signs:=0;
	prevsign:=1;
	pFirstItem:=nil;
	pCurrentItem:=nil;
	result:=1;
	prevsign:=1;
	for i:=1 to (length(expr)+1) do
	begin
	    if i<= length(expr) then
	    begin
		if expr[i]='(' then inc(level);
		if expr[i]=')' then dec(level);
	    end;
	    if (((expr[i]='*') or (expr[i]='/') or ((i=Length(expr)+1) and (prevsign<>1))) and (level=0)) then
	    begin
		if pFirstItem=nil then
		begin
		    new(pFirstItem);
		    pCurrentItem:=pFirstItem
		end
		else
		begin
		    new (pCurrentItem^.next);
		    pCurrentItem:=pCurrentItem^.next
		end;
		pCurrentItem^.expr:=copy(expr,prevsign,i-prevsign);
		if negative=True then pCurrentItem^.inv:=True else pCurrentItem^.inv:=False;
		if expr[i]='/' then negative:=True else negative:=False;
		prevsign:=i+1;
		pCurrentItem^.next:=nil;
	    end;	
	end;
	if pFirstItem<>nil then
	begin
    	    pCurrentItem:=pFirstItem;
    	    while pCurrentItem<>nil do
    	    begin
		if pCurrentItem^.inv=False then result:=result*eval(pCurrentItem^.expr) else result:=result/eval(pCurrentItem^.expr);
    		pCurrentItem:=pCurrentItem^.next;
    	    end;
	    DisposeList(pFirstItem);
	    eval:=result;
	end
	else
	begin
	    if copy(expr,1,3) = 'sin' then eval:=sin(eval(copy(expr,5,length(expr)-5)))
	    else
	    if copy(expr,1,3) = 'cos' then eval:=cos(eval(copy(expr,5,length(expr)-5)))
	    else
	    if (copy(expr,1,3) = 'ANS') or (copy(expr,1,3) = 'ans') then eval:=ans
	    else
	    if copy(expr,1,3) = 'abs' then eval:=abs(eval(copy(expr,5,length(expr)-5)))
	    else
	    if copy(expr,1,3) = 'int' then eval:=int(eval(copy(expr,5,length(expr)-5)))
	    else
	    if copy(expr,1,4) = 'sqrt' then eval:=sqrt(eval(copy(expr,6,length(expr)-6)))
	    else
	    if copy(expr,1,3) = 'exp' then eval:=exp(eval(copy(expr,5,length(expr)-5)))
	    else
	    if copy(expr,1,3) = 'sqr' then eval:=sqr(eval(copy(expr,5,length(expr)-5)))
	    else
	    if copy(expr,1,2) = 'ln' then eval:=ln(eval(copy(expr,4,length(expr)-4)))
	    else
	    if copy(expr,1,6) = 'arctan' then eval:=arctan(eval(copy(expr,8,length(expr)-8)))
	    else
	    begin
		Val(expr,result,err);
		eval:=result;
	    end;
	end;
    end;
end;

BEGIN
    Write('Enter filename: ');
    readln(plik);
    if plik<>'' then
    begin
        assign(TextFile, plik);
        reset(textfile);
        i:=0;
        while not eof(TextFile) do
        begin
            inc(i);
            readln(TextFile, expression);
	    RemoveSpaces(expression);
    	    ans:=eval(expression);
	    if copy(expression,length(expression),1)<>';' then Writeln(ans:0:4);
        end;
        close(TextFile);
    end
    else
    begin
	repeat
	    Write('Enter expression: ');
	    readln(expression);
	    if expression<>'' then
	    begin
		if expression='help' then
		begin
		    Writeln('*** Calculator ***');
		    Writeln('Available operands:');
		    Writeln('+ - * /');
		    Writeln('Available functions:');
		    Writeln('sin, cos, abs, int, ln, sqrt, sqr, ln, arctan');
		    Writeln('ANS or ans returns evaluation of previous expression.');
		    Writeln('Hit Enter to quit.');
		end
		else
		begin
		    RemoveSpaces(expression);
		    ans:=eval(expression);
		    Writeln(ans:0:4);
		end;
	    end;					
	until (expression='');
    end;
END.

