From 6ba9ec2030c58d739ec6602380e01b3f186c74a2 Mon Sep 17 00:00:00 2001 From: rsk0315 Date: Mon, 4 Dec 2023 23:28:02 +0900 Subject: [PATCH] pseudocode --- playground/css/pseudocode.css | 99 ++++++++++++++++ playground/index.md | 1 + playground/js/pseudocode.js | 208 ++++++++++++++++++++++++++++++++++ playground/pseudocode.html | 27 +++++ 4 files changed, 335 insertions(+) create mode 100644 playground/css/pseudocode.css create mode 100644 playground/js/pseudocode.js create mode 100644 playground/pseudocode.html diff --git a/playground/css/pseudocode.css b/playground/css/pseudocode.css new file mode 100644 index 00000000..cc8dd34c --- /dev/null +++ b/playground/css/pseudocode.css @@ -0,0 +1,99 @@ +body { + font-family: Lato; +} +button { + font-family: Lato; + color: #5f6368; + background-color: white; + border-radius: 4px; + border: 1px solid #dadce0; +} +button:focus { + color: white; + background-color: #ffadba; + border-radius: 4px; + border: 1px solid #ffadba; + font-weight: bold; + outline: 2px solid rgba(255, 173, 186, 0.7); +} +.error { + font-family: monospace; + color: #a11 +} +.frame-wrapper { + position: relative; +} +.frame-wrapper .text-label { + background-color: white; + box-shadow: 5px 0 0 white,-5px 0 0 white; + color: #5f6368; + display: inline; + font-size: 12px; + left: 12px; + margin-bottom: 0; + max-width: 300px; + padding: 0; + position: absolute; + top: 0px; + word-wrap: break-word; +} +.frame-wrapper .div-label { + background-color: white; + box-shadow: 5px 0 0 white,-5px 0 0 white; + color: #5f6368; + display: inline; + font-size: 12px; + left: 12px; + margin-bottom: 0; + max-width: 300px; + padding: 0; + position: absolute; + top: -8px; + word-wrap: break-word; +} +.frame-wrapper .text-label.focused, .frame-wrapper .div-label.focused { + color: #ffadba; +} +.frame-wrapper .textinput { + border: 1px solid #dadce0; + border-radius: 4px; + box-sizing: border-box; + color: #3c4043; + font-size: 14px; + margin: 8px 0; +} +.frame-wrapper input.textinput { + padding: 1px 8px; + height: 36px; +} +.frame-wrapper textarea.textinput { + padding: 8px; + min-height: 36px; + width: 100%; +} +.frame-wrapper div.textinput { + padding: 8px; + min-height: 30px; +} +.frame-wrapper .textinput:focus { + border: 1px solid #ffadba; + outline: 1px solid rgba(255, 173, 186, 0.7); +} + +.pseudocode { + -moz-border-radius: 3px; + -moz-box-shadow: 0 2px 0 rgba(34,58,112,0.4),0 0 0 2px #fff inset; + -webkit-border-radius :3px; + -webkit-box-shadow: 0 2px 0 rgba(34,58,112,0.4),0 0 0 2px #fff inset; + background-color: #eee; + border: 1px solid #ccc; + border-radius: 5px; + box-shadow: 0 2px 0 rgba(34,58,112,0.4),0 0 0 2px #fff inset; + color: #333; + display: block; + font-family: Menlo, monospace; + line-height: 1.4; + margin: .4em .1em; + padding: .8em 1.8em; + text-shadow: 0 0px 0 #fff; +} diff --git a/playground/index.md b/playground/index.md index 531f825b..31eb2567 100644 --- a/playground/index.md +++ b/playground/index.md @@ -8,3 +8,4 @@ - [コマンドをコピペする UI のやつ](rich-terminal.html) - [mod 998244353 の計算機](mod_calculator/) - [mod 1000000007 の計算機](mod_calculator/?mod=1000000007) +- [pseudocode](pseudocode.html) diff --git a/playground/js/pseudocode.js b/playground/js/pseudocode.js new file mode 100644 index 00000000..e94aaf25 --- /dev/null +++ b/playground/js/pseudocode.js @@ -0,0 +1,208 @@ +const macros = { + '\\halfopen': '[#1, #2)', + '\\floor': '\\lfloor #1\\rfloor', + '\\ceil': '\\lceil #1\\rceil', + '\\rounded': '\\lfloor #1\\rceil', + '\\Floor': '\\left\\lfloor #1\\right\\rfloor', + '\\Ceil': '\\left\\lceil #1\\right\\rceil', + '\\Rounded': '\\left\\lfloor #1\\right\\rceil', + '\\angled': '\\langle #1\\rangle', + '\\Angled': '\\left\\langle #1\\right\\rangle', + '\\lcm': '\\operatorname*{lcm}', + '\\gcd': '\\operatorname*{gcd}', + '\\poly': '\\operatorname{poly}', + '\\polylog': '\\operatorname{polylog}', + '\\concat': '\\mathrel{+\\!\\!+}', + '\\mex': '\\operatorname*{mex}', + '\\qed': '\\square', + '\\Q': '\\mathbb{Q}', + '\\dd': '\\mathrm{d}', + '\\ForallL': '{}^{\\forall}#1.\\:#2', + '\\Forall': '{}^{\\forall}#1.\\,\\left[#2\\right]', + '\\ExistsL': '{}^{\\exists}#1.\\:#2', + '\\Exists': '{}^{\\exists}#1.\\,\\left[#2\\right]', + '\\roundp': '(\\kern-.2em[#1]\\kern-.2em)', + '\\bigroundp': '\\big(\\kern-.25em\\big[#1\\big]\\kern-.25em\\big)', + '\\Bigroundp': '\\Big(\\kern-.3em\\Big[#1\\Big]\\kern-.3em\\Big)', + '\\biggroundp': '\\bigg(\\kern-.3em\\bigg[#1\\bigg]\\kern-.3em\\bigg)', + '\\Biggroundp': '\\Bigg(\\kern-.35em\\Bigg[#1\\Bigg]\\kern-.35em\\Bigg)', + '\\hfloor': '\\lfloor\\hspace{-.25em}\\lfloor#1\\rfloor\\hspace{-.25em}\\rfloor', + '\\xgets': '\\xleftarrow{#1}', + '\\eod': '\\blacksquare', // end of
+}; +const KaTeXOptions = { + delimiters: [ + {left: '$$', right: '$$', display: true}, + {left: '\\[', right: '\\]', display: true}, + {left: '$', right: '$', display: false}, + {left: '\\(', right: '\\)', display: false}, + ], + macros: macros, +}; + +// --- + +function split(input) { + const res = []; + let inside = false; + let cur = ""; + for (const line of input.split('\n')) { + if (line === "```") { + if (inside) { + res.push(cur); + cur = ""; + } + inside = !inside; + } else { + if (inside) { + cur += line + "\n"; + } + } + } + return res; +} + +function modifyMessage(msg, e) { + return `${msg} + ${e}` + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/(.)\u0332/g, '$1'); +} + +function markup(code, elt) { + const RE = /(?:\[(?[^\]\}]+)\])|(?:\{(?.+)\})|(?\n)/g; + + let out = "$\\begin{aligned}"; + let indent = 0; + let blank = true; + let lineno = 0; + let opened = false; + let opening = false; + for (const match of code.matchAll(RE)) { + if (match.groups.NL) { + out += "\\\\\n"; + blank = true; + if (opening) { + out += "&\\begin{aligned}"; + opening = false; + opened = true; + } + continue; + } + + if (blank) { + if (match.groups.CK === 'function') { + if (opened) { + out += "\\end{aligned}\\\\\n"; + lineno = 0; + opened = false; + } + opening = true; + } else { + if (opened) { + out += `\\qquad{\\scriptsize ${++lineno}\\colon}`; + } + out += "&"; + if (match.groups.CK && match.groups.CK.startsWith('end')) { + --indent; + } + out += "\\quad ".repeat(indent); + if (match.groups.CK && !match.groups.CK.startsWith('end')) { + if (opened && !["function", "return"].includes(match.groups.CK)) { + ++indent; + } + } + } + blank = false; + } else { + out += "\\;"; + } + + if (match.groups.CK) { + if (match.groups.CK === "function") { + out += "&"; + } + out += `\\text{\\textbf{${match.groups.CK}}}`; + } else if (match.groups.CE) { + out += ` ${match.groups.CE} `; + } + } + if (opened) { + out += "\\end{aligned}" + } + out += "\\end{aligned}$"; + + elt.innerHTML = out; + + let err = ""; + renderMathInElement(elt, { + delimiters: [ + { left: '$$', right: '$$', display: true }, + { left: '$', right: '$', display: false }, + ], + macros, + errorCallback: ((msg, e) => { + console.error(e); + }), + }); + if (err) { + elt.innerText += 'error, see console.'; + } +} + +function render() { + const input = window.document.getElementById('input'); + const inputText = input.value; + + const codeInputs = split(inputText); + const output = window.document.getElementById('output'); + output.innerHTML = ""; + for (const code of codeInputs) { + const div = document.createElement("div"); + const span = document.createElement("span"); + span.classList.add("pseudocode"); + markup(code, span); + div.appendChild(span); + output.appendChild(div); + } +} + +const CACHE_KEY = 'PseudocodeInput'; + +function loadCache() { + const input = window.document.getElementById('input'); + const cache = localStorage.getItem(CACHE_KEY) + if (cache !== null) input.value = cache; +} + +function storeCache() { + const input = window.document.getElementById('input'); + localStorage.setItem(CACHE_KEY, input.value); +} + +window.onload = function() { + const input = window.document.getElementById('input'); + input.addEventListener('input', function() { + storeCache(); + }); + loadCache(); + render(); + + for (const io of ['input', 'output']) { + const ioEl = window.document.getElementById(io); + const ioLabel = window.document.getElementById(`${io}-label`); + ioEl.addEventListener('focus', function() { + ioLabel.classList.add('focused'); + }); + ioEl.addEventListener('focusout', function() { + ioLabel.classList.remove('focused'); + }); + } + + { + const el = window.document.getElementById('output-label'); + renderMathInElement(el); + } + input.focus(); +} diff --git a/playground/pseudocode.html b/playground/pseudocode.html new file mode 100644 index 00000000..c3261894 --- /dev/null +++ b/playground/pseudocode.html @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + +

Pseudocode Playground

+
+ + +
+
+ +
+
+ +