A format for expressing an ordered list of integers is to use a comma separated list of either

 individual integers
 Or a range of integers denoted by the starting integer separated from the end integer in the range by a dash, '-'. (The range includes all integers in the interval including both endpoints)
 The range syntax is to be used only for, and for every range that expands to more than two values.
Example

The list of integers:

 -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20
Is accurately expressed by the range expression:

 -6,-3-1,3-5,7-11,14,15,17-20
(And vice-versa).



Task

Expand the range description:

  -6,-3--1,3-5,7-11,14,15,17-20 

Note that the second element above,
is the range from minus 3 to minus 1. 



Related task

   Range extraction


include "cowgol.coh";
 
# Callback interface 
interface RangeCb(n: int32);
 
# This will call `cb' for each number in the range, in ascending order.
# It will return NULL on success, or the location of an error if
# there is one.
sub Expand(ranges: [uint8], cb: RangeCb): (err: [uint8]) is
    err := 0 as [uint8];
    loop
        # Grab first number
        var n1: int32;
        var next: [uint8];
        (n1, next) := AToI(ranges);
 
        if next == ranges then
            # No number here!
            err := ranges; 
            break;
        elseif [next] == ',' or [next] == 0 then
            # Only one number, not a range
            cb(n1);
        elseif [next] != '-' then
            # No dash! 
            err := ranges;
            break;
        else    
            # Grab second number
            ranges := @next next;
            var n2: int32;
            (n2, next) := AToI(ranges);
            if next == ranges or n1 >= n2 then
                # No second number, or first not before second
                err := ranges;
                break;
            end if;
 
            # We need all numbers from n1 to n2 inclusive
            while n1 <= n2 loop
                cb(n1);
                n1 := n1 + 1;
            end loop;
        end if;
 
        # stop if end reached
        if [next] == 0 then break;
        elseif [next] != ',' then
            err := ranges;
            break;
        end if;
        ranges := @next next;
    end loop;
end sub;
 
# This function will use `Expand' above to expand a comma-separated
# range list, and reformat it as a comma-separated list of integers.
sub ExpandFmt(ranges: [uint8], buf: [uint8]): (err: [uint8]) is
    # Format and add number to buffer
    sub AddNum implements RangeCb is
        buf := IToA(n, 10, buf);
        [buf] := ',';
        buf := @next buf;
    end sub;
 
    # Expand range, adding numbers to buffer
    err := Expand(ranges, AddNum);
    [@prev buf] := 0;
end sub;
 
# Expand and print, and/or give error
sub PrintExpansion(ranges: [uint8]) is
    var buf: uint8[256];
    var err := ExpandFmt(ranges, &buf[0]);
    print(ranges);
    print_nl();
    print("  >> ");
    if err == 0 as [uint8] then
        # everything is OK
        print(&buf[0]);
    else
        # error
        print("error at: ");
        print(err);
    end if;
    print_nl();
end sub;
 
# Try it on the given input
PrintExpansion("-6,-3--1,3-5,7-11,14,15,17-20");
 
# Try it on a couple of wrong ones
PrintExpansion("-6-3--1,3-5,7-11,14,15,17-20"); # misformatted range
PrintExpansion("-6,-3--1,5-3,7-11,14,15,17-20"); # numbers not in order