index.js
/**
   *
   * @memberof excelFormulaUtilities.parser
   * @function
   * @param {string} formula
   * @param {object} options optional param
   *<pre>
   *   TEMPLATE VALUES
   *  {{autoindent}} - apply auto indent based on current tree level
   *  {{token}} - the named token such as FUNCTION_NAME or "string"
   *  {{autolinebreak}} - apply line break automatically. tests for next element only at this point
   *
   * Options include:
   *  tmplFunctionStart           - template for the start of a function, the {{token}} will contain the name of the function.
   *  tmplFunctionStop            - template for when the end of a function has been reached.
   *  tmplOperandError            - template for errors.
   *  tmplOperandRange            - template for ranges and variable names.
   *  tmplOperandLogical          - template for logical operators such as + - = ...
   *  tmplOperandNumber           - template for numbers.
   *  tmplOperandText             - template for text/strings.
   *  tmplArgument				- template for argument separators such as ,.
   *  tmplFunctionStartArray      - template for the start of an array.
   *  tmplFunctionStartArrayRow   - template for the start of an array row.
   *  tmplFunctionStopArrayRow    - template for the end of an array row.
   *  tmplFunctionStopArray       - template for the end of an array.
   *  tmplSubexpressionStart      - template for the sub expression start
   *  tmplSubexpressionStop       - template for the sub expression stop
   *  tmplIndentTab               - template for the tab char.
   *  tmplIndentSpace             - template for space char.
   *  autoLineBreak               - when rendering line breaks automatically which types should it break on. "this.TOK_SUBTYPE_STOP | this.TOK_SUBTYPE_START | this.TOK_TYPE_ARGUMENT"
   *  newLine                     - used for the {{autolinebreak}} replacement as well as some string parsing. if this is not set correctly you may get undesired results. usually \n for text or <br /> for html
   *  trim: true                  - trim the output.
   *	customTokenRender: null     - this is a call back to a custom token function. your call back should look like
   *                                EXAMPLE:
   *
   *                                    customTokenRender: function(tokenString, token, indent, lineBreak){
   *                                        let outStr = token,
   *                                            useTemplate = true;
   *                                        // In the return object "useTemplate" tells formatFormula()
   *                                        // weather or not to apply the template to what your return from the "tokenString".
   *                                        return {tokenString: outStr, useTemplate: useTemplate};
   *                                    }
   *
   *</pre>
   * @returns {string}
   */


Options include:
tmplFunctionStart           - template for the start of a function, the {{token}} will contain the name of the function.
tmplFunctionStop            - template for when the end of a function has been reached.
tmplOperandError            - template for errors.
tmplOperandRange            - template for ranges and variable names.
tmplOperandLogical          - template for logical operators such as + - =
tmplOperandNumber           - template for numbers.
tmplOperandText             - template for text/strings.
tmplArgument                              - template for argument separators such as ,.
tmplFunctionStartArray      - template for the start of an array.
tmplFunctionStartArrayRow   - template for the start of an array row.
tmplFunctionStopArrayRow    - template for the end of an array row.
tmplFunctionStopArray       - template for the end of an array.
tmplSubexpressionStart      - template for the sub expression start
tmplSubexpressionStop       - template for the sub expression stop
tmplIndentTab               - template for the tab char.
tmplIndentSpace             - template for space char.
autoLineBreak               - when rendering line breaks automatically which types should it break on. "this.TOK_SUBTYPE_STOP | this.TOK_SUBTYPE_START | this.TOK_TYPE_ARGUMENT"
newLine                     - used for the {{autolinebreak}} replacement as well as some string parsing. if this is not set correctly you may get undesired results. usually \n for text or  for html
trim: true                  - trim the output.
customTokenRender: null     - this is a call back to a custom token function. your call back should look like
EXAMPLE.

function(tokenString, token, indent, lineBreak){
let outStr = token,
useTemplate = true;
In the return object "useTemplate" tells formatFormula()
weather or not to apply the template to what your return from the "tokenString".
return {tokenString: outStr, useTemplate: useTemplate};
}


