Package abstrasy

Source Code of abstrasy.Node$VDelegable

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;
    }


}
TOP

Related Classes of abstrasy.Node$VDelegable

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.