package abstrasy;
import abstrasy.externals.AExtClonable;
import abstrasy.interpreter.AbortException;
import abstrasy.interpreter.ContextualCall;
import abstrasy.interpreter.ExternalTK;
import abstrasy.interpreter.InterpreterException;
import abstrasy.interpreter.RestartException;
import abstrasy.interpreter.RetryException;
import abstrasy.interpreter.SilentException;
import abstrasy.interpreter.StdErrors;
import abstrasy.pcfx.PCFx;
import abstrasy.pcfx.PCFx_clone;
import java.util.ArrayList;
import java.util.Vector;
/**
* 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 (experimental)
*/
public final class Node {
/**
* Compteur de créations de nodes...
*/
private static int serial_cnt = 0;
private int mySerialID = serial_cnt++;
static int getSerialCnt() {
return serial_cnt;
}
/**
* fin du compteur.
*/
/**
* Profillings...
*/
private static int execClone_cnt = 0;
static int getExecCloneCnt() {
return execClone_cnt;
}
/**
* fin profillings...
*/
public static final int TYPE_NONE = 0; // *** Pas de valeur non affecté ***
/**
* Types primaires ayant un corolaire quoté
*/
public static final int TYPE_PCODE = 1 << 0; // pcode -> TYPE OPERATOR
public static final int TYPE_SYMBOL = 1 << 1; // symbole
public static final int TYPE_EXPR = 1 << 2; // expression stricte
public static final int TYPE_LAZY = 1 << 3; // expression paresseuse
public static final int TYPE_NUMBER = 1 << 4; // nombre
public static final int TYPE_STRING = 1 << 5; // chaîne de caractères
public static final int TYPE_ALIST = 1 << 6; // liste abstraite
/**
* Types priamaires n'ayant pas de corolaire quoté
*/
public static final int TYPE_REF = 1 << 7; // ref d'indirection
public static final int TYPE_NOTHING = 1 << 8; // nothing
public static final int TYPE_SCOPE = 1 << 9; // scope
public static final int TYPE_NAMESPACE = 1 << 10; // namespace
public static final int TYPE_EXTERNAL = 1 << 11; // external
public static final int TYPE_FUNCTION = 1 << 12; // function
public static final int TYPE_PATTERN = 1 << 13; // pattern
public static final int TYPE_INTERFACE = 1 << 14; // interface
public static final int TYPE_CLIST = 1 << 15; // liste concrète
public static final int TYPE_HASH = 1 << 16; // hash
public static final int TYPE_BYTES = 1 << 17; //bytes
/**
* Offset de décalage pour le masque des types quotés
*/
public static final long TYPE_Q_shiftOffset = 32;
/**
* Liste des types quotés possibles
*/
public static final long TYPE_QPCODE = ((long) TYPE_PCODE) << TYPE_Q_shiftOffset;
public static final long TYPE_QEXPR = ((long) TYPE_EXPR) << TYPE_Q_shiftOffset;
public static final long TYPE_QLAZY = ((long) TYPE_LAZY) << TYPE_Q_shiftOffset;
public static final long TYPE_QSYMBOL = ((long) TYPE_SYMBOL) << TYPE_Q_shiftOffset;
public static final long TYPE_QNUMBER = ((long) TYPE_NUMBER) << TYPE_Q_shiftOffset;
public static final long TYPE_QSTRING = ((long) TYPE_STRING) << TYPE_Q_shiftOffset;
public static final long TYPE_QALIST = ((long) TYPE_ALIST) << TYPE_Q_shiftOffset;
/**
* Groupes de types par caractéristiques
*/
public static final long VTYPE_TYPES =
TYPE_NOTHING | TYPE_PCODE | TYPE_EXPR | TYPE_SYMBOL | TYPE_LAZY | TYPE_NUMBER | TYPE_STRING | TYPE_ALIST | TYPE_NAMESPACE | TYPE_EXTERNAL | TYPE_FUNCTION |
TYPE_SCOPE | TYPE_PATTERN | TYPE_INTERFACE | TYPE_CLIST |TYPE_HASH | TYPE_REF | TYPE_BYTES;
public static final long VTYPE_QTYPES = TYPE_QPCODE | TYPE_QEXPR | TYPE_QSYMBOL | TYPE_QLAZY | TYPE_QNUMBER | TYPE_QSTRING | TYPE_QALIST;
public static final long VTYPE_VALUABLE =
TYPE_NOTHING | TYPE_LAZY | TYPE_NUMBER | TYPE_STRING | TYPE_NAMESPACE | TYPE_EXTERNAL | TYPE_FUNCTION | TYPE_PATTERN | TYPE_INTERFACE |
TYPE_PCODE | TYPE_SCOPE | TYPE_CLIST | TYPE_HASH | TYPE_BYTES | VTYPE_QTYPES;
public static final long VTYPE_BY_VALUE = TYPE_NUMBER | TYPE_STRING | TYPE_PCODE | TYPE_SYMBOL | TYPE_NOTHING | TYPE_QNUMBER | TYPE_QSTRING | TYPE_QPCODE | TYPE_QSYMBOL;
public static final long VTYPE_TRANS_BYREF =
TYPE_CLIST | TYPE_HASH | TYPE_BYTES |TYPE_NAMESPACE | TYPE_LAZY | TYPE_FUNCTION | TYPE_PATTERN | TYPE_INTERFACE | TYPE_EXTERNAL | TYPE_SCOPE | TYPE_QALIST |
TYPE_QLAZY;
public static final long VTYPE_DELEGABLE = TYPE_NAMESPACE | TYPE_EXTERNAL | TYPE_FUNCTION;
public static final long VTYPE_NEEDSELF = TYPE_NAMESPACE | TYPE_EXTERNAL;
public static final long VTYPE_COMPARABLE = TYPE_NUMBER | TYPE_STRING | TYPE_CLIST | VTYPE_DELEGABLE;
public static final long VTYPE_EQUALISABLE = VTYPE_VALUABLE;
public static final long VTYPE_TRUEEQUIVALENT = TYPE_NUMBER | TYPE_STRING | TYPE_CLIST ;
public static final long VTYPE_REDEX = TYPE_ALIST | TYPE_EXPR;
public static final long VTYPE_INDIRECTION=TYPE_SYMBOL | TYPE_REF;
/*
*
* le contenu des éléments suivants est verrouillé en même temps que le contenant:
*
* - (nothing)
* - number
* - string
* - operator
*
*/
/*
*
* Le contenu des éléments suivants est immuable et nécessite un symbole immutable dans tous les cas.
*
*/
public static final long VTYPE_ALWAYS_FINAL = TYPE_LAZY | TYPE_EXPR | TYPE_PATTERN;
/*
*
* Les composites autres que ci-dessus ont tous un contenu mutable. Toutefois, le contenant ne peut plus être modifié.
*
* Cela inclu les list, hash, scope et namespace.
*
*/
public static final long VTYPE_NEED_LOCALHEAP = TYPE_FUNCTION;
public static final long VTYPE_METAEXPRESSION = TYPE_LAZY | TYPE_QLAZY | TYPE_QEXPR | TYPE_QALIST;
public static final double TRUE = -1;
public static final double FALSE = 0;
public static final Node NOTHING_FINAL_NODE = Node.createNothing().letFinal(true);
/*
* Type de Node...
*/
private int type;
public void setType(long type) throws InterpreterException {
if ((type & VTYPE_QTYPES) != 0) {
throw new InterpreterException(StdErrors.extend(StdErrors.Internal_error, "QTYPE in Node.setType()"));
}
this.type = (int) type;
}
public void setType(int type) {
this.type = type;
}
public int getType() {
return type;
}
private long getQType_() {
return ((long) type) << (quoted ? TYPE_Q_shiftOffset: 0);
}
public long getQType() {
return ((long) type) << (quoted ? TYPE_Q_shiftOffset: 0);
}
/*
* Gestion du verrouillage:
* -----------------------
*
* Cette variable interne permet d'assurer la gestion de l'atomicité des accès aux données ainsi que le verrouillage de celles-ci dans un cadre d'exécution concurrente.
*
*
*/
/**
* Important !!! : le type atomique doit être maintenu.
*/
volatile private AtomicLock locked_by = new AtomicLock();
/**
* Verrouillage utilisé par atomic pour atteindre un verrou sans attente passive.
*
* @param interpreter
* @throws Exception
*/
public boolean tryLock(Interpreter interpreter) throws Exception {
return locked_by.tryLock(interpreter, this);
}
/**
* Déverrouillage du node sans attente passive.
*
* Est utilisé pas atomic et lock. Toutefois, c'est cette méthoque que
* l'opérateur atomic appelle directement à la fin de la section (atomic n {...})
*
*/
public void unlock(Interpreter interpreter) throws Exception {
locked_by.unlock(interpreter, this);
}
/**
* Le node est-il verrouillé par un autre acteur (à l'exclusion de celui fourni en argument).
*
* @return
*/
public boolean isUnlocked() {
return (locked_by.get() != null); // attention!!! verrouillé par un autre interpréteur que celui fourni en argument !!
}
/**
* Le node est-il verrouillé par mon acteur (moi-même).
*
* @param interpreter
* @return
*/
public boolean isLockedBy(Interpreter interpreter) {
return locked_by.isLockedBy(interpreter); // attention!!! est-ce verrouillé par l'interpréter fourni en argument !!!
}
/**
* obtenir l'interpréteur du verrous en cours...
*/
public Interpreter getlock_by() {
return locked_by.get();
}
/**
* Acquisition d'un verroux via une attente active.
*
* Est utilisé par (sync v {...}) et (share v {...}):
*
* - sync demande un verrou exclusif:
* ---------------------------------
* Cela signifie qu'une exception doit être déclenchée si un autre processus essaye d'accéder
* à la données sans demander de verrou au préalable.
*
* - share demande un verrou non exclusif:
* ------------------------------------
* Ce la signifie qu'aucune exception ne sera déclenchée si on accède à la donnée (pour la consulter)
* sans avoir demandé de verrou au préalable.
*
*
* S'arrête en cas d'exception ou de threadRaising.
*
* @param interpreter
* @throws Exception
*/
public void acquirelock(Interpreter interpreter) throws Exception {
locked_by.acquirelock(interpreter, this);
}
/**
* Effectue un verrouillage avec attente passive (lock n {...}).
*
* @throws Exception
*/
public void lockLock() throws Exception {
//Interpreter myself = (Interpreter) Thread.currentThread();
Interpreter.getSemaphore().lockNode(this);
}
/**
* Effectue un déverrouillage avec notification pour d'autres éventuels acteurs en
* attente passive.
*
* Correspond à la fin d'une section (lock n {...}).
*
* @throws Exception
*/
public void unlockLock() throws Exception {
Interpreter.getSemaphore().unlockNode(this);
}
/*
* Indique si le node est quoté...
*/
private boolean quoted = false;
public synchronized boolean isQuoted() {
return quoted;
}
public synchronized void setQuoted(boolean quoted) {
this.quoted = quoted;
}
public synchronized Node letQuoted(boolean quoted) {
this.quoted = quoted;
return this;
}
/*
* S'il s'agit d'un Pcode...
*/
public int getPCFx_Code() {
//if(pcfx==null) return PCoder.PC_ERR;
return getPCFx_().getPcode().getCode();
}
/*
* La variable pcfx est retirée depuis le 1 mars 2012. Dorénavant, la variable pcfx est une union avec value (external étant supprimé).
*
* De cette manière, la structure Node est un peut plus légère sans que cela ne soit un handicape important du point de vue des performances.
*
* On notera que la référence pcfx ne doit plus être systématiquement recopiée lors de chaque déréférencement de Node puisqu'elle est comprise dans ext.
*
* Pour l'instant, setPCFx() et getPCFx() sont privés. Mais, il n'est pas interdit d'étendre le langage en ajoutant ultérieurement de nouveaux opérateurs.
* Dans ce cas, ces méthodes devront certainement passer public.
*
*/
private void setPCFx_(PCFx pcfx) {
this.value = pcfx;
}
private PCFx getPCFx_() {
return (PCFx) this.value;
}
/*
* value:
* -----
* String stocke les chaînes de caractères et les noms de symboles.
*
* Reçoit aussi le nom des classes.
*
* Number utilise value en y stockant un Double.
*
* On notera que les accesseurs sont synchronisés pour plus de sureté.
*
*/
volatile private Object value;
public void setString(String value) {
this.value = value;
}
public String getString() {
return (String) value;
}
public void setSymbol(ASymbol value) {
this.value = value;
}
public ASymbol getSymbol() {
return (ASymbol) value;
}
public Hash getHash() {
return (Hash) value;
}
public Bytes getBytes() {
return (Bytes) value;
}
private void setNumber_(double number) {
value = new Double(number);
}
private double getNumber_() {
return ((Double) value).doubleValue();
}
public void setNumber(double number) {
value = new Double(number);
}
public double getNumber() {
return ((Double) value).doubleValue();
}
public synchronized double compareAndSwap_number(double expected, double value) {
double old = getNumber_();
if (old == expected)
setNumber_(value);
return old;
}
/*
* selfFx:
* ------
* Est réservé pour stocker la référence de l'espace de noms d'une fonction.
*
* Il s'agit d'une information qui est propre au Node et qui est uniquement
* temporaire. Cette information n'est donc ni copiée (deref) ni clonée (clone).
*
* Ignoré lors de deref et de clone !!!
*
*
*
*/
//volatile private Node selfFx;
public void setSelfFx(Node self) {
if ((this.type & Node.VTYPE_NEED_LOCALHEAP) == 0)
return;
if (value instanceof Function)
((Function) value).setSelfFx(self);
}
public Node getSelfFx() {
if ((this.type & Node.VTYPE_NEED_LOCALHEAP) == 0)
return null;
if (value instanceof Function)
return ((Function) value).getSelfFx();
else
return null;
}
/*
* ext:
* =========
*
* Le 1 mars 2012, external devient ext. Cette variable est utilisé pour stocker tout objet externe ou d'extension.
*
* Si le node est du type external, il stocke l'objet abstrait qui n'appartient pas au langage. Pour ce type de données, un adaptateur doit être créé. Pour cela,
* un objHeap est nécessaire.
*
* Dans le cas ou le node est du type function, ext est aussi utilisé pour stocker la table de hashage pour les données des fonctions mémoizées.
*
* Si le node est du type PCode, alors ext contient un lien vers le PCFX.
*
*/
//private Object ext;
public void setExternal(Object external) {
this.value = external;
}
public Object getExternal() {
return value;
}
/*
* DTrace contient toutes les informations de traçage en cas d'erreur...
*/
private DTrace dTrace;
public void setDTrace(DTrace dTrace) {
this.dTrace = dTrace;
}
public DTrace getDTrace() {
return dTrace;
}
/*
* Numéro de série Unique IDdentity:
* ================================
*
* Il s'agit d'un numéro de série qui est attribuée à un Node pour identifier de façon unique les données qui lui sont assignées.
*
* Attention, toutefois, le numéro de série est un integer. Ainsi, les numéros identifiants de deux Nodes différents peuvent être identiquse alors que leur données
* sont différentes (après au moin un cycle). Donc, pour distinguer deux Nodes, si leur numéro sont identiques, les identifiants de chaque données sont comparées.
*
* On notera que l'adresse des données en mémoire ne peut être efficacement utilisée dans le cas d'un système géré par un GC. Celui-ci peut en effet déplacer
* et recopier les données dans la mémoire. Leur emplacement n'est donc pas une constante fiable.
*
* Cependant, le test des identités des éléments de données ajouté à celui d'un numéro de série fourni un test identitaire sûr et performant. Le test en profondeur
* n'étant réalisé que lorsque les numéro de série sont identiques.
*
*/
static private int NODE_UID = 0;
private int node_uid = getUID_();
private synchronized int getUID_() {
if (node_uid == 0) {
node_uid = ++NODE_UID;
if (NODE_UID == 0)
return getUID_(); // si le UID est 0, on recommence...
}
return node_uid;
}
public int getUID() {
return getUID_();
}
/**
* Test l'identité du Node courant avec celui fourni en argument. Si le Node courant a la même identité que l'argument, la méthode
* retourne true, sinon false.
*
* Attention, l'identité testée est celle des données et non celle du Node lui-même. Cela même si un numéro de série node_uid est conservé
* dans la structure du Node.
*
* @param node
* @return
*/
private boolean equalsIdentity_(Node node) {
// arrête dès qu'une identité est fausse...
return (getUID_() == node.getUID_()) && (value == node.value) && /*&& (selfFx == node.selfFx)*/(childs == node.childs);
}
public boolean equalsIdentity(Node node) {
return equalsIdentity_(node);
}
/******/
public Node() {
/**
* Par défaut, un nouveau Node est une expression...
*/
type = TYPE_EXPR;
}
/**
* Constructeur de string
* @param nValue
*/
public Node(String nValue) {
type = TYPE_STRING;
value = nValue; // new String(nValue);
}
/**
* Consturcteur de number
* @param n
*/
public Node(double n) {
type = TYPE_NUMBER;
setNumber_(n);
}
/**
* Constructeur de scope
* @param heap
*/
public Node(Heap heap) {
type = TYPE_SCOPE;
this.value = heap;
finalNode = true;
}
/*
* Détermine si node est d'un type TOUJOURS FINAL.
* Généralement utilisé comme garde-fou.
*/
public boolean isAlwaysFinal() {
return ((type & VTYPE_ALWAYS_FINAL) != 0) && !quoted;
}
public static Node createClone(Node node) throws Exception {
// utilisé pour le clonage profond...
// Cependant, le uid n'est pas copié - Nous avons 2 entités différentes...
return Node.createClone(node, new Bivalence());
}
public static Node createClone(Node node, Bivalence bival) throws Exception {
// utilisé pour le clonage profond...
// Cependant, le uid n'est pas copié - Nous avons 2 entités différentes...
if (node == null) {
if (Interpreter.isDebugMode())
Interpreter.Log("Clone of null!!!"); // pour debug...
throw new InterpreterException(StdErrors.Internal_error);
}
Node t = (Node) bival.findRight(node);
if (t == null) {
t = new Node(); // expression par défaut...
bival.add(node, t);
t.type = node.type; // on fixe le tyep ici...
//if (node.selfFx != null)
// t.selfFx = node.selfFx; /** Node.createClone(node.selfRoot, bival); // N'est pas cloné **/
if (node.value != null)
if (node.value instanceof Heap) {
t.value = Heap.createClone((Heap) node.value, bival);
}
else if (node.value instanceof AExtClonable) {
// pour pas le confondre avec une structure interne (comme la memoization par exemple) ou un objet d'un autre type que ExternalObject.
Object o = ((AExtClonable) node.value).clone_my_self(bival); // cloner aussi !!
if (o == null)
throw new InterpreterException(StdErrors.Object_not_clonable);
t.value = o;
}
else {
//
// Il ne s'agit pas d'un Heap, ni d'un external clonable.
//
// Il devrait donc s'agir d'un objet ou d'un marqueur à partager:
//
t.value = node.value;
}
t.dTrace = node.dTrace;
boolean reportFinal = node.isAlwaysFinal();
t.finalNode = reportFinal;
t.quoted = node.quoted;
int ns = node.size_();
if (ns != 0) {
//System.out.println("clone childs :" +t.typeOfNode()); // pour debug
_ensureChilds(t, ns);
for (int i = 0; i < ns; )
t.addElement_(Node.createClone(node.elementAt_(i++), bival)); // pour un clonage element par element...
if (reportFinal)
allFinal_(t); // reporter la finalité en profondeur (mais sans copier)
}
}
execClone_cnt++;
return t;
}
final private static void qlink_(Node s, Node t) throws InterpreterException {
synchronized (s) {
synchronized (t) {
t.type = s.type;
t.value = s.value;
//t.selfFx = s.selfFx;
t.dTrace = s.dTrace;
t.childs = s.childs;
t.quoted = s.quoted;
t.finalNode = s.finalNode || s.isAlwaysFinal();
/*
* Protection inclusive pour les éléments toujours finaux (lazy, function, class, etc...).
* Agit comme un garde-fou.
*/
if ((s.type & VTYPE_BY_VALUE) == 0) {
/*
* On lie l'identité uniquement s'il s'agit d'un objet composé (donc pas un primitif, d'où le ==).
* De cette manière, les primitifs NUMBER, STRING, SYMBOL, NOTHING et PCODE sont duppliqués et non liés.
*
* Dans le cas contraire, le Node reçoit la même identité que la données originale.
*
* On doit considérer le Node comme un conteneur qui permet de lier les données qui
* décrive la donnée et la donnée elle-même.
*
*/
t.node_uid = s.node_uid;
/*
* Idem pour le verrou.
*
* Cependant, le traitement est ici plus complex. Il faut tenir compte des règles de propagation suivantes:
*
* Soit (set! t s) où t <- s.
*
* Ainsi, N.lck(x) exprime le node 'N' disposant du verrou 'lck' avec un réentrant de 'x'.
*
* Donc, si 'lck' est différent de null, de me (moi) ou de share (partage), il y a une erreur (accès illégal).
*
* En outre t ne peut jamais être share puisque les ressource verrouillée avec share sont accessible en lecture seul.
*
* La table des cas restant est donc celle-ci:
*
* | t.before | s.before | cas | t.after | s.after |
* +------------+------------+-----+------------+------------+
* | t.null(0) | s.null(0) | 0 | s.null(0) | s.null(0) |
* | t.null(0) | s.me(n) | 1 | s.me(n) | s.me(n) |
* | t.null(0) | s.share(n) | 1* | s.share(n) | s.share(n) | * peut être traité de la même façon que le cas précédent.
* | t.me(m) | s.null(0) | 2 | t.me(m) | t.me(m) |
* | t.me(m) | s.me(n) | 3 | s.me(m+n) | s.me(m+n) |
* | t.me(m) | s.share(n) | Err : * Droits incompatibles
* | t.share(m) | * | Err : * Droits insuffisants
*
* On constate que mis à part le cas 2, la remontée écrase toujours le verrou de la destination.
*
* *** Attention!!! Le sens est important et doit être maintenu. ***
*
*
* Dans le cas 3, on constate que le compteurs réentrants s'additionnent dans le verrou résultant.
*
* ===================================================================================================================
*
* REMARQUES COMPLEMENTAIRES
* -------------------------
*
* Il est possible que l'état des verrous soit modifié dans l'intervalle entre le moment où la propagation
* débute et le moment où elle se termine.
*
* Dans ce cas toutefois, il s'agit d'un des cas où un des argument est libre de verrou au départ de la
* propagation. Cela ne doit pas arriver si une bonne pratique est appliquée. En effet, si un Node peut être
* verrouillé par un autre processus, IL FAUT TOUJOURS le verrouiller soi-même avant de l'affecter à son tour.
*
* Dans le cas d'une mauvaise pratique, la méthode ci-dessous peut détecter un verrouillage postérieur au point
* initial de la propagation (si l'état du verrou change entre le début et la fin de la propagation).
*
* Il ne devrait donc pas y avoir de souci à ce sujet dans les autres partie du progarmme.
*
*/
/*
* On est seul à pouvoir changer changer les référence AtomicLocks ici.
*
*/
AtomicLock tlck = t.locked_by;
AtomicLock slck = s.locked_by;
int cas = (tlck.get() != null ? 2: 0) | (slck.get() != null ? 1: 0);
Interpreter me = Interpreter.mySelf();
Interpreter s_me = slck.isExclusive() ? me: Interpreter.SHARED_INTERPRETER;
switch (cas) {
case 0:
t.locked_by = slck;
/** Je vérifie si les états des verrous 'sortants' n'ont pas changés **/
if (tlck.get() != null || slck.get() != null)
throw new InterpreterException(StdErrors.Concurrent_access_to_locked_data);
break;
case 1:
t.locked_by = slck;
/** Je vérifie si les états des verrous 'sortants' n'ont pas changés **/
if (tlck.get() != null || slck.get() != s_me)
throw new InterpreterException(StdErrors.Concurrent_access_to_locked_data);
break;
case 2:
s.locked_by = tlck;
/** Je vérifie si les états des verrous 'sortants' n'ont pas changés **/
if (slck.get() != null || tlck.get() != me)
throw new InterpreterException(StdErrors.Concurrent_access_to_locked_data);
break;
case 3:
// l'ordre est inversé puisque les 2 nodes sont à moi...
/**
*
* Attention!!! Ici, les deux verrous doivent être de moi ABSOLUMENT!...
*
**/
if (slck.get() != me || tlck.get() != me)
throw new InterpreterException(StdErrors.Concurrent_access_rights_mismatch);
/** Les états des verrous 'sortants' ne peut pas changés dans ce cas particulier **/
slck.extractReentrantOf(tlck);
t.locked_by = slck;
}
}
//else
// // les types par valeur nécessite un nouveau uid.
// t.node_uid=0;
}
}
}
/**
* Réalise le déréférencement du Node courant en copiant le descripteur de la variable vers le Node
* de destination posé en argument.
*
* Rappel: Le déréférencement consiste à copier le contenant d'une donnée. Dans le cas d'une valeur
* primitive, cela implique la dupplication de la valeur. Toutefois, dans le cas des valeurs
* composites comme les objets et les listes, c'est simplement la référence de l'élément qui
* est duppliqué dans le Node de destination.
*
* La méthode est synchronisée sur le Node courant (la source) de manière à interdire les modifications
* de valeurs de celle-ci.
*
* @param toNode
*/
final public void derefTo(Node toNode) throws Exception {
qlink_(this, toNode);
//toNode.finalNode = this.isAlwaysFinal(); // protection obsolète (voir qlink_)...
}
/**
* Effectue l'opération de déréférencement, autrement dit, copie le descripteur de la variable.
* Si la valeur est du type primitif, sa valeur est duppliqué. S'il s'agit d'une valeur composite,
* la valeur est inchangée. Toutefois, le descripteur d'origine est protégé.
*
* Le déréférencement du Node courant est une opération synchronizée sur le node courant lui-même.
* De cette manière, les valeurs primitives de celui-ci ne peuvent être modifiée durant le déréférencement.
*
* Notez toutefois que cette protection en s'étend pas aux élément composites: Objet et liste.
*
* @return node_dereferece
*/
public Node deref() throws InterpreterException {
Node rnode = new Node();
qlink_(this, rnode);
return rnode;
}
/**
* Cette méthode retourne un Node dont le contenant est déréférencé de façon non sûre.
*
* Il n'est pas tenu compte de certains aspects ce qui rend ce déréférencement plus rapide mais
* également moins sûr.
*
* Cette méthode ne devrait être utilisée que dans des cas particulier où aucun verrou ne doit
* être pris en compte.
*
* @return Node deref
*/
public Node deref_unsafe() {
Node t = new Node();
t.type = type;
t.value = value;
//t.selfFx = selfFx;
t.dTrace = dTrace;
t.childs = childs;
t.quoted = quoted;
t.finalNode = finalNode;
t.locked_by = locked_by;
if ((type & VTYPE_BY_VALUE) == 0)
t.node_uid = node_uid;
return t;
}
/**
* Sécurise les valeurs finales qui pourraient être altérées dans un procéssus.
* Si le node est final, la méthode retourne un clone de celui-ci. Sinon,
* le node est retourné immédiatement.
*
* Est équivalent à Node.secure(Node), mais n'est pas static.
*
* @return
* @throws Exception
*/
public Node secure() throws Exception {
if(finalNode)
if(isAlwaysFinal())
return this;
else
return Node.createClone(this); // un clone n'est final que si always final
else
return this.deref();
}
public Node select(int start, int len) throws Exception {
if (start < 0)
throw new InterpreterException(StdErrors.extend(StdErrors.Out_of_range, "start pos(" + start + ") < 0"));
if (len < 0)
throw new InterpreterException(StdErrors.extend(StdErrors.Out_of_range, "lenght of section(" + len + ") < 0"));
if (start + len > size_())
throw new InterpreterException(StdErrors.extend(StdErrors.Out_of_range, start + "+" + len + ">" + size_()));
if(type!=TYPE_CLIST)
throw new InterpreterException(StdErrors.Argument_type_mismatch);
Node r = Node.createCList();
int endp = start + len;
for (int i = start; i < endp; i++)
r.addElement(elementAt_(i).secure());
return r;
}
final public static Node createRef(Node target) {
Node res = new Node();
res.type = TYPE_REF;
res.value = target;
return res;
}
// version private static final de createCList()...
// placé ici pour optimisation du compilateur java...
final private static Node createAList_() {
Node res = new Node();
res.type = TYPE_ALIST;
return res;
}
public static Node createAList() {
Node res = new Node();
res.type = TYPE_ALIST;
return res;
}
// version private static final de createCList()...
// placé ici pour optimisation du compilateur java...
final private static Node createCList_() {
Node res = new Node();
res.type = TYPE_CLIST;
return res;
}
/**
* force l'état de finalité du node en cours ainsi que de tous
* ceux qu'il contient.
*/
private static void allFinal_(Node node) {
node.finalNode = true;
int sz = node.size_();
if (sz > 0) {
for (int i = --sz; i >= 0; )
allFinal_(node.elementAt_(i--));
}
return;
}
public static void allFinal(Node node) {
allFinal_(node);
return;
}
public static void linkSwap(Node fromNode, Node toNode) {
int t_int;
Node t_node;
Childs t_vector;
boolean t_boolean;
Object t_object;
DTrace t_dtrace;
//
t_int = toNode.type;
toNode.type = fromNode.type;
fromNode.type = t_int;
//
t_object = toNode.value;
toNode.value = fromNode.value;
fromNode.value = t_object;
//
//t_node = toNode.selfFx;
//toNode.selfFx = fromNode.selfFx;
//fromNode.selfFx = t_node;
//
t_dtrace = toNode.dTrace;
toNode.dTrace = fromNode.dTrace;
fromNode.dTrace = t_dtrace;
//
t_vector = toNode.childs;
toNode.childs = fromNode.childs;
fromNode.childs = t_vector;
//
t_int = toNode.node_uid;
toNode.node_uid = fromNode.node_uid;
fromNode.node_uid = t_int;
//
t_boolean = toNode.finalNode;
toNode.finalNode = fromNode.finalNode;
fromNode.finalNode = t_boolean;
//
t_boolean = toNode.quoted;
toNode.quoted = fromNode.quoted;
fromNode.quoted = t_boolean;
//
//
}
/**
* Nettoyer le Node courant et en faire une primitive NOTHING.
*
* Cette méthode est synchronisée.
*
*/
public synchronized void setNothing() throws Exception {
qlink_(NOTHING_FINAL_NODE, this);
this.finalNode = false;
}
/**
* Childs remplace l'élément Vector des premiéres versions BMacro.
*
*/
private Childs childs;
final private static void _ensureChilds(Node node) {
if (node.childs == null)
node.childs = new Childs();
}
final private static void _ensureChilds(Node node, int minCapacity) {
_ensureChilds(node);
node.childs.ensure(minCapacity);
}
public Node[] getArray() {
_ensureChilds(this);
return childs.getArray();
}
public Node[] getArray(int index, int len) {
_ensureChilds(this);
return childs.getArray(index, len);
}
public void setArray(Node[] array) {
_ensureChilds(this);
childs.setArray(array);
}
private void _addNode_(Node node) throws InterpreterException {
childs.add(node);
}
private Node append_(Node childNode) throws InterpreterException {
_ensureChilds(this);
_addNode_(childNode);
return this;
}
public Node append(Node childNode) throws InterpreterException {
_ensureChilds(this);
_addNode_(childNode);
return this;
}
private void addElement_(Node obj) throws InterpreterException {
_ensureChilds(this);
_addNode_(obj);
}
public void addElement(Node obj) throws InterpreterException {
_ensureChilds(this);
_addNode_(obj);
}
private void appendChildsOf_(Node src, int index, int len) throws InterpreterException {
if (len > 0) {
int toend = index + len;
if (src.size_() >= toend) {
_ensureChilds(this);
if (len > 2) {
childs.append(src.childs, index, len);
}
else
for (int i = index; i < toend; )
_addNode_(src.elementAt_(i++));
//childs.append(src.childs, index, len);
}
else {
throw new RuntimeException("Internal precond: appendChildOf(" + src + "," + index + ") [??? " + index + " <= " + src.size_() + "]");
}
}
}
private void appendChildsOf_(Node src, int index) throws InterpreterException {
appendChildsOf_(src, index, src.size_() - index);
}
private void appendChildsOf_(Node src) throws InterpreterException {
/*
* Cette méthode peut facilement être optimisée... Il s'agit d'une variante de: appendChildsOf_(src, 0, src.size_());
*/
int len = src.size_();
if (len > 0) {
_ensureChilds(this);
if (len < 2) {
for (int i = 0; i < len; )
_addNode_(src.elementAt_(i++));
}
else {
childs.append(src.childs);
}
}
}
public void appendChildsOf(Node src, int index) throws InterpreterException {
appendChildsOf_(src, index);
}
public void appendChildsOf(Node src) throws InterpreterException {
appendChildsOf_(src);
}
public void clearChilds() {
childs = null;
}
public Childs getChilds() {
return childs;
}
public void setChilds(Childs childs) {
this.childs = childs;
}
public void setSize(int eSize) {
_ensureChilds(this);
childs.setSize(eSize);
}
private boolean hasChilds_() {
return childs == null ? false: childs.hasChildren();
}
public boolean hasChilds() {
return childs == null ? false: childs.hasChildren();
}
private Node elementAt_(int index) {
_ensureChilds(this);
return childs.get(index);
}
public Node elementAt(int index) {
_ensureChilds(this);
return childs.get(index);
}
public void setElementAt(Node obj, int index) throws InterpreterException {
_ensureChilds(this);
childs.set(obj, index);
}
public int size() {
return childs == null ? 0: childs.size();
}
private int size_() {
return childs == null ? 0: childs.size();
}
int size_deep() {
int c = 1; // moi... je dois me compter...
if (childs != null) {
int ei = childs.size();
for (int i = 0; i < ei; i++)
c += childs.get(i).size_deep();
}
return c;
}
public void insertElementAt(Node obj, int index) throws InterpreterException {
_ensureChilds(this);
childs.insert(obj, index);
}
public void reverseElements() {
_ensureChilds(this);
childs.reverse();
}
private Node firstElement_() {
_ensureChilds(this);
return childs.first();
}
public Node firstElement() {
return firstElement_();
}
public void removeElementAt(int index) {
_ensureChilds(this);
childs.remove(index);
}
public int indexOf(Node nodeIdentity) {
_ensureChilds(this);
int i = childs.size();
while (--i >= 0) {
if (nodeIdentity.equalsIdentity_(childs.get(i)))
return i;
}
return i; // si -1 alors pas trouvé...
}
/**
* - parse -------------------------------------
*
**/
final private static boolean isNextCharNOT4GtLt(String src, int pos) {
if (pos >= src.length())
return true;
char c = src.charAt(pos);
return (c != '?' && c != '=' && c != '>');
}
final private static void parse_checkCombiExpr(Node node, int parCnt, int accCnt, int croCnt, int dicCnt) throws Exception {
String expl = "";
boolean err = false;
Interpreter interpreter = Interpreter.mySelf();
expl = "missing ";
if (interpreter.parse_parCnt != parCnt) {
err = true;
expl += ((interpreter.parse_parCnt > parCnt) ? "\')\'": "\'(\'");
}
if (interpreter.parse_accCnt != accCnt) {
err = true;
if (expl.length() > 0)
expl += " or ";
expl += ((interpreter.parse_accCnt > accCnt) ? "\'}\'": "\'{\'");
}
if (interpreter.parse_croCnt != croCnt) {
err = true;
if (expl.length() > 0)
expl += " or ";
expl += ((interpreter.parse_croCnt > croCnt) ? "\']\'": "\'[\'");
}
if (interpreter.parse_dicCnt != dicCnt) {
err = true;
if (expl.length() > 0)
expl += " or ";
expl += ((interpreter.parse_dicCnt > dicCnt) ? "\'>\'": "\'<\'");
}
if (err)
throw new InterpreterException(StdErrors.createTrace(node, new InterpreterException(StdErrors.extend(StdErrors.Syntax_error, expl))));
}
static private class ParseBag {
boolean inQuote = false;
}
final static private String stringFormattedToString(String v) {
return (isStringFormatted(v)) ? unescapeString(v.substring(1, v.length() - 1)): v;
}
final static private String autoFormatSYMBOL(String v) {
String vr = v;
if (v.length() > 0) {
if (isStringNumber(v)) {
if (v.endsWith(".0"))
vr = v.substring(0, v.length() - 2);
}
else {
boolean alreadyFormatted = v.startsWith("\"") && v.endsWith("\"");
if (!alreadyFormatted)
vr = "\"" + escapeString(vr) + "\"";
}
}
else {
vr = "\"\"";
}
return vr;
}
final private static String parse_addArg(Node node, String source, String cd, ParseBag bag) throws Exception {
String cClean = cd.trim();
if (cClean.length() > 0) {
Node nvNode = new Node();
nvNode.setDTrace(new DTrace(Interpreter.mySelf().parse_linCnt, source));
nvNode.finalNode = true;
nvNode.setQuoted(bag.inQuote);
bag.inQuote = false;
int pc = PCoder.getCode(cClean);
if (pc != PCoder.PC_ERR) {
nvNode.type = TYPE_PCODE;
nvNode.setPCFx_(PCoder.getPcfx(cClean));
}
else {
if (isStringNumber(cClean)) {
nvNode.type = TYPE_NUMBER;
try {
nvNode.setNumber_(Double.parseDouble(cClean));
}
catch (Exception ex) {
if (Interpreter.isDebugMode())
ex.printStackTrace();
throw new InterpreterException(StdErrors.createTrace(node,
new InterpreterException(StdErrors.extend(StdErrors.Syntax_error, "malformed number \'" + cClean + "\'"))));
}
}
else if (isStringFormatted(cClean)) {
nvNode.type = TYPE_STRING;
try {
nvNode.value = stringFormattedToString(cClean);
}
catch (Exception ex) {
if (Interpreter.isDebugMode())
ex.printStackTrace();
throw new InterpreterException(StdErrors.createTrace(node,
new InterpreterException(StdErrors.extend(StdErrors.Syntax_error, "malformed string (" + cClean + ")"))));
}
}
else if (isHexNumber(cClean)) {
nvNode.type = TYPE_NUMBER;
try {
nvNode.setNumber_(Node.parseHex(cClean));
}
catch (Exception ex) {
if (Interpreter.isDebugMode())
ex.printStackTrace();
throw new InterpreterException(StdErrors.createTrace(node,
new InterpreterException(StdErrors.extend(StdErrors.Syntax_error, "malformed hexadecimal number \'" + cClean + "\'"))));
}
}
else if (isBinNumber(cClean)) {
nvNode.type = TYPE_NUMBER;
try {
nvNode.setNumber_(Node.parseBin(cClean));
}
catch (Exception ex) {
if (Interpreter.isDebugMode())
ex.printStackTrace();
throw new InterpreterException(StdErrors.createTrace(node,
new InterpreterException(StdErrors.extend(StdErrors.Syntax_error, "malformed binary number \'" + cClean + "\'"))));
}
}
else if (isOctNumber(cClean)) {
nvNode.type = TYPE_NUMBER;
try {
nvNode.setNumber_(Node.parseOct(cClean));
}
catch (Exception ex) {
if (Interpreter.isDebugMode())
ex.printStackTrace();
throw new InterpreterException(StdErrors.createTrace(node,
new InterpreterException(StdErrors.extend(StdErrors.Syntax_error, "malformed octal number \'" + cClean + "\'"))));
}
}
else if (isStringSymbol(cClean)) {
nvNode.type = TYPE_SYMBOL;
String v = cClean;
if (v.charAt(0) == '$')
v = v.substring(1);
//System.out.println(v);
nvNode.value = new ASymbol(v);
}
else {
throw new InterpreterException(StdErrors.createTrace(node,
new InterpreterException(StdErrors.extend(StdErrors.Syntax_error, "malformed symbol \'" + cClean + "\'"))));
}
}
node.addElement_(nvNode);
}
Interpreter.mySelf().parse_linCnt += Interpreter.mySelf().parse_strLineOffset;
Interpreter.mySelf().parse_strLineOffset = 0;
return "";
}
final private static int parse(Node node, String source, int beginAt) throws Exception {
boolean fin = false;
String cd = "";
char strDelim = '_';
boolean inStr = false;
boolean inComment = false;
boolean inBlockComment = false;
int sAutoDoc = 0;
ParseBag parseBag = new ParseBag();
Interpreter interpreter = Interpreter.mySelf();
while ((beginAt < source.length()) && (fin == false)) {
char c = source.charAt(beginAt++);
//System.out.print(c);
if (c == '\n')
interpreter.parse_linCnt++;
if (inBlockComment) {
if (c == PCoder.COMMENT_BLOCK_2) {
if (beginAt < source.length()) {
if (source.charAt(beginAt) == PCoder.COMMENT_BLOCK_1) {
beginAt++;
inBlockComment = false;
}
}
}
}
else if (inComment) {
if ((c == '\n') || (c == '\r'))
inComment = false;
}
else if (inStr) {
if (strDelim != '`' && ((c == '\n') || (c == '\r')))
throw new InterpreterException(StdErrors.createTrace(node, new InterpreterException(StdErrors.extend(StdErrors.Syntax_error, "malformed string"))));
cd = cd + c;
if (c == '\\') {
c = source.charAt(beginAt++);
cd = cd + c;
if (c == '\n')
interpreter.parse_strLineOffset++;
}
else if (c == strDelim) {
inStr = false;
}
}
else if (c == '(') {
cd = parse_addArg(node, source, cd, parseBag);
Node nvNode = new Node();
nvNode.setDTrace(new DTrace(interpreter.parse_linCnt, source));
nvNode.finalNode = true;
nvNode.setQuoted(parseBag.inQuote);
parseBag.inQuote = false;
int parCnt = interpreter.parse_parCnt;
int accCnt = interpreter.parse_accCnt;
int croCnt = interpreter.parse_croCnt;
int dicCnt = interpreter.parse_dicCnt;
interpreter.parse_parCnt++;
beginAt = parse(nvNode, source, beginAt);
node.addElement_(nvNode);
parse_checkCombiExpr(nvNode, parCnt, accCnt, croCnt, dicCnt);
}
else if (c == ')') {
interpreter.parse_parCnt--;
cd = parse_addArg(node, source, cd, parseBag);
fin = true;
}
else if (c == '{') {
cd = parse_addArg(node, source, cd, parseBag);
Node nvNode = new Node();
nvNode.setDTrace(new DTrace(interpreter.parse_linCnt, source));
nvNode.type = TYPE_LAZY;
nvNode.finalNode = true;
nvNode.setQuoted(parseBag.inQuote);
parseBag.inQuote = false;
int parCnt = interpreter.parse_parCnt;
int accCnt = interpreter.parse_accCnt;
int croCnt = interpreter.parse_croCnt;
int dicCnt = interpreter.parse_dicCnt;
interpreter.parse_accCnt++;
beginAt = parse(nvNode, source, beginAt);
node.addElement_(nvNode);
parse_checkCombiExpr(nvNode, parCnt, accCnt, croCnt, dicCnt);
}
else if (c == '}') {
interpreter.parse_accCnt--;
cd = parse_addArg(node, source, cd, parseBag);
fin = true;
}
else if (c == '[') {
cd = parse_addArg(node, source, cd, parseBag);
Node nvNode = createAList_();
nvNode.setDTrace(new DTrace(interpreter.parse_linCnt, source));
nvNode.finalNode = true;
nvNode.setQuoted(parseBag.inQuote);
parseBag.inQuote = false;
int parCnt = interpreter.parse_parCnt;
int accCnt = interpreter.parse_accCnt;
int croCnt = interpreter.parse_croCnt;
int dicCnt = interpreter.parse_dicCnt;
interpreter.parse_croCnt++;
beginAt = parse(nvNode, source, beginAt);
node.addElement_(nvNode);
parse_checkCombiExpr(nvNode, parCnt, accCnt, croCnt, dicCnt);
}
else if (c == ']') {
interpreter.parse_croCnt--;
cd = parse_addArg(node, source, cd, parseBag);
fin = true;
}
/*else if ((c == '<') && isNextCharNOT4GtLt(source, beginAt)) {
cd = parse_addArg(node, source, cd, parseBag);
Node nvNode = createAList_();
nvNode.setDTrace(new DTrace(interpreter.parse_linCnt, source));
nvNode.finalNode = true;
nvNode.setQuoted(parseBag.inQuote);
parseBag.inQuote = false;
int parCnt = interpreter.parse_parCnt;
int accCnt = interpreter.parse_accCnt;
int croCnt = interpreter.parse_croCnt;
int dicCnt = interpreter.parse_dicCnt;
interpreter.parse_dicCnt++;
beginAt = parse(nvNode, source, beginAt);
if (nvNode.size_() != 2)
throw new InterpreterException(StdErrors.createTrace(node, new InterpreterException(StdErrors.extend(StdErrors.Syntax_error, "malformed pair"))));
nvNode.type = Node.TYPE_DICO;
nvNode.finalNode = true;
//if (!nvNode.isValidPair()) {
// throw new InterpreterException(StdErrors.createTrace(node, new InterpreterException(StdErrors.extend(StdErrors.Syntax_error, "invalid pair"))));
//}
//if (node.findPair(Node.getPairKey(nvNode)) != null) {
// throw new InterpreterException(StdErrors.createTrace(node,
// new InterpreterException(StdErrors.extend(StdErrors.Syntax_error, "multiple declarations of \'" + Node.getPairKey(nvNode) + "\'"))));
//}
node.addElement_(nvNode);
parse_checkCombiExpr(nvNode, parCnt, accCnt, croCnt, dicCnt);
}
else if ((c == '>') && isNextCharNOT4GtLt(source, beginAt)) {
interpreter.parse_dicCnt--;
cd = parse_addArg(node, source, cd, parseBag);
fin = true;
}*/
else if (c == '@' || c == ',' || c == ';' || c == '~') { // sucre syntaxique (@var ...) (,var) (;var) (~var ...)...
cd = cd + c;
cd = parse_addArg(node, source, cd, parseBag);
}
/**
* enlever ce sucre -> (unquote ...)
*
else if (c == '$') { // sucre $var...
cd = cd + c;
cd = parse_addArg(source, cd, parseBag);
}**/
else if (c == '\'') { // sucre 'var...
if (parseBag.inQuote)
throw new InterpreterException(StdErrors.createTrace(node, new InterpreterException(StdErrors.extend(StdErrors.Syntax_error, "invalid quote"))));
cd = parse_addArg(node, source, cd, parseBag);
parseBag.inQuote = true;
}
/*else if (c == ',') { // sucre ,var...
if (parseBag.inQuote) {
throw new InterpreterException(InterpreterException.EC_SYNTAX,
Node.formatLineNumber(interpreter.parse_linCnt) + "ERREUR DE SYNTAXE: Quotation d\'une antiquotation...");
}
if (parseBag.inQuasi) {
throw new InterpreterException(InterpreterException.EC_SYNTAX,
Node.formatLineNumber(interpreter.parse_linCnt) + "ERREUR DE SYNTAXE: Antiquotation invalide...");
}
cd = parse_addArg(source, cd, parseBag);
parseBag.inQuasi = true;
}*/
else if (Character.isWhitespace(c)) {
cd = parse_addArg(node, source, cd, parseBag);
}
else if ((c == '\"' || c == '`')) {
cd = parse_addArg(node, source, cd, parseBag);
cd = cd + c;
strDelim = c;
inStr = true;
}
else if (c == PCoder.COMMENT) {
cd = parse_addArg(node, source, cd, parseBag);
inComment = true;
}
else if (c == PCoder.COMMENT_BLOCK_1) {
if (beginAt < source.length()) {
if (source.charAt(beginAt) == PCoder.COMMENT_BLOCK_2) {
cd = parse_addArg(node, source, cd, parseBag);
inBlockComment = true;
sAutoDoc = beginAt + 1;
}
else {
cd = cd + c;
}
}
}
else {
cd = cd + c;
}
}
cd = parse_addArg(node, source, cd, parseBag);
return beginAt;
}
final private static void parse(Node node, String source) throws Exception {
Interpreter interpreter = Interpreter.mySelf();
interpreter.parse_parCnt = 0;
interpreter.parse_dicCnt = 0;
interpreter.parse_accCnt = 0;
interpreter.parse_croCnt = 0;
interpreter.parse_linCnt = 0;
interpreter.parse_strLineOffset = 0;
parse(node, source, 0);
if (interpreter.parse_parCnt != 0)
throw new InterpreterException(StdErrors.extend(StdErrors.Syntax_error, "missing parentheses"));
if (interpreter.parse_accCnt != 0)
throw new InterpreterException(StdErrors.extend(StdErrors.Syntax_error, "missing braces"));
if (interpreter.parse_croCnt != 0)
throw new InterpreterException(StdErrors.extend(StdErrors.Syntax_error, "missing brackets"));
if (interpreter.parse_dicCnt != 0)
throw new InterpreterException(StdErrors.extend(StdErrors.Syntax_error, "missing chevrons"));
}
public static Node compile(String source) throws Exception {
//System.out.println("COMPILING FROM -> " + source);
Node pcode = new Node();
parse(pcode, source);
//if (Interpreter.mySelf().isOptimizing()) {
//pcode = pcode.optimize_private();
//}
//System.out.println("COMPILING RAW -> " + pcode);
//allFinal(pcode);
return pcode;
}
/**
* --- fin du parser ----------------------------------------
**/
/**
* Un Node peut contenir une valeur finale.
* Celle-ci ne peut être référencée. Elle doit être
* copiée ou duppliquée.
*/
private boolean finalNode = false;
public void setFinalNode(boolean finalNode) {
this.finalNode = finalNode;
}
/**
* Un Node est final si sa valeur ne peut être référencée
* (elle doit être obligatoirement copiée/duppliquée)
* ou si le Node est protégé contre la modification.
* @return
*/
public boolean isFinalNode() {
return finalNode;
}
public Node letFinal(boolean finalNode) {
this.finalNode = finalNode;
return this;
}
public String sourceLine() {
String res = "";
if (this.dTrace != null) {
String[] ls = this.dTrace.geSource().split("\n");
if (ls.length > this.dTrace.getLineNumber())
res = ls[this.dTrace.getLineNumber()].trim();
}
else {
try {
res = this.nodeToString();
}
catch (Exception e) {
Interpreter.LogAlways(e.toString());
res = "ERROR";
}
}
return res;
}
final private static String formatLineNumber(int lineNumber) {
String lnum = "000000" + (lineNumber + 1);
return "\n@" + lnum.substring(lnum.length() - 6, lnum.length()) + "> ";
}
public String traceLineNumber() {
return dTrace != null ? Node.formatLineNumber(this.dTrace.getLineNumber()): formatLineNumber(0);
}
/**
* Retourne le code sérialisé, pré-compilé et prêt pour l'évaluation.
*
* On notera que la méthode retourne la structure incluse dans son expression super-englobante.
* Pour obtenir l'élément sérialisé, il faut extraîre le premier élément de l'expression englobante.
*
* @param vector (Node.list contenant les référence des élément contenaire déjà rencontré).
* @return Structure sérialisée et prête à être évaluée (dans son expr super-englobante).
* @throws Exception
*/
public Node toSerialized(Node vector) throws Exception {
Interpreter interpreter = Interpreter.interpr_getNewChildInterpreter();
String src = this.nodeToSerializedString(vector);
//System.out.println(src);
interpreter.setSource(src);
interpreter.compile();
Node r = interpreter.getNode();
if (r.size_() == 1)
r = r.firstElement_();
return r.letQuoted(true);
}
String nodeToSerializedString(Node vector) throws Exception {
StringBuffer sb = new StringBuffer();
return nodeToSerializedString(sb, vector).toString();
}
private StringBuffer nodeToSerializedString(StringBuffer res, Node vector) throws Exception {
if (vector.indexOf(this) >= 0) // si déjà dans la liste...
throw new InterpreterException(StdErrors.Circulare_reference);
if (quoted)
res.append('\'');
String spc1 = "";
String ares;
switch (type) {
case TYPE_NUMBER:
String nstr;
double number = getNumber_();
if (number == Math.rint(number)) {
nstr = "" + (long) number;
}
else {
nstr = "" + number;
}
ares = autoFormatSYMBOL(nstr);
res.append(ares);
break;
case TYPE_STRING:
ares = "\"" + escapeString(stringFormattedToString((String) value)) + "\"";
res.append(ares);
break;
case TYPE_SYMBOL:
ares = "$" + getSymbol().getStr();
res.append(ares);
break;
case TYPE_ALIST:
case TYPE_CLIST:
res.append('[');
spc1 = "]";
break;
case TYPE_REF:
res.append('(').append(PCoder.getReserved(PCoder.PC_REFsucre));
((Node)value).nodeToSerializedString(res, vector);
res.append(')');
return res;
case TYPE_BYTES:
{
res.append('(').append(PCoder.getReserved(PCoder.PC_BYTES));
Bytes bytes=(Bytes)value;
for(int i=0;i<bytes.length();)
res.append(' ').append(bytes.peek(i++));
res.append(')');
return res;
}
case TYPE_NAMESPACE:
case TYPE_EXTERNAL:
{
vector.addElement(this); // enregistrer la référence actuelle...
Node serialobj = Node.VDelegable.evalMethod_unary_or_null(this, PCoder.getMethod(PCoder.PC_QUOTE), vector);
//System.out.println("*2*");
if (serialobj != null) {
if (res.length() > 0) {
switch (res.charAt(res.length() - 1)) {
case '(':
case ')':
case '{':
case '}':
case '[':
case ']':
case '\'':
break;
default:
res.append(' ');
}
}
serialobj.letQuoted(false).nodeToSerializedString(res, vector);
}
else
throw new InterpreterException(StdErrors.Object_not_serializable);
break;
}
case TYPE_HASH:
{
res.append('(').append(PCoder.getReserved(PCoder.PC_HASH));
Hash hash = getHash();
if (hash.size() > 0) {
ArrayList<String> ks = hash.keys_strings();
for (int i = 0; i < ks.size(); i++) {
res.append(' ').append(PCoder.getReserved(PCoder.PC_REFsucre)).append(ks.get(i)).append(' ');
hash.get(ks.get(i)).nodeToSerializedString(res, vector);
}
}
res.append(')');
break;
}
case TYPE_SCOPE:
{
Heap heap = (Heap) this.getExternal();
ArrayList<String> symbols = heap.getSlots();
res.append('(').append(PCoder.getReserved(PCoder.PC_SCOPE)).append('{');
for (int i = 0; i < symbols.size(); i++) {
String symbs = symbols.get(i);
Node subel = heap.get(symbs);
if (subel.isNamespace()) {
// vobject... -> sérialisation obligatoire...
res.append('(').append(PCoder.getReserved(PCoder.PC_DEFINE)).append(" \'$").append(symbs).append('(').append(PCoder.getReserved(PCoder.PC_UNQUOTE))
.append(' ');
subel.nodeToSerializedString(res, vector);
res.append("))");
}
else {
// autres...
res.append('(').append(PCoder.getReserved(PCoder.PC_DEFINE)).append(" \'$").append(symbs).append(' ');
subel.nodeToSerializedString(res, vector);
res.append(')');
}
}
res.append("})");
break;
}
case TYPE_NOTHING:
res.append('(').append(PCoder.getReserved(PCoder.PC_NOTHING)).append(')');
break;
case TYPE_PCODE:
res.append(PCoder.getReserved(this.getPCFx_Code()));
break;
case TYPE_EXPR:
res.append('(');
spc1 = ")";
break;
case TYPE_LAZY:
res.append('{');
spc1 = "}";
break;
case TYPE_INTERFACE:
{
res.append('(').append(PCoder.getReserved(PCoder.PC_INTERFACE));
Interface ix = (Interface) this.value;
res.append(' ').append(ix.getBodyIx().nodeToSerializedString(vector)).append(')');
return res;
}
case TYPE_PATTERN:
{
res.append('(').append(PCoder.getReserved(PCoder.PC_PATTERN));
Pattern px = (Pattern) this.value;
if (px.getPrimaryType() == Node.TYPE_EXTERNAL)
res.append(' ').append(PCoder.getReserved(PCoder.PC_EXTERNAL));
if (px.getPrimaryType() == Node.TYPE_NAMESPACE)
res.append(' ').append(PCoder.getReserved(PCoder.PC_NAMESPACE));
if (px.getPrimaryType() == Node.TYPE_FUNCTION)
res.append(' ').append(PCoder.getReserved(PCoder.PC_FUNCTION));
res.append(' ').append(px.getBodyPx().nodeToSerializedString(vector)).append(')');
return res;
}
case TYPE_FUNCTION:
{
Function fx = (Function) this.value;
res.append('(').append(PCoder.getReserved(PCoder.PC_FUNCTION)).append(' ');
if (fx.isInlined())
res.append(PCoder.getReserved(PCoder.PC_INLINE)).append(' ');
if (fx.isOverrided())
res.append(PCoder.getReserved(PCoder.PC_OVERRIDE)).append(' ');
if (fx.isWrapper() && !fx.isOverrided())
res.append(PCoder.getReserved(PCoder.PC_WRAPPER)).append(' ');
if (fx.getSuperFx() != null)
res.append(fx.getSuperFx().nodeToSerializedString(vector)).append(' ');
if (fx.getBodyFx() != null)
res.append(fx.getBodyFx().nodeToSerializedString(vector));
if (fx.getScopeYieldFx() != null)
res.append(' ').append(PCoder.getReserved(PCoder.PC_YIELD)).append(' ').append(fx.getScopeYieldFx().nodeToSerializedString(vector));
if (fx.getScopeWithFx() != null)
res.append(' ').append(PCoder.getReserved(PCoder.PC_WITH)).append(' ').append(fx.getScopeWithFx().nodeToSerializedString(vector));
res.append(')');
return res;
}
default: // inconnu
throw new InterpreterException(StdErrors.extend(StdErrors.Object_not_serializable, "UNKNOW-TYPE:" + type));
}
if (this.hasChilds_()) {
vector.addElement(this);
for (int i = 0; i < size_(); i++) {
ares = this.elementAt_(i).nodeToSerializedString(vector).trim();
if (ares.length() > 0) {
if (res.length() > 0) {
switch (res.charAt(res.length() - 1)) {
case '(':
case ')':
case '{':
case '}':
case '[':
case ']':
case '\'':
res.append(ares);
break;
default:
res.append(' ').append(ares);
}
}
else {
res.append(ares);
}
}
}
}
res.append(spc1);
return res;
}
public String nodeToString() throws Exception {
return this.nodeToString(0);
}
public String toString() {
try {
return this.nodeToString();
}
catch (Exception e) {
Interpreter.LogAlways(e.toString());
return "ERROR";
}
}
private String _nesting_to_space_(int nesting) {
String s = " ";
StringBuilder r = new StringBuilder();
for (int i = 0; i < nesting; i++) {
r.append(s);
}
return r.toString();
}
String nodeToString(int nesting) throws Exception {
if (nesting > 32) {
return ".?.";
}
String spc = _nesting_to_space_(nesting);
String res = quoted ? "\'": "";
String spc1 = "";
String ares;
switch (type) {
case TYPE_NUMBER:
String nstr;
double number = getNumber_();
long lnum = Math.round(number);
if (number == lnum)
nstr = "" + lnum;
else
nstr = "" + number;
ares = autoFormatSYMBOL(nstr);
res = res + ares;
break;
case TYPE_STRING:
ares = "\"" + escapeString(stringFormattedToString((getString()))) + "\"";
res = res + ares;
break;
case TYPE_SYMBOL:
ares = "$" + getSymbol().getStr();
res = res + ares;
break;
case TYPE_REF:
res += "("+PCoder.getReserved(PCoder.PC_REFsucre) + ((Node)value).nodeToString(nesting+1)+")";
return res;
case TYPE_ALIST:
case TYPE_CLIST:
res = res + "[";
spc1 = "]";
break;
case TYPE_BYTES:
{
res += "(" + PCoder.getReserved(PCoder.PC_BYTES);
Bytes bytes=(Bytes)value;
for(int i=0;i<bytes.length();)
res+=" 0x"+Integer.toHexString(bytes.peek(i++));
res+=")";
return res;
}
case TYPE_HASH:
{
res += "(" + PCoder.getReserved(PCoder.PC_HASH);
Hash hash = getHash();
if (hash.size() > 0) {
ArrayList<String> ks = hash.keys_strings();
for (int i = 0; i < ks.size(); i++) {
res += "\n" +
_nesting_to_space_(nesting + 1) + PCoder.getReserved(PCoder.PC_REFsucre) + ks.get(i) + " ";
res += hash.get(ks.get(i)).nodeToString(nesting + 1);
}
res += "\n" +
_nesting_to_space_(nesting) + ")";
}
else
res += ")";
break;
}
case TYPE_NAMESPACE:
res = res + "(" + PCoder.getReserved(PCoder.PC_NAMESPACE) + " {" + Tools.replaceCSeq("\n" +
((Heap) this.getExternal()).toString(this, nesting), "\n" +
_nesting_to_space_(nesting), "\n" +
_nesting_to_space_(nesting + 1)) + "\n" +
_nesting_to_space_(nesting) + "})";
break;
case TYPE_SCOPE:
res = res + "(" + PCoder.getReserved(PCoder.PC_SCOPE) + " {" + Tools.replaceCSeq("\n" +
((Heap) this.getExternal()).toString(this, nesting), "\n" +
_nesting_to_space_(nesting), "\n" +
_nesting_to_space_(nesting + 1)) + "\n" +
_nesting_to_space_(nesting) + "})";
break;
case TYPE_EXTERNAL:
res = res + "(" + PCoder.getReserved(PCoder.PC_EXTERNAL) + " \"" + this.value.getClass().getName() + "\")";
break;
case TYPE_NOTHING:
res = res + "(" + PCoder.getReserved(PCoder.PC_NOTHING) + ")";
break;
case TYPE_PCODE:
res = res + PCoder.getReserved(this.getPCFx_Code());
break;
case TYPE_EXPR:
res = res + "(";
spc1 = ")";
break;
case TYPE_LAZY:
res = res + "{"; //+(!needClosure ? "#|Closure Optimized|# ":"");
spc1 = "}";
break;
case TYPE_INTERFACE:
{
res = res + "(" + PCoder.getReserved(PCoder.PC_INTERFACE);
Interface ix = (Interface) this.getExternal();
res += " " + ix.getBodyIx().nodeToString(nesting + 1) + ")";
return res;
}
case TYPE_PATTERN:
{
res = res + "(" + PCoder.getReserved(PCoder.PC_PATTERN);
Pattern px = (Pattern) this.value;
if (px.getPrimaryType() == Node.TYPE_EXTERNAL)
res += " " + PCoder.getReserved(PCoder.PC_EXTERNAL);
if (px.getPrimaryType() == Node.TYPE_NAMESPACE)
res += " " + PCoder.getReserved(PCoder.PC_NAMESPACE);
if (px.getPrimaryType() == Node.TYPE_FUNCTION)
res += " " + PCoder.getReserved(PCoder.PC_FUNCTION);
res += " " + px.getBodyPx().nodeToString(nesting + 1) + ")";
return res;
}
case TYPE_FUNCTION:
{
Function fx = (Function) this.value;
res = res + "(" + PCoder.getReserved(PCoder.PC_FUNCTION);
if (fx.isInlined())
res += " " + PCoder.getReserved(PCoder.PC_INLINE);
if (fx.isOverrided())
res += " " + PCoder.getReserved(PCoder.PC_OVERRIDE);
if (fx.isWrapper() && !fx.isOverrided())
res += " " + PCoder.getReserved(PCoder.PC_WRAPPER);
res += (fx.getSuperFx() != null ? " " + fx.getSuperFx().nodeToString(nesting + 1): "");
res += fx.getBodyFx() != null ? (res.indexOf('\n') >= 0 ? "\n" +
_nesting_to_space_(nesting + 1): " ") + fx.getBodyFx().nodeToString(nesting + 1): "";
res += (fx.getScopeYieldFx() != null ? "\n" +
_nesting_to_space_(nesting + 1) + PCoder.getReserved(PCoder.PC_YIELD) + "\n" +
_nesting_to_space_(nesting + 2) + fx.getScopeYieldFx().nodeToString(nesting + 2): "");
res += (fx.getScopeWithFx() != null ? "\n" +
_nesting_to_space_(nesting + 1) + PCoder.getReserved(PCoder.PC_WITH) + "\n" +
_nesting_to_space_(nesting + 2) + fx.getScopeWithFx().nodeToString(nesting + 2): "");
res = (res.indexOf('\n') >= 0 ? "\n" +
_nesting_to_space_(nesting) + res + "\n" +
_nesting_to_space_(nesting): res) + ")";
return res;
}
default: // inconnu
res = res + " *** INCONNU *** (" + type + ")";
}
if (hasChilds_()) {
boolean allchilds = true;
for (int i = 0; i < size_(); i++) {
Node tn = this.elementAt_(i);
if (tn != null && !tn.hasChilds_()) {
allchilds = false;
i = size_();
}
}
for (int i = 0; i < size_(); i++) {
int rlineLN = res.length();
int rlastNL = res.lastIndexOf("\n");
int signifc = res.length();
if (rlastNL >= 0) {
rlineLN = rlineLN - rlastNL;
signifc = res.substring(rlastNL, res.length()).trim().length();
}
Node tn = this.elementAt_(i);
ares = tn != null ? tn.nodeToString(nesting + 1): "###NULL###";
if (ares.length() > 0) {
int alineLN = ares.length();
int afirstNL = ares.indexOf("\n");
if (afirstNL >= 0) {
alineLN = afirstNL;
if (i == size_() - 1)
allchilds = true;
}
int mxlw = 130 - spc.length();
if (mxlw < 20)
mxlw = 20;
if ((rlineLN + 1 + alineLN) > mxlw) {
if (signifc == 0) {
res = res + " " + ares;
}
else {
res = res + "\n" +
spc + ares;
}
}
else {
if (i == 0) {
if (allchilds)
res = res + "\n" +
spc + " ";
res = res + ares;
}
else {
res = res + " " + ares;
}
}
if ((i + 1) < size_()) {
if (this.elementAt_(i).hasChilds_() && this.elementAt_(i + 1).hasChilds_()) {
res = res + (res.charAt(res.length() - 1) != '\n' ? "\n": "") + spc;
allchilds = true;
}
}
}
}
if (allchilds) {
res = res + (res.charAt(res.length() - 1) != '\n' ? "\n": "") + spc;
}
res = res + spc1;
}
else {
res = res + spc1; // liste vide
}
return res;
}
/*final private static String getVObjectClassID_(Node obj) {
Node snode = obj.objHeap.get(PCoder.CLASS_ID);
if (snode != null && snode.isVString()) {
return (String) snode.value;
}
else {
return null;
}
}*/
/*
* Traitement des symboles (private) doit être placé avant pour être performant.
*
*/
final private boolean isXSymbol_() {
return type == TYPE_SYMBOL;
}
final private boolean isUnquotedVSymbol_() {
return type == TYPE_SYMBOL && !quoted;
}
final private boolean isQSymbol_() {
return type == TYPE_SYMBOL && quoted;
}
final private boolean isIndirection_(){
return (type & VTYPE_INDIRECTION)!=0 && !quoted;
}
/*-------------------------------------------------------------------------------------
*
* Instrumentalisation des assertions requireX() en vue d'assurer facilement la
* protection des données et des accès.
*
*/
private static final void requireReadLockAccess_(Node node) throws InterpreterException {
synchronized (node) {
AtomicLock alck = node.locked_by;
Interpreter lck = alck.get();
// uniquement s'il y a un verrou...
if (lck != null && alck.isExclusive() && lck != Interpreter.mySelf())
throw new InterpreterException(StdErrors.Concurrent_access_to_locked_data);
}
}
private static final void requireWriteLockAccess_(Node node) throws InterpreterException {
synchronized (node) {
AtomicLock alck = node.locked_by;
Interpreter lck = alck.get();
// uniquement s'il y a un verrou...
if (lck != null && !alck.isExclusive() && lck != Interpreter.mySelf())
throw new InterpreterException(StdErrors.Exclusive_lock_needed);
}
}
private static final void requireMutable_(Node node) throws InterpreterException {
if (node.isFinalNode())
throw new InterpreterException(StdErrors.Illegal_access_to_final_value);
}
/**
* Déclenche une exception si le Node courant
* ne dispose pas d'un verrou en lecture valable (s'il y en a un).
*
* @return this
* @throws InterpreterException
*/
public Node requireReadLockAccess() throws InterpreterException {
requireReadLockAccess_(this);
return this;
}
/**
* Déclenche une exception si le Node courant
* ne dispose pas d'un verrou en écriture valable (s'il y en a un).
*
* @return this
* @throws InterpreterException
*/
public Node requireWriteLockAccess() throws InterpreterException {
requireWriteLockAccess_(this);
return this;
}
/**
* Déclenche une exception si le Node courant
* n'est pas mutable.
*
* @return
* @throws InterpreterException
*/
public Node requireMutable() throws InterpreterException {
requireMutable_(this);
return this;
}
public static final int ACCESSTYPE_READLOCK = 1 << 0;
public static final int ACCESSTYPE_WRITELOCK = 1 << 1;
public static final int ACCESSTYPE_MUTABLE = 1 << 2;
public static final int ACCESSVTYPE_MUTABLE_WRITELOCK = ACCESSTYPE_WRITELOCK | ACCESSTYPE_MUTABLE;
/**
* Contrôleur de type d'accès au node courant à partir d'un masque.
*
* @param accessMask
* @throws InterpreterException
*/
public void requireAccessType(int accessMask) throws InterpreterException {
if ((accessMask & ACCESSTYPE_READLOCK) != 0)
requireReadLockAccess_(this);
if ((accessMask & ACCESSTYPE_WRITELOCK) != 0)
requireWriteLockAccess_(this);
if ((accessMask & ACCESSTYPE_MUTABLE) != 0)
requireMutable_(this);
}
/*
*
--------------------------------------------------------------------------------------*/
/**
* Instrumentalisation de getSymbolicValue(Node).
*
* Test également le droit d'accès concurrent.
*
* @param r
* @return
* @throws Exception
*/
final private static Node getSymbolicValue_(Node r) throws Exception {
if (r.isUnquotedVSymbol_())
return getSymbolicValue_(Heap.getv(r.getSymbol()));
else if(r.type==TYPE_REF)
return getSymbolicValue_(((Node)r.value).requireReadLockAccess());
return r;
}
/**
* idem que getSymbolicValue_(node) mais sans vérifier les droit d'accès concurrents.
*
* @param r
* @return
* @throws Exception
*/
final private static Node getSymbolicValue_unsafeAccess(Node r) throws Exception {
if (r.isUnquotedVSymbol_())
r = Heap.getv_unsafe(r.getSymbol());
else if(r.type==TYPE_REF)
return getSymbolicValue_unsafeAccess((Node)r.value);
return r;
}
/*
* Correctif du 9 mai 2011:
* ========================
* Les éléments déclarés sont obligatoirement évalué une fois avant d'être stockés dans des variables.
* Lorsque ceux-ci sont stockés de cette manière, il sont déjà sous forme évaluée.
* Les symbole ne doivent pas être résolus ici.
*
* Correctif du 2 août 2012:
* ========================
* needSubExec() est remplacé par beta_reduction_() qui effectue la réduction de l'expression s'il
* s'avère qu'elle contient des redex (c-à-d des sous expressions issues du résultat de l'évalutation
* d'un niveau interne (une fonction peut retourner une expression évaluable. Il s'agit d'un redex
* qui doit être évalué d'une manière itérative (et non pas récursive) comme s'il s'agit d'un callback.
*
*/
private boolean isRedex_() {
return !quoted && (type & VTYPE_REDEX) != 0 ;
}
final private static boolean containsRedex_(Node node) {
for (int i = node.size_() - 1; i >= 0; )
if (node.elementAt_(i--).isRedex_())
return true;
return false;
}
final private static Node beta_reduction_(Node xnode) throws Exception {
Node redex = xnode;
while (redex != null && !redex.quoted && redex.isRedex_())
redex = redex.exec_q(false);
return redex;
}
/*
*
*
*
*/
private Node exec_q(boolean doULink) throws Exception {
/**
* Si le node est quoté, le retourner lui-même...
*/
if (quoted)
return this;
/**
* Le node est évaluable...
* L'analyse va se poursuivre selon le type.
*/
switch (type) {
/*
* Attention, nous essayons d'économiser la pile en évitent les imbrications...
*/
case TYPE_EXPR:
{
Interpreter interpreter = Interpreter.mySelf();
interpreter.throwInterThreadException();
if (containsRedex_(this)) {
/**
* s'il est nécessaire d'évaluer l'expression en profondeur uniquement...
*/
Node cmdNode = new Node();
cmdNode.dTrace = this.dTrace;
//
//--
boolean oldTailNode = interpreter.isTailNode();
boolean oldTerminalNode = interpreter.isTerminalNode();
//
int tailNodeIndex = size_() - 1;
for (int i = 0; i <= tailNodeIndex; ) {
if (interpreter.hasNoBreakCode()) {
boolean isTail = (i == tailNodeIndex);
interpreter.setTailNode(isTail);
interpreter.setTerminalNode(isTail && cmdNode.size_() == 0);
Node terme = beta_reduction_(elementAt(i++));
if (terme != null)
cmdNode.addElement_(terme);
}
else
break;
}
interpreter.setTailNode(oldTailNode);
interpreter.setTerminalNode(oldTerminalNode);
//--
if (cmdNode.size_() > 0)
return cmdNode.eval_q();
// sinon...
return null;
}
// sinon...
/**
* s'il n'est pas nécessaire d'évaluer l'expression en profondeur...
*/
if (size_() > 0)
return eval_q();
// sinon...
return null;
}
case TYPE_LAZY:
/**
* s'il est permis d'évaluer les sections lazy...
*/
if (doULink) {
/**
* lors de l'entrée d'une fonction ou d'une nouvelle section lazy,
* on vérifie s'il n'y a pas une action break, etc...
*/
Interpreter interpreter = Interpreter.mySelf();
interpreter.throwInterThreadException();
if (containsRedex_(this)) {
/**
* faire ce qui suit si une analyse en profondeur doit être réalisée...
*/
Node cmdNode = new Node();
cmdNode.dTrace = this.dTrace;
cmdNode.node_uid = this.node_uid;
//
//--
boolean oldTailNode = interpreter.isTailNode();
boolean oldTerminalNode = interpreter.isTerminalNode();
//
int tailNodeIndex = size_() - 1;
for (int i = 0; i <= tailNodeIndex; ) {
if (interpreter.hasNoBreakCode()) {
boolean isTail = (i == tailNodeIndex);
interpreter.setTailNode(isTail);
interpreter.setTerminalNode(isTail && cmdNode.size_() == 0);
Node terme = beta_reduction_(elementAt(i++));
if (terme != null)
cmdNode.addElement_(terme);
}
else
break;
}
interpreter.setTailNode(oldTailNode);
interpreter.setTerminalNode(oldTerminalNode);
//--
// evaluation et fin...
if (cmdNode.size_() > 0) { // optimisation 24/02/2011 : (cmdNode != null &&) pas nécessaire...
//
if (!interpreter.hasNoBreakCode()) {
int breakCode = interpreter.getBreakCode();
if (breakCode == Interpreter.BREAKCODE_RETURN || breakCode == Interpreter.BREAKCODE_TAIL) {
Node untest = cmdNode.firstElement_();
if ((!untest.quoted) && (untest.isPCode() || untest.isLazy_() || untest.isXSymbol_())) {
throw new InterpreterException(StdErrors.createTrace(this, new InterpreterException(StdErrors.Stack_less_error)));
}
}
}
//
interpreter.setTailNode(true);
interpreter.setTerminalNode(true);
Node resultNode = cmdNode.eval_q();
interpreter.setTailNode(oldTailNode);
interpreter.setTerminalNode(oldTerminalNode);
return resultNode;
}
// sinon...
return null;
}
// sinon...
/**
* évaluer ceci si l'analyse en profondeur n'est pas requise...
*/
if (size_() > 0) {
//d'où la suite...
boolean oldTailNode = interpreter.isTailNode();
boolean oldTerminalNode = interpreter.isTailNode();
interpreter.setTailNode(true);
interpreter.setTerminalNode(true);
Node resultNode = eval_q();
interpreter.setTailNode(oldTailNode);
interpreter.setTerminalNode(oldTerminalNode);
return resultNode;
}
// sinon...
return null;
}
/*
* dans le cas ou l'évaluation des sections lazy n'est pas autorisée...
*/
return this;
case TYPE_ALIST:
{
/*
* Cette partie doit être conservée pour permettre la création de liste vide et
* éviter qu'une liste ne contenant qu'un seul élément ne soit simplifiée par eval_q(...).
*
*/
/**
* Constitue l'évaluation d'une liste ou d'une paire associative déclarée.
*
* Ce traitement consiste donc à transformer une liste/paire associative déclarative en un
* éléments évalué que l'on peut placer dans une variable.
*
* Remarque: Les références des symboles ne doivent pas être duppliqués.
*
*/
Node cmdNode9 = createCList_();
cmdNode9.dTrace = this.dTrace;
int ssize = size_();
Interpreter interpreter = Interpreter.mySelf();
//
for (int i = 0; i < ssize; ) {
if (interpreter.hasNoBreakCode()) {
Node terme = beta_reduction_(elementAt_(i++));
if (terme != null) {
/**
* attention, ici il ne faut duppliquer les références résultantes de la déréférence des symboles.
*/
terme = getSymbolicValue_(terme);
cmdNode9.addElement_(terme.secure());
}
}
else
break;
}
//
//
//
return cmdNode9;
}
default:
/**
* tout autre type, le renvoyer lui-même...
*/
return this;
}
}
public Node exec(boolean doULink) throws Exception {
return exec_q(doULink);
}
/**
*
* @return Object
* @throws Exception
* @TODO
*/
public Object getExternalInstanceOf(Class<?>... types) throws InterpreterException {
Object res = null;
if (this.isExternal())
res = this.value;
if (res != null)
if (types != null)
for (int i = 0; i < types.length; )
if (types[i++].isAssignableFrom(res.getClass()))
return res;
Interpreter.Log("Require external class of:");
for (int i = 0; i < types.length; )
Interpreter.Log(" " + types[i++].getName());
Interpreter.Log("---> for : " + this);
throw new InterpreterException(StdErrors.Type_mismatch);
}
static public boolean isStringNumber(String v) {
boolean r = true;
int i = 0;
int sz = v.length();
while (i < sz) {
char c = v.charAt(i++);
boolean isDigit = ((c >= '0') && (c <= '9')) || (c == '.') || (c == '-') || (c == 'e') || (c == 'E');
if (!isDigit) {
r = false;
i = sz;
}
}
if (r) {
if (v.charAt(0) == '-') {
r = ((v.charAt(1) >= '0') && (v.charAt(1) <= '9'));
}
else {
r = ((v.charAt(0) >= '0') && (v.charAt(0) <= '9'));
}
}
return r;
}
static public boolean isHexNumber(String v) {
boolean r = true;
int sz = v.length();
if ((sz < 3) || (sz > 10)) {
r = false;
}
else if (v.charAt(0) != '0') {
r = false;
}
else if (v.charAt(1) != 'x') {
r = false;
}
else {
int i = 2; // on passe le 0x...
while (i < sz) {
char c = v.charAt(i++);
boolean isDigit = ((c >= '0') && (c <= '9')) || ((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f'));
if (!isDigit) {
r = false;
i = sz;
}
}
}
return r;
}
static public boolean isBinNumber(String v) {
boolean r = true;
int sz = v.length();
if ((sz < 3) || (sz > 34)) {
r = false;
}
else if (v.charAt(0) != '0') {
r = false;
}
else if (v.charAt(1) != 'b') {
r = false;
}
else {
int i = 2; // on passe le 0b...
while (i < sz) {
char c = v.charAt(i++);
boolean isDigit = ((c >= '0') && (c <= '1'));
if (!isDigit) {
r = false;
i = sz;
}
}
}
return r;
}
static public boolean isOctNumber(String v) {
boolean r = true;
int sz = v.length();
if ((sz < 3) || (sz > 13)) {
r = false;
}
else if (v.charAt(0) != '0') {
r = false;
}
else if (v.charAt(1) != 'o') {
r = false;
}
else {
int i = 2;
while (i < sz) {
char c = v.charAt(i++);
boolean isDigit = ((c >= '0') && (c <= '7'));
if (!isDigit) {
r = false;
i = sz;
}
}
}
if (r && (sz == 13)) {
r = v.compareTo("0o37777777777") <= 0;
}
return r;
}
final static private int countChars(String source, int sep) {
int res = 0;
int sln = source.length();
if (sln > 0) {
int i = 0;
while (i < sln) {
i = source.indexOf(sep, i);
if (i >= 0) {
res++;
}
else {
i = sln;
}
i++;
}
}
return ++res;
}
final static private String[] splitAtChar(String source, int sep) {
String[] str = new String[countChars(source, sep)];
//System.out.println("SymbolCount:"+str.length);
int res = 0;
int sln = source.length();
if (sln > 0) {
int i = 0;
int io = i;
while (i < sln) {
i = source.indexOf(sep, i);
if (i >= 0) {
if (i == 0) {
str[res++] = "";
}
else {
str[res++] = source.substring(io, i);
}
io = ++i;
}
else {
i = sln;
}
}
str[res++] = source.substring(io, sln);
}
else {
str[0] = source;
}
return str;
}
/**
* Vérifie si la partie de symbole foruni est valide.
* @param v
* @return
*/
final static private boolean isStringSymbolPart_(String v) {
/**
* récupérer et tester la longueur du symbole...
*/
int sz = v.length();
if (sz == 0)
return false;
/**
* Tester d'abord le premier caractère...
*
* - il ne peut s'agir que d'une lettre.
*/
int i = 0;
char s = v.charAt(i++);
/*
* Cas particulier des symboles de matching:
* ========================================
* Les symboles de matching commencent par un point d'interrogation. Toutefois, celui-ci doit obligatoirement être suivi
* d'un caractère alphabétique.
*
* On ne peut pas affecter ou assigner un symbole de matching.
*
*/
if (s == '?' && i < v.length())
s = v.charAt(i++);
if (!(((s >= 'a') && (s <= 'z')) || ((s >= 'A') && (s <= 'Z'))))
return false;
s = v.charAt(--sz);
/**
* Le symbole peut se terminer par ?, ! ou _, mais ces caractères doivent obligatoirement
* être précédé d'une lettre ou d'un chiffre...
*/
if ((s == '?') || (s == '!') || (s == '_')) {
if (sz == 0)
return false;
s = v.charAt(--sz);
}
if (!(((s >= 'a') && (s <= 'z')) || ((s >= 'A') && (s <= 'Z')) || ((s >= '0') && (s <= '9'))))
return false;
/**
* vérifier le reste... Le corps à proprement parlé
*/
while (i < sz) {
s = v.charAt(i++);
if (!(((s >= 'a') && (s <= 'z')) || ((s >= 'A') && (s <= 'z')) || ((s >= '0') && (s <= '9')) || (s == '-') || (s == '_') || (s == '.')))
return false;
}
return true;
}
/**
* Vérifie si la chaîne de caractères fournie constitue une forme valide de symbole.
* @param v0
* @return vrai/faux...
*/
static public boolean isStringSymbol(String v0) {
if (v0 == null)
return false;
if (v0.length() == 0)
return false;
String v = v0;
if (v.charAt(0) == '$')
v = v.substring(1);
int firstSep = v.indexOf(PCoder.SEP);
//if (firstSep == 0) {
// return false;
//}
//else
if (firstSep >= 0) {
String ids[] = splitAtChar(v, PCoder.SEP);
boolean res = true;
for (int i = 0; i < ids.length; i++) {
if (ids[i].length() != 0) {
// on test s'il y a un élément...
res = res && isStringSymbolPart_(ids[i]);
}
else if (i != 0) {
// on ignore UNIQUEMENT si le premier est vide... (synonyme de SELF)
return false;
}
if (!res)
i = ids.length;
}
return res;
}
else {
return isStringSymbolPart_(v);
}
}
static public boolean isStringFormatted(String v) {
boolean r = false;
int sz = v.length();
if (--sz >= 1)
r = (v.charAt(0) == '\"' && v.charAt(sz) == '\"') || (v.charAt(0) == '`' && v.charAt(sz) == '`');
return r;
}
final private static String hexToUnicode(String str) {
return "" + ((char) Integer.parseInt(str.toUpperCase(), 16));
}
final private static String hexToChar(String str) {
int len = str.length();
if (len < 2 || len % 2 == 1)
throw new IllegalArgumentException("Bad hexadecimal format.");
StringBuffer buf = new StringBuffer();
for (int i = 0; i < len; i += 2) {
String s = str.substring(i, i + 2);
int hex = Integer.parseInt(s.toUpperCase(), 16);
buf.append((char) hex);
}
return buf.toString();
}
public static double parseHex(String hex) {
int n = 0;
// attention on passe le 0x du début...
for (int i = 2; i < hex.length(); i++)
n = (n << 4) + Integer.parseInt(hex.substring(i, i + 1).toUpperCase(), 16);
return n;
}
public static double parseOct(String hex) {
int n = 0;
// attention on passe le 0o du début...
for (int i = 2; i < hex.length(); i++)
n = (n << 3) + Integer.parseInt(hex.substring(i, i + 1).toUpperCase(), 8);
return n;
}
public static double parseBin(String bin) {
int n = 0;
// on passe le 0b du début...
for (int i = 2; i < bin.length(); i++)
n = (n << 1) + Integer.parseInt(bin.substring(i, i + 1), 2);
return n;
}
static public String unescapeString(String s) {
StringBuilder r = new StringBuilder();
int i = 0;
char c;
boolean insertIt = true;
while (i < s.length()) {
c = s.charAt(i++);
if (c == '\\') {
insertIt = true;
c = s.charAt(i++);
if (c == 'r') {
c = '\r';
}
else if (c == 'n') {
c = '\n';
}
else if (c == 't') {
c = '\t';
}
else if (c == 'a') {
c = '\u0007';
}
else if (c == 'f') {
c = '\f';
}
else if (c == 'e') {
c = '\u001B';
}
else if (c == '\"') {
c = '\"';
}
else if (c == '\'') {
c = '\'';
}
else if (c == '`') {
c = '`';
}
else if (c == '\\') {
c = '\\';
}
else if (c == 'x') {
String hex = "" + s.charAt(i++) + s.charAt(i++);
hex = hex.toUpperCase();
c = hexToChar(hex).charAt(0);
}
else if (c == 'u') {
String hex = "" + s.charAt(i++) + s.charAt(i++) + s.charAt(i++) + s.charAt(i++);
hex = hex.toUpperCase();
r.append(hexToUnicode(hex));
insertIt = false;
}
else {
if (!Character.isISOControl(c))
throw new IllegalArgumentException("Bad format : \'\\" + c + "\'.");
insertIt = false;
}
if (insertIt)
r.append(c);
}
else {
if (!Character.isISOControl(c) || c == '\n') // exception pour délimiteur ` ... `...
r.append(c);
}
}
return r.toString();
}
static public String escapeString(String s) {
StringBuilder r = new StringBuilder();
int i = 0;
char c;
String cs = "";
while (i < s.length()) {
c = s.charAt(i++);
if (c == '\r') {
cs = "\\r";
}
else if (c == '\n') {
cs = "\\n";
}
else if (c == '\t') {
cs = "\\t";
}
else if (c == '\u0007') {
cs = "\\a";
}
else if (c == '\f') {
cs = "\\f";
}
else if (c == '\u001B') {
cs = "\\e";
}
else if (c == '\\') {
cs = "\\\\";
}
else if (c == '\'') {
cs = "\\\'";
}
else if (c == '\"') {
cs = "\\\"";
}
else if (c == '`') {
cs = "\\`";
}
else if (c < 32) {
String toHex = "00" + Integer.toHexString(c);
cs = "\\x" + toHex.substring(toHex.length() - 2, toHex.length());
}
else if (c > 255) {
String toHex = "0000" + Integer.toHexString(c);
cs = "\\u" + toHex.substring(toHex.length() - 4, toHex.length());
}
else {
cs = "" + c;
}
r.append(cs);
}
return r.toString();
}
public void isGoodArgsLength(boolean fixed, int minArgs) throws Exception {
if (fixed) {
if (size_() != minArgs)
throw new InterpreterException(StdErrors.Argument_count_mismatch);
}
else {
if (size_() < minArgs)
throw new InterpreterException(StdErrors.Argument_count_mismatch);
}
}
/**
* @//deprecated isGoodArgsLenght_EXT() remplacer par isGoodArgsLenght() ou isGoodArgsCnt()
*/
public void isGoodArgsCnt(int argsCnt) throws Exception {
if (size_() != argsCnt)
throw new InterpreterException(StdErrors.Argument_count_mismatch);
}
public void isGoodArgsCnt(int... argsCnt) throws Exception {
int ss = size_();
for (int i = 0; i < argsCnt.length; )
if (ss == argsCnt[i++])
return;
throw new InterpreterException(StdErrors.Argument_count_mismatch);
}
public void requirePCode(int pcode) throws InterpreterException {
if (!this.isPCode_(pcode))
throw new InterpreterException(StdErrors.extend(StdErrors.Syntax_error, "\'" + PCoder.getReserved(pcode) + "\' expected."));
}
public void requirePCode(int index, int pcode) throws Exception {
if (!getSymbolicValue_(elementAt_(index)).isPCode_(pcode))
throw new InterpreterException(StdErrors.extend(StdErrors.Syntax_error, "\'" + PCoder.getReserved(pcode) + "\' expected."));
// from attendu...
}
public boolean isSymbol() {
return type == TYPE_SYMBOL;
}
public boolean isQSymbol() {
return type == TYPE_SYMBOL && quoted;
}
public boolean isString() {
return type == TYPE_STRING; //( (this.type & Node.TYPE_STRING) != 0);
}
public boolean isNumber() {
return type == TYPE_NUMBER; //( (this.type & Node.TYPE_NUMBER) != 0);
}
public boolean isNothing() {
return type == TYPE_NOTHING; //( (this.type & Node.TYPE_NOTHING) != 0);
}
final private boolean isExpr_() {
return type == TYPE_EXPR;
}
public boolean isExpr() {
return type == TYPE_EXPR;
}
public boolean isLazy() {
return type == TYPE_LAZY; //( (this.type & Node.TYPE_FUNCTION) != 0);
}
private boolean isLazy_() {
return type == TYPE_LAZY;
}
public boolean isPattern() {
return type == TYPE_PATTERN;
}
public boolean isInterface() {
return type == TYPE_INTERFACE;
}
public boolean isPCode() {
return type == TYPE_PCODE; //( (this.type & Node.TYPE_PCODE) != 0);
}
private boolean isPCode_(int pcode) {
return (type == TYPE_PCODE) && (getPCFx_().getPcode().getCode() == pcode);
}
public boolean isPCode(int pcode) {
return (type == TYPE_PCODE) && (getPCFx_().getPcode().getCode() == pcode);
// optimisation du 28/02/2011: tester d'abord le code avant le type.
// optimisation du 09/03/2012: utiliser des méthodes privées.
}
public boolean isNamespace() {
return type == TYPE_NAMESPACE;
}
public boolean isDelegable() {
return (type & VTYPE_DELEGABLE) != 0;
}
public boolean isNeedSelf() {
return (type & VTYPE_NEEDSELF) != 0;
}
public boolean isScope() {
return type == TYPE_SCOPE;
}
public boolean isFunction() {
return type == TYPE_FUNCTION;
}
/**
* Le type du Node nécessite le transport du Heap local dans lequel la données est
* stockée. Bien entendu, ce type de Node est différent de VObject qui transporte
* d'office son propre heap.
*
* Dans l'état actuel du développement, cette méthode ne concerne que le type
* Function (pour implémenter SELF).
*
* @return boolean
*/
public boolean isNeedLocalHeapVType() {
return ((this.type & Node.VTYPE_NEED_LOCALHEAP) != 0);
}
public boolean isExternal() {
return type == TYPE_EXTERNAL;
}
public boolean isValuable() {
return ((this.type & Node.VTYPE_VALUABLE) != 0);
}
public boolean isSameType(Node otherNode) {
return (this.type == otherNode.type);
}
static public boolean isTrueEquivalent(Node xnode) throws InterpreterException {
if (xnode == null)
return false;
if (xnode.quoted)
throw new InterpreterException(StdErrors.Cannot_estimate_parameter);
switch (xnode.type) {
case Node.TYPE_NUMBER:
return (xnode.getNumber_() != FALSE);
case Node.TYPE_NOTHING:
return false;
case Node.TYPE_STRING:
return (((String) xnode.value).length() > 0);
case Node.TYPE_CLIST:
return (xnode.size_() > 0);
default:
throw new InterpreterException(StdErrors.Cannot_estimate_parameter);
}
}
public static boolean isSerializable(Node node) throws Exception {
try {
node.nodeToSerializedString(Node.createCList_()); // si le node ne peut être sérialisé, on lance une exception...
}
catch (Exception e) {
return false; // ici on récupere l'exception sans la propager...
}
return true;
}
/*public static boolean isMemoizable(Node node) throws Exception {
//
// La mémoization ne peut être réalisée de façon raisonable que sur tous les éléments
// sérilisables!!!.
///
return isSerializable(node);
}*/
/**
* La classe utilitaire Node.VDelegable permet de regrouper les méthodes les plus utiles pour le traitement des Delegable Objects.
*
*/
final public static class VDelegable {
/**
* Permet de savoir si le Delegable dispose du trait dont le nom symbolique et le type est fourni.
*
* @param self : Node de l'objet delegable
* @param symbol : Symbole à rechercher
* @param qtype : Masque QType des types primaires acceptés.
* @return true / false
* @throws Exception
*/
public static final boolean hasSlot(Node self, String symbol, long qtype) {
switch (self.type) {
case Node.TYPE_NAMESPACE:
case Node.TYPE_EXTERNAL:
return Heap.exists(self, new ASymbol(symbol), qtype);
case Node.TYPE_FUNCTION:
Node s = Function.TK.getSlot(self, symbol);
return (s != null && (s.getQType_() & qtype) != 0);
default:
return false;
}
}
/**
* Idem que hasSlot(Node, String,long), mais en se basant uniquement sur le nom symbolique.
*
* @param self : Node de l'objet delegable
* @param symbol : Symbole à rechercher
* @return true / false
*/
public static final boolean hasSlot(Node self, String symbol) {
switch (self.type) {
case Node.TYPE_NAMESPACE:
case Node.TYPE_EXTERNAL:
return Heap.exists(self, new ASymbol(symbol));
case Node.TYPE_FUNCTION:
Node s = Function.TK.getSlot(self, symbol);
return (s != null);
default:
return false;
}
}
public static final ArrayList<String> slotsOf(Node self) throws Exception {
switch (self.type) {
case Node.TYPE_NAMESPACE:
return ((Heap) self.getExternal()).getSlots();
case Node.TYPE_EXTERNAL:
return ExternalTK.getExternalSlots(self);
case Node.TYPE_FUNCTION:
return Function.TK.getSlots(self);
default:
throw new InterpreterException(StdErrors.extend(StdErrors.Delegable_object_required, self.toString()));
}
}
/**
* Retourne le contenu du trait dont le nom symbolique est fourni.
*
* @param self : Node de l'objet delegable
* @param symbol : Symbole à rechercher
* @return slot / null
*/
public static final Node getSlot(Node self, String symbol) {
switch (self.type) {
case Node.TYPE_NAMESPACE:
case Node.TYPE_EXTERNAL:
return Heap.getv_unsafe_or_null(self, new ASymbol(symbol));
case Node.TYPE_FUNCTION:
return Function.TK.getSlot(self, symbol);
default:
return null;
}
}
/**
* Idem, mais en tenant compte d'un masque de types acceptés.
*
* Si le trait n'est pas d'un type accepté, une exception est déclenchée.
*
* Si le trait n'existe pas, une exception est également déclenchée.
*
* @param self : Node de l'objet delegable
* @param symbol : Symbole à rechercher
* @param qtype : Masque QType des types primaires acceptés.
* @return slot : Node du type primaire QType
* @throws InterpreterException
*/
public static final Node getSlot(Node self, String symbol, long qtype) throws InterpreterException {
Node slot;
switch (self.type) {
case Node.TYPE_NAMESPACE:
case Node.TYPE_EXTERNAL:
slot = Heap.getv_unsafe(self, new ASymbol(symbol));
break;
case Node.TYPE_FUNCTION:
slot = Function.TK.getSlot(self, symbol);
break;
default:
return null;
}
slot.requireNodeType(qtype);
return slot;
}
/**
* Evalue la méthode dont le nom symbolique est indiqué en lui fournissant la liste d'arguments argv.
*
* Si argv est null, la méthode est appelée avec une liste argv vide [].
*
* On notera que le nom symbolique de la méthode est automatiquement "selfiée" (précédée par ':'), si
* cela est nécessaire.
*
* @param self : Node de l'objet delegable
* @param symbol : Symbole à rechercher
* @param argv : Liste d'arguments ou null s'il n'y en a pas.
* @return le Node résultat ou null
* @throws Exception
*/
public static final Node evalMethod(Node self, String symbol, Node argv) throws Exception {
switch (self.type) {
case Node.TYPE_NAMESPACE:
{
if (self.isNamespace()) {
ContextualCall ccall = ContextualCall.createContextualCall(self, symbol);
if (ccall != null)
return ccall.call(argv);
else
throw new InterpreterException(StdErrors.extend(StdErrors.Symbol_not_defined, symbol));
}
else
throw new InterpreterException(StdErrors.Internal_error);
}
case Node.TYPE_EXTERNAL:
{
Node extargs = Node.createCList_().append_(Node.createQSymbol(symbol));
if (argv != null)
extargs.appendChildsOf(argv);
return ExternalTK.evalMethod(self, extargs);
}
case Node.TYPE_FUNCTION:
{
Node expr = new Node().append(self).append_(Node.createQSymbol(PCoder.unselfing(symbol)));
if (argv != null)
expr.appendChildsOf(argv);
return expr.exec_q(false);
}
default:
throw new InterpreterException(StdErrors.extend(StdErrors.Delegable_object_required, self.toString()));
}
}
/**
* Evalue la méthode unaire dont le nom symbolique est indiqué en lui fournissant son argument unique.
*
* Si arg0 est null, la méthode est appelée avec une liste argv vide [].
*
* On notera que le nom symbolique de la méthode est automatiquement "selfiée" (précédée par ':'), si
* cela est nécessaire.
*
* @param self : Node de l'objet delegable
* @param symbol : Symbole à rechercher
* @param arg0 : Argument unique ou null s'il n'y en a pas.
* @return le Node résultat ou null
* @throws Exception
*/
public static final Node evalMethod_unary(Node self, String symbol, Node arg0) throws Exception {
switch (self.type) {
case Node.TYPE_NAMESPACE:
{
if (self.isNamespace()) {
ContextualCall ccall = ContextualCall.createContextualCall(self, symbol);
if (ccall != null)
return ccall.call_unary(arg0);
else
throw new InterpreterException(StdErrors.extend(StdErrors.Symbol_not_defined, symbol));
}
else
throw new InterpreterException(StdErrors.Internal_error);
}
case Node.TYPE_EXTERNAL:
{
Node extargs = Node.createCList_().append_(Node.createQSymbol(symbol));
if (arg0 != null)
extargs.addElement_(arg0);
return ExternalTK.evalMethod(self, extargs);
}
case Node.TYPE_FUNCTION:
{
Node expr = new Node().append(self).append_(Node.createQSymbol(PCoder.unselfing(symbol)));
if (arg0 != null)
expr.append(arg0);
return expr.exec_q(false);
}
default:
throw new InterpreterException(StdErrors.extend(StdErrors.Delegable_object_required, self.toString()));
}
}
/**
* Evalue la méthode dont le nom symbolique est indiqué en lui fournissant la liste d'arguments argv.
*
* Si argv est null, la méthode est appelée avec une liste argv vide [].
*
* Si la méthode appelée n'existe pas, evalMethod_or_null() retourne null sans produire d'exception.
*
* On notera que le nom symbolique de la méthode est automatiquement "selfiée" (précédée par ':'), si
* cela est nécessaire.
*
* @param self : Node de l'objet delegable
* @param symbol : Symbole à rechercher
* @param argv : Liste d'arguments ou null s'il n'y en a pas.
* @return le Node résultat ou null
* @throws Exception
*/
public static final Node evalMethod_or_null(Node self, String symbol, Node argv) throws Exception {
switch (self.type) {
case Node.TYPE_NAMESPACE:
{
if (self.isNamespace()) {
ContextualCall ccall = ContextualCall.createContextualCall(self, symbol);
if (ccall != null)
return ccall.call(argv);
else
return null;
}
else
return null;
}
case Node.TYPE_EXTERNAL:
{
Node extargs = Node.createCList_().append_(Node.createQSymbol(symbol));
if (argv != null)
extargs.appendChildsOf(argv);
return ExternalTK.evalMethod_or_null(self, extargs);
}
case Node.TYPE_FUNCTION:
{
String symb = PCoder.unselfing(symbol);
if (Function.TK.getSlot(self, symb) != null) {
Node expr = new Node().append(self).append_(Node.createQSymbol(symb));
if (argv != null)
expr.appendChildsOf(argv);
return expr.exec_q(false);
}
else
return null;
}
default:
throw new InterpreterException(StdErrors.extend(StdErrors.Delegable_object_required, self.toString()));
}
}
/**
* Evalue la méthode dont le nom symbolique est indiqué en lui fournissant la liste d'arguments argvx
* à extraire.
*
* Si argvx est null, la méthode est appelée avec une liste argv vide [].
*
* Le paramètre index indique la position de départ des éléments de argv à considérer.
* Ainsi, si index vaut 2, la méthode sera appelée une liste argv obtenue en extrayant les élément de
* argvx à partir de la position 2 (soit si argvx -> [0 1 2 .. n], argv -> [2 .. n]).
*
* Si la méthode appelée n'existe pas, evalMethod_or_null() retourne null sans produire d'exception.
*
* On notera que le nom symbolique de la méthode est automatiquement "selfiée" (précédée par ':'), si
* cela est nécessaire.
*
* @param self : Node de l'objet delegable
* @param symbol : Symbole à rechercher
* @param argvx : Liste d'arguments à extraire ou null s'il n'y en a pas.
* @param index : Position de départ de l'extraction des arguments s'il y en a.
* @return le Node résultat ou null
* @throws Exception
*/
public static final Node evalMethod_or_null(Node self, String symbol, Node argvx, int index) throws Exception {
switch (self.type) {
case Node.TYPE_NAMESPACE:
{
if (self.isNamespace()) {
ContextualCall ccall = ContextualCall.createContextualCall(self, symbol);
if (ccall != null)
return ccall.call(argvx, index);
else
return null;
}
else
return null;
}
case Node.TYPE_EXTERNAL:
{
Node extargs = Node.createCList_().append_(Node.createQSymbol(symbol));
if (argvx != null)
extargs.appendChildsOf(argvx, index);
return ExternalTK.evalMethod_or_null(self, extargs);
}
case Node.TYPE_FUNCTION:
{
String symb = PCoder.unselfing(symbol);
if (Function.TK.getSlot(self, symb) != null) {
Node expr = new Node().append(self).append_(Node.createQSymbol(symb));
if (argvx != null)
expr.appendChildsOf(argvx, index);
return expr.exec_q(false);
}
else
return null;
}
default:
throw new InterpreterException(StdErrors.extend(StdErrors.Delegable_object_required, self.toString()));
}
}
/**
* Evalue la méthode unaire dont le nom symbolique est indiqué en lui fournissant son argument unique.
*
* Si arg0 est null, la méthode est appelée avec une liste argv vide [].
*
* Si la méthode appelée n'existe pas, evalMethod_unary_or_null() retourne null sans produire d'exception.
*
* On notera que le nom symbolique de la méthode est automatiquement "selfiée" (précédée par ':'), si
* cela est nécessaire.
*
* @param self : Node de l'objet delegable
* @param symbol : Symbole à rechercher
* @param arg0 : Argument unique ou null s'il n'y en a pas.
* @return le Node résultat ou null
* @throws Exception
*/
public static final Node evalMethod_unary_or_null(Node self, String symbol, Node arg0) throws Exception {
switch (self.type) {
case Node.TYPE_NAMESPACE:
{
if (self.isNamespace()) {
ContextualCall ccall = ContextualCall.createContextualCall(self, symbol);
if (ccall != null)
return ccall.call_unary(arg0);
else
return null;
}
else
return null;
}
case Node.TYPE_EXTERNAL:
{
Node extargs = Node.createCList_().append_(Node.createQSymbol(symbol));
if (arg0 != null)
extargs.addElement_(arg0);
return ExternalTK.evalMethod_or_null(self, extargs);
}
case Node.TYPE_FUNCTION:
{
String symb = PCoder.unselfing(symbol);
if (Function.TK.getSlot(self, symb) != null) {
Node expr = new Node().append(self).append_(Node.createQSymbol(symb));
if (arg0 != null)
expr.appendChildsOf(arg0);
return expr.exec_q(false);
}
else
return null;
}
default:
throw new InterpreterException(StdErrors.extend(StdErrors.Delegable_object_required, self.toString()));
}
}
/**
* Evalue la méthode dont le nom symbolique est indiqué en lui fournissant la liste d'arguments argvx
* à extraire.
*
* Si argvx est null, la méthode est appelée avec une liste argv vide [].
*
* Le paramètre index indique la position de départ des éléments de argv à considérer.
* Ainsi, si index vaut 2, la méthode sera appelée une liste argv obtenue en extrayant les élément de
* argvx à partir de la position 2 (soit si argvx -> [0 1 2 .. n], argv -> [2 .. n]).
*
* On notera que le nom symbolique de la méthode est automatiquement "selfiée" (précédée par ':'), si
* cela est nécessaire.
*
* @param self : Node de l'objet delegable
* @param symbol : Symbole à rechercher
* @param argvx : Liste d'arguments à extraire ou null s'il n'y en a pas.
* @param index : Position de départ de l'extraction des arguments s'il y en a.
* @return le Node résultat ou null
* @throws Exception
*/
public static final Node evalMethod(Node self, String symbol, Node argvx, int index) throws Exception {
switch (self.type) {
case Node.TYPE_NAMESPACE:
{
if (self.isNamespace()) {
ContextualCall ccall = ContextualCall.createContextualCall(self, symbol);
if (ccall != null && argvx != null)
return ccall.call(argvx, index);
else
throw new InterpreterException(StdErrors.extend(StdErrors.Symbol_not_defined, symbol));
}
else
throw new InterpreterException(StdErrors.Internal_error);
}
case Node.TYPE_EXTERNAL:
{
Node extargs = Node.createCList_().append_(Node.createQSymbol(symbol));
if (argvx != null)
extargs.appendChildsOf(argvx, index);
return ExternalTK.evalMethod(self, extargs);
}
case Node.TYPE_FUNCTION:
{
Node expr = new Node().append(self).append_(Node.createQSymbol(PCoder.unselfing(symbol)));
if (argvx != null)
expr.appendChildsOf(argvx, index);
return expr.exec_q(false);
}
default:
throw new InterpreterException(StdErrors.extend(StdErrors.Delegable_object_required, self.toString()));
}
}
}
/*****************************************************************************************************
*
* Conversion node2???:
* ====================
*
* Méthodes de conversion.
*
*/
/**
* Convertir un nombre vers une chaîne de caractères (String).
* La conversion s'efforce d'éléiminer les décimales inutiles.
*
* @param number
* @return String
*/
private static String number2string(double number) {
String nstr;
if (number == ((long) number)) {
nstr = "" + ((long) number);
}
else {
nstr = "" + number;
}
return autoFormatSYMBOL(nstr);
}
/**
* Convertir un Node vers un autre Node du type vString.
*
* @param xnode
* @return Node
* @throws Exception
*/
public static Node node2VString(Node xnode) throws Exception {
/**
* Convertion de type -> VString selon le type d'origine
*/
long qt=xnode.getQType_();
if (qt==Node.TYPE_STRING)
return new Node((String) xnode.value);
if (qt==Node.TYPE_NUMBER)
return new Node(number2string(xnode.getNumber_()));
if (qt==Node.TYPE_BYTES){
Bytes bytes=(Bytes) xnode.value;
return new Node(bytes.peek_string(0, bytes.length()));
}
if (xnode.isDelegable()) {
Node rnode = VDelegable.evalMethod_or_null(xnode, PCoder.getMethod(PCoder.PC_STRING), null);
if (rnode == null)
rnode = new Node(xnode.toString());
return rnode;
}
return new Node(xnode.toString());
}
/**
* Convertir un Node vers un Node de type vNumber.
*
* Remarque: Ceci n'est pas possible avec tous les types de Node.
*
* @param xnode
* @return
* @throws Exception
*/
public static Node node2VNumber(Node xnode) throws Exception {
/**
* Convertion de type -> VNumber selon le type d'origine
*/
Node rnode = null;
if (xnode.isNumber()) {
rnode = new Node(xnode.getNumber_());
}
else if (xnode.isString()) {
String sval = (String) xnode.value;
if (Node.isStringNumber(sval) || Node.isHexNumber(sval) || Node.isBinNumber(sval) || Node.isOctNumber(sval)) {
try {
if (Node.isStringNumber(sval)) {
rnode = new Node(Double.parseDouble(sval));
}
else if (Node.isHexNumber(sval)) {
rnode = new Node(Node.parseHex(sval));
}
else if (Node.isBinNumber(sval)) {
rnode = new Node(Node.parseBin(sval));
}
else if (Node.isOctNumber(sval)) {
rnode = new Node(Node.parseOct(sval));
}
else {
throw new InterpreterException(StdErrors.Internal_error);
}
}
catch (Exception ex) {
if (Interpreter.isDebugMode()) {
ex.printStackTrace();
}
throw new InterpreterException(StdErrors.extend(StdErrors.Cannot_estimate_parameter, "malformed number"));
}
}
else {
throw new InterpreterException(StdErrors.extend(StdErrors.Cannot_estimate_parameter, "malformed number"));
}
}
else if (xnode.isDelegable()) {
rnode = VDelegable.evalMethod(xnode, PCoder.getMethod(PCoder.PC_NUMBER), null);
}
else {
throw new InterpreterException(StdErrors.extend(StdErrors.Cannot_estimate_parameter, "can not be converted"));
}
return rnode;
}
public static Node node2VBytes(Node arg0) throws Exception {
/**
* Convertion de type -> VBytes selon le type d'origine
*/
Node rnode = null;
long qt=arg0.getQType();
if(qt==Node.TYPE_NUMBER)
return Node.createBytes(new Bytes((int)arg0.getNumber()));
else if(qt==Node.TYPE_STRING)
return Node.createBytes(new Bytes(arg0.getString()));
else if(qt==Node.TYPE_BYTES)
return Node.createBytes( (Bytes) ((Bytes)arg0.value).clone_my_self(null) );
else if(qt==Node.TYPE_CLIST){
Bytes bytes=new Bytes(arg0.size());
for(int i=0;i<arg0.size();i++){
double ref=arg0.getSubNode(i,Node.TYPE_NUMBER).getNumber();
int octet=((int)ref) & 0xFF;
if(octet!=ref)
throw new InterpreterException(StdErrors.extend(StdErrors.Invalid_parameter,arg0.getSubNode(i,Node.TYPE_NUMBER).toString()));
bytes.poke(i, (byte) octet);
}
return Node.createBytes(bytes);
}
else if (arg0.isDelegable())
/**
* utilise la méthode bytes de l'objet délégable.
*/
return VDelegable.evalMethod(arg0, PCoder.getMethod(PCoder.PC_BYTES), null);
else {
throw new InterpreterException(StdErrors.extend(StdErrors.Cannot_estimate_parameter, "can not be converted"));
}
}
public static Node node2VList(Node xnode) throws Exception {
/**
* Convertion de type -> VList selon le type d'origine
*/
Node rnode = null;
long qt=xnode.getQType_();
if (qt==Node.TYPE_NUMBER) {
/**
* créer une liste de n éléments dont la valeur est NOTHING par défaut...
*/
int siz = (int) Math.rint(xnode.getNumber_());
if (siz < 0) {
throw new InterpreterException(StdErrors.extend(StdErrors.Out_of_range, "" + siz));
}
rnode = createCList_();
for (int i = 0; i < siz; i++) {
rnode.addElement_(Node.createNothing());
}
}
else if (qt==Node.TYPE_STRING) {
/**
* créer une liste à partir des caractères de la chaîne...
*/
rnode = createCList_();
String tmpStr = (String) xnode.value;
int tmpStrLen = tmpStr.length();
StringBuffer tmpStrBuf = new StringBuffer();
for (int i = 0; i < tmpStrLen; ) {
tmpStrBuf.setLength(0);
rnode.addElement_(new Node(tmpStrBuf.append(tmpStr.charAt(i++)).toString()));
}
}
else if (qt==Node.TYPE_BYTES) {
/**
* créer une liste à partir des octets de bytes...
*/
rnode = createCList_();
Bytes tmp = (Bytes) xnode.value;
for (int i=0;i<tmp.length(); )
rnode.addElement_(new Node(tmp.peek(i++)));
}
else if (qt==Node.TYPE_CLIST) {
/**
* copier la strcuture de la liste...
*/
rnode = Node.createCList();
for(int i=0;i<xnode.size();i++)
rnode.addElement(xnode.elementAt(i).secure());
}
else if (xnode.isDelegable()) {
/**
* utilise la méthode list de l'objet délégable.
*/
//if (has_list_(xnode.objHeap))
rnode = VDelegable.evalMethod(xnode, PCoder.getMethod(PCoder.PC_LIST), null);
//else
// throw new InterpreterException(StdErrors.extend(StdErrors.Cannot_estimate_parameter, "can not be converted"));
}
else {
throw new InterpreterException(StdErrors.extend(StdErrors.Cannot_estimate_parameter, "can not be converted"));
}
return rnode;
}
public static int compareNodes(Node a, Node b) throws Exception {
/**
* Compare les valeurs des nodes a et b.
* Retourne: 0 si leur valeur est équivalente
* -1 si a < b
* +1 si a > b
* En cas de problème (ex: types incomparables), la fonction
* déclance une exception.
*/
int res = 0; // 0 : a == b, -1: a < b, 1: a > b...
/*
* Types délégables
*/
if (!a.quoted && a.isDelegable()) {
Interpreter interpreter = Interpreter.mySelf();
NodeTags compareStack_A = interpreter.getCompareStack_A();
NodeTags compareStack_B = interpreter.getCompareStack_B();
int lock_A = compareStack_A.lock();
int lock_B = compareStack_B.lock();
compareStack_A.push(a);
compareStack_B.push(b);
if (a.type == b.type && compareStack_A.count(a) >= 2 && compareStack_B.count(b) >= 2) {
compareStack_A.unlock(lock_A);
compareStack_B.unlock(lock_B);
return 0;
// on retourne ici, car il s'agit d'une définition circulaire où les éléments sont les mêmes...
}
Node rn = Node.VDelegable.evalMethod_unary_or_null(a, PCoder.getMethod(PCoder.PC_GT_DEFAULT_COMPARE), b);
if (rn == null)
throw new InterpreterException(StdErrors.Object_not_comparable);
else if (rn.type != TYPE_NUMBER)
throw new InterpreterException(StdErrors.Object_not_comparable);
// on fini toujours par ici dans le cas d'un délégable...
compareStack_A.unlock(lock_A);
compareStack_B.unlock(lock_B);
return (int) rn.getNumber_();
}
/*
* Types non délégables
*/
switch (a.type) {
case TYPE_NUMBER:
if (a.type != b.type)
throw new InterpreterException(StdErrors.Object_not_comparable);
// comparaison de nombres...
if (a.getNumber_() > b.getNumber_())
return 1;
else if (a.getNumber_() < b.getNumber_())
return -1;
else
return 0;
case TYPE_STRING:
if (a.type != b.type)
throw new InterpreterException(StdErrors.Object_not_comparable);
// comparaison de chaînes...
return a.getString().compareTo(b.getString());
case TYPE_CLIST:
{
if (a.type != b.type)
throw new InterpreterException(StdErrors.Object_not_comparable);
// cas de listes, on se fie à leurs longueurs respectives... -> NUMBER
int lca = a.size_();
int lcb = b.size_();
if (lca > lcb) {
return 1;
}
else if (lca < lcb) {
return -1;
}
else {
// égalité de longueur...
Interpreter interpreter = Interpreter.mySelf();
NodeTags compareStack_A = interpreter.getCompareStack_A();
NodeTags compareStack_B = interpreter.getCompareStack_B();
int lock_A = compareStack_A.lock();
int lock_B = compareStack_B.lock();
compareStack_A.push(a);
compareStack_B.push(b);
if (compareStack_A.count(a) >= 2 && compareStack_B.count(b) >= 2) {
compareStack_A.unlock(lock_A);
compareStack_B.unlock(lock_B);
return 0;
// on retourne ici, car il s'agit d'une définition circulaire où les éléments sont les mêmes...
}
for (int i = 0; i < lca; i++) {
res = Node.compareNodes(a.elementAt_(i), b.elementAt_(i));
if (res != 0) {
compareStack_A.unlock(lock_A);
compareStack_B.unlock(lock_B);
return res;
}
}
// cas d'égalité...
compareStack_A.unlock(lock_A);
compareStack_B.unlock(lock_B);
return 0;
}
}
// comparer les données de types comparables (limité aux nombres et aux chaînes)...
default:
throw new InterpreterException(StdErrors.Object_not_comparable);
}
}
/**
* Retourne true si a est EQUIPOLENT à b.
*
* Le test est générique et prend a comme argument privilégié. Cela signifie
* que si a est un Délégable et qu'il contient la méthode :compare, celle-ci
* est appelée avec b comme argument. Le test est alors délégué à l'objet
* délégable.
*
* Si a n'est pas un Délégable, ou s'il est Délégable mais ne dispose pas de
* la méthode :compare, les éléments internes sont testés un à un.
*
* Si a n'est pas un Délégable, il s'agit d'un type primaire. Le test est alors
* réalisé sans délégation.
*
* ===========================================================================
* Dans le cas de la circularité:
* -----------------------------
* Le test d'égalité se poursuit tant qu'il y a des éléments et que ceux qui
* ont déjà été examinés sont équipolents. De ce fait, en cas de circularité
* on peut entrer dans une boucle infinie.
*
* ===========================================================================
*
* @param a
* @param b
* @return
* @throws Exception
*/
public static boolean equalsNodes(Node a, Node b) throws Exception {
/**
* Si a n'a pas le même état de quotation que b, ils sont différents...
*/
if (a.quoted != b.quoted)
return false;
/**
* Si a et b ont le même UID, il s'agit de la même entité, ils sont
* forcément égaux.
*/
if (a.getUID_() == b.getUID_())
return true;
/**
* On switch l'algorithme en fonction du type de a...
*/
// vérifier le transtypage... TOUS les cas d'exclusion !...
switch (a.type) {
case TYPE_NOTHING:
return b.type == a.type;
case TYPE_NUMBER:
return b.type == a.type && a.getNumber() == b.getNumber();
case TYPE_STRING:
return b.type == a.type && a.getString().equals(b.getString());
case TYPE_SYMBOL:
return b.type == a.type && a.getSymbol().equals(b.getSymbol());
case TYPE_PCODE:
return b.type == a.type && a.getPCFx_Code() == b.getPCFx_Code();
case TYPE_PATTERN:
return b.type == a.type && ((Pattern) a.value).equals((Pattern) b.value);
case TYPE_INTERFACE:
return b.type == a.type && ((Interface) a.value).equals((Interface) b.value);
case TYPE_ALIST:
case TYPE_CLIST:
case TYPE_EXPR:
case TYPE_LAZY:
{
if (b.type != a.type)
return false;
if (a.size_() != b.size_())
return false;
Interpreter interpreter = Interpreter.mySelf();
NodeTags equStack_A = interpreter.getEquStack_A();
NodeTags equStack_B = interpreter.getEquStack_B();
// déjà testé ou pas ?
int lock_A = equStack_A.lock();
int lock_B = equStack_B.lock();
equStack_A.push(a);
equStack_B.push(b);
if (equStack_A.count(a) >= 2 && equStack_B.count(b) >= 2) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return true;
// on retourne ici, car il s'agit d'une définition circulaire où les éléments sont les mêmes...
}
for (int i = a.size_() - 1; i >= 0; i--) {
if (!Node.equalsNodes(a.elementAt_(i), b.elementAt_(i))) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return false;
// on retourne ici, après avoir restauré les equStack...
}
}
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return true;
// on retourne ici, après avoir restauré les equStack...
}
case TYPE_SCOPE:
{
if (b.type != a.type)
return false;
Heap ha = (Heap) a.getExternal();
Heap hb = (Heap) b.getExternal();
ArrayList<String> ca_s = ha.getSlots();
ArrayList<String> cb_s = hb.getSlots();
if (ca_s.size() != cb_s.size())
return false;
/*
* Il est possible de vérifier si les scopes sont équipolents.
*
* Pour cela, il faut tester un a un les traits.
* Il s'agit d'un test en profondeur assez long.
*
*/
Interpreter interpreter = Interpreter.mySelf();
NodeTags equStack_A = interpreter.getEquStack_A();
NodeTags equStack_B = interpreter.getEquStack_B();
// déjà testé ou pas ?
int lock_A = equStack_A.lock();
int lock_B = equStack_B.lock();
equStack_A.push(a);
equStack_B.push(b);
if (equStack_A.count(a) >= 2 && equStack_B.count(b) >= 2) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return true;
// on retourne ici, car il s'agit d'une définition circulaire où les éléments sont les mêmes...
}
for (int i = 0; i < ca_s.size(); i++) {
String symbol = ca_s.get(i);
Node cb_i = hb.get(symbol); // rechercher le Node lié au même symbole dans cb.
// si le symbole n'a pas de valeur, ce n'est pas equipolent...
if (cb_i == null) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return false;
}
Node ca_i = ha.get(symbol); // rechercher le Node pour pouvoir le tester...
if (ca_i == null) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return false;
}
if (!Node.equalsNodes(ca_i, cb_i)) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return false;
}
}
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return true;
}
case TYPE_EXTERNAL:
{
Interpreter interpreter = Interpreter.mySelf();
NodeTags equStack_A = interpreter.getEquStack_A();
NodeTags equStack_B = interpreter.getEquStack_B();
// déjà testé ou pas ?
int lock_A = equStack_A.lock();
int lock_B = equStack_B.lock();
equStack_A.push(a);
equStack_B.push(b);
if (b.type == a.type && equStack_A.count(a) >= 2 && equStack_B.count(b) >= 2) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return true;
// on retourne ici, car il s'agit d'une définition circulaire où les éléments sont les mêmes...
}
Node res = a.quoted ? null: Node.VDelegable.evalMethod_unary_or_null(a, PCoder.getMethod(PCoder.PC_EQUALS), b);
if (res != null) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return Node.equalsNodes(new Node(TRUE), res);
// ne peut jamais être circulaire ici (compare à -1)...
}
res = a.quoted ? null: Node.VDelegable.evalMethod_unary_or_null(a, PCoder.getMethod(PCoder.PC_GT_DEFAULT_COMPARE), b);
if (res != null) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return Node.equalsNodes(new Node(0), res);
// ne peut jamais être circulaire ici compare à 0)...
}
// si pas du même type...
if (b.type != a.type) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return false;
}
// plus d'autre possibilité...
boolean r = (a.value.equals(b.value));
// dans tous les cas...
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return r;
}
case TYPE_NAMESPACE:
{
Interpreter interpreter = Interpreter.mySelf();
NodeTags equStack_A = interpreter.getEquStack_A();
NodeTags equStack_B = interpreter.getEquStack_B();
// déjà testé ou pas ?
int lock_A = equStack_A.lock();
int lock_B = equStack_B.lock();
equStack_A.push(a);
equStack_B.push(b);
if (b.type == a.type && equStack_A.count(a) >= 2 && equStack_B.count(b) >= 2) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return true;
// on retourne ici, car il s'agit d'une définition circulaire où les éléments sont les mêmes...
}
Node res = a.quoted ? null: Node.VDelegable.evalMethod_unary_or_null(a, PCoder.getMethod(PCoder.PC_EQUALS), b);
if (res != null) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return Node.equalsNodes(new Node(TRUE), res);
// ne peut jamais être circulaire ici (compare à -1)...
}
res = a.quoted ? null: Node.VDelegable.evalMethod_unary_or_null(a, PCoder.getMethod(PCoder.PC_GT_DEFAULT_COMPARE), b);
if (res != null) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return Node.equalsNodes(new Node(0), res);
// ne peut jamais être circulaire ici compare à 0)...
}
// si pas du même type...
if (b.type != a.type) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return false;
}
Heap ha = ((Heap) a.getExternal());
Heap hb = ((Heap) b.getExternal());
ArrayList<String> a_s = ha.getSlots();
ArrayList<String> b_s = hb.getSlots();
// pas le même nombre de slots...
if (a_s.size() != b_s.size()) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return false;
}
for (int i = 0; i < a_s.size(); i++) {
String symbole = a_s.get(i);
Node b_i = hb.get(symbole); // rechercher le Node lié au même symbole dans cb.
// si le symbole n'a pas de valeur, ce n'est pas equipolent...
if (b_i == null) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return false;
}
Node a_i = ha.get(symbole); // rechercher le Node pour pouvoir le tester...
if (a_i == null) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return false;
}
if (!Node.equalsNodes(a_i, b_i)) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return false;
}
}
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return true;
}
case TYPE_FUNCTION:
{
Interpreter interpreter = Interpreter.mySelf();
NodeTags equStack_A = interpreter.getEquStack_A();
NodeTags equStack_B = interpreter.getEquStack_B();
// déjà testé ou pas ?
int lock_A = equStack_A.lock();
int lock_B = equStack_B.lock();
equStack_A.push(a);
equStack_B.push(b);
if (b.type == a.type && equStack_A.count(a) >= 2 && equStack_B.count(b) >= 2) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return true;
// on retourne ici, car il s'agit d'une définition circulaire où les éléments sont les mêmes...
}
Node res = a.quoted ? null: Node.VDelegable.evalMethod_unary_or_null(a, PCoder.getMethod(PCoder.PC_EQUALS), b);
if (res != null) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return Node.equalsNodes(new Node(TRUE), res);
// ne peut jamais être circulaire ici (compare à -1)...
}
res = a.quoted ? null: Node.VDelegable.evalMethod_unary_or_null(a, PCoder.getMethod(PCoder.PC_GT_DEFAULT_COMPARE), b);
if (res != null) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return Node.equalsNodes(new Node(0), res);
// ne peut jamais être circulaire ici compare à 0)...
}
if (b.type != a.type) {
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return false;
}
boolean r = ((Function) a.getExternal()).equals((Function) b.getExternal());
// dans tous les cas...
equStack_A.unlock(lock_A);
equStack_B.unlock(lock_B);
return r;
}
default:
return false;
}
//return res;
}
public static Node type_of(Node node) throws Exception {
long qtype = node.getQType_();
if (qtype == Node.TYPE_NUMBER)
return Node.createPCode(PCoder.PC_isNUMBER);
if (qtype == Node.TYPE_QNUMBER)
return Node.createPCode(PCoder.PC_isQUOTED_NUMBER);
if (qtype == Node.TYPE_STRING)
return Node.createPCode(PCoder.PC_isSTRING);
if (qtype == Node.TYPE_QSTRING)
return Node.createPCode(PCoder.PC_isQUOTED_STRING);
if (qtype == Node.TYPE_PCODE)
return Node.createPCode(PCoder.PC_isOPERATOR);
if (qtype == Node.TYPE_QPCODE)
return Node.createPCode(PCoder.PC_isQUOTED_OPERATOR);
if (qtype == Node.TYPE_LAZY)
return Node.createPCode(PCoder.PC_isLAZY);
if (qtype == Node.TYPE_QLAZY)
return Node.createPCode(PCoder.PC_isQUOTED_LAZY);
if (qtype == Node.TYPE_CLIST)
return Node.createPCode(PCoder.PC_isLIST);
if (qtype == Node.TYPE_QALIST)
return Node.createPCode(PCoder.PC_isQUOTED_LIST);
if (qtype == Node.TYPE_QSYMBOL)
return Node.createPCode(PCoder.PC_isQUOTED_SYMBOL);
if (qtype == Node.TYPE_QEXPR)
return Node.createPCode(PCoder.PC_isQUOTED_EXPR);
if (qtype == Node.TYPE_HASH)
return Node.createPCode(PCoder.PC_isHASH);
if (qtype == Node.TYPE_NOTHING)
return Node.createPCode(PCoder.PC_isNOTHING);
if (qtype == Node.TYPE_SCOPE)
return Node.createPCode(PCoder.PC_isSCOPE);
if (qtype == Node.TYPE_PATTERN)
return Node.createPCode(PCoder.PC_isPATTERN);
if (qtype == Node.TYPE_INTERFACE)
return Node.createPCode(PCoder.PC_isINTERFACE);
if (node.isDelegable()) {
Node r = Node.VDelegable.evalMethod_or_null(node, PCoder.getMethod(PCoder.PC_TYPE_OF), null);
if (r != null)
return r;
}
if (qtype == Node.TYPE_FUNCTION)
return Node.createPCode(PCoder.PC_isFUNCTION);
if (qtype == Node.TYPE_NAMESPACE)
return Node.createPCode(PCoder.PC_isNAMESPACE);
if (qtype == Node.TYPE_EXTERNAL)
return Node.createPCode(PCoder.PC_isEXTERNAL);
throw new InterpreterException(StdErrors.extend(StdErrors.Not_found, "type:" + node.typeOfNode()));
}
/**
* Récupère la valeur du Node courant. S'il s'agit d'un symbole, celui-ci est résolu
* à l'aide de getSymbolicValue_(Node) qui teste en même temps les droits d'accès.
*
* @return Node
* @throws Exception
*/
public Node getSymbolicValue() throws Exception {
return getSymbolicValue_(this);
}
/**
* Retourne un Vector contenant tous les Node enfants du Node courant.
*
* @return Node
*/
public Vector<Node> getChildrens() {
Vector<Node> childs = new Vector<Node>(size_());
for (int i = 0; i < size_(); )
childs.add(this.childs.get(i++));
return childs;
}
public void setChildrens(Vector<Node> childs) throws Exception {
clearChilds();
for (int i = 0; i < childs.size(); )
addElement_(childs.elementAt(i++));
}
public static final String QTypeToString(long qtype) {
String typ = "";
if ((qtype & TYPE_NOTHING) == TYPE_NOTHING)
typ = (typ + " NOTHING ").trim();
if ((qtype & TYPE_ALIST) == TYPE_ALIST)
typ = (typ + " ALIST ").trim();
if ((qtype & TYPE_QALIST) == TYPE_QALIST)
typ = (typ + " QUOTED-ALIST ").trim();
if ((qtype & TYPE_CLIST) == TYPE_CLIST)
typ = (typ + " LIST ").trim();
typ = (typ + " QUOTED-PAIR ").trim();
if ((qtype & TYPE_STRING) == TYPE_STRING)
typ = (typ + " STRING ").trim();
if ((qtype & TYPE_QSTRING) == TYPE_QSTRING)
typ = (typ + " QUOTED-STRING ").trim();
if ((qtype & TYPE_NUMBER) == TYPE_NUMBER)
typ = (typ + " NUMBER ").trim();
if ((qtype & TYPE_QNUMBER) == TYPE_QNUMBER)
typ = (typ + " QUOTED-NUMBER ").trim();
if ((qtype & TYPE_NAMESPACE) == TYPE_NAMESPACE)
typ = (typ + " OBJECT ").trim();
if ((qtype & TYPE_SCOPE) == TYPE_SCOPE)
typ = (typ + " SCOPE ").trim();
if ((qtype & TYPE_EXTERNAL) == TYPE_EXTERNAL)
typ = (typ + " EXTERNAL ").trim();
if ((qtype & TYPE_LAZY) == TYPE_LAZY)
typ = (typ + " LAZY ").trim();
if ((qtype & TYPE_QLAZY) == TYPE_QLAZY)
typ = (typ + " QUOTED-LAZY ").trim();
if ((qtype & TYPE_PATTERN) == TYPE_PATTERN)
typ = (typ + " PATTERN ").trim();
if ((qtype & TYPE_INTERFACE) == TYPE_INTERFACE)
typ = (typ + " INTERFACE ").trim();
if ((qtype & TYPE_FUNCTION) == TYPE_FUNCTION)
typ = (typ + " FUNCTION ").trim();
if ((qtype & TYPE_PCODE) == TYPE_PCODE)
typ = (typ + " OPERATOR ").trim();
if ((qtype & TYPE_QPCODE) == TYPE_QPCODE)
typ = (typ + " QUOTED-OPERATOR ").trim();
if ((qtype & TYPE_SYMBOL) == TYPE_SYMBOL)
typ = (typ + " SYMBOL ").trim();
if ((qtype & TYPE_QSYMBOL) == TYPE_QSYMBOL)
typ = (typ + " QUOTED-SYMBOL ").trim();
if ((qtype & TYPE_EXPR) == TYPE_EXPR)
typ = (typ + " EXPR ").trim();
if ((qtype & TYPE_QEXPR) == TYPE_QEXPR)
typ = (typ + " QUOTED-EXPR ").trim();
return typ;
}
/**
* Retourne true si le Node correspond au masque de types fournis.
* Ne crée pas d'exception à la différence de requireNodeType().
* Il s'agit de la version privée...
* @param masque
* @return boolean
*/
private static boolean isNodeType_(Node node, long masque) {
long masque2 = 0;
if (masque != 0) {
if ((masque & VTYPE_QTYPES) != 0) {
/**
* contient des valeurs quotées (et non quotées)...
*/
long masqueq = masque >> TYPE_Q_shiftOffset;
long masquen = masque & VTYPE_TYPES;
masque2 = (node.type & masquen) | (node.quoted ? (node.type & masqueq): 0);
}
else {
/**
* ne quotient pas de valeurs quotées...
*/
masque2 = (node.quoted ? 0: node.type) & masque;
}
}
return masque2 != 0;
}
/**
* Retourne true si le Node correspond au masque de types fournis.
* Ne crée pas d'exception à la différence de requireNodeType().
* @param masque
* @return
*/
public boolean isNodeType(long masque) {
return isNodeType_(this, masque);
}
/**
* Retourne true si le Node correspond au masque de types fournis.
* Ne crée pas d'exception à la différence de requireNodeType().
* Le numéro index correspond à l'index du child Node.
* @param index
* @param masque
* @return
*/
public boolean isNodeType(int index, long masque) {
return isNodeType_(elementAt_(index), masque);
}
private void requireNodeType_(long masque) throws InterpreterException {
/**
* Attention, requérir NOTHING n'a pas de sens...
*/
if (masque != 0) {
long masque2 = 0;
if ((masque & VTYPE_QTYPES) != 0) {
/**
* contient des valeurs quotées (et non quotées)...
*/
long masqueq = masque >> TYPE_Q_shiftOffset;
long masquen = masque & VTYPE_TYPES;
masque2 = (type & masquen) | (quoted ? (type & masqueq): 0);
}
else {
/**
* ne quotient pas de valeurs quotées...
*/
masque2 = (quoted ? 0: type) & masque;
}
if (masque2 == 0) {
String typ = QTypeToString(masque);
Interpreter.Log("ERR: Type masks (Waiting for : " + typ + ") on " + this.toString() + " ...");
if (typ.indexOf(' ') < 0) {
throw new InterpreterException(StdErrors.extend(StdErrors.Argument_type_mismatch, typ + " required"));
}
else {
throw new InterpreterException(StdErrors.Argument_type_mismatch);
}
}
}
}
/**
* Permet de récupérer la valeur de l'élément à la position index dans l'expression courante.
* Si cet élément est une expression paresseuse, celle-ci est évaluée et le résultat retourné.
*
* Cette méthode vérifie également les droits d'accès concurrents.
*
* @param index
* @param masque
* @return Node
* @throws Exception
*/
public Node getSubNode_LazyValue(int index, long masque) throws Exception {
Node res = getSymbolicValue_(elementAt_(index));
if (res.isLazy() && !res.quoted) {
Heap.push();
res = res.exec(true);
Heap.pull();
if (res == null)
res = Node.createNothing();
else
res = getSymbolicValue_(res);
}
res.requireNodeType_(masque);
return res;
}
/**
* Retourne true si le prochain SubNode correspond au masque et s'il
* existe. Le test est réalisé d'une manière non destructive.
*
* @param index
* @param masque
* @return
*/
public boolean testSubNode(int index, long masque) {
if (size_() <= index)
return false;
Node xnode;
try {
xnode = getSymbolicValue_(elementAt_(index));
}
catch (Exception e) {
return false;
}
if (xnode == null)
return false;
return isNodeType_(xnode, masque);
}
// version privée ...
private static final Node getSubNode_(Node fromnode, int index, long masque) throws Exception {
if (fromnode.size_() <= index)
throw new InterpreterException(StdErrors.Argument_count_mismatch);
Node xnode = getSymbolicValue_(fromnode.elementAt_(index));
xnode.requireNodeType_(masque);
return xnode;
}
// version public...
/**
* Retourne la valeur du Node de l'expression courrante et dont le numéro index est fourni.
* La méthode valide aussi le type de Node attendu en fonction du masque indiqué. Si le Node
* ne correespond pas à un type compatible, une exception est lévée.
*
* On notera que si le Node est un symbole, celui-ci est automatiquement résolu.
*
* De plus, le droit d'accès à la valeur est testée également. S'il s'agit d'un accès concurrent,
* le test prévient une éventuelle violation.
*
* @param index
* @param masque
* @return
* @throws Exception
*/
public Node getSubNode(int index, long masque) throws Exception {
return getSubNode_(this, index, masque);
}
/**
* Idem que getSubNode(index,maque), sauf que le test du droit d'accès concurrent n'est pas
* réalisé. Cette méthode est utile pour les opérateur atomic et lock qui doivent pouvoir
* accéder aux variables alors qu'elles sont peut-être déjà verrouillées.
*
* @param index
* @param masque
* @return
* @throws Exception
*/
public Node getSubNode_unsafeAccess(int index, long masque) throws Exception {
Node xnode = getSymbolicValue_unsafeAccess(this.elementAt_(index));
xnode.requireNodeType_(masque);
return xnode;
}
/**
* Déclenche une exception si le type du Node courant ne correspond pas au masque fourni.
* @param masque
* @throws Exception
*/
public void requireNodeType(long masque) throws InterpreterException {
requireNodeType_(masque);
}
/**
* Déclenche une exception si le type du Node à la position index de l'expression courante
* ne correspond pas au masque.
*
* On notera que l'élément n'est pas résolu avant le test de type.
*
* @param index
* @param masque
* @throws Exception
*/
public void requireNodeType(int index, long masque) throws Exception {
elementAt_(index).requireNodeType_(masque);
}
public String typeOfNode() {
String res = "UNDEFINED";
//
switch (type) {
case TYPE_NOTHING:
res = "NOTHING";
break;
case TYPE_NUMBER:
res = "NUMBER";
break;
case TYPE_STRING:
res = "STRING";
break;
case TYPE_CLIST:
res = "LIST";
break;
case TYPE_ALIST:
res = "ALIST";
break;
case TYPE_NAMESPACE:
res = "NAMESPACE";
break;
case TYPE_EXTERNAL:
res = "EXTERNAL";
break;
case TYPE_FUNCTION:
res = "FUNCTION";
break;
case TYPE_PATTERN:
res = "PATTERN";
break;
case TYPE_PCODE:
res = "OPERATOR";
break;
case TYPE_SYMBOL:
res = "SYMBOL";
break;
case TYPE_EXPR:
res = "EXPR";
break;
case TYPE_LAZY:
res = "LAZY";
break;
case TYPE_INTERFACE:
res = "INTERFACE";
break;
case TYPE_SCOPE:
res = "SCOPE";
break;
default:
res = "NONE";
}
//
if (this.quoted)
res = "QUOTED-" + res;
//
return res;
}
/**
* Méthode privée interne pour comptacter une expression en liste ou en une seule entité
* s'il n'y a qu'un seul descendant.
* La méthode gére aussi le déréférencement des symboles. Les Node de symboles sont d'ailleurs
* duppliqué pour garantir la capture des primitives.
*
* @param firstNode : premier node déjà évalué de l'expression
* @param expr : expression source
* @return
* @throws Exception
*/
final private static Node eval_q_compactExpr(Node expr, Node firstNode) throws Exception {
if (expr.size_() > 1)
throw new InterpreterException(StdErrors.extend(StdErrors.Syntax_error, expr.toString()));
/**
* si l'expression source contient plus d'un élément, je crée une liste dans
* laquelle je vais placer les valeurs éventuellement déréférencées.
*
* Corrigé le 31/01/2010:
*
* Il n'est plus autorisé de transformer une expression en liste du simple fait
* qu'il y a plusieurs éléments...
*
* C'est une erreur...
*
*/
/**
* il n'y a qu'un seul élement dans l'expression...
*/
if (firstNode != null)
return firstNode;
else {
Node res = expr.elementAt_(0);
return res.isIndirection_() ? getSymbolicValue_(res): res; // l'élément est-il un symbole évaluable
}
}
private Node eval_q() throws Exception {
try {
if (quoted)
return this;
if (hasChilds_()) {
Node firstNode = firstElement_();
if (firstNode.isIndirection_())
try {
firstNode = getSymbolicValue_(firstNode);
}
catch (Exception ie) {
if (Interpreter.isDebugMode())
ie.printStackTrace();
// firstNode-> Symbol
throw new InterpreterException(StdErrors.extend(StdErrors.Operating_symbol_error, firstNode.toString() + " -> " + ie.getMessage()));
}
switch (firstNode.type) {
case TYPE_PCODE:
return firstNode.quoted ? eval_q_compactExpr(this, firstNode): firstNode.getPCFx_().eval(this);
/**
* Si le pcode est quoté, compacter l'expression... Sinon, evaluer le pcode
*/
case TYPE_EXTERNAL:
{
if (firstNode.quoted || size_() == 1)
return eval_q_compactExpr(this, firstNode);
/**
* si l'élément est quoté, ou si l'expression ne contient qu'un élément (l'external quoté ou non),
* on compact l'expression...
*/
/**
* si on arrive ici, l'external n'est pas quoté et l'expression dispose d'au moins un élément supplémentaire...
* Mais la forme est-elle correcte ?
* (external 'symbole ...)
*/
//System.out.println("A*"+this);
Node symbole = elementAt_(1); // déjà substitué
if (symbole.isQSymbol_()) {
Node extargs = new Node();
extargs.setDTrace(getDTrace());
extargs.appendChildsOf(this, 1);
//System.out.println("B*"+extargs);
/*
*
*/
return ExternalTK.evalMethod(firstNode, extargs);
/*
*
*/
}
//sinon...
/**
* la forme n'est pas correcte...
* On compact l'expression...
*/
return eval_q_compactExpr(this, firstNode);
}
case TYPE_FUNCTION:
// cas où le premier noeud est une fonction définie...
if (firstNode.quoted)
return eval_q_compactExpr(this, firstNode);
/**
* la fonction définie est quotée...
*/
Function fn = (Function) firstNode.value;
/**
* Prise en charge des fonctions INLINE (l.bruninx, 2012-06-28).
*
* Ce fonctions sont plus légères et généralement 2 x plus rapides que
* les fonctions traditionnelles.
*
*/
if (fn.isInlined()) {
Node expendExpr = new Node();
expendExpr.setDTrace(this.getDTrace());
expendExpr.appendChildsOf(fn.getBodyFx());
//System.out.println("0*"+expendExpr);
int heap_cnt = 0;
if (size_() > 1)
expendExpr.appendChildsOf(this, 1);
//
// Si la fonction inline fait partie du contexte d'un objet, on place le SELF.
//
//
//System.out.println(Interpreter.mySelf().getGLOBAL().dump(""));
// on détecte l'objet si SELF est défini...
boolean has_self = firstNode.getSelfFx() != null;
Node old_self = null;
if (has_self) {
Node.requireReadLockAccess_(firstNode.getSelfFx()); // attention, correctif 2012-04-23, l.bruninx.
old_self = SELF.swap(firstNode.getSelfFx());
}
//
// Il s'agit d'une fonction. Il y a donc un isolement du contexte de travail.
//
Heap.push();
heap_cnt++;
//
Node res = expendExpr.exec_q(false);
//
// Il faut démonter tous les Heaps alloués
//
while ((heap_cnt--) > 0)
Heap.pull();
if (has_self)
SELF.restore(old_self);
//
//System.out.println(Interpreter.mySelf().getGLOBAL().dump(""));
return res;
} /** fin de function inline **/
/**
* Si on arrive ici, on a une fonction définie non quotée...
*/
//System.out.println("a*"+this);
// à traiter seulement s'il y a des fonctions à traiter...
Interpreter interpreter = Interpreter.mySelf();
int heap_cnt = 0;
/**
* tail-call automatique...
*/
if (interpreter.isTerminalNode() && !interpreter.isCanLoop() && !interpreter.isInLoop()) {
if (interpreter.getCallerSignature() == firstNode.getUID_()) {
// si est tail recursive...
//
//if (size_() > 1) {
//if(interpreter.isCallerFramed()){
Node argv = Node.createCList_(); //Heap.getARGVREF(); // variable déréférence masquée de argv pour le tail-recursif
//argv.clearChilds();
int argi = 1;
int mysize = size_();
while (argi < mysize)
argv.addElement_(getSubNode_(this, argi++, VTYPE_VALUABLE));
Heap.swapv(ASymbol.SYMBOL_ARGV, argv);
if (Interpreter.isDebugMode()) {
String debugStr = "Tail-Call (<0x" + Long.toHexString(firstNode.getUID_()) + ">";
for (int i = 0; i < argv.size(); )
debugStr = debugStr.concat(" " + argv.elementAt(i++));
debugStr = debugStr.concat(")");
Interpreter.Log(debugStr);
}
//}
//else {
// throw new InterpreterException(StdErrors.Illegal_arguments_use_in_tail_call);
//}
//}
//else if (Interpreter.isDebugMode()) {
// Interpreter.Log("Tail-Call (<0x" + Long.toHexString(firstNode.getUID_()) + ">)");
//}
//
interpreter.setBreakCode(Interpreter.BREAKCODE_TAIL);
return null;
}
}
/**
* tail-call automatique...
*/
// Le corps de la fonction.
Node fonction = fn.getBodyFxOrWrapper();
// Charger la pile de heap selon l'algo profondeur d'abord et revenir...
heap_cnt += fn.pushHeaps();
// les argv maintenant
Heap.push();
heap_cnt++;
/**
* important avant l'établissement de SELF et THIS !!!
*/
Node argv = createCList_();
// gérer la liste des args...
int argcnt = 1;
while (argcnt < size_())
argv.addElement_(getSubNode_(this, argcnt++, Node.VTYPE_VALUABLE));
// System.out.println("b*"+argv);
//Heap.newARGVREF();
//Heap.setARGVREF(argv); // deref caché de argv... Pour le tail recursif...
Heap.defv(ASymbol.SYMBOL_ARGV, argv.deref());
//
// Assignation de This...
//
Node oldThisNode = interpreter.getThisFunction();
//Node oldWrapper = interpreter.getWrapperFunction();
interpreter.setThisFunction(firstNode);
Heap.newRETURN();
boolean has_self = firstNode.getSelfFx() != null;
Node old_self = null;
if (has_self) {
// s'il y s'agit du contexte d'un objet à espace de noms, on l'applique à Self...
// on détecte l'objet si SELF est défini...
Node.requireReadLockAccess_(firstNode.getSelfFx()); // attention, correctif 2012-04-23, l.bruninx.
old_self = SELF.swap(firstNode.getSelfFx());
}
/**
* fin de la correction du 25/12/2009
*/
//
long oldCaller = interpreter.getCallerSignature();
interpreter.setCallerSignature(firstNode.getUID_());
//
boolean tailRecursion = true;
/*
* Memoization intégrée dépréciée momentanément (l.bruninx, 2012-07-20)
*
boolean memoizable = false;
boolean memoized = firstNode.value instanceof Hashtable;
if (memoized) {
memoizable = isMemoizable(argv);
if (memoizable) {
Hashtable<MemoizeKey, Node> memoiz = (Hashtable<MemoizeKey, Node>) firstNode.value;
Node memores = memoiz.get(new MemoizeKey(argv));
if (memores != null) {
// protection de la valeur de retour (soit finale ou clonée)...
Heap.setRETURN(memores.isFinalNode() ? memores: Node.createClone(memores));
tailRecursion = false;
//
// on termine, on a trouvé la réponse...
//
}
}
else if (!memoizable) {
throw new InterpreterException(StdErrors.Memoizable_argument_required);
}
}
* Memoization intégrée...
*/
// espace local
Heap.push();
heap_cnt++;
Heap local = Heap.current();
//
while (tailRecursion) {
tailRecursion = false;
if (local.isLoaded())
local.clear();
// on évalue le corps de la fonction
// System.out.println("c*"+fonction +" : "+firstNode);
fonction.exec_q(true);
/*
* Obligation de renvoyer le résultat par (return ...)
*/
if (interpreter.getCallerSignature() == firstNode.getUID_()) {
switch (interpreter.getBreakCode()) {
case Interpreter.BREAKCODE_TAIL:
{
tailRecursion = true;
interpreter.consumeBreakCode_onTail();
/*
* Dépréciée momentanément (l.bruninx, 2012-07-20)
*
* Mémoization intégrée sur tail-call...
* Il s'agit du même code que lorsqu'il n'y a pas de terminalité.
* Cependant, le code est ici simplifié puisque certains tests sont inutiles.
if (memoized) {
memoizable = isMemoizable(argv);
if (memoizable) {
Hashtable<MemoizeKey, Node> memoiz = (Hashtable<MemoizeKey, Node>) firstNode.value;
Node memores = memoiz.get(new MemoizeKey(argv));
if (memores != null) {
// protection de la valeur de retour (soit finale ou clonée)...
Heap.setRETURN(memores.isFinalNode() ? memores: Node.createClone(memores));
tailRecursion = false;
//
// on termine, on a trouvé la réponse...
//
}
}
else if (!memoizable)
throw new InterpreterException(StdErrors.Memoizable_argument_required);
}
* Memoization intégrée sur tail-call : même code que memoization standard, mais simplifiée...
*/
break;
}
case Interpreter.BREAKCODE_RETURN:
interpreter.consumeBreakCode_onReturn();
break;
}
}
}
// supprimer l'espace local...
Heap.pull();
heap_cnt--;
//
interpreter.setCallerSignature(oldCaller);
/*
* rétablir This et wrapper (old)...
*/
//interpreter.setWrapperFunction(oldWrapper);
interpreter.setThisFunction(oldThisNode);
Node result = Heap.getRETURN();
/*
* Mémoization dépréciée momentanément (l.bruninx, 2012-07-20)
*
* expérimental: Memoization intégrée...
*
if (memoized && result != null) {
Hashtable<MemoizeKey, Node> memoiz = (Hashtable<MemoizeKey, Node>) firstNode.value;
MemoizeKey clememoize = new MemoizeKey(argv);
if (!memoiz.containsKey(clememoize))
memoiz.put(clememoize, result);
}
* expérimental: memoization intégrée...
*/
while ((heap_cnt--) > 0)
Heap.pull();
if (has_self)
SELF.restore(old_self);
/**
* Fin de la correction 2 du 25/12/2009...
*/
return result;
case TYPE_NAMESPACE:
/**
* MODIFICATION DU 15/01/2010 rev 6196:
* -----------------------------------
* On supprime le champ lambda des objets pour permettre une nouvelle forme:
* (objet 'symbole args...)
*
*/
if (firstNode.quoted || size_() == 1)
return eval_q_compactExpr(this, firstNode);
/**
* si l'objet est quoté, ou si l'expression ne contient qu'un élément (l'objet quoté ou non),
* on compact l'expression...
*/
/**
* si on arrive ici, l'objet n'est pas quoté et l'expression dispose d'au moins un élément supplémentaire...
* Mais la forme est-elle correcte ?
* (objet 'symbole ...)
*/
Node symbole = elementAt_(1); // déjà substitué
if (symbole.isQSymbol_()) {
/**
* Optilisation 2012-04-16 par accès direct, l.bruninx...
*/
return Node.VDelegable.evalMethod(firstNode, symbole.getSymbol().getStr(), this, 2);
/*
*
*/
}
//sinon...
/**
* la forme n'est pas correcte...
* On compact l'expression...
*/
return eval_q_compactExpr(this, firstNode);
default:
return eval_q_compactExpr(this, firstNode);
/**
* gestion du déréférencement avec dupplications éventuelles des références attrubuée à la méthode
* eval_q_compactExpr(...).
*/
}
}
//sinon...
// un seul noeud...
//System.out.println("*1:"+this.toString());
return this;
}
catch (StackOverflowError sterr) {
throw new InterpreterException(StdErrors.Stack_overflow);
}
catch (RestartException ex) {
throw ex;
}
catch (RetryException ex) {
throw ex;
}
catch (AbortException ex) {
throw ex;
}
catch (SilentException ex) {
throw ex;
}
catch (Exception ex) {
if (Interpreter.isDebugMode())
ex.printStackTrace();
throw new InterpreterException(StdErrors.createTrace(this, ex));
}
}
public static Node createScope(Node lazyExpr) throws Exception {
Heap heap = new Heap();
Heap.push(heap);
Node r = lazyExpr.exec(true);
Heap.pull();
if (r != null)
throw new InterpreterException(StdErrors.Void_return_results);
return new Node(heap);
}
/**
* Creation d'un node du type Namespace à partir du heap fourni.
* Si le heap est null, alors un nouveau heap vide est créé.
*
* @param heap (null si la méthode doit créer un nouveau heap)
* @return Node.Namespace
*/
public static Node createNamespace(Heap heap) {
Heap objectHeap = heap;
if (objectHeap == null)
objectHeap = new Heap();
Node objectNode = new Node(objectHeap);
objectNode.setFinalNode(false);
objectNode.setType(TYPE_NAMESPACE);
return objectNode;
}
/**
* Créer un nouveau delegable à partir d'un pattern ou d'un délégable..
*
* @param scratchNode (pattern/delegable)
* @return delegable
* @throws Exception
*/
public static Node createDelegableFrom(Node scratchNode, Node postinit) throws Exception {
if (scratchNode.getQType_() == TYPE_PATTERN) {
Pattern pattern = (Pattern) scratchNode.getExternal();
Node fxClassNode = pattern.getBodyPx();
Heap.push();
Node xret = fxClassNode.exec(true);
Heap.pull();
//
// Contrôle du retour d'expression:
// -------------------------------
//
// Doit être du type primaire indiqué par le pattern.
// Doit aussi consistant avec l'interface de contrainte s'il y en a une.
//
pattern.requireAll(xret);
//
// post-initialisation... s'il y en a une...
//
if (postinit != null) {
Node old_self = null;
if (xret.isNeedSelf())
old_self = SELF.swap(xret);
Heap.push();
Node xnode = postinit.exec(true);
Heap.pull();
if (xret.isNeedSelf())
SELF.restore(old_self);
if (xnode != null)
throw new InterpreterException(StdErrors.extend(StdErrors.Void_return_results, scratchNode.toString()));
}
return xret;
}
else {
// fonction, external ou namespace...
scratchNode.requireNodeType(Node.VTYPE_DELEGABLE);
Node objectNode = Node.VDelegable.evalMethod_or_null(scratchNode, PCoder.getMethod(PCoder.PC_NEW), null);
if (objectNode == null)
objectNode = PCFx_clone.cloneOp(scratchNode);
Node xnode = null;
if (objectNode != null) {
if (postinit != null) {
Node old_self = null;
if (objectNode.isNeedSelf())
old_self = SELF.swap(objectNode);
Heap.push();
xnode = postinit.exec(true);
Heap.pull();
if (objectNode.isNeedSelf())
SELF.restore(old_self);
}
}
if (xnode != null)
throw new InterpreterException(StdErrors.extend(StdErrors.Void_return_results, scratchNode.toString()));
return objectNode;
}
}
public static Node createExternal(Object obj) {
Node res = new Node();
res.type = Node.TYPE_EXTERNAL;
res.value = obj;
return res;
}
// version public de createCList()...
public static Node createCList() {
return createCList_();
}
// private static final Node EMPTYLIST = createEmptyList();
public static Node createLazy() {
Node res = new Node();
res.finalNode = true;
res.type = Node.TYPE_LAZY;
return res;
}
/*----------------------------------------------------------------------------------*
* Structure Function Inline
*----------------------------------------------------------------------------------
*/
public static Node createInlineFunction(Node inlineBody) throws InterpreterException {
Node res = new Node();
res.type = Node.TYPE_FUNCTION;
res.value = Function.createInlineFunction(inlineBody);
return res;
}
/*----------------------------------------------------------------------------------*
* Structure Funtion classique
*----------------------------------------------------------------------------------
*/
public static Node createFunction(Node bodyFx) throws Exception {
Node res = new Node();
res.type = Node.TYPE_FUNCTION;
res.value = Function.createFunction(bodyFx);
return res;
}
public static Node createFunction(Node bodyFx, Node scopeYieldFx, Node scopeWithFx) throws Exception {
Node res = new Node();
res.type = Node.TYPE_FUNCTION;
res.value = Function.createFunction(bodyFx, scopeYieldFx, scopeWithFx);
return res;
}
/*----------------------------------------------------------------------------------*
* Structure Funtion overide
*----------------------------------------------------------------------------------
*/
public static Node createOverridedFunction(Node thisFx, Node scopeYieldFx, Node scopeWithFx, Node superfx) throws Exception {
Node res = new Node();
res.type = Node.TYPE_FUNCTION;
res.value = Function.createOverridedFunction(thisFx, scopeYieldFx, scopeWithFx, superfx);
return res;
}
public static Node createPattern(long primaryType, Node bodyPx, Node iface) {
Node pnode = new Node();
pnode.type = Node.TYPE_PATTERN;
pnode.finalNode = true; // un pattern est toujours finale
pnode.value = Pattern.createPattern(primaryType, bodyPx, iface);
return pnode;
}
public static Node createPattern(String externalId) throws InterpreterException {
Node pnode = new Node();
pnode.type = Node.TYPE_PATTERN;
pnode.finalNode = true; // un pattern est toujours finale
pnode.value = Pattern.createPattern(externalId);
return pnode;
}
public static Node createInterface(Node bodyIx) throws Exception {
Node pnode = new Node();
pnode.type = Node.TYPE_INTERFACE;
pnode.finalNode = true; // une interface est toujours finale
Interface iface = Interface.createInterface();
pnode.value = iface;
iface.loadBodyIx(pnode, bodyIx);
return pnode;
}
public static Node createExpr() {
Node res = new Node();
res.type = Node.TYPE_EXPR;
res.finalNode=true;
return res;
}
public static Node createQExpr() {
Node res = new Node();
res.type = Node.TYPE_EXPR;
res.quoted = true;
return res;
}
public static Node createPCode(int pcode) {
Node res = new Node();
res.type = Node.TYPE_PCODE;
res.setPCFx_(PCoder.getPcfx(PCoder.getReserved(pcode)));
return res;
}
public static Node createSymbol(String symbol) {
Node res = new Node();
res.type = Node.TYPE_SYMBOL;
String v = symbol;
if (v.charAt(0) == '$')
v = v.substring(1);
//System.out.println(v);
res.value = new ASymbol(v);
return res;
}
public static Node createQSymbol(String symbol) {
Node res = new Node();
res.type = Node.TYPE_SYMBOL;
res.quoted = true;
String v = symbol;
if (v.charAt(0) == '$')
v = v.substring(1);
//System.out.println(v);
res.value = new ASymbol(v);
return res;
}
public static Node createSymbol(ASymbol symbol) {
Node res = new Node();
res.type = Node.TYPE_SYMBOL;
res.value = symbol;
return res;
}
public static Node createRegister(String symbol) {
Node res = new Node();
res.type = Node.TYPE_SYMBOL;
String v = symbol;
if (v.charAt(0) == '$')
v = v.substring(1);
res.value = new ASymbol(PCoder.REG + v);
return res;
}
public static Node createNothing() {
//System.out.println("CreateNothing");
Node res = new Node();
res.type = Node.TYPE_NOTHING;
return res;
}
public static Node createVOIDNODE() {
Node res = new Node();
res.type = Node.TYPE_NONE;
return res;
}
public static Node createHash() {
Node res = new Node();
res.type = Node.TYPE_HASH;
res.value = new Hash();
return res;
}
public static Node createHash(Hash hash) {
Node res = new Node();
res.type = Node.TYPE_HASH;
res.value = hash;
return res;
}
public static Node createBytes(Bytes bytes){
Node res = new Node();
res.type = Node.TYPE_BYTES;
res.value = bytes;
return res;
}
/*
* Non utilisée - l.bruninx, 2012-04-23.
*
public Node getSubPairProperty_fromPath(String[] path, int type, Node defaultProperty) throws Exception {
Node xnode = null;
Node pnode = this;
for (int i = 0; i < path.length; i++) {
boolean inPath = ((i + 1) < path.length) && (path.length > 1);
xnode = pnode.getSubPair(path[i], (inPath ? Node.TYPE_LIST: type));
if (inPath)
pnode = xnode;
if (xnode == null)
break;
}
if (xnode == null)
xnode = defaultProperty;
return xnode;
}
*/
/**
* Encode automatiquement une valeur quotée.
* Cet encodage est utilisé dans les fonction split et join qui utilise une liste quotée pour y placer
* la valeur quotée. Normalement, dans cet encodage, les liste ne sont pas automatiquement quotées.
* Ainsi, lorsqu'un élément est une liste quotée, cela signale que l'élement était à l'origine quoté.
*
* e -> 'e
* 'e -> '['e]
* [x] -> '[[x]]
* '[x] -> '['[x]]
*
* @param src Valeur à encoder (si nécessaire).
* @return Valeur encodée.
*/
public static Node quoteEncoded_encode(Node src) throws Exception {
Node res = src.toSerialized(Node.createCList_());
if (src.quoted || src.getQType()==Node.TYPE_ALIST) {
res = Node.createAList_().append_(res);
return res.letQuoted(true).letFinal(false);
}
return res.letQuoted(true).letFinal(false);
}
/**
* Décode automatiquement une valeur encodée avec quoteEncoded_encode().
* Est utilisé par split et join dans le cas des sexpr et des functions.
*
* Peut retourner une expression redex !!!...
*
* @param src Valeur encodée
* @return Valeur décodée (si nécessaire).
*/
public static Node quoteEncoded_decode(Node src) throws Exception {
Node res = src;
if (src.quoted) {
if (src.type!=Node.TYPE_ALIST)
res=Node.createClone(src).letQuoted(false);
else if (src.size_() == 1) {
res=Node.createClone(src.elementAt(0));
}
else {
throw new InterpreterException(StdErrors.Quote_encoded_error);
}
}
return res;
}
}