State Machine Lang
by Douglas_Meyer
CSS
pre {
background-color: #DDD;
white-space: pre-wrap;
}
JavaScript
console.clear();
window.onerror = log;
function doc(fn){
return fn.toString().replace(/^\s*function\s*\(\)\s*\{\s*\/\*\s*([\s\S]*\S)\s*\*\/\}\s*$/, "$1");
}
function log(str){
const pre = document.createElement('pre');
document.body.appendChild(pre);
pre.textContent = str;
}
// ---- Lexical Analyzer
const lex = str => str.match(/(:?'(:?\\.|[^'])*'|"(:?\\.|[^"])*"|[,=+*:{}-]|[^\s:,]+)/g);
// ---- Parser
const parse = tokens => {
let c = 0;
const peek = () => tokens[c];
const consume = () => tokens[c++];
const die = (msg) => {
console.error(msg);
throw msg;
};
const parseName = () => {
if (!/^[a-zA-Z0-9_]+$/.test(peek())) die(`Expected name got ${peek()}`);
return consume();
};
const parseExpression = () => {
if ('= != & + -'.split(' ').includes(peek())) return {
op: consume(), exp: [ parseExpression(), parseExpression() ]
};
if (peek() === 'toInteger') return { op: consume(), exp: parseExpression() }
if (/^\d+$/.test(peek())) return { value: parseInt(consume()) };
if (/^[a-zA-Z0-9_]+$/.test(peek())) return { variable: consume() };
if (/^(:?'(:?\\.|[^'])*'|"(:?\\.|[^"])*")$/.test(peek())) return { value: consume().slice(1,-1) };
};
const parseState = () => {
const state = {};
if (peek() !== '{') die(`Expected '{' got ${peek()}`);
consume();
while (peek() !== '}') {
const name = parseName();
if (',}'.split('').includes(peek())) {
state[name] = { variable: name };
if (peek() === '}') break;
consume();
continue;
}
if (peek() !== ':') die(`Expected ':' got ${peek()}`);
consume();
state[name] = parseExpression();
if (peek() === ',') consume();
}
consume();
return state;
};
parseTransition = () => {
const stateName = parseName();
const condition = parseExpression();
if (peek() !== ':') die(`Expected ':' got ${peek()}`);
consume();
const output = peek() === '>' && consume() &&...