text: 'use strict';
  
  const debug = require('debug')('elastic-muto');
  
  const isNil = require('lodash.isnil');
  const isEmpty = require('lodash.isempty');
  const isString = require('lodash.isstring');
  const isFunction = require('lodash.isfunction');
  const cosmiconfig = require('cosmiconfig');
  
  const mutoParser = require('./muto-parser');
  const qryBuilderDef = require('./query-builder-def');
  
  const explorer = cosmiconfig('muto', {
      packageProp: false,
      rc: false,
      js: 'muto.config.js',
      argv: false,
      sync: true
  });
  
  const qryBuilder = Object.assign({}, qryBuilderDef);
  try {
      debug('Loading user config using cosmiconfig(sync)');
      const result = explorer.load('.');
      if (!isNil(result)) {
          debug('Successfully parsed config -', result);
          Object.assign(qryBuilder, result.config);
      } else {
          debug('muto.config.js not found');
      }
  } catch (err) {
      console.error('Failed to parse config!');
      console.error(err);
  }
  
  /**
   * Parses given expression and generates an `elastic-builder` query object.
   *
   * @example
   * // Pass expression as string
   * const qry = muto.parse(
   *     '["discount"] is false or (["psngr_cnt"] > 81 and ["booking_mode"] contains "Airport")'
   * )
   *
   * // OR
   * // Pass conditions using helper classes
   * const qry = muto.parse(
   *     muto.where()
   *         .or(muto.cn('discount').is(false))
   *         .or(
   *             muto.where()
   *                 .and(muto.cn('psngr_cnt', 'gt', 81))
   *                 .and('["booking_mode"] contains "Airport"')
   *         )
   * );
   *
   * qry.toJSON()
   * {
   *   "bool": {
   *     "should": [
   *       { "term": { "discount": false } },
   *       {
   *         "bool": {
   *           "must": [
   *             {
   *               "range": { "psngr_cnt": { "gt": 81 } }
   *             },
   *             {
   *               "match": { "booking_mode": "Airport" }
   *             }
   *           ]
   *         }
   *       }
   *     ]
   *   }
   * }
   *
   * @param {string|Where|Condition} expr
   * @param {Array} [notAnalysedFields]
   * @returns {Object} `elastic-builder` `BoolQuery` object generated by
   * parsing the expression
   */
  module.exports = function parse(expr, notAnalysedFields) {
      if (isEmpty(expr)) {
          throw new Error('Expression cannot be empty!');
      }
  
      let strExpr;
      if (isString(expr)) strExpr = expr;
      else if (isFunction(expr.build)) strExpr = expr.build();
      else strExpr = expr.toString();
  
      debug("Parsing expression '%s'", strExpr);
      /*
          Wrap expression in ().
          This handles the edge case of
          (ConditionA and ConditionB) or (ConditionC and ConditionD)
          Was not able to handle in PEG.js due to
          GrammarError: Possible infinite loop when parsing (left recursion: Expression -> Expression).
          (expr) will be parsed the same as ((expr))
          So even if expression is already wrapped, it is okay.
      */
      try {
          return mutoParser.parse(
              `(${strExpr})`,
              Object.assign(
                  { notAnalysedFields: new Set(notAnalysedFields), debug },
                  qryBuilder
              )
          );
      } catch (err) {
          console.error('Failed to parse expression', strExpr);
          err.expression = strExpr;
          throw err;
      }
  };
  