New directory tree, with preprocessor/ inside interpretor/.
[Faustine.git] / interpretor / preprocessor / faust-0.9.47mr3 / compiler / evaluate / environment.cpp
diff --git a/interpretor/preprocessor/faust-0.9.47mr3/compiler/evaluate/environment.cpp b/interpretor/preprocessor/faust-0.9.47mr3/compiler/evaluate/environment.cpp
new file mode 100644 (file)
index 0000000..38bb617
--- /dev/null
@@ -0,0 +1,195 @@
+#include "environment.hh"
+#include "errormsg.hh"
+#include "boxes.hh"
+#include "ppbox.hh"
+#include "names.hh"
+
+
+//-----------------------new environment management----------------------------
+//
+// The environement is made of layers. Each layer contains a set of definitions
+// stored as properties of the layer. Each definition can refers to other
+// definitions of the same layer or of subsequent layers. Recursive
+// definitions are not allowed. Multiple defintions of the same symbol
+// in a layer is allowed but generate a warning when the definition is
+// different
+//-----------------------------------------------------------------------------
+
+
+
+/**
+ * Push a new (unique) empty layer (where multiple definitions can be stored)
+ * on top of an existing environment.
+ * @param lenv the old environment
+ * @return the new environment
+*/
+static Tree pushNewLayer(Tree lenv)
+{
+    return tree(unique("ENV_LAYER"), lenv);
+}
+
+
+
+/**
+ * Push a new environment barrier on top of an existing environment so
+ * that searchIdDef (used by the pattern matcher) will not look after
+ * the barrier. This barrier will not any influence on regular environment
+ * lookup.
+ * @param lenv the old environment
+ * @return the new environment
+*/
+Sym BARRIER = symbol ("BARRIER");
+
+Tree pushEnvBarrier(Tree lenv)
+{
+    return tree(BARRIER, lenv);
+}
+
+
+/**
+ * Test if the environment is a barrier (or nil) so
+ * that searchIdDef will know where to stop when searching
+ * an environment.
+ * @param lenv the environment to test
+ * @return true is barrier reached
+*/
+bool isEnvBarrier(Tree lenv)
+{
+    return isNil(lenv) || (lenv->node() == Node(BARRIER));
+}
+
+
+/**
+ * Add a definition (as a property) to the current top level layer. Check
+ * and warn for multiple definitions.
+ * @param id the symbol id to be defined
+ * @param def the definition to be binded to the symbol id
+ * @param lenv the environment where to add this new definition
+*/
+static void addLayerDef(Tree id, Tree def, Tree lenv)
+{
+    // check for multiple definitions of a symbol in the same layer
+    Tree olddef;
+    if (getProperty(lenv, id, olddef)) {
+        if (def == olddef) {
+            evalwarning(getDefFileProp(id), getDefLineProp(id), "equivalent re-definitions of", id);
+        } else {
+            fprintf(stderr, "%s:%d: ERROR: redefinition of symbols are not allowed : ", getDefFileProp(id), getDefLineProp(id));
+            print(id,stderr);
+            fprintf(stderr, " is already defined in file \"%s\" line %d \n", getDefFileProp(id), getDefLineProp(id));
+            gErrorCount++;
+        }
+    }
+    setProperty(lenv, id, def);
+}
+
+
+/**
+ * Push a new layer and add a single definition.
+ * @param id the symbol id to be defined
+ * @param def the definition to be binded to the symbol id
+ * @param lenv the environment where to push the layer and add the definition
+ * @return the new environment
+ */
+Tree pushValueDef(Tree id, Tree def, Tree lenv)
+{
+    Tree lenv2 = pushNewLayer(lenv);
+    addLayerDef(id, def, lenv2);
+    return lenv2;
+}
+
+
+/**
+ * Push a new layer with multiple definitions creating the appropriate closures
+ * @param ldefs list of pairs (symbol id x definition) to be binded to the symbol id
+ * @param visited set of visited symbols (used for recursive definition detection)
+ * @param lenv the environment where to push the layer and add all the definitions
+ * @return the new environment
+*/
+Tree pushMultiClosureDefs(Tree ldefs, Tree visited, Tree lenv)
+{
+    Tree lenv2 = pushNewLayer(lenv);
+    while (!isNil(ldefs)) {
+        Tree def = hd(ldefs);
+        Tree id = hd(def);
+        Tree rhs= tl(def);
+        Tree cl = closure(tl(def),nil,visited,lenv2);
+        stringstream s; s << boxpp(id);
+        if (!isBoxCase(rhs)) setDefNameProperty(cl,s.str());
+        addLayerDef( id, cl, lenv2 );
+        ldefs = tl(ldefs);
+    }
+    return lenv2;
+}
+
+
+/**
+ * Search the environment (until first barrier) for
+ * the definition of a symbol ID and return it. Used by the
+ * pattern matcher.
+ * @param id the symbol ID to search
+ * @param def where to store the definition if any
+ * @param lenv the environment
+ * @return true if a definition was found
+ */
+bool searchIdDef(Tree id, Tree& def, Tree lenv)
+{
+    // search the environment until a definition is found
+    // or a barrier (or nil) is reached
+
+    while (!isEnvBarrier(lenv) && !getProperty(lenv, id, def)) {
+        lenv = lenv->branch(0);
+    }
+    return !isEnvBarrier(lenv);
+}
+
+/**
+ * Replace closure that point to oldEnv with closure on newEnv
+ */
+static void updateClosures(vector<Tree>& clos, Tree oldEnv, Tree newEnv)
+{
+       for (unsigned int i=0; i < clos.size(); i++) {
+        Tree exp, genv, visited, lenv;
+        if (isClosure(clos[i], exp, genv, visited, lenv)) {
+            if (lenv == oldEnv) {
+                clos[i] = closure(exp, genv, visited, newEnv);
+            }
+        }
+    }
+}
+
+/**
+ * Create a new environment by copying an existing one and replacing some definitions
+ * @param xenv existing environment we will copy
+ * @param ldefs list of pairs (symbol id x definition) that will replace old definitions
+ * @param visited set of visited symbols (used for recursive definition detection)
+ * @param lenv the current environment to evaluate the definitions
+ * @return the new environment
+*/
+Tree copyEnvReplaceDefs(Tree anEnv, Tree ldefs, Tree visited, Tree curEnv)
+{
+    vector<Tree>    ids, clos;
+    Tree            copyEnv;
+
+    anEnv->exportProperties(ids, clos);             // get the definitions of the environment
+    copyEnv = pushNewLayer(anEnv->branch(0));       // create new environment with same stack
+    updateClosures(clos, anEnv, copyEnv);           // update the closures replacing oldEnv with newEnv
+
+       for (unsigned int i=0; i < clos.size(); i++) {           // transfers the updated definitions to the new environment
+        setProperty(copyEnv, ids[i], clos[i]);
+    }
+
+    while (!isNil(ldefs)) {                         // replace the old definitions with the new ones
+        Tree def = hd(ldefs);
+        Tree id = hd(def);
+        Tree rhs= tl(def);
+        Tree cl = closure(rhs,nil,visited,curEnv);
+        stringstream s; s << boxpp(id);
+        if (!isBoxCase(rhs)) setDefNameProperty(cl,s.str());
+        setProperty(copyEnv, id, cl);
+        ldefs = tl(ldefs);
+    }
+    return copyEnv;
+}
+
+