Source: EBNF/Alt.js

"use strict";

/** @fileOverview Implements `class page.EBNF.Alt`.
  * @author Axel T. Schreiner <ats@cs.rit.edu>
  * @version 1.5.0
  */

/** Creates a new representation of a choice for EBNF.
  * @class Represents a choice for EBNF.
  *
  * @example
  * choice: a | b;
  *
  * @private
  *
  * @param {eNode[]} _nodes alternatives, at least two.
  *
  * @property {eNode[]} nodes descendants, at least two.
  * @property {page.BNF.NT} nt corresponding non-terminal for BNF.
  * @property {boolean} empty true if no input can be accepted.
  * @property {Map} first terminals at front, maps ord to {@link page.BNF.T}.
  * @property {Map} follow terminals following, maps ord to {@link page.BNF.T}.
  */
page.EBNF.Alt = function (_nodes) {
  this.nodes = _nodes;
  this.nt = undefined;  
  this.empty = undefined;
  this.first = undefined;
  this.follow = undefined;
};

page.baseclass(page.EBNF.Alt, 'page.EBNF.Alt');

/** Displays alternatives in EBNF notation.
  * @returns {string}
  */
page.EBNF.Alt.prototype.toString = function () {
  return this.nodes.join(' | ');
};

/** Called once by {@link page.EBNF#init} to recursively translate into BNF.
  *
  *     a | b | c
  *
  * is translated to
  *
  *     $#: a;
  *     $#: b;
  *     $#: c;
  *
  * @private
  *
  * @param {page.EBNF} _grammar object to which the construct belongs.
  * @param {page.EBNF.NT} [_nt] left-hand side, if known; avoids the need for a fresh non-terminal.
  *
  * @returns  {page.BNF.NT} non-terminal representing the translated construct.
  */
page.EBNF.Alt.prototype.toBNF = function (_grammar, _nt) {  
  _grammar.assert('__WHERE__', !this.nt);
  _grammar.assert('__WHERE__', this.nodes.length > 1);

  var result = this.nt = _nt ? _nt : _grammar.bnf.nt('$');   // $#
  var at = _grammar.bnf.rules.length;                         // placeholder
  this.nodes.forEach(function (node) { _grammar.bnf.rules.push(null); });
  // create rules
  this.nodes.forEach(function (node) {              // $#: choice
    _grammar.bnf.rules[at++] = _grammar.bnf.rule(result, [
      node instanceof page.BNF.T ? node : node.toBNF(_grammar)
    ]);
  });
  return result;
};

/** Called once by {@link page.EBNF#init} to complete EBNF grammar initialization.
  * Imports {@link page.BNF.NT}`.empty`, {@link page.BNF.NT}`.first`, and
  * {@link page.BNF.NT}`.follow` from {@link page.EBNF.Alt}`.nt`
  * and then delegates to the descendants.
  * @private
  */
page.EBNF.Alt.prototype.init = function () {
  this.assert('__WHERE__', !this.first);

  this.empty = this.nt.empty;
  this.first = this.nt.first;
  this.follow = this.nt.follow;

  this.nodes.forEach(function (node) { if (node.init) node.init(); });
};