1 (* Title: Pure/Thy/thy_read.ML |
1 (* Title: Pure/Thy/thy_read.ML |
2 ID: $Id$ |
2 ID: $Id$ |
3 Author: Sonia Mahjoub / Tobias Nipkow / L C Paulson / Carsten Clasohm |
3 Author: Carsten Clasohm and Markus Wenzel and Sonia Mahjoub and |
4 Copyright 1994 TU Muenchen |
4 Tobias Nipkow and L C Paulson |
5 |
5 Copyright 1994 TU Muenchen |
|
6 |
|
7 (* FIXME !? *) |
6 Reading and writing the theory definition files. |
8 Reading and writing the theory definition files. |
7 |
9 |
|
10 (* FIXME !? *) |
8 For theory XXX, the input file is called XXX.thy |
11 For theory XXX, the input file is called XXX.thy |
9 the output file is called .XXX.thy.ML |
12 the output file is called .XXX.thy.ML |
10 and it then tries to read XXX.ML |
13 and it then tries to read XXX.ML |
11 *) |
14 *) |
12 |
15 |
13 datatype thy_info = ThyInfo of {path: string, |
16 datatype thy_info = ThyInfo of {path: string, |
14 children: string list, |
17 children: string list, |
15 thy_time: string option, |
18 thy_time: string option, |
16 ml_time: string option, |
19 ml_time: string option, |
17 theory: Thm.theory option, |
20 theory: theory option, |
18 thms: thm Symtab.table}; |
21 thms: thm Symtab.table}; |
19 |
22 |
20 signature READTHY = |
23 signature READTHY = |
21 sig |
24 sig |
22 datatype basetype = Thy of string |
25 datatype basetype = Thy of string |
31 val time_use_thy : string -> unit |
34 val time_use_thy : string -> unit |
32 val unlink_thy : string -> unit |
35 val unlink_thy : string -> unit |
33 val base_on : basetype list -> string -> bool -> theory |
36 val base_on : basetype list -> string -> bool -> theory |
34 val store_theory : theory * string -> unit |
37 val store_theory : theory * string -> unit |
35 |
38 |
36 val store_thm : thm * string -> thm |
39 val theory_of_sign: Sign.sg -> theory |
37 val qed : string -> unit |
40 val theory_of_thm: thm -> theory |
38 val get_thm : theory -> string -> thm |
41 val store_thm: string * thm -> thm |
39 val get_thms : theory -> (string * thm) list |
42 val qed: string -> unit |
|
43 val get_thm: theory -> string -> thm |
|
44 val thms_of: theory -> (string * thm) list |
40 end; |
45 end; |
41 |
46 |
42 |
47 |
43 functor ReadthyFUN(structure ThySyn: THY_SYN): READTHY = |
48 functor ReadthyFUN(structure ThySyn: THY_SYN): READTHY = |
44 struct |
49 struct |
45 open Symtab; |
|
46 |
50 |
47 datatype basetype = Thy of string |
51 datatype basetype = Thy of string |
48 | File of string; |
52 | File of string; |
49 |
53 |
50 val loaded_thys = ref (make [("Pure", ThyInfo {path = "", children = [], |
54 val loaded_thys = ref (Symtab.make [("Pure", ThyInfo {path = "", children = [], |
51 thy_time = Some "", ml_time = Some "", |
55 thy_time = Some "", ml_time = Some "", |
52 theory = Some pure_thy, |
56 theory = Some pure_thy, |
53 thms = null})]); |
57 thms = Symtab.null})]); |
54 |
58 |
55 val loadpath = ref ["."]; (*default search path for theory files *) |
59 val loadpath = ref ["."]; (*default search path for theory files *) |
56 |
60 |
57 val delete_tmpfiles = ref true; (*remove temporary files after use *) |
61 val delete_tmpfiles = ref true; (*remove temporary files after use *) |
58 |
62 |
59 (*Make name of the output ML file for a theory *) |
63 (*Make name of the output ML file for a theory *) |
60 fun out_name tname = "." ^ tname ^ ".thy.ML"; |
64 fun out_name tname = "." ^ tname ^ ".thy.ML"; |
61 |
65 |
62 (*Read a file specified by thy_file containing theory thy *) |
66 (*Read a file specified by thy_file containing theory thy *) |
63 fun read_thy tname thy_file = |
67 fun read_thy tname thy_file = |
64 let |
68 let |
65 val instream = open_in thy_file; |
69 val instream = open_in thy_file; |
66 val outstream = open_out (out_name tname); |
70 val outstream = open_out (out_name tname); |
67 in |
71 in |
68 output (outstream, ThySyn.parse tname (input (instream, 999999))); |
72 output (outstream, ThySyn.parse tname (input (instream, 999999))); |
69 close_out outstream; |
73 close_out outstream; |
70 close_in instream |
74 close_in instream |
71 end; |
75 end; |
72 |
76 |
73 fun file_exists file = |
77 fun file_exists file = |
74 let val instream = open_in file in close_in instream; true end |
78 let val instream = open_in file in close_in instream; true end |
75 handle Io _ => false; |
79 handle Io _ => false; |
76 |
80 |
77 (*Get thy_info for a loaded theory *) |
81 (*Get thy_info for a loaded theory *) |
78 fun get_thyinfo tname = lookup (!loaded_thys, tname); |
82 fun get_thyinfo tname = Symtab.lookup (!loaded_thys, tname); |
79 |
83 |
80 (*Check if a theory was already loaded *) |
84 (*Check if a theory was already loaded *) |
81 fun already_loaded thy = |
85 fun already_loaded thy = |
82 let val t = get_thyinfo thy |
86 let val t = get_thyinfo thy |
83 in if is_none t then false |
87 in if is_none t then false |
84 else let val ThyInfo {thy_time, ml_time, ...} = the t |
88 else let val ThyInfo {thy_time, ml_time, ...} = the t |
85 in if is_none thy_time orelse is_none ml_time then false |
89 in if is_none thy_time orelse is_none ml_time then false |
86 else true |
90 else true |
87 end |
91 end |
88 end; |
92 end; |
89 |
93 |
90 (*Check if a theory file has changed since its last use. |
94 (*Check if a theory file has changed since its last use. |
91 Return a pair of boolean values for .thy and for .ML *) |
95 Return a pair of boolean values for .thy and for .ML *) |
92 fun thy_unchanged thy thy_file ml_file = |
96 fun thy_unchanged thy thy_file ml_file = |
93 let val t = get_thyinfo thy |
97 let val t = get_thyinfo thy |
94 in if is_some t then |
98 in if is_some t then |
95 let val ThyInfo {thy_time, ml_time, ...} = the t |
99 let val ThyInfo {thy_time, ml_time, ...} = the t |
96 val tn = is_none thy_time; |
100 val tn = is_none thy_time; |
97 val mn = is_none ml_time |
101 val mn = is_none ml_time |
98 in if not tn andalso not mn then |
102 in if not tn andalso not mn then |
99 ((file_info thy_file = the thy_time), |
103 ((file_info thy_file = the thy_time), |
100 (file_info ml_file = the ml_time)) |
104 (file_info ml_file = the ml_time)) |
101 else if not tn andalso mn then (true, false) |
105 else if not tn andalso mn then (true, false) |
102 else (false, false) |
106 else (false, false) |
103 end |
107 end |
104 else (false, false) |
108 else (false, false) |
110 specified.*) |
114 specified.*) |
111 fun find_file "" name = |
115 fun find_file "" name = |
112 let fun find_it (curr :: paths) = |
116 let fun find_it (curr :: paths) = |
113 if file_exists (tack_on curr name) then |
117 if file_exists (tack_on curr name) then |
114 tack_on curr name |
118 tack_on curr name |
115 else |
119 else |
116 find_it paths |
120 find_it paths |
117 | find_it [] = "" |
121 | find_it [] = "" |
118 in find_it (!loadpath) end |
122 in find_it (!loadpath) end |
119 | find_file path name = |
123 | find_file path name = |
120 if file_exists (tack_on path name) then tack_on path name |
124 if file_exists (tack_on path name) then tack_on path name |
121 else ""; |
125 else ""; |
122 |
126 |
123 (*Get absolute pathnames for a new or already loaded theory *) |
127 (*Get absolute pathnames for a new or already loaded theory *) |
124 fun get_filenames path name = |
128 fun get_filenames path name = |
125 let fun make_absolute file = |
129 let fun make_absolute file = |
126 if file = "" then "" else |
130 if file = "" then "" else |
127 if hd (explode file) = "/" then file else tack_on (pwd ()) file; |
131 if hd (explode file) = "/" then file else tack_on (pwd ()) file; |
128 |
132 |
129 fun new_filename () = |
133 fun new_filename () = |
130 let val found = find_file path (name ^ ".thy") |
134 let val found = find_file path (name ^ ".thy") |
131 handle FILE_NOT_FOUND => ""; |
135 handle FILE_NOT_FOUND => ""; |
164 end; |
168 end; |
165 |
169 |
166 (*Remove theory from all child lists in loaded_thys *) |
170 (*Remove theory from all child lists in loaded_thys *) |
167 fun unlink_thy tname = |
171 fun unlink_thy tname = |
168 let fun remove (ThyInfo {path, children, thy_time, ml_time, theory, thms}) = |
172 let fun remove (ThyInfo {path, children, thy_time, ml_time, theory, thms}) = |
169 ThyInfo {path = path, children = children \ tname, |
173 ThyInfo {path = path, children = children \ tname, |
170 thy_time = thy_time, ml_time = ml_time, |
174 thy_time = thy_time, ml_time = ml_time, |
171 theory = theory, thms = thms} |
175 theory = theory, thms = thms} |
172 in loaded_thys := mapst remove (!loaded_thys) end; |
176 in loaded_thys := Symtab.map remove (!loaded_thys) end; |
173 |
177 |
174 (*Remove a theory from loaded_thys *) |
178 (*Remove a theory from loaded_thys *) |
175 fun remove_thy tname = |
179 fun remove_thy tname = |
176 loaded_thys := make (filter_out (fn (id, _) => id = tname) |
180 loaded_thys := Symtab.make (filter_out (fn (id, _) => id = tname) |
177 (alist_of (!loaded_thys))); |
181 (Symtab.dest (!loaded_thys))); |
178 |
182 |
179 (*Change thy_time and ml_time for an existent item *) |
183 (*Change thy_time and ml_time for an existent item *) |
180 fun set_info thy_time ml_time tname = |
184 fun set_info thy_time ml_time tname = |
181 let val ThyInfo {path, children, theory, thms, ...} = |
185 let val ThyInfo {path, children, theory, thms, ...} = |
182 the (lookup (!loaded_thys, tname)); |
186 the (Symtab.lookup (!loaded_thys, tname)); |
183 in loaded_thys := update ((tname, |
187 in loaded_thys := Symtab.update ((tname, |
184 ThyInfo {path = path, children = children, |
188 ThyInfo {path = path, children = children, |
185 thy_time = Some thy_time, ml_time = Some ml_time, |
189 thy_time = Some thy_time, ml_time = Some ml_time, |
186 theory = theory, thms = thms}), !loaded_thys) |
190 theory = theory, thms = thms}), !loaded_thys) |
187 end; |
191 end; |
188 |
192 |
189 (*Mark theory as changed since last read if it has been completly read *) |
193 (*Mark theory as changed since last read if it has been completly read *) |
190 fun mark_outdated tname = |
194 fun mark_outdated tname = |
191 if already_loaded tname then set_info "" "" tname else (); |
195 if already_loaded tname then set_info "" "" tname else (); |
192 |
196 |
193 (*Read .thy and .ML files that haven't been read yet or have changed since |
197 (*Read .thy and .ML files that haven't been read yet or have changed since |
194 they were last read; |
198 they were last read; |
195 loaded_thys is a thy_info list ref containing all theories that have |
199 loaded_thys is a thy_info list ref containing all theories that have |
196 completly been read by this and preceeding use_thy calls. |
200 completly been read by this and preceeding use_thy calls. |
197 If a theory changed since its last use its children are marked as changed *) |
201 If a theory changed since its last use its children are marked as changed *) |
198 fun use_thy name = |
202 fun use_thy name = |
199 let val (path, tname) = split_filename name; |
203 let val (path, tname) = split_filename name; |
200 val (thy_file, ml_file) = get_filenames path tname; |
204 val (thy_file, ml_file) = get_filenames path tname; |
201 val (abs_path, _) = if thy_file = "" then split_filename ml_file |
205 val (abs_path, _) = if thy_file = "" then split_filename ml_file |
202 else split_filename thy_file; |
206 else split_filename thy_file; |
203 val (thy_uptodate, ml_uptodate) = thy_unchanged tname |
207 val (thy_uptodate, ml_uptodate) = thy_unchanged tname |
204 thy_file ml_file; |
208 thy_file ml_file; |
205 |
209 |
206 (*Set absolute path for loaded theory *) |
210 (*Set absolute path for loaded theory *) |
207 fun set_path () = |
211 fun set_path () = |
208 let val ThyInfo {children, thy_time, ml_time, theory, thms, ...} = |
212 let val ThyInfo {children, thy_time, ml_time, theory, thms, ...} = |
209 the (lookup (!loaded_thys, tname)); |
213 the (Symtab.lookup (!loaded_thys, tname)); |
210 in loaded_thys := update ((tname, |
214 in loaded_thys := Symtab.update ((tname, |
211 ThyInfo {path = abs_path, children = children, |
215 ThyInfo {path = abs_path, children = children, |
212 thy_time = thy_time, ml_time = ml_time, |
216 thy_time = thy_time, ml_time = ml_time, |
213 theory = theory, thms = thms}), |
217 theory = theory, thms = thms}), |
214 !loaded_thys) |
218 !loaded_thys) |
215 end; |
219 end; |
227 else () |
231 else () |
228 end; |
232 end; |
229 |
233 |
230 (*Remove all theorems associated with a theory*) |
234 (*Remove all theorems associated with a theory*) |
231 fun delete_thms () = |
235 fun delete_thms () = |
232 let val tinfo = case lookup (!loaded_thys, tname) of |
236 let val tinfo = case Symtab.lookup (!loaded_thys, tname) of |
233 Some (ThyInfo {path, children, thy_time, ml_time, theory, ...}) => |
237 Some (ThyInfo {path, children, thy_time, ml_time, theory, ...}) => |
234 ThyInfo {path = path, children = children, |
238 ThyInfo {path = path, children = children, |
235 thy_time = thy_time, ml_time = ml_time, |
239 thy_time = thy_time, ml_time = ml_time, |
236 theory = theory, thms = null} |
240 theory = theory, thms = Symtab.null} |
237 | None => ThyInfo {path = "", children = [], |
241 | None => ThyInfo {path = "", children = [], |
238 thy_time = None, ml_time = None, |
242 thy_time = None, ml_time = None, |
239 theory = None, thms = null}; |
243 theory = None, thms = Symtab.null}; |
240 in loaded_thys := update ((tname, tinfo), !loaded_thys) end; |
244 in loaded_thys := Symtab.update ((tname, tinfo), !loaded_thys) end; |
241 in if thy_uptodate andalso ml_uptodate then () |
245 in if thy_uptodate andalso ml_uptodate then () |
242 else |
246 else |
243 ( |
247 ( |
244 delete_thms (); |
248 delete_thms (); |
245 |
249 |
343 |
347 |
344 (*Show the cycle that would be created by add_child *) |
348 (*Show the cycle that would be created by add_child *) |
345 fun show_cycle base = |
349 fun show_cycle base = |
346 let fun find_it result curr = |
350 let fun find_it result curr = |
347 let val tinfo = get_thyinfo curr |
351 let val tinfo = get_thyinfo curr |
348 in if base = curr then |
352 in if base = curr then |
349 error ("Cyclic dependency of theories: " |
353 error ("Cyclic dependency of theories: " |
350 ^ child ^ "->" ^ base ^ result) |
354 ^ child ^ "->" ^ base ^ result) |
351 else if is_some tinfo then |
355 else if is_some tinfo then |
352 let val ThyInfo {children, ...} = the tinfo |
356 let val ThyInfo {children, ...} = the tinfo |
353 in seq (find_it ("->" ^ curr ^ result)) children |
357 in seq (find_it ("->" ^ curr ^ result)) children |
354 end |
358 end |
355 else () |
359 else () |
356 end |
360 end |
357 in find_it "" child end; |
361 in find_it "" child end; |
358 |
362 |
359 (*Check if a cycle would be created by add_child *) |
363 (*Check if a cycle would be created by add_child *) |
360 fun find_cycle base = |
364 fun find_cycle base = |
361 if base mem (list_descendants [child]) then show_cycle base |
365 if base mem (list_descendants [child]) then show_cycle base |
362 else (); |
366 else (); |
363 |
367 |
364 (*Add child to child list of base *) |
368 (*Add child to child list of base *) |
365 fun add_child base = |
369 fun add_child base = |
366 let val tinfo = |
370 let val tinfo = |
367 case lookup (!loaded_thys, base) of |
371 case Symtab.lookup (!loaded_thys, base) of |
368 Some (ThyInfo {path, children, thy_time, ml_time, |
372 Some (ThyInfo {path, children, thy_time, ml_time, |
369 theory, thms}) => |
373 theory, thms}) => |
370 ThyInfo {path = path, children = child ins children, |
374 ThyInfo {path = path, children = child ins children, |
371 thy_time = thy_time, ml_time = ml_time, |
375 thy_time = thy_time, ml_time = ml_time, |
372 theory = theory, thms = thms} |
376 theory = theory, thms = thms} |
373 | None => ThyInfo {path = "", children = [child], |
377 | None => ThyInfo {path = "", children = [child], |
374 thy_time = None, ml_time = None, |
378 thy_time = None, ml_time = None, |
375 theory = None, thms = null}; |
379 theory = None, thms = Symtab.null}; |
376 in loaded_thys := Symtab.update ((base, tinfo), !loaded_thys) end; |
380 in loaded_thys := Symtab.update ((base, tinfo), !loaded_thys) end; |
377 |
381 |
378 (*Load a base theory if not already done |
382 (*Load a base theory if not already done |
379 and no cycle would be created *) |
383 and no cycle would be created *) |
380 fun load base = |
384 fun load base = |
411 val mergelist = (unlink_thy child; |
415 val mergelist = (unlink_thy child; |
412 load_base bases); |
416 load_base bases); |
413 in writeln ("Loading theory " ^ (quote child)); |
417 in writeln ("Loading theory " ^ (quote child)); |
414 merge_thy_list mk_draft (map get_theory mergelist) end; |
418 merge_thy_list mk_draft (map get_theory mergelist) end; |
415 |
419 |
416 (*Change theory object for an existent item of loaded_thys |
420 (*Change theory object for an existent item of loaded_thys |
417 or create a new item *) |
421 or create a new item *) |
418 fun store_theory (thy, tname) = |
422 fun store_theory (thy, tname) = |
419 let val tinfo = case lookup (!loaded_thys, tname) of |
423 let val tinfo = case Symtab.lookup (!loaded_thys, tname) of |
420 Some (ThyInfo {path, children, thy_time, ml_time, thms, ...}) => |
424 Some (ThyInfo {path, children, thy_time, ml_time, thms, ...}) => |
421 ThyInfo {path = path, children = children, |
425 ThyInfo {path = path, children = children, |
422 thy_time = thy_time, ml_time = ml_time, |
426 thy_time = thy_time, ml_time = ml_time, |
423 theory = Some thy, thms = thms} |
427 theory = Some thy, thms = thms} |
424 | None => ThyInfo {path = "", children = [], |
428 | None => ThyInfo {path = "", children = [], |
425 thy_time = Some "", ml_time = Some "", |
429 thy_time = Some "", ml_time = Some "", |
426 theory = Some thy, thms = null}; |
430 theory = Some thy, thms = Symtab.null}; |
427 in loaded_thys := Symtab.update ((tname, tinfo), !loaded_thys) end; |
431 in loaded_thys := Symtab.update ((tname, tinfo), !loaded_thys) end; |
428 |
432 |
429 (*Store a theorem in the ThyInfo of the theory it is associated with*) |
433 |
430 fun store_thm (thm, tname) = |
434 |
431 let val thy_name = !(hd (stamps_of_thm thm)); |
435 (** store and retrieve theorems **) |
432 |
436 |
433 val ThyInfo {path, children, thy_time, ml_time, theory, thms} = |
437 (* thyinfo_of_sign *) |
434 case get_thyinfo thy_name of |
438 |
435 Some tinfo => tinfo |
439 fun thyinfo_of_sign sg = |
436 | None => error ("store_thm: Theory \"" ^ thy_name |
440 let |
437 ^ "\" is not stored in loaded_thys"); |
441 val ref xname = hd (#stamps (Sign.rep_sg sg)); |
438 in loaded_thys := |
442 val opt_info = get_thyinfo xname; |
439 Symtab.update ((thy_name, ThyInfo {path = path, children = children, |
443 |
440 thy_time = thy_time, ml_time = ml_time, |
444 fun eq_sg (ThyInfo {theory = None, ...}) = false |
441 theory = theory, |
445 | eq_sg (ThyInfo {theory = Some thy, ...}) = Sign.eq_sg (sg, sign_of thy); |
442 thms = Symtab.update ((tname, thm), thms)}), |
446 |
443 !loaded_thys); |
447 val show_sg = Pretty.str_of o Sign.pretty_sg; |
444 thm |
448 in |
445 end; |
449 if is_some opt_info andalso eq_sg (the opt_info) then |
446 |
450 (xname, the opt_info) |
447 (*Store theorem in loaded_thys and a ML variable*) |
451 else |
448 fun qed name = use_string ["val " ^ name ^ " = store_thm (result(), " |
452 (case Symtab.find_first (eq_sg o snd) (! loaded_thys) of |
449 ^ quote name ^ ");"]; |
453 Some name_info => name_info |
450 |
454 | None => error ("Theory " ^ show_sg sg ^ " not stored by loader")) |
451 fun name_of_thy thy = !(hd (stamps_of_thy thy)); |
455 end; |
452 |
456 |
453 (*Retrieve a theorem specified by theory and name*) |
457 |
454 fun get_thm thy tname = |
458 (* theory_of_sign, theory_of_thm *) |
455 let val thy_name = name_of_thy thy; |
459 |
456 |
460 fun theory_of_sign sg = |
457 val ThyInfo {thms, ...} = |
461 (case thyinfo_of_sign sg of |
458 case get_thyinfo thy_name of |
462 (_, ThyInfo {theory = Some thy, ...}) => thy |
459 Some tinfo => tinfo |
463 | _ => sys_error "theory_of_sign"); |
460 | None => error ("get_thm: Theory \"" ^ thy_name |
464 |
461 ^ "\" is not stored in loaded_thys"); |
465 val theory_of_thm = theory_of_sign o #sign o rep_thm; |
462 in the (lookup (thms, tname)) end; |
466 |
463 |
467 |
464 (*Retrieve all theorems belonging to the specified theory*) |
468 (* store theorems *) |
465 fun get_thms thy = |
469 |
466 let val thy_name = name_of_thy thy; |
470 (*store a theorem in the thy_info of its theory*) |
467 |
471 fun store_thm (name, thm) = |
468 val ThyInfo {thms, ...} = |
472 let |
469 case get_thyinfo thy_name of |
473 val (thy_name, ThyInfo {path, children, thy_time, ml_time, theory, thms}) = |
470 Some tinfo => tinfo |
474 thyinfo_of_sign (#sign (rep_thm thm)); |
471 | None => error ("get_thms: Theory \"" ^ thy_name |
475 val thms' = Symtab.update_new ((name, thm), thms) |
472 ^ "\" is not stored in loaded_thys"); |
476 handle Symtab.DUPLICATE s => error ("Duplicate theorem name " ^ quote s); |
473 in alist_of thms end; |
477 in |
|
478 loaded_thys := Symtab.update |
|
479 ((thy_name, ThyInfo {path = path, children = children, |
|
480 thy_time = thy_time, ml_time = ml_time, theory = theory, thms = thms'}), |
|
481 ! loaded_thys); |
|
482 thm |
|
483 end; |
|
484 |
|
485 (*store result of proof in loaded_thys and as ML value*) |
|
486 fun qed name = |
|
487 use_string ["val " ^ name ^ " = store_thm (" ^ quote name ^ ", result ());"]; |
|
488 |
|
489 |
|
490 (* retrieve theorems *) |
|
491 |
|
492 fun thmtab thy = |
|
493 let val (_, ThyInfo {thms, ...}) = thyinfo_of_sign (sign_of thy) |
|
494 in thms end; |
|
495 |
|
496 (*get a stored theorem specified by theory and name*) |
|
497 fun get_thm thy name = |
|
498 (case Symtab.lookup (thmtab thy, name) of |
|
499 Some thm => thm |
|
500 | None => raise THEORY ("get_thm: no theorem " ^ quote name, [thy])); |
|
501 |
|
502 (*get stored theorems of a theory*) |
|
503 val thms_of = Symtab.dest o thmtab; |
|
504 |
|
505 |
474 end; |
506 end; |
|
507 |