strip much of complexity from evaluator
This commit is contained in:
@ -24,7 +24,6 @@ import { InputOptions } from "../Classes/ZPlayer";
|
|||||||
import { type ShapeObject } from "../API/DOM/Canvas";
|
import { type ShapeObject } from "../API/DOM/Canvas";
|
||||||
import { nearScales } from "zifferjs";
|
import { nearScales } from "zifferjs";
|
||||||
import { MidiConnection } from "../IO/MidiConnection";
|
import { MidiConnection } from "../IO/MidiConnection";
|
||||||
import { evaluateOnce } from "../Evaluator";
|
|
||||||
import { DrunkWalk } from "../Utils/Drunk";
|
import { DrunkWalk } from "../Utils/Drunk";
|
||||||
import { Editor } from "../main";
|
import { Editor } from "../main";
|
||||||
import { LRUCache } from "lru-cache";
|
import { LRUCache } from "lru-cache";
|
||||||
@ -42,6 +41,7 @@ import {
|
|||||||
} from "superdough";
|
} from "superdough";
|
||||||
import { getScaleNotes } from "zifferjs";
|
import { getScaleNotes } from "zifferjs";
|
||||||
import drums from "../tidal-drum-machines.json";
|
import drums from "../tidal-drum-machines.json";
|
||||||
|
import { updatePlayPauseIcon } from '../DOM/UILogic';
|
||||||
|
|
||||||
export async function loadSamples() {
|
export async function loadSamples() {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
@ -395,7 +395,7 @@ export class UserAPI {
|
|||||||
this.almostAlways = Probability.almostAlways(this);
|
this.almostAlways = Probability.almostAlways(this);
|
||||||
this.always = Probability.always();
|
this.always = Probability.always();
|
||||||
this.dice = Probability.dice(this);
|
this.dice = Probability.dice(this);
|
||||||
this.osc = OSC.osc();
|
this.osc = OSC.osc(this.app);
|
||||||
this.getOSC = OSC.getOSC();
|
this.getOSC = OSC.getOSC();
|
||||||
this.gif = Canvas.gif(this.app);
|
this.gif = Canvas.gif(this.app);
|
||||||
this.scope = Canvas.scope(this.app);
|
this.scope = Canvas.scope(this.app);
|
||||||
@ -479,9 +479,8 @@ export class UserAPI {
|
|||||||
this.patternCache.clear();
|
this.patternCache.clear();
|
||||||
if (this.app.isPlaying) {
|
if (this.app.isPlaying) {
|
||||||
} else {
|
} else {
|
||||||
this.app.setButtonHighlighting("play", true);
|
this.app.clock.resume();
|
||||||
this.app.isPlaying = !this.app.isPlaying;
|
updatePlayPauseIcon(this.app, "play");
|
||||||
this.app.clock.start();
|
|
||||||
this.app.api.MidiConnection.sendStartMessage();
|
this.app.api.MidiConnection.sendStartMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -59,6 +59,14 @@ export const beat = (app: Editor) => (n: number | number[] = 1, nudge: number =
|
|||||||
return results.some((value) => value === true);
|
return results.some((value) => value === true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// export const beat = (app: Editor) => (n: number | number[] = 1, nudge: number = 0): boolean => {
|
||||||
|
// const nArray = !Array.isArray(n) ? [n] : n;
|
||||||
|
// return nArray.some(
|
||||||
|
// (value) =>
|
||||||
|
// !((app.clock.time_position.grain - nudge * app.clock.time_position.ppqn) % Math.floor(value * app.clock.time_position.ppqn))
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
|
||||||
export const bar = (app: Editor) => (n: number | number[] = 1, nudge: number = 0): boolean => {
|
export const bar = (app: Editor) => (n: number | number[] = 1, nudge: number = 0): boolean => {
|
||||||
const nArray = Array.isArray(n) ? n : [n];
|
const nArray = Array.isArray(n) ? n : [n];
|
||||||
const barLength = app.clock.time_position.num * app.clock.ppqn;
|
const barLength = app.clock.time_position.num * app.clock.ppqn;
|
||||||
|
|||||||
@ -44,10 +44,10 @@ export const installWindowBehaviors = (
|
|||||||
window.addEventListener("resize", () =>
|
window.addEventListener("resize", () =>
|
||||||
handleResize(app.interface.scope as HTMLCanvasElement),
|
handleResize(app.interface.scope as HTMLCanvasElement),
|
||||||
);
|
);
|
||||||
window.addEventListener("beforeunload", (event) => {
|
// window.addEventListener("beforeunload", (event) => {
|
||||||
event.preventDefault();
|
// event.preventDefault();
|
||||||
saveBeforeExit(app);
|
// saveBeforeExit(app);
|
||||||
});
|
// });
|
||||||
window.addEventListener("visibilitychange", (event) => {
|
window.addEventListener("visibilitychange", (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
saveState(app);
|
saveState(app);
|
||||||
|
|||||||
106
src/Evaluator.ts
106
src/Evaluator.ts
@ -1,112 +1,32 @@
|
|||||||
import type { Editor } from "./main";
|
import type { Editor } from "./main";
|
||||||
import type { File } from "./Editor/FileManagement";
|
import type { File } from "./Editor/FileManagement";
|
||||||
|
|
||||||
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
|
||||||
const codeReplace = (code: string): string => code.replace(/->|::/g, "&&");
|
|
||||||
const cache = new Map<string, Function>();
|
|
||||||
const MAX_CACHE_SIZE = 40;
|
|
||||||
|
|
||||||
async function tryCatchWrapper(application: Editor, code: string): Promise<boolean> {
|
async function tryCatchWrapper(application: Editor, code: string): Promise<boolean> {
|
||||||
/**
|
|
||||||
* Wraps the provided code in a try-catch block and executes it.
|
|
||||||
*
|
|
||||||
* @param application - The editor application.
|
|
||||||
* @param code - The code to be executed.
|
|
||||||
* @returns A promise that resolves to a boolean indicating whether the code executed successfully or not.
|
|
||||||
*/
|
|
||||||
try {
|
try {
|
||||||
await new Function(`"use strict"; ${codeReplace(code)}`).call(application.api);
|
await new Function(`"use strict"; ${code}`).call(application.api);
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
application.interface["error_line"].innerHTML = error as string;
|
console.error(error);
|
||||||
application.api._reportError(error as string);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const addFunctionToCache = (code: string, fn: Function) => {
|
export async function tryEvaluate(application: Editor, code: File): Promise<void> {
|
||||||
/**
|
|
||||||
* Adds a function to the cache.
|
|
||||||
* @param code - The code associated with the function.
|
|
||||||
* @param fn - The function to be added to the cache.
|
|
||||||
*/
|
|
||||||
if (cache.size >= MAX_CACHE_SIZE) {
|
|
||||||
cache.delete(cache.keys().next().value);
|
|
||||||
}
|
|
||||||
cache.set(code, fn);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export async function tryEvaluate(application: Editor, code: File, timeout = 5000): Promise<void> {
|
|
||||||
/**
|
|
||||||
* Tries to evaluate the provided code within a specified timeout period.
|
|
||||||
* Increments the evaluation count of the code file.
|
|
||||||
* If the code is valid, updates the committed code and adds the evaluated function to the cache.
|
|
||||||
* If the code is invalid, retries the evaluation.
|
|
||||||
* @param application - The editor application.
|
|
||||||
* @param code - The code file to evaluate.
|
|
||||||
* @param timeout - The timeout period in milliseconds (default: 5000).
|
|
||||||
* @returns A Promise that resolves when the evaluation is complete.
|
|
||||||
*/
|
|
||||||
code.evaluations!++;
|
|
||||||
|
|
||||||
const cachedFunction = cache.get(code.candidate);
|
|
||||||
if (cachedFunction) {
|
|
||||||
cachedFunction.call(application.api);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const wrappedCode = `let i = ${code.evaluations}; ${code.candidate}`;
|
const wrappedCode = `let i = ${code.evaluations}; ${code.candidate}`;
|
||||||
const isCodeValid = await Promise.race([
|
const isCodeValid = await tryCatchWrapper(application, wrappedCode);
|
||||||
tryCatchWrapper(application, wrappedCode),
|
|
||||||
delay(timeout)
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (isCodeValid) {
|
if (isCodeValid) {
|
||||||
code.committed = code.candidate;
|
code.committed = code.candidate;
|
||||||
const newFunction = new Function(`"use strict"; ${codeReplace(wrappedCode)}`);
|
|
||||||
addFunctionToCache(code.candidate, newFunction);
|
|
||||||
} else {
|
} else {
|
||||||
application.api.logOnce("Compilation error!");
|
console.error("Compilation error!");
|
||||||
await delay(100);
|
|
||||||
await tryEvaluate(application, code, timeout);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function evaluate(application: Editor, code: File, timeout = 1000): Promise<void> {
|
export async function evaluateOnce(application: Editor, code: File): Promise<void> {
|
||||||
/**
|
const wrappedCode = `let i = ${code.evaluations}; ${code.candidate}`;
|
||||||
* Evaluates the given code using the provided application and timeout.
|
const isCodeValid = await tryCatchWrapper(application, wrappedCode);
|
||||||
* @param application The editor application.
|
if (isCodeValid) {
|
||||||
* @param code The code file to evaluate.
|
code.committed = code.candidate;
|
||||||
* @param timeout The timeout value in milliseconds (default: 1000).
|
} else {
|
||||||
* @returns A Promise that resolves when the evaluation is complete.
|
console.error("Compilation error!");
|
||||||
*/
|
|
||||||
try {
|
|
||||||
await Promise.race([
|
|
||||||
tryCatchWrapper(application, code.committed as string),
|
|
||||||
delay(timeout)
|
|
||||||
]);
|
|
||||||
code.evaluations!++;
|
|
||||||
} catch (error) {
|
|
||||||
application.interface["error_line"].innerHTML = error as string;
|
|
||||||
console.error(error);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const evaluateOnce = async (
|
|
||||||
application: Editor,
|
|
||||||
code: string,
|
|
||||||
): Promise<void> => {
|
|
||||||
/**
|
|
||||||
* Evaluates the code once without any caching or error-handling mechanisms besides the tryCatchWrapper.
|
|
||||||
*
|
|
||||||
* @param application - The application object that contains the Editor API.
|
|
||||||
* @param code - The code to be evaluated.
|
|
||||||
* @returns A promise that resolves when the code has been evaluated.
|
|
||||||
*/
|
|
||||||
await tryCatchWrapper(application, code);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user