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() &&...