@@ -4,7 +4,7 @@ import { Context, interrupt, Result, resume, runFilesInContext } from 'js-slang'
4
4
import { ACORN_PARSE_OPTIONS , TRY_AGAIN } from 'js-slang/dist/constants' ;
5
5
import { InterruptedError } from 'js-slang/dist/errors/errors' ;
6
6
import { manualToggleDebugger } from 'js-slang/dist/stdlib/inspector' ;
7
- import { Chapter , ErrorSeverity , ErrorType , Variant } from 'js-slang/dist/types' ;
7
+ import { Chapter , ErrorSeverity , ErrorType , SourceError , Variant } from 'js-slang/dist/types' ;
8
8
import { SagaIterator } from 'redux-saga' ;
9
9
import { call , put , race , select , take } from 'redux-saga/effects' ;
10
10
import * as Sourceror from 'sourceror' ;
@@ -326,17 +326,31 @@ export function* evalCode(
326
326
) {
327
327
yield * dumpDisplayBuffer ( workspaceLocation , isStoriesBlock , storyEnv ) ;
328
328
if ( ! isStoriesBlock ) {
329
- yield put ( actions . evalInterpreterError ( context . errors , workspaceLocation ) ) ;
330
- // enable the CSE machine visualizer during errors
331
- if ( context . executionMethod === 'cse-machine' && needUpdateCse ) {
332
- yield put ( actions . updateStepsTotal ( context . runtime . envStepsTotal + 1 , workspaceLocation ) ) ;
333
- yield put ( actions . toggleUpdateCse ( false , workspaceLocation as any ) ) ;
334
- yield put (
335
- actions . updateBreakpointSteps ( context . runtime . breakpointSteps , workspaceLocation )
336
- ) ;
337
- yield put (
338
- actions . updateChangePointSteps ( context . runtime . changepointSteps , workspaceLocation )
339
- ) ;
329
+ const specialError = checkSpecialError ( context . errors ) ;
330
+ if ( specialError !== null ) {
331
+ switch ( specialError ) {
332
+ case 'source_academy_interrupt' : {
333
+ yield * handleSourceAcademyInterrupt ( context , entrypointCode , workspaceLocation ) ;
334
+ break ;
335
+ }
336
+ // This should not happen but we check just in case
337
+ default : {
338
+ yield put ( actions . evalInterpreterError ( context . errors , workspaceLocation ) ) ;
339
+ }
340
+ }
341
+ } else {
342
+ yield put ( actions . evalInterpreterError ( context . errors , workspaceLocation ) ) ;
343
+ // enable the CSE machine visualizer during errors
344
+ if ( context . executionMethod === 'cse-machine' && needUpdateCse ) {
345
+ yield put ( actions . updateStepsTotal ( context . runtime . envStepsTotal + 1 , workspaceLocation ) ) ;
346
+ yield put ( actions . toggleUpdateCse ( false , workspaceLocation as any ) ) ;
347
+ yield put (
348
+ actions . updateBreakpointSteps ( context . runtime . breakpointSteps , workspaceLocation )
349
+ ) ;
350
+ yield put (
351
+ actions . updateChangePointSteps ( context . runtime . changepointSteps , workspaceLocation )
352
+ ) ;
353
+ }
340
354
}
341
355
} else {
342
356
// Safe to use ! as storyEnv will be defined from above when we call from EVAL_STORY
@@ -412,3 +426,34 @@ export function* evalCode(
412
426
introIcon && introIcon . classList . remove ( 'side-content-tab-alert-error' ) ;
413
427
}
414
428
}
429
+
430
+ // Special module errors
431
+ const specialErrors = [ 'source_academy_interrupt' ] as const ;
432
+ type SpecialError = ( typeof specialErrors ) [ number ] ;
433
+
434
+ function checkSpecialError ( errors : SourceError [ ] ) : SpecialError | null {
435
+ if ( errors . length !== 1 ) {
436
+ return null ;
437
+ }
438
+ const firstError = errors [ 0 ] as any ;
439
+ if ( typeof firstError . error !== 'string' ) {
440
+ return null ;
441
+ }
442
+ if ( ! specialErrors . includes ( firstError . error ) ) {
443
+ return null ;
444
+ }
445
+
446
+ return firstError . error as SpecialError ;
447
+ }
448
+
449
+ function * handleSourceAcademyInterrupt (
450
+ context : Context ,
451
+ entrypointCode : string ,
452
+ workspaceLocation : WorkspaceLocation
453
+ ) {
454
+ yield put (
455
+ actions . evalInterpreterSuccess ( 'Program has been interrupted by module' , workspaceLocation )
456
+ ) ;
457
+ context . errors = [ ] ;
458
+ yield put ( actions . notifyProgramEvaluated ( null , null , entrypointCode , context , workspaceLocation ) ) ;
459
+ }
0 commit comments