HOME Try Sather download Post Messages

12. Exception


1. Introduction

I will talk about exception handling of Sather in this chapter.

The exception handling of Sather is structured comparing with those of other languages.

2. Raising and catching exceptions

The exceptions are sent by raise statement and captured by protect. The raise statement should be inside of protect at runtime. When raise are called, an instance of Exception class named exception is sent then it is caught by the protect. The protect handles the exception according to its type.

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:

You should define these exception classes by yourself.

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

3. Summary

I have explained on the exception handling of Sather in this chapter.

Download and play with the code presented in this chapter.


HOME Try Sather download Post Messages