{$mode objfpc} {$coperators+} {$modeswitch duplicatelocals}
uses
	SysUtils, StrUtils, Math;

type
	Calculator = object
		class function Calculate(const s: string): double; static;

	private	type
		OpEnum = (NoOp, PlusOp, MinusOp, MulOp, DivOp, PowOp);
	const
		OpSyms: array[PlusOp .. High(OpEnum)] of char = '+-*/^';
		OpPrecedences: array[OpEnum] of int32 = (-1, 0, 0, 1, 1, 2);
		RightAssocOps = [PowOp];
	var
		s: string;
		p: SizeInt;
		function ScanPart(lhs: double; minPrecedence: SizeInt): double;
		function ScanPrimary: double;
		procedure SkipSpaces;
		procedure Fail(const what: string);
		function RecognizeOperator: OpEnum;
	end;

	class function Calculator.Calculate(const s: string): double;
	var
		c: Calculator;
	begin
		c.s := s;
		c.p := 1;
		result := c.ScanPart(c.ScanPrimary, 0);
		if c.p <= length(s) then c.Fail('неожиданное продолжение');
	end;

	function Calculator.ScanPart(lhs: double; minPrecedence: SizeInt): double;
	var
		op, nextOp: OpEnum;
		rhs: double;
	begin
		nextOp := RecognizeOperator;
		repeat
			op := nextOp;
			if OpPrecedences[op] < minPrecedence then exit(lhs);
			p += 1;
			rhs := ScanPrimary;
			nextOp := RecognizeOperator;
			while OpPrecedences[nextOp] + ord(nextOp in RightAssocOps) > OpPrecedences[op] do
			begin
				rhs := ScanPart(rhs, OpPrecedences[op] + ord(OpPrecedences[nextOp] > OpPrecedences[op]));
				nextOp := RecognizeOperator;
			end;
			case op of
				NoOp: ;
				PlusOp: lhs := lhs + rhs;
				MinusOp: lhs := lhs - rhs;
				MulOp: lhs := lhs * rhs;
				DivOp: lhs := lhs / rhs;
				PowOp: lhs := Power(lhs, rhs);
			end;
		until false;
	end;

	function Calculator.ScanPrimary: double;
	var
		nume: SizeInt;
	begin
		SkipSpaces;
		if p > length(s) then Fail('неожиданный конец');
		if s[p] = '(' then
		begin
			p += 1;
			result := ScanPart(ScanPrimary(), 0);
			if (p > length(s)) or (s[p] <> ')') then Fail('ожидается )');
			p += 1;
			exit;
		end;
		nume := p;
		while (nume <= length(s)) and ((s[nume] in ['0' .. '9', '.']) or (nume = p) and (s[nume] in ['-', '+'])) do nume += 1;
		if TryStrToFloat(Copy(s, p, nume - p), result) then
		begin
			p := nume;
			exit;
		end;
		case s[p] of
			'+': begin p += 1; exit(ScanPrimary()); end;
			'-': begin p += 1; exit(-ScanPrimary()); end;
			else Fail('ожидается число, унарный оператор, или скобка');
		end;
	end;

	procedure Calculator.SkipSpaces;
	begin
		while (p <= length(s)) and (s[p] = ' ') do p += 1;
	end;

	procedure Calculator.Fail(const what: string);
	begin
		raise Exception.Create(StuffString(s, p, 0, '|') + ': ' + what + '.');
	end;

	function Calculator.RecognizeOperator: OpEnum;
	begin
		SkipSpaces;
		if p <= length(s) then
			for result := PlusOp to High(OpEnum) do
				if OpSyms[result] = s[p] then exit;
		result := NoOp;
	end;

const
	Examples: array[0 .. 2] of string =
	(
		'2 + 2 * 2',
		'(2 + --3) * 2^2^(1 + 1)^2',
		'2^^^2'
	);

var
	ex: string;

begin
	for ex in Examples do
		try
			writeln(ex + ' = ' + FloatToStr(Calculator.Calculate(ex)));
		except
			on e: Exception do writeln(e.Message);
		end;
end.