lumos-language
Version:
Lumos Language - Interactive CLI
287 lines (257 loc) • 9.47 kB
HTML
<html>
<head>
<meta charset="UTF-8" />
<title>Lumos Language</title>
<link rel="apple-touch-icon" sizes="180x180" href="https://cdn.glitch.global/a6e15949-0cae-4ce8-a653-5883a6d0adc5/Lumos.png" />
<link rel="icon" type="image/x-icon" href="https://cdn.glitch.global/a6e15949-0cae-4ce8-a653-5883a6d0adc5/Lumos.ico" />
<style>
body {
font-family: monospace;
background: #1e1e1e;
color: #d4d4d4;
padding: 1em;
}
.header {
display: flex;
}
h1 {
font-size: 4em;
display: inline-block;
vertical-align: middle;
}
h3 {
font-size: 2em;
}
iframe {
display: block;
margin: 0 auto;
}
img {
width: 10%;
height: 10%;
}
#output {
white-space: pre-wrap;
margin-top: 1em;
}
input[type="text"] {
width: 100%;
padding: 0.5em;
background: #2e2e2e;
color: white;
border: none;
}
.history-item {
cursor: pointer;
color: #9cdcfe;
}
#compileButton {
margin-top: 1em;
background: #007acc;
color: white;
border: none;
padding: 0.5em 1em;
cursor: pointer;
font-weight: bold;
}
#compileButton:hover {
background: #005f9e;
}
h5, #logo {
text-align: center;
}
.github {
mix-blend-mode: darken;
}
.npm {
mix-blend-mode: color;
}
.github, .npm {
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<div class="header">
<img src="https://cdn.glitch.global/a6e15949-0cae-4ce8-a653-5883a6d0adc5/Lumos.png?v=1748865997035" />
<h1>Lumos Language</h1>
</div>
<h4>Press Enter / Return to execute your code.</h4>
<input type="text" id="input" placeholder="Enter command..." />
<button id="compileButton">Compile</button>
<br />
<h3>Usage</h3>
<iframe src="https://cdn.glitch.global/a6e15949-0cae-4ce8-a653-5883a6d0adc5/Lumos.pdf?v=1748869028196" width="80%" height="50%"></iframe>
<div id="output"></div>
<script>
class BreakException {}
class ContinueException {}
const vars = {};
const functions = {};
const history = [];
function evaluateExpression(expr) {
try {
return Function(...Object.keys(vars), `return (${expr})`)(...Object.values(vars));
} catch (e) {
throw new Error("Invalid expression: " + expr);
}
}
function preprocessCommand(command) {
command = command.replace(/\r\n/g, "\n");
const timesRegex = /^(\d+)\.times\s+do\s+\|([a-zA-Z_][a-zA-Z0-9_]*)\|([\s\S]+?)end$/;
const timesMatch = command.match(timesRegex);
if (timesMatch) {
const count = parseInt(timesMatch[1]);
const varName = timesMatch[2];
const body = timesMatch[3].trim();
let result = "";
for (let i = 0; i < count; i++) {
vars[varName] = i;
result += interpret(body) + "\n";
}
return result.trim();
}
const ifElseRegex = /^if\s*\((.+?)\)\s*\{([\s\S]+?)\}(?:\s*elsif\s*\((.+?)\)\s*\{([\s\S]+?)\})?(?:\s*else\s*\{([\s\S]+?)\})?$/;
const ifElseMatch = command.match(ifElseRegex);
if (ifElseMatch) {
const [, cond1, body1, cond2, body2, body3] = ifElseMatch;
if (evaluateExpression(cond1)) return interpret(body1.trim());
if (cond2 && evaluateExpression(cond2)) return interpret(body2.trim());
if (body3) return interpret(body3.trim());
return "If condition was false.";
}
return null;
}
function interpret(command) {
const preprocessed = preprocessCommand(command);
if (preprocessed !== null) return preprocessed;
command = command.trim();
if (/^let\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$/.test(command)) {
const [, name, value] = command.match(/^let\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$/);
vars[name] = evaluateExpression(value);
return `${name} = ${vars[name]}`;
}
if (/^def\s+([a-zA-Z_][a-zA-Z0-9_]*)\(([^)]*)\)\s*\{([\s\S]*)\}$/.test(command)) {
const [, name, args, body] = command.match(/^def\s+([a-zA-Z_][a-zA-Z0-9_]*)\(([^)]*)\)\s*\{([\s\S]*)\}$/);
functions[name] = {
args: args.split(",").map((s) => s.trim()).filter((s) => s),
body: body.trim(),
};
return `Function ${name} defined.`;
}
if (/^([a-zA-Z_][a-zA-Z0-9_]*)\((.*)\)$/.test(command)) {
const [, name, argstr] = command.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\((.*)\)$/);
if (!functions[name]) throw new Error("Undefined function: " + name);
const func = functions[name];
const argValues = argstr.split(",").map((s) => evaluateExpression(s.trim()));
const localVars = {};
func.args.forEach((arg, i) => (localVars[arg] = argValues[i]));
const prevVars = Object.assign({}, vars);
Object.assign(vars, localVars);
let result;
try {
result = interpret(func.body);
} finally {
Object.assign(vars, prevVars);
}
return result;
}
let loopMatch = command.match(/^for\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+?)\s+to\s+(.+?)\s*\{([\s\S]+)\}$/);
if (loopMatch) {
const vname = loopMatch[1];
const start = evaluateExpression(loopMatch[2]);
const end = evaluateExpression(loopMatch[3]);
const body = loopMatch[4];
for (let i = start; i <= end; i++) {
vars[vname] = i;
try {
interpret(body.trim());
} catch (e) {
if (e instanceof BreakException) break;
if (e instanceof ContinueException) continue;
throw e;
}
}
return `Looped ${vname} from ${start} to ${end}`;
}
let whileMatch = command.match(/^while\s*\((.+?)\)\s*\{([\s\S]+)\}$/);
if (whileMatch) {
const condition = whileMatch[1];
const body = whileMatch[2];
while (evaluateExpression(condition)) {
try {
interpret(body.trim());
} catch (e) {
if (e instanceof BreakException) break;
if (e instanceof ContinueException) continue;
throw e;
}
}
return "While loop executed.";
}
if (command === "break") throw new BreakException();
if (command === "continue") throw new ContinueException();
return evaluateExpression(command);
}
const input = document.getElementById("input");
const output = document.getElementById("output");
input.addEventListener("keydown", function (e) {
if (e.key === "Enter") {
const command = input.value.trim();
if (command === "") return;
history.push(command);
const historyItem = document.createElement("div");
historyItem.className = "history-item";
historyItem.textContent = command;
historyItem.onclick = () => {
input.value = command;
input.focus();
};
output.appendChild(historyItem);
try {
const result = interpret(command);
const resultDiv = document.createElement("div");
resultDiv.textContent = `> ${result}`;
output.appendChild(resultDiv);
} catch (err) {
const errorDiv = document.createElement("div");
errorDiv.textContent = `! ${err.message}`;
errorDiv.style.color = "red";
output.appendChild(errorDiv);
}
input.value = "";
}
});
document.getElementById("compileButton").addEventListener("click", () => {
output.appendChild(document.createElement("hr"));
for (let i = 0; i < history.length; i++) {
const command = history[i];
const lineHeader = `Line ${i + 1}: `;
const historyLine = document.createElement("div");
historyLine.className = "history-item";
historyLine.textContent = lineHeader + command;
output.appendChild(historyLine);
try {
const result = interpret(command);
const resultDiv = document.createElement("div");
resultDiv.textContent = `> ${result}`;
output.appendChild(resultDiv);
} catch (err) {
const errorDiv = document.createElement("div");
errorDiv.textContent = `! ${err.message}`;
errorDiv.style.color = "red";
output.appendChild(errorDiv);
}
}
});
</script>
<footer>
<div id="logo">
<a href="https://github.com/Uchida16104/Lumos-Language"><img class="github" src="https://icon2.cleanpng.com/20180530/ywr/kisspng-github-computer-icons-directory-5b0ec64b102792.7107546015276949230662.jpg" /></a>
<a href="https://www.npmjs.com/package/lumos-language"><img class="npm" src="https://icon2.cleanpng.com/20180618/hxa/aa6nx3pxr.webp" /></a>
</div>
<h5>2025 © Hirotoshi Uchida</h5>
</footer>
</body>
</html>