@@ -19,23 +19,28 @@ import CseMachine from './CseMachine';
19
19
import { CseAnimation } from './CseMachineAnimation' ;
20
20
import { Config , ShapeDefaultProps } from './CseMachineConfig' ;
21
21
import {
22
- Closure ,
23
22
Data ,
24
23
DataArray ,
25
24
EnvTree ,
26
25
EnvTreeNode ,
27
26
GlobalFn ,
28
- ReferenceType
27
+ NonGlobalFn ,
28
+ ReferenceType ,
29
+ StreamFn
29
30
} from './CseMachineTypes' ;
30
31
import {
31
- convertClosureToGlobalFn ,
32
+ assert ,
32
33
deepCopyTree ,
33
34
getNextChildren ,
35
+ isBuiltInFn ,
34
36
isClosure ,
35
37
isDataArray ,
36
- isFunction ,
38
+ isEnvEqual ,
37
39
isGlobalFn ,
40
+ isNonGlobalFn ,
38
41
isPrimitiveData ,
42
+ isSourceObject ,
43
+ isStreamFn ,
39
44
isUnassigned ,
40
45
setDifference
41
46
} from './CseMachineUtils' ;
@@ -63,8 +68,6 @@ export class Layout {
63
68
/** scale factor for zooming and out of canvas */
64
69
static scaleFactor = 1.02 ;
65
70
66
- /** the environment tree */
67
- static environmentTree : EnvTree ;
68
71
/** the global environment */
69
72
static globalEnvNode : EnvTreeNode ;
70
73
/** grid of frames */
@@ -79,7 +82,7 @@ export class Layout {
79
82
static previousStashComponent : StashStack ;
80
83
81
84
/** memoized values */
82
- static values = new Map < Data , Value > ( ) ;
85
+ static values = new Map < string | ( ( ) => any ) , Value > ( ) ;
83
86
/** memoized layout */
84
87
static prevLayout : React . ReactNode ;
85
88
static currentDark : React . ReactNode ;
@@ -140,8 +143,7 @@ export class Layout {
140
143
Layout . key = 0 ;
141
144
142
145
// deep copy so we don't mutate the context
143
- Layout . environmentTree = deepCopyTree ( envTree ) ;
144
- Layout . globalEnvNode = Layout . environmentTree . root ;
146
+ Layout . globalEnvNode = deepCopyTree ( envTree ) . root ;
145
147
Layout . control = control ;
146
148
Layout . stash = stash ;
147
149
@@ -201,50 +203,50 @@ export class Layout {
201
203
202
204
const preludeEnvNode = Layout . globalEnvNode . children [ 0 ] ;
203
205
const preludeEnv = preludeEnvNode . environment ;
204
- const globalEnvNode = Layout . globalEnvNode ;
205
- const globalEnv = globalEnvNode . environment ;
206
-
207
- const preludeValueKeyMap = new Map (
208
- Object . entries ( preludeEnv . head ) . map ( ( [ key , value ] ) => [ value , key ] )
209
- ) ;
206
+ const globalEnv = Layout . globalEnvNode . environment ;
207
+
208
+ // Add bindings from prelude environment head to global environment head
209
+ for ( const [ key , value ] of Object . entries ( preludeEnv . head ) ) {
210
+ delete preludeEnv . head [ key ] ;
211
+ globalEnv . head [ key ] = value ;
212
+ if ( isStreamFn ( value ) && isEnvEqual ( value . environment , preludeEnv ) ) {
213
+ Object . defineProperty ( value , 'environment' , { value : globalEnv } ) ;
214
+ }
215
+ }
210
216
211
- // Change environments of each array and closure in the prelude to be the global environment
217
+ // Move objects from prelude environment heap to global environment heap
212
218
for ( const value of preludeEnv . heap . getHeap ( ) ) {
213
- Object . defineProperty ( value , 'environment' , { value : globalEnvNode . environment } ) ;
214
- globalEnv . heap . add ( value ) ;
215
- const key = preludeValueKeyMap . get ( value ) ;
216
- if ( key ) {
217
- globalEnv . head [ key ] = value ;
219
+ Object . defineProperty ( value , 'environment' , { value : globalEnv } ) ;
220
+ if ( isDataArray ( value ) ) {
221
+ for ( const item of value ) {
222
+ if ( isStreamFn ( item ) && isEnvEqual ( item . environment , preludeEnv ) ) {
223
+ Object . defineProperty ( item , 'environment' , { value : globalEnv } ) ;
224
+ }
225
+ }
218
226
}
227
+ preludeEnv . heap . move ( value , globalEnv . heap ) ;
219
228
}
220
229
221
230
// update globalEnvNode children
222
- globalEnvNode . resetChildren ( preludeEnvNode . children ) ;
231
+ Layout . globalEnvNode . resetChildren ( preludeEnvNode . children ) ;
223
232
224
233
// update the tail of each child's environment to point to the global environment
225
- globalEnvNode . children . forEach ( node => {
234
+ Layout . globalEnvNode . children . forEach ( node => {
226
235
node . environment . tail = globalEnv ;
227
236
} ) ;
228
-
229
- // go through new bindings and update closures to be global functions
230
- for ( const value of Object . values ( globalEnv . head ) ) {
231
- if ( isClosure ( value ) ) {
232
- convertClosureToGlobalFn ( value ) ;
233
- }
234
- }
235
237
}
236
238
237
239
/** remove any global functions not referenced elsewhere in the program */
238
240
private static removeUnreferencedGlobalFns ( ) : void {
239
- const referencedGlobalFns = new Set < GlobalFn > ( ) ;
241
+ const referencedFns = new Set < GlobalFn | NonGlobalFn > ( ) ;
240
242
const visitedData = new Set < DataArray > ( ) ;
241
243
242
244
const findGlobalFnReferences = ( envNode : EnvTreeNode ) : void => {
243
245
const headValues = Object . values ( envNode . environment . head ) ;
244
246
const unreferenced = setDifference ( envNode . environment . heap . getHeap ( ) , new Set ( headValues ) ) ;
245
247
for ( const data of headValues ) {
246
248
if ( isGlobalFn ( data ) ) {
247
- referencedGlobalFns . add ( data ) ;
249
+ referencedFns . add ( data ) ;
248
250
} else if ( isDataArray ( data ) ) {
249
251
findGlobalFnReferencesInData ( data ) ;
250
252
}
@@ -263,7 +265,7 @@ export class Layout {
263
265
visitedData . add ( data ) ;
264
266
data . forEach ( d => {
265
267
if ( isGlobalFn ( d ) ) {
266
- referencedGlobalFns . add ( d ) ;
268
+ referencedFns . add ( d ) ;
267
269
} else if ( isDataArray ( d ) ) {
268
270
findGlobalFnReferencesInData ( d ) ;
269
271
}
@@ -273,15 +275,18 @@ export class Layout {
273
275
// First, add any referenced global functions in the stash
274
276
for ( const item of Layout . stash . getStack ( ) ) {
275
277
if ( isGlobalFn ( item ) ) {
276
- referencedGlobalFns . add ( item ) ;
278
+ referencedFns . add ( item ) ;
277
279
} else if ( isDataArray ( item ) ) {
278
280
findGlobalFnReferencesInData ( item ) ;
279
281
}
280
282
}
281
283
282
- // Then, find any references within any arrays inside the global environment heap
284
+ // Then, find any references within any arrays inside the global environment heap,
285
+ // and also add any non-global functions created in the global frame
283
286
for ( const data of Layout . globalEnvNode . environment . heap . getHeap ( ) ) {
284
- if ( isDataArray ( data ) ) {
287
+ if ( isNonGlobalFn ( data ) ) {
288
+ referencedFns . add ( data ) ;
289
+ } else if ( isDataArray ( data ) ) {
285
290
findGlobalFnReferencesInData ( data ) ;
286
291
}
287
292
}
@@ -293,13 +298,12 @@ export class Layout {
293
298
Object . entries ( Layout . globalEnvNode . environment . head ) . map ( ( [ key , value ] ) => [ value , key ] )
294
299
) ;
295
300
301
+ let i = 0 ;
296
302
const newHead = { } ;
297
303
const newHeap = new Heap ( ) ;
298
- for ( const fn of referencedGlobalFns ) {
299
- newHead [ functionNames . get ( fn ) ! ] = fn ;
300
- if ( fn . hasOwnProperty ( 'environment' ) ) {
301
- newHeap . add ( fn as Closure ) ;
302
- }
304
+ for ( const fn of referencedFns ) {
305
+ if ( isClosure ( fn ) ) newHeap . add ( fn ) ;
306
+ if ( isGlobalFn ( fn ) ) newHead [ functionNames . get ( fn ) ?? `${ i ++ } ` ] = fn ;
303
307
}
304
308
305
309
// add any arrays from the original heap to the new heap
@@ -349,50 +353,44 @@ export class Layout {
349
353
}
350
354
}
351
355
352
- /** memoize `Value` (used to detect circular references in non-primitive `Value`) */
353
- static memoizeValue ( value : Value ) : void {
354
- Layout . values . set ( value . data , value ) ;
355
- }
356
-
357
- /** create an instance of the corresponding `Value` if it doesn't already exists,
358
- * else, return the existing value */
356
+ /** Creates an instance of the corresponding `Value` if it doesn't already exists,
357
+ * else, returns the existing value */
359
358
static createValue ( data : Data , reference : ReferenceType ) : Value {
360
359
if ( isUnassigned ( data ) ) {
360
+ assert ( reference instanceof Binding ) ;
361
361
return new UnassignedValue ( reference ) ;
362
362
} else if ( isPrimitiveData ( data ) ) {
363
363
return new PrimitiveValue ( data , reference ) ;
364
364
} else {
365
- // try to find if this value is already created
366
- const existingValue = Layout . values . get ( data ) ;
365
+ const existingValue = Layout . values . get (
366
+ isBuiltInFn ( data ) || isStreamFn ( data ) ? data : data . id
367
+ ) ;
367
368
if ( existingValue ) {
368
369
existingValue . addReference ( reference ) ;
369
370
return existingValue ;
370
371
}
371
372
372
- // else create a new one
373
- let newValue : Value = new PrimitiveValue ( null , reference ) ;
373
+ let newValue : Value | undefined ;
374
374
if ( isDataArray ( data ) ) {
375
375
newValue = new ArrayValue ( data , reference ) ;
376
- } else if ( isFunction ( data ) ) {
377
- if ( isClosure ( data ) ) {
378
- // normal JS Slang function
379
- newValue = new FnValue ( data , reference ) ;
380
- } else {
381
- if ( reference instanceof Binding ) {
382
- // function from the global env (has no extra props such as env, fnName)
383
- newValue = new GlobalFnValue ( data , reference ) ;
384
- } else {
385
- // this should be impossible, since bindings for global function always get
386
- // drawn first, before any other values like arrays get drawn
387
- throw new Error ( 'First reference of global function value is not a binding!' ) ;
388
- }
389
- }
376
+ } else if ( isGlobalFn ( data ) ) {
377
+ assert ( reference instanceof Binding ) ;
378
+ newValue = new GlobalFnValue ( data , reference ) ;
379
+ } else if ( isNonGlobalFn ( data ) ) {
380
+ newValue = new FnValue ( data , reference ) ;
381
+ } else if ( isSourceObject ( data ) ) {
382
+ return new PrimitiveValue ( data . toReplString ( ) , reference ) ;
390
383
}
391
384
392
- return newValue ;
385
+ return newValue ?? new PrimitiveValue ( null , reference ) ;
393
386
}
394
387
}
395
388
389
+ static memoizeValue ( data : GlobalFn | NonGlobalFn | StreamFn | DataArray , value : Value ) {
390
+ if ( isBuiltInFn ( data ) || isStreamFn ( data ) ) Layout . values . set ( data , value ) ;
391
+ else Layout . values . set ( data . id , value ) ;
392
+ }
393
+
396
394
/**
397
395
* Scrolls diagram to top left, resets the zoom, and saves the diagram as multiple images of width < MaxExportWidth.
398
396
*/
0 commit comments