fork(1) download
  1. {$mode objfpc} {$coperators+} {$modeswitch duplicatelocals}
  2. uses
  3. SysUtils, StrUtils, Math;
  4.  
  5. type
  6. Calculator = object
  7. class function Calculate(const s: string): double; static;
  8.  
  9. private type
  10. OpEnum = (NoOp, PlusOp, MinusOp, MulOp, DivOp, PowOp);
  11. const
  12. OpSyms: array[PlusOp .. High(OpEnum)] of char = '+-*/^';
  13. OpPrecedences: array[OpEnum] of int32 = (-1, 0, 0, 1, 1, 2);
  14. RightAssocOps = [PowOp];
  15. var
  16. s: string;
  17. p: SizeInt;
  18. function ScanPart(lhs: double; minPrecedence: SizeInt): double;
  19. function ScanPrimary: double;
  20. procedure SkipSpaces;
  21. procedure Fail(const what: string);
  22. function RecognizeOperator: OpEnum;
  23. end;
  24.  
  25. class function Calculator.Calculate(const s: string): double;
  26. var
  27. c: Calculator;
  28. begin
  29. c.s := s;
  30. c.p := 1;
  31. result := c.ScanPart(c.ScanPrimary, 0);
  32. if c.p <= length(s) then c.Fail('неожиданное продолжение');
  33. end;
  34.  
  35. function Calculator.ScanPart(lhs: double; minPrecedence: SizeInt): double;
  36. var
  37. op, nextOp: OpEnum;
  38. rhs: double;
  39. begin
  40. nextOp := RecognizeOperator;
  41. repeat
  42. op := nextOp;
  43. if OpPrecedences[op] < minPrecedence then exit(lhs);
  44. p += 1;
  45. rhs := ScanPrimary;
  46. nextOp := RecognizeOperator;
  47. while OpPrecedences[nextOp] + ord(nextOp in RightAssocOps) > OpPrecedences[op] do
  48. begin
  49. rhs := ScanPart(rhs, OpPrecedences[op] + ord(OpPrecedences[nextOp] > OpPrecedences[op]));
  50. nextOp := RecognizeOperator;
  51. end;
  52. case op of
  53. NoOp: ;
  54. PlusOp: lhs := lhs + rhs;
  55. MinusOp: lhs := lhs - rhs;
  56. MulOp: lhs := lhs * rhs;
  57. DivOp: lhs := lhs / rhs;
  58. PowOp: lhs := Power(lhs, rhs);
  59. end;
  60. until false;
  61. end;
  62.  
  63. function Calculator.ScanPrimary: double;
  64. var
  65. nume: SizeInt;
  66. begin
  67. SkipSpaces;
  68. if p > length(s) then Fail('неожиданный конец');
  69. if s[p] = '(' then
  70. begin
  71. p += 1;
  72. result := ScanPart(ScanPrimary(), 0);
  73. if (p > length(s)) or (s[p] <> ')') then Fail('ожидается )');
  74. p += 1;
  75. exit;
  76. end;
  77. nume := p;
  78. while (nume <= length(s)) and ((s[nume] in ['0' .. '9', '.']) or (nume = p) and (s[nume] in ['-', '+'])) do nume += 1;
  79. if TryStrToFloat(Copy(s, p, nume - p), result) then
  80. begin
  81. p := nume;
  82. exit;
  83. end;
  84. case s[p] of
  85. '+': begin p += 1; exit(ScanPrimary()); end;
  86. '-': begin p += 1; exit(-ScanPrimary()); end;
  87. else Fail('ожидается число, унарный оператор, или скобка');
  88. end;
  89. end;
  90.  
  91. procedure Calculator.SkipSpaces;
  92. begin
  93. while (p <= length(s)) and (s[p] = ' ') do p += 1;
  94. end;
  95.  
  96. procedure Calculator.Fail(const what: string);
  97. begin
  98. raise Exception.Create(StuffString(s, p, 0, '|') + ': ' + what + '.');
  99. end;
  100.  
  101. function Calculator.RecognizeOperator: OpEnum;
  102. begin
  103. SkipSpaces;
  104. if p <= length(s) then
  105. for result := PlusOp to High(OpEnum) do
  106. if OpSyms[result] = s[p] then exit;
  107. result := NoOp;
  108. end;
  109.  
  110. const
  111. Examples: array[0 .. 2] of string =
  112. (
  113. '2 + 2 * 2',
  114. '(2 + --3) * 2^2^(1 + 1)^2',
  115. '2^^^2'
  116. );
  117.  
  118. var
  119. ex: string;
  120.  
  121. begin
  122. for ex in Examples do
  123. try
  124. writeln(ex + ' = ' + FloatToStr(Calculator.Calculate(ex)));
  125. except
  126. on e: Exception do writeln(e.Message);
  127. end;
  128. end.
Success #stdin #stdout 0.01s 5304KB
stdin
Standard input is empty
stdout
2 + 2 * 2 = 6
(2 + --3) * 2^2^(1 + 1)^2 = 327680
2^|^^2: ожидается число, унарный оператор, или скобка.