@@ -68,17 +68,19 @@ class Env extends Map {
6868 // use inherited set, get for copying references
6969 // insert and retrieve values with setThunk and getValue
7070 // encoding of value is a Generator Object that yields value forever - this is opaque
71- setThunk ( i , thunk ) {
71+ setThunk ( i , val ) {
7272 this . set ( i , function * ( ) {
7373 // console.warn(`expensively calculating ${ i }`);
74- const result = thunk ( ) ;
74+ let result = ( yield val ) ?? val ; // If val is not A or V, then it need not be evaluated
7575 while ( true ) yield result ;
7676 } ( ) ) ;
7777 return this ;
7878 }
79- getValue ( i ) {
79+ // Second argument provides an interface for storing the evaluated term inside
80+ // the generator, for future accesses.
81+ getValue ( i , result ) {
8082 // console.warn(`inexpensively fetching ${ i }`);
81- return this . get ( i ) . next ( ) . value ;
83+ return this . get ( i ) . next ( result ) . value ;
8284 }
8385}
8486
@@ -93,7 +95,7 @@ class Tuple {
9395function Primitive ( v ) { return new Tuple ( new V ( "<primitive>" ) , new Env ( [ [ "<primitive>" , function * ( ) { while ( true ) yield v ; } ( ) ] ] ) ) ; }
9496
9597const primitives = new Env ;
96- primitives . setThunk ( "trace" , ( ) => evalLC ( new Tuple ( Primitive ( function ( v ) { console . log ( String ( v . term ) ) ; return v ; } ) , new Env ) ) ) ;
98+ primitives . setThunk ( "trace" , new Tuple ( Primitive ( function ( v ) { console . log ( String ( v . term ) ) ; return v ; } ) , new Env ) ) ;
9799
98100const Y = new L ( "f" , new A ( new L ( "x" , new A ( new V ( "f" ) , new A ( new V ( "x" ) , new V ( "x" ) ) ) ) , new L ( "x" , new A ( new V ( "f" ) , new A ( new V ( "x" ) , new V ( "x" ) ) ) ) ) ) ;
99101
@@ -299,7 +301,7 @@ function parseWith(cfg={}) {
299301 if ( i === code . length ) {
300302 const [ name , term ] = r ;
301303 const wrapped = wrap ( name , term ) ;
302- return env . setThunk ( name , ( ) => evalLC ( wrapped ) ) ;
304+ return env . setThunk ( name , wrapped ) ;
303305 } else
304306 error ( i , "defn: incomplete parse" ) ;
305307 }
@@ -319,7 +321,7 @@ function compileWith(cfg={}) {
319321 const env = parseWith ( { numEncoding, purity, verbosity} ) ( code ) ;
320322 const r = { } ;
321323 for ( const [ name ] of env )
322- Object . defineProperty ( r , name , { get ( ) { return env . getValue ( name ) ; } , enumerable : true } ) ;
324+ Object . defineProperty ( r , name , { get ( ) { return evalLC ( new Tuple ( new V ( name ) , env ) ) ; } , enumerable : true } ) ;
323325 return r ;
324326 } ;
325327}
@@ -335,13 +337,16 @@ function evalLC(term) {
335337 let argEnv ;
336338 if ( arg . term && arg . env ) ( { term : arg , env : argEnv } = arg ) ; // If callback is passed another callback, or a term
337339 const termVal = new Tuple ( typeof arg !== 'number' ? arg : fromInt ( arg ) , new Env ( argEnv ) ) ;
338- const newEnv = new Env ( env ) . setThunk ( term . name , ( ) => evalLC ( termVal ) ) ;
340+ const newEnv = new Env ( env ) . setThunk ( term . name , termVal ) ;
339341 return runEval ( new Tuple ( term . body , newEnv ) , stack ) ;
340342 }
341343 return Object . assign ( result , { term, env} ) ;
342344 }
343345
344- function runEval ( { term, env} , stack ) { // stack: [[term, isRight]], term: LC term, env = Env { name => term }
346+ // term :: Tuple
347+ // isRight :: bool (indicating whether the term is left or right side of an Application)
348+ // isEvaluated :: bool (indicating whether the current term should be stored in the Env)
349+ function runEval ( { term, env} , stack ) { // stack: [[term, isRight, isEvaluated]], term: LC term, env = Env { name => term }
345350 while ( ! ( term instanceof L ) || stack . length > 0 ) {
346351 if ( term instanceof V )
347352 if ( term . name === "()" )
@@ -350,17 +355,25 @@ function evalLC(term) {
350355 let res = env . getValue ( term . name ) ;
351356 if ( ! res . env )
352357 term = res ;
353- else
358+ else {
359+ if ( res . term instanceof V || res . term instanceof A )
360+ // Push a frame to the stack to indicate when the value should be stored back
361+ stack . push ( [ new Tuple ( term , env ) , false , true ] ) ;
354362 ( { term, env} = res ) ;
363+ }
355364 }
356365 else if ( term instanceof A ) {
357366 stack . push ( [ new Tuple ( term . right , new Env ( env ) ) , true ] ) ;
358367 term = term . left ;
359368 } else if ( term instanceof L ) {
360- let [ { term : lastTerm , env : lastEnv } , isRight ] = stack . pop ( ) ;
361- if ( isRight ) {
369+ let [ { term : lastTerm , env : lastEnv } , isRight , isEvaluated ] = stack . pop ( ) ;
370+ if ( isEvaluated ) {
371+ // A non-evaluated term was received from an Env, but it is now evaluated.
372+ // Store it.
373+ lastEnv . getValue ( lastTerm . name , new Tuple ( term , env ) ) ;
374+ } else if ( isRight ) {
362375 if ( term . name !== "_" )
363- env = new Env ( env ) . setThunk ( term . name , ( ) => evalLC ( new Tuple ( lastTerm , lastEnv ) ) ) ;
376+ env = new Env ( env ) . setThunk ( term . name , new Tuple ( lastTerm , lastEnv ) ) ;
364377 term = term . body ;
365378 } else { // Pass the function some other function.
366379 term = lastTerm ( awaitArg ( term , stack , env ) ) ;
@@ -370,8 +383,12 @@ function evalLC(term) {
370383 ( { term, env} = term ) ;
371384 } else { // Not a term
372385 if ( stack . length === 0 ) return term ;
373- let [ { term : lastTerm , env : lastEnv } , isRight ] = stack . pop ( ) ;
374- if ( isRight ) {
386+ let [ { term : lastTerm , env : lastEnv } , isRight , isEvaluated ] = stack . pop ( ) ;
387+ if ( isEvaluated ) {
388+ // A non-evaluated term was received from an Env, but it is now evaluated.
389+ // Store it.
390+ lastEnv . getValue ( lastTerm . name , new Tuple ( term , env ) ) ;
391+ } else if ( isRight ) {
375392 stack . push ( [ new Tuple ( term , new Env ( env ) ) , false ] ) ;
376393 term = lastTerm ;
377394 env = lastEnv ;
0 commit comments