@trebco/treb 36.1.4 → 37.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (552) hide show
  1. package/api-config.json +1 -1
  2. package/build/package.json +119 -0
  3. package/build/treb-base-types/src/api_types.d.ts +11 -0
  4. package/build/treb-base-types/src/api_types.js +22 -0
  5. package/build/treb-base-types/src/api_types.js.map +1 -0
  6. package/build/treb-base-types/src/area-utils.d.ts +9 -0
  7. package/build/treb-base-types/src/area-utils.js +50 -0
  8. package/build/treb-base-types/src/area-utils.js.map +1 -0
  9. package/build/treb-base-types/src/area.d.ts +182 -0
  10. package/build/treb-base-types/src/area.js +715 -0
  11. package/build/treb-base-types/src/area.js.map +1 -0
  12. package/build/treb-base-types/src/basic_types.d.ts +20 -0
  13. package/build/treb-base-types/src/basic_types.js +22 -0
  14. package/build/treb-base-types/src/basic_types.js.map +1 -0
  15. package/build/treb-base-types/src/cell.d.ts +167 -0
  16. package/build/treb-base-types/src/cell.js +432 -0
  17. package/build/treb-base-types/src/cell.js.map +1 -0
  18. package/build/treb-base-types/src/cells.d.ts +251 -0
  19. package/build/treb-base-types/src/cells.js +1136 -0
  20. package/build/treb-base-types/src/cells.js.map +1 -0
  21. package/build/treb-base-types/src/color.d.ts +35 -0
  22. package/build/treb-base-types/src/color.js +162 -0
  23. package/build/treb-base-types/src/color.js.map +1 -0
  24. package/build/treb-base-types/src/dom-utilities.d.ts +70 -0
  25. package/build/treb-base-types/src/dom-utilities.js +144 -0
  26. package/build/treb-base-types/src/dom-utilities.js.map +1 -0
  27. package/build/treb-base-types/src/evaluate-options.d.ts +35 -0
  28. package/build/treb-base-types/src/evaluate-options.js +22 -0
  29. package/build/treb-base-types/src/evaluate-options.js.map +1 -0
  30. package/build/treb-base-types/src/font-stack.d.ts +37 -0
  31. package/build/treb-base-types/src/font-stack.js +93 -0
  32. package/build/treb-base-types/src/font-stack.js.map +1 -0
  33. package/build/treb-base-types/src/gradient.d.ts +18 -0
  34. package/build/treb-base-types/src/gradient.js +86 -0
  35. package/build/treb-base-types/src/gradient.js.map +1 -0
  36. package/build/treb-base-types/src/import.d.ts +48 -0
  37. package/build/treb-base-types/src/import.js +22 -0
  38. package/build/treb-base-types/src/import.js.map +1 -0
  39. package/build/treb-base-types/src/index-standalone.d.ts +6 -0
  40. package/build/treb-base-types/src/index-standalone.js +27 -0
  41. package/build/treb-base-types/src/index-standalone.js.map +1 -0
  42. package/build/treb-base-types/src/index.d.ts +22 -0
  43. package/build/treb-base-types/src/index.js +45 -0
  44. package/build/treb-base-types/src/index.js.map +1 -0
  45. package/build/treb-base-types/src/layout.d.ts +22 -0
  46. package/build/treb-base-types/src/layout.js +22 -0
  47. package/build/treb-base-types/src/layout.js.map +1 -0
  48. package/build/treb-base-types/src/localization.d.ts +37 -0
  49. package/build/treb-base-types/src/localization.js +157 -0
  50. package/build/treb-base-types/src/localization.js.map +1 -0
  51. package/build/treb-base-types/src/rectangle.d.ts +51 -0
  52. package/build/treb-base-types/src/rectangle.js +123 -0
  53. package/build/treb-base-types/src/rectangle.js.map +1 -0
  54. package/build/treb-base-types/src/render_text.d.ts +34 -0
  55. package/build/treb-base-types/src/render_text.js +22 -0
  56. package/build/treb-base-types/src/render_text.js.map +1 -0
  57. package/build/treb-base-types/src/style.d.ts +214 -0
  58. package/build/treb-base-types/src/style.js +373 -0
  59. package/build/treb-base-types/src/style.js.map +1 -0
  60. package/build/treb-base-types/src/table.d.ts +58 -0
  61. package/build/treb-base-types/src/table.js +27 -0
  62. package/build/treb-base-types/src/table.js.map +1 -0
  63. package/build/treb-base-types/src/text_part.d.ts +26 -0
  64. package/build/treb-base-types/src/text_part.js +47 -0
  65. package/build/treb-base-types/src/text_part.js.map +1 -0
  66. package/build/treb-base-types/src/theme.d.ts +120 -0
  67. package/build/treb-base-types/src/theme.js +460 -0
  68. package/build/treb-base-types/src/theme.js.map +1 -0
  69. package/build/treb-base-types/src/union.d.ts +73 -0
  70. package/build/treb-base-types/src/union.js +61 -0
  71. package/build/treb-base-types/src/union.js.map +1 -0
  72. package/build/treb-base-types/src/value-type.d.ts +86 -0
  73. package/build/treb-base-types/src/value-type.js +168 -0
  74. package/build/treb-base-types/src/value-type.js.map +1 -0
  75. package/build/treb-base-types/src/worker-proxy.d.ts +95 -0
  76. package/build/treb-base-types/src/worker-proxy.js +221 -0
  77. package/build/treb-base-types/src/worker-proxy.js.map +1 -0
  78. package/build/treb-calculator/src/calculator.d.ts +249 -0
  79. package/build/treb-calculator/src/calculator.js +2755 -0
  80. package/build/treb-calculator/src/calculator.js.map +1 -0
  81. package/build/treb-calculator/src/complex-math.d.ts +75 -0
  82. package/build/treb-calculator/src/complex-math.js +559 -0
  83. package/build/treb-calculator/src/complex-math.js.map +1 -0
  84. package/build/treb-calculator/src/dag/array-vertex.d.ts +71 -0
  85. package/build/treb-calculator/src/dag/array-vertex.js +156 -0
  86. package/build/treb-calculator/src/dag/array-vertex.js.map +1 -0
  87. package/build/treb-calculator/src/dag/calculation_leaf_vertex.d.ts +48 -0
  88. package/build/treb-calculator/src/dag/calculation_leaf_vertex.js +84 -0
  89. package/build/treb-calculator/src/dag/calculation_leaf_vertex.js.map +1 -0
  90. package/build/treb-calculator/src/dag/graph.d.ts +134 -0
  91. package/build/treb-calculator/src/dag/graph.js +842 -0
  92. package/build/treb-calculator/src/dag/graph.js.map +1 -0
  93. package/build/treb-calculator/src/dag/spreadsheet_vertex.d.ts +58 -0
  94. package/build/treb-calculator/src/dag/spreadsheet_vertex.js +232 -0
  95. package/build/treb-calculator/src/dag/spreadsheet_vertex.js.map +1 -0
  96. package/build/treb-calculator/src/dag/spreadsheet_vertex_base.d.ts +20 -0
  97. package/build/treb-calculator/src/dag/spreadsheet_vertex_base.js +25 -0
  98. package/build/treb-calculator/src/dag/spreadsheet_vertex_base.js.map +1 -0
  99. package/build/treb-calculator/src/dag/state_leaf_vertex.d.ts +43 -0
  100. package/build/treb-calculator/src/dag/state_leaf_vertex.js +81 -0
  101. package/build/treb-calculator/src/dag/state_leaf_vertex.js.map +1 -0
  102. package/build/treb-calculator/src/dag/vertex.d.ts +71 -0
  103. package/build/treb-calculator/src/dag/vertex.js +274 -0
  104. package/build/treb-calculator/src/dag/vertex.js.map +1 -0
  105. package/build/treb-calculator/src/descriptors.d.ts +189 -0
  106. package/build/treb-calculator/src/descriptors.js +22 -0
  107. package/build/treb-calculator/src/descriptors.js.map +1 -0
  108. package/build/treb-calculator/src/expression-calculator.d.ts +127 -0
  109. package/build/treb-calculator/src/expression-calculator.js +1033 -0
  110. package/build/treb-calculator/src/expression-calculator.js.map +1 -0
  111. package/build/treb-calculator/src/function-error.d.ts +35 -0
  112. package/build/treb-calculator/src/function-error.js +85 -0
  113. package/build/treb-calculator/src/function-error.js.map +1 -0
  114. package/build/treb-calculator/src/function-library.d.ts +22 -0
  115. package/build/treb-calculator/src/function-library.js +96 -0
  116. package/build/treb-calculator/src/function-library.js.map +1 -0
  117. package/build/treb-calculator/src/functions/base-functions.d.ts +7 -0
  118. package/build/treb-calculator/src/functions/base-functions.js +2611 -0
  119. package/build/treb-calculator/src/functions/base-functions.js.map +1 -0
  120. package/build/treb-calculator/src/functions/beta.d.ts +17 -0
  121. package/build/treb-calculator/src/functions/beta.js +201 -0
  122. package/build/treb-calculator/src/functions/beta.js.map +1 -0
  123. package/build/treb-calculator/src/functions/checkbox.d.ts +3 -0
  124. package/build/treb-calculator/src/functions/checkbox.js +128 -0
  125. package/build/treb-calculator/src/functions/checkbox.js.map +1 -0
  126. package/build/treb-calculator/src/functions/complex-functions.d.ts +2 -0
  127. package/build/treb-calculator/src/functions/complex-functions.js +217 -0
  128. package/build/treb-calculator/src/functions/complex-functions.js.map +1 -0
  129. package/build/treb-calculator/src/functions/date-utils.d.ts +3 -0
  130. package/build/treb-calculator/src/functions/date-utils.js +59 -0
  131. package/build/treb-calculator/src/functions/date-utils.js.map +1 -0
  132. package/build/treb-calculator/src/functions/finance-functions.d.ts +2 -0
  133. package/build/treb-calculator/src/functions/finance-functions.js +547 -0
  134. package/build/treb-calculator/src/functions/finance-functions.js.map +1 -0
  135. package/build/treb-calculator/src/functions/fp.d.ts +2 -0
  136. package/build/treb-calculator/src/functions/fp.js +463 -0
  137. package/build/treb-calculator/src/functions/fp.js.map +1 -0
  138. package/build/treb-calculator/src/functions/function-utilities.d.ts +2 -0
  139. package/build/treb-calculator/src/functions/function-utilities.js +36 -0
  140. package/build/treb-calculator/src/functions/function-utilities.js.map +1 -0
  141. package/build/treb-calculator/src/functions/gamma.d.ts +20 -0
  142. package/build/treb-calculator/src/functions/gamma.js +142 -0
  143. package/build/treb-calculator/src/functions/gamma.js.map +1 -0
  144. package/build/treb-calculator/src/functions/information-functions.d.ts +2 -0
  145. package/build/treb-calculator/src/functions/information-functions.js +71 -0
  146. package/build/treb-calculator/src/functions/information-functions.js.map +1 -0
  147. package/build/treb-calculator/src/functions/lambda-functions.d.ts +2 -0
  148. package/build/treb-calculator/src/functions/lambda-functions.js +85 -0
  149. package/build/treb-calculator/src/functions/lambda-functions.js.map +1 -0
  150. package/build/treb-calculator/src/functions/matrix-functions.d.ts +2 -0
  151. package/build/treb-calculator/src/functions/matrix-functions.js +144 -0
  152. package/build/treb-calculator/src/functions/matrix-functions.js.map +1 -0
  153. package/build/treb-calculator/src/functions/normal.d.ts +2 -0
  154. package/build/treb-calculator/src/functions/normal.js +32 -0
  155. package/build/treb-calculator/src/functions/normal.js.map +1 -0
  156. package/build/treb-calculator/src/functions/regex-functions.d.ts +2 -0
  157. package/build/treb-calculator/src/functions/regex-functions.js +188 -0
  158. package/build/treb-calculator/src/functions/regex-functions.js.map +1 -0
  159. package/build/treb-calculator/src/functions/sparkline.d.ts +37 -0
  160. package/build/treb-calculator/src/functions/sparkline.js +264 -0
  161. package/build/treb-calculator/src/functions/sparkline.js.map +1 -0
  162. package/build/treb-calculator/src/functions/statistics-functions.d.ts +6 -0
  163. package/build/treb-calculator/src/functions/statistics-functions.js +989 -0
  164. package/build/treb-calculator/src/functions/statistics-functions.js.map +1 -0
  165. package/build/treb-calculator/src/functions/students-t.d.ts +3 -0
  166. package/build/treb-calculator/src/functions/students-t.js +64 -0
  167. package/build/treb-calculator/src/functions/students-t.js.map +1 -0
  168. package/build/treb-calculator/src/functions/text-functions.d.ts +3 -0
  169. package/build/treb-calculator/src/functions/text-functions.js +320 -0
  170. package/build/treb-calculator/src/functions/text-functions.js.map +1 -0
  171. package/build/treb-calculator/src/index.d.ts +2 -0
  172. package/build/treb-calculator/src/index.js +22 -0
  173. package/build/treb-calculator/src/index.js.map +1 -0
  174. package/build/treb-calculator/src/notifier-types.d.ts +26 -0
  175. package/build/treb-calculator/src/notifier-types.js +22 -0
  176. package/build/treb-calculator/src/notifier-types.js.map +1 -0
  177. package/build/treb-calculator/src/primitives.d.ts +15 -0
  178. package/build/treb-calculator/src/primitives.js +398 -0
  179. package/build/treb-calculator/src/primitives.js.map +1 -0
  180. package/build/treb-calculator/src/utilities.d.ts +68 -0
  181. package/build/treb-calculator/src/utilities.js +324 -0
  182. package/build/treb-calculator/src/utilities.js.map +1 -0
  183. package/build/treb-charts/src/chart-functions.d.ts +8 -0
  184. package/build/treb-charts/src/chart-functions.js +209 -0
  185. package/build/treb-charts/src/chart-functions.js.map +1 -0
  186. package/build/treb-charts/src/chart-types.d.ts +233 -0
  187. package/build/treb-charts/src/chart-types.js +57 -0
  188. package/build/treb-charts/src/chart-types.js.map +1 -0
  189. package/build/treb-charts/src/chart-utils.d.ts +106 -0
  190. package/build/treb-charts/src/chart-utils.js +1060 -0
  191. package/build/treb-charts/src/chart-utils.js.map +1 -0
  192. package/build/treb-charts/src/chart.d.ts +23 -0
  193. package/build/treb-charts/src/chart.js +94 -0
  194. package/build/treb-charts/src/chart.js.map +1 -0
  195. package/build/treb-charts/src/default-chart-renderer.d.ts +16 -0
  196. package/build/treb-charts/src/default-chart-renderer.js +533 -0
  197. package/build/treb-charts/src/default-chart-renderer.js.map +1 -0
  198. package/build/treb-charts/src/index.d.ts +5 -0
  199. package/build/treb-charts/src/index.js +24 -0
  200. package/build/treb-charts/src/index.js.map +1 -0
  201. package/build/treb-charts/src/main.d.ts +1 -0
  202. package/build/treb-charts/src/main.js +34 -0
  203. package/build/treb-charts/src/main.js.map +1 -0
  204. package/build/treb-charts/src/quicksort.d.ts +1 -0
  205. package/build/treb-charts/src/quicksort.js +49 -0
  206. package/build/treb-charts/src/quicksort.js.map +1 -0
  207. package/build/treb-charts/src/rectangle.d.ts +18 -0
  208. package/build/treb-charts/src/rectangle.js +41 -0
  209. package/build/treb-charts/src/rectangle.js.map +1 -0
  210. package/build/treb-charts/src/renderer-type.d.ts +24 -0
  211. package/build/treb-charts/src/renderer-type.js +22 -0
  212. package/build/treb-charts/src/renderer-type.js.map +1 -0
  213. package/build/treb-charts/src/renderer.d.ts +127 -0
  214. package/build/treb-charts/src/renderer.js +1518 -0
  215. package/build/treb-charts/src/renderer.js.map +1 -0
  216. package/build/treb-charts/src/util.d.ts +18 -0
  217. package/build/treb-charts/src/util.js +71 -0
  218. package/build/treb-charts/src/util.js.map +1 -0
  219. package/build/treb-data-model/src/annotation.d.ts +167 -0
  220. package/build/treb-data-model/src/annotation.js +120 -0
  221. package/build/treb-data-model/src/annotation.js.map +1 -0
  222. package/build/treb-data-model/src/conditional_format.d.ts +155 -0
  223. package/build/treb-data-model/src/conditional_format.js +62 -0
  224. package/build/treb-data-model/src/conditional_format.js.map +1 -0
  225. package/build/treb-data-model/src/data-validation.d.ts +28 -0
  226. package/build/treb-data-model/src/data-validation.js +22 -0
  227. package/build/treb-data-model/src/data-validation.js.map +1 -0
  228. package/build/treb-data-model/src/data_model.d.ts +173 -0
  229. package/build/treb-data-model/src/data_model.js +637 -0
  230. package/build/treb-data-model/src/data_model.js.map +1 -0
  231. package/build/treb-data-model/src/index.d.ts +13 -0
  232. package/build/treb-data-model/src/index.js +28 -0
  233. package/build/treb-data-model/src/index.js.map +1 -0
  234. package/build/treb-data-model/src/language-model.d.ts +22 -0
  235. package/build/treb-data-model/src/language-model.js +22 -0
  236. package/build/treb-data-model/src/language-model.js.map +1 -0
  237. package/build/treb-data-model/src/named.d.ts +124 -0
  238. package/build/treb-data-model/src/named.js +372 -0
  239. package/build/treb-data-model/src/named.js.map +1 -0
  240. package/build/treb-data-model/src/serialize_options.d.ts +49 -0
  241. package/build/treb-data-model/src/serialize_options.js +22 -0
  242. package/build/treb-data-model/src/serialize_options.js.map +1 -0
  243. package/build/treb-data-model/src/sheet.d.ts +499 -0
  244. package/build/treb-data-model/src/sheet.js +2904 -0
  245. package/build/treb-data-model/src/sheet.js.map +1 -0
  246. package/build/treb-data-model/src/sheet_collection.d.ts +58 -0
  247. package/build/treb-data-model/src/sheet_collection.js +112 -0
  248. package/build/treb-data-model/src/sheet_collection.js.map +1 -0
  249. package/build/treb-data-model/src/sheet_selection.d.ts +42 -0
  250. package/build/treb-data-model/src/sheet_selection.js +39 -0
  251. package/build/treb-data-model/src/sheet_selection.js.map +1 -0
  252. package/build/treb-data-model/src/sheet_types.d.ts +104 -0
  253. package/build/treb-data-model/src/sheet_types.js +22 -0
  254. package/build/treb-data-model/src/sheet_types.js.map +1 -0
  255. package/build/treb-data-model/src/types.d.ts +59 -0
  256. package/build/treb-data-model/src/types.js +22 -0
  257. package/build/treb-data-model/src/types.js.map +1 -0
  258. package/build/treb-embed/src/custom-element/spreadsheet-constructor.d.ts +75 -0
  259. package/build/treb-embed/src/custom-element/spreadsheet-constructor.js +1144 -0
  260. package/build/treb-embed/src/custom-element/spreadsheet-constructor.js.map +1 -0
  261. package/build/treb-embed/src/custom-element/treb-global.d.ts +36 -0
  262. package/build/treb-embed/src/custom-element/treb-global.js +64 -0
  263. package/build/treb-embed/src/custom-element/treb-global.js.map +1 -0
  264. package/build/treb-embed/src/custom-element/treb-spreadsheet-element.d.ts +1 -0
  265. package/build/treb-embed/src/custom-element/treb-spreadsheet-element.js +61 -0
  266. package/build/treb-embed/src/custom-element/treb-spreadsheet-element.js.map +1 -0
  267. package/build/treb-embed/src/embedded-spreadsheet.d.ts +1358 -0
  268. package/build/treb-embed/src/embedded-spreadsheet.js +5205 -0
  269. package/build/treb-embed/src/embedded-spreadsheet.js.map +1 -0
  270. package/build/treb-embed/src/index.d.ts +12 -0
  271. package/build/treb-embed/src/index.js +34 -0
  272. package/build/treb-embed/src/index.js.map +1 -0
  273. package/build/treb-embed/src/options.d.ts +266 -0
  274. package/build/treb-embed/src/options.js +56 -0
  275. package/build/treb-embed/src/options.js.map +1 -0
  276. package/build/treb-embed/src/plugin.d.ts +9 -0
  277. package/build/treb-embed/src/plugin.js +22 -0
  278. package/build/treb-embed/src/plugin.js.map +1 -0
  279. package/build/treb-embed/src/progress-dialog.d.ts +49 -0
  280. package/build/treb-embed/src/progress-dialog.js +178 -0
  281. package/build/treb-embed/src/progress-dialog.js.map +1 -0
  282. package/build/treb-embed/src/selection-state.d.ts +15 -0
  283. package/build/treb-embed/src/selection-state.js +22 -0
  284. package/build/treb-embed/src/selection-state.js.map +1 -0
  285. package/build/treb-embed/src/spinner.d.ts +8 -0
  286. package/build/treb-embed/src/spinner.js +40 -0
  287. package/build/treb-embed/src/spinner.js.map +1 -0
  288. package/build/treb-embed/src/toolbar-message.d.ts +72 -0
  289. package/build/treb-embed/src/toolbar-message.js +22 -0
  290. package/build/treb-embed/src/toolbar-message.js.map +1 -0
  291. package/build/treb-embed/src/types.d.ts +185 -0
  292. package/build/treb-embed/src/types.js +45 -0
  293. package/build/treb-embed/src/types.js.map +1 -0
  294. package/build/treb-embed/tsconfig.tsbuildinfo +1 -0
  295. package/build/treb-export/src/address-type.d.ts +34 -0
  296. package/build/treb-export/src/address-type.js +53 -0
  297. package/build/treb-export/src/address-type.js.map +1 -0
  298. package/build/treb-export/src/base-template.d.ts +1 -0
  299. package/build/treb-export/src/base-template.js +22 -0
  300. package/build/treb-export/src/base-template.js.map +1 -0
  301. package/build/treb-export/src/column-width.d.ts +2 -0
  302. package/build/treb-export/src/column-width.js +80 -0
  303. package/build/treb-export/src/column-width.js.map +1 -0
  304. package/build/treb-export/src/drawing/bubble-chart-template.d.ts +514 -0
  305. package/build/treb-export/src/drawing/bubble-chart-template.js +544 -0
  306. package/build/treb-export/src/drawing/bubble-chart-template.js.map +1 -0
  307. package/build/treb-export/src/drawing/chart-template-components2.d.ts +365 -0
  308. package/build/treb-export/src/drawing/chart-template-components2.js +386 -0
  309. package/build/treb-export/src/drawing/chart-template-components2.js.map +1 -0
  310. package/build/treb-export/src/drawing/chart.d.ts +26 -0
  311. package/build/treb-export/src/drawing/chart.js +247 -0
  312. package/build/treb-export/src/drawing/chart.js.map +1 -0
  313. package/build/treb-export/src/drawing/column-chart-template2.d.ts +490 -0
  314. package/build/treb-export/src/drawing/column-chart-template2.js +518 -0
  315. package/build/treb-export/src/drawing/column-chart-template2.js.map +1 -0
  316. package/build/treb-export/src/drawing/donut-chart-template2.d.ts +272 -0
  317. package/build/treb-export/src/drawing/donut-chart-template2.js +293 -0
  318. package/build/treb-export/src/drawing/donut-chart-template2.js.map +1 -0
  319. package/build/treb-export/src/drawing/drawing.d.ts +49 -0
  320. package/build/treb-export/src/drawing/drawing.js +193 -0
  321. package/build/treb-export/src/drawing/drawing.js.map +1 -0
  322. package/build/treb-export/src/drawing/embedded-image.d.ts +12 -0
  323. package/build/treb-export/src/drawing/embedded-image.js +54 -0
  324. package/build/treb-export/src/drawing/embedded-image.js.map +1 -0
  325. package/build/treb-export/src/drawing/scatter-chart-template2.d.ts +520 -0
  326. package/build/treb-export/src/drawing/scatter-chart-template2.js +551 -0
  327. package/build/treb-export/src/drawing/scatter-chart-template2.js.map +1 -0
  328. package/build/treb-export/src/export.d.ts +72 -0
  329. package/build/treb-export/src/export.js +2039 -0
  330. package/build/treb-export/src/export.js.map +1 -0
  331. package/build/treb-export/src/import-export-messages.d.ts +31 -0
  332. package/build/treb-export/src/import-export-messages.js +22 -0
  333. package/build/treb-export/src/import-export-messages.js.map +1 -0
  334. package/build/treb-export/src/import.d.ts +33 -0
  335. package/build/treb-export/src/import.js +1258 -0
  336. package/build/treb-export/src/import.js.map +1 -0
  337. package/build/treb-export/src/index.worker.d.ts +1 -0
  338. package/build/treb-export/src/index.worker.js +93 -0
  339. package/build/treb-export/src/index.worker.js.map +1 -0
  340. package/build/treb-export/src/metadata.d.ts +51 -0
  341. package/build/treb-export/src/metadata.js +153 -0
  342. package/build/treb-export/src/metadata.js.map +1 -0
  343. package/build/treb-export/src/ooxml.d.ts +7 -0
  344. package/build/treb-export/src/ooxml.js +41 -0
  345. package/build/treb-export/src/ooxml.js.map +1 -0
  346. package/build/treb-export/src/relationship.d.ts +8 -0
  347. package/build/treb-export/src/relationship.js +27 -0
  348. package/build/treb-export/src/relationship.js.map +1 -0
  349. package/build/treb-export/src/shared-strings.d.ts +11 -0
  350. package/build/treb-export/src/shared-strings.js +105 -0
  351. package/build/treb-export/src/shared-strings.js.map +1 -0
  352. package/build/treb-export/src/template-2.d.ts +1 -0
  353. package/build/treb-export/src/template-2.js +22 -0
  354. package/build/treb-export/src/template-2.js.map +1 -0
  355. package/build/treb-export/src/unescape_xml.d.ts +1 -0
  356. package/build/treb-export/src/unescape_xml.js +61 -0
  357. package/build/treb-export/src/unescape_xml.js.map +1 -0
  358. package/build/treb-export/src/workbook-sheet.d.ts +75 -0
  359. package/build/treb-export/src/workbook-sheet.js +128 -0
  360. package/build/treb-export/src/workbook-sheet.js.map +1 -0
  361. package/build/treb-export/src/workbook-style.d.ts +110 -0
  362. package/build/treb-export/src/workbook-style.js +1134 -0
  363. package/build/treb-export/src/workbook-style.js.map +1 -0
  364. package/build/treb-export/src/workbook-theme.d.ts +13 -0
  365. package/build/treb-export/src/workbook-theme.js +85 -0
  366. package/build/treb-export/src/workbook-theme.js.map +1 -0
  367. package/build/treb-export/src/workbook.d.ts +123 -0
  368. package/build/treb-export/src/workbook.js +644 -0
  369. package/build/treb-export/src/workbook.js.map +1 -0
  370. package/build/treb-export/src/xml-test.d.ts +9 -0
  371. package/build/treb-export/src/xml-test.js +52 -0
  372. package/build/treb-export/src/xml-test.js.map +1 -0
  373. package/build/treb-export/src/xml-utils.d.ts +76 -0
  374. package/build/treb-export/src/xml-utils.js +223 -0
  375. package/build/treb-export/src/xml-utils.js.map +1 -0
  376. package/build/treb-export/src/zip-wrapper.d.ts +22 -0
  377. package/build/treb-export/src/zip-wrapper.js +93 -0
  378. package/build/treb-export/src/zip-wrapper.js.map +1 -0
  379. package/build/treb-format/src/format.d.ts +130 -0
  380. package/build/treb-format/src/format.js +805 -0
  381. package/build/treb-format/src/format.js.map +1 -0
  382. package/build/treb-format/src/format_cache.d.ts +55 -0
  383. package/build/treb-format/src/format_cache.js +166 -0
  384. package/build/treb-format/src/format_cache.js.map +1 -0
  385. package/build/treb-format/src/format_parser.d.ts +70 -0
  386. package/build/treb-format/src/format_parser.js +618 -0
  387. package/build/treb-format/src/format_parser.js.map +1 -0
  388. package/build/treb-format/src/index.d.ts +4 -0
  389. package/build/treb-format/src/index.js +25 -0
  390. package/build/treb-format/src/index.js.map +1 -0
  391. package/build/treb-format/src/number_format_section.d.ts +58 -0
  392. package/build/treb-format/src/number_format_section.js +78 -0
  393. package/build/treb-format/src/number_format_section.js.map +1 -0
  394. package/build/treb-format/src/value_parser.d.ts +48 -0
  395. package/build/treb-format/src/value_parser.js +244 -0
  396. package/build/treb-format/src/value_parser.js.map +1 -0
  397. package/build/treb-grid/src/editors/autocomplete.d.ts +39 -0
  398. package/build/treb-grid/src/editors/autocomplete.js +316 -0
  399. package/build/treb-grid/src/editors/autocomplete.js.map +1 -0
  400. package/build/treb-grid/src/editors/autocomplete_matcher.d.ts +74 -0
  401. package/build/treb-grid/src/editors/autocomplete_matcher.js +212 -0
  402. package/build/treb-grid/src/editors/autocomplete_matcher.js.map +1 -0
  403. package/build/treb-grid/src/editors/editor.d.ts +214 -0
  404. package/build/treb-grid/src/editors/editor.js +879 -0
  405. package/build/treb-grid/src/editors/editor.js.map +1 -0
  406. package/build/treb-grid/src/editors/external_editor.d.ts +11 -0
  407. package/build/treb-grid/src/editors/external_editor.js +118 -0
  408. package/build/treb-grid/src/editors/external_editor.js.map +1 -0
  409. package/build/treb-grid/src/editors/formula_bar.d.ts +85 -0
  410. package/build/treb-grid/src/editors/formula_bar.js +444 -0
  411. package/build/treb-grid/src/editors/formula_bar.js.map +1 -0
  412. package/build/treb-grid/src/editors/overlay_editor.d.ts +85 -0
  413. package/build/treb-grid/src/editors/overlay_editor.js +353 -0
  414. package/build/treb-grid/src/editors/overlay_editor.js.map +1 -0
  415. package/build/treb-grid/src/index.d.ts +12 -0
  416. package/build/treb-grid/src/index.js +28 -0
  417. package/build/treb-grid/src/index.js.map +1 -0
  418. package/build/treb-grid/src/layout/base_layout.d.ts +346 -0
  419. package/build/treb-grid/src/layout/base_layout.js +2050 -0
  420. package/build/treb-grid/src/layout/base_layout.js.map +1 -0
  421. package/build/treb-grid/src/layout/grid_layout.d.ts +19 -0
  422. package/build/treb-grid/src/layout/grid_layout.js +235 -0
  423. package/build/treb-grid/src/layout/grid_layout.js.map +1 -0
  424. package/build/treb-grid/src/layout/mock-layout.d.ts +10 -0
  425. package/build/treb-grid/src/layout/mock-layout.js +37 -0
  426. package/build/treb-grid/src/layout/mock-layout.js.map +1 -0
  427. package/build/treb-grid/src/render/selection-renderer.d.ts +97 -0
  428. package/build/treb-grid/src/render/selection-renderer.js +315 -0
  429. package/build/treb-grid/src/render/selection-renderer.js.map +1 -0
  430. package/build/treb-grid/src/render/svg_header_overlay.d.ts +20 -0
  431. package/build/treb-grid/src/render/svg_header_overlay.js +76 -0
  432. package/build/treb-grid/src/render/svg_header_overlay.js.map +1 -0
  433. package/build/treb-grid/src/render/svg_selection_block.d.ts +27 -0
  434. package/build/treb-grid/src/render/svg_selection_block.js +106 -0
  435. package/build/treb-grid/src/render/svg_selection_block.js.map +1 -0
  436. package/build/treb-grid/src/render/tile_renderer.d.ts +121 -0
  437. package/build/treb-grid/src/render/tile_renderer.js +1609 -0
  438. package/build/treb-grid/src/render/tile_renderer.js.map +1 -0
  439. package/build/treb-grid/src/types/border_constants.d.ts +9 -0
  440. package/build/treb-grid/src/types/border_constants.js +34 -0
  441. package/build/treb-grid/src/types/border_constants.js.map +1 -0
  442. package/build/treb-grid/src/types/clipboard_data.d.ts +11 -0
  443. package/build/treb-grid/src/types/clipboard_data.js +22 -0
  444. package/build/treb-grid/src/types/clipboard_data.js.map +1 -0
  445. package/build/treb-grid/src/types/clipboard_data2.d.ts +46 -0
  446. package/build/treb-grid/src/types/clipboard_data2.js +22 -0
  447. package/build/treb-grid/src/types/clipboard_data2.js.map +1 -0
  448. package/build/treb-grid/src/types/drag_mask.d.ts +10 -0
  449. package/build/treb-grid/src/types/drag_mask.js +78 -0
  450. package/build/treb-grid/src/types/drag_mask.js.map +1 -0
  451. package/build/treb-grid/src/types/external_editor_config.d.ts +33 -0
  452. package/build/treb-grid/src/types/external_editor_config.js +22 -0
  453. package/build/treb-grid/src/types/external_editor_config.js.map +1 -0
  454. package/build/treb-grid/src/types/grid.d.ts +806 -0
  455. package/build/treb-grid/src/types/grid.js +6410 -0
  456. package/build/treb-grid/src/types/grid.js.map +1 -0
  457. package/build/treb-grid/src/types/grid_base.d.ts +442 -0
  458. package/build/treb-grid/src/types/grid_base.js +3523 -0
  459. package/build/treb-grid/src/types/grid_base.js.map +1 -0
  460. package/build/treb-grid/src/types/grid_command.d.ts +408 -0
  461. package/build/treb-grid/src/types/grid_command.js +75 -0
  462. package/build/treb-grid/src/types/grid_command.js.map +1 -0
  463. package/build/treb-grid/src/types/grid_events.d.ts +93 -0
  464. package/build/treb-grid/src/types/grid_events.js +36 -0
  465. package/build/treb-grid/src/types/grid_events.js.map +1 -0
  466. package/build/treb-grid/src/types/grid_options.d.ts +50 -0
  467. package/build/treb-grid/src/types/grid_options.js +34 -0
  468. package/build/treb-grid/src/types/grid_options.js.map +1 -0
  469. package/build/treb-grid/src/types/scale-control.d.ts +21 -0
  470. package/build/treb-grid/src/types/scale-control.js +148 -0
  471. package/build/treb-grid/src/types/scale-control.js.map +1 -0
  472. package/build/treb-grid/src/types/set_range_options.d.ts +24 -0
  473. package/build/treb-grid/src/types/set_range_options.js +22 -0
  474. package/build/treb-grid/src/types/set_range_options.js.map +1 -0
  475. package/build/treb-grid/src/types/tab_bar.d.ts +84 -0
  476. package/build/treb-grid/src/types/tab_bar.js +426 -0
  477. package/build/treb-grid/src/types/tab_bar.js.map +1 -0
  478. package/build/treb-grid/src/types/tile.d.ts +29 -0
  479. package/build/treb-grid/src/types/tile.js +22 -0
  480. package/build/treb-grid/src/types/tile.js.map +1 -0
  481. package/build/treb-grid/src/types/update_flags.d.ts +48 -0
  482. package/build/treb-grid/src/types/update_flags.js +22 -0
  483. package/build/treb-grid/src/types/update_flags.js.map +1 -0
  484. package/build/treb-grid/src/util/fontmetrics.d.ts +21 -0
  485. package/build/treb-grid/src/util/fontmetrics.js +82 -0
  486. package/build/treb-grid/src/util/fontmetrics.js.map +1 -0
  487. package/build/treb-grid/src/util/ua.d.ts +33 -0
  488. package/build/treb-grid/src/util/ua.js +86 -0
  489. package/build/treb-grid/src/util/ua.js.map +1 -0
  490. package/build/treb-parser/src/csv-parser.d.ts +13 -0
  491. package/build/treb-parser/src/csv-parser.js +107 -0
  492. package/build/treb-parser/src/csv-parser.js.map +1 -0
  493. package/build/treb-parser/src/index.d.ts +4 -0
  494. package/build/treb-parser/src/index.js +25 -0
  495. package/build/treb-parser/src/index.js.map +1 -0
  496. package/build/treb-parser/src/md-parser.d.ts +97 -0
  497. package/build/treb-parser/src/md-parser.js +403 -0
  498. package/build/treb-parser/src/md-parser.js.map +1 -0
  499. package/build/treb-parser/src/parser-types.d.ts +345 -0
  500. package/build/treb-parser/src/parser-types.js +53 -0
  501. package/build/treb-parser/src/parser-types.js.map +1 -0
  502. package/build/treb-parser/src/parser.d.ts +422 -0
  503. package/build/treb-parser/src/parser.js +2418 -0
  504. package/build/treb-parser/src/parser.js.map +1 -0
  505. package/build/treb-utils/src/event_source.d.ts +34 -0
  506. package/build/treb-utils/src/event_source.js +110 -0
  507. package/build/treb-utils/src/event_source.js.map +1 -0
  508. package/build/treb-utils/src/ievent_source.d.ts +9 -0
  509. package/build/treb-utils/src/ievent_source.js +22 -0
  510. package/build/treb-utils/src/ievent_source.js.map +1 -0
  511. package/build/treb-utils/src/index.d.ts +6 -0
  512. package/build/treb-utils/src/index.js +30 -0
  513. package/build/treb-utils/src/index.js.map +1 -0
  514. package/build/treb-utils/src/measurement.d.ts +42 -0
  515. package/build/treb-utils/src/measurement.js +145 -0
  516. package/build/treb-utils/src/measurement.js.map +1 -0
  517. package/build/treb-utils/src/scale.d.ts +16 -0
  518. package/build/treb-utils/src/scale.js +106 -0
  519. package/build/treb-utils/src/scale.js.map +1 -0
  520. package/build/treb-utils/src/serialize_html.d.ts +5 -0
  521. package/build/treb-utils/src/serialize_html.js +128 -0
  522. package/build/treb-utils/src/serialize_html.js.map +1 -0
  523. package/build/treb-utils/src/validate_uri.d.ts +20 -0
  524. package/build/treb-utils/src/validate_uri.js +55 -0
  525. package/build/treb-utils/src/validate_uri.js.map +1 -0
  526. package/dist/{chunk-Z4XFMZ2X.mjs → chunk-E35ONJUS.mjs} +1 -1
  527. package/dist/treb-export-worker.mjs +2 -2
  528. package/dist/treb-spreadsheet.mjs +4 -4
  529. package/dist/treb.d.ts +1 -1
  530. package/esbuild-composite.mjs +5 -1
  531. package/package.json +67 -3
  532. package/treb-embed/src/custom-element/spreadsheet-constructor.ts +7 -3
  533. package/treb-embed/src/embedded-spreadsheet.ts +1 -1
  534. package/treb-grid/src/types/grid_options.ts +1 -1
  535. package/tsproject.json +1 -2
  536. package/dist/chunk-43DLP2OX.mjs +0 -11
  537. package/dist/chunk-4CKS56PE.mjs +0 -11
  538. package/dist/chunk-75PARUQE.mjs +0 -11
  539. package/dist/chunk-7QD63AZS.mjs +0 -24601
  540. package/dist/chunk-A55ARVRD.mjs +0 -11
  541. package/dist/chunk-DESAKYW4.mjs +0 -11
  542. package/dist/chunk-EQ2R5W6P.mjs +0 -24565
  543. package/dist/chunk-IYJU2J6D.mjs +0 -24601
  544. package/dist/chunk-KSJFPGXT.mjs +0 -11
  545. package/dist/chunk-MQK4DNXI.mjs +0 -11
  546. package/dist/chunk-ORQFKLXM.mjs +0 -24601
  547. package/dist/chunk-SFDNNDHY.mjs +0 -11
  548. package/dist/chunk-T47DX5MI.mjs +0 -11
  549. package/dist/chunk-T6ILBVEX.mjs +0 -11
  550. package/dist/chunk-TPRCDYYG.mjs +0 -11
  551. package/dist/chunk-YAHNOOHO.mjs +0 -11
  552. package/dist/chunk-YLCFKX2G.mjs +0 -24601
@@ -0,0 +1,2418 @@
1
+ /*
2
+ * This file is part of TREB.
3
+ *
4
+ * TREB is free software: you can redistribute it and/or modify it under the
5
+ * terms of the GNU General Public License as published by the Free Software
6
+ * Foundation, either version 3 of the License, or (at your option) any
7
+ * later version.
8
+ *
9
+ * TREB is distributed in the hope that it will be useful, but WITHOUT ANY
10
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
+ * details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License along
15
+ * with TREB. If not, see <https://www.gnu.org/licenses/>.
16
+ *
17
+ * Copyright 2022-2026 trebco, llc.
18
+ * info@treb.app
19
+ *
20
+ */
21
+ import { ArgumentSeparatorType, DecimalMarkType, DefaultParserConfig } from './parser-types';
22
+ /**
23
+ * regex determines if a sheet name requires quotes. centralizing
24
+ * this to simplify maintenance and reduce overlap/errors
25
+ */
26
+ export const QuotedSheetNameRegex = /[\s-+=<>!()]/;
27
+ /**
28
+ * similarly, illegal sheet name. we don't actually handle this in
29
+ * the parser, but it seems like a reasonable place to keep this
30
+ * definition.
31
+ */
32
+ export const IllegalSheetNameRegex = /['*\\]/;
33
+ const DOUBLE_QUOTE = 0x22; // '"'.charCodeAt(0);
34
+ const SINGLE_QUOTE = 0x27; // `'`.charCodeAt(0);
35
+ const NON_BREAKING_SPACE = 0xa0;
36
+ const SPACE = 0x20;
37
+ const TAB = 0x09;
38
+ const CR = 0x0a;
39
+ const LF = 0x0d;
40
+ const ZERO = 0x30;
41
+ const NINE = 0x39;
42
+ const PERIOD = 0x2e;
43
+ const PLUS = 0x2b;
44
+ const MINUS = 0x2d;
45
+ const OPEN_PAREN = 0x28;
46
+ const CLOSE_PAREN = 0x29;
47
+ const COMMA = 0x2c;
48
+ const PERCENT = 0x25;
49
+ const UNDERSCORE = 0x5f;
50
+ const DOLLAR_SIGN = 0x24;
51
+ const OPEN_BRACE = 0x7b;
52
+ const CLOSE_BRACE = 0x7d;
53
+ const OPEN_SQUARE_BRACKET = 0x5b;
54
+ const CLOSE_SQUARE_BRACKET = 0x5d;
55
+ const QUESTION_MARK = 0x3f;
56
+ const EXCLAMATION_MARK = 0x21;
57
+ // const COLON = 0x3a; // became an operator
58
+ const SEMICOLON = 0x3b;
59
+ const HASH = 0x23; // #
60
+ const AT = 0x40; // @
61
+ const UC_A = 0x41;
62
+ const LC_A = 0x61;
63
+ const UC_E = 0x45;
64
+ const LC_E = 0x65;
65
+ const UC_Z = 0x5a;
66
+ const LC_Z = 0x7a;
67
+ const LC_I = 0x69;
68
+ // const LC_J = 0x6a;
69
+ // there are a couple of characters we don't want in this
70
+ // range; we should split into separate ranges. also we
71
+ // probably have characters we don't need (atm)
72
+ const ACCENTED_RANGE_START = 192;
73
+ const ACCENTED_RANGE_END = 382; // bumping up for polish // 312;
74
+ /**
75
+ * precedence map
76
+ */
77
+ const binary_operators_precendence = {
78
+ '==': 6,
79
+ '!=': 6, // FIXME: we should not support these (legacy)
80
+ '<>': 6,
81
+ '=': 6, // these are the appropriate equality operators for SL
82
+ '<': 7,
83
+ '>': 7,
84
+ '<=': 7,
85
+ '>=': 7,
86
+ '+': 9,
87
+ '-': 9,
88
+ '&': 9,
89
+ '*': 10,
90
+ '/': 10,
91
+ '^': 11, // highest math op
92
+ ':': 13, // range operator
93
+ };
94
+ /* *
95
+ * binary ops are sorted by length so we can compare long ops first
96
+ switching to a composite w/ unary operators
97
+ * /
98
+ const binary_operators = Object.keys(binary_operators_precendence).sort(
99
+ (a, b) => b.length - a.length,
100
+ );
101
+
102
+ /**
103
+ * unary operators. atm we have no precedence issues, unary operators
104
+ * always have absolute precedence. (for numbers, these are properly part
105
+ * of the number, but consider `=-SUM(1,2)` -- this is an operator).
106
+ *
107
+ * implicit intersection operator should now have precedence over +/-.
108
+ */
109
+ const unary_operators = { '@': 50, '-': 100, '+': 100 };
110
+ /**
111
+ * to avoid the double - and +, we're just adding our one extra unary
112
+ * operator. doing this dynamically would be silly, although this does
113
+ * make this code more fragile.
114
+ */
115
+ const composite_operators = [...Object.keys(binary_operators_precendence), '@'].sort((a, b) => b.length - a.length);
116
+ /**
117
+ * parser for spreadsheet language.
118
+ *
119
+ * FIXME: this is stateless, think about exporting a singleton.
120
+ *
121
+ * (there is internal state, but it's only used during a Parse() call,
122
+ * which runs synchronously). one benefit of using a singleton would be
123
+ * consistency in decimal mark, we'd only have to set once.
124
+ *
125
+ * FIXME: the internal state is starting to grate. there's no reason for
126
+ * it and it just confuses things because parsing is stateless (except for
127
+ * configuration). internal state just keeps results from the last parse
128
+ * operation. we should refactor so parsing is clean and returns all
129
+ * results directly, caller can store if necessary.
130
+ *
131
+ * FIXME: split rendering into a separate class? would be a little cleaner.
132
+ *
133
+ * FIXME: we don't currently handle full-width punctuation. it would be
134
+ * simple to parse, a little more complicated to keep track of if we wanted
135
+ * to be able to rewrite. TODO/FIXME.
136
+ *
137
+ */
138
+ export class Parser {
139
+ /**
140
+ * accessor replacing old field. the actual value is moved to flags,
141
+ * and should be set via the SetLocaleSettings method.
142
+ */
143
+ get argument_separator() {
144
+ return this.flags.argument_separator;
145
+ }
146
+ /**
147
+ * accessor replacing old field. the actual value is moved to flags,
148
+ * and should be set via the SetLocaleSettings method.
149
+ */
150
+ get decimal_mark() {
151
+ return this.flags.decimal_mark;
152
+ }
153
+ /**
154
+ * unifying flags
155
+ */
156
+ flags = {
157
+ ...DefaultParserConfig,
158
+ };
159
+ /**
160
+ * FIXME: why is this a class member? at a minimum it could be static
161
+ * FIXME: why are we doing this with a regex?
162
+ */
163
+ r1c1_regex = /[rR]((?:\[[-+]{0,1}\d+\]|\d*))[cC]((?:\[[-+]{0,1}\d+\]|\d*))$/;
164
+ /**
165
+ * internal argument separator, as a number. this is set internally on
166
+ * parse call, following the argument_separator value.
167
+ */
168
+ argument_separator_char = COMMA;
169
+ /**
170
+ * internal decimal mark, as a number.
171
+ */
172
+ decimal_mark_char = PERIOD;
173
+ /**
174
+ * imaginary number value. this is "i", except for those EE weirdos who
175
+ * use "j". although I guess those guys put it in front, so it won't really
176
+ * work anyway... let's stick with "i" for now.
177
+ */
178
+ imaginary_char = LC_I;
179
+ /**
180
+ * imaginary number as text for matching
181
+ */
182
+ imaginary_number = 'i';
183
+ /**
184
+ * internal counter for incrementing IDs
185
+ */
186
+ id_counter = 0;
187
+ expression = '';
188
+ data = [];
189
+ index = 0;
190
+ length = 0;
191
+ /** success flag */
192
+ valid = true;
193
+ /** rolling error state */
194
+ error_position;
195
+ /** rolling error state */
196
+ error;
197
+ dependencies = {
198
+ addresses: {},
199
+ ranges: {},
200
+ };
201
+ // referenced addresses -- used to merge ranges/addresses, although I'm
202
+ // not sure that's actually all that useful
203
+ address_refcount = {};
204
+ /**
205
+ * full list of referenced addresses and ranges. we're adding this
206
+ * to support highlighting, for which we need multiple instances
207
+ * of a single address. the original dep list was used for graph dependencies,
208
+ * so we compressed the list.
209
+ *
210
+ * FIXME: use a single list, i.e. something like
211
+ *
212
+ * address -> [instance, instance]
213
+ *
214
+ * because that's a big API change it's going to have to wait. for now,
215
+ * use a second list.
216
+ *
217
+ * UPDATE: adding (otherwise unused) tokens, which could be named ranges.
218
+ * in the future we may pass in a list of names at parse time, and resolve
219
+ * them; for now we are just listing names.
220
+ */
221
+ full_reference_list = [];
222
+ /**
223
+ * cache for storing/restoring parser state, if we toggle it
224
+ */
225
+ parser_state_cache = [];
226
+ /**
227
+ * step towards protecting these values and setting them in one
228
+ * operation.
229
+ *
230
+ * UPDATE: switch order. argument separator is optional and implied.
231
+ */
232
+ SetLocaleSettings(decimal_mark, argument_separator) {
233
+ if (typeof argument_separator === 'undefined') {
234
+ argument_separator = (decimal_mark === DecimalMarkType.Comma) ?
235
+ ArgumentSeparatorType.Semicolon :
236
+ ArgumentSeparatorType.Comma;
237
+ }
238
+ // I suppose semicolon and period is allowable, although no one
239
+ // uses it. this test only works because we know the internal type
240
+ // representation, but that's fragile and not a good idea. FIXME
241
+ if (argument_separator === decimal_mark) {
242
+ throw new Error('invalid locale setting');
243
+ }
244
+ this.flags.argument_separator = argument_separator;
245
+ this.flags.decimal_mark = decimal_mark;
246
+ }
247
+ /**
248
+ * save local configuration to a buffer, so it can be restored. we're doing
249
+ * this because in a lot of places we're caching parser flagss, changing
250
+ * them, and then restoring them. that's become repetitive, fragile to
251
+ * changes or new flags, and annoying.
252
+ *
253
+ * config is managed in a list with push/pop semantics. we store it as
254
+ * JSON so there's no possibility we'll accidentally mutate.
255
+ *
256
+ * FIXME: while we're at it why not migrate the separators -> flags, so
257
+ * there's a single location for this kind of state? (...TODO)
258
+ *
259
+ */
260
+ Save() {
261
+ this.parser_state_cache.push(JSON.stringify(this.flags));
262
+ }
263
+ /**
264
+ * restore persisted config
265
+ * @see Save
266
+ */
267
+ Restore() {
268
+ const json = this.parser_state_cache.shift();
269
+ if (json) {
270
+ try {
271
+ this.flags = JSON.parse(json);
272
+ }
273
+ catch (err) {
274
+ console.error(err);
275
+ }
276
+ }
277
+ else {
278
+ console.warn("No parser state to restore");
279
+ }
280
+ }
281
+ /**
282
+ * recursive tree walk that allows substitution. this should be
283
+ * a drop-in replacement for the original Walk function but I'm
284
+ * keeping it separate temporarily just in case it breaks something.
285
+ *
286
+ * @param func - in this version function can return `true` (continue
287
+ * walking subtree), `false` (don't walk subtree), or an ExpressionUnit.
288
+ * in the last case, we'll replace the original unit with the substitution.
289
+ * obviously in that case we don't recurse.
290
+ */
291
+ Walk2(unit, func) {
292
+ const result = func(unit);
293
+ if (typeof result === 'object') {
294
+ return result;
295
+ }
296
+ switch (unit.type) {
297
+ case 'address':
298
+ case 'missing':
299
+ case 'literal':
300
+ case 'complex':
301
+ case 'identifier':
302
+ case 'operator':
303
+ case 'structured-reference':
304
+ break;
305
+ case 'dimensioned':
306
+ if (result) {
307
+ unit.expression = this.Walk2(unit.expression, func); // could be an issue
308
+ unit.unit = this.Walk2(unit.unit, func); // could be an issue
309
+ }
310
+ break;
311
+ case 'range':
312
+ if (func(unit)) {
313
+ unit.start = this.Walk2(unit.start, func); // could be an issue
314
+ unit.end = this.Walk2(unit.end, func); // could be an issue
315
+ }
316
+ break;
317
+ case 'binary':
318
+ if (func(unit)) {
319
+ unit.left = this.Walk2(unit.left, func);
320
+ unit.right = this.Walk2(unit.right, func);
321
+ }
322
+ break;
323
+ case 'unary':
324
+ if (func(unit)) {
325
+ unit.operand = this.Walk2(unit.operand, func);
326
+ }
327
+ break;
328
+ case 'group':
329
+ if (func(unit)) {
330
+ unit.elements = unit.elements.map(source => this.Walk2(source, func));
331
+ }
332
+ break;
333
+ case 'implicit-call':
334
+ if (func(unit)) {
335
+ unit.call = this.Walk2(unit.call, func);
336
+ unit.args = unit.args.map(source => this.Walk2(source, func));
337
+ }
338
+ break;
339
+ case 'call':
340
+ if (func(unit)) {
341
+ unit.args = unit.args.map(source => this.Walk2(source, func));
342
+ }
343
+ break;
344
+ }
345
+ return unit;
346
+ }
347
+ /**
348
+ * recursive tree walk.
349
+ *
350
+ * @param func function called on each node. for nodes that have children
351
+ * (operations, calls, groups) return false to skip the subtree, or true to
352
+ * traverse.
353
+ */
354
+ Walk(unit, func) {
355
+ switch (unit.type) {
356
+ case 'address':
357
+ case 'missing':
358
+ case 'literal':
359
+ case 'complex':
360
+ case 'identifier':
361
+ case 'operator':
362
+ case 'structured-reference':
363
+ func(unit);
364
+ return;
365
+ case 'dimensioned':
366
+ if (func(unit)) {
367
+ this.Walk(unit.expression, func);
368
+ this.Walk(unit.unit, func);
369
+ }
370
+ return;
371
+ case 'range':
372
+ if (func(unit)) {
373
+ this.Walk(unit.start, func);
374
+ this.Walk(unit.end, func);
375
+ }
376
+ return;
377
+ case 'binary':
378
+ if (func(unit)) {
379
+ this.Walk(unit.left, func);
380
+ this.Walk(unit.right, func);
381
+ }
382
+ return;
383
+ case 'unary':
384
+ if (func(unit)) {
385
+ this.Walk(unit.operand, func);
386
+ }
387
+ return;
388
+ case 'group':
389
+ if (func(unit)) {
390
+ // unit.elements.forEach((element) => this.Walk(element, func));
391
+ for (const element of unit.elements) {
392
+ this.Walk(element, func);
393
+ }
394
+ }
395
+ return;
396
+ case 'implicit-call':
397
+ if (func(unit)) {
398
+ this.Walk(unit.call, func);
399
+ for (const arg of unit.args) {
400
+ this.Walk(arg, func);
401
+ }
402
+ }
403
+ return;
404
+ case 'call':
405
+ if (func(unit)) {
406
+ for (const arg of unit.args) {
407
+ this.Walk(arg, func);
408
+ }
409
+ // unit.args.forEach((arg) => this.Walk(arg, func));
410
+ }
411
+ }
412
+ }
413
+ /** utility: transpose array */
414
+ Transpose(arr) {
415
+ const m = arr.length;
416
+ const transposed = [];
417
+ let n = 0;
418
+ for (let i = 0; i < m; i++) {
419
+ if (Array.isArray(arr[i])) {
420
+ n = Math.max(n, arr[i].length);
421
+ }
422
+ }
423
+ for (let i = 0; i < n; i++) {
424
+ transposed[i] = [];
425
+ for (let j = 0; j < m; j++) {
426
+ transposed[i][j] = arr[j] ? arr[j][i] : undefined;
427
+ }
428
+ }
429
+ return transposed;
430
+ }
431
+ /**
432
+ * renders the passed expression as a string.
433
+ * @param unit base expression
434
+ * @param offset offset for addresses, used to offset relative addresses
435
+ * (and ranges). this is for copy-and-paste or move operations.
436
+ * @param missing string to represent missing values (can be '', for functions)
437
+ *
438
+ * FIXME: we're accumulating too many arguments. need to switch to an
439
+ * options object. do that after the structured reference stuff merges.
440
+ *
441
+ */
442
+ Render(unit, options = {}) {
443
+ // defaults
444
+ const offset = options.offset || { rows: 0, columns: 0 };
445
+ const missing = options.missing ?? '(missing)';
446
+ // the rest are optional
447
+ /*
448
+ offset: { rows: number; columns: number } = { rows: 0, columns: 0 },
449
+ missing = '(missing)',
450
+ convert_decimal?: DecimalMarkType,
451
+ convert_argument_separator?: ArgumentSeparatorType,
452
+ convert_imaginary_number?: 'i'|'j',
453
+ long_structured_references?: boolean,
454
+ table_name?: string,
455
+
456
+ ): string {
457
+ */
458
+ const { convert_decimal, convert_argument_separator,
459
+ // convert_imaginary_number,
460
+ long_structured_references, table_name, } = options;
461
+ // use default separator, unless we're explicitly converting.
462
+ let separator = this.flags.argument_separator + ' ';
463
+ if (convert_argument_separator === ArgumentSeparatorType.Comma) {
464
+ separator = ', ';
465
+ }
466
+ else if (convert_argument_separator === ArgumentSeparatorType.Semicolon) {
467
+ separator = '; ';
468
+ }
469
+ /*
470
+ let imaginary_character = this.imaginary_number;
471
+ if (convert_imaginary_number) {
472
+ imaginary_character = convert_imaginary_number;
473
+ }
474
+ */
475
+ // this is only used if we're converting.
476
+ const decimal = convert_decimal === DecimalMarkType.Comma ? ',' : '.';
477
+ const decimal_rex = this.flags.decimal_mark === DecimalMarkType.Comma ? /,/ : /\./;
478
+ // we need this for complex numbers, but I don't want to change the
479
+ // original at the moment, just in case. we can run through that later.
480
+ const decimal_rex_g = this.flags.decimal_mark === DecimalMarkType.Comma ? /,/g : /\./g;
481
+ switch (unit.type) {
482
+ case 'address':
483
+ if (options.pass_through_addresses) {
484
+ return unit.label;
485
+ }
486
+ return options.r1c1 ? this.R1C1Label(unit, options) : this.AddressLabel(unit, offset);
487
+ case 'range':
488
+ if (options.pass_through_addresses) {
489
+ return unit.label;
490
+ }
491
+ return options.r1c1 ?
492
+ this.R1C1Label(unit.start, options) + ':' +
493
+ this.R1C1Label(unit.end, options) :
494
+ this.AddressLabel(unit.start, offset) + ':' + this.AddressLabel(unit.end, offset);
495
+ case 'missing':
496
+ return missing;
497
+ case 'array':
498
+ // we have to transpose because we're column-major but the
499
+ // format is row-major
500
+ return '{' +
501
+ this.Transpose(unit.values).map((row) => row.map((value) => {
502
+ if (typeof value === 'string') {
503
+ return '"' + value + '"';
504
+ }
505
+ return value;
506
+ }).join(', ')).join('; ') + '}';
507
+ case 'binary':
508
+ // in some cases we might see range constructs as binary units
509
+ // because one side (or maybe both sides) of the range is a
510
+ // function. in that case we don't want a space in front of the
511
+ // operator.
512
+ // UPDATE: for aesthetic reasons, also remove spaces around a
513
+ // power operator (caret, "^")
514
+ // FIXME: parameterize?
515
+ {
516
+ const separator = ((unit.operator === ':' || unit.operator === '^') ? '' : ' ');
517
+ return (this.Render(unit.left, options) +
518
+ separator +
519
+ unit.operator +
520
+ separator +
521
+ this.Render(unit.right, options));
522
+ }
523
+ case 'unary':
524
+ return (unit.operator +
525
+ this.Render(unit.operand, options));
526
+ case 'complex':
527
+ // formatting complex value (note for searching)
528
+ // this uses small regular "i"
529
+ // as with literals, we want to preserve the original text,
530
+ // which might have slight precision differences from what
531
+ // we would render.
532
+ if (unit.text) {
533
+ if (convert_decimal) {
534
+ // we don't support grouping numbers for complex, so there's
535
+ // no need to handle grouping
536
+ const text = unit.text;
537
+ return text.replace(decimal_rex_g, decimal);
538
+ }
539
+ else {
540
+ return unit.text;
541
+ }
542
+ }
543
+ else {
544
+ // if we don't have the original text for whatever reason, format
545
+ // and convert if necessary.
546
+ let imaginary_text = Math.abs(unit.imaginary).toString();
547
+ if (convert_decimal === DecimalMarkType.Comma || this.flags.decimal_mark === DecimalMarkType.Comma) {
548
+ imaginary_text = imaginary_text.replace(/\./, ',');
549
+ }
550
+ if (unit.real) {
551
+ let real_text = unit.real.toString();
552
+ if (convert_decimal === DecimalMarkType.Comma || this.flags.decimal_mark === DecimalMarkType.Comma) {
553
+ real_text = real_text.replace(/\./, ',');
554
+ }
555
+ const i = Math.abs(unit.imaginary);
556
+ return `${real_text}${unit.imaginary < 0 ? ' - ' : ' + '}${i === 1 ? '' : imaginary_text}i`;
557
+ }
558
+ else if (unit.imaginary === -1) {
559
+ return `-i`;
560
+ }
561
+ else if (unit.imaginary === 1) {
562
+ return `i`;
563
+ }
564
+ else {
565
+ return `${unit.imaginary < 0 ? '-' : ''}${imaginary_text}i`;
566
+ }
567
+ }
568
+ break;
569
+ case 'literal':
570
+ if (typeof unit.value === 'string') {
571
+ // escape any quotation marks in string
572
+ return '"' + unit.value.replace(/"/g, '""') + '"';
573
+ }
574
+ else if (typeof unit.value === 'boolean') {
575
+ // use render option (replacement) value; then flags value; then a default
576
+ if (unit.value) {
577
+ return options.boolean_true || this.flags.boolean_true || 'true'; // default
578
+ }
579
+ else {
580
+ return options.boolean_false || this.flags.boolean_false || 'false'; // default
581
+ }
582
+ }
583
+ else if (convert_decimal && typeof unit.value === 'number') {
584
+ if (unit.text) {
585
+ // here we want to translate the literal typed-in value.
586
+ // users can type in a decimal point and possibly grouping.
587
+ // if we are converting from dot to comma, we need to make
588
+ // sure to remove any existing commas. for the time being
589
+ // we will just remove them.
590
+ // what about the alternate case? in that case, we're not allowing
591
+ // users to type in groupings (I think), so we can skip that part.
592
+ // ACTUALLY, we don't allow grouping at all. we normalize it
593
+ // if you type in a number. why? consider functions, grouping
594
+ // looks like parameter separation. so no.
595
+ let text = unit.text;
596
+ if (convert_decimal === DecimalMarkType.Comma &&
597
+ this.flags.decimal_mark === DecimalMarkType.Period) {
598
+ text = text.replace(/,/g, ''); // remove grouping
599
+ }
600
+ return text.replace(decimal_rex, decimal);
601
+ }
602
+ else {
603
+ // this always works because this function is guaranteed
604
+ // to return value in dot-decimal format without separators.
605
+ return unit.value.toString().replace(/\./, decimal);
606
+ }
607
+ }
608
+ else if (unit.text)
609
+ return unit.text;
610
+ return unit.value.toString();
611
+ case 'identifier':
612
+ return unit.name;
613
+ case 'operator':
614
+ return '[' + unit.operator + ']'; // this should be invalid output
615
+ case 'group':
616
+ if (unit.explicit) {
617
+ return ('(' +
618
+ unit.elements
619
+ .map((x) => this.Render(x, options)).join(separator) +
620
+ ')');
621
+ }
622
+ else {
623
+ return unit.elements
624
+ .map((x) => this.Render(x, options)).join(separator);
625
+ }
626
+ case 'implicit-call':
627
+ return this.Render(unit.call, options) +
628
+ '(' + unit.args.map(element => this.Render(element, options)).join(separator) + ')';
629
+ case 'call':
630
+ return (unit.name +
631
+ '(' +
632
+ unit.args
633
+ .map((x) => this.Render(x, options)).join(separator) +
634
+ ')');
635
+ case 'dimensioned':
636
+ return this.Render(unit.expression) + ' ' + this.Render(unit.unit);
637
+ case 'structured-reference':
638
+ // not sure of the rules around one or two braces for the
639
+ // column name... certainly spaces means you need at least one
640
+ {
641
+ let column = unit.column;
642
+ if (/[^A-Za-z]/.test(column)) {
643
+ column = '[' + column + ']';
644
+ }
645
+ let table = unit.table;
646
+ // console.info("RENDER SR", unit, table_name, long_structured_references);
647
+ if (!table && long_structured_references && table_name) {
648
+ table = table_name;
649
+ }
650
+ switch (unit.scope) {
651
+ case 'all':
652
+ return `${table}[[#all],${column}]`;
653
+ case 'row':
654
+ if (long_structured_references) {
655
+ return `${table}[[#this row],${column}]`;
656
+ }
657
+ else {
658
+ return `${table}[@${column}]`;
659
+ }
660
+ case 'column':
661
+ return `${table}[${column}]`;
662
+ }
663
+ // this is here in case we add a new scope in the future,
664
+ // so we remember to handle this case
665
+ throw new Error('unhandled scope in structured reference');
666
+ }
667
+ }
668
+ return '??';
669
+ }
670
+ /**
671
+ * parses expression and returns the root of the parse tree, plus a
672
+ * list of dependencies (addresses and ranges) found in the expression.
673
+ *
674
+ * NOTE that in the new address parsing structure, we will overlap ranges
675
+ * and addresses (range corners). this is OK because ranges are mapped
676
+ * to individual address dependencies. it's just sloppy (FIXME: refcount?)
677
+ */
678
+ Parse(expression) {
679
+ // normalize
680
+ expression = expression.trim();
681
+ // remove leading =
682
+ if (expression[0] === '=') {
683
+ expression = expression.substr(1).trim();
684
+ }
685
+ this.expression = expression;
686
+ this.data = [];
687
+ this.length = expression.length;
688
+ this.index = 0;
689
+ this.valid = true;
690
+ this.error_position = undefined;
691
+ this.error = undefined;
692
+ this.dependencies.addresses = {};
693
+ this.dependencies.ranges = {};
694
+ this.address_refcount = {};
695
+ this.full_reference_list = [];
696
+ // reset ID
697
+ this.id_counter = 0;
698
+ // set separator
699
+ switch (this.flags.argument_separator) {
700
+ case ArgumentSeparatorType.Semicolon:
701
+ this.argument_separator_char = SEMICOLON;
702
+ break;
703
+ default:
704
+ this.argument_separator_char = COMMA;
705
+ break;
706
+ }
707
+ // and decimal mark
708
+ switch (this.flags.decimal_mark) {
709
+ case DecimalMarkType.Comma:
710
+ this.decimal_mark_char = COMMA;
711
+ break;
712
+ default:
713
+ this.decimal_mark_char = PERIOD;
714
+ break;
715
+ }
716
+ // NOTE on this function: charCodeAt returns UTF-16. codePointAt returns
717
+ // unicode. length returns UTF-16 length. any characters that are not
718
+ // representable as a single character in UTF-16 will be 'the first unit
719
+ // of a surrogate pair...' and so on.
720
+ //
721
+ // we want UTF-16, not unicode. for the parser itself, we are only really
722
+ // looking for ASCII, so it's not material. for anything else, if we
723
+ // construct strings from the original data we want to map the UTF-16,
724
+ // otherwise we will construct the string incorrectly. this applies to
725
+ // strings, function names, and anything else.
726
+ //
727
+ // which is all a long way of saying, don't be tempted to replace this
728
+ // with codePointAt.
729
+ for (let i = 0; i < this.length; i++) {
730
+ this.data[i] = expression.charCodeAt(i);
731
+ }
732
+ const expr = this.ParseGeneric();
733
+ // last pass: convert any remaining imaginary values to complex values.
734
+ // FIXME: could do this elsewhere? not sure we should be adding yet
735
+ // another loop...
736
+ // (moving)
737
+ // remove extraneous addresses
738
+ // NOTE: we still may have duplicates that have different absolute/relative
739
+ // modifiers, e.g. C3 and $C$3 (and $C3 and C$3). not sure what we should
740
+ // do about that, since some consumers may consider these different -- we
741
+ // need to establish a contract about this
742
+ const addresses = {};
743
+ for (const key of Object.keys(this.dependencies.addresses)) {
744
+ if (this.address_refcount[key]) {
745
+ addresses[key] = this.dependencies.addresses[key];
746
+ }
747
+ }
748
+ this.dependencies.addresses = addresses;
749
+ return {
750
+ expression: expr || undefined,
751
+ valid: this.valid,
752
+ error: this.error,
753
+ error_position: this.error_position,
754
+ dependencies: this.dependencies,
755
+ separator: this.flags.argument_separator,
756
+ decimal_mark: this.flags.decimal_mark,
757
+ full_reference_list: this.full_reference_list.slice(0),
758
+ };
759
+ }
760
+ /** generates column label ("A") from column index (0-based) */
761
+ ColumnLabel(column) {
762
+ if (column === Infinity) {
763
+ return '';
764
+ }
765
+ let s = String.fromCharCode(65 + (column % 26));
766
+ while (column > 25) {
767
+ column = Math.floor(column / 26) - 1;
768
+ s = String.fromCharCode(65 + (column % 26)) + s;
769
+ }
770
+ return s;
771
+ }
772
+ /**
773
+ * generates absolute or relative R1C1 address
774
+ *
775
+ * FIXME: not supporting relative (offset) addresses atm? I'd like to
776
+ * change this but I don't want to break anything...
777
+ */
778
+ R1C1Label(address, options) {
779
+ const force_relative = !!options.r1c1_force_relative;
780
+ const base = options.r1c1_base;
781
+ let label = '';
782
+ if (address.sheet) { // && (!base?.sheet || base?.sheet !== address.sheet)) {
783
+ label = (QuotedSheetNameRegex.test(address.sheet) ?
784
+ '\'' + address.sheet + '\'' : address.sheet) + '!';
785
+ }
786
+ let row = '';
787
+ let column = '';
788
+ if (force_relative && options.r1c1_proper_semantics && base) {
789
+ if (address.absolute_row) {
790
+ row = (address.row + 1).toString();
791
+ }
792
+ else {
793
+ const delta_row = address.row - base.row;
794
+ if (delta_row) {
795
+ row = `[${delta_row}]`;
796
+ }
797
+ }
798
+ if (address.absolute_column) {
799
+ column = (address.column + 1).toString();
800
+ }
801
+ else {
802
+ const delta_column = address.column - base.column;
803
+ if (delta_column) {
804
+ column = `[${delta_column}]`;
805
+ }
806
+ }
807
+ }
808
+ else if (force_relative && base) {
809
+ const delta_row = address.row - base.row;
810
+ const delta_column = address.column - base.column;
811
+ if (delta_row) {
812
+ row = `[${delta_row}]`;
813
+ }
814
+ if (delta_column) {
815
+ column = `[${delta_column}]`;
816
+ }
817
+ }
818
+ else {
819
+ row = address.offset_row ? `[${address.row}]` : (address.row + 1).toString();
820
+ column = address.offset_column ? `[${address.column}]` : (address.column + 1).toString();
821
+ }
822
+ /*
823
+ const row = (address.absolute_row || !base) ? (address.row + 1).toString() : `[${address.row - base.row}]`;
824
+ const column = (address.absolute_column || !base) ? (address.column + 1).toString() : `[${address.column - base.column}]`;
825
+ */
826
+ label += `R${row}C${column}`;
827
+ return label;
828
+ }
829
+ /**
830
+ * generates address label ("C3") from address (0-based).
831
+ *
832
+ * @param offset - offset by some number of rows or columns
833
+ * @param r1c1 - if set, return data in R1C1 format.
834
+ */
835
+ AddressLabel(address, offset) {
836
+ let column = address.column;
837
+ if (!address.absolute_column && address.column !== Infinity)
838
+ column += offset.columns;
839
+ let row = address.row;
840
+ if (!address.absolute_row && address.row !== Infinity)
841
+ row += offset.rows;
842
+ if (row < 0 || column < 0 || (row === Infinity && column === Infinity))
843
+ return '#REF';
844
+ let label = '';
845
+ if (address.sheet) {
846
+ label = (QuotedSheetNameRegex.test(address.sheet) ?
847
+ '\'' + address.sheet + '\'' : address.sheet) + '!';
848
+ }
849
+ if (row === Infinity) {
850
+ return label +
851
+ (address.absolute_column ? '$' : '') +
852
+ this.ColumnLabel(column);
853
+ }
854
+ if (column === Infinity) {
855
+ return label +
856
+ (address.absolute_row ? '$' : '') +
857
+ (row + 1);
858
+ }
859
+ return (label +
860
+ (address.absolute_column ? '$' : '') +
861
+ this.ColumnLabel(column) +
862
+ (address.absolute_row ? '$' : '') +
863
+ (row + 1) +
864
+ (address.spill ? '#' : ''));
865
+ }
866
+ /**
867
+ * base parse routine; may recurse inside parens (either as grouped
868
+ * operations or in function arguments).
869
+ *
870
+ * @param exit exit on specific characters
871
+ */
872
+ ParseGeneric(exit = [0], explicit_group = false) {
873
+ let stream = [];
874
+ for (; this.index < this.length;) {
875
+ const unit = this.ParseNext(stream.length === 0);
876
+ if (typeof unit === 'number') {
877
+ if (exit.some((test) => unit === test)) {
878
+ break;
879
+ }
880
+ else if (unit === OPEN_PAREN) {
881
+ // note that function calls are handled elsewhere,
882
+ // so we only have to worry about grouping. parse
883
+ // up to the closing paren...
884
+ // actually now we have implicit calls, so we need
885
+ // to manage that here.
886
+ this.index++; // open paren
887
+ const group = this.ParseGeneric([CLOSE_PAREN], true);
888
+ this.index++; // close paren
889
+ // and wrap up in a group element to prevent reordering.
890
+ // flag indicates that this is a user grouping, not ours
891
+ // skip nulls
892
+ // ...don't skip nulls? don't know what the rationale was
893
+ // but for implicit calls we will need to support empty arguments
894
+ // if (group) {
895
+ stream.push({
896
+ type: 'group',
897
+ id: this.id_counter++,
898
+ elements: group ? [group] : [],
899
+ explicit: true,
900
+ });
901
+ //}
902
+ }
903
+ else {
904
+ // this can probably move to PNext? except for the test
905
+ // on looking for a binary operator? (...)
906
+ const operator = this.ConsumeOperator();
907
+ if (operator) {
908
+ stream.push(operator);
909
+ }
910
+ else if (explicit_group && unit === this.argument_separator_char) {
911
+ // adding a new unit type here to explicitly show we're in
912
+ // a group; prevents later passes from treating arguments as
913
+ // fractions or something else. we just need to remove these
914
+ // later
915
+ stream.push({
916
+ type: 'group-separator',
917
+ position: this.index,
918
+ id: this.id_counter++,
919
+ });
920
+ this.index++;
921
+ }
922
+ else {
923
+ this.error = `unexpected character [1]: ${String.fromCharCode(unit)}, 0x${unit.toString(16)}`;
924
+ this.valid = false;
925
+ this.index++;
926
+ }
927
+ }
928
+ }
929
+ else {
930
+ stream.push(unit);
931
+ }
932
+ }
933
+ // why do we build ranges after doing reordering? since ranges
934
+ // have the highest precedence (after complex numbers), why not
935
+ // just run through them now? also we could merge the complex
936
+ // composition (or not, since that's optional)
937
+ // ...
938
+ // OK, doing that now (testing). a side benefit is that this solves
939
+ // one of the problems we had with complex numbers, mismatching naked
940
+ // column identifiers like I:J. if we do ranges first we will not run
941
+ // into that problem.
942
+ if (stream.length) {
943
+ stream = this.BinaryToRange2(stream);
944
+ // FIXME: fractions should perhaps move, not sure about the proper
945
+ // ordering...
946
+ if (this.flags.fractions) {
947
+ // the specific pattern we are looking for for a fraction is
948
+ //
949
+ // literal (integer)
950
+ // literal (integer)
951
+ // operator (/)
952
+ // literal (integer)
953
+ //
954
+ // NOTE: excel actually translates these functions after you
955
+ // enter them to remove the fractions. not sure why, but it's
956
+ // possible that exporting them to something else (lotus?) wouldn't
957
+ // work. we can export them to excel, however, so maybe we can just
958
+ // leave as-is.
959
+ const rebuilt = [];
960
+ const IsInteger = (test) => {
961
+ return (test.type === 'literal')
962
+ && ((typeof test.value) === 'number')
963
+ && (test.value % 1 === 0); // bad typescript
964
+ };
965
+ let i = 0;
966
+ for (; i < stream.length - 3; i++) {
967
+ if (IsInteger(stream[i])
968
+ && IsInteger(stream[i + 1])
969
+ && (stream[i + 2].type === 'operator' && stream[i + 2].operator === '/')
970
+ && IsInteger(stream[i + 3])) {
971
+ const a = stream[i];
972
+ const b = stream[i + 1];
973
+ const c = stream[i + 3];
974
+ const f = ((a.value < 0) ? -1 : 1) * (b.value / c.value);
975
+ i += 3;
976
+ rebuilt.push({
977
+ id: stream[i].id,
978
+ type: 'literal',
979
+ text: this.expression.substring(a.position, c.position + 1),
980
+ value: a.value + f,
981
+ position: a.position,
982
+ });
983
+ }
984
+ else {
985
+ rebuilt.push(stream[i]);
986
+ }
987
+ }
988
+ for (; i < stream.length; i++) {
989
+ rebuilt.push(stream[i]);
990
+ }
991
+ stream = rebuilt;
992
+ }
993
+ // so we're moving complex handling to post-reordering, to support
994
+ // precedence properly. there's still one thing we have to do here,
995
+ // though: handle those cases of naked imaginary values "i". these
996
+ // will be text identifiers, because they don't look like anything
997
+ // else. the previous routine will have pulled out column ranges like
998
+ // I:I so we don't have to worry about that anymore.
999
+ stream = stream.map(test => {
1000
+ if (test.type === 'identifier' && test.name === this.imaginary_number) {
1001
+ return {
1002
+ type: 'complex',
1003
+ real: 0,
1004
+ imaginary: 1,
1005
+ position: test.position,
1006
+ text: test.name,
1007
+ id: this.id_counter++,
1008
+ };
1009
+ }
1010
+ return test;
1011
+ });
1012
+ if (this.flags.dimensioned_quantities) {
1013
+ // support dimensioned quantities. we need to think a little about what
1014
+ // should and should not be supported here -- definitely a literal
1015
+ // followed by an identifier; definitely not two identifiers in a row;
1016
+ // (really?) definitely not expressions followed by identifiers...
1017
+ //
1018
+ // what about
1019
+ // group: (3+2)mm [yes]
1020
+ // call: sin(3)mm [yes]
1021
+ // name?: Xmm [...]
1022
+ //
1023
+ // what about space?
1024
+ // 10 fluid ounces
1025
+ // 10 fl oz
1026
+ //
1027
+ const rebuilt = [];
1028
+ let unit;
1029
+ for (let i = 0; i < stream.length; i++) {
1030
+ //for (const entry of stream) {
1031
+ const entry = stream[i];
1032
+ if (!unit) {
1033
+ unit = entry;
1034
+ }
1035
+ else if (entry.type === 'identifier' && (unit.type === 'literal' || unit.type === 'group' || unit.type === 'call')) {
1036
+ // check for multi-word unit (unit has spaces)
1037
+ const identifier = entry;
1038
+ while (stream[i + 1]?.type === 'identifier') {
1039
+ identifier.name += (' ' + stream[++i].name);
1040
+ }
1041
+ rebuilt.push({
1042
+ type: 'dimensioned',
1043
+ expression: unit,
1044
+ unit: entry,
1045
+ id: this.id_counter++,
1046
+ });
1047
+ unit = undefined; // consume
1048
+ }
1049
+ else {
1050
+ rebuilt.push(unit);
1051
+ unit = entry;
1052
+ }
1053
+ }
1054
+ // trailer
1055
+ if (unit) {
1056
+ rebuilt.push(unit);
1057
+ }
1058
+ stream = rebuilt;
1059
+ }
1060
+ }
1061
+ // console.info("STREAM\n", stream, "\n\n");
1062
+ if (stream.length === 0)
1063
+ return null;
1064
+ if (stream.length === 1)
1065
+ return stream[0];
1066
+ // fix ordering of binary operations based on precedence; also
1067
+ // convert and validate ranges
1068
+ return this.BinaryToComplex(this.ArrangeUnits(stream));
1069
+ }
1070
+ /**
1071
+ * helper function, @see BinaryToRange
1072
+ * @param unit
1073
+ * @returns
1074
+ */
1075
+ UnitToAddress(unit) {
1076
+ // console.info("U2", unit);
1077
+ // for literals, only numbers are valid
1078
+ if (unit.type === 'literal') {
1079
+ if (typeof unit.value === 'number' && unit.value > 0 && !/\./.test(unit.text || '')) {
1080
+ return {
1081
+ type: 'address',
1082
+ position: unit.position,
1083
+ label: unit.value.toString(),
1084
+ row: unit.value - 1,
1085
+ id: this.id_counter++,
1086
+ column: Infinity,
1087
+ };
1088
+ }
1089
+ }
1090
+ else {
1091
+ // UPDATE: sheet names... we may actually need a subparser for this?
1092
+ // or can we do it with a regex? (...)
1093
+ let sheet;
1094
+ let name = unit.name;
1095
+ const tokens = name.split('!');
1096
+ if (tokens.length > 1) {
1097
+ sheet = tokens.slice(0, tokens.length - 1).join('!');
1098
+ name = name.substr(sheet.length + 1);
1099
+ if (sheet[0] === '\'') {
1100
+ if (sheet.length > 1 && sheet[sheet.length - 1] === '\'') {
1101
+ sheet = sheet.substr(1, sheet.length - 2);
1102
+ }
1103
+ else {
1104
+ // console.info('mismatched single quote');
1105
+ return undefined;
1106
+ }
1107
+ }
1108
+ }
1109
+ const absolute = name[0] === '$';
1110
+ name = (absolute ? name.substr(1) : name).toUpperCase();
1111
+ const as_number = Number(name);
1112
+ // if it looks like a number, consider it a number and then be strict
1113
+ if (!isNaN(as_number)) {
1114
+ if (as_number > 0 && as_number !== Infinity && !/\./.test(name)) {
1115
+ return {
1116
+ type: 'address',
1117
+ position: unit.position,
1118
+ absolute_row: absolute,
1119
+ label: unit.name,
1120
+ row: as_number - 1,
1121
+ id: this.id_counter++,
1122
+ column: Infinity,
1123
+ sheet,
1124
+ };
1125
+ }
1126
+ }
1127
+ else if (/[A-Z]{1,3}/.test(name)) {
1128
+ let column = -1; // clever
1129
+ for (let i = 0; i < name.length; i++) {
1130
+ const char = name[i].charCodeAt(0);
1131
+ column = 26 * (1 + column) + (char - UC_A);
1132
+ }
1133
+ return {
1134
+ type: 'address',
1135
+ position: unit.position,
1136
+ absolute_column: absolute,
1137
+ label: unit.name,
1138
+ column,
1139
+ id: this.id_counter++,
1140
+ row: Infinity,
1141
+ sheet,
1142
+ };
1143
+ }
1144
+ }
1145
+ return undefined;
1146
+ }
1147
+ /**
1148
+ * rewrite of binary to range. this version operates on the initial stream,
1149
+ * which should be OK because range has the highest precedence so we would
1150
+ * never reorder a range.
1151
+ *
1152
+ * ACTUALLY this will break in the case of
1153
+ *
1154
+ * -15:16
1155
+ *
1156
+ * (I think that's the only case). we can fix that though. this should
1157
+ * not impact the case of `2-15:16`, because in that case the - will look
1158
+ * like an operator and not part of the number. the same goes for a leading
1159
+ * `+` which will get dropped implicitly but has no effect (we might want
1160
+ * to preserve it for consistency though).
1161
+ *
1162
+ * NOTE: that error existed in the old version, too, and this way is perhaps
1163
+ * better for fixing it. we should merge this into main.
1164
+ *
1165
+ *
1166
+ * old version comments:
1167
+ * ---
1168
+ *
1169
+ * converts binary operations with a colon operator to ranges. this also
1170
+ * validates that there are no colon operations with non-address operands
1171
+ * (which is why it's called after precendence reordering; colon has the
1172
+ * highest preference). recursive only over binary ops AND unary ops.
1173
+ *
1174
+ * NOTE: there are other legal arguments to a colon operator. specifically:
1175
+ *
1176
+ * (1) two numbers, in either order
1177
+ *
1178
+ * 15:16
1179
+ * 16:16
1180
+ * 16:15
1181
+ *
1182
+ * (2) with one or both optionally having a $
1183
+ *
1184
+ * 15:$16
1185
+ * $16:$16
1186
+ *
1187
+ * (3) two column identifiers, in either order
1188
+ *
1189
+ * A:F
1190
+ * B:A
1191
+ *
1192
+ * (4) and the same with $
1193
+ *
1194
+ * $A:F
1195
+ * $A:$F
1196
+ *
1197
+ * because none of these are legal in any other context, we leave the
1198
+ * default treatment of them UNLESS they are arguments to the colon
1199
+ * operator, in which case we will grab them. that does mean we parse
1200
+ * them twice, but (...)
1201
+ *
1202
+ * FIXME: will need some updated to rendering these, we don't have any
1203
+ * handler for rendering infinity
1204
+ */
1205
+ BinaryToRange2(stream) {
1206
+ const result = [];
1207
+ for (let i = 0; i < stream.length; i++) {
1208
+ const a = stream[i];
1209
+ const b = stream[i + 1];
1210
+ const c = stream[i + 2];
1211
+ let range;
1212
+ let label = '';
1213
+ let negative; // this is a fix for the error case `-14:15`, see below
1214
+ if (a && b && c && b.type === 'operator' && b.operator === ':') {
1215
+ if (a.type === 'address' && c.type === 'address') {
1216
+ // construct a label using the full text. there's a possibility,
1217
+ // I suppose, that there are spaces (this should probably not be
1218
+ // legal). this is a canonical label, though (generated)
1219
+ // it might be better to let this slip, or treat it as an error
1220
+ // and force a correction... not sure (TODO/FIXME)
1221
+ const start_index = a.position + a.label.length;
1222
+ const end_index = c.position;
1223
+ range = {
1224
+ type: 'range',
1225
+ id: this.id_counter++,
1226
+ position: a.position,
1227
+ start: a,
1228
+ end: c,
1229
+ label: a.label +
1230
+ this.expression.substring(start_index, end_index) +
1231
+ c.label,
1232
+ };
1233
+ label = range.start.label + ':' + range.end.label;
1234
+ this.address_refcount[range.start.label]--;
1235
+ this.address_refcount[range.end.label]--;
1236
+ // remove entries from the list for start, stop
1237
+ const positions = [a.position, c.position];
1238
+ this.full_reference_list = this.full_reference_list.filter((test) => {
1239
+ return (test.position !== positions[0] && test.position !== positions[1]);
1240
+ });
1241
+ }
1242
+ else if ((a.type === 'literal' || a.type === 'identifier')
1243
+ && (c.type === 'literal' || c.type === 'identifier')) {
1244
+ // see if we can plausibly interpret both of these as rows or columns
1245
+ // this is a fix for the case of `-14:15`, which is kind of a rare
1246
+ // case but could happen. in that case we need to invert the first number,
1247
+ // so it parses as an address properly, and also insert a "-" which
1248
+ // should be treated as a unary operator.
1249
+ // if this happens, the first part must look like a negative number,
1250
+ // e.g. -10, so there are no leading spaces or intervening spaces
1251
+ // between the - and the value. therefore...
1252
+ let left = this.UnitToAddress(a);
1253
+ if (!left && a.type === 'literal' && typeof a.value === 'number' && a.value < 0) {
1254
+ const test = {
1255
+ ...a,
1256
+ text: (a.text || '').replace(/^-/, ''), // <- ...sign always in position 0
1257
+ position: a.position + 1, // <- ...advance 1
1258
+ value: -a.value, // <- ...invert value
1259
+ };
1260
+ left = this.UnitToAddress(test);
1261
+ if (left) {
1262
+ // if that worked, we need to insert an operator into the
1263
+ // stream to reflect the - sign. we use the original position.
1264
+ negative = {
1265
+ type: 'operator',
1266
+ operator: '-',
1267
+ position: a.position,
1268
+ id: this.id_counter++,
1269
+ };
1270
+ }
1271
+ }
1272
+ const right = this.UnitToAddress(c);
1273
+ // and they need to match
1274
+ if (left && right
1275
+ && ((left.column === Infinity && right.column === Infinity)
1276
+ || (left.row === Infinity && right.row === Infinity))) {
1277
+ label = left.label + ':' + right.label;
1278
+ // we don't support out-of-order ranges, so we should correct.
1279
+ // they just won't work otherwise. (TODO/FIXME)
1280
+ range = {
1281
+ type: 'range',
1282
+ id: this.id_counter++,
1283
+ position: left.position,
1284
+ start: left,
1285
+ end: right,
1286
+ label,
1287
+ };
1288
+ }
1289
+ }
1290
+ }
1291
+ if (range) {
1292
+ if (negative) {
1293
+ result.push(negative);
1294
+ }
1295
+ result.push(range);
1296
+ this.dependencies.ranges[label] = range;
1297
+ this.full_reference_list.push(range);
1298
+ // skip
1299
+ i += 2;
1300
+ }
1301
+ else {
1302
+ result.push(a);
1303
+ }
1304
+ }
1305
+ return result;
1306
+ }
1307
+ /**
1308
+ * we've now come full circle. we started with handling ranges as
1309
+ * binary operators; then we added complex composition as a first-pass
1310
+ * function; then we moved ranges to a first-pass function; and now we're
1311
+ * moving complex composition to a lower-level restructuring of binary
1312
+ * operations.
1313
+ *
1314
+ * that allows better precedence handling for (potentially) ambiguous
1315
+ * constructions like =B3 * 2 + 3i. we do have parens, so.
1316
+ *
1317
+ * @param unit
1318
+ * @returns
1319
+ */
1320
+ BinaryToComplex(unit) {
1321
+ if (unit.type === 'binary') {
1322
+ if ((unit.operator === '+' || unit.operator === '-')
1323
+ && unit.left.type === 'literal'
1324
+ && typeof unit.left.value === 'number'
1325
+ && unit.right.type === 'complex' // 'imaginary') {
1326
+ && !unit.right.composited) {
1327
+ // ok, compose
1328
+ // console.info("WANT TO COMPOSE", unit);
1329
+ let text = '';
1330
+ text = this.expression.substring(unit.left.position, unit.right.position + (unit.right.text?.length || 0));
1331
+ let imaginary_value = unit.right.imaginary;
1332
+ if (unit.operator === '-') {
1333
+ imaginary_value = -imaginary_value;
1334
+ }
1335
+ return {
1336
+ type: 'complex',
1337
+ position: unit.left.position,
1338
+ text: text,
1339
+ id: this.id_counter++,
1340
+ imaginary: imaginary_value,
1341
+ real: unit.left.value,
1342
+ composited: true,
1343
+ };
1344
+ }
1345
+ else {
1346
+ unit.left = this.BinaryToComplex(unit.left);
1347
+ unit.right = this.BinaryToComplex(unit.right);
1348
+ }
1349
+ }
1350
+ else if (unit.type === 'unary' &&
1351
+ (unit.operator === '-' || unit.operator === '+') &&
1352
+ unit.operand.type === 'complex' &&
1353
+ unit.operand.text === this.imaginary_number) {
1354
+ // sigh... patch fix for very special case of "-i"
1355
+ // actually: why do I care about this? we could let whomever is using
1356
+ // the result deal with this particular case... although it's more
1357
+ // properly our responsibility if we are parsing complex numbers.
1358
+ // we only have to worry about mischaracterizing the range label,
1359
+ // e.g. "-i:j", but we should have already handled that in a prior pass.
1360
+ return {
1361
+ ...unit.operand,
1362
+ position: unit.position,
1363
+ text: this.expression.substring(unit.position, unit.operand.position + (unit.operand.text || '').length),
1364
+ imaginary: unit.operand.imaginary * (unit.operator === '-' ? -1 : 1),
1365
+ };
1366
+ }
1367
+ return unit;
1368
+ }
1369
+ /**
1370
+ * reorders operations for precendence
1371
+ *
1372
+ * this method was written with the assumption that groups were
1373
+ * always an error. that's no longer true, with implicit calls.
1374
+ * we should still error if it's not an _explicit_ group, i.e. there's
1375
+ * just a bunch of naked tokens.
1376
+ *
1377
+ */
1378
+ ArrangeUnits(stream) {
1379
+ // probably should not happen
1380
+ if (stream.length === 0)
1381
+ return { type: 'missing', id: this.id_counter++ };
1382
+ // this is probably already covered
1383
+ if (stream.length === 1)
1384
+ return stream[0];
1385
+ const stack = [];
1386
+ // work left-to-right (implied precendence), unless there
1387
+ // is actual precendence. spreadsheet language only supports
1388
+ // binary operators, so we always expect unit - operator - unit
1389
+ //
1390
+ // UPDATE: that's incorrect. SL supports unary + and - operators.
1391
+ // which makes this more complicated.
1392
+ //
1393
+ // we explicitly support unfinished expressions for the first pass
1394
+ // to build dependencies, but if they're invalid the resulting
1395
+ // parse tree isn't expected to be correct. in that case we
1396
+ // generally will pass back a bag of parts, with a flag set.
1397
+ for (let index = 0; index < stream.length; index++) {
1398
+ let element = stream[index];
1399
+ if (element.type === 'group-separator') {
1400
+ continue; // drop
1401
+ }
1402
+ // given that we need to support unary operators, the logic needs
1403
+ // to be a little different. operators are OK at any position, provided
1404
+ // we can construct either a unary or binary operation.
1405
+ if (element.type === 'operator') {
1406
+ if (stack.length === 0 || stack[stack.length - 1].type === 'operator') {
1407
+ // valid if unary operator and we can construct a unary operation.
1408
+ // in this case we do it with recursion.
1409
+ if (unary_operators[element.operator]) {
1410
+ const right = this.BinaryToComplex(this.ArrangeUnits(stream.slice(index + 1)));
1411
+ // this ensures we return the highest-level group, even if we recurse
1412
+ if (!this.valid) {
1413
+ return {
1414
+ type: 'group',
1415
+ id: this.id_counter++,
1416
+ elements: stream,
1417
+ explicit: false,
1418
+ };
1419
+ }
1420
+ // if it succeeded, then we need to apply the unary operator to
1421
+ // the result, or if it's a binary operation, to the left-hand side
1422
+ // (because we have precedence) -- unless it's a range [this is now
1423
+ // handled above]
1424
+ if (right.type === 'binary') {
1425
+ right.left = {
1426
+ type: 'unary',
1427
+ id: this.id_counter++,
1428
+ operator: element.operator,
1429
+ operand: right.left,
1430
+ position: element.position,
1431
+ };
1432
+ element = right;
1433
+ }
1434
+ else {
1435
+ // create a unary operation which will replace the element
1436
+ element = {
1437
+ type: 'unary',
1438
+ id: this.id_counter++,
1439
+ operator: element.operator,
1440
+ operand: right,
1441
+ position: element.position,
1442
+ };
1443
+ }
1444
+ // end loop after this pass, because the recurse consumes everything else
1445
+ index = stream.length;
1446
+ }
1447
+ else {
1448
+ this.error = `unexpected character [2]: ${element.operator}`;
1449
+ this.error_position = element.position;
1450
+ this.valid = false;
1451
+ return {
1452
+ type: 'group',
1453
+ id: this.id_counter++,
1454
+ elements: stream,
1455
+ explicit: false,
1456
+ };
1457
+ }
1458
+ }
1459
+ else {
1460
+ stack.push(element);
1461
+ continue;
1462
+ }
1463
+ }
1464
+ //
1465
+ // why is this 2? are we thinking about combining complex numbers?
1466
+ // or ranges? (those would be binary). or was this for dimensioned
1467
+ // quantities? [actually that makes sense] [A: no, it wasn't that]
1468
+ //
1469
+ // actually what's the case where this is triggered and it's _not_
1470
+ // an error? can we find that?
1471
+ //
1472
+ if (stack.length < 2) {
1473
+ // we know that `element` is not an operator, because we
1474
+ // would have consumed it
1475
+ if (stack.length === 1) {
1476
+ const a = stack[0].type;
1477
+ // support for lambdas
1478
+ if (element.type === 'group' && element.explicit) {
1479
+ if (a === 'address' || a === 'call' || a === 'identifier' || a === 'implicit-call') {
1480
+ // our parser seems to create implicit groups from these
1481
+ // values in parens. we should fix that, but we can unpack it.
1482
+ let args = element.elements;
1483
+ if (args.length === 1 && args[0].type === 'group' && !args[0].explicit) {
1484
+ args = args[0].elements;
1485
+ }
1486
+ // create an implicit call. replace on the stack.
1487
+ stack[0] = {
1488
+ type: 'implicit-call',
1489
+ call: stack[0],
1490
+ args,
1491
+ id: this.id_counter++,
1492
+ position: stack[0].position,
1493
+ };
1494
+ continue;
1495
+ }
1496
+ }
1497
+ /*
1498
+ else if (a !== 'operator') {
1499
+
1500
+ // console.warn("unexpected element", stack[0], element);
1501
+
1502
+ this.error = `unexpected element [3]: ${element.type}`;
1503
+ this.error_position = (element.type === 'missing' || element.type === 'group' || element.type === 'dimensioned') ? -1 : element.position;
1504
+ this.valid = false;
1505
+ return {
1506
+ type: 'group',
1507
+ id: this.id_counter++,
1508
+ elements: stream,
1509
+ explicit: false,
1510
+ };
1511
+
1512
+ }
1513
+ */
1514
+ }
1515
+ stack.push(element);
1516
+ }
1517
+ else if (stack[stack.length - 1].type === 'operator') {
1518
+ const left = stack[stack.length - 2];
1519
+ const operator_unit = stack[stack.length - 1];
1520
+ const operator = operator_unit.operator;
1521
+ // assume we can construct it as follows: [A op B]
1522
+ const operation = {
1523
+ type: 'binary',
1524
+ id: this.id_counter++,
1525
+ left,
1526
+ operator,
1527
+ position: operator_unit.position,
1528
+ right: element,
1529
+ };
1530
+ // we have to reorder if left (A) is a binary operation, and the
1531
+ // precedence of the new operator is higher. note that we will
1532
+ // deal with range operations later, for now just worry about
1533
+ // operator precedence
1534
+ if (left.type === 'binary' &&
1535
+ binary_operators_precendence[operator] >
1536
+ binary_operators_precendence[left.operator]) {
1537
+ // so we have [[A op1 B] op2 C], and we need to re-order this into [A op1 [B op2 C]].
1538
+ operation.left = left.left; // <- A
1539
+ operation.operator = left.operator; // <- op1
1540
+ operation.position = left.position;
1541
+ operation.right = {
1542
+ type: 'binary',
1543
+ id: this.id_counter++,
1544
+ left: left.right, // <- B
1545
+ right: element, // <- C
1546
+ operator, // <- op2
1547
+ position: operator_unit.position,
1548
+ };
1549
+ }
1550
+ stack.splice(-2, 2, operation);
1551
+ }
1552
+ else {
1553
+ /*
1554
+ this.error = `multiple expressions`;
1555
+ this.error_position = (element as {position?: number}).position;
1556
+ this.valid = false;
1557
+ return {
1558
+ type: 'group',
1559
+ id: this.id_counter++,
1560
+ elements: stream,
1561
+ explicit: false,
1562
+ };
1563
+ */
1564
+ stack.push(element);
1565
+ }
1566
+ }
1567
+ if (stack.length > 1) {
1568
+ return {
1569
+ type: 'group',
1570
+ id: this.id_counter++,
1571
+ elements: stack,
1572
+ explicit: false,
1573
+ };
1574
+ }
1575
+ return stack[0];
1576
+ }
1577
+ /**
1578
+ * parses literals and tokens from the stream, ignoring whitespace,
1579
+ * and stopping on unexpected tokens (generally operators or parens).
1580
+ *
1581
+ * @param naked treat -/+ as signs (part of numbers) rather than operators.
1582
+ */
1583
+ ParseNext(naked = true) {
1584
+ this.ConsumeWhiteSpace();
1585
+ const char = this.data[this.index];
1586
+ if (char === DOUBLE_QUOTE) {
1587
+ return {
1588
+ type: 'literal',
1589
+ id: this.id_counter++,
1590
+ position: this.index,
1591
+ value: this.ConsumeString(),
1592
+ };
1593
+ }
1594
+ else if ((char >= ZERO && char <= NINE) || char === this.decimal_mark_char) {
1595
+ return this.ConsumeNumber();
1596
+ }
1597
+ else if (char === OPEN_BRACE) {
1598
+ return this.ConsumeArray();
1599
+ }
1600
+ else if (naked && (char === MINUS || char === PLUS)) {
1601
+ // there's a case where you type '=-func()', which should support
1602
+ // '=+func()' as well, both of which are naked operators and not numbers.
1603
+ // the only way to figure this out is to check for a second number char.
1604
+ // this is turning into lookahead, which we did not want to do...
1605
+ const check = this.data[this.index + 1];
1606
+ if ((check >= ZERO && check <= NINE) ||
1607
+ check === this.decimal_mark_char) {
1608
+ return this.ConsumeNumber();
1609
+ }
1610
+ }
1611
+ else if ((char >= UC_A && char <= UC_Z) ||
1612
+ (char >= LC_A && char <= LC_Z) ||
1613
+ char === UNDERSCORE ||
1614
+ char === HASH || // new: only allowed in position 1, always an error
1615
+ char === SINGLE_QUOTE ||
1616
+ char === DOLLAR_SIGN ||
1617
+ // we used to not allow square brackets to start tokens, because
1618
+ // we only supported them for relative R1C1 references -- hence you'd
1619
+ // need the R first. but we now allow them for "structured references".
1620
+ char === OPEN_SQUARE_BRACKET ||
1621
+ (char >= ACCENTED_RANGE_START && char <= ACCENTED_RANGE_END) // adding accented characters, needs some testing
1622
+ ) {
1623
+ return this.ConsumeToken(char);
1624
+ }
1625
+ // else throw(new Error('Unexpected character: ' + char));
1626
+ return char;
1627
+ }
1628
+ ConsumeArray() {
1629
+ const expression = {
1630
+ type: 'array',
1631
+ id: this.id_counter++,
1632
+ values: [],
1633
+ position: this.index,
1634
+ };
1635
+ this.index++;
1636
+ let row = 0;
1637
+ let column = 0;
1638
+ while (this.index < this.length) {
1639
+ const item = this.ParseNext();
1640
+ const start_position = this.index;
1641
+ if (typeof item === 'number') {
1642
+ this.index++;
1643
+ switch (item) {
1644
+ case SEMICOLON:
1645
+ //column = 0;
1646
+ //row++;
1647
+ column++;
1648
+ row = 0;
1649
+ break;
1650
+ case COMMA:
1651
+ //column++;
1652
+ row++;
1653
+ break;
1654
+ case CLOSE_BRACE:
1655
+ return expression;
1656
+ default:
1657
+ if (this.valid) {
1658
+ this.error = `invalid character in array literal`;
1659
+ this.error_position = start_position;
1660
+ this.valid = false;
1661
+ }
1662
+ break;
1663
+ }
1664
+ }
1665
+ else {
1666
+ switch (item.type) {
1667
+ case 'literal':
1668
+ if (!expression.values[row]) {
1669
+ expression.values[row] = [];
1670
+ }
1671
+ expression.values[row][column] = item.value;
1672
+ break;
1673
+ default:
1674
+ if (this.valid) {
1675
+ this.error = `invalid value in array literal`;
1676
+ this.error_position = start_position;
1677
+ this.valid = false;
1678
+ }
1679
+ break;
1680
+ }
1681
+ }
1682
+ }
1683
+ return expression;
1684
+ }
1685
+ ConsumeOperator() {
1686
+ for (const operator of composite_operators) {
1687
+ if (this.expression.substr(this.index, operator.length) === operator) {
1688
+ const position = this.index;
1689
+ this.index += operator.length;
1690
+ return {
1691
+ type: 'operator',
1692
+ id: this.id_counter++,
1693
+ operator,
1694
+ position,
1695
+ };
1696
+ }
1697
+ }
1698
+ return null;
1699
+ }
1700
+ /** consume function arguments, which can be of any type */
1701
+ ConsumeArguments() {
1702
+ this.index++; // open paren
1703
+ let argument_index = 0;
1704
+ const args = [];
1705
+ for (; this.index < this.length;) {
1706
+ const unit = this.ParseGeneric([
1707
+ this.argument_separator_char,
1708
+ CLOSE_PAREN,
1709
+ ]);
1710
+ if (null !== unit)
1711
+ args.push(unit);
1712
+ // why did parsing stop?
1713
+ const char = this.data[this.index];
1714
+ if (char === this.argument_separator_char) {
1715
+ this.index++;
1716
+ argument_index++;
1717
+ for (let i = args.length; i < argument_index; i++) {
1718
+ args.push({ type: 'missing', id: this.id_counter++ });
1719
+ }
1720
+ }
1721
+ else if (char === CLOSE_PAREN) {
1722
+ this.index++;
1723
+ return args;
1724
+ }
1725
+ // else console.info('UNEXPECTED (CA)', char);
1726
+ }
1727
+ return args;
1728
+ }
1729
+ /**
1730
+ * consume token. also checks for function call, because parens
1731
+ * have a different meaning (grouping/precedence) when they appear
1732
+ * not immediately after a token.
1733
+ *
1734
+ * regarding periods: as long as there's no intervening whitespace
1735
+ * or operator, period should be a valid token character. tokens
1736
+ * cannot start with a period.
1737
+ *
1738
+ * NOTE: that's true irrespective of decimal mark type.
1739
+ *
1740
+ * you can have tokens (addresses) with single quotes; these are used
1741
+ * to escape sheet names with spaces (which is a bad idea, but hey). this
1742
+ * should only be legal if the token starts with a single quote, and only
1743
+ * for one (closing) quote.
1744
+ *
1745
+ * R1C1 relative notation uses square brackets, like =R2C[-1] or =R[-1]C[-2].
1746
+ * that's pretty easy to see. there's also regular R1C1, like =R1C1.
1747
+ *
1748
+ * "structured references" use square brackets. they can start with
1749
+ * square brackets -- in that case the table source is implicit (has to
1750
+ * be in the table). otherwise they look like =TableName[@ColumnName]. that
1751
+ * @ is optional and (I think) means don't spill.
1752
+ *
1753
+ */
1754
+ ConsumeToken(initial_char) {
1755
+ const token = [initial_char];
1756
+ const position = this.index;
1757
+ let single_quote = (initial_char === SINGLE_QUOTE);
1758
+ let square_bracket = 0; // now balancing // false; // this one can't be initial
1759
+ // this is a set-once flag for square brackets; it can
1760
+ // short-circuit the check for structured references.
1761
+ let braces = false;
1762
+ // also watch first char
1763
+ if (initial_char === OPEN_SQUARE_BRACKET) {
1764
+ square_bracket = 1;
1765
+ braces = true;
1766
+ }
1767
+ for (++this.index; this.index < this.length; this.index++) {
1768
+ const char = this.data[this.index];
1769
+ if ((char >= UC_A && char <= UC_Z) ||
1770
+ (char >= LC_A && char <= LC_Z) ||
1771
+ (char >= ACCENTED_RANGE_START && char <= ACCENTED_RANGE_END) ||
1772
+ char === UNDERSCORE ||
1773
+ char === DOLLAR_SIGN ||
1774
+ char === PERIOD ||
1775
+ char === EXCLAMATION_MARK ||
1776
+ single_quote || // ((char === SINGLE_QUOTE || char === SPACE) && single_quote) ||
1777
+ (char >= ZERO && char <= NINE) // tokens can't start with a number, but this loop starts at index 1
1778
+ // we now allow square brackets for structured references;
1779
+ // minus is still only allowed in R1C1 references, so keep
1780
+ // that restriction
1781
+ || char === OPEN_SQUARE_BRACKET
1782
+ || (square_bracket > 0 && char === CLOSE_SQUARE_BRACKET)
1783
+ || (char === MINUS && this.flags.r1c1 && (square_bracket === 1))
1784
+ // the @ sign can appear after the first square bracket...
1785
+ // but only immediately?
1786
+ || (square_bracket > 0 && char === AT && this.data[this.index - 1] === OPEN_SQUARE_BRACKET)
1787
+ // comma can appear in the first level. this is maybe an older
1788
+ // syntax? it looks like `Table2[[#this row],[region]]
1789
+ || (square_bracket === 1 && (char === COMMA || char === SPACE))
1790
+ // structured references allow basically any character, if
1791
+ // it's in the SECOND bracket. not sure what's up with that.
1792
+ || (square_bracket > 1)
1793
+ // I think that's all the rules for structured references.
1794
+ // testing question marks, which are legal in defined names
1795
+ // (but I think not in table names or column names)
1796
+ || (char === QUESTION_MARK && square_bracket === 0)
1797
+ // moving
1798
+ // || (char === HASH) // FIXME: this should only be allowed at the end...
1799
+ /*
1800
+
1801
+ || (this.flags.r1c1 && (
1802
+ char === OPEN_SQUARE_BRACKET ||
1803
+ char === CLOSE_SQUARE_BRACKET ||
1804
+ (char === MINUS && square_bracket)
1805
+ ))
1806
+ */
1807
+ ) {
1808
+ token.push(char);
1809
+ if (char === OPEN_SQUARE_BRACKET) {
1810
+ // square_bracket = true;
1811
+ square_bracket++;
1812
+ braces = true;
1813
+ }
1814
+ if (char === CLOSE_SQUARE_BRACKET) {
1815
+ // square_bracket = false;
1816
+ square_bracket--;
1817
+ }
1818
+ if (char === SINGLE_QUOTE) {
1819
+ single_quote = false; // one only
1820
+ }
1821
+ }
1822
+ else
1823
+ break;
1824
+ }
1825
+ // hash at end only
1826
+ if (this.data[this.index] === HASH) {
1827
+ token.push(this.data[this.index++]);
1828
+ }
1829
+ const str = token.map((num) => String.fromCharCode(num)).join('');
1830
+ // special handling: unbalanced single quote (probably sheet name),
1831
+ // this is an error
1832
+ if (single_quote) { // unbalanced
1833
+ this.error = `unbalanced single quote`;
1834
+ this.error_position = position;
1835
+ this.valid = false;
1836
+ return {
1837
+ type: 'identifier',
1838
+ id: this.id_counter++,
1839
+ name: str,
1840
+ position,
1841
+ };
1842
+ }
1843
+ // check unbalanced square bracket as well, could be a runaway structured
1844
+ // reference
1845
+ if (square_bracket) {
1846
+ this.error = `unbalanced square bracket`;
1847
+ this.error_position = position;
1848
+ this.valid = false;
1849
+ return {
1850
+ type: 'identifier',
1851
+ id: this.id_counter++,
1852
+ name: str,
1853
+ position,
1854
+ };
1855
+ }
1856
+ /* remove special handling
1857
+
1858
+ // special handling
1859
+
1860
+ if (str.toLowerCase() === 'true') {
1861
+ return {
1862
+ type: 'literal',
1863
+ id: this.id_counter++,
1864
+ value: true,
1865
+ position,
1866
+ };
1867
+ }
1868
+ if (str.toLowerCase() === 'false') {
1869
+ return {
1870
+ type: 'literal',
1871
+ id: this.id_counter++,
1872
+ value: false,
1873
+ position,
1874
+ };
1875
+ }
1876
+
1877
+ */
1878
+ // function takes precendence over address? I guess so
1879
+ this.ConsumeWhiteSpace();
1880
+ // UPDATE: UNLESS the token is an address, because that's not
1881
+ // a legal function name. so change that precedence rule, address
1882
+ // comes first.
1883
+ // erm -- that's not 100% correct. LOG10 is a valid cell address
1884
+ // and a valid function name. there might be others as well.
1885
+ if (this.flags.spreadsheet_semantics) {
1886
+ const address = this.ConsumeAddress(str, position);
1887
+ if (address)
1888
+ return address;
1889
+ }
1890
+ // [FIXME: what about braces? (...)]
1891
+ const next_char = this.data[this.index];
1892
+ if (next_char === OPEN_PAREN) {
1893
+ const args = this.ConsumeArguments();
1894
+ return {
1895
+ type: 'call',
1896
+ id: this.id_counter++,
1897
+ name: str,
1898
+ args,
1899
+ position,
1900
+ end: this.index, // testing
1901
+ };
1902
+ }
1903
+ if (this.flags.spreadsheet_semantics) {
1904
+ // check for address. in the case of a range, we'll see an address, the
1905
+ // range operator, and a second address. that will be turned into a range
1906
+ // later.
1907
+ // moved up
1908
+ // const address = this.ConsumeAddress(str, position);
1909
+ // if (address) return address;
1910
+ // check for structured reference, if we had square brackets
1911
+ if (braces) {
1912
+ const structured = this.ConsumeStructuredReference(str, position);
1913
+ if (structured) {
1914
+ return structured;
1915
+ }
1916
+ }
1917
+ }
1918
+ // move true/false handling here
1919
+ // should we accept english even if it's not the active language? (...)
1920
+ const lc = str.toLowerCase();
1921
+ if (lc === 'true' || (this.flags.boolean_true && lc === this.flags.boolean_true.toLowerCase())) {
1922
+ return {
1923
+ type: 'literal',
1924
+ id: this.id_counter++,
1925
+ value: true,
1926
+ position,
1927
+ };
1928
+ }
1929
+ if (lc === 'false' || (this.flags.boolean_false && lc === this.flags.boolean_false.toLowerCase())) {
1930
+ return {
1931
+ type: 'literal',
1932
+ id: this.id_counter++,
1933
+ value: false,
1934
+ position,
1935
+ };
1936
+ }
1937
+ const identifier = {
1938
+ type: 'identifier',
1939
+ id: this.id_counter++,
1940
+ name: str,
1941
+ position,
1942
+ };
1943
+ this.full_reference_list.push(identifier);
1944
+ return identifier;
1945
+ }
1946
+ /**
1947
+ * like ConsumeAddress, look for a structured reference.
1948
+ */
1949
+ ConsumeStructuredReference(token, position) {
1950
+ // structured references look something like
1951
+ //
1952
+ // [@Column1]
1953
+ // [@[Column with spaces]]
1954
+ // [[#This Row],[Column2]]
1955
+ //
1956
+ // @ means the same as [#This Row]. there are probably other things
1957
+ // that use the # syntax, but I haven't seen them yet.
1958
+ //
1959
+ // some observations: case is not matched for the "this row" text.
1960
+ // I think that's true of column names as well, but that's not relevant
1961
+ // at this stage. whitespace around that comma is ignored. I _think_
1962
+ // whitespace around column names is also ignored, but spaces within
1963
+ // a column name are OK, at least within the second set of brackets.
1964
+ // const index = position;
1965
+ const token_length = token.length;
1966
+ const label = token;
1967
+ let table = '';
1968
+ let i = 0;
1969
+ for (; i < token_length; i++) {
1970
+ if (token[i] === '[') {
1971
+ token = token.substring(i);
1972
+ break;
1973
+ }
1974
+ table += token[i];
1975
+ }
1976
+ // after the table, must start and end with brackets
1977
+ if (token[0] !== '[' || token[token.length - 1] !== ']') {
1978
+ return undefined;
1979
+ }
1980
+ token = token.substring(1, token.length - 1);
1981
+ const parts = token.split(',').map(part => part.trim());
1982
+ let scope = 'column';
1983
+ // let this_row = false;
1984
+ let column = '';
1985
+ if (parts.length > 2) {
1986
+ return undefined; // ??
1987
+ }
1988
+ else if (parts.length === 2) {
1989
+ if (/\[#this row\]/i.test(parts[0])) {
1990
+ scope = 'row';
1991
+ }
1992
+ else if (/\[#all\]/i.test(parts[0])) {
1993
+ scope = 'all';
1994
+ }
1995
+ column = parts[1];
1996
+ }
1997
+ else {
1998
+ column = parts[0];
1999
+ if (column[0] === '@') {
2000
+ scope = 'row';
2001
+ column = column.substring(1, column.length);
2002
+ }
2003
+ }
2004
+ if (column[0] === '[' && column[column.length - 1] === ']') {
2005
+ column = column.substring(1, column.length - 1);
2006
+ }
2007
+ const reference = {
2008
+ type: 'structured-reference',
2009
+ id: this.id_counter++,
2010
+ label,
2011
+ position,
2012
+ scope,
2013
+ column,
2014
+ table,
2015
+ };
2016
+ // console.info(reference);
2017
+ this.full_reference_list.push(reference);
2018
+ return reference;
2019
+ }
2020
+ /**
2021
+ * consumes address. this is outside of the normal parse flow;
2022
+ * we already have a token, here we're checking if it's an address.
2023
+ *
2024
+ * this used to check for ranges as well, but we now treat ranges as
2025
+ * an operation on two addresses; that supports whitespace between the
2026
+ * tokens.
2027
+ *
2028
+ * FIXME: that means we can now inline the column/row routines, since
2029
+ * they are not called more than once
2030
+ */
2031
+ ConsumeAddress(token, position) {
2032
+ const index = position;
2033
+ const token_length = token.length;
2034
+ // FIXME: should mark this (!) when it hits, rather than search
2035
+ // UPDATE: ! is legal in sheet names, although it needs to be quoted.
2036
+ let sheet;
2037
+ const tokens = token.split('!');
2038
+ if (tokens.length > 1) {
2039
+ sheet = tokens.slice(0, tokens.length - 1).join('!');
2040
+ position += sheet.length + 1;
2041
+ }
2042
+ // handle first
2043
+ if (this.flags.r1c1) {
2044
+ const match = tokens[tokens.length - 1].match(this.r1c1_regex);
2045
+ if (match) {
2046
+ const r1c1 = {
2047
+ type: 'address',
2048
+ id: this.id_counter++,
2049
+ label: token, // TODO
2050
+ row: 0,
2051
+ column: 0,
2052
+ // absolute_row: false, // TODO: is this supported?
2053
+ // absolute_column: false, // TODO: is this supported?
2054
+ position: index,
2055
+ sheet,
2056
+ r1c1: true,
2057
+ };
2058
+ if (match[1][0] === '[') { // relative
2059
+ r1c1.offset_row = true;
2060
+ r1c1.row = Number(match[1].substring(1, match[1].length - 1));
2061
+ }
2062
+ else if (match[1]) { // absolute
2063
+ r1c1.row = Number(match[1]) - 1; // R1C1 is 1-based
2064
+ if (this.flags.r1c1_proper_semantics) {
2065
+ r1c1.absolute_row = true;
2066
+ }
2067
+ }
2068
+ else {
2069
+ r1c1.offset_row = true;
2070
+ r1c1.row = 0;
2071
+ }
2072
+ if (match[2][0] === '[') { // relative
2073
+ r1c1.offset_column = true;
2074
+ r1c1.column = Number(match[2].substring(1, match[2].length - 1));
2075
+ }
2076
+ else if (match[2]) { // absolute
2077
+ r1c1.column = Number(match[2]) - 1; // R1C1 is 1-based
2078
+ if (this.flags.r1c1_proper_semantics) {
2079
+ r1c1.absolute_column = true;
2080
+ }
2081
+ }
2082
+ else {
2083
+ r1c1.offset_column = true;
2084
+ r1c1.column = 0;
2085
+ }
2086
+ return r1c1;
2087
+ }
2088
+ }
2089
+ // FIXME: can inline
2090
+ const c = this.ConsumeAddressColumn(position);
2091
+ if (!c)
2092
+ return null;
2093
+ position = c.position;
2094
+ // things that look like an address but have row 0 are legal
2095
+ // as names. so this should be a token if r === 0.
2096
+ const r = this.ConsumeAddressRow(position);
2097
+ if (!r)
2098
+ return null;
2099
+ position = r.position;
2100
+ // special hack for LOG10. ugh. can't find any other functions with
2101
+ // this problem, in english at least. btw what's the translation for
2102
+ // log10?
2103
+ if (c.column === 8508 && r.row === 9) {
2104
+ return null;
2105
+ }
2106
+ const label = sheet ?
2107
+ sheet + token.substr(sheet.length, position - index).toUpperCase() :
2108
+ token.substr(0, position - index).toUpperCase();
2109
+ if (sheet && sheet[0] === '\'') {
2110
+ sheet = sheet.substr(1, sheet.length - 2);
2111
+ }
2112
+ const addr = {
2113
+ type: 'address',
2114
+ id: this.id_counter++,
2115
+ label, // : token.substr(0, position - index).toUpperCase(),
2116
+ row: r.row,
2117
+ column: c.column,
2118
+ absolute_row: r.absolute,
2119
+ absolute_column: c.absolute,
2120
+ position: index,
2121
+ sheet,
2122
+ spill: r.spill,
2123
+ };
2124
+ // if that's not the complete token, then it's invalid
2125
+ if (token_length !== position - index)
2126
+ return null;
2127
+ // store ref, increment count
2128
+ this.dependencies.addresses[addr.label] = addr;
2129
+ this.address_refcount[addr.label] =
2130
+ (this.address_refcount[addr.label] || 0) + 1;
2131
+ // add to new address list. use the actual object (not a clone or copy);
2132
+ // we update the list later, and we may want to remove it (if it turns
2133
+ // out it's part of a range)
2134
+ this.full_reference_list.push(addr);
2135
+ return addr;
2136
+ }
2137
+ /**
2138
+ * consumes a row, possibly absolute ($). returns the numeric row
2139
+ * (0-based) and metadata.
2140
+ *
2141
+ * note that something like "X0" is a legal token, because 0 is not
2142
+ * a valid row. but at the same time it can't have a $ in it. although
2143
+ * maybe "X$0" is a token but not a valid name? dunno
2144
+ */
2145
+ ConsumeAddressRow(position) {
2146
+ const absolute = this.data[position] === DOLLAR_SIGN;
2147
+ if (absolute)
2148
+ position++;
2149
+ const start = position;
2150
+ let value = 0;
2151
+ for (;; position++) {
2152
+ const char = this.data[position];
2153
+ if (char >= ZERO && char <= NINE) {
2154
+ value *= 10;
2155
+ value += char - ZERO;
2156
+ }
2157
+ else
2158
+ break;
2159
+ }
2160
+ if (start === position) {
2161
+ return false;
2162
+ }
2163
+ // handle token X0. should ~maybe~ handle this only if !absolute
2164
+ // temp leaving this separate from the above test just so it's clear
2165
+ // what we are doing
2166
+ if (value === 0) {
2167
+ return false;
2168
+ }
2169
+ let spill = false;
2170
+ if (this.data[position] === HASH) {
2171
+ position++;
2172
+ spill = true;
2173
+ }
2174
+ return { absolute, row: value - 1, position, spill };
2175
+ }
2176
+ /**
2177
+ * consumes a column, possibly absolute ($). returns the numeric
2178
+ * column (0-based) and metadata
2179
+ */
2180
+ ConsumeAddressColumn(position) {
2181
+ let column = -1; // clever
2182
+ let length = 0; // max 3 chars for column
2183
+ const absolute = this.data[position] === DOLLAR_SIGN;
2184
+ if (absolute)
2185
+ position++;
2186
+ for (;; position++, length++) {
2187
+ if (length >= 4)
2188
+ return false; // max 3 chars for column
2189
+ const char = this.data[position];
2190
+ if (char >= UC_A && char <= UC_Z) {
2191
+ column = 26 * (1 + column) + (char - UC_A);
2192
+ }
2193
+ else if (char >= LC_A && char <= LC_Z) {
2194
+ column = 26 * (1 + column) + (char - LC_A);
2195
+ }
2196
+ else
2197
+ break;
2198
+ }
2199
+ if (column < 0)
2200
+ return false;
2201
+ return { absolute, column, position };
2202
+ }
2203
+ /**
2204
+ * consumes number. supported formats (WIP):
2205
+ *
2206
+ * -3
2207
+ * +3
2208
+ * 100.9
2209
+ * 10.0%
2210
+ * 1e-2.2
2211
+ *
2212
+ * ~1,333,123.22~
2213
+ *
2214
+ * UPDATE: commas (separators) are not acceptable in numbers passed
2215
+ * in formulae, can't distinguish between them and function argument
2216
+ * separators.
2217
+ *
2218
+ * regarding the above, a couple of rules:
2219
+ *
2220
+ * 1. +/- is only legal in position 0 or immediately after e/E
2221
+ * 2. only one decimal point is allowed.
2222
+ * 3. any number of separators, in any position, are legal, but
2223
+ * only before the decimal point.
2224
+ * 4. only one % is allowed, and only in the last position
2225
+ *
2226
+ * NOTE: this is probably going to break on unfinished strings that
2227
+ * end in - or +... if they're not treated as operators...
2228
+ *
2229
+ * FIXME: find test cases for that so we can fix it
2230
+ *
2231
+ * UPDATE: exporting original text string for preservation/insertion.
2232
+ * this function now returns a tuple of [value, text].
2233
+ *
2234
+ * UPDATE: we now (at least in a branch) consume complex numbers. the last
2235
+ * element of the return array is a boolean which is set if the value is an
2236
+ * imaginary number. when parsing, we will only see the imaginary part;
2237
+ * we'll use a separate step to put complex numbers together.
2238
+ *
2239
+ *
2240
+ */
2241
+ ConsumeNumber() {
2242
+ const starting_position = this.index;
2243
+ // for exponential notation
2244
+ let exponent = 0;
2245
+ let negative_exponent = false;
2246
+ // general
2247
+ let negative = false;
2248
+ let integer = 0;
2249
+ let decimal = 0;
2250
+ let fraction = 0;
2251
+ let state = 'integer';
2252
+ let position = 0;
2253
+ let imaginary = false;
2254
+ const start_index = this.index;
2255
+ for (; this.index < this.length; this.index++, position++) {
2256
+ const char = this.data[this.index];
2257
+ if (char === this.decimal_mark_char) {
2258
+ if (state === 'integer')
2259
+ state = 'fraction';
2260
+ else
2261
+ break; // end of token; not consuming
2262
+ }
2263
+ else if (char === PERCENT) {
2264
+ // FIXME: disallow combination of exponential and percent notation
2265
+ integer /= 100; // this is a dumb way to do this
2266
+ fraction /= 100;
2267
+ this.index++; // we are consuming
2268
+ break; // end of token
2269
+ }
2270
+ else if (char === PLUS || char === MINUS) {
2271
+ // NOTE: handling of positive/negative exponent in exponential
2272
+ // notation is handled separately, see below
2273
+ if (position === 0) {
2274
+ if (char === MINUS)
2275
+ negative = true;
2276
+ }
2277
+ else
2278
+ break; // end of token -- not consuming
2279
+ }
2280
+ else if (char === UC_E || char === LC_E) {
2281
+ if (state === 'integer' || state === 'fraction') {
2282
+ state = 'exponent';
2283
+ if (this.index < this.length - 1) {
2284
+ if (this.data[this.index + 1] === PLUS)
2285
+ this.index++;
2286
+ else if (this.data[this.index + 1] === MINUS) {
2287
+ this.index++;
2288
+ negative_exponent = true;
2289
+ }
2290
+ }
2291
+ }
2292
+ else
2293
+ break; // not sure what this is, then
2294
+ }
2295
+ else if (char === this.imaginary_char) {
2296
+ // FIXME: this should only be set if it's exactly '8i' and not '8in',
2297
+ // since we want to use that for dimensioned quantities. what's legit
2298
+ // after the i and what is not? let's exclude anything in the "word"
2299
+ // range...
2300
+ // peek
2301
+ const peek = this.data[this.index + 1];
2302
+ if ((peek >= UC_A && peek <= UC_Z) ||
2303
+ (peek >= LC_A && peek <= LC_Z) ||
2304
+ (peek >= ACCENTED_RANGE_START && peek <= ACCENTED_RANGE_END) ||
2305
+ peek === UNDERSCORE) {
2306
+ break; // start of an identifier
2307
+ }
2308
+ // actually we could use our dimension logic instead of this... turn
2309
+ // this off when using dimensioned quantities and move it in there?
2310
+ if (state === 'integer' || state === 'fraction') {
2311
+ this.index++; // consume
2312
+ imaginary = true;
2313
+ break; // end of token
2314
+ }
2315
+ }
2316
+ else if (char >= ZERO && char <= NINE) {
2317
+ switch (state) {
2318
+ case 'integer':
2319
+ integer = integer * 10 + (char - ZERO);
2320
+ break;
2321
+ case 'fraction':
2322
+ fraction = fraction * 10 + (char - ZERO);
2323
+ decimal++;
2324
+ break;
2325
+ case 'exponent':
2326
+ exponent = exponent * 10 + (char - ZERO);
2327
+ break;
2328
+ }
2329
+ }
2330
+ else
2331
+ break;
2332
+ }
2333
+ // NOTE: multiplying returns fp noise, but dividing does not? need
2334
+ // to check more browsers... maybe we should store the value in some
2335
+ // other form? (that's a larger TODO)
2336
+ // let value = integer + fraction * Math.pow(10, -decimal);
2337
+ let value = integer + fraction / (Math.pow(10, decimal)); // <- this is cleaner?
2338
+ if (state === 'exponent') {
2339
+ value = value * Math.pow(10, (negative_exponent ? -1 : 1) * exponent);
2340
+ }
2341
+ // const text = this.expression.substring(start_index, this.index) || '';
2342
+ // return [negative ? -value : value, text, imaginary];
2343
+ if (imaginary) {
2344
+ return {
2345
+ type: 'complex',
2346
+ id: this.id_counter++,
2347
+ position: starting_position,
2348
+ imaginary: negative ? -value : value,
2349
+ real: 0,
2350
+ text: this.expression.substring(start_index, this.index) || '',
2351
+ };
2352
+ }
2353
+ else {
2354
+ return {
2355
+ type: 'literal',
2356
+ id: this.id_counter++,
2357
+ position: starting_position,
2358
+ value: negative ? -value : value,
2359
+ text: this.expression.substring(start_index, this.index) || '',
2360
+ };
2361
+ }
2362
+ /*
2363
+ return {
2364
+ type: imaginary ? 'imaginary' : 'literal',
2365
+ id: this.id_counter++,
2366
+ position: starting_position,
2367
+ value: negative ? -value : value,
2368
+ text: this.expression.substring(start_index, this.index) || '',
2369
+ };
2370
+ */
2371
+ }
2372
+ /**
2373
+ * in spreadsheet language ONLY double-quoted strings are legal. there
2374
+ * are no escape characters, and a backslash is a legal character. to
2375
+ * embed a quotation mark, use "" (double-double quote); that's an escaped
2376
+ * double-quote.
2377
+ */
2378
+ ConsumeString() {
2379
+ this.index++; // open quote
2380
+ const str = [];
2381
+ for (; this.index < this.length; this.index++) {
2382
+ const char = this.data[this.index];
2383
+ if (char === DOUBLE_QUOTE) {
2384
+ // always do this: either it's part of the string (and
2385
+ // we want to skip the next one), or it's the end of the
2386
+ // string and we want to close the literal.
2387
+ this.index++;
2388
+ // check for an escaped double-quote; otherwise close the string
2389
+ // note (1) we already incremented, so check the current value,
2390
+ // and (2) it will increment again on the loop pass so it will
2391
+ // drop the extra one. I note these because this was confusing to
2392
+ // write.
2393
+ if (this.index >= this.length ||
2394
+ this.data[this.index] !== DOUBLE_QUOTE) {
2395
+ break;
2396
+ }
2397
+ }
2398
+ str.push(char);
2399
+ }
2400
+ return str.map((char) => String.fromCharCode(char)).join('');
2401
+ }
2402
+ /** run through any intervening whitespace */
2403
+ ConsumeWhiteSpace() {
2404
+ for (; this.index < this.length;) {
2405
+ const char = this.data[this.index];
2406
+ if (char === SPACE ||
2407
+ char === TAB ||
2408
+ char === CR ||
2409
+ char === LF ||
2410
+ char === NON_BREAKING_SPACE) {
2411
+ this.index++;
2412
+ }
2413
+ else
2414
+ return;
2415
+ }
2416
+ }
2417
+ }
2418
+ //# sourceMappingURL=parser.js.map