import { Env } from "./Env" ; import { BooleanLiteralExpr , CallExpr , CaseExpr , Expr , ExprScope , ExprVisitor , HasAttributeExpr , InterpolateExpr , LiteralExpr , LookupExpr , MatchExpr , MatchLabel , NullLiteralExpr , NumberLiteralExpr , ObjectLiteralExpr , StepExpr , StringLiteralExpr , VarExpr } from "./Expr" ; export interface InstantiationContext { /**
     * The  Env used to lookup for names.
      env : Env ; /**
     * The names to preserve during the instantiation.
     */ preserve ? : Set < string > ; } const emptyEnv = new Env ( ) ; hidden export class ExprInstantiator implements ExprVisitor < Expr , InstantiationContext > { visitNullLiteralExpr ( expr , _context ) { return expr ; } visitBooleanLiteralExpr ( expr , _context ) { return expr ; } visitNumberLiteralExpr ( expr , _context ) { return expr ; } visitStringLiteralExpr ( expr , _context ) { return expr ; } visitObjectLiteralExpr ( expr , _context ) { return expr ; } visitVarExpr ( expr , context ) { if ( context . preserve && context . preserve . has ( expr . name ) ) { return expr ; } const value = context . env . lookup ( expr . name ) ; return LiteralExpr . fromValue ( value !== undefined ? value : null ) ; } visitHasAttributeExpr ( expr , context ) { if ( context . preserve && context . preserve . has ( expr . name ) ) { return expr ; } const value = context . env . lookup ( expr . name ) !== undefined ; return LiteralExpr . fromValue ( value ) ; } private visitCallExprImpl ( expr , context , constructor ) { const args = expr . args . map ( arg => arg . accept ( this , context ) ) ; if ( args . some ( ( a , i ) => a !== expr . args [ i ] ) ) { return constructor ( expr . op , args ) ; } return expr ; } visitCallExpr ( expr , context ) { return this . visitCallExprImpl ( expr , context , ( op , args ) => { return new CallExpr ( op , args ) ; } ) ; } visitLookupExpr ( expr , context ) { return this . visitCallExprImpl ( expr , context , ( op , args ) => { return new LookupExpr ( args ) ; } ) ; } visitMatchExpr ( match , context ) { const value = match . value . accept ( this , context ) ; if ( value instanceof LiteralExpr ) { const r = value . value ; for ( const [ label , body ] of match . branches ) { if ( Array . isArray ( label ) && ( label as any [ ] ) . includes ( r ) ) { return body . accept ( this , context ) ; } else if ( label === r ) { return body . accept ( this , context ) ; } } return match . fallback . accept ( this , context ) ; } let changed = match . value !== value ; const branches = match . branches . map ( ( [ label , branch ] ) => { const newBranch = branch . accept ( this , context ) ; if ( newBranch !== branch ) { changed = true ; } return [ label , newBranch ] ; } ) ; const fallback = match . fallback . accept ( this , context ) ; if ( fallback !== match . fallback ) { changed = true ; } return changed ? new MatchExpr ( value , branches , fallback ) : match ; } visitCaseExpr ( expr , context ) { const branches = [ ] ; let changed = false ; for ( const [ condition , branch ] of expr . branches ) { const newCondition = condition . accept ( this , context ) ; const deps = newCondition . dependencies ( ) ; if ( ! condition . isDynamic ( ) && deps . properties . size === 0 ) { if ( Boolean ( newCondition . evaluate ( emptyEnv , ExprScope . Condition ) ) ) { return branch . accept ( this , context ) ; } } else { if ( newCondition !== condition ) { changed = true ; } branches . push ( [ newCondition , branch ] ) ; } } if ( branches . length === 0 ) { return expr . fallback . accept ( this , context ) ; } if ( branches . length !== expr . branches . length ) { changed = true ; } branches . forEach ( branch => { const instantiatedBranch = branch [ 1 ] . accept ( this , context ) ; if ( instantiatedBranch !== branch [ 1 ] ) { changed = true ; } branch [ 1 ] = instantiatedBranch ; } ) ; const fallback = expr . fallback . accept ( this , context ) ; if ( fallback !== expr . fallback ) { changed = true ; } if ( ! changed ) { return expr ; } return new CaseExpr ( branches , fallback ) ; } visitStepExpr ( expr , context ) { const input = expr . input . accept ( this , context ) ; const defaultValue = expr . defaultValue . accept ( this , context ) ; const stops = expr . stops . map ( ( [ key , value ] ) => [ key , value . accept ( this , context ) ] ) ; return new StepExpr ( input , defaultValue , stops ) ; } visitInterpolateExpr ( expr , context ) { const input = expr . input . accept ( this , context ) ; const stops = expr . stops . map ( ( [ key , value ] ) => [ key , value . accept ( this , context ) ] ) ; return new InterpolateExpr ( expr . mode , input , stops ) ; } }