JSFiddle - React, Tailwind, and code Playground
HTML
<textarea id=codes></textarea><br>
<button onclick="update()">Update</button>
<button onclick="step()">Step</button>
<p id=states>
CSS
p{font-family:monospace}
JavaScript
var rules = [], state = "";
function update(){
let code = codes.value.split(/\n::=\n(.*)/s);
rules = code[0].split`\n`.filter(a=>a);
state = code[1]||'';
states.innerText = state;
states.style.color = "black";
}
function step(){
let matchingRules = [];
for (let rule of rules){
let allMatches = matches(rule);
if (allMatches.length){
matchingRules.push([rule,allMatches]);
}
}
if (!matchingRules.length){
states.style.color = "#202020";
return;
}
let ruleMatch = chooseRandom(matchingRules);
let rule = ruleMatch[0];
let match = chooseRandom(ruleMatch[1]);
let toReplace = rule.split('::=').slice(1).join('::=');
toReplace = toReplace.replace(/\\n/g,'\n');
[...toReplace.matchAll(/\$(\d+)/g)].reverse().forEach(a=>{
toReplace = toReplace.substring(0,a.index)+(match[0][Number(a[0].slice(1))]||"")+toReplace.substring(a.index+a[0].length,toReplace.length);
});
console.log(match, toReplace);
if (toReplace[0]=="~"){
alert(toReplace.substring(1))
toReplace="";
}
toReplace = toReplace.replace(/:::/g,prompt)
state = state.substring(0,match[1]) + toReplace + state.substring(match[2],state.length);
states.innerText = state;
}
function chooseRandom(items){
return items[Math.floor(Math.random()*items.length)];
}
function matches(rule){
let re = RegExp(rule.split('::=')[0],'gs');
let matches = [];
while ((match = re.exec(state)) != null){
matches.push([match, match.index, re.lastIndex]);
if (match.index===re.lastIndex) re.lastIndex++;
}
return matches;
}