From 6617491263c5cb5c54c8166abca913057149911d Mon Sep 17 00:00:00 2001 From: Jeremy Walker Date: Mon, 10 Feb 2025 15:25:53 +0000 Subject: [PATCH] WIP carnage --- .../execProjectTest.ts | 20 ++++- .../components/bootcamp/types/Tasks.d.ts | 5 +- app/javascript/interpreter/interpreter.ts | 73 ++++++++++++++----- 3 files changed, 76 insertions(+), 22 deletions(-) diff --git a/app/javascript/components/bootcamp/SolveExercisePage/test-runner/generateAndRunTestSuite/execProjectTest.ts b/app/javascript/components/bootcamp/SolveExercisePage/test-runner/generateAndRunTestSuite/execProjectTest.ts index 1cba3139a8..2201c5aabb 100644 --- a/app/javascript/components/bootcamp/SolveExercisePage/test-runner/generateAndRunTestSuite/execProjectTest.ts +++ b/app/javascript/components/bootcamp/SolveExercisePage/test-runner/generateAndRunTestSuite/execProjectTest.ts @@ -1,10 +1,16 @@ -import { evaluateFunction, interpret } from '@/interpreter/interpreter' +import { + evaluateFunction, + evaluateFunctions, + interpret, + Interpreter, +} from '@/interpreter/interpreter' import { type Project } from '@/components/bootcamp/SolveExercisePage/utils/exerciseMap' import type { Exercise } from '../../exercises/Exercise' import { AnimationTimeline } from '../../AnimationTimeline/AnimationTimeline' import { generateExpects } from './generateExpects' import { TestRunnerOptions } from '@/components/bootcamp/types/TestRunner' import { filteredStdLibFunctions } from '@/interpreter/stdlib' +import { fn } from 'jquery' /** This is of type TestCallback @@ -42,7 +48,16 @@ export function execProjectTest( }) let evaluated - if (testData.function) { + if (testData.functions) { + const interpreter = new Interpreter(options.studentCode, context) + interpreter.compile() + evaluated = interpreter.execute() + + const functions = testData.functions.map((fnData) => { + return { name: fnData[0], args: fnData[1] || [] } + }) + evaluated = evaluateFunctions(options.studentCode, context, functions) + } else if (testData.function) { evaluated = evaluateFunction( options.studentCode, context, @@ -54,6 +69,7 @@ export function execProjectTest( } const { frames } = evaluated + console.log('frames!', frames) const { animations } = exercise const animationTimeline = buildAnimationTimeline(exercise, frames, animations) diff --git a/app/javascript/components/bootcamp/types/Tasks.d.ts b/app/javascript/components/bootcamp/types/Tasks.d.ts index 5c305a83f8..e27c6a79f1 100644 --- a/app/javascript/components/bootcamp/types/Tasks.d.ts +++ b/app/javascript/components/bootcamp/types/Tasks.d.ts @@ -13,8 +13,9 @@ declare type TaskTest = { name: string slug: string data: any - function: string - params: string[] + function?: string + functions?: SetupFunction[] + params?: string[] imageSlug?: string matcher?: string expected?: string diff --git a/app/javascript/interpreter/interpreter.ts b/app/javascript/interpreter/interpreter.ts index 90ffec408c..60e1fcfade 100644 --- a/app/javascript/interpreter/interpreter.ts +++ b/app/javascript/interpreter/interpreter.ts @@ -19,6 +19,11 @@ export interface SomethingWithLocation { location: Location } +type FunctionCall = { + name: string + args: any[] +} + export type Toggle = 'ON' | 'OFF' export type LanguageFeatures = { @@ -93,6 +98,15 @@ export function evaluateFunction( interpreter.compile() return interpreter.evaluateFunction(functionCall, ...args) } +export function evaluateFunctions( + sourceCode: string, + context: EvaluationContext = {}, + functions: FunctionCall[] +): EvaluateFunctionResult { + const interpreter = new Interpreter(sourceCode, context) + interpreter.compile() + return interpreter.evaluateFunctions(functions) +} export class Interpreter { private readonly parser: Parser @@ -153,30 +167,53 @@ export class Interpreter { name: string, ...args: any[] ): EvaluateFunctionResult { - const callingCode = `${name}(${args - .map((arg) => JSON.stringify(arg)) - .join(', ')})` - - // Create a new parser with wrapTopLevelStatements set to false - // and use it to generate the calling statements. - const callingStatements = new Parser( - this.externalFunctions.map((f) => f.name), - this.languageFeatures, - false - ).parse(callingCode) - - if (callingStatements.length !== 1) - this.error('CouldNotEvaluateFunction', Location.unknown, { - callingStatements, - }) + return this.evaluateFunctions([{ name, args }]) + } + public evaluateFunctions(functions: FunctionCall[]): EvaluateFunctionResult { const executor = new Executor( this.sourceCode, this.languageFeatures, this.externalFunctions ) - executor.execute(this.statements) - return executor.evaluateSingleExpression(callingStatements[0]) + + let result: EvaluateFunctionResult = { + value: undefined, + frames: [], + error: null, + callExpressions: [], + } + + functions.forEach((fn) => { + const callingCode = `${fn.name}(${fn.args + .map((arg) => JSON.stringify(arg)) + .join(', ')})` + + // Create a new parser with wrapTopLevelStatements set to false + // and use it to generate the calling statements. + const callingStatements = new Parser( + this.externalFunctions.map((f) => f.name), + this.languageFeatures, + false + ).parse(callingCode) + + if (callingStatements.length !== 1) + this.error('CouldNotEvaluateFunction', Location.unknown, { + callingStatements, + }) + + executor.execute(this.statements) + const evalResult = executor.evaluateSingleExpression(callingStatements[0]) + result = { + value: evalResult.value, + frames: result.frames.concat(evalResult.frames), + error: evalResult.error, + } + if (evalResult.error) { + return result + } + }) + return result } private error(