"use strict"; /** @fileOverview Implements `class page.EBNF.Rep`. * @author Axel T. Schreiner <ats@cs.rit.edu> * @version 1.5.0 */ /** Creates a new representation of an iteration for EBNF. * @class Represents an iteration for EBNF. * * @example * some: item +; * many: item *; * optional: item ?; * * @private * * @param {eNode} _node body; null if called during unmarshalling. * @param {number} _min minimum number of occurences, 0 or 1. * @param {number} _max maximum number of occurences, 1 or undefined. * * @property {eNode} node body. * @property {number} min minimum number of occurences, 0 or 1. * @property {number} max maximum number of occurences, 1 or undefined. * @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.Rep = function (_node, _min, _max) { this.node = _node; this.min = _min; this.max = _max; this.nt = undefined; this.empty = undefined; this.first = undefined; this.follow = undefined; }; page.baseclass(page.EBNF.Rep, 'page.EBNF.Rep'); /** Displays iteration in EBNF notation. * @returns {string} */ page.EBNF.Rep.prototype.toString = function () { var result; if (this.node instanceof page.EBNF.Alt || this.node instanceof page.EBNF.Seq || this.node instanceof page.EBNF.Lst) result = '(' + this.node + ')'; else result = this.node.toString(); if (this.min == 1) result += '+'; else if (this.max) result += '?'; else result += '*'; return result; }; /** Called once by {@link page.EBNF#init} to recursively translate into BNF. * * a+ * a* * a? * * where `.min` and `.max` are `1:undefined`, `0:undefined`, or `0:1`, respectively, are translated to * * $#: a $##; # .min == 1 * $##: ; * $##: a $##; * * $#: $##; # if left-hand side is known * $##: ; # .min == 0 * $##: a $##; * * $#: ; # .min == 0, .max == 1 * $#: a; * * @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.Rep.prototype.toBNF = function (_grammar, _nt) { _grammar.assert('__WHERE__', !this.nt); var nt1, result = this.nt = _nt ? _nt : _grammar.bnf.nt('$'); // $# var at = _grammar.bnf.rules.length; // placeholder if (this.min == 1 || (!this.max && _nt)) _grammar.bnf.rules.push(null); _grammar.bnf.rules.push(null, null); var node = this.node instanceof page.BNF.T ? this.node : this.node.toBNF(_grammar); if (this.min == 0 && this.max == 1) { // opt _grammar.bnf.rules[at++] = _grammar.bnf.rule(result, [ ]); // $#: _grammar.bnf.rules[at] = _grammar.bnf.rule(result, [ node ]); // $#: node } else { if (this.min == 1) { // some nt1 = _grammar.bnf.nt('$'); _grammar.bnf.rules[at++] = _grammar.bnf.rule(result, [ node, nt1 ]); // $#: node $## } else if (_nt) { // many, explicit nt nt1 = _grammar.bnf.nt('$'); _grammar.bnf.rules[at++] = _grammar.bnf.rule(result, [ nt1 ]); // $#: $## } else // many nt1 = result; _grammar.bnf.rules[at++] = _grammar.bnf.rule(nt1, [ ]); // $##: _grammar.bnf.rules[at] = _grammar.bnf.rule(nt1, [ node, nt1 ]); // $##: node $## } 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.Rep}`.nt` * and then delegates to the descendant. * @private */ page.EBNF.Rep.prototype.init = function () { this.assert('__WHERE__', !this.first); this.empty = this.nt.empty; this.first = this.nt.first; this.follow = this.nt.follow; if (this.node.init) this.node.init(); };