Task

Create a function that takes a Roman numeral as its argument and returns its value as a numeric decimal integer.

You don't need to validate the form of the Roman numeral.

Modern Roman numerals are written by expressing each decimal digit of the number to be encoded separately,

starting with the leftmost decimal digit and skipping any 0s   (zeroes).

1990 is rendered as   MCMXC     (1000 = M,   900 = CM,   90 = XC)     and

2008 is rendered as   MMVIII       (2000 = MM,   8 = VIII).

The Roman numeral for 1666,   MDCLXVI,   uses each letter in descending order.




include "cowgol.coh";
include "argv.coh";
 
# Decode the Roman numeral in the given string.
# Returns 0 if the string does not contain a valid Roman numeral.
sub romanToDecimal(str: [uint8]): (rslt: uint16) is
    # Look up a Roman digit
    sub digit(char: uint8): (val: uint16) is
        # Definition of Roman numerals
        record RomanDigit is
            char: uint8;
            value: uint16;
        end record;
 
        var digits: RomanDigit[] := {
            {'I',1}, {'V',5}, {'X',10}, {'L',50},
            {'C',100}, {'D',500}, {'M',1000}
        };
 
        char := char & ~32; # make uppercase
 
        # Look up given digit
        var i: @indexof digits := 0;
        while i < @sizeof digits loop
            val := digits[i].value;
            if digits[i].char == char then
                return;
            end if;
            i := i + 1;
        end loop;
        val := 0;
    end sub;
 
    rslt := 0;
    while [str] != 0 loop
        var cur := digit([str]); # get value of current digit
        if cur == 0 then rslt := 0; return; end if; # stop when invalid
        str := @next str;
        if digit([str]) > cur then
            # a digit followed by a larger digit should be subtracted from
            # the total
            rslt := rslt - cur;
        else
            rslt := rslt + cur;
        end if;
    end loop;
end sub;
 
# Read a Roman numeral from the command line and print its output
ArgvInit();
var argmt := ArgvNext();
if argmt == (0 as [uint8]) then
    # No argument
    print("No argument\n");
    ExitWithError();
end if;
 
print_i16(romanToDecimal(argmt));
print_nl();