Home Reference Source Test

src/Botlang.js

'use strict';

import { Input, Lexer } from './Lexer';
import Parser from './Parser';
import pkg from '../package.json';

/**
 * Botlnag interpreter.
 */
class Botlang {
  /**
   * Create a new Botlang interpreter.
   * @param {String} brain
   */
  constructor(brain) {
    const input = new Input(brain),
          lexer = new Lexer(input),
          parser = new Parser(lexer);

    /**
     * @private
     * @type {Object}
     */
    this.program = parser.parse();
  }

  /**
   * @private
   * @param {String} trigger
   * @param {Object} node
   * @type {String}
   */
  static evalTriggerNode(trigger, node) {
    if (0 === node.responses.length) return null;

    let reply = node.responses[Math.floor(Math.random() * node.responses.length)].value;

    // Do string substitution
    const pos = reply.indexOf('$');
    if (-1 < pos) {
      const pattern = node.src.replace(/\*/, '(\\w+)').replace(/\$/, '(\\w+)'),
            match = new RegExp(pattern, 'i').exec(trigger);

      if (Array.isArray(match)) {
        const substitution = undefined !== match[2]
          ? match[2]
          : match[1];

        reply = `${reply.substring(0, pos)}${substitution}${reply.substring(pos + 1)}`;
      }
    }

    return reply;
  }

  /**
   * @private
   * @type {Object}
   */
  match(message) {
    let res = null;

    this.program.body.forEach((el) => {
      if ('trigger' === el.type && new RegExp(el.pattern, 'i').test(message)) res = Botlang.evalTriggerNode(message, el);
    });

    return res || 'Sorry, I do not know the answer to that question.';
  }

  /**
   * Returns a reply to a given message accordingly to the defined brain.
   * @return {String}
   */
  reply(message) {
    if ('' === message) return '';
    return this.match(message);
  }

  /**
   * Return the current version
   * @return {String}
   */
  static version() {
    return pkg.version;
  }
}

export default Botlang;