1package body Spreadsheet_references is 2 3 --------------- 4 -- Reference -- 5 --------------- 6 7 function Reference( 8 row, column: Positive; 9 style : Reference_style:= A1 10 ) 11 return String 12 is 13 rs: constant String:= Positive'Image(row); 14 cs: constant String:= Positive'Image(column); 15 -- Skip the @#*$! leading space... 16 r: constant String:= rs(rs'First+1..rs'Last); 17 c: constant String:= cs(cs'First+1..cs'Last); 18 -- A bit tricky: 'A'..'Z' are not like digits, 19 -- or rather like digits without a zero (1..9)! 20 -- 21 -- You have exactly (26**n - 1) * 26 / 25 combinations 22 -- for a code with 1 to n letters! 23 24 function Base_26(n: Natural) return String is 25 begin 26 if n <= 25 then 27 return (1 => Character'Val(Character'Pos('A') + n)); 28 else 29 return Base_26(n / 26 - 1) & Base_26(n mod 26); 30 end if; 31 end Base_26; 32 begin 33 case style is 34 when A1 => 35 return Base_26(column - 1) & r; 36 when R1C1 => 37 return 'R' & r & 'C' & c; 38 end case; 39 end Reference; 40 41 --------- 42 -- Row -- 43 --------- 44 45 function Row (reference: String) return Positive is 46 r, c: Positive; 47 begin 48 Split(reference, r, c); 49 return r; 50 end Row; 51 52 ------------ 53 -- Column -- 54 ------------ 55 56 function Column (reference: String) return Positive is 57 r, c: Positive; 58 begin 59 Split(reference, r, c); 60 return c; 61 end Column; 62 63 ----------- 64 -- Split -- 65 ----------- 66 67 procedure Split (reference: String; row, column: out Positive) is 68 phase: Positive range 1..4:= 1; 69 r, c, d, cc: Natural:= 0; 70 s: Character; 71 begin 72 if reference = "" then 73 raise Invalid_spreadsheet_reference; 74 end if; 75 for i in reference'Range loop 76 s:= reference(i); 77 if s in 'a'..'z' then 78 s:= Character'Val(Character'Pos(s) - Character'Pos('a') + Character'Pos('A')); 79 end if; 80 case s is 81 when '0'..'9' => 82 case phase is 83 when 1 => 84 if i = reference'First then 85 raise Invalid_spreadsheet_reference; -- cannot start with a digit 86 end if; 87 phase:= 2; 88 when 2 | 4 => 89 null; -- already in a digit 90 when 3 => -- We begin the column in R1C1 style 91 if c /= 18 or cc /= 3 then 92 -- 1st letter code must be exactly "R" and 2nd must be "C" 93 raise Invalid_spreadsheet_reference; 94 end if; 95 c:= 0; 96 phase:= 4; 97 end case; 98 d:= Character'Pos(s) - Character'Pos('0'); 99 case phase is 100 when 1 | 3 => 101 null; -- we never get here. 102 when 2 => 103 r:= r * 10 + d; 104 when 4 => 105 c:= c * 10 + d; 106 end case; 107 when 'A'..'Z' => 108 case phase is 109 when 1 | 3 => 110 null; -- already in a letter code 111 when 2 => 112 phase:= 3; 113 when 4 => -- not a 3rd letter code! 114 raise Invalid_spreadsheet_reference; 115 end case; 116 d:= Character'Pos(s) - Character'Pos('A') + 1; 117 case phase is 118 when 2 | 4 => 119 null; -- we never get here. 120 when 1 => 121 c:= c * 26 + d; 122 when 3 => 123 cc:= cc * 26 + d; 124 end case; 125 when others => 126 raise Invalid_spreadsheet_reference; 127 end case; 128 end loop; 129 row:= r; 130 column:= c; 131 end Split; 132 133end Spreadsheet_references; 134