package abstrasy.pcfx;
import abstrasy.Function;
import abstrasy.Heap;
import abstrasy.Node;
import abstrasy.PCoder;
import abstrasy.ASymbol;
import abstrasy.interpreter.InterpreterException;
import abstrasy.interpreter.StdErrors;
/**
* Abstrasy Interpreter
*
* Copyright : Copyright (c) 2006-2012, Luc Bruninx.
*
* Concédée sous licence EUPL, version 1.1 uniquement (la «Licence»).
*
* Vous ne pouvez utiliser la présente oeuvre que conformément à la Licence.
* Vous pouvez obtenir une copie de la Licence à l’adresse suivante:
*
* http://www.osor.eu/eupl
*
* Sauf obligation légale ou contractuelle écrite, le logiciel distribué sous
* la Licence est distribué "en l’état", SANS GARANTIES OU CONDITIONS QUELLES
* QU’ELLES SOIENT, expresses ou implicites.
*
* Consultez la Licence pour les autorisations et les restrictions
* linguistiques spécifiques relevant de la Licence.
*
*
* @author Luc Bruninx
* @version 1.0
*/
public class PCFx_function extends PCFx {
public PCFx_function() {
}
/*
* Memoization native dépréciée momentanément (l.bruninx, 2012-07-20)
* ------------------------------------------------------------------
* La mémoization implémentée jusqu'ici utilise un type interne non accessible par le programmeur.
* Cette implémentation entre en conflit avec l'utilisation de Node.external pour y mettre le heap
* d'un node de type OBJECT ou NAMESPACE.
*
* Cette dernière implémentation est prioritaire sur la première d'autant plus qu'elle permet de
* résoudre également un souci de partage des données dans un contexte parallèlisé. Le objHeap d'un
* Node FUNCTION peut être réécrit par des procéssus différents. Alors que dans le cas d'un object,
* le node contenant reste le même.
*
private static final void memoize(Node fx, Node args, Node rnode) throws Exception {
boolean memoizable = Node.isMemoizable(args);
Node memoizeN=fx.elementAt(2);
if (memoizeN.getExternal() == null) {
fx.setElementAt(Node.createExternal(new Hashtable<MemoizeKey, Node>()),2);
}
if (memoizable) {
Hashtable<MemoizeKey,Node> memoiz = (Hashtable<MemoizeKey,Node>) fx.elementAt(2).getExternal();
memoiz.put(new MemoizeKey(args), rnode);
}
else {
throw new InterpreterException(StdErrors.Memoizable_argument_required);
}
}
private static final void memoize_dump(Node fx, Node lanode) throws Exception {
for (int i = 0; i < lanode.size(); i++) {
// traiter cas par cas...
Node segnode = lanode.getSubNode(i, Node.TYPE_LIST | Node.TYPE_QEXPR);
segnode.requirePCode(1, PCoder.PC_RETURN);
Node anode = segnode.getSubNode(0, Node.TYPE_LIST);
Node rnode = segnode.getSubNode(2, Node.VTYPE_VALUABLE);
segnode.isGoodArgsCnt(3);
memoize(fx, anode, rnode);
}
}
*/
/**
* eval
*
* @param startAt Node
* @return Node
* @throws Exception
* @todo Implémenter cette méthode abstrasy.PCFx
*/
public Node eval(Node startAt) throws Exception {
/**
* formes:
*
* Forme générale:
* (function [final] [inline|override] ['symbole] [{...}] [[with [{...}|scope]|[yield [{...}|scope]] )
*
*
*
*
* Fonction inline: (cas particulier)
* ---------------
* (function inline 'symbol {...})
* (function inline {...}) -> fx
*
*
*
* Fonction classique:
* ------------------
* (function {...}) -> fx
* (function 'symbol {...})
*
*
*
*
* Fonction avec fermeture "privée":
* --------------------------------
* (function {...} with lazy/scope) -> fx
* (function 'symbol {...} with lazy/scope)
*
* La fonction utilise un scope pour stocker des variables privées. Le scope liée à la fonction
* à l'aide de with n'est pas "exporté" lors d'un override. Il s'agit donc d'une fermeture "privée".
*
* Fonction avec fermeture "rapportée":
* -----------------------------------
* (function {...} yield lazy/scope) -> fx
* (function 'symbol {...} yield lazy/scope)
*
* La fonction utilise un scope pour stocker des variables. Toutefois le scope est "rapporté" en
* cas d'un override. Les variables du scope yield sont donc disponibles pour le corps de la
* fonction override.
*
* Fonction avec fermetures "privée" et "rapportée":
* ------------------------------------------------
* (function {...} with lazy/scope yield lazy/scope) -> fx
* (function 'symbol {...} with lazy/scope yield lazy/scope)
*
* On peut associer les deux modes de exportation des fermetures.
*
*
* Fonction wrapper:
* ----------------
* (function wrapper with ... yield ...) -> fx
* (function wrapper 'symbole with ... yield ...)
*
*
* Fonction override (simple copie, mais pas la même identité):
* -----------------------------------------------------------
* (function override 'super)
* (function override super) -> fx
*
* Fonction override avec remplacement du corps de la fonction:
* -----------------------------------------------------------
* (function override 'super {...})
* (function override super {...}) -> fx
*
* Fonction override avec fermetures (toutes les variantes possibles):
* ------------------------------------------------------------------
* (function override 'symbol {...} with ... yield ...)
* (function override 'symbol with ... yield ...)
* (function override symbol {...} with ... yield ...) -> fx
* (function override symbol with ... yield ...) -> fx
*
*/
// o_rel : position relative du dernier argument lu...
int o_rel = 1;
// o_arg dernier argument lu...
Node o_arg=startAt.elementAt(o_rel).getSymbolicValue();
//
// option final
//
boolean e_final = (!o_arg.isQuoted()) && o_arg.isPCode(PCoder.PC_FINAL);
if(e_final)
o_arg=startAt.elementAt(++o_rel).getSymbolicValue();
//
// option inline | override | wrapper
//
boolean e_inline = (!o_arg.isQuoted()) && o_arg.isPCode(PCoder.PC_INLINE);
boolean e_override = (!o_arg.isQuoted()) && o_arg.isPCode(PCoder.PC_OVERRIDE);
boolean e_wrapper = (!o_arg.isQuoted()) && o_arg.isPCode(PCoder.PC_WRAPPER);
if(e_inline||e_override||e_wrapper)
o_arg=startAt.elementAt(++o_rel).getSymbolicValue();
//
//
// On peut déjà faire un dispatching en fonction de l'option inline ou override
//
// (function [final] [inline|override] *...)
//
if (e_inline) {
/**
* inline:
* ------
*/
o_arg.requireNodeType(Node.TYPE_QSYMBOL|Node.TYPE_LAZY);
if(o_arg.isQSymbol()){
//
// forme : ... 'symbole {...})
//
ASymbol e_symbole = o_arg.getSymbol();
Node e_body = startAt.getSubNode(++o_rel, Node.TYPE_LAZY);
startAt.isGoodArgsCnt(o_rel+1); // Vérifie la forme
Node e_function = Node.createInlineFunction(e_body);
if (e_final && !e_symbole.isImmutable())
throw new InterpreterException(StdErrors.Immutable_symbol_required);
Heap.defv(e_symbole, e_function.letFinal(e_symbole.isImmutable()));
return null;
}
else{
//
// forme : ... {...})
//
startAt.isGoodArgsCnt(o_rel+1);
Node e_body = o_arg;
Node e_function = Node.createInlineFunction(e_body).letFinal(e_final);
return e_function;
}
}
else if(e_override){
/**
* override
*/
//
// forme : ... 'symbole ...
// et : ... function ...
//
o_arg.requireNodeType(Node.TYPE_QSYMBOL|Node.TYPE_FUNCTION);
ASymbol e_symbole=null;
Node e_body=null;
Node e_super=null;
if(o_arg.isQSymbol()){
e_symbole = o_arg.getSymbol();
e_super = Heap.getv(e_symbole);
e_super.requireAccessType(Node.ACCESSVTYPE_MUTABLE_WRITELOCK);
e_super.requireNodeType(Node.TYPE_FUNCTION);
}
else{
e_super = o_arg;
e_super.requireAccessType(Node.ACCESSVTYPE_MUTABLE_WRITELOCK);
}
//
// forme : ... [{...}] ...
//
if(startAt.testSubNode(++o_rel, Node.TYPE_LAZY)){
e_body=startAt.getSubNode(o_rel, Node.TYPE_LAZY);
}
else{
o_rel--; // Attention!!! il faut revenir en arrière...
e_body=((Function) e_super.getExternal()).getBodyFx();
}
//
// Rechercher la forme [with lazy/scope] et/ou [from lazy/scope]
//
Node e_with=null;
Node e_yield=null;
while(startAt.size()>o_rel+1){
o_arg=startAt.getSubNode(++o_rel, Node.TYPE_PCODE);
if(o_arg.isPCode(PCoder.PC_WITH))
if(e_with!=null)
throw new InterpreterException(StdErrors.extend(StdErrors.Duplicate_parameters,o_arg.toString()));
else{
e_with=startAt.getSubNode(++o_rel, Node.TYPE_LAZY|Node.TYPE_SCOPE);
if(e_with.isLazy())
e_with=Node.createScope(e_with);
}
else if(o_arg.isPCode(PCoder.PC_YIELD))
if(e_yield!=null)
throw new InterpreterException(StdErrors.extend(StdErrors.Duplicate_parameters,o_arg.toString()));
else{
e_yield=startAt.getSubNode(++o_rel, Node.TYPE_LAZY|Node.TYPE_SCOPE);
if(e_yield.isLazy())
e_yield=Node.createScope(e_yield);
}
}
//
// On a tout ce qu'il faut...
//
startAt.isGoodArgsCnt(o_rel+1); // Vérifie la forme
Node e_function = Node.createOverridedFunction(e_body,e_yield,e_with,e_super);
if (e_symbole == null)
return e_function.letFinal(e_final);
else {
e_function.derefTo(e_super);
return null;
}
}
else if(e_wrapper){
/**
* wrapper
*/
//
// forme : ... ['symbole] ...
//
o_arg.requireNodeType(Node.TYPE_QSYMBOL|Node.TYPE_PCODE);
ASymbol e_symbole=null;
if(o_arg.isQSymbol()){
e_symbole = o_arg.getSymbol();
o_arg=startAt.getSubNode(++o_rel, Node.TYPE_PCODE);
}
//
// Rechercher la forme [with lazy/scope] et/ou [from lazy/scope]
//
// Attention, dans ce cas, o_arg contient déjà le premier PCode with ou yield.
// --------------------------------------------------------------------------
// La boucle est donc différente ici que dans les autres cas...
//
// De plus, il est obligatoire de stipuler au moins un des deux scopes.
// -----------
//
Node e_with=null;
Node e_yield=null;
do{
// on ne cherche o_arg que s'il est null
if(o_arg==null)
o_arg=startAt.getSubNode(++o_rel, Node.TYPE_PCODE);
if(o_arg.isPCode(PCoder.PC_WITH))
if(e_with!=null)
throw new InterpreterException(StdErrors.extend(StdErrors.Duplicate_parameters,o_arg.toString()));
else{
e_with=startAt.getSubNode(++o_rel, Node.TYPE_LAZY|Node.TYPE_SCOPE);
if(e_with.isLazy())
e_with=Node.createScope(e_with);
}
else if(o_arg.isPCode(PCoder.PC_YIELD))
if(e_yield!=null)
throw new InterpreterException(StdErrors.extend(StdErrors.Duplicate_parameters,o_arg.toString()));
else{
e_yield=startAt.getSubNode(++o_rel, Node.TYPE_LAZY|Node.TYPE_SCOPE);
if(e_yield.isLazy())
e_yield=Node.createScope(e_yield);
}
// on met o_arg=null pour la suite...
o_arg=null;
}while(startAt.size()>o_rel+1);
//
// On a tout ce qu'il faut...
//
startAt.isGoodArgsCnt(o_rel+1); // Vérifie la forme
Node e_function = Node.createFunction(null,e_yield,e_with); // on crée une fonction wrapper (sans corps)
if(e_symbole!=null){
if (e_final && !e_symbole.isImmutable())
throw new InterpreterException(StdErrors.Immutable_symbol_required);
Heap.defv(e_symbole, e_function.letFinal(e_symbole.isImmutable()));
return null;
}
else
return e_function.letFinal(e_final);
}
else{
/**
* classique
*/
//
// forme : ... 'symbole {...} ...
// et : ... {...} ...
//
o_arg.requireNodeType(Node.TYPE_QSYMBOL|Node.TYPE_LAZY);
ASymbol e_symbole=null;
if(o_arg.isQSymbol()){
e_symbole = o_arg.getSymbol();
o_arg=startAt.getSubNode(++o_rel, Node.TYPE_LAZY);
}
Node e_body = o_arg;
//
// Rechercher la forme [with lazy/scope] et/ou [from lazy/scope]
//
Node e_with=null;
Node e_yield=null;
while(startAt.size()>o_rel+1){
o_arg=startAt.getSubNode(++o_rel, Node.TYPE_PCODE);
if(o_arg.isPCode(PCoder.PC_WITH))
if(e_with!=null)
throw new InterpreterException(StdErrors.extend(StdErrors.Duplicate_parameters,o_arg.toString()));
else{
e_with=startAt.getSubNode(++o_rel, Node.TYPE_LAZY|Node.TYPE_SCOPE);
if(e_with.isLazy())
e_with=Node.createScope(e_with);
}
else if(o_arg.isPCode(PCoder.PC_YIELD))
if(e_yield!=null)
throw new InterpreterException(StdErrors.extend(StdErrors.Duplicate_parameters,o_arg.toString()));
else{
e_yield=startAt.getSubNode(++o_rel, Node.TYPE_LAZY|Node.TYPE_SCOPE);
if(e_yield.isLazy())
e_yield=Node.createScope(e_yield);
}
}
//
// On a tout ce qu'il faut...
//
startAt.isGoodArgsCnt(o_rel+1); // Vérifie la forme
Node e_function = Node.createFunction(e_body,e_yield,e_with);
if(e_symbole!=null){
if (e_final && !e_symbole.isImmutable())
throw new InterpreterException(StdErrors.Immutable_symbol_required);
Heap.defv(e_symbole, e_function.letFinal(e_symbole.isImmutable()));
return null;
}
else
return e_function.letFinal(e_final);
}
}
}