Package kiss.lang

Source Code of kiss.lang.Environment$EnvioronmentIterator

package kiss.lang;

import java.util.Iterator;
import java.util.Map;

import kiss.lang.expression.Constant;
import kiss.lang.impl.IExitResult;
import kiss.lang.impl.KissException;
import clojure.lang.APersistentMap;
import clojure.lang.IMapEntry;
import clojure.lang.IPersistentMap;
import clojure.lang.IPersistentSet;
import clojure.lang.ISeq;
import clojure.lang.PersistentHashMap;
import clojure.lang.PersistentHashSet;
import clojure.lang.RT;
import clojure.lang.Symbol;

/**
* This is the immutable environment used by the Kiss compiler
*
* Design intent:
*  - Behaves like an immutable map of Symbols -> Values
*  - Maintains a dependency graph for recompilation
*  - Maintains a "result" value, so it can include an expression return value
* It is a first class object, but probably shouldn't be messed with outside of the kiss.core functions.
*
* @author Mike
*
*/
public final class Environment extends APersistentMap {
  private static final long serialVersionUID = -2048617052932290067L;
 
  public static final Environment EMPTY = new Environment();
 
  public final IPersistentMap map; // Symbol -> Mapping
  public final IPersistentMap dependencies; // Symbol -> set of Symbols
  public final IPersistentMap dependents; // Symbol -> set of Symbols
  public final Object result;

  private Environment() {
    this(PersistentHashMap.EMPTY,PersistentHashMap.EMPTY,PersistentHashMap.EMPTY,null);
  }
 
  private Environment(IPersistentMap map, IPersistentMap deps, IPersistentMap backDeps, Object result) {
    this.map=map;
    this.result=result;
    this.dependencies=deps;
    this.dependents=backDeps;
  }
 
  public Environment withResult(Object value) {
    if (value==result) return this;
    return new Environment(map,dependencies,dependents,value);
  }
 
  /**
   * Redefine a symbol in this environment to a new Expression.
   *
   * @param key
   * @param body
   * @return
   */
  public Environment define(Symbol key, Expression body) {
    return define(key,body,PersistentHashMap.EMPTY);
  }
 
  public Environment define(Symbol key, Expression body, IPersistentMap bindings) {
    // handle define with local bindings
    if (bindings.count()>0) {
      Expression newBody=body.substitute(bindings);
      if (newBody!=body) newBody=newBody.optimise(); // re-optimise if needed
      body=newBody;
    }
   
    // manage dependency updates
    IPersistentMap tempDependencies=this.dependencies;
    IPersistentMap tempDependents=this.dependents;
   
    IPersistentSet free=body.accumulateFreeSymbols(PersistentHashSet.EMPTY);
   
    IPersistentSet oldDeps=(IPersistentSet) tempDependencies.valAt(key);
    if ((oldDeps==null)) oldDeps=PersistentHashSet.EMPTY;

    // update dependencies to match the free variables in the expression
    tempDependencies=tempDependencies.assoc(key, free);
    tempDependents=updateBackDeps(key,tempDependents,oldDeps,free);
   
    // Compute which symbols cannot yet be bound from the current environment
    IPersistentSet unbound=free;
    for (ISeq s=RT.seq(unbound);s!=null; s=s.next()) {
      Symbol sym=(Symbol) s.first();
      if (isBound(sym)) {
        unbound=unbound.disjoin(sym);
      }
    }
   
    if (unbound.count()==0) {
      Environment newEnv=body.compute(this, bindings);
      Object value=newEnv.getResult();
      newEnv = new Environment(map.assoc(key, Mapping.createExpression(body, value, null)),tempDependencies,tempDependents,value);
     
      newEnv=updateDependents(newEnv,key);
     
      return newEnv;
    } else {
      return new Environment(map.assoc(key, Mapping.createExpression(body, null, unbound)),tempDependencies,tempDependents,null)
    }
  }
 
  @SuppressWarnings("unchecked")
  private static Environment updateDependents(Environment e, Symbol key) {
    // get the set of symbols that depend directly or indirectly on the given key
    IPersistentSet ss = e.accumulateDependents(PersistentHashSet.EMPTY,key);
   
    // check if there are any dependents
    if (ss==PersistentHashSet.EMPTY) return e;
   
    for (Symbol s:((java.util.Collection<Symbol>)ss)) {
      Mapping m=(Mapping) e.map.valAt(s);
      IPersistentSet sunbound=m.getUnbound();
      if (sunbound.count()==0) {
        // TODO: update needed!
      }
    }
    return e;
   
  }
 
  @SuppressWarnings("unchecked")
  private IPersistentSet accumulateDependents(IPersistentSet set, Symbol key) {
    IPersistentSet ss=(IPersistentSet)(dependents.valAt(key));
    if ((ss==null)||(ss==PersistentHashSet.EMPTY)) return set;
    for (Symbol s: ((java.util.Collection<Symbol>)ss)) {
      if (!set.contains(s)) {
        set=(IPersistentSet) set.cons(s);
        accumulateDependents(set,s);
      }
    }
    return set;
  }
 
  private boolean isBound(Symbol sym) {
    Object m= map.valAt(sym);
    if (m==null) return false;
    return ((Mapping)m).isBound();
  }

