import * as THREE from "three" ; import { Env , Value } from "./Env" ; import { ExprEvaluator , ExprEvaluatorContext , OperatorDescriptor } from "./ExprEvaluator" ; import { ExprInstantiator , InstantiationContext } from "./ExprInstantiator" ; import { ExprParser } from "./ExprParser" ; import { ExprPool } from "./ExprPool" ; import { interpolatedPropertyDefinitionToJsonExpr , isInterpolatedPropertyDefinition } from "./InterpolatedPropertyDefs" ; import { Pixels } from "./Pixels" ; import { RGBA } from "./RGBA" ; import { Definition , Definitions , getDefinitionValue } from "./Theme" ; export * from "./Env" ; const exprEvaluator = new ExprEvaluator ( ) ; const exprInstantiator = new ExprInstantiator ( ) ; /**
 * A visitor for  Expr nodes.
 *  internal export interface ExprVisitor < Result , Context > { visitNullLiteralExpr ( expr , context ) : Result ; visitBooleanLiteralExpr ( expr , context ) : Result ; visitNumberLiteralExpr ( expr , context ) : Result ; visitStringLiteralExpr ( expr , context ) : Result ; visitObjectLiteralExpr ( expr , context ) : Result ; visitVarExpr ( expr , context ) : Result ; visitHasAttributeExpr ( expr , context ) : Result ; visitCallExpr ( expr , context ) : Result ; visitLookupExpr ( expr , context ) : Result ; visitMatchExpr ( expr , context ) : Result ; visitCaseExpr ( expr , context ) : Result ; visitStepExpr ( expr , context ) : Result ; visitInterpolateExpr ( expr , context ) : Result ; } /**
 * The dependencies of an  Expr .
 *  internal export class ExprDependencies { /**
     * The properties needed to evaluate the  Expr .
      readonly properties = new Set < string > ( ) ; /**
     * `true` if the expression depends on the feature state.
     */ featureState ? : boolean ; /**
     * `true` if this expression cannot be cached.
     */ volatile ? : boolean ; } class ComputeExprDependencies implements ExprVisitor < void , ExprDependencies > { static instance = new ComputeExprDependencies ( ) ; /**
     * Gets the dependencies of an  Expr .
     *
     *  param expr - The  Expr to process.
     *  param scope param dependencies static of ( expr ) { const dependencies = new ExprDependencies ( ) ; expr . accept ( this . instance , dependencies ) ; return dependencies ; } visitNullLiteralExpr ( expr , context ) { } visitBooleanLiteralExpr ( expr , context ) { } visitNumberLiteralExpr ( expr , context ) { } visitStringLiteralExpr ( expr , context ) { } visitObjectLiteralExpr ( expr , context ) { } visitVarExpr ( expr , context ) { context . properties . add ( expr . name ) ; } visitHasAttributeExpr ( expr , context ) { context . properties . add ( expr . name ) ; } visitCallExpr ( expr , context ) { expr . args . forEach ( childExpr => childExpr . accept ( this , context ) ) ; switch ( expr . op ) { case "dynamic-properties" : context . volatile = true ; break ; case "feature-state" : context . featureState = true ; context . properties . add ( "$state" ) ; context . properties . add ( "$id" ) ; break ; case "id" : context . properties . add ( "$id" ) ; break ; case "zoom" : case "world-ppi-scale" : case "world-discrete-ppi-scale" : context . properties . add ( "$zoom" ) ; break ; case "geometry-type" : context . properties . add ( "$geometryType" ) ; break ; default : break ; } } visitLookupExpr ( expr , context ) { return this . visitCallExpr ( expr , context ) ; } visitMatchExpr ( expr , context ) { expr . value . accept ( this , context ) ; expr . branches . forEach ( ( [ _ , branch ] ) => branch . accept ( this , context ) ) ; expr . fallback . accept ( this , context ) ; } visitCaseExpr ( expr , context ) { expr . branches . forEach ( ( [ condition , branch ] ) => { condition . accept ( this , context ) ; branch . accept ( this , context ) ; } ) ; expr . fallback . accept ( this , context ) ; } visitStepExpr ( expr , context ) { expr . input . accept ( this , context ) ; expr . defaultValue . accept ( this , context ) ; expr . stops . forEach ( ( [ _ , value ] ) => value . accept ( this , context ) ) ; } visitInterpolateExpr ( expr , context ) { expr . input . accept ( this , context ) ; expr . stops . forEach ( ( [ _ , value ] ) => value . accept ( this , context ) ) ; } } /**
 * A type represeting JSON values.
 */ export type JsonValue = null | boolean | number | string | JsonObject | JsonArray ; /**
 * A type representing JSON arrays.
 */ export interface JsonArray extends Array < JsonValue > { } /**
 * A type representing JSON objects.
 */ export interface JsonObject { [ name ] : JsonValue ; } /**
 * The JSON representation of an  Expr object.
  export type JsonExpr = JsonArray ; export function isJsonExpr ( v ) { return Array . isArray ( v ) && v . length > 0 && typeof v [ 0 ] === "string" ; } /**
 * Internal state needed by  Expr . fromJSON to resolve `"ref"` expressions.
  interface ReferenceResolverState { definitions : Definitions ; lockedNames : Set < string > ; cache : Map < string , Expr > ; } /**
 * The evaluation scope of an  Expr .
 *  internal export enum ExprScope { /**
     * The scope of an  Expr used as value of an attribute.
      Value , /**
     * The scope of an  Expr used in a [[Technique]] `when` condition.
      Condition , /**
     * The scope of an  Expr used as dynamic property attribute value.
      Dynamic } /**
 * Abstract class representing the
 *  https * used in  Theme .
  export abstract class Expr { /**
     * Tests of given value is an  Expr .
     *
     *  param value static isExpr ( value ) { return value instanceof Expr ; } param code returns The parsed  Expr .
     *  deprecated `string` encoded expression are deprecated. Use  Expr . fromJSON instead.
      static parse ( code ) { const parser = new ExprParser ( code ) ; const expr = parser . parse ( ) ; return expr ; } remarks The optional set of  Theme . definitions is used
     * to resolve the  https .
     *
     *  param json param definitions param definitionExprCache example static fromJSON ( json , definitions ? , definitionExprCache ? ) { const referenceResolverState = definitions !== undefined ? { definitions , lockedNames : new Set ( ) , cache : definitionExprCache ?? new Map < string , Expr > ( ) } : undefined ; return parseNode ( json , referenceResolverState ) ; } private m_dependencies ? : ExprDependencies ; private m_isDynamic ? : boolean ; /**
     * Evaluate an expression returning a  Value object.
     *
     *  param env - The  Env used to lookup symbols.
     *  param scope param cache evaluate ( env , scope = ExprScope . Value , cache ? ) { return this . accept ( exprEvaluator , new ExprEvaluatorContext ( exprEvaluator , env , scope , cache ) ) ; } /**
     * Instantiates this  Expr .
     *
     *  remarks param context instantiate ( context ) { return this . accept ( exprInstantiator , context ) ; } /**
     * Gets the dependencies of this  Expr .
      dependencies ( ) { if ( ! this . m_dependencies ) { this . m_dependencies = ComputeExprDependencies . of ( this ) ; } return this . m_dependencies ; } /**
     * Create a unique object that is structurally equivalent to this  Expr .
     *
     *  param pool - The [[ExprPool]] used to create a unique
     * equivalent object of this  Expr .
      intern ( pool ) { return pool . add ( this ) ; } toJSON ( ) { return new ExprSerializer ( ) . serialize ( this ) ; } /**
     * Returns `true` if a dynamic execution context is required to evaluate this  Expr .
      isDynamic ( ) { if ( this . m_isDynamic === undefined ) { this . m_isDynamic = this . exprIsDynamic ( ) ; } return this . m_isDynamic ; } param visitor param context abstract accept < Result , Context > ( visitor , context ) ; /**
     * Update the dynamic state of this  Expr .
     *
     * `exprIsDynamic` must never be called directly.
     *  internal protected abstract exprIsDynamic ( ) ; } internal export type RelationalOp = "<" | ">" | "<=" | ">=" ; internal export type EqualityOp = "~=" | "^=" | "$=" | "==" | "!=" ; internal export type BinaryOp = RelationalOp | EqualityOp ; internal export class VarExpr extends Expr { constructor ( readonly name ) { super ( ) ; } @override  accept < Result , Context > ( visitor , context ) { return visitor . visitVarExpr ( this , context ) ; } @override  protected exprIsDynamic ( ) { return false ; } } internal export abstract class LiteralExpr extends Expr { param value static fromValue ( value ) { switch ( typeof value ) { case "boolean" : return new BooleanLiteralExpr ( value ) ; case "number" : return new NumberLiteralExpr ( value ) ; case "string" : return new StringLiteralExpr ( value ) ; case "object" : return value === null ? NullLiteralExpr . instance : new ObjectLiteralExpr ( value ) ; default : throw new Error ( `failed to create a literal from '${ value }'` ) ; } } abstract get value ( ) : Value ; @override  protected exprIsDynamic ( ) { return false ; } } internal export class NullLiteralExpr extends LiteralExpr { static instance = new NullLiteralExpr ( ) ; @override  readonly value : Value = null ; protected constructor ( ) { super ( ) ; } @override  accept < Result , Context > ( visitor , context ) { return visitor . visitNullLiteralExpr ( this , context ) ; } @override  protected exprIsDynamic ( ) { return false ; } } internal export class BooleanLiteralExpr extends LiteralExpr { constructor ( readonly value ) { super ( ) ; } @override  accept < Result , Context > ( visitor , context ) { return visitor . visitBooleanLiteralExpr ( this , context ) ; } } internal export class NumberLiteralExpr extends LiteralExpr { constructor ( readonly value ) { super ( ) ; } @override  accept < Result , Context > ( visitor , context ) { return visitor . visitNumberLiteralExpr ( this , context ) ; } } internal export class StringLiteralExpr extends LiteralExpr { private m_promotedValue ? : RGBA | Pixels | null ; constructor ( readonly value ) { super ( ) ; } /**
     * Returns the value of parsing this string as [[RGBA]] or [[Pixels]] constant.
     */ get promotedValue ( ) : RGBA | Pixels | undefined { if ( this . m_promotedValue === undefined ) { this . m_promotedValue = RGBA . parse ( this . value ) ?? Pixels . parse ( this . value ) ?? null ; } return this . m_promotedValue ?? undefined ; } @override  accept < Result , Context > ( visitor , context ) { return visitor . visitStringLiteralExpr ( this , context ) ; } } internal export class ObjectLiteralExpr extends LiteralExpr { constructor ( readonly value ) { super ( ) ; } get isArrayLiteral ( ) { return Array . isArray ( this . value ) ; } @override  accept < Result , Context > ( visitor , context ) { return visitor . visitObjectLiteralExpr ( this , context ) ; } } internal export class HasAttributeExpr extends Expr { constructor ( readonly name ) { super ( ) ; } @override  accept < Result , Context > ( visitor , context ) { return visitor . visitHasAttributeExpr ( this , context ) ; } @override  protected exprIsDynamic ( ) { return false ; } } internal export class CallExpr extends Expr { descriptor ? : OperatorDescriptor ; constructor ( readonly op , readonly args ) { super ( ) ; } /**
     * Returns the child nodes of this  Expr .
     *
     *  deprecated Use  CallExpr . args instead.
      get children ( ) { return this . args ; } @override  accept < Result , Context > ( visitor , context ) { return visitor . visitCallExpr ( this , context ) ; } @override  protected exprIsDynamic ( ) { const descriptor = this . descriptor ?? ExprEvaluator . getOperator ( this . op ) ; if ( descriptor && descriptor . isDynamicOperator && descriptor . isDynamicOperator ( this ) ) { return true ; } return this . args . some ( e => e . isDynamic ( ) ) ; } } /**
 * A `lookup` expression is a call expression using the `lookup` operator. Then only difference is
 * that the lookup table definition (first argument) is cached as a map for fast search
 * (see  ExprEvaluator . visitLookupExpr ).
 *  internal export class LookupExpr extends CallExpr { /**
     * Creates a lookup expression from a  JsonArray .
     *  param node The  JsonArray to parse.
     *  param referenceResolverState returns static parseArray ( node , referenceResolverState ? ) { const lookupTableNode = node [ 1 ] ; if ( lookupTableNode === undefined ) { throw new Error ( "missing lookup table in 'lookup' expression" ) ; } const lookupTableExpr = parseNode ( lookupTableNode , referenceResolverState ) ; if ( ! Array . isArray ( lookupTableNode ) || ! ( lookupTableExpr instanceof ObjectLiteralExpr ) ) { throw new Error ( `Invalid lookup table expression for operator 'lookup'. It must be a literal or a ref to one.` ) ; } const lookupTable = lookupTableExpr . value ; if ( ! Array . isArray ( lookupTable ) ) { throw new Error ( `Invalid lookup table type (${ typeof lookupTable }) for operator 'lookup'` ) ; } const args = node . slice ( 2 ) . map ( childExpr => parseNode ( childExpr , referenceResolverState ) ) ; args . unshift ( lookupTableExpr ) ; return new LookupExpr ( args ) ; } param args constructor ( readonly args ) { super ( "lookup" , args ) ; } @override  accept < Result , Context > ( visitor , context ) { return visitor . visitLookupExpr ( this , context ) ; } } /**
 * The labels of a  MatchExpr expression.
 *  internal export type MatchLabel = number | string | number [ ] | string [ ] ; internal export class MatchExpr extends Expr { param node static isValidMatchLabel ( node ) { switch ( typeof node ) { case "number" : case "string" : return true ; case "object" : if ( ! Array . isArray ( node ) || node . length === 0 ) { return false ; } const elementTy = typeof node [ 0 ] ; if ( elementTy === "number" || elementTy === "string" ) { return node . every ( t => typeof t === elementTy ) ; } return false ; default : return false ; } } constructor ( readonly value , readonly branches , readonly fallback ) { super ( ) ; } @override  accept < Result , Context > ( visitor , context ) { return visitor . visitMatchExpr ( this , context ) ; } @override  protected exprIsDynamic ( ) { return ( this . value . isDynamic ( ) || this . branches . some ( ( [ _ , branch ] ) => branch . isDynamic ( ) ) || this . fallback . isDynamic ( ) ) ; } } internal export class CaseExpr extends Expr { constructor ( readonly branches , readonly fallback ) { super ( ) ; } @override  accept < Result , Context > ( visitor , context ) { return visitor . visitCaseExpr ( this , context ) ; } @override  protected exprIsDynamic ( ) { return ( this . branches . some ( ( [ cond , branch ] ) => cond . isDynamic ( ) || branch . isDynamic ( ) ) || this . fallback . isDynamic ( ) ) ; } } internal export class StepExpr extends Expr { constructor ( readonly input , readonly defaultValue , readonly stops ) { super ( ) ; } @override  accept < Result , Context > ( visitor , context ) { return visitor . visitStepExpr ( this , context ) ; } @override  protected exprIsDynamic ( ) { return ( this . input . isDynamic ( ) || this . defaultValue . isDynamic ( ) || this . stops . some ( ( [ _ , value ] ) => value . isDynamic ( ) ) ) ; } } /**
 * The type of the interpolation mode.
 */ export type InterpolateMode = [ "discrete" ] | [ "linear" ] | [ "cubic" ] | [ "exponential" , number ] ; internal export class InterpolateExpr extends Expr { constructor ( readonly mode , readonly input , readonly stops ) { super ( ) ; } @override  accept < Result , Context > ( visitor , context ) { return visitor . visitInterpolateExpr ( this , context ) ; } @override  protected exprIsDynamic ( ) { return this . input . isDynamic ( ) || this . stops . some ( ( [ _ , value ] ) => value . isDynamic ( ) ) ; } } internal class ExprSerializer implements ExprVisitor < JsonValue , void > { serialize ( expr ) { return expr . accept ( this , undefined ) ; } visitNullLiteralExpr ( expr , context ) { return null ; } visitBooleanLiteralExpr ( expr , context ) { return expr . value ; } visitNumberLiteralExpr ( expr , context ) { return expr . value ; } visitStringLiteralExpr ( expr , context ) { return expr . value ; } visitObjectLiteralExpr ( expr , context ) { if ( expr . value instanceof THREE . Vector2 ) { return [ "make-vector" , expr . value . x , expr . value . y ] ; } else if ( expr . value instanceof THREE . Vector3 ) { return [ "make-vector" , expr . value . x , expr . value . y , expr . value . z ] ; } else if ( expr . value instanceof THREE . Vector4 ) { return [ "make-vector" , expr . value . x , expr . value . y , expr . value . z , expr . value . w ] ; } return [ "literal" , expr . value as JsonObject ] ; } visitVarExpr ( expr , context ) { return [ "get" , expr . name ] ; } visitHasAttributeExpr ( expr , context ) { return [ "has" , expr . name ] ; } visitCallExpr ( expr , context ) { return [ expr . op , ... expr . args . map ( childExpr => this . serialize ( childExpr ) ) ] ; } visitLookupExpr ( expr , context ) { return this . visitCallExpr ( expr , context ) ; } visitMatchExpr ( expr , context ) { const branches = [ ] ; for ( const [ label , body ] of expr . branches ) { branches . push ( label , this . serialize ( body ) ) ; } return [ "match" , this . serialize ( expr . value ) , ... branches , this . serialize ( expr . fallback ) ] ; } visitCaseExpr ( expr , context ) { const branches = [ ] ; for ( const [ condition , body ] of expr . branches ) { branches . push ( this . serialize ( condition ) , this . serialize ( body ) ) ; } return [ "case" , ... branches , this . serialize ( expr . fallback ) ] ; } visitStepExpr ( expr , context ) { const result = [ "step" ] ; result . push ( this . serialize ( expr . input ) ) ; result . push ( this . serialize ( expr . defaultValue ) ) ; expr . stops . forEach ( ( [ key , value ] ) => { result . push ( key ) ; result . push ( this . serialize ( value ) ) ; } ) ; return result ; } visitInterpolateExpr ( expr , context ) { const result = [ "interpolate" , expr . mode ] ; result . push ( this . serialize ( expr . input ) ) ; expr . stops . forEach ( ( [ key , value ] ) => { result . push ( key ) ; result . push ( this . serialize ( value ) ) ; } ) ; return result ; } } function parseNode ( node , referenceResolverState ) { if ( Array . isArray ( node ) ) { return parseCall ( node , referenceResolverState ) ; } else if ( node === null ) { return NullLiteralExpr . instance ; } else if ( typeof node === "boolean" ) { return new BooleanLiteralExpr ( node ) ; } else if ( typeof node === "number" ) { return new NumberLiteralExpr ( node ) ; } else if ( typeof node === "string" ) { return new StringLiteralExpr ( node ) ; } throw new Error ( `failed to create expression from: ${ JSON . stringify ( node ) }` ) ; } function parseCall ( node , referenceResolverState ? ) { const op = node [ 0 ] ; if ( typeof op !== "string" ) { throw new Error ( "expected a builtin function name" ) ; } switch ( op ) { case "!has" : case "!in" : return new CallExpr ( "!" , [ parseCall ( [ op . slice ( 1 ) , ... node . slice ( 1 ) ] ) ] ) ; case "ref" : return resolveReference ( node , referenceResolverState ) ; case "get" : return parseGetExpr ( node , referenceResolverState ) ; case "has" : return parseHasExpr ( node , referenceResolverState ) ; case "literal" : return parseLiteralExpr ( node ) ; case "match" : return parseMatchExpr ( node , referenceResolverState ) ; case "case" : return parseCaseExpr ( node , referenceResolverState ) ; case "interpolate" : return parseInterpolateExpr ( node , referenceResolverState ) ; case "step" : return parseStepExpr ( node , referenceResolverState ) ; case "lookup" : return LookupExpr . parseArray ( node , referenceResolverState ) ; default : return makeCallExpr ( op , node , referenceResolverState ) ; } } function parseGetExpr ( node , referenceResolverState ? ) { if ( node [ 2 ] !== undefined ) { return makeCallExpr ( "get" , node , referenceResolverState ) ; } const name = node [ 1 ] ; if ( typeof name !== "string" ) { throw new Error ( `expected the name of an attribute` ) ; } return new VarExpr ( name ) ; } function parseHasExpr ( node , referenceResolverState ? ) { if ( node [ 2 ] !== undefined ) { return makeCallExpr ( "has" , node , referenceResolverState ) ; } const name = node [ 1 ] ; if ( typeof name !== "string" ) { throw new Error ( `expected the name of an attribute` ) ; } return new HasAttributeExpr ( name ) ; } function parseLiteralExpr ( node ) { const obj = node [ 1 ] ; if ( obj === null || typeof obj !== "object" ) { throw new Error ( "expected an object or array literal" ) ; } return new ObjectLiteralExpr ( obj ) ; } function parseMatchExpr ( node , referenceResolverState ? ) { if ( node . length < 4 ) { throw new Error ( "not enough arguments" ) ; } if ( ! ( node . length % 2 ) ) { throw new Error ( "fallback is missing in 'match' expression" ) ; } const value = parseNode ( node [ 1 ] , referenceResolverState ) ; const conditions = [ ] ; for ( let i = 2 ; i < node . length - 1 ; i += 2 ) { const label = node [ i ] ; if ( ! MatchExpr . isValidMatchLabel ( label ) ) { throw new Error ( `'${ JSON . stringify ( label ) }' is not a valid label for 'match'` ) ; } const expr = parseNode ( node [ i + 1 ] , referenceResolverState ) ; conditions . push ( [ label , expr ] ) ; } const fallback = parseNode ( node [ node . length - 1 ] , referenceResolverState ) ; return new MatchExpr ( value , conditions , fallback ) ; } function parseCaseExpr ( node , referenceResolverState ? ) { if ( node . length < 3 ) { throw new Error ( "not enough arguments" ) ; } if ( node . length % 2 ) { throw new Error ( "fallback is missing in 'case' expression" ) ; } const branches = [ ] ; for ( let i = 1 ; i < node . length - 1 ; i += 2 ) { const condition = parseNode ( node [ i ] , referenceResolverState ) ; const expr = parseNode ( node [ i + 1 ] , referenceResolverState ) ; branches . push ( [ condition , expr ] ) ; } const caseFallback = parseNode ( node [ node . length - 1 ] , referenceResolverState ) ; return new CaseExpr ( branches , caseFallback ) ; } function isInterpolationMode ( object ) { if ( ! Array . isArray ( object ) ) { return false ; } switch ( object [ 0 ] ) { case "discrete" : case "linear" : case "cubic" : case "exponential" : return true ; default : return false ; } } function parseInterpolateExpr ( node , referenceResolverState ? ) { const mode = node [ 1 ] as any ; if ( ! isInterpolationMode ( mode ) ) { throw new Error ( "expected an interpolation type" ) ; } if ( mode [ 0 ] === "exponential" && typeof mode [ 1 ] !== "number" ) { throw new Error ( "expected the base of the exponential interpolation" ) ; } const input = node [ 2 ] !== undefined ? parseNode ( node [ 2 ] , referenceResolverState ) : undefined ; if ( ! Expr . isExpr ( input ) ) { throw new Error ( `expected the input of the interpolation` ) ; } if ( node . length === 3 || ! ( node . length % 2 ) ) { throw new Error ( "invalid number of samples" ) ; } const stops = [ ] ; for ( let i = 3 ; i < node . length - 1 ; i += 2 ) { const key = node [ i ] as number ; const value = parseNode ( node [ i + 1 ] , referenceResolverState ) ; stops . push ( [ key , value ] ) ; } return new InterpolateExpr ( mode , input , stops ) ; } function parseStepExpr ( node , referenceResolverState ? ) { if ( node . length < 2 ) { throw new Error ( "expected the input of the 'step' operator" ) ; } if ( node . length < 3 || ! ( node . length % 2 ) ) { throw new Error ( "not enough arguments" ) ; } const input = parseNode ( node [ 1 ] , referenceResolverState ) ; const defaultValue = parseNode ( node [ 2 ] , referenceResolverState ) ; const stops = [ ] ; for ( let i = 3 ; i < node . length ; i += 2 ) { const key = node [ i ] as number ; const value = parseNode ( node [ i + 1 ] , referenceResolverState ) ; stops . push ( [ key , value ] ) ; } return new StepExpr ( input , defaultValue , stops ) ; } function makeCallExpr ( op , node , referenceResolverState ? ) { return new CallExpr ( op , node . slice ( 1 ) . map ( childExpr => parseNode ( childExpr , referenceResolverState ) ) ) ; } function resolveReference ( node , referenceResolverState ? ) { if ( typeof node [ 1 ] !== "string" ) { throw new Error ( `expected the name of an attribute` ) ; } if ( referenceResolverState === undefined ) { throw new Error ( `ref used with no definitions` ) ; } const name = node [ 1 ] as string ; if ( referenceResolverState . lockedNames . has ( name ) ) { throw new Error ( `circular referene to '${ name }'` ) ; } if ( ! ( name in referenceResolverState . definitions ) ) { throw new Error ( `definition '${ name }' not found` ) ; } const cachedEntry = referenceResolverState . cache . get ( name ) ; if ( cachedEntry !== undefined ) { return cachedEntry ; } let definitionEntry = referenceResolverState . definitions [ name ] as any ; let result ; const definitionValue = getDefinitionValue ( definitionEntry ) ; if ( isInterpolatedPropertyDefinition ( definitionValue ) ) { return Expr . fromJSON ( interpolatedPropertyDefinitionToJsonExpr ( definitionEntry ) ) ; } else if ( isJsonExpr ( definitionValue ) ) { definitionEntry = definitionValue ; } else { return Expr . fromJSON ( definitionValue ) ; } if ( isJsonExpr ( definitionEntry ) ) { referenceResolverState . lockedNames . add ( name ) ; try { result = parseNode ( definitionEntry , referenceResolverState ) ; } finally { referenceResolverState . lockedNames . delete ( name ) ; } } else { throw new Error ( `unsupported definition ${ name }` ) ; } referenceResolverState . cache . set ( name , result ) ; return result ; }