Source: Parser.js

"use strict";

require("../src/metaclass");

var TransformStream = require("stream").Transform,
    util = require("util");

var merge = require("merge");

var Logger = require("./logger"),
    StreamEmitter = require("./StreamEmitter");

util.inherits(Parser, TransformStream);
StreamEmitter.mixin(Parser);

module.exports = Parser;

Parser.WHITESPACE = "whitespace";
Parser.PROMPT = "prompt";

/**
 * @typedef {Object} ParserConfiguration
 * @property {string|Writable} log - If a string, path to a log file, else a Writable stream.
 */
/**
 * Abstract Base class for Parsers.
 *
 * @param {ParserConfiguration} [config]
 * @constructor
 * @abstract
 */
function Parser(config) {
  TransformStream.call(this, { objectMode: true });

  this._variables = null;
  this._logger = Logger.initLogger(config);

  this.reset();
}

Parser.prototype.reset = function() {
  this._resetParser();
};

//noinspection JSUnusedGlobalSymbols
Parser.prototype._transform = function(chunk, encoding, done) {
  try {
    try {
      chunk.split("").forEach(this._character.bind(this));

      // flush through any remaining chars
      this._flush(function() {});
    }
    catch (err) {
      this._event("parsingerror", err);
    }
  }
  finally {
    done();
  }
};

//noinspection JSUnusedGlobalSymbols
Parser.prototype._flush = function(done) {
  try {
    while (this._next.length) {
      this._tokenise();
    }
  }
  catch (err) {
    this._event("parsingerror", err);
  }

  done();
};

Parser.prototype._character = function(char) {
  this._next.push(char);

  // this method is abstract.
  this._tokenise();
};

Parser.prototype._tokenise = function() {
  throw new Error("Not implemented");
};

Parser.prototype._newToken = function(tokenType) {
  this._tokenType = tokenType;
  this._tokenValue = this._next[0];
  this._consumeChar();
};

Parser.prototype._continueToken = function() {
  this._tokenValue += this._next[0];
  this._consumeChar();
};

/**
 * @ignore
 *
 * @param [properties] Optional properties to place into the token
 * @protected
 */
Parser.prototype._endToken = function(properties) {
  var token = merge(properties, {
    type: this._tokenType,
    value: this._tokenValue
  });

  this._resetToken();

  // this method is abstract
  this._parse(token);
};

Parser.prototype._resetToken = function() {
  this._tokenType = null;
  this._tokenValue = null;
};

Parser.prototype._continuePrompt = function() {
  if (this._next[0] === " ") {
    this._endToken();
    this._consumeChar();

    return;
  }

  this._continueToken();
};

Parser.prototype._consumeChar = function(numChars) {
  numChars = numChars || 1;

  while (numChars) {
    this._lastChar = this._next.shift();

    numChars--;
  }
};

//noinspection JSUnusedLocalSymbols
Parser.prototype._parser = function(token) { // jshint ignore:line
  throw new Error("Not implemented");
};

/**
 * @ignore
 *
 * @param pos From the bottom of the stack
 * @protected
 */
Parser.prototype._peekAtToken = function(pos) {
  pos = pos || 0;

  return this._parserStack[pos] || {
        type: null,
        value: null
      };
};

Parser.prototype._topToken = function() {
  return this._parserStack.top();
};

Parser.prototype._shiftToken = function() {
  return this._parserStack.shift();
};

Parser.prototype._pushToken = function(token) {
  this._parserStack.push(token);
};

Parser.prototype._resetParser = function() {
  this._next = [];
  this._tokenType = null;
  this._parserAllowedFollow = null;

  this._parserStack = [];

  // semantic info storage during a parse
  this._parserAux = {
    symbolStack: []
  };

  this._tokenValue = null;
  this._lastChar = null;
};