import type { Editor } from "./main"; import type { File } from "./FileManagement"; const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); const codeReplace = (code: string): string => { return code.replace(/->|::/g, "&&"); }; const tryCatchWrapper = async ( application: Editor, code: string, ): Promise => { /** * 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 { await new Function(`"use strict"; ${codeReplace(code)}`).call( application.api, ); return true; } catch (error) { application.interface.error_line.innerHTML = error as string; application.api._reportError(error as string); return false; } }; const cache = new Map(); const MAX_CACHE_SIZE = 40; const addFunctionToCache = (code: string, fn: Function) => { /** * 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 const tryEvaluate = async ( application: Editor, code: File, timeout = 5000, ): Promise => { /** * 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 candidateCode = code.candidate; try { const cachedFunction = cache.get(candidateCode); if (cachedFunction) { cachedFunction.call(application.api); } else { const wrappedCode = `let i = ${code.evaluations}; ${candidateCode}`; const isCodeValid = await Promise.race([ tryCatchWrapper(application, wrappedCode), delay(timeout), ]); if (isCodeValid) { code.committed = code.candidate; const newFunction = new Function( `"use strict"; ${codeReplace(wrappedCode)}`, ); addFunctionToCache(candidateCode, newFunction); } else { await evaluate(application, code, timeout); } } } catch (error) { application.interface.error_line.innerHTML = error as string; application.api._reportError(error as string); } }; export const evaluate = async ( application: Editor, code: File, timeout = 1000, ): Promise => { /** * Evaluates the given code using the provided application and timeout. * @param application The editor application. * @param code The code file to evaluate. * @param timeout The timeout value in milliseconds (default: 1000). * @returns A Promise that resolves when the evaluation is complete. */ try { await Promise.race([ tryCatchWrapper(application, code.committed as string), delay(timeout), ]); if (code.evaluations) code.evaluations++; } catch (error) { application.interface.error_line.innerHTML = error as string; console.log(error); } }; export const evaluateOnce = async ( application: Editor, code: string, ): Promise => { /** * 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); };