--- /dev/null
+/* rrr - recursive random rewrites
+ Copyright 2014 Joe Wreschnig
+ Licensed under the terms of the GNU GPL v2 or later
+ @license https://www.gnu.org/licenses/gpl-2.0.html
+ @source: https://yukkurigames.com/rrr/
+*//*
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ As additional permission, you may distribute the program or works
+ based on it without the copy of the GNU GPL normally required,
+ provided you include this license notice and a URL through which
+ recipients can access the corresponding source code.
+*/
+
+(function (exports) {
+ "use strict";
+
+ function randfloat (hi) { return Math.random() * hi; }
+ function randint (hi) { return randfloat(hi) | 0; }
+ function randchoice (seq) { return seq[randint(seq.length)]; }
+ function randrange (lo, hi) { return lo + randint(hi - lo); }
+
+ function Productions (dfn) {
+ this.__total = 0;
+ if (Array.isArray(dfn) || dfn.toString() === dfn) {
+ this.__rules = Array.prototype.slice.call(dfn);
+ } else if (typeof dfn === "function") {
+ this.__rules = dfn;
+ } else {
+ this.__rules = {};
+ Object.keys(dfn).forEach(function (key) {
+ var value = dfn[key];
+ this.__total += value;
+ this.__rules[key] = value;
+ }, this);
+ }
+ }
+
+ Productions.prototype.randproduction = function () {
+ if (Array.isArray(this.__rules))
+ return randchoice(this.__rules);
+
+ else if (typeof this.__rules === "function")
+ return this.__rules();
+
+ var x = randfloat(this.__total);
+ for (var k in this.__rules) {
+ x -= this.__rules[k];
+ if (x < 0)
+ return k;
+ }
+ return null;
+ };
+
+ function Grammar (productions, start) {
+ this.start = start || "";
+ this.productions = {};
+ for (var k in productions)
+ this.productions[k] = new Productions(productions[k]);
+ }
+
+ function randcount (lo, hi, rep) {
+ var n = hi ? randrange(+lo, +hi + 1)
+ : lo ? +lo
+ : 1;
+
+ var m = 1;
+ switch (rep) {
+ case '*': m = 0; while (Math.random() < 0.5) ++m; break;
+ case '+': while (Math.random() < 0.5) ++m; break;
+ case '?': m = +(Math.random() < 0.5); break;
+ }
+ return n * m;
+ }
+
+ function repeat (n, f) {
+ var r = [];
+ while (n-- > 0)
+ r.push(f());
+ return r;
+ }
+
+ var R = /<([^> ]+)( ?)>(?:{([0-9]+)(?:,([0-9]+))?}|(\*|\+|\?))?/g;
+ Grammar.prototype.expand = function (start) {
+ var g = this;
+ start = start || this.start;
+ return start.replace(R, function _ (match, p, w, lo, hi, rep) {
+ var prod = g.productions[p];
+ return repeat(randcount(lo, hi, rep), function () {
+ return prod.randproduction().replace(R, _);
+ }).join(w);
+ });
+ };
+
+ exports.Grammar = Grammar;
+})(typeof module !== "undefined" ? module.exports : this.rrr = {});