  private IPersistentMap updateBackDeps(Symbol key,IPersistentMap backDeps,
      IPersistentSet oldDeps, IPersistentSet newDeps) {
    if (oldDeps==newDeps) return backDeps;
   
    // add new back dependencies
    for (ISeq s=newDeps.seq(); s!=null; s=s.next()) {
      Symbol sym=(Symbol)s.first();
      if (oldDeps.contains(sym)) continue;
      IPersistentSet bs=(IPersistentSet) backDeps.valAt(sym);
      if (bs==null) bs=PersistentHashSet.EMPTY;
      backDeps=backDeps.assoc(sym, bs.cons(key));
    }
   
    // remove old back dependencies
    for (ISeq s=oldDeps.seq(); s!=null; s=s.next()) {
      Symbol sym=(Symbol)s.first();
      if (newDeps.contains(sym)) continue;
      IPersistentSet bs=(IPersistentSet) backDeps.valAt(sym);
      bs=bs.disjoin(key);
      if (bs.count()==0) {
        backDeps=backDeps.without(sym);
      } else {
        backDeps=backDeps.assoc(sym, bs);
      }
    }
   
    return backDeps;
  }

  @Override
  public IPersistentMap without(Object key) {
    Mapping m=getMapping(key);
    if (m==null) return this;
    return new Environment(map.without(key),dependencies,dependents, result);
  }
 
  @Override
  public Environment assoc(Object key, Object val) {
    return define((Symbol) key,Constant.create(val));
  }
 

  @Override
  public Environment assocEx(Object key, Object val) {
    return define((Symbol) key,Constant.create(val));
  }

 
  public Mapping getMapping(Object key) {
    return (Mapping)map.valAt(key);
  }

  @SuppressWarnings("unchecked")
  @Override
  public Iterator<?> iterator() {
    return new EnvioronmentIterator(map.iterator());
  }
 
  private static final class EnvioronmentIterator implements Iterator<Entry<Symbol,Mapping>> {
    final Iterator<Entry<Symbol,Mapping>> source;
   
    private EnvioronmentIterator(Iterator<Entry<Symbol,Mapping>> vs) {
      this.source=vs;
    }
   
    @Override
    public boolean hasNext() {
      return source.hasNext();
    }

    @SuppressWarnings("unchecked")
    @Override
    public Entry<Symbol,Mapping> next() {
      Entry<Symbol,Mapping> entry=source.next();
      Mapping m=entry.getValue();
      return m.toMapEntry(entry.getKey());
    }

    @Override
    public void remove() {
      throw new UnsupportedOperationException("Immutable!");
    }   
  }
 
  /**
   * Merges a second environment into this one
   * @param e
   * @return
   */
  @SuppressWarnings("unchecked")
  public Environment merge(Environment e) {
    Environment result=this;
    for (Object o : e.map) {
      Map.Entry<Symbol, Mapping> ent=(Entry<Symbol, Mapping>) o;
      result=result.define(ent.getKey(), ent.getValue().getExpression());
    }
    return result;
  }

  @Override
  public boolean containsKey(Object key) {
    return map.containsKey(key);
  }

  @Override
  public IMapEntry entryAt(Object key) {
    Mapping m=getMapping(key);
    if (m==null) return null;
    return m.toMapEntry(key);
  }

  @Override
  public int count() {
    return map.count();
  }

  @Override
  public Environment empty() {
    return EMPTY;
  }

  @Override
  public ISeq seq() {
    return clojure.lang.IteratorSeq.create(iterator());
  }

  @Override
  public Object valAt(Object key) {
    Mapping m=getMapping(key);
    if (m==null) return null;
    return m.getValue();
  }

  @Override
  public Object valAt(Object key, Object notFound) {
    Mapping m=getMapping(key);
    if (m==null) return notFound;
    return m.getValue();
  }

  /**
   * Returns the current result in the environment.
   * @return
   */
  public Object getResult() {
    return result;
  }

  @SuppressWarnings("unchecked")
  public void validate() {
    for (Object o : map) {
      Map.Entry<Symbol, Mapping> ent=(Entry<Symbol, Mapping>) o;
      Symbol key=ent.getKey();
      Mapping m=ent.getValue();
      if (m==null) throw new KissException("Unexcpected null mapping for symbol: "+key);
     
      // check free symbols equals dependencies
      IPersistentSet free=m.getExpression().accumulateFreeSymbols(PersistentHashSet.EMPTY);
      IPersistentSet ds=(IPersistentSet) dependencies.valAt(key);
      if (!free.equiv(ds)) {
        throw new KissException("Mismatched dependencies for symbol: "+key+" free="+free+" deps="+ds);
      }
     
      // check unbound dependencies are consistent
      IPersistentSet unbound=m.getUnbound();
      for (ISeq s=unbound.seq(); s!=null; s=s.next()) {
        Symbol sym=(Symbol)s.first();
        if (isBound(sym)) throw new KissException("Expected symbol to be unbound: "+sym);
      }
     
      // check reverse dependencies
      for (ISeq s=ds.seq(); s!=null; s=s.next()) {
        Symbol sym=(Symbol)s.first();
        IPersistentSet bs=(IPersistentSet) dependents.valAt(sym);
        if (!bs.contains(key)) throw new KissException("Missing back dependency from "+sym+"=>"+key);
      }
    }
  }

  /**
   * Returns true if the current result is an exit condition. This includes recur and return conditions.
   *
   * @return
   */
  public boolean isExiting() {
    return result instanceof IExitResult;
  }
}
TOP

Related Classes of kiss.lang.Environment$EnvioronmentIterator

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.