@returns {string}
Options include : tmplFunctionStart - template for the start of a function the {{ token }} will contain the name of the function . tmplFunctionStop - template for when the end of a function has been reached . tmplOperandError - template for errors . tmplOperandRange - template for ranges and variable names . tmplOperandLogical - template for logical operators such as + - = tmplOperandNumber - template for numbers . tmplOperandText - template for text / strings . tmplArgument - template for argument separators such as . tmplFunctionStartArray - template for the start of an array . tmplFunctionStartArrayRow - template for the start of an array row . tmplFunctionStopArrayRow - template for the end of an array row . tmplFunctionStopArray - template for the end of an array . tmplSubexpressionStart - template for the sub expression start tmplSubexpressionStop - template for the sub expression stop tmplIndentTab - template for the tab char . tmplIndentSpace - template for space char . autoLineBreak - when rendering line breaks automatically which types should it break on . " this . TOK_SUBTYPE_STOP | this . TOK_SUBTYPE_START | this . TOK_TYPE_ARGUMENT " newLine - used for the {{ autolinebreak }} replacement as well as some string parsing . if this is not set correctly you may get undesired results . usually \ n for text or for html trim : true - trim the output . customTokenRender : null - this is a call back to a custom token function . your call back should look like EXAMPLE . function ( tokenString token indent lineBreak ) { let outStr = token useTemplate = true ; In the return object " useTemplate " tells formatFormula () weather or not to apply the template to what your return from the " tokenString " . return { tokenString : outStr useTemplate : useTemplate } ; } @returns { string }
static formatFormula(formula, options) {
    formula = formula.replace(/^\s*=\s+/, "=");
    let isFirstToken = true,
      defaultOptions = {
        tmplFunctionStart: "{{autoindent}}{{token}}(\n",
        tmplFunctionStop: "\n{{autoindent}}{{token}})",
        tmplOperandError: " {{token}}",
        tmplOperandRange: "{{autoindent}}{{token}}",
        tmplLogical: "{{token}}{{autolinebreak}}",
        tmplOperandLogical: "{{autoindent}}{{token}}",
        tmplOperandNumber: "{{autoindent}}{{token}}",
        tmplOperandText: '{{autoindent}}"{{token}}"',
        tmplArgument: "{{token}}\n",
        tmplOperandOperatorInfix: " {{token}}{{autolinebreak}}",
        tmplFunctionStartArray: "",
        tmplFunctionStartArrayRow: "{",
        tmplFunctionStopArrayRow: "}",
        tmplFunctionStopArray: "",
        tmplSubexpressionStart: "{{autoindent}}(\n",
        tmplSubexpressionStop: "\n)",
        tmplIndentTab: "\t",
        tmplIndentSpace: " ",
        autoLineBreak:
          "TOK_TYPE_FUNCTION | TOK_TYPE_ARGUMENT | TOK_SUBTYPE_LOGICAL | TOK_TYPE_OP_IN ",
        newLine: "\n",
        customTokenRender: null,
        prefix: "",
        postfix: "",
      };
    if (options) {
      options = this.extend(true, defaultOptions, options);
    } else {
      options = defaultOptions;
    }
    let indentCount = 0;
    let indent_f = function () {
      let s = "",
        i = 0;
      for (; i < indentCount; i += 1) {
        s += options.tmplIndentTab;
      }
      return s;
    };
    let tokens = this.getTokens(formula);
    let outputFormula = "";
    let autoBreakArray = options.autoLineBreak.replace(/\s/gi, "").split("|");
    let isNewLine = true;
    let testAutoBreak = function (nextToken) {
      let i = 0;
      for (; i < autoBreakArray.length; i += 1) {
        if (
          nextToken !== null &&
          typeof nextToken !== "undefined" &&
          (ExcelFormula.types[autoBreakArray[i]] ===
            nextToken.type.toString() ||
            ExcelFormula.types[autoBreakArray[i]] ===
              nextToken.subtype.toString())
        ) {
          return true;
        }
      }
      return false;
    };
    let lastToken = null;
    while (tokens.moveNext()) {
      let token = tokens.current();
      let nextToken = tokens.next();
      if (token.subtype.toString() === this.TOK_SUBTYPE_STOP) {
        indentCount -= indentCount > 0 ? 1 : 0;
      }
      let matchBeginNewline = new RegExp("^" + options.newLine, ""),
        matchEndNewLine = new RegExp(options.newLine + "$", ""),
        autoBreak = testAutoBreak(nextToken),
        autoIndent = isNewLine,
        indent = autoIndent ? indent_f() : options.tmplIndentSpace,
        lineBreak = autoBreak ? options.newLine : "";
      outputFormula += ExcelFormula.applyTokenTemplate(
        token,
        options,
        indent,
        lineBreak,
        options.customTokenRender,
        lastToken
      );
      if (token.subtype.toString() === this.TOK_SUBTYPE_START) {
        indentCount += 1;
      }
      isNewLine = autoBreak || matchEndNewLine.test(outputFormula);
      isFirstToken = false;
      lastToken = token;
    }
    outputFormula =
      options.prefix + ExcelFormula.trim(outputFormula) + options.postfix;
    return outputFormula;
  }
