package abstrasy.pcfx;
import abstrasy.Heap;
import abstrasy.Interpreter;
import abstrasy.Node;
import abstrasy.PCoder;
import abstrasy.interpreter.BaseContextSnapshot;
import abstrasy.interpreter.InterpreterException;
import abstrasy.interpreter.StdErrors;
import java.util.ArrayList;
/**
* Abstrasy Interpreter
*
* Copyright : Copyright (c) 2006-2012, Luc Bruninx.
*
* Concédée sous licence EUPL, version 1.1 uniquement (la «Licence»).
*
* Vous ne pouvez utiliser la présente oeuvre que conformément à la Licence.
* Vous pouvez obtenir une copie de la Licence à l’adresse suivante:
*
* http://www.osor.eu/eupl
*
* Sauf obligation légale ou contractuelle écrite, le logiciel distribué sous
* la Licence est distribué "en l’état", SANS GARANTIES OU CONDITIONS QUELLES
* QU’ELLES SOIENT, expresses ou implicites.
*
* Consultez la Licence pour les autorisations et les restrictions
* linguistiques spécifiques relevant de la Licence.
*
*
* @author Luc Bruninx
* @version 1.0
*/
public class PCFx_infer extends PCFx {
public PCFx_infer() {
}
private static final class Rule {
int ruleType;
Node condition;
Node consequenceThen;
Node consequenceElse;
Rule(int ruleType, Node condition, Node consequenceThen, Node consequenceElse) {
this.ruleType = ruleType;
this.condition = condition;
this.consequenceThen = consequenceThen;
this.consequenceElse = consequenceElse;
}
Node getCondition() {
return condition;
}
Node getConsequenceThen() {
return consequenceThen;
}
Node getConsequenceElse() {
return consequenceElse;
}
public int getRuleType() {
return ruleType;
}
}
private final static Node evalSection(Heap local, Node thenode) throws Exception {
if(thenode!=null && thenode.isLazy()) {
if (local.isLoaded()) {
local.clear();
}
return thenode.exec(true);
}
return thenode;
}
private static final void addRules(ArrayList<Rule> rules, Node startAt) throws Exception {
/*
* vérification élémentaire de la syntaxe
*
* Minumum (each-time|once {...} then ...
*/
startAt.isGoodArgsCnt(4, 6);
Node rt_node = startAt.getSubNode(0, Node.TYPE_PCODE);
int ruleType = rt_node.getPCFx_Code();
if (ruleType != PCoder.PC_ONCE && ruleType != PCoder.PC_EACH_TIME) {
throw new InterpreterException(StdErrors.extend(StdErrors.Syntax_error,
"\'" + PCoder.getReserved(PCoder.PC_ONCE) + "\' or \'" + PCoder.getReserved(PCoder.PC_EACH_TIME) + "\'expected."));
}
Node cd_node = startAt.getSubNode(1, Node.TYPE_LAZY);
startAt.requirePCode(2, PCoder.PC_THEN);
Node th_node = startAt.elementAt(3);
/*
* clause else ... ?
*/
if (ruleType == PCoder.PC_ONCE) {
/*
* Si régle est once{...} then{...}, il n'y a pas de else...
*
* On enregistre donc la règle et on continue si la fin n'est pas atteinte...
*/
startAt.isGoodArgsCnt(4);
rules.add(new Rule(ruleType, cd_node, th_node, null));
}
else {
/*
* Si la règle est each-time{...} then{...}, il est possible qu'il y ait un else...
*
* On va donc analyser la suite en conséquence...
*/
if (startAt.size() > 4) {
startAt.requirePCode(4, PCoder.PC_ELSE);
Node el_node = startAt.elementAt(5);
rules.add(new Rule(ruleType, cd_node, th_node, el_node));
}
else {
rules.add(new Rule(ruleType, cd_node, th_node, null));
//puis on continue... each-time|once doit normalement se trouver à la position pos...
}
}
}
/**
* eval
*
* @param startAt Node
* @return Node
* @throws Exception
* @todo Implémenter cette méthode abstrasy.PCFx
*/
public Node eval(Node startAt) throws Exception {
/*
* forme (infer règles)
* ----------------------------------------------------------------------------------------------------------------------
*
* où rôles est une liste de la forme:
*
* once{condition} then ... : défini une règle qui ne peut être vérifiée qu'une seule fois.
*
* each-time{condition} then ... : défini une règle qui peut être vérifiée un nombre indéfini de fois.
* each-time{condition} then ... else ...
*
* La boucle d'inférence s'arrête lorsque plus aucune condition n'est vérifiée ou lorsqu'un résultat est déterminé.
*
* Exemple:
* =======
*
* (define 'x 32)
* (define 'i (floor(sqrt x)))
* (infer [
* '(each-time{zero? (mod x i)} then{ (incr! x) (setn! i (floor (sqrt x))) } else{decr! i})
* '(once{<=? i 1} then {,x})
* ])
*
* Retourne le nombre premier qui suit 32, soit dans ce cas 37.
*
* Remarques:
* =========
*
* Attention, once ne permet pas de définir de conséquence alternative (else). Ceci pour éviter des éventuelles
* confusions au niveau sémantique entre once et each-time.
*
* En effet, once="une fois que" et each-time="chaque fois que", ainsi on pourrait conclure que la proposition
* else qui serait assiciée à once voudrait dire "chaque fois que la condition 'une fois que' n'est pas remplie alors...".
*
* Cette interprétation peut porter à confusion. Aussi, once n'accepte pas de proposition alternative. Le programmeur
* est encourrager à formuler ses règles en tenant compte de ce point.
*
* Par ailleurs, comme infer prend comme argument une liste de règles, Il est donc possible, de modifier cette liste
* en fonction des besoins. On notera toutefois que si la liste à la source des règles est modifiée, il est nécessaire
* de l'inférer à nouveau.
*
*/
/*
* vérification élémentaire de la syntaxe.
*/
startAt.isGoodArgsCnt(2,4);
/*
* liste des règles
*/
// longueur de l'expression...
int size = startAt.size();
// fermeture...
Heap fermeture = null;
// liste des règles
ArrayList<Rule> rules = new ArrayList<Rule>();
// résultat...
Node res = null;
//
Interpreter interpreter = Interpreter.mySelf();
/*
* récupération de la liste des règles
*/
Node liste_r = startAt.getSubNode(1, Node.TYPE_CLIST);
/*
* rechercher la description de la fermeture s'il y en a une...
*/
Node with_cache = null;
if (size == 4) {
startAt.requirePCode(2,PCoder.PC_WITH);
/*
* il y a une fermeture...
*/
with_cache = startAt.getSubNode(3, Node.TYPE_LAZY);
fermeture = new Heap();
}
/**
* protection du système...
*/
BaseContextSnapshot contextSnapShot = new BaseContextSnapshot(interpreter);
/**
* boucle principale...
*/
interpreter.setCanLoop(true);
interpreter.setInLoop(true);
Exception allExcep = null;
try {
/**
* Lecture des règles...
*
*/
for (int i = 0; i < liste_r.size(); i++) {
Node elem_r = liste_r.getSubNode(i, Node.TYPE_QEXPR);
addRules(rules, elem_r);
}
/**
* On va commencé la boucle d'inférence...
*/
// placer la fermeture s'il y en a une...
if(fermeture!=null){
Heap.push(fermeture);
with_cache.exec(true);
}
// préparer le heap local pour le traitement...
Heap.push();
Heap local = Heap.current();
// préparer la boucle d'inférence...
int verif;
do {
verif = 0;
int i = 0;
while (i < rules.size() && interpreter.hasNoBreakCode()) {
/*
* Conserver rules.size() la taille pouvant être modifiée en cours d'évaluation.
* De plus, en cas de code break quelconque, on s'arrête (skip-loop) et (break-loop).
* Par contre dans le cas de (complete-loop) on termine l'analyse de l'ensemble des règles
* avant d'arrêter.
*/
Rule rule = (Rule) rules.get(i);
if (Node.isTrueEquivalent(evalSection(local, rule.condition))) {
/*
* condition vérifiée... On traite la partie then...
*/
verif++;
res = evalSection(local, rule.getConsequenceThen().getSymbolicValue()); // évaluation pareseuse des symboles...
if (res != null) {
break;
}
if (rule.getRuleType() == PCoder.PC_ONCE) {
// once : supprimer la règle (l'index actuel pointe donc sur la règle suivante)
rules.remove(i);
}
else {
// each-time : on ne supprime pas la règle et on passe à la suivante
i++;
}
}
else if (rule.getConsequenceElse() != null) {
// étant donné le parsing précédent, pas besoin de valider: il ne peut s'agir que de each-time...
/*
* si pas vérifié et qu'il y a une clause else... On traite la partie else...
*/
verif++;
res = evalSection(local, rule.getConsequenceElse().getSymbolicValue()); // évaluation paresseuse des symboles...
if (res != null) {
break;
}
// on ne peut avoir ici que each-time -> on ne supprime pas la règle et on passe à la suivante
i++;
}
else {
/*
* si pas vérifié et pas de else... On passe alors au suivant...
*/
i++;
}
}
}
while (verif > 0 && res == null && interpreter.isCanIterate());
/*
* Si (skip-loop), on repart...
* Si (complete-loop), on s'arrête...
* Si (break-loop), on s'arrête aussi...
*/
// Supprime le heap local...
Heap.pull();
// Supprime la fermeture s'il y en a une...
if(fermeture!=null){
Heap.pull();
}
}
catch (Exception ex) {
allExcep = ex;
}
/*
* restauration du système...
*/
contextSnapShot.restore(); // remplace restore_ignoreBreakCode() qui ne restaure pas les état BREAK (ce qui était une erreur de logique).
/*
* propagation de l'exception éventuelle...
*/
if (allExcep != null) {
throw allExcep;
}
/*
* si tout va bien, on retourne le résultat...
*/
return res;
}
}