prototype-3x5/src/3x5.ts

169 lines
3.8 KiB
TypeScript

import { Button, RegisterButtonOnClick } from './lib/button';
import { Card, CardVm, GetField } from './lib/card';
import { ALL as Debug, RegisterJumpHere } from './lib/debug';
import { Expr } from './lib/expr';
import { ALL as HTML } from './lib/html';
import { parse } from './parser';
import { runNoctl } from './vm';
import { AsHtml, AsText, TextPiece } from './words';
/**
* Updates a card's fields
* @param state VM state
* @param card
* @param fields
*/
function parseFields(card: Card, fields: string) {
const script = parse(fields);
const newFields: Record<string, string> = {};
if (script[0]) {
script[1].forEach(([name, value = undefined]) => {
if (name != undefined) {
newFields[AsText(name as TextPiece)] = value
? AsText(value as TextPiece)
: "";
}
});
card.fields = newFields;
} else {
console.error(script[1]);
}
}
/**
* Global state: a single card
*/
let theCard: Card = {
id: 100,
fields: {},
code: "",
};
const TEXTAREA_STYLE: Partial<CSSStyleDeclaration> = {
display: "block",
width: "100%",
};
const fieldInput = document.createElement("textarea");
Object.assign(fieldInput.style, TEXTAREA_STYLE, { height: "8em" });
fieldInput.value = String.raw`
title "Hello, World!"
`.trim();
const codeInput = Object.assign(document.createElement("textarea"), {
oninput: render,
});
Object.assign(codeInput.style, TEXTAREA_STYLE, { height: "20em" });
codeInput.value = String.raw`
h1 [get title]
button disabled
button "Hello" -onClick {
alert Hiya
}
para [2 + 2]
block {
This is a paragraph of text, with one [b bold] word. Yes, this means there has to be some magic in text processing... <b>this</b> won't work.
}
block -red "Beware!"
para "All text should be quoted, it's clearer that way. & blockquotes already should contain paragraphs. (maybe normalize nested paragraphs)"
block {
First block
} {
Second block
Is this markdown-parsed?
[button "No we want to render UI" \\{noop}]
} {
Since we want escapes to work, these blocks [i will] be subject to substitutions, maybe via a relaxed bareWordTmpl.
}
# A comment
para {
line endings escaped\
one slash
not escaped if \\
two slashes
escaped with a slash if \\\
three slashes
not escaped with two slashes if \\\\
four slashes
escaped with two slashes if \\\\\
five slashes
not escaped with three slashes if \\\\\\
six slashes
}
`.trim();
const state = document.createElement("pre");
const display = document.createElement("blockquote");
const debugDisplay = document.createElement("pre");
const COMMANDS = {
...HTML,
get: GetField,
expr: Expr,
...Debug,
button: Button,
};
function render() {
parseFields(theCard, fieldInput.value);
theCard.code = codeInput.value;
const vm: CardVm = {
mode: ["render"],
commands: COMMANDS,
output: "",
card: theCard,
};
const script = parse(theCard.code);
let html = "";
if (script[0]) {
runNoctl(vm, script[1], (word) => (vm.output += AsHtml(word) + "\n"));
} else {
vm.output = script[1];
}
html = vm.output;
state.textContent = JSON.stringify(theCard, null, 2);
display.innerHTML = html;
debugDisplay.textContent = html;
}
function triggerEvent(handlerPos: number) {
parseFields(theCard, fieldInput.value);
theCard.code = codeInput.value;
const vm: CardVm = {
mode: ["findingAction", handlerPos],
commands: COMMANDS,
output: "",
card: theCard,
};
const script = parse(theCard.code);
if (script[0]) {
runNoctl(vm, script[1], (word) => {});
} else {
console.debug(script[1]);
}
state.textContent = JSON.stringify(theCard, null, 2);
}
render();
document.body.append(fieldInput, codeInput, display, state, debugDisplay);
RegisterJumpHere(codeInput);
RegisterButtonOnClick(triggerEvent);