static formatFormula ( formula , options ) { formula = formula . replace ( / ^\s*=\s+ / , "=" ) ; let isFirstToken = true , defaultOptions = { tmplFunctionStart : "{{autoindent}}{{token}}(\n" , tmplFunctionStop : "\n{{autoindent}}{{token}})" , tmplOperandError : " {{token}}" , tmplOperandRange : "{{autoindent}}{{token}}" , tmplLogical : "{{token}}{{autolinebreak}}" , tmplOperandLogical : "{{autoindent}}{{token}}" , tmplOperandNumber : "{{autoindent}}{{token}}" , tmplOperandText : '{{autoindent}}"{{token}}"' , tmplArgument : "{{token}}\n" , tmplOperandOperatorInfix : " {{token}}{{autolinebreak}}" , tmplFunctionStartArray : "" , tmplFunctionStartArrayRow : "{" , tmplFunctionStopArrayRow : "}" , tmplFunctionStopArray : "" , tmplSubexpressionStart : "{{autoindent}}(\n" , tmplSubexpressionStop : "\n)" , tmplIndentTab : "\t" , tmplIndentSpace : " " , autoLineBreak : "TOK_TYPE_FUNCTION | TOK_TYPE_ARGUMENT | TOK_SUBTYPE_LOGICAL | TOK_TYPE_OP_IN " , newLine : "\n" , customTokenRender : null , prefix : "" , postfix : "" , } ; if ( options ) { options = this . extend ( true , defaultOptions , options ) ; } else { options = defaultOptions ; } let indentCount = 0 ; let indent_f = function ( ) { let s = "" , i = 0 ; for ( ; i < indentCount ; i += 1 ) { s += options . tmplIndentTab ; } return s ; } ; let tokens = this . getTokens ( formula ) ; let outputFormula = "" ; let autoBreakArray = options . autoLineBreak . replace ( / \s / gi , "" ) . split ( "|" ) ; let isNewLine = true ; let testAutoBreak = function ( nextToken ) { let i = 0 ; for ( ; i < autoBreakArray . length ; i += 1 ) { if ( nextToken !== null && typeof nextToken !== "undefined" && ( ExcelFormula . types [ autoBreakArray [ i ] ] === nextToken . type . toString ( ) || ExcelFormula . types [ autoBreakArray [ i ] ] === nextToken . subtype . toString ( ) ) ) { return true ; } } return false ; } ; let lastToken = null ; while ( tokens . moveNext ( ) ) { let token = tokens . current ( ) ; let nextToken = tokens . next ( ) ; if ( token . subtype . toString ( ) === this . TOK_SUBTYPE_STOP ) { indentCount -= indentCount > 0 ? 1 : 0 ; } let matchBeginNewline = new RegExp ( "^" + options . newLine , "" ) , matchEndNewLine = new RegExp ( options . newLine + "$" , "" ) , autoBreak = testAutoBreak ( nextToken ) , autoIndent = isNewLine , indent = autoIndent ? indent_f ( ) : options . tmplIndentSpace , lineBreak = autoBreak ? options . newLine : "" ; outputFormula += ExcelFormula . applyTokenTemplate ( token , options , indent , lineBreak , options . customTokenRender , lastToken ) ; if ( token . subtype . toString ( ) === this . TOK_SUBTYPE_START ) { indentCount += 1 ; } isNewLine = autoBreak || matchEndNewLine . test ( outputFormula ) ; isFirstToken = false ; lastToken = token ; } outputFormula = options . prefix + ExcelFormula . trim ( outputFormula ) + options . postfix ; return outputFormula ; }
@memberof excelFormulaUtilities.parser
 @function
 @param {string} formula
 @param {object} options optional param
<pre>
   TEMPLATE VALUES
  {{autoindent}} - apply auto indent based on current tree level
  {{token}} - the named token such as FUNCTION_NAME or "string"
  {{autolinebreak}} - apply line break automatically.
@memberof excelFormulaUtilities . parser @function @param { string } formula @param { object } options optional param <pre > TEMPLATE VALUES {{ autoindent }} - apply auto indent based on current tree level {{ token }} - the named token such as FUNCTION_NAME or " string " {{ autolinebreak }} - apply line break automatically .
//Quick fix for trailing space after = sign //trim: true, //Tokens // TODO this strips out spaces which breaks part of issue 28.  'Data Sheet' gets changed to DataSheet