mirror of
https://github.com/voltrevo/ValueScript.git
synced 2026-04-18 03:00:27 -04:00
Update website
This commit is contained in:
@@ -221,6 +221,52 @@ const files: Record<string, string | nil> = {
|
||||
return res;
|
||||
}
|
||||
`),
|
||||
|
||||
"examples/idGenerationError.ts": blockTrim(`
|
||||
export default function main() {
|
||||
let nextId = 1;
|
||||
|
||||
function generateId() {
|
||||
const result = nextId;
|
||||
nextId++;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return [
|
||||
generateId(),
|
||||
generateId(),
|
||||
generateId(),
|
||||
];
|
||||
}
|
||||
`),
|
||||
|
||||
"examples/idGeneration.ts": blockTrim(`
|
||||
export default function main() {
|
||||
let idGen = new IdGenerator();
|
||||
|
||||
return [
|
||||
idGen.generate(),
|
||||
idGen.generate(),
|
||||
idGen.generate(),
|
||||
];
|
||||
}
|
||||
|
||||
class IdGenerator {
|
||||
nextId: number;
|
||||
|
||||
constructor() {
|
||||
this.nextId = 1;
|
||||
}
|
||||
|
||||
generate() {
|
||||
const result = this.nextId;
|
||||
this.nextId++;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
`),
|
||||
};
|
||||
|
||||
export default files;
|
||||
|
||||
@@ -22,25 +22,8 @@
|
||||
<div class="display-title">Outcome</div>
|
||||
<div id="outcome">"The playground is loading"</div>
|
||||
|
||||
<!--
|
||||
<div class="display-title">Stats</div>
|
||||
<div id="stats"><div class="table-wrap"><table
|
||||
>
|
||||
<tr>
|
||||
<td>Steps</td><td></td><td id="steps"></td><td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Step Limit</td><td class="clickable" id="steps-dec">-</td><td id="stepLimit">100000</td><td class="clickable" id="steps-inc">+</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Characters</td><td></td><td id="chars"></td><td></td>
|
||||
</tr>
|
||||
</table
|
||||
></div></div>
|
||||
|
||||
<div class="display-title">Notes</div>
|
||||
<div id="notes"></div>
|
||||
-->
|
||||
<div class="display-title">Diagnostics</div>
|
||||
<div id="diagnostics"></div>
|
||||
|
||||
<div class="display-title">Assembly</div>
|
||||
<div id="vsm">@main = function() {
|
||||
|
||||
@@ -4,7 +4,12 @@ import files from "./files.ts";
|
||||
import assert from "./helpers/assert.ts";
|
||||
import nil from "./helpers/nil.ts";
|
||||
import notNil from "./helpers/notNil.ts";
|
||||
import VslibPool, { Job } from "./vslib/VslibPool.ts";
|
||||
import VslibPool, {
|
||||
CompilerOutput,
|
||||
Diagnostic,
|
||||
Job,
|
||||
RunResult,
|
||||
} from "./vslib/VslibPool.ts";
|
||||
|
||||
function domQuery<T = HTMLElement>(query: string): T {
|
||||
return <T> <unknown> notNil(document.querySelector(query) ?? nil);
|
||||
@@ -17,6 +22,7 @@ const filePreviousEl = domQuery("#file-previous");
|
||||
const fileNextEl = domQuery("#file-next");
|
||||
const outcomeEl = domQuery("#outcome");
|
||||
const vsmEl = domQuery("#vsm");
|
||||
const diagnosticsEl = domQuery("#diagnostics");
|
||||
|
||||
for (const filename of Object.keys(files)) {
|
||||
const option = document.createElement("option");
|
||||
@@ -109,8 +115,8 @@ editorEl.innerHTML = "";
|
||||
timerId = setTimeout(handleUpdate, 100);
|
||||
});
|
||||
|
||||
let compileJob: Job<string> | nil = nil;
|
||||
let runJob: Job<string> | nil = nil;
|
||||
let compileJob: Job<CompilerOutput> | nil = nil;
|
||||
let runJob: Job<RunResult> | nil = nil;
|
||||
let updateId = 0;
|
||||
|
||||
function handleUpdate() {
|
||||
@@ -124,10 +130,70 @@ editorEl.innerHTML = "";
|
||||
compileJob = vslibPool.compile(source);
|
||||
runJob = vslibPool.run(source);
|
||||
|
||||
renderJob(compileJob, vsmEl);
|
||||
renderJob(runJob, outcomeEl);
|
||||
renderJob(
|
||||
compileJob,
|
||||
vsmEl,
|
||||
(el, compilerOutput) => {
|
||||
el.textContent = compilerOutput.assembly.join("\n");
|
||||
},
|
||||
);
|
||||
|
||||
function renderJob(job: Job<string>, el: HTMLElement) {
|
||||
renderJob(
|
||||
runJob,
|
||||
outcomeEl,
|
||||
(el, runResult) => {
|
||||
if ("Ok" in runResult.output) {
|
||||
el.textContent = runResult.output.Ok;
|
||||
} else if ("Err" in runResult.output) {
|
||||
el.textContent = runResult.output.Err;
|
||||
} else {
|
||||
never(runResult.output);
|
||||
}
|
||||
|
||||
diagnosticsEl.innerHTML = "";
|
||||
|
||||
for (const diagnostic of runResult.diagnostics) {
|
||||
const diagnosticEl = document.createElement("div");
|
||||
|
||||
diagnosticEl.classList.add(
|
||||
"diagnostic",
|
||||
toKebabCase(diagnostic.level),
|
||||
);
|
||||
|
||||
const { line, col } = toLineCol(source, diagnostic.span.start);
|
||||
diagnosticEl.textContent = `${line}:${col}: ${diagnostic.message}`;
|
||||
|
||||
diagnosticsEl.appendChild(diagnosticEl);
|
||||
}
|
||||
|
||||
monaco.editor.setModelMarkers(
|
||||
model,
|
||||
"valuescript",
|
||||
runResult.diagnostics.map((diagnostic) => {
|
||||
const { line, col } = toLineCol(source, diagnostic.span.start);
|
||||
const { line: endLine, col: endCol } = toLineCol(
|
||||
source,
|
||||
diagnostic.span.end,
|
||||
);
|
||||
|
||||
return {
|
||||
severity: toMonacoSeverity(diagnostic.level),
|
||||
startLineNumber: line,
|
||||
startColumn: col,
|
||||
endLineNumber: endLine,
|
||||
endColumn: endCol,
|
||||
message: diagnostic.message,
|
||||
};
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
function renderJob<T>(
|
||||
job: Job<T>,
|
||||
el: HTMLElement,
|
||||
apply: (el: HTMLElement, jobResult: T) => void,
|
||||
) {
|
||||
const startTime = Date.now();
|
||||
|
||||
const loadingInterval = setInterval(() => {
|
||||
@@ -140,7 +206,7 @@ editorEl.innerHTML = "";
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
el.textContent = await job.wait();
|
||||
apply(el, await job.wait());
|
||||
el.classList.remove("error");
|
||||
} catch (err) {
|
||||
if (!(err instanceof Error)) {
|
||||
@@ -158,4 +224,34 @@ editorEl.innerHTML = "";
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
function toMonacoSeverity(level: Diagnostic["level"]): any {
|
||||
switch (level) {
|
||||
case "Error":
|
||||
return monaco.MarkerSeverity.Error;
|
||||
case "InternalError":
|
||||
return monaco.MarkerSeverity.Error;
|
||||
case "Lint":
|
||||
return monaco.MarkerSeverity.Warning;
|
||||
case "CompilerDebug":
|
||||
return monaco.MarkerSeverity.Info;
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
function never(_: never): never {
|
||||
throw new Error("This should not happen");
|
||||
}
|
||||
|
||||
function toKebabCase(str: string): string {
|
||||
// account for leading capital letters
|
||||
str = str.replace(/^[A-Z]/, (match) => match.toLowerCase());
|
||||
|
||||
return str.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
||||
}
|
||||
|
||||
function toLineCol(str: string, index: number): { line: number; col: number } {
|
||||
const lines = str.slice(0, index).split("\n");
|
||||
|
||||
return { line: lines.length, col: lines[lines.length - 1].length + 1 };
|
||||
}
|
||||
|
||||
@@ -96,41 +96,41 @@ a, a:visited {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
#display #notes {
|
||||
#display #diagnostics {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#display .note {
|
||||
#display .diagnostic {
|
||||
padding: 0.5em 1.5em;
|
||||
}
|
||||
|
||||
#notes > .note:not(:first-child) {
|
||||
#diagnostics > .diagnostic:not(:first-child) {
|
||||
border-top: 1px solid black;
|
||||
}
|
||||
|
||||
.note.info {
|
||||
.diagnostic.info {
|
||||
background-color: hsla(240, 100%, 50%, 0.1);
|
||||
}
|
||||
|
||||
.note.warn {
|
||||
.diagnostic.warn, .diagnostic.lint {
|
||||
background-color: hsla(30, 100%, 50%, 0.1);
|
||||
}
|
||||
|
||||
.note.error {
|
||||
.diagnostic.error {
|
||||
background-color: hsla(0, 100%, 50%, 0.1);
|
||||
}
|
||||
|
||||
#display .note .note {
|
||||
#display .diagnostic .diagnostic {
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
#display .note .note:first-child {
|
||||
#display .diagnostic .diagnostic:first-child {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
#display .note .note:not(:first-child) {
|
||||
#display .diagnostic .diagnostic:not(:first-child) {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,20 +36,49 @@ const workerUrl = URL.createObjectURL(
|
||||
new Blob([workerScript], { type: "application/javascript" }),
|
||||
);
|
||||
|
||||
export type Diagnostic = {
|
||||
level: "Lint" | "Error" | "InternalError" | "CompilerDebug";
|
||||
message: string;
|
||||
span: {
|
||||
start: number;
|
||||
end: number;
|
||||
ctxt: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type CompilerOutput = {
|
||||
diagnostics: Diagnostic[];
|
||||
assembly: string[];
|
||||
};
|
||||
|
||||
export type RunResult = {
|
||||
diagnostics: Diagnostic[];
|
||||
output:
|
||||
| { Ok: string }
|
||||
| { Err: string };
|
||||
};
|
||||
|
||||
export type Job<T> = {
|
||||
wait: () => Promise<T>;
|
||||
cancel: () => void;
|
||||
};
|
||||
|
||||
export function mapJob<U, V>(job: Job<U>, f: (x: U) => V): Job<V> {
|
||||
return {
|
||||
wait: () => job.wait().then(f),
|
||||
cancel: job.cancel,
|
||||
};
|
||||
}
|
||||
|
||||
export default class VslibPool {
|
||||
#pool = new valuescript.WorkerPool(workerUrl);
|
||||
|
||||
run(source: string) {
|
||||
return this.#Job("run", [source]) as Job<string>;
|
||||
return this.#Job("run", [source]) as Job<RunResult>;
|
||||
}
|
||||
|
||||
compile(source: string) {
|
||||
return this.#Job("compile", [source]) as Job<string>;
|
||||
return this.#Job("compile", [source]) as Job<CompilerOutput>;
|
||||
}
|
||||
|
||||
#Job(method: string, args: unknown[]) {
|
||||
@@ -70,7 +99,7 @@ export default class VslibPool {
|
||||
|
||||
worker.onmessage = (evt) => {
|
||||
if ("ok" in evt.data) {
|
||||
resolve(evt.data.ok);
|
||||
resolve(JSON.parse(evt.data.ok));
|
||||
} else if ("err" in evt.data) {
|
||||
if (evt.data.err instanceof Error) {
|
||||
reject(evt.data.err);
|
||||
|
||||
Reference in New Issue
Block a user