ParseFormulaTest.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. <?php
  2. namespace PhpOffice\PhpSpreadsheetTests\Calculation;
  3. use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
  4. use PhpOffice\PhpSpreadsheet\NamedRange;
  5. use PhpOffice\PhpSpreadsheet\Spreadsheet;
  6. use PHPUnit\Framework\TestCase;
  7. class ParseFormulaTest extends TestCase
  8. {
  9. /**
  10. * @dataProvider providerBinaryOperations
  11. */
  12. public function testParseOperations(array $expectedStack, string $formula): void
  13. {
  14. $spreadsheet = new Spreadsheet();
  15. $spreadsheet->addNamedRange(new NamedRange('GROUP1', $spreadsheet->getActiveSheet(), 'B2:D4'));
  16. $spreadsheet->addNamedRange(new NamedRange('GROUP2', $spreadsheet->getActiveSheet(), 'D4:F6'));
  17. $parser = Calculation::getInstance($spreadsheet);
  18. $stack = $parser->parseFormula($formula);
  19. self::assertSame($expectedStack, $stack);
  20. }
  21. public function providerBinaryOperations(): array
  22. {
  23. return [
  24. 'Unary negative with Value' => [
  25. [
  26. ['type' => 'Value', 'value' => 3, 'reference' => null],
  27. ['type' => 'Unary Operator', 'value' => '~', 'reference' => null],
  28. ],
  29. '=-3',
  30. ],
  31. 'Unary negative percentage with Value' => [
  32. [
  33. ['type' => 'Value', 'value' => 3, 'reference' => null],
  34. ['type' => 'Unary Operator', 'value' => '%', 'reference' => null],
  35. ['type' => 'Unary Operator', 'value' => '~', 'reference' => null],
  36. ],
  37. '=-3%',
  38. ],
  39. 'Binary minus with Values' => [
  40. [
  41. ['type' => 'Value', 'value' => 3, 'reference' => null],
  42. ['type' => 'Value', 'value' => 4, 'reference' => null],
  43. ['type' => 'Binary Operator', 'value' => '-', 'reference' => null],
  44. ],
  45. '=3-4',
  46. ],
  47. 'Unary negative with Cell Reference' => [
  48. [
  49. ['type' => 'Cell Reference', 'value' => 'A1', 'reference' => 'A1'],
  50. ['type' => 'Unary Operator', 'value' => '~', 'reference' => null],
  51. ],
  52. '=-A1',
  53. ],
  54. 'Unary negative with FQ Cell Reference' => [
  55. [
  56. ['type' => 'Cell Reference', 'value' => "'Sheet 1'!A1", 'reference' => "'Sheet 1'!A1"],
  57. ['type' => 'Unary Operator', 'value' => '~', 'reference' => null],
  58. ],
  59. "=-'Sheet 1'!A1",
  60. ],
  61. 'Unary negative percentage with Cell Reference' => [
  62. [
  63. ['type' => 'Cell Reference', 'value' => 'A1', 'reference' => 'A1'],
  64. ['type' => 'Unary Operator', 'value' => '%', 'reference' => null],
  65. ['type' => 'Unary Operator', 'value' => '~', 'reference' => null],
  66. ],
  67. '=-A1%',
  68. ],
  69. 'Unary negative with Defined Name' => [
  70. [
  71. ['type' => 'Defined Name', 'value' => 'DEFINED_NAME', 'reference' => 'DEFINED_NAME'],
  72. ['type' => 'Unary Operator', 'value' => '~', 'reference' => null],
  73. ],
  74. '=-DEFINED_NAME',
  75. ],
  76. 'Unary negative percentage with Defined Name' => [
  77. [
  78. ['type' => 'Defined Name', 'value' => 'DEFINED_NAME', 'reference' => 'DEFINED_NAME'],
  79. ['type' => 'Unary Operator', 'value' => '%', 'reference' => null],
  80. ['type' => 'Unary Operator', 'value' => '~', 'reference' => null],
  81. ],
  82. '=-DEFINED_NAME%',
  83. ],
  84. 'Integer Numbers with Operator' => [
  85. [
  86. ['type' => 'Value', 'value' => 2, 'reference' => null],
  87. ['type' => 'Value', 'value' => 3, 'reference' => null],
  88. ['type' => 'Binary Operator', 'value' => '*', 'reference' => null],
  89. ],
  90. '=2*3',
  91. ],
  92. 'Float Numbers with Operator' => [
  93. [
  94. ['type' => 'Value', 'value' => 2.5, 'reference' => null],
  95. ['type' => 'Value', 'value' => 3.5, 'reference' => null],
  96. ['type' => 'Binary Operator', 'value' => '*', 'reference' => null],
  97. ],
  98. '=2.5*3.5',
  99. ],
  100. 'Strings with Operator' => [
  101. [
  102. ['type' => 'Value', 'value' => '"HELLO"', 'reference' => null],
  103. ['type' => 'Value', 'value' => '"WORLD"', 'reference' => null],
  104. ['type' => 'Binary Operator', 'value' => '&', 'reference' => null],
  105. ],
  106. '="HELLO"&"WORLD"',
  107. ],
  108. 'Error' => [
  109. [
  110. ['type' => 'Value', 'value' => '#DIV0!', 'reference' => null],
  111. ],
  112. '=#DIV0!',
  113. ],
  114. 'Cell Range' => [
  115. [
  116. ['type' => 'Cell Reference', 'value' => 'A1', 'reference' => 'A1'],
  117. ['type' => 'Cell Reference', 'value' => 'C3', 'reference' => 'C3'],
  118. ['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
  119. ],
  120. '=A1:C3',
  121. ],
  122. 'Chained Cell Range' => [
  123. [
  124. ['type' => 'Cell Reference', 'value' => 'A1', 'reference' => 'A1'],
  125. ['type' => 'Cell Reference', 'value' => 'C3', 'reference' => 'C3'],
  126. ['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
  127. ['type' => 'Cell Reference', 'value' => 'E5', 'reference' => 'E5'],
  128. ['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
  129. ],
  130. '=A1:C3:E5',
  131. ],
  132. 'Cell Range Intersection' => [
  133. [
  134. ['type' => 'Cell Reference', 'value' => 'A1', 'reference' => 'A1'],
  135. ['type' => 'Cell Reference', 'value' => 'C3', 'reference' => 'C3'],
  136. ['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
  137. ['type' => 'Cell Reference', 'value' => 'B2', 'reference' => 'B2'],
  138. ['type' => 'Cell Reference', 'value' => 'D4', 'reference' => 'D4'],
  139. ['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
  140. ['type' => 'Binary Operator', 'value' => '∩', 'reference' => null],
  141. ],
  142. '=A1:C3 B2:D4',
  143. ],
  144. 'Row Range' => [
  145. [
  146. ['type' => 'Row Reference', 'value' => 'A2', 'reference' => 'A2'],
  147. ['type' => 'Row Reference', 'value' => 'XFD3', 'reference' => 'XFD3'],
  148. ['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
  149. ],
  150. '=2:3',
  151. ],
  152. 'Column Range' => [
  153. [
  154. ['type' => 'Column Reference', 'value' => 'B1', 'reference' => 'B1'],
  155. ['type' => 'Column Reference', 'value' => 'C1048576', 'reference' => 'C1048576'],
  156. ['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
  157. ],
  158. '=B:C',
  159. ],
  160. 'Combined Cell Reference and Column Range' => [
  161. [
  162. ['type' => 'Column Reference', 'value' => "'sheet1'!A1", 'reference' => "'sheet1'!A1"],
  163. ['type' => 'Column Reference', 'value' => "'sheet1'!A1048576", 'reference' => "'sheet1'!A1048576"],
  164. ['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
  165. ['type' => 'Operand Count for Function MIN()', 'value' => 1, 'reference' => null],
  166. ['type' => 'Function', 'value' => 'MIN(', 'reference' => null],
  167. ['type' => 'Cell Reference', 'value' => "'sheet1'!A1", 'reference' => "'sheet1'!A1"],
  168. ['type' => 'Binary Operator', 'value' => '+', 'reference' => null],
  169. ],
  170. "=MIN('sheet1'!A:A) + 'sheet1'!A1",
  171. ],
  172. 'Combined Cell Reference and Column Range with quote' => [
  173. [
  174. ['type' => 'Column Reference', 'value' => "'Mark's sheet1'!A1", 'reference' => "'Mark's sheet1'!A1"],
  175. ['type' => 'Column Reference', 'value' => "'Mark's sheet1'!A1048576", 'reference' => "'Mark's sheet1'!A1048576"],
  176. ['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
  177. ['type' => 'Operand Count for Function MIN()', 'value' => 1, 'reference' => null],
  178. ['type' => 'Function', 'value' => 'MIN(', 'reference' => null],
  179. ['type' => 'Cell Reference', 'value' => "'Mark's sheet1'!A1", 'reference' => "'Mark's sheet1'!A1"],
  180. ['type' => 'Binary Operator', 'value' => '+', 'reference' => null],
  181. ],
  182. "=MIN('Mark''s sheet1'!A:A) + 'Mark''s sheet1'!A1",
  183. ],
  184. 'Combined Cell Reference and Column Range with unescaped quote' => [
  185. [
  186. ['type' => 'Column Reference', 'value' => "'Mark's sheet1'!A1", 'reference' => "'Mark's sheet1'!A1"],
  187. ['type' => 'Column Reference', 'value' => "'Mark's sheet1'!A1048576", 'reference' => "'Mark's sheet1'!A1048576"],
  188. ['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
  189. ['type' => 'Operand Count for Function MIN()', 'value' => 1, 'reference' => null],
  190. ['type' => 'Function', 'value' => 'MIN(', 'reference' => null],
  191. ['type' => 'Cell Reference', 'value' => "'Mark's sheet1'!A1", 'reference' => "'Mark's sheet1'!A1"],
  192. ['type' => 'Binary Operator', 'value' => '+', 'reference' => null],
  193. ],
  194. "=MIN('Mark's sheet1'!A:A) + 'Mark's sheet1'!A1",
  195. ],
  196. 'Combined Column Range and Cell Reference' => [
  197. [
  198. ['type' => 'Cell Reference', 'value' => "'sheet1'!A1", 'reference' => "'sheet1'!A1"],
  199. ['type' => 'Column Reference', 'value' => "'sheet1'!A1", 'reference' => "'sheet1'!A1"],
  200. ['type' => 'Column Reference', 'value' => "'sheet1'!A1048576", 'reference' => "'sheet1'!A1048576"],
  201. ['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
  202. ['type' => 'Operand Count for Function MIN()', 'value' => 1, 'reference' => null],
  203. ['type' => 'Function', 'value' => 'MIN(', 'reference' => null],
  204. ['type' => 'Binary Operator', 'value' => '+', 'reference' => null],
  205. ],
  206. "='sheet1'!A1 + MIN('sheet1'!A:A)",
  207. ],
  208. 'Combined Column Range and Cell Reference with quote' => [
  209. [
  210. ['type' => 'Cell Reference', 'value' => "'Mark's sheet1'!A1", 'reference' => "'Mark's sheet1'!A1"],
  211. ['type' => 'Column Reference', 'value' => "'Mark's sheet1'!A1", 'reference' => "'Mark's sheet1'!A1"],
  212. ['type' => 'Column Reference', 'value' => "'Mark's sheet1'!A1048576", 'reference' => "'Mark's sheet1'!A1048576"],
  213. ['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
  214. ['type' => 'Operand Count for Function MIN()', 'value' => 1, 'reference' => null],
  215. ['type' => 'Function', 'value' => 'MIN(', 'reference' => null],
  216. ['type' => 'Binary Operator', 'value' => '+', 'reference' => null],
  217. ],
  218. "='Mark''s sheet1'!A1 + MIN('Mark''s sheet1'!A:A)",
  219. ],
  220. 'Combined Column Range and Cell Reference with unescaped quote' => [
  221. [
  222. ['type' => 'Cell Reference', 'value' => "'Mark's sheet1'!A1", 'reference' => "'Mark's sheet1'!A1"],
  223. ['type' => 'Column Reference', 'value' => "'Mark's sheet1'!A1", 'reference' => "'Mark's sheet1'!A1"],
  224. ['type' => 'Column Reference', 'value' => "'Mark's sheet1'!A1048576", 'reference' => "'Mark's sheet1'!A1048576"],
  225. ['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
  226. ['type' => 'Operand Count for Function MIN()', 'value' => 1, 'reference' => null],
  227. ['type' => 'Function', 'value' => 'MIN(', 'reference' => null],
  228. ['type' => 'Binary Operator', 'value' => '+', 'reference' => null],
  229. ],
  230. "='Mark's sheet1'!A1 + MIN('Mark's sheet1'!A:A)",
  231. ],
  232. 'Range with Defined Names' => [
  233. [
  234. ['type' => 'Defined Name', 'value' => 'GROUP1', 'reference' => 'GROUP1'],
  235. ['type' => 'Defined Name', 'value' => 'D4', 'reference' => 'GROUP2'],
  236. ['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
  237. ['type' => 'Defined Name', 'value' => 'F6', 'reference' => 'GROUP2'],
  238. ['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
  239. ],
  240. '=GROUP1:GROUP2',
  241. ],
  242. 'Named Range with Binary Operator' => [
  243. [
  244. ['type' => 'Defined Name', 'value' => 'DEFINED_NAME_1', 'reference' => 'DEFINED_NAME_1'],
  245. ['type' => 'Defined Name', 'value' => 'DEFINED_NAME_2', 'reference' => 'DEFINED_NAME_2'],
  246. ['type' => 'Binary Operator', 'value' => '/', 'reference' => null],
  247. ],
  248. '=DEFINED_NAME_1/DEFINED_NAME_2',
  249. ],
  250. 'Named Range Intersection' => [
  251. [
  252. ['type' => 'Defined Name', 'value' => 'DEFINED_NAME_1', 'reference' => 'DEFINED_NAME_1'],
  253. ['type' => 'Defined Name', 'value' => 'DEFINED_NAME_2', 'reference' => 'DEFINED_NAME_2'],
  254. ['type' => 'Binary Operator', 'value' => '∩', 'reference' => null],
  255. ],
  256. '=DEFINED_NAME_1 DEFINED_NAME_2',
  257. ],
  258. // 'Structured Reference Arithmetic' => [
  259. // [
  260. // ['type' => 'Structured Reference', 'value' => '[@Quantity]', 'reference' => null],
  261. // ['type' => 'Structured Reference', 'value' => '[@[Unit Price]]', 'reference' => null],
  262. // ['type' => 'Binary Operator', 'value' => '*', 'reference' => null],
  263. // ],
  264. // '=[@Quantity]*[@[Unit Price]]',
  265. // ],
  266. // 'Structured Reference Intersection' => [
  267. // [
  268. // ['type' => 'Structured Reference', 'value' => 'DeptSales[[Sales Person]:[Sales Amount]]', 'reference' => null],
  269. // ['type' => 'Structured Reference', 'value' => 'DeptSales[[Region]:[% Commission]]', 'reference' => null],
  270. // ['type' => 'Binary Operator', 'value' => '∩', 'reference' => null],
  271. // ],
  272. // '=DeptSales[[Sales Person]:[Sales Amount]] DeptSales[[Region]:[% Commission]]',
  273. // ],
  274. // 'Cell Range Union' => [
  275. // [
  276. // ['type' => 'Cell Reference', 'value' => 'A1', 'reference' => 'A1'],
  277. // ['type' => 'Cell Reference', 'value' => 'C3', 'reference' => 'C3'],
  278. // ['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
  279. // ['type' => 'Cell Reference', 'value' => 'B2', 'reference' => 'B2'],
  280. // ['type' => 'Cell Reference', 'value' => 'D4', 'reference' => 'D4'],
  281. // ['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
  282. // ['type' => 'Binary Operator', 'value' => '∪', 'reference' => null],
  283. // ],
  284. // '=A1:C3,B2:D4',
  285. // ],
  286. // 'Named Range Union' => [
  287. // [
  288. // ['type' => 'Defined Name', 'value' => 'DEFINED_NAME_1', 'reference' => 'DEFINED_NAME_1'],
  289. // ['type' => 'Defined Name', 'value' => 'DEFINED_NAME_2', 'reference' => 'DEFINED_NAME_2'],
  290. // ['type' => 'Binary Operator', 'value' => '∪', 'reference' => null],
  291. // ],
  292. // '=DEFINED_NAME_1,DEFINED_NAME_2',
  293. // ],
  294. ];
  295. }
  296. }