1. IMPLEMENTATION OF THE MECHANISM FOR CATCHING UNDEFINED PREDICATE/METHODS ALGORITHM 1) When compiling a flora program with debug option turned on, we take the intermediate representation of the program from the compiler, compute skeletons for rule heads and facts (see below), and remove duplicates. These skeletons are then dumped into a file with extension .fld. When the program is loaded, the skeletons are loaded into the fld storage trie of the corresponding module. 2) The basic idea is to add patch rules to trap undefined predicates. For example, if we have the following patch rules: flapply(X) :- storage_find_fact(fldstorage, flapply(X)), !, fail. flapply(X) :- flora_error_undefined(flapply(X)), abort. All undefined HiLog predicates whose arities are 0 will be trapped. Such rules can be found in genincludes/flrpreddef.flh. Unfortunately, this doesn't work for F-logic predicates because of the inheritance rules. For example, in closure/flrtrailer.fli, we have isa(O,C) :- silent_sub(S,C), inferred_isa(O,S). It is possible that sub(_,C) is not defined, we will get an error message saying "_::C" is undefined when the query is "_:C". We can handle this problem in the following way: F-logic wrappers in the bodies of user program rules and queries (except in database operations) are prefixed by 'd_'. This has the effect that the wrappers in the rule bodies in the program (e.g., d_isa/2) are different from the wrappers in the rule bodies in the trailers (e.g., isa/2). The undefinedness checking rules (below) are then attached to the d_* versions of the wrappers, but not to the wrappers used in the trailer. As a result, Flora won't complain that trailer predicates are undefined (because they are allowed to). a) When debug option is turned on, we need patch rules such as: check_undefined(O,C) :- storage_find_fact(fldstorage,isa(O,C)), !, fail. check_undefined(O,C) :- storage_find_fact(fldstorage,sub(O,C)), !, fail. check_undefined(_,_). d_isa(O,C) :- check_undefined(O,C), !, flora_error_undefined(isa(O,C)), abort. d_isa(O,C) :- isa(O,C). These patch rules are in genincludes/flrpreddef.flh; they are generated from templates in genincludes/flrpreddef.fli. b) When debug option is turned off, we need patch rules such as: d_isa(O,C) :- isa(O,C). These patch rules are in genincludes/flrnopreddef.flh. If a flora program has the debug option turned on, its .P file will include flrpreddef.flh in the end; otherwise, its .P file will include flrnopreddef.flh in the end. 3) When insert, insertall, btinsert, btinsertall is executed, we need to compute the skeleton(s) of the inserted fact(s) and insert the skeleton(s) into the fld storage. 4) debug option is local to module. This is done by keeping track of the loaded modules that require debugging in flora_debug_module_registry ALGORITHM REVISED 1) When compiling a flora program, we take the intermediate representation of the program from the compiler, compute skeletons for rule heads and facts (see below), and remove duplications. These skeletons will be dumped into a file with extension .fld. When this program is loaded, the skeletons will be loaded into the fld storage trie of the corresponding module. 2) The basic idea is to add patch rules to trap undefined predicates. For hilog predicates, we have patch rules such as: flapply(X) :- this_module_is_in_debugging_mode, storage_find_fact(fldstorage, flapply(X)), !, fail. flapply(X) :- this_module_is_in_debugging_mode, flora_error_undefined(flapply(X)). where * this_module_is_in_debugging_mode succeeds only when the debug option of the module is turned on. See section 3) for more detail. * flora_error_undefined handles the case where the skeleton of the predicate never appears in any rule head or fact. It's not necessarily undefined because of database operations. See section 4). With thses two rules, all undefined HiLog predicates whose arities are 0 will be trapped when the debug option is turned on. Such patch rules can be found in genincludes/flrpreddef.fli. Unfortunately, this doesn't work for F-logic predicates because of the inheritance rules. For example, in closure/flrtrailer.fli, we have isa(O,C) :- silent_sub(S,C), inferred_isa(O,S). It is possible that sub(_,C) is not defined, we will get an error message saying "_::C" is undefined when the query is "_:C". We can handle this problem in the following way: F-logic wrappers in the bodies of user program rules and queries (except in database operations) are prefixed by 'd_'. This has the effect that the wrappers in the rule bodies in the program (e.g., d_isa/2) are different from the wrappers in the rule bodies in the trailers (e.g., isa/2). The undefinedness checking rules (below) are then attached to the d_* versions of the wrappers, but not to the wrappers used in the trailer. As a result, Flora won't complain that trailer predicates are undefined (because they are allowed to). Patch rules for trapping undefined isa are: d_isa(X,Y) :- this_module_is_in_debugging_mode, not storage_find_fact(fldstorage, isa(X,Y)), not storage_find_fact(fldstorage, sub(X,Y)), flora_error_undefined(isa(X,Y)). d_isa(X,Y) :- isa(X,Y). Such rules are also in genincludes/flrpreddef.fli. flWrapAround('flrpreddef.fli') generates flrpreddef.flh, which is included in the end of .P files 3) Debug option is local to module and can be dynamically turned on/off. This is done by keeping track of the loaded modules that require debugging in flora_debug_module_registry. In the system module flora(sys), we have debug[#check_undefined(Flag)] debug[#check_undefined(Flag,Module)] to query, turn on, or turn off the debug option of a certain module or all modules. 4) Insert operations(insert, insertall, btinsert, btinsertall) may produce some new skeletons which are not in any fact or rule head. If we check and insert skeletons for every insert operation, insert operations will be slowed down a lot. We can do it in a lazy way as follows. When flora_error_undefined(P) is called, we compute the skeleton of P, check whether there is a fact in the fdb storage that have the same skeleton. If so, build the skeleton of the fact, insert it into fld storage, then fail; otherwise, throw FLORA_UNDEFINED_EXCEPTION(P,ErrorMessage). SKELETONS A skeleton represents a generalization of the terms in the rule heads. For instance, for p(X)(a,Z)(b,D) :- ... the skeleton is flapply(flapply(flapply(p,_),_,_) _, _) for Z(X)(c,Z)(c,D) :- ... it is flapply(flapply(flapply(_,_),_,_) _, _) For F-logic molecules, say, T[Z(X)(c,Z)(c,D) -> ...] :- ... Q[p(X)(a,Z)(b,D) ->> ...] :- ... you put into the trie the following skeletons: fd(_, flapply(flapply(flapply(_,_),_,_) _, _), _) mvd(_, flapply(flapply(flapply(p,_),_,_) _, _), _) 2. IMPLEMENTATION OF NUMBERED ANONYMOUS OID SYNTAX The goal is to allow _#1, _#2, in addition to _#. The idea is that two occurrences of, say, _#2 within the same rule would be assigned the same oid and _#1,_#2, _# would be different. Also, occurrences of _#1 across different rules would be different as well. The implementation is as follows. First, numbered (_#1, _#2) etc and unnumbered (_#) oids should use difference naming schemas. The numbered oids can use, say, _$_$_flora_newoid|. For instance, _#345 that appears in the clause number 9876 would be replaced with _$_$_flora_newoid9876|345. We should also check that each numbered oid that occurs inside a rule is referenced there at least twice and issue a warning if not. This can be done by asserting ruleoid(345,1) when the compiler sees _#345 within the clause 9876 the first time. While processing that rule, the compiler would increment the count. For instance, in our case it would retract the above fact and assert ruleoid(345,2) When it is done working with the rule, it should check if there are facts of the form ruleoid(_,1) and issue an error if there are. The ruleoid/2 relation is reset by the compiler at the beginning of each new rule. 3. Translation of @prologall() p(X,Y)@prologall() becomes: flora_plg2hilog(NewX,X,flapply,0), % don't unify vars flora_plg2hilog(NewY,Y,flapply,0), p(NewX,NewY), flora_post_p2h_convertion(NewX,X), % unify vars flora_post_p2h_convertion(NewY,Y) This works because: a. flora_plg2hilog(NewX,X,0) creates a new variable NewX and doesn't unify it with X b. p/2 is evaluated with respect to the new variables c. The resulting bindings for NewX, NewY are converted to hilog and unified with X,Y. d. If X or Y are already prolog then NewX, NewY are unified as Prolog predicates. This is because flora_post_p2h_convertion/2 is defined as flora_post_p2h_convertion(PrologTerm,HiLogTerm) :- (flora_plg2hlg(PrologTerm,HiLogTerm,WRAP_HILOG,P2H_UNIFY_VARS), ! ; PrologTerm = HiLogTerm ). In our situation, flora_plg2hlg/4 fails ONLY if HiLogTerm (i.e., X or Y) were originally Prolog terms.