HOME Sather を試そう download 書き込む

12. 例外


1. 初めに

今回は Sather の例外処理について述べます。

Sather の例外は他の言語に比べれば "構造化" されているといえます。

2. 例外の発生と補足

Sather の例外は raise で発生させ、protect で捕捉します。raise は実行時に protect の 内側になければなりません。raise が呼び出されると例外インスタンス exception が生成し、それが protect で捕捉されます。 exception の型によって処理を分けます。 簡単な例を挙げて説明しましょう。先の逆ポーランド記法電卓で、式をファイルから読み取ることにします。 そして、以下のような例外が発生するようにします。 これらの例外クラスは自分で定義する必要があります。

[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.

3. 終わりに

今回は例を挙げて例外処理について説明しました。

例によって付録にコードをつけておきますので、気が向いたら遊んでみてください。


HOME Sather を試そう download 書き込む