Search code examples
c#parsingscriban

Scriban - list parsed expressions (variables)


I'm wondering how to list all parsed expressions (variables) by using a simple code snippet:

var template = Template.Parse(@"
<ul id='products'>
  {{ for product in products }}
    <li>
      <h2>{{ product.name }}</h2>
           Price: {{ product.price }}
           {{ product.description | string.truncate 15 }}
    </li>
  {{ end }}
</ul>
");

I need to know the used variables in orde to fetch only data (DB data) I really need. Is there some built-in method or do I need to implement my own?


Solution

  • Looks like you may use custom TemplateContext to visit expression tree during evaluation. But this requires that all global variables from the template (products here) are registered in the context, at least in a dummy form.

    E.g:

        class ScriptVariableMemberExpressionVisitorTemplateContext : TemplateContext
        {
            private readonly Action<ScriptVariableGlobal, ScriptVariableGlobal> memberExpressionVisitor;
    
            public ScriptVariableMemberExpressionVisitorTemplateContext(
                Action<ScriptVariableGlobal, ScriptVariableGlobal> memberExpressionVisitor)
            {
                this.memberExpressionVisitor = memberExpressionVisitor ??
                                               throw new ArgumentNullException(nameof(memberExpressionVisitor));
            }
    
            protected override object EvaluateImpl(ScriptNode scriptNode)
            {
                var memberExpression = scriptNode as Scriban.Syntax.ScriptMemberExpression;
    
                if (memberExpression != null
                    && memberExpression.Target is ScriptVariableGlobal
                    && memberExpression.Member is ScriptVariableGlobal)
                {
                    this.memberExpressionVisitor((ScriptVariableGlobal)memberExpression.Target, (ScriptVariableGlobal)memberExpression.Member);
                }
    
                return base.EvaluateImpl(scriptNode);
            }
        }
    
    ...
    
        var template = Template.Parse(@"
    <ul id='products'>
      {{ for product in products }}
        <li>
          <h2>{{ product.name }}</h2>
               Price: {{ product.price }}
               {{ product.description | string.truncate 15 }}
        </li>
      {{ end }}
    </ul>
    ");
    
        Dictionary<string, HashSet<string>> productFields = new Dictionary<string, HashSet<string>>();
        var context = new ScriptVariableMemberExpressionVisitorTemplateContext(
            (target, member) =>
            {
                if (!productFields.ContainsKey(target.Name))
                {
                    productFields[target.Name] = new HashSet<string>();
                }
                productFields[target.Name].Add(member.Name);
            });
    
        var scriptObject = new ScriptObject();
        scriptObject.Import(new { products = new[] { new { } } }); //required: `products` object from the template
        context.PushGlobal(scriptObject);
        template.Evaluate(context);
    
        productFields["product"].ToList().ForEach(Console.WriteLine); //show fields from product object, used by the template
    

    The output is the list of fields from the template:

    name
    price
    description