All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jason.asSemantics.TransitionSystem Maven / Gradle / Ivy

Go to download

Jason is a fully-fledged interpreter for an extended version of AgentSpeak, a BDI agent-oriented logic programming language.

There is a newer version: 2.3
Show newest version
// ----------------------------------------------------------------------------
// Copyright (C) 2003 Rafael H. Bordini, Jomi F. Hubner, et al.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// To contact the authors:
// http://www.inf.ufrgs.br/~bordini
// http://www.das.ufsc.br/~jomi
//
//----------------------------------------------------------------------------

package jason.asSemantics;

import jason.JasonException;
import jason.NoValueException;
import jason.RevisionFailedException;
import jason.architecture.AgArch;
import jason.asSemantics.GoalListener.FinishStates;
import jason.asSyntax.ASSyntax;
import jason.asSyntax.Atom;
import jason.asSyntax.BinaryStructure;
import jason.asSyntax.InternalActionLiteral;
import jason.asSyntax.ListTerm;
import jason.asSyntax.ListTermImpl;
import jason.asSyntax.Literal;
import jason.asSyntax.LiteralImpl;
import jason.asSyntax.LogicalFormula;
import jason.asSyntax.NumberTerm;
import jason.asSyntax.NumberTermImpl;
import jason.asSyntax.Plan;
import jason.asSyntax.PlanBody;
import jason.asSyntax.PlanBody.BodyType;
import jason.asSyntax.PlanLibrary;
import jason.asSyntax.StringTermImpl;
import jason.asSyntax.Structure;
import jason.asSyntax.Term;
import jason.asSyntax.Trigger;
import jason.asSyntax.Trigger.TEOperator;
import jason.asSyntax.Trigger.TEType;
import jason.asSyntax.UnnamedVar;
import jason.asSyntax.VarTerm;
import jason.asSyntax.parser.ParseException;
import jason.bb.BeliefBase;
import jason.jeditplugin.Config;
import jason.runtime.Settings;
import jason.stdlib.add_nested_source;
import jason.stdlib.desire;
import jason.stdlib.fail_goal;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;


public class TransitionSystem {

    public enum State { StartRC, SelEv, RelPl, ApplPl, SelAppl, FindOp, AddIM, ProcAct, SelInt, ExecInt, ClrInt }
    
    private Logger        logger     = null;

    private Agent         ag         = null;
    private AgArch        agArch     = null;
    private Circumstance  C          = null;
    private Settings      setts      = null;
    private State         step       = State.StartRC; // first step of the SOS                                                                                                
    private int           nrcslbr    = Settings.ODefaultNRC; // number of reasoning cycles since last belief revision    
    
    private boolean       sleepingEvt    = false;
    
    private List  goalListeners = null;
    
    // both configuration and configuration' point to this
    // object, this is just to make it look more like the SOS
    private TransitionSystem      confP;
    private TransitionSystem      conf;

    private Queue taskForBeginOfCycle = new ConcurrentLinkedQueue();
    
    public TransitionSystem(Agent a, Circumstance c, Settings s, AgArch ar) {
        ag     = a;
        agArch = ar;
        
        if (s == null)
            setts = new Settings();
        else
            setts = s;

        if (c == null)
            C = new Circumstance();
        else
            C = c;
        C.setTS(this);
        
        // we need to initialise this "aliases"
        conf = confP = this;

        nrcslbr = setts.nrcbp(); // to do BR to start with

        setLogger(agArch);
        if (setts != null && setts.verbose() >= 0)
            logger.setLevel(setts.logLevel());
        
        if (a != null)
            a.setTS(this);
        
        if (ar != null)
            ar.setTS(this);
    }

    public void setLogger(AgArch arch) {
        if (arch != null)
            logger = Logger.getLogger(TransitionSystem.class.getName() + "." + arch.getAgName());
        else
            logger = Logger.getLogger(TransitionSystem.class.getName());
    }
    public void setLogger(Logger l) {
        logger = l;
    }
    
    // ---------------------------------------------------------
    //    Goal Listeners support methods
    
    private Map listenersMap; // map the circumstance listeners created for the goal listeners, used in remove goal listener
    
    /** adds an object that will be notified about events on goals (creation, suspension, ...) */
    public void addGoalListener(final GoalListener gl) {
        if (goalListeners == null) {
            goalListeners = new ArrayList();
            listenersMap  = new HashMap();
        } else { 
            // do not install two MetaEventGoalListener
            for (GoalListener g: goalListeners)
                if (g instanceof GoalListenerForMetaEvents)
                    return;
        }
        
        // we need to add a listener in C to map intention events to goal events
        CircumstanceListener cl = new CircumstanceListener() {
            
            public void intentionDropped(Intention i) {
                for (IntendedMeans im: i) //.getIMs()) 
                    if (im.getTrigger().isAddition() && im.getTrigger().isGoal()) 
                        gl.goalFinished(im.getTrigger(), FinishStates.dropped);                         
            }
            
            public void intentionSuspended(Intention i, String reason) {
                for (IntendedMeans im: i) //.getIMs()) 
                    if (im.getTrigger().isAddition() && im.getTrigger().isGoal()) 
                        gl.goalSuspended(im.getTrigger(), reason);                         
            }
            
            public void intentionResumed(Intention i) {
                for (IntendedMeans im: i) //.getIMs()) 
                    if (im.getTrigger().isAddition() && im.getTrigger().isGoal()) 
                        gl.goalResumed(im.getTrigger());                         
            }
            
            public void eventAdded(Event e) {
                if (e.getTrigger().isAddition() && e.getTrigger().isGoal())
                    gl.goalStarted(e);
            }
            
            public void intentionAdded(Intention i) {  }
        };
        C.addEventListener(cl);
        listenersMap.put(gl,cl);

        goalListeners.add(gl);
    }
    
    public boolean hasGoalListener() {
        return goalListeners != null && !goalListeners.isEmpty();
    }
    
    public List getGoalListeners() {
        return goalListeners;
    }
    
    public boolean removeGoalListener(GoalListener gl) {
        CircumstanceListener cl = listenersMap.get(gl);
        if (cl != null) C.removeEventListener(cl);
        return goalListeners.remove(gl);
    }

    /** ******************************************************************* */
    /* SEMANTIC RULES */
    /** ******************************************************************* */
    
    private void applySemanticRule() throws JasonException {
        // check the current step in the reasoning cycle
        // only the main parts of the interpretation appear here
        // the individual semantic rules appear below

        switch (step) {
        case StartRC:   applyProcMsg(); break;
        case SelEv:     applySelEv(); break;
        case RelPl:     applyRelPl();  break;
        case ApplPl:    applyApplPl(); break;
        case SelAppl:   applySelAppl(); break;
        case FindOp:    applyFindOp(); break;
        case AddIM:     applyAddIM(); break;
        case ProcAct:   applyProcAct(); break;
        case SelInt:    applySelInt(); break;
        case ExecInt:   applyExecInt(); break;
        case ClrInt:    confP.step = State.StartRC;
                        applyClrInt(conf.C.SI);
                        break;
        }
    }

    // the semantic rules are referred to in comments in the functions below

    private final String kqmlReceivedFunctor = Config.get().getKqmlFunctor();
    
    private void applyProcMsg() throws JasonException {
        confP.step = State.SelEv;
        if (conf.C.hasMsg()) {
            Message m = conf.ag.selectMessage(conf.C.getMailBox());
            if (m == null) return;
            
            // get the content, it can be any term (literal, list, number, ...; see ask)
            Term content = null;
            if (m.getPropCont() instanceof Term) {
                content = (Term)m.getPropCont();
            } else {
                try {
                    content = ASSyntax.parseTerm(m.getPropCont().toString());
                } catch (ParseException e) {
                    logger.warning("The content of the message '"+m.getPropCont()+"' is not a term!");
                    return;
                }
            }

            // check if an intention was suspended waiting this message
            Intention intention = null;
            if (m.getInReplyTo() != null) {
                intention = getC().getPendingIntentions().get(m.getInReplyTo());
            }
            // is it a pending intention?
            if (intention != null) {
                // unify the message answer with the .send fourth argument.
                // the send that put the intention in Pending state was
                // something like
                //    .send(ag1,askOne, value, X)
                // if the answer was tell 3, unifies X=3
                // if the answer was untell 3, unifies X=false
                Structure send = (Structure)intention.peek().getCurrentStep().getBodyTerm();
                if (m.isUnTell() && send.getTerm(1).toString().equals("askOne")) {
                    content = Literal.LFalse;
                } else if (content.isLiteral()) { // adds source in the content if possible
                    content = ((Literal)content).forceFullLiteralImpl();
                    ((Literal)content).addSource(new Atom(m.getSender()));
                } else if (send.getTerm(1).toString().equals("askAll") && content.isList()) { // adds source in each answer if possible
                    ListTerm tail = new ListTermImpl();
                    for (Term t: ((ListTerm)content)) {
                        if (t.isLiteral()) {
                            t = ((Literal)t).forceFullLiteralImpl();
                            ((Literal)t).addSource(new Atom(m.getSender()));
                        }
                        tail.append(t);
                    }
                    content = tail;
                }
                
                // test the case of sync ask with many receivers
                Unifier un = intention.peek().getUnif();
                Term rec = send.getTerm(0).capply(un);
                if (rec.isList()) { // send to many receivers
                    // put the answers in the unifier
                    VarTerm answers = new VarTerm("AnsList___"+m.getInReplyTo());
                    ListTerm listOfAnswers = (ListTerm)un.get(answers);
                    if (listOfAnswers == null) {
                        listOfAnswers = new ListTermImpl();
                        un.unifies(answers, listOfAnswers);
                    }
                    listOfAnswers.append(content);
                    int nbReceivers = ((ListTerm)send.getTerm(0)).size();
                    if (listOfAnswers.size() == nbReceivers) { // all agents have answered
                        resumeSyncAskIntention(m.getInReplyTo(), send.getTerm(3), listOfAnswers);
                    }
                } else {
                    resumeSyncAskIntention(m.getInReplyTo(), send.getTerm(3), content);
                }

                // the message is not an ask answer
            } else if (conf.ag.socAcc(m)) {

                if (! m.isReplyToSyncAsk()) { // ignore answer after the timeout
                    // generate an event
                    String sender = m.getSender();
                    if (sender.equals(getUserAgArch().getAgName()))
                        sender = "self";
                    
                    boolean added = false;
                    if (!setts.isSync() && !ag.getPL().hasUserKqmlReceivedPlans() && content.isLiteral() && !content.isList()) { // optimisation to jump kqmlPlans
                        if (m.getIlForce().equals("achieve") ) {                             
                            content = add_nested_source.addAnnotToList(content, new Atom(sender));
                            C.addEvent(new Event(new Trigger(TEOperator.add, TEType.achieve, (Literal)content), Intention.EmptyInt));
                            added = true;                            
                        } else if (m.getIlForce().equals("tell") ) {
                            content = add_nested_source.addAnnotToList(content, new Atom(sender));
                            getAg().addBel((Literal)content);
                            added = true;
                        }
                    }
                    
                    if (!added) {
                        Literal received = new LiteralImpl(kqmlReceivedFunctor).addTerms(
                                new Atom(sender),
                                new Atom(m.getIlForce()),
                                content,
                                new Atom(m.getMsgId()));
                        
                        updateEvents(new Event(new Trigger(TEOperator.add, TEType.achieve, received), Intention.EmptyInt));
                    }
                } else {
                    logger.fine("Ignoring message "+m+" because it is received after the timeout.");
                }
            }
        }
    }
    
    private void resumeSyncAskIntention(String msgId, Term answerVar, Term answerValue) throws JasonException {
        Intention i = getC().removePendingIntention(msgId);
        i.peek().removeCurrentStep(); // removes the .send in the plan body
        if (i.peek().getUnif().unifies(answerVar, answerValue)) {
            getC().resumeIntention(i);
        } else {
            generateGoalDeletion(i, JasonException.createBasicErrorAnnots("ask_failed", "reply of an ask message ('"+answerValue+"') does not unify with fourth argument of .send ('"+answerVar+"')"));
        }                        
        
    }

    private void applySelEv() throws JasonException {
        
        // Rule for atomic, if there is an atomic intention, do not select event
        if (C.hasAtomicIntention()) {
            confP.step = State.ProcAct; // need to go to ProcAct to see if an atomic intention received a feedback action
            return;            
        }

        // Rule for atomic, events from atomic intention have priority
        confP.C.SE = C.removeAtomicEvent();
        if (confP.C.SE != null) {
            confP.step = State.RelPl;
            return;
        }

        if (conf.C.hasEvent()) {
            // Rule SelEv1
            confP.C.SE = conf.ag.selectEvent(confP.C.getEvents());
            if (logger.isLoggable(Level.FINE)) 
                logger.fine("Selected event "+confP.C.SE);
            if (confP.C.SE != null) {
                if (ag.hasCustomSelectOption() || setts.verbose() == 2) // verbose == 2 means debug mode 
                    confP.step = State.RelPl;
                else 
                    confP.step = State.FindOp;
                return;
            }
        }
        // Rule SelEv2
        // directly to ProcAct if no event to handle
        confP.step = State.ProcAct;
    }

    private void applyRelPl() throws JasonException {
        // get all relevant plans for the selected event
        confP.C.RP = relevantPlans(conf.C.SE.trigger);

        // Rule Rel1
        if (confP.C.RP != null || setts.retrieve()) 
            // retrieve is mainly for Coo-AgentSpeak
            confP.step = State.ApplPl;
        else
            applyRelApplPlRule2("relevant");
    }
    
    private void applyApplPl() throws JasonException {
        confP.C.AP = applicablePlans(confP.C.RP);

        // Rule Appl1
        if (confP.C.AP != null || setts.retrieve()) 
            // retrieve is mainly for Coo-AgentSpeak
            confP.step = State.SelAppl;
        else
            applyRelApplPlRule2("applicable");
    }

    /** generates goal deletion event */
    private void applyRelApplPlRule2(String m) throws JasonException {
        confP.step = State.ProcAct; // default next step
        if (conf.C.SE.trigger.isGoal() && !conf.C.SE.trigger.isMetaEvent()) {
            // can't carry on, no relevant/applicable plan.
            try {
                if (conf.C.SE.getIntention() != null && conf.C.SE.getIntention().size() > 3000) {
                    logger.warning("we are likely in a problem with event "+conf.C.SE.getTrigger()+" the intention stack has already "+conf.C.SE.getIntention().size()+" intended means!");                     
                } 
                String msg = "Found a goal for which there is no "+m+" plan:" + conf.C.SE.getTrigger();
                if (!generateGoalDeletionFromEvent(JasonException.createBasicErrorAnnots("no_"+m, msg))) { 
                    logger.warning(msg);
                }
            } catch (Exception e) {
                e.printStackTrace();
                return;
            }
                            
        } else if (conf.C.SE.isInternal()) {
            // e.g. belief addition as internal event, just go ahead
            // but note that the event was relevant, yet it is possible
            // the programmer just wanted to add the belief and it was
            // relevant by chance, so just carry on instead of dropping the
            // intention
            confP.C.SI = conf.C.SE.intention;
            joinRenamedVarsIntoIntentionUnifier(confP.C.SI.peek(), confP.C.SI.peek().unif);
            updateIntention();
        } else if (setts.requeue()) {  
            // if external, then needs to check settings
            confP.C.addEvent(conf.C.SE);
        } else {
            // current event is external and irrelevant,
            // discard that event and select another one
            confP.step = State.SelEv;
        }
    }
    

    private void applySelAppl() throws JasonException {
        // Rule SelAppl
        confP.C.SO = conf.ag.selectOption(confP.C.AP);

        if (confP.C.SO != null) {
            confP.step = State.AddIM;
            if (logger.isLoggable(Level.FINE)) logger.fine("Selected option "+confP.C.SO+" for event "+confP.C.SE);
        } else {
            logger.fine("** selectOption returned null!");
            generateGoalDeletionFromEvent(JasonException.createBasicErrorAnnots("no_option", "selectOption returned null"));
            // can't carry on, no applicable plan.
            confP.step = State.ProcAct;
        }
    }

    /**
     * This step is new in Jason 1.1 and replaces the steps RelPl->ApplPl->SelAppl when the user
     * does not customise selectOption. This version does not create the RP and AP lists and thus 
     * optimise the reasoning cycle. It searches for the first option and automatically selects it.
     * 
     * @since 1.1
     */
    private void applyFindOp() throws JasonException {
        confP.step = State.AddIM; // default next step
        
        // get all relevant plans for the selected event
        //Trigger te = (Trigger) conf.C.SE.trigger.clone();
        List candidateRPs = conf.ag.pl.getCandidatePlans(conf.C.SE.trigger);
        if (candidateRPs != null) {
            for (Plan pl : candidateRPs) {
                Unifier relUn = pl.isRelevant(conf.C.SE.trigger);
                if (relUn != null) { // is relevant
                    LogicalFormula context = pl.getContext();
                    if (context == null) { // context is true
                        confP.C.SO = new Option(pl, relUn);
                        return;
                    } else {
                        Iterator r = context.logicalConsequence(ag, relUn);
                        if (r != null && r.hasNext()) {
                            confP.C.SO = new Option(pl, r.next());
                            return;
                        }
                    } 
                }
            }
            applyRelApplPlRule2("applicable");   
        } else {
            // problem: no plan
            applyRelApplPlRule2("relevant");   
        }        
    }
    
    private void applyAddIM() throws JasonException {
        // create a new intended means
        IntendedMeans im = new IntendedMeans(conf.C.SO, conf.C.SE.getTrigger());

        // Rule ExtEv
        if (conf.C.SE.intention == Intention.EmptyInt) {
            Intention intention = new Intention();
            intention.push(im);
            confP.C.addIntention(intention);
        } else {
            // Rule IntEv
            
            // begin tail recursion optimisation (TRO)
            if (setts.isTROon()) {
                IntendedMeans top = confP.C.SE.intention.peek(); // top = the IM that will be removed from the intention due to TRO
                if (top != null && top.getTrigger().isGoal() && im.getTrigger().isGoal() && // are both goal
                        top.getCurrentStep().getBodyNext() == null && // the plan below is finished
                        top.getTrigger().getPredicateIndicator().equals( im.getTrigger().getPredicateIndicator()) // goals are equals
                    ) { 
                    confP.C.SE.intention.pop(); // remove the top IM
                    
                    IntendedMeans imBase = confP.C.SE.intention.peek(); // base = where the new IM will be place on top of
                    if (imBase != null) {
                        // move top relevant values into the base (relevant = renamed vars in base)
                        for (VarTerm v: imBase.renamedVars) {
                            VarTerm vvl = (VarTerm)imBase.renamedVars.function.get(v);
                            Term t = top.unif.get(vvl);
                            if (t != null) { // if v has got a value in top unif, put the value in the unifier
                                if (t instanceof Literal) {
                                    //Literal l= (Literal)t.clone();
                                    //l.apply(top.unif);
                                    Literal l= (Literal)t.capply(top.unif);
                                    l.makeVarsAnnon(top.renamedVars);
                                    im.unif.function.put(vvl, l);                        
                                } else {
                                    im.unif.function.put(vvl, t);                        
                                }
                            } else {
                                // the vvl was renamed again in top, just replace in base the new value
                                VarTerm v0 = (VarTerm)top.renamedVars.function.get(vvl);
                                if (v0 != null) {
                                    imBase.renamedVars.function.put(v, v0);
                                }
                            }
                        }            
                    }
                }           
                // end of TRO
            }

            confP.C.SE.intention.push(im);
            confP.C.addIntention(confP.C.SE.intention);
        }
        confP.step = State.ProcAct;
    }

    private void applyProcAct() throws JasonException {
        confP.step = State.SelInt; // default next step
        if (conf.C.hasFeedbackAction()) {
            ActionExec a = null;
            synchronized (conf.C.getFeedbackActions()) {
                a = conf.ag.selectAction(conf.C.getFeedbackActions());
            }
            if (a != null) {
                confP.C.SI = a.getIntention();

                // remove the intention from PA (PA has all pending action, including those in FA;
                // but, if the intention is not in PA, it means that the intention was dropped
                // and should not return to I)
                if (C.removePendingAction(confP.C.SI.getId()) != null) {
                    if (a.getResult()) {
                        // add the intention back in I
                        updateIntention();
                        applyClrInt(confP.C.SI);
                        
                        if (hasGoalListener())
                            for (GoalListener gl: getGoalListeners())
                                for (IntendedMeans im: confP.C.SI) //.getIMs())
                                    gl.goalResumed(im.getTrigger());
                    } else {
                        String reason = a.getFailureMsg();
                        if (reason == null) reason = "";
                        ListTerm annots = JasonException.createBasicErrorAnnots("action_failed", reason);
                        if (a.getFailureReason() != null) 
                            annots.append(a.getFailureReason());
                        generateGoalDeletion(conf.C.SI, annots);
                        C.removeAtomicIntention(); // if (potential) atomic intention is not removed, it will be selected in selInt or selEv and runs again
                    }
                } else {
                    applyProcAct(); // get next action
                }
            }
        }
    }

    private void applySelInt() throws JasonException {
        confP.step = State.ExecInt; // default next step

        // Rule for Atomic Intentions
        confP.C.SI = C.removeAtomicIntention();
        if (confP.C.SI != null) {
            return;
        }
        
        // Rule SelInt1
        if (!conf.C.isAtomicIntentionSuspended() && conf.C.hasIntention()) { // the isAtomicIntentionSuspended is necessary because the atomic intention may be suspended (the above removeAtomicInt returns null in that case)
                                                                             // but no other intention could be selected
            confP.C.SI = conf.ag.selectIntention(conf.C.getIntentions());
            if (logger.isLoggable(Level.FINE)) logger.fine("Selected intention "+confP.C.SI);            
            if (confP.C.SI != null) { // the selectIntention function returned null
                return;             
            }
        }

        confP.step = State.StartRC;
    }

