grammar/dependency/hooks.js

  1. const FormulaError = require('../../formulas/error');
  2. const {FormulaHelpers} = require('../../formulas/helpers');
  3. const {Parser} = require('../parsing');
  4. const lexer = require('../lexing');
  5. const Utils = require('./utils');
  6. const {formatChevrotainError} = require('../utils');
  7. class DepParser {
  8. /**
  9. *
  10. * @param {{onVariable: Function}} [config]
  11. */
  12. constructor(config) {
  13. this.data = [];
  14. this.utils = new Utils(this);
  15. config = Object.assign({
  16. onVariable: () => null,
  17. }, config);
  18. this.utils = new Utils(this);
  19. this.onVariable = config.onVariable;
  20. this.functions = {}
  21. this.parser = new Parser(this, this.utils);
  22. }
  23. /**
  24. * Get value from the cell reference
  25. * @param ref
  26. * @return {*}
  27. */
  28. getCell(ref) {
  29. // console.log('get cell', JSON.stringify(ref));
  30. if (ref.row != null) {
  31. if (ref.sheet == null)
  32. ref.sheet = this.position ? this.position.sheet : undefined;
  33. const idx = this.data.findIndex(element => {
  34. return (element.from && element.from.row <= ref.row && element.to.row >= ref.row
  35. && element.from.col <= ref.col && element.to.col >= ref.col)
  36. || (element.row === ref.row && element.col === ref.col && element.sheet === ref.sheet)
  37. });
  38. if (idx === -1)
  39. this.data.push(ref);
  40. }
  41. return 0;
  42. }
  43. /**
  44. * Get values from the range reference.
  45. * @param ref
  46. * @return {*}
  47. */
  48. getRange(ref) {
  49. // console.log('get range', JSON.stringify(ref));
  50. if (ref.from.row != null) {
  51. if (ref.sheet == null)
  52. ref.sheet = this.position ? this.position.sheet : undefined;
  53. const idx = this.data.findIndex(element => {
  54. return element.from && element.from.row === ref.from.row && element.from.col === ref.from.col
  55. && element.to.row === ref.to.row && element.to.col === ref.to.col;
  56. });
  57. if (idx === -1)
  58. this.data.push(ref);
  59. }
  60. return [[0]]
  61. }
  62. /**
  63. * TODO:
  64. * Get references or values from a user defined variable.
  65. * @param name
  66. * @return {*}
  67. */
  68. getVariable(name) {
  69. // console.log('get variable', name);
  70. const res = {ref: this.onVariable(name, this.position.sheet)};
  71. if (res.ref == null)
  72. return FormulaError.NAME;
  73. if (FormulaHelpers.isCellRef(res))
  74. this.getCell(res.ref);
  75. else {
  76. this.getRange(res.ref);
  77. }
  78. return 0;
  79. }
  80. /**
  81. * Retrieve values from the given reference.
  82. * @param valueOrRef
  83. * @return {*}
  84. */
  85. retrieveRef(valueOrRef) {
  86. if (FormulaHelpers.isRangeRef(valueOrRef)) {
  87. return this.getRange(valueOrRef.ref);
  88. }
  89. if (FormulaHelpers.isCellRef(valueOrRef)) {
  90. return this.getCell(valueOrRef.ref)
  91. }
  92. return valueOrRef;
  93. }
  94. /**
  95. * Call an excel function.
  96. * @param name - Function name.
  97. * @param args - Arguments that pass to the function.
  98. * @return {*}
  99. */
  100. callFunction(name, args) {
  101. args.forEach(arg => {
  102. if (arg == null)
  103. return;
  104. this.retrieveRef(arg);
  105. });
  106. return {value: 0, ref: {}};
  107. }
  108. /**
  109. * Check and return the appropriate formula result.
  110. * @param result
  111. * @return {*}
  112. */
  113. checkFormulaResult(result) {
  114. this.retrieveRef(result);
  115. }
  116. /**
  117. * Parse an excel formula and return the dependencies
  118. * @param {string} inputText
  119. * @param {{row: number, col: number, sheet: string}} position
  120. * @param {boolean} [ignoreError=false] if true, throw FormulaError when error occurred.
  121. * if false, the parser will return partial dependencies.
  122. * @returns {Array.<{}>}
  123. */
  124. parse(inputText, position, ignoreError = false) {
  125. if (inputText.length === 0) throw Error('Input must not be empty.');
  126. this.data = [];
  127. this.position = position;
  128. const lexResult = lexer.lex(inputText);
  129. this.parser.input = lexResult.tokens;
  130. try {
  131. const res = this.parser.formulaWithBinaryOp();
  132. this.checkFormulaResult(res);
  133. } catch (e) {
  134. if (!ignoreError) {
  135. throw FormulaError.ERROR(e.message, e);
  136. }
  137. }
  138. if (this.parser.errors.length > 0 && !ignoreError) {
  139. const error = this.parser.errors[0];
  140. throw formatChevrotainError(error, inputText);
  141. }
  142. return this.data;
  143. }
  144. }
  145. module.exports = {
  146. DepParser,
  147. };