import { List , Map , Record } from 'immutable' ; import { StepperDirectives } from "../index" ; interface AnalysisC { functionCallStack : any ; callReturn ? : { func : any ; args : any ; result : any ; } ; } export const StackFrame = Record ( { scope : null , key : null , func : null , args : null , localNames : List ( ) , localMap : Map ( ) , directives : List ( ) } ) ; export const analyseState = function ( programState ) : AnalysisC { const functionCallStack = analyseScope ( programState . scope ) ; const result = { functionCallStack } ; if ( programState . direction === 'out' ) { result . callReturn = { func : programState . control . values [ 0 ] , args : programState . control . values . slice ( 1 ) , result : programState . result } ; } return Object . freeze ( result ) ; } ; const analyseScope = function ( scope ) { if ( ! scope ) { return List ( ) ; } let functionCallStack = analyseScope ( scope . parent ) ; switch ( scope . kind ) { case 'function' : { const func = scope . values [ 0 ] ; const args = scope . values . slice ( 1 ) ; functionCallStack = functionCallStack . push ( StackFrame ( { scope : scope , key : scope . key , func , args , directives : List ( scope . directives ) } ) ) ; break ; } case 'block' : { functionCallStack = functionCallStack . updateIn ( [ functionCallStack . size - 1 , 'directives' ] , directives => directives . concat ( scope . directives ) ) ; break ; } case 'variable' : { const { name , type , ref } = scope ; functionCallStack = functionCallStack . update ( functionCallStack . size - 1 , function ( stackFrame ) { stackFrame = stackFrame . update ( 'localNames' , function ( localNames ) { const i = localNames . indexOf ( name ) ; if ( - 1 !== i ) { localNames = localNames . delete ( i ) ; } localNames = localNames . push ( name ) ; return localNames ; } ) ; stackFrame = stackFrame . setIn ( [ 'localMap' , name ] , Object . freeze ( { type , ref } ) ) ; return stackFrame ; } ) ; break ; } } return functionCallStack ; } ; export const collectDirectives = function ( functionCallStack , focusDepth ) : StepperDirectives { const ordered = [ ] ; const functionCallStackMap = { } ; for ( let depth = functionCallStack . size - 1 - focusDepth ; depth >= 0 ; depth -= 1 ) { const stackFrame = functionCallStack . get ( depth ) ; const directives = stackFrame . get ( 'directives' ) ; directives . forEach ( function ( directive ) { const { key } = directive ; if ( key in functionCallStackMap ) { functionCallStackMap [ key ] . push ( stackFrame ) ; } else { ordered . push ( directive ) ; functionCallStackMap [ key ] = [ stackFrame ] ; } } ) ; } return Object . freeze ( { ordered , functionCallStackMap , functionCallStack : null } ) ; } ;