HOME | 11. Procedures as data | Try Sather | 13. Programming by contract | download | Post Messages |
The exception handling of Sather is structured
comparing with those of other languages.
Let's equip exception handlers to the reverse polish calculator presented previously. This version of the calculator reads input from a file and sends following exceptions when it finds errors:
[code 1]
01: -- rpcalcf.sa: a simple reverse polish calculator, third version 02: -- include exception handling 03: 04: class COMMANDLINE_EXC < $STR is -- command line exception 05: 06: readonly attr str:STR; 07: 08: create:SAME is 09: res:SAME:=new; 10: res.str:= "Invalid command line.\n"; 11: return res; 12: end; 13: end; 14: 15: class OPEN_FILE_EXC < $STR is -- file open error exception 16: 17: readonly attr str:STR; 18: 19: create(fname:STR):SAME is 20: res:SAME:=new; 21: res.str:= "Cannot open " + fname + ".\n"; 22: return res; 23: end; 24: end; 25: 26: class PARSE_EXC < $STR is -- wrong formula exception 27: 28: readonly attr str:STR; 29: 30: create(s:STR):SAME is 31: res:SAME:=new; 32: res.str:= s + "\n"; 33: return res; 34: end; 35: end; 36: 37: 38: 39: class OPERATORS is -- defining operators, same as rpcalc.sa 40: 41: attr biop:MAP{STR,ROUT{FLTD,FLTD}:FLTD}; -- MAP of binary operators 42: attr unop:MAP{STR,ROUT{FLTD}:FLTD}; -- unary operators 43: 44: create:SAME is 45: return new.init; 46: end; 47: 48: init:SAME is 49: init_biop; 50: init_unop; 51: return self; 52: end; 53: 54: init_biop is 55: biop:=#; 56: biop["+"]:=bind(_.plus(_)); 57: biop["-"]:=bind(_.minus(_)); 58: biop["*"]:=bind(_.times(_)); 59: biop["/"]:=bind(_.div(_)); 60: biop["^"]:=bind(_.pow(_)); 61: end; 62: 63: init_unop is 64: unop:=#; 65: unop["exp"] := bind(_.exp); 66: unop["log"] := bind(_.log); 67: unop["sin"] := bind(_.sin); 68: unop["cos"] := bind(_.cos); 69: unop["tan"] := bind(_.tan); 70: unop["asin"]:= bind(_.asin); 71: unop["acos"]:= bind(_.acos); 72: unop["atan"]:= bind(_.atan); 73: end; 74: 75: is_operator(s:STR):BOOL is 76: return is_biop(s) or is_unop(s); 77: end; 78: 79: is_biop(s:STR):BOOL is 80: return biop.has_ind(s); 81: end; 82: 83: is_unop(s:STR):BOOL is 84: return unop.has_ind(s); 85: end; 86: 87: apply(s:STR, a:FLTD):FLTD is 88: return unop[s].call(a); 89: end; 90: 91: apply(s:STR, a,b:FLTD):FLTD is 92: return biop[s].call(a,b); 93: end; 94: 95: end; --OPERATORS 96: 97: class CALCULATOR is -- a reverse polish calculator 98: 99: const re:REGEXP:=REGEXP::regexp("^[+\\-]?[0-9]+\\.?[0-9]*$", false); 100: attr stack:STACK{FLTD}; 101: attr str_queue:QUEUE{STR}; 102: attr op:OPERATORS; 103: 104: create(fname:STR):SAME is 105: res:SAME:=new; 106: res.str_queue:=readfile(fname); 107: res.stack:=#; 108: res.op:=#; 109: return res; 110: end; 111: 112: readfile(fname:STR):QUEUE{STR} is -- reading input and returning a QUEUE{STR} 113: q:QUEUE{STR}:=#; 114: line, word,contents:STR; 115: f:FILE:=FILE::open_for_read(fname); 116: if f.error then raise #OPEN_FILE_EXC(fname); end; 117: contents:=f.str; 118: f.close; 119: if contents[contents.size-1]/='\n' then contents := contents + '\n'; end; 120: loop 121: line:=contents.split!('\n'); 122: line:=line.head(line.size-1) + " "; 123: loop 124: word:=line.split!(' '); 125: word:=word.head(word.size-1); 126: if word.size > 0 then 127: q.enq(word); 128: end; 129: end; 130: end; 131: return q; 132: end; 133: 134: is_number(word:STR):BOOL is 135: return re.match(word); 136: end; 137: 138: uncalc(word:STR) is 139: if stack.is_empty then raise #PARSE_EXC("no enough stack for unary op."); end; 140: stack.push(op.apply(word, stack.pop)); 141: end; 142: 143: bicalc(word:STR) is 144: if stack.size < 2 then raise #PARSE_EXC("no enough stack for binary op."); end; 145: f1,f2 :FLTD; 146: f2:=stack.pop; 147: f1:=stack.pop; 148: stack.push(op.apply(word,f1,f2)); 149: end; 150: 151: push_to_stack(word:STR) is 152: stack.push(word.cursor.get_fltd); 153: end; 154: 155: run is -- calculating according to str_queue 156: word:STR; 157: loop 158: word := str_queue.elt!; 159: if op.is_unop(word) then uncalc(word); 160: elsif op.is_biop(word) then bicalc(word); 161: elsif is_number(word) then push_to_stack(word); 162: else raise #PARSE_EXC("\"" + word + "\" is not operator nor number."); -- raise an exception if find a token unable to parse 163: end; 164: end; 165: 166: if stack.size/=1 then raise #PARSE_EXC("stack is not finished at the end."); end; -- if not stack.size = 1, raise an exception 167: #OUT + stack.top +"\n"; 168: end; 169: end; -- CALCULATOR 170: 171: 172: class MAIN is 173: 174: main(av: ARRAY{STR}) is 175: protect -- surrounding the unsafe routines with protect 176: if av.size/=2 then raise #COMMANDLINE_EXC; end; 177: calc:CALCULATOR:=#(av[1]); 178: calc.run; 179: -- sorting the exceptions 180: when COMMANDLINE_EXC then 181: #ERR + exception; -- The instance name is always exception 182: UNIX::exit(1); 183: when OPEN_FILE_EXC then 184: #ERR + exception; 185: UNIX::exit(2); 186: when PARSE_EXC then 187: #ERR + exception; 188: UNIX::exit(3); 189: else 190: #ERR + "Unknown exception.\n"; 191: UNIX::exit(4); 192: end; 193: end; 194: end;If you give a following file (rpcalf.inp) to the calculator, the parse exception is sent and caught.
17 4 % 3.3 0.7 + /
$ sacomp rpcalcf.sa -o rpcalcf $ ./rpcalcf rpcalcf.inp "%" is not operator nor number.
Download and play with the code presented in this chapter.
HOME | 11. Procedures as data | Try Sather | 13. Programming by contract | download | Post Messages |