/** <summary>
         *  To write out the value of a condition expr, invoke the evaluator in eval.g
         *  to walk the condition tree computing the boolean value.  If result
         *  is true, then write subtemplate.
         *  </summary>
         */
        public override int Write(StringTemplate self, IStringTemplateWriter @out)
        {
            if (AST == null || self == null || @out == null)
            {
                return(0);
            }
            //System.Console.Out.WriteLine( "evaluating conditional tree: " + AST.ToStringTree() );
#if !COMPILE_EXPRESSIONS
            ActionEvaluator eval = null;
#endif
            int n = 0;
            try
            {
                bool testedTrue = false;
                // get conditional from tree and compute result
#if COMPILE_EXPRESSIONS
                if (EvaluateCondition == null)
                {
                    EvaluateCondition = GetEvaluator(this, AST.GetChild(0));
                }
                bool includeSubtemplate = EvaluateCondition(self, @out);   // eval and write out tree
#else
                ITree cond = AST.GetChild(0);
                eval = new ActionEvaluator(self, this, @out, cond);
                // eval and write out trees
                bool includeSubtemplate = eval.ifCondition();
#endif
                //System.Console.Out.WriteLine( "subtemplate " + _subtemplate );
                // IF
                if (includeSubtemplate)
                {
                    n          = WriteSubTemplate(self, @out, _subtemplate);
                    testedTrue = true;
                }
                // ELSEIF
                else if (_elseIfSubtemplates != null && _elseIfSubtemplates.Count > 0)
                {
                    for (int i = 0; i < _elseIfSubtemplates.Count; i++)
                    {
                        ElseIfClauseData elseIfClause = _elseIfSubtemplates[i];
#if COMPILE_EXPRESSIONS
                        if (elseIfClause.EvaluateCondition == null)
                        {
                            elseIfClause.EvaluateCondition = GetEvaluator(this, elseIfClause.expr.AST);
                        }
                        includeSubtemplate = elseIfClause.EvaluateCondition(self, @out);
#else
                        eval = new ActionEvaluator(self, this, @out, elseIfClause.expr.AST);
                        includeSubtemplate = eval.ifCondition();
#endif
                        if (includeSubtemplate)
                        {
                            WriteSubTemplate(self, @out, elseIfClause.st);
                            testedTrue = true;
                            break;
                        }
                    }
                }
                // ELSE
                if (!testedTrue && _elseSubtemplate != null)
                {
                    // evaluate ELSE clause if present and IF condition failed
                    StringTemplate s = _elseSubtemplate.GetInstanceOf();
                    s.EnclosingInstance = self;
                    s.Group             = self.Group;
                    s.NativeGroup       = self.NativeGroup;
                    n = s.Write(@out);
                }
                // cond==false and no else => Missing output not empty
                if (!testedTrue && _elseSubtemplate == null)
                {
                    n = Missing;
                }
            }
            catch (RecognitionException re)
            {
                self.Error("can't evaluate tree: " + AST.ToStringTree(), re);
            }
            return(n);
        }
        static System.Func <StringTemplate, IStringTemplateWriter, bool> GetEvaluator(ASTExpr chunk, ITree condition)
        {
            if (EnableDynamicMethods)
            {
                try
                {
                    DynamicMethod method = null;
#if CACHE_FUNCTORS
                    if (!_methods.TryGetValue(condition, out method))
#endif
                    {
                        Type[] parameterTypes = { typeof(ASTExpr), typeof(StringTemplate), typeof(IStringTemplateWriter) };
                        method = new DynamicMethod("ConditionEvaluator" + _evaluatorNumber, typeof(bool), parameterTypes, typeof(ConditionalExpr), true);
#if !NETSTANDARD2_0
                        method.DefineParameter(1, ParameterAttributes.None, "chunk");
                        method.DefineParameter(2, ParameterAttributes.None, "self");
                        method.DefineParameter(3, ParameterAttributes.None, "writer");
#endif
                        _evaluatorNumber++;

                        var             gen          = method.GetILGenerator();
                        ActionEvaluator evalCompiled = new ActionEvaluator(null, chunk, null, condition);
                        evalCompiled.ifConditionCompiled(gen);
                        gen.Emit(OpCodes.Ret);
#if CACHE_FUNCTORS
                        _methods[condition] = method;
#endif
                    }

                    var dynamicEvaluator = (System.Func <StringTemplate, IStringTemplateWriter, bool>)method.CreateDelegate(typeof(System.Func <StringTemplate, IStringTemplateWriter, bool>), chunk);
                    return(dynamicEvaluator);
                }
                catch
                {
                    // fall back to functional (or interpreted) version
                }
            }

            if (EnableFunctionalMethods)
            {
                try
                {
                    ActionEvaluator            evalFunctional      = new ActionEvaluator(null, chunk, null, condition);
                    var                        functionalEvaluator = evalFunctional.ifConditionFunctional();
                    HoldsConditionFuncAndChunk holder = new HoldsConditionFuncAndChunk()
                    {
                        func  = functionalEvaluator,
                        chunk = chunk
                    };
                    return((System.Func <StringTemplate, IStringTemplateWriter, bool>)System.Delegate.CreateDelegate(typeof(System.Func <StringTemplate, IStringTemplateWriter, bool>), holder, typeof(ConditionalExpr).GetMethod("CallFunctionalConditionEvaluator")));
                }
                catch
                {
                    // fall back to interpreted version
                }
            }

            return(new System.Func <StringTemplate, IStringTemplateWriter, bool>((self, @out) =>
            {
                ActionEvaluator eval = new ActionEvaluator(self, chunk, @out, condition);
                return eval.ifCondition();
            }));
        }