antlr / antlrcs

The C# port of ANTLR 3, StringTemplate 3, and StringTemplate 4

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Separator output bug

BrianCatlin opened this issue · comments

When iterating over a list, separators for items skipped at the beginning of a list are not output, but once an item in the list is output, separators are output for subsequent items, even when items are skipped. The following C# code illustrates this. The output is: "2 is even," (note the trailing comma separator, but there isn't a comma before the 2, for the first skipped item)

static void Main (string [] args)
        {
        List <Item> items = new List <Item> () { new Item ("1", false), new Item ("2", true), new Item ("3",false)};
        string      string_template_group =
@"
even_params (parameters) ::=
<<
<parameters:{p|<if (p.is_even)><p.value> is even<endif>}; separator = "", "">
>>
";
        TemplateGroup   group = new TemplateGroupString (string_template_group);

        Template even_params = group.GetInstanceOf ("even_params");
        even_params.Add ("parameters", items);
        Console.WriteLine (even_params.Render ());
        }   // End Main

class Item
    {
    public string   value   { get; set; }
    public bool     is_even { get; set; }

    public Item (string V, bool E)
        {
        value = V;
        is_even = E;
        }

    }   // End class Item

Hi Brian,
I am no expert, but in your template, the odd parameters are not skipped per say, you simply don't output anything when treating them.
Considering that it has treated said parameter (even by not printing anything), string template logically adds a separator.
As ways to obtain what you are looking for, you could filter out items by adding a .where(i => i.isEven) LINQ call to items when adding them to the template ?
The template can then become the dead simple

<parameters;separator= "", "">

Another possibility that I see is to use two templates combined with the use of first() and rest() functions
You print the first element without a comma and then call another template:

$if(first($parameters).isEven)$$first($parameters).value$ is even$endif$$PrintFollowers(rest(parameters))$

In PrintFollowers you simply add the comma directly to each even parameter print.

Please notice that I am using slightly modified attribute names in the code above and '$' as delimiters 😅 .
Also I would suggest making the Value parameter as an int and compute isEven with a modulo for easier testing 😉

I hope this helps.
Kind regards.

Thank you for your response @chaami

If what you say is true, then there should have been a separator between all items in the list, but there isn't; however, notice that there aren't separators for items not output for items at the beginning of the list, but once an item is output (in my example, the second item), then ST outputs a separator for every subsequent item. So, clearly ST knows that it shouldn't be outputting separators at the beginning of the list, but once it does output an item it forgot to suppress separators for items not output.

I hadn’t thought about using LINQ for filtering. Thank you for that!

Hi @BrianCatlin,
Oups, actually the second workaround that I have given you missing the case when the first member is odd !
Complete solution:

<if(first(parameters).isEven)><first(parameters).value> is even<PrintFollowers(rest(parameters))>
<else><even_params(rest(parameters))><endif>

Actually if print the length of the list with

<length(parameters:{p|<if (p.is_even)><p.value> is even<endif>})>

Should yield to 3.
My guess is that StringTemplate handles gracefully empty first elements of lists and then you are on your own 😉
strip() unfortunately only remove null values...
I haven't found a way to push a null in a list through templates (only by specifically adding null in the c# Add call).

Thanks!

Glad I could help...
It helped me dig a little deeper into StringTemplate 😉
Good luck !