![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Sather の例外は他の言語に比べれば "構造化" されているといえます。
[code 1]
01: -- rpcalcf.sa: a simple reverse polish calculator, third version 02: -- include exception handling 03: 04: class COMMANDLINE_EXC < $STR is -- コマンドラインの例外を定義 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 -- ファイルが開けないときの例外を定義 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 -- 計算式がおかしいときの例外を定義 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 -- オペレータを定義 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 -- 計算機クラスを定義 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 -- 入力ファイルを読み込んで 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 -- 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."); -- 解釈できないトークンがあったら例外を発生 163: end; 164: end; 165: 166: if stack.size/=1 then raise #PARSE_EXC("stack is not finished at the end."); end; -- 最後に stack.size = 1 になっていなければ例外を発生 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 -- 例外を発生しうるルーチンを protect で囲む 176: if av.size/=2 then raise #COMMANDLINE_EXC; end; 177: calc:CALCULATOR:=#(av[1]); 178: calc.run; 179: -- 例外の種類によって処理を分ける。 180: when COMMANDLINE_EXC then 181: #ERR + exception; -- 例外のインスタンス名は 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 + "Unnkown exception.\n"; 191: UNIX::exit(4); 192: end; 193: end; 194: end;次のような入力ファイル (rpcalf.inp) を作って実行すると例外が捕捉されます。
17 4 % 3.3 0.7 + /
$ sacomp rpcalcf.sa -o rpcalcf $ ./rpcalcf rpcalcf.inp "%" is not operator nor number.
例によって付録にコードをつけておきますので、気が向いたら遊んでみてください。
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |