DYNAMICALLY CREATING MODULES, INSERTING RULES, AND DELETING RULES
=================================================================
Chang Zhao, Nov. 25, 2002

1. Syntax
(1) create a module
	newmodule{Module,TrailerType}
    or
	newmodule{Module}
where Module is an atom or a regular variable, so is TrailerType. At runtime,
the system will check to make sure Module is bound to some atom which is not 
an existing module name, and TrailerType is one of NONE, BASIC, FLOGIC

(2) insert a list of rules
	insertrule_a{RuleList}
    or
	insertrule_z{RuleList}
where RuleList is a comma-seperated list of rules in the form of
	Rule
    or
	(Rule)@Module
The syntax of Rule is the same as static rules expect that Rule doesn't have
the delimter '.'. If there are more than one rules in the list, each rule
should be included in a pair of parentheses. Module can be an atom, a regular
variable, or _@. At runtime, the system will check to make sure that Module
is bound to some already loaded module name.

(3) delete a list of rules
        deleterule_a{RuleList}
    or
        deleterule_z{RuleList}
    or
        deleterule{RuleList}
where RuleList is the same as rule insertion.

(4) table syntax
The compile directive table can't be used to table predicates in other modules.
To be able to table predicates in another module, the instruction
	?- table(Module, SpecList).
is used, where Module is an atom or a variable, and SpecList is a list of
specifications in the form of Pred/Arity. An example is
	?- table(mod1, (tc/1/2, e/2)).
When this command is executed, the module must have been loaded and there is
not a rule for that module satisfying both of the following:
    a) the head is subsumed by Pred/Arity
    b) when the rule is compiled/inserted, it's not tabled

(5) index syntax
Similarly, the compile directive index can't be used to index predicates in
other modules. But the instruct index can:
	?- index(Module, Arity-Position).
The module must be a loaded user module, and both Arity and Position are
positive integers with Arity>=Position. Assume A=Arity+1, P=Position+1,
this instruction will be translated into
	?- index(Wrap_dyna_hilog_of_Module/A,P).
	?- index(Wrap_dynz_hilog_of_Module/A,P).

(6) example
A simple example is as follows:
e(a,b). e(b,c). e(c,d).
?- newmodule{mod1}.
?- table(mod1, tc/2).
?- M=mod1, insertrule_a{((tc(X,Y) :- e(X,Y)@_@)@M), 
		        ((tc(X,Y) :- tc(X,Z),e(Z,Y)@_@)@M)
		       }.
?- tc(X,Y)@mod1.
?- deleterule_a{(tc(X,Y) :- tc(X,Z),e(Z,Y)@_@)@mod1}.
?- refresh{tc(X,Y)}.
?- tc(X,Y)@mod1.
?- M=mod1, deleterule_a{(tc(X,Y) :- P@SomeModule)@M}. 
?- refresh{tc(X,Y)}.
?- tc(X,Y)@mod1.


2. Implementation
(1) new wrappers
For hilog predicates, we have new wrappers WRAP_DYNA_HILOG, WRAP_DYNZ_HILOG,
and WRAP_TDYN_HILOG. WRAP_DYNA_HILOG is used to wrap hilog rule heads which
are inserted by insertrule_a. WRAP_DYNZ_HILOG is for those inserted by
insertrule_z. WRAP_TDYN_HILOG is for dynamically inserted rules whose heads
are tabled.  

For flogic predicates, similarly we have WRAP_DYNA_... and WRAP_DYNZ_... 
For example, for WRAP_ISA, we have added WRAP_DYNA_ISA and WRAP_DYNZ_ISA.

(2) patch rules
Let's take flapply/2 and isa/2 as examples to show patch rules for hilog and
flogic.

Between the patch rules from flrpatch.flh which are bridges to the fdb storage
and the user program body, the following codes are added:
?- abolish(WRAP_DYNA_HILOG/2).
?- abolish(WRAP_DYNZ_HILOG/2).
:- dynamic WRAP_DYNA_HILOG/2,WRAP_DYNZ_HILOG/2.
WRAP_HILOG(X1,X2) :- WRAP_DYNA_HILOG(X1,X2).

?- abolish(WRAP_TDYN_HILOG/3).
:- table WRAP_TDYN_HILOG/3.
:- dynamic WRAP_TDYN_HILOG/3.
/* the purpose of the rule below is to make table_state/2@flora(sys) work
WRAP_TABLED_HILOG(0,X1,X2) :- WRAP_TDYN_HILOG(_RuleNum,X1,X2).

?- abolish(WRAP_DYNA_ISA/2).
?- abolish(WRAP_DYNZ_ISA/2).
:- dynamic WRAP_DYNA_ISA/2, WRAP_DYNZ_ISA/2.
derived_isa(X,Y) :- WRAP_DYNA_ISA(X,Y).

After the user program body, the following rules are added between trailer
rules and rules for undefinedness checking:
WRAP_HILOG(X1,X2) :- WRAP_DYNZ_HILOG(X1,X2).
derived_isa(X,Y) :- WRAP_DYNZ_ISA(X,Y).

(3) flora_rule_signature(Head,BodySignature,RuleList,BridgeRuleList)
We maintain a flora_rule_signature record for each dynamic rule. When the rule
is inserted, its signature is inserted into flora_rule_signature. When it is
deleted, its signature is deleted from flora_rule_signature. This is necessary
mainly because of two reasons:
    a) A rule inserted may correspond to as many as three rules because of
    conjuction in rule head and tabling. For example
        :- table p/1.
        ?- insertrule_a{(p(X),q(X)) :- r(X)}.
    the rule p(X):-r(X) is actually translated into three rules:
        newpredicate1(X) :- r(X).
        flapply(p,X) :- tabled_flapply(p,X,1).
        tabled_flapply(p,X,1) :- newpredicate1(X).
    In this case, BridgeRuleList is [newpredicate1(X):-r(X)], and RuleList is
    [(flapply(p,X):-tabled_flapply(p,X,1)),
    (tabled_flapply(p,X,1):-newpredicate(X))].

    b) We want the dynamic rule p(X) :- p(X)@foo be deleted when the user issues
        ?- deleterule_a{p(X) :- p(X)@M}.
    To make 'usermod'foo'flapply(p,X) unify with fllibmodlit(flapply,[p,X],M),
    we build the canonical form BodySignature of rule body when the rule is
    inserted. When a rule is to be deleted, its canonical form is buit and
    match with those in flora_rule_signature. BodySignature takes the form:
        (Wrap,Args,Module,Callable)
    Some examples are as follows:
        p(a)@foo         canonical: (flapply,[p,a],foo,p(a)@foo)
        X@Y              canonical: (_P,_A,Y,X)
        a:b@X            canonical: (d_isa,[a,b],X,_Call)

Notice that flora_rule_signature contains Head instead of HeadSigature,
because it is required that the predicate name and module name of the rule
head be bound when the rule is to be deleted.

(4) compilation
Similar to insert and delete, newmodule and insertrule_a (insertrule_z) are
compiled into system calls FLLIBNEWMODULE and FLLIBINSERTRULE_A
(FLLIBINSERTRULE_Z), respectively. Something noticeable is that the split of
rules does not occur at compile time. For example, 
	o:c[a->v] :- Body
is not split into
	newpredicate :- Body
	o:c :- newpredicate
	o[a->v] :- newpredicate
at compile time, but run time to insure the uniqueness of newpredicate. So
after compilation, the head of a rule is a list with possibly more than
one elements.

The distribution of workspace occurs at compile time. FLLIBMODLIT is still used
for variable module name.

(5) execution
FLLIBNEWMODULE(Module) is treated as FLLIBNEWMODULE(Module,NONE). 
FLLIBNEWMODULE(Module,TrailerType) first checks to make sure Module is bound
to an atom which is not an existing module name. Then it compiles and loads
trailer/patch.P for the given module name. At last it loads the correct trailer.

FLLIBINSERTRULE_A(RuleList) does the following for each element Rule in the
list RuleList, where Rule is encoded as FLSYSRULEUPDATE(HeadList,Body):
    a) build the canonical form of the rule body
    b) remove fllibmodlit in Body where module name is already bound. Currently
this is only done for the subgoals and control constructs but not for meta
arguments.
    c) if there is only one element Head in HeadList, go to c). else generate
a new predicate in the same module as those in the HeadList. Its arguments
are all the variables in Body. A global counter is maintained to generate
new predicates. Assert a rule whose head is this new predicate and the body
is Body. For each element Head in HeadList and the new predicate, do c).
    d) for a rule to be inserted with Head and Body, first get rid of 
fllibmodlit from Head if any. Then get the module name from Head and checks
whether it is a loaded user module. The Head might be 
	* a tabled hilog predicate Pred(Args)
	  In this case, compute a new dynamic rule number RN and assert the
	  following rules:
	  WRAP_DYNA_HILOG(Pred,Args) :- WRAP_TDYN_HILOG(RN,Pred,Args).
	  WRAP_TDYN_HILOG(RN,Pred,Args) :- Body.
	  A global counter is maintained for RN.
	* a hilog predicate Pred(Args) which is not tabled
	  In this cse, assert the following rule and enter_not_tabled_registry
	  with the fingerprint of the Head:
	  WRAP_DYNA_HILOG(Pred,Args) :- Body.
	* otherwise just assert
	  Head :- Body.
    e) insert a record into flora_rule_signature
    f) insert the skeleton of the head into corresponding fld trie.

FLLIBINSERTRULE_Z(RuleList) is almost the same as FLLIBINSERTRULE_A(RuleList),
but just substitute WRAP_DYNA_HILOG with WRAP_DYNZ_HILOG.

FLLIBDELETERULE_A(RuleList) and FLLIBDELETERULE_Z(RuleList) does the following:
        for each element FLSYSRULEUPDATE(HeadList,Body) in RuleList
            build the canonical form BodySig of Body
            for each element Head in HeadList
                find a flora_rule_signature record with the Head and BodySig
                delete related rules as directed by the record
            end for
        end for
FLLIBDELETERULE(RuleList) :- FLLIBDELETERULE_A(RuleList).
FLLIBDELETERULE(RuleList) :- FLLIBDELETERULE_Z(RuleList).

3. What's left
(1) cut in rules inserted by insertrule_a doesn't not work properly
    

