Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some idea on how to implement the parameters for modifiers #52

Open
4skinSkywalker opened this issue Aug 7, 2021 · 0 comments
Open

Comments

@4skinSkywalker
Copy link

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant