Remove nonstandard Markdown hints.
[rrr.git] / rrr.js
1 /* rrr - recursive random rewrites
2 Copyright 2014 Joe Wreschnig
3 Licensed under the terms of the GNU GPL v2 or later
4 @license https://www.gnu.org/licenses/gpl-2.0.html
5 @source: https://yukkurigames.com/rrr/
6 *//*
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 As additional permission, you may distribute the program or works
18 based on it without the copy of the GNU GPL normally required,
19 provided you include this license notice and a URL through which
20 recipients can access the corresponding source code.
21 */
22
23 (function (exports) {
24 "use strict";
25
26 function randfloat (hi) { return Math.random() * hi; }
27 function randint (hi) { return randfloat(hi) | 0; }
28 function randchoice (seq) { return seq[randint(seq.length)]; }
29 function randrange (lo, hi) { return lo + randint(hi - lo); }
30
31 function Productions (dfn) {
32 this.__total = 0;
33 if (Array.isArray(dfn) || dfn.toString() === dfn) {
34 this.__rules = Array.prototype.slice.call(dfn);
35 } else if (typeof dfn === "function") {
36 this.__rules = dfn;
37 } else {
38 this.__rules = {};
39 Object.keys(dfn).forEach(function (key) {
40 var value = dfn[key];
41 this.__total += value;
42 this.__rules[key] = value;
43 }, this);
44 }
45 }
46
47 Productions.prototype.randproduction = function () {
48 if (Array.isArray(this.__rules))
49 return randchoice(this.__rules);
50
51 else if (typeof this.__rules === "function")
52 return this.__rules();
53
54 var x = randfloat(this.__total);
55 for (var k in this.__rules) {
56 x -= this.__rules[k];
57 if (x < 0)
58 return k;
59 }
60 return null;
61 };
62
63 function Grammar (productions, start) {
64 this.start = start || "";
65 this.productions = {};
66 for (var k in productions)
67 this.productions[k] = new Productions(productions[k]);
68 }
69
70 function randcount (lo, hi, rep) {
71 var n = hi ? randrange(+lo, +hi + 1)
72 : lo ? +lo
73 : 1;
74
75 var m = 1;
76 switch (rep) {
77 case '*': m = 0; while (Math.random() < 0.5) ++m; break;
78 case '+': while (Math.random() < 0.5) ++m; break;
79 case '?': m = +(Math.random() < 0.5); break;
80 }
81 return n * m;
82 }
83
84 function repeat (n, f) {
85 var r = [];
86 while (n-- > 0)
87 r.push(f());
88 return r;
89 }
90
91 var R = /<([^> ]+)( ?)>(?:{([0-9]+)(?:,([0-9]+))?}|(\*|\+|\?))?/g;
92 Grammar.prototype.expand = function (start) {
93 var g = this;
94 start = start || this.start;
95 return start.replace(R, function _ (match, p, w, lo, hi, rep) {
96 var prod = g.productions[p];
97 return repeat(randcount(lo, hi, rep), function () {
98 return prod.randproduction().replace(R, _);
99 }).join(w);
100 });
101 };
102
103 exports.Grammar = Grammar;
104 })(typeof module !== "undefined" ? module.exports : this.gengram = {});