    @SuppressWarnings("unchecked")
    private void applyExecInt() throws JasonException {
        confP.step = State.ClrInt; // default next step
        
        if (conf.C.SI.isFinished()) {
            return;
        }
        
        // get next formula in the body of the intended means
        // on the top of the selected intention

        IntendedMeans im = conf.C.SI.peek();

        if (im.isFinished()) { 
            // for empty plans! may need unif, etc
            updateIntention();
            return;
        }
        Unifier     u = im.unif;
        PlanBody    h = im.getCurrentStep();
        
        Term bTerm = h.getBodyTerm();

        if (bTerm instanceof VarTerm) { // de-var bTerm
            bTerm = bTerm.capply(u);
            if (bTerm.isVar()) { // the case of !A with A not ground
                String msg = h.getSrcInfo()+": "+ "Variable '"+bTerm+"' must be ground.";
                if (!generateGoalDeletion(conf.C.SI, JasonException.createBasicErrorAnnots("body_var_without_value", msg)))
                    logger.log(Level.SEVERE, msg);
                return;
            }
            if (bTerm.isPlanBody()) { 
                if (h.getBodyType() != BodyType.action) { // the case of ...; A = { !g }; +g; ....
                    String msg = h.getSrcInfo()+": "+ "The operator '"+h.getBodyType()+"' is lost with the variable '"+bTerm+"' unified with a plan body. ";
                    if (!generateGoalDeletion(conf.C.SI, JasonException.createBasicErrorAnnots("body_var_with_op", msg))) 
                        logger.log(Level.SEVERE, msg);
                    return;
                }
                h = (PlanBody)bTerm;
                if (h.getPlanSize() > 1) { // the case of A unified with {a;b;c}
                    h.add(im.getCurrentStep().getBodyNext());
                    im.insertAsNextStep(h.getBodyNext());
                }
                bTerm = h.getBodyTerm();
            }
        }
            
        Literal body = null;
        if (bTerm instanceof Literal)
            body = (Literal)bTerm;

        switch (h.getBodyType()) {

        case none: break;
        
        // Rule Action
        case action:
            body = (Literal)body.capply(u);
            confP.C.A = new ActionExec(body, conf.C.SI);
            break;

        case internalAction:
            boolean ok = false;
            List errorAnnots = null;
            try {
                InternalAction ia = ((InternalActionLiteral)bTerm).getIA(ag);
                Term[] terms      = ia.prepareArguments(body, u); // clone and apply args
                Object oresult    = ia.execute(this, u, terms);
                if (oresult != null) {
                    ok = oresult instanceof Boolean && (Boolean)oresult;
                    if (!ok && oresult instanceof Iterator) { // ia result is an Iterator
                        Iterator iu = (Iterator)oresult;
                        if (iu.hasNext()) {
                            // change the unifier of the current IM to the first returned by the IA
                            im.unif = iu.next(); 
                            ok = true;
                        }
                    }
                    if (!ok) { // IA returned false
                        errorAnnots = JasonException.createBasicErrorAnnots("ia_failed", ""); 
                    }
                }

                if (ok && !ia.suspendIntention())
                    updateIntention();
            } catch (JasonException e) {
                errorAnnots = e.getErrorTerms();
                if (!generateGoalDeletion(conf.C.SI, errorAnnots))
                    logger.log(Level.SEVERE, body.getErrorMsg()+": "+ e.getMessage());
                ok = true; // just to not generate the event again
            } catch (Exception e) {
                if (body == null) 
                    logger.log(Level.SEVERE, "Selected an intention with null body in '"+h+"' and IM "+im, e);
                else
                    logger.log(Level.SEVERE, body.getErrorMsg()+": "+ e.getMessage(), e);
            }
            if (!ok)
                generateGoalDeletion(conf.C.SI, errorAnnots);

            break;

        case constraint:
            Iterator iu = ((LogicalFormula)bTerm).logicalConsequence(ag, u);
            if (iu.hasNext()) {
                im.unif = iu.next();
                updateIntention();
            } else {
                String msg = "Constraint "+h+" was not satisfied ("+h.getSrcInfo()+") un="+u;
                generateGoalDeletion(conf.C.SI, JasonException.createBasicErrorAnnots(new Atom("constraint_failed"), msg));
                logger.fine(msg);
            }
            break;

        // Rule Achieve
        case achieve:
            body = prepareBodyForEvent(body, u, conf.C.SI.peek());
            Event evt = conf.C.addAchvGoal(body, conf.C.SI);
            confP.step = State.StartRC;
            checkHardDeadline(evt);
            break;

        // Rule Achieve as a New Focus (the !! operator)
        case achieveNF:
            body = prepareBodyForEvent(body, u, null);
            evt  = conf.C.addAchvGoal(body, Intention.EmptyInt);
            checkHardDeadline(evt);
            updateIntention();
            break;

        // Rule Test
        case test:
            LogicalFormula f = (LogicalFormula)bTerm;
            if (conf.ag.believes(f, u)) {
                updateIntention();
            } else {
                boolean fail = true;
                // generate event when using literal in the test (no events for log. expr. like ?(a & b))
                if (f.isLiteral() && !(f instanceof BinaryStructure)) { 
                    body = prepareBodyForEvent(body, u, conf.C.SI.peek());
                    if (body.isLiteral()) { // in case body is a var with content that is not a literal (note the VarTerm pass in the instanceof Literal)
                        Trigger te = new Trigger(TEOperator.add, TEType.test, body);
                        evt = new Event(te, conf.C.SI);
                        if (ag.getPL().hasCandidatePlan(te)) {
                            if (logger.isLoggable(Level.FINE)) logger.fine("Test Goal '" + h + "' failed as simple query. Generating internal event for it: "+te);
                            conf.C.addEvent(evt);
                            confP.step = State.StartRC;
                            fail = false;
                        }
                    }
                }
                if (fail) {
                    if (logger.isLoggable(Level.FINE)) logger.fine("Test '"+h+"' failed ("+h.getSrcInfo()+").");
                    generateGoalDeletion(conf.C.SI, JasonException.createBasicErrorAnnots("test_goal_failed", "Failed to test '"+h+"'"));
                }
            }
            break;

            
        case delAddBel: 
            // -+a(1,X) ===> remove a(_,_), add a(1,X)
            // change all vars to anon vars to remove it
            Literal b2 = prepareBodyForEvent(body, u, conf.C.SI.peek()); 
            b2.makeTermsAnnon(); // do not change body (but b2), to not interfere in addBel
            // to delete, create events as external to avoid that
            // remove/add create two events for the same intention
            // (in future releases, creates a two branches for this operator)

            try {
                List[] result = ag.brf(null, b2, conf.C.SI); // the intention is not the new focus
                if (result != null) { // really delete something
                    // generate events
                    updateEvents(result,Intention.EmptyInt);
                }
            } catch (RevisionFailedException re) {
                generateGoalDeletion(conf.C.SI, JasonException.createBasicErrorAnnots("belief_revision_failed", "BRF failed for '"+body+"'"));
                break;
            }

            // add the belief, so no break;
            
        // Rule AddBel
        case addBel:
        case addBelBegin:
        case addBelEnd:
        case addBelNewFocus:
            // calculate focus
            Intention newfocus = Intention.EmptyInt;
            boolean isSameFocus = setts.sameFocus() && h.getBodyType() != BodyType.addBelNewFocus;
            if (isSameFocus) {
                newfocus = conf.C.SI;
                body = prepareBodyForEvent(body, u, newfocus.peek());
            } else {
                body = prepareBodyForEvent(body, u, null);
            }
            
            // call BRF
            try {
                List[] result;
                if (h.getBodyType() == BodyType.addBelEnd)
                    result = ag.brf(body,null,conf.C.SI, true); 
                else
                    result = ag.brf(body,null,conf.C.SI); // use default (well documented and used) method in case someone has overridden it 
                if (result != null) { // really added something
                    // generate events
                    updateEvents(result,newfocus);
                    if (!isSameFocus) {
                        updateIntention();
                    }                    
                } else {
                    updateIntention();                    
                }
            } catch (RevisionFailedException re) {
                generateGoalDeletion(conf.C.SI, null);
            }
            break;
            
        case delBel:

            newfocus = Intention.EmptyInt;
            if (setts.sameFocus()) {
                newfocus = conf.C.SI;
                body = prepareBodyForEvent(body, u, newfocus.peek());
            } else {
                body = prepareBodyForEvent(body, u, null);
            }
            // call BRF
            try {
                List[] result = ag.brf(null, body, conf.C.SI); // the intention is not the new focus
                if (result != null) { // really change something
                    // generate events
                    updateEvents(result,newfocus);
                    if (!setts.sameFocus()) {
                        updateIntention();
                    }                    
                } else {
                    updateIntention();                    
                }
            } catch (RevisionFailedException re) {
                generateGoalDeletion(conf.C.SI, null);
            }            
            break;
        }
    }
    
    // add the self source in the body in case no other source was given
    private Literal prepareBodyForEvent(Literal body, Unifier u, IntendedMeans imRenamedVars) {
        body = (Literal)body.capply(u);
        Unifier renamedVars = new Unifier();
        //getLogger().info("antes "+body+" "+u+" ");
        body.makeVarsAnnon(renamedVars); // free variables in an event cannot conflict with those in the plan
        //getLogger().info("depois "+body+" "+renamedVars);
        if (imRenamedVars != null) {
            imRenamedVars.renamedVars = renamedVars;
            
            // Code for TRO (Tail Recursion Opt)
            if (setts.isTROon()) {
                // renamed vars binded with another var in u need to be preserved (since u will be lost in TRO)
                Map adds = null;
                for (VarTerm v: renamedVars) {
                    Term t = u.function.get(v);
                    if (t != null && t.isVar()) {
                        //getLogger().info("adding "+t+"="+v+"="+renamedVars.function.get(v)+" u="+u);
                        if (adds == null)
                            adds = new HashMap();
                        try {
                            adds.put((VarTerm)t,renamedVars.function.get(v));                            
                        } catch (Exception e) {
                            logger.log(Level.SEVERE, "*** Error adding var into renamed vars. var="+v+", value="+t+".", e);
                        }
                    }
                }
                if (adds != null)
                    renamedVars.function.putAll(adds);
                // end code for TRO
            }
        }

        body = body.forceFullLiteralImpl();
        if (!body.hasSource()) { // do not add source(self) in case the programmer set the source
            body.addAnnot(BeliefBase.TSelf);
        }
        return body;
    }

    public void applyClrInt(Intention i) throws JasonException {
        while (true) { // quit the method by return
            // Rule ClrInt
            if (i == null)
                return;
            
            if (i.isFinished()) {
                // intention finished, remove it
                confP.C.dropIntention(i);
                //conf.C.SI = null;
                return;
            }
    
            IntendedMeans im = i.peek();
            if (!im.isFinished()) {
                // nothing to do
                return;
            }
    
            // remove the finished IM from the top of the intention
            IntendedMeans topIM = i.pop();
            Trigger topTrigger = topIM.getTrigger();
            Literal topLiteral = topTrigger.getLiteral();
            if (logger.isLoggable(Level.FINE)) 
                logger.fine("Returning from IM "+topIM.getPlan().getLabel()+", te="+topTrigger+" unif="+topIM.unif);
            
            // produce ^!g[state(finished)[reason(achieved)]] event
            if (!topTrigger.isMetaEvent() && topTrigger.isGoal() && hasGoalListener()) {
                for (GoalListener gl: goalListeners) {
                    gl.goalFinished(topTrigger, FinishStates.achieved);
                }
            }
            
            // if has finished a failure handling IM ...
            if (im.getTrigger().isGoal() && !im.getTrigger().isAddition() && !i.isFinished()) {//i.size() > 0) {
                // needs to get rid of the IM until a goal that
                // has failure handling. E.g,
                //   -!b
                //   +!c
                //   +!d
                //   +!b
                //   +!s: !b; !z
                // should became
                //   +!s: !z
                im = i.peek();
                if (im.isFinished() 
                        || !(im.unif.unifies(im.getCurrentStep().getBodyTerm(), topLiteral) && im.getCurrentStep().getBodyType() == BodyType.achieve)
                        || im.getCurrentStep().getBodyTerm() instanceof VarTerm) {
                    im = i.pop(); // +!c above
                }
                while (!i.isFinished() && 
                       !(im.unif.unifies(im.getTrigger().getLiteral(), topLiteral) && im.getTrigger().isGoal()) &&
                       !(im.unif.unifies(im.getCurrentStep().getBodyTerm(), topLiteral) && im.getCurrentStep().getBodyType() == BodyType.achieve)) {
                    im = i.pop();
                }
            }

            if (!i.isFinished()) {
                im = i.peek(); // +!s or +?s
                if (!im.isFinished()) {
                    // removes !b or ?s
                    // unifies the final event with the body that called it

                    // old code:
                    /*topLiteral.apply(topIM.unif);
                    topLiteral.makeVarsAnnon();
                    Literal cstep = (Literal)im.removeCurrentStep();
                    boolean r = im.unif.unifies(cstep,topLiteral);
                    */
                    
                    // new code optimised: handle directly renamed vars for the call                    
                    // get vars in the unifier that comes from makeVarAnnon (stored in renamedVars)
                    joinRenamedVarsIntoIntentionUnifier(im,topIM.unif);
                    im.removeCurrentStep();
                }
            }
        }
    }

    private void joinRenamedVarsIntoIntentionUnifier(IntendedMeans im, Unifier values) {
        if (im.renamedVars != null) {
            for (VarTerm ov: im.renamedVars.function.keySet()) {
                //System.out.println("looking for a value for "+ov+" in "+im.renamedVars+" and "+topIM.unif);
                UnnamedVar vt = (UnnamedVar)im.renamedVars.function.get(ov);
                //System.out.println("   via "+vt);
                im.unif.unifiesNoUndo(ov, vt); // introduces the renaming in the current unif
                // if vt has got a value from the top (a "return" value), include this value in the current unif
                Term vl = values.function.get(vt);
                //System.out.println(ov+"="+vt+"="+vl);
                if (vl != null) { // vt has value in top
                    //System.out.println("   and found "+vl);
                    vl = vl.capply(values);
                    if (vl.isLiteral())
                        ((Literal)vl).makeVarsAnnon();
                    im.unif.bind(vt, vl);
                }
            }
        }
        
    }
    
    /**********************************************/
    /* auxiliary functions for the semantic rules */
    /**********************************************/

    public List




© 2015 - 2025 Weber Informatics LLC | Privacy Policy