Some idea on how to implement the parameters for modifiers
4skinSkywalker opened this issue · 0 comments
Hi, I've fiddled with a piece of code which is similar to yours in terms of functionalities.
I've distinguished allocation from interpolation:
Allocation: [[ key = {{ value | modifier : parameter }} ]]
Interpolation: {{ value | modifier : parameter }}
As you can see the allocation can also contains an interpolation, but it's not necessary when you have to pop, to do that:
[[ key = POP ]]
With this syntax is possible to chain modifier with their parameter:
{{ value | modifier1 | modifier2 : param1 | modifier3 : param1 : param2 }}
In the example value
is fed to modifier1
which has no params, the result is then fed into modifier2
which has a single param and all is ultimately fed into modifier3
which has a couple of params.
class Grammar {
constructor() {
this.rules = {};
this.modifiers = {};
this.variables = {};
this.randomFn = Math.random;
}
// rule: "key -> value1 | value2 | value3"
setRule(rule) {
let validationRegex = /^ *[^->]+ * -> *[^->|]+( *\| *[^->| ]+[^->|]*)* *$/;
if (!validationRegex.test(rule)) {
console.error("Something wrong in your rule");
return;
}
let [key, values] = rule.split(/ *-> */);
values = values.split(/ *\| */);
this.rules[key] = values;
}
setModifier(name, fn) {
this.modifiers[name] = fn;
}
pickRandom(list) {
return list[Math.floor(this.randomFn() * list.length)];
}
evaluate(expr) {
let [value, ...modifiers] = expr.split(/ *\| */);
if (this.variables[expr]) {
return this.variables[expr][this.variables[expr].length - 1];
}
if (!this.rules[value]) {
console.error("No rule found with the name: " + value);
return;
}
value = this.pickRandom(this.rules[value]);
if (!modifiers.length) {
return value;
}
for (let modifier of modifiers) {
let [modifierName, ...params] = modifier.split(/ *: */);
if (!this.modifiers[modifierName]) {
console.error("No modifier found with the name: " + modifierName);
return;
}
value = this.modifiers[modifierName](value, ...params)
}
return value;
}
expand(text) {
console.log(text); // Console each step
let interpolation = " *{{ *([^|]+?( *\\| *([^: ]+( *: *[^: ]+)*) *?)*) *}} *";
let allocation = ` *\\[\\[ *([^{}[\\]]+?) *=(${interpolation}| *POP *)\\]\\] *`;
let interpolationRegex = new RegExp(`^${interpolation}$`);
let allocationRegex = new RegExp(`^${allocation}$`);
if (!(new RegExp(interpolation)).test(text) && !(new RegExp(allocation)).test(text)) {
return text;
}
let result = text.replace(/(\[\[(.*?)\]\]|{{(.*?)}})/g, text => {
if (allocationRegex.test(text)) {
let [_, key, pop, expr] = text.match(allocationRegex);
if (pop.trim() === "POP") {
if (!this.variables[key]) {
console.error("Nothing to pop at: " + key);
return;
}
this.variables[key].pop();
if (!this.variables[key].length) {
delete this.variables[key];
}
return "";
}
let value = this.evaluate(expr);
if (!this.variables[key]) {
this.variables[key] = [];
}
this.variables[key].push(value);
return value;
}
if (interpolationRegex.test(text)) {
let expr = text.match(interpolationRegex)[1];
return this.evaluate(expr);
}
console.error("Something wrong in your text");
});
this.expand(result);
}
}
var g = new Grammar();
g.setRule("adj -> dark | stormy | beautiful");
g.setRule("noun -> {{adj}} night");
g.setModifier("discapitalize", (text, howMany) => {
howMany = Number(howMany);
let result = "";
for (let i = 0; i < text.length; i++) {
if (i < howMany) {
result += text[i].toLowerCase();
} else {
result += text[i];
}
}
return result;
});
g.setModifier("capitalize", (text, howMany) => {
howMany = Number(howMany);
let result = "";
for (let i = 0; i < text.length; i++) {
if (i < howMany) {
result += text[i].toUpperCase();
} else {
result += text[i];
}
}
return result;
});
g.expand("It was a [[ adjective = {{ adj | capitalize : 3 | discapitalize : 2 }} ]] and {{ adjective }} {{ noun }}");
// The above expansion will result in the following rounds:
// Step 0: It was a [[ adjective = {{ adj | capitalize : 3 | discapitalize : 2 }} ]] and {{ adjective }} {{ noun }}
// Step 1: It was a beAutiful and beAutiful {{adj}} night
// Step 2: It was a beAutiful and beAutiful dark night