Package com.stuffwithstuff.magpie.interpreter

Source Code of com.stuffwithstuff.magpie.interpreter.Interpreter

package com.stuffwithstuff.magpie.interpreter;

import java.util.*;
import java.util.Map.Entry;

import com.stuffwithstuff.magpie.MagpieHost;
import com.stuffwithstuff.magpie.SourceFile;
import com.stuffwithstuff.magpie.ast.*;
import com.stuffwithstuff.magpie.intrinsic.ClassInit;
import com.stuffwithstuff.magpie.intrinsic.FieldGetter;
import com.stuffwithstuff.magpie.intrinsic.FieldSetter;
import com.stuffwithstuff.magpie.parser.MagpieParser;
import com.stuffwithstuff.magpie.parser.ParseException;

public class Interpreter {
  public Interpreter(MagpieHost host) {
    mHost = host;

    // Bootstrap the base module with the core definitions.
    mBaseModule = new Module("magpie.core", mHost.loadModule("magpie.core"), this);
   
    EnvironmentBuilder builder = new EnvironmentBuilder(this);
    mClass = builder.createClassClass();
    builder.initialize();
   
    Scope scope = mBaseModule.getScope();
    mArrayClass = scope.get("Array").asClass();
    mBoolClass = scope.get("Bool").asClass();
    mFnClass = scope.get("Function").asClass();
    mIntClass = scope.get("Int").asClass();
    mListClass = scope.get("List").asClass();
    mNothingClass = scope.get("Nothing").asClass();
    mRecordClass = scope.get("Record").asClass();
    mStringClass = scope.get("String").asClass();
   
    mTrue = mBaseModule.instantiate(mBoolClass, true);
    mFalse = mBaseModule.instantiate(mBoolClass, false);
    mNothing = mBaseModule.instantiate(mNothingClass, null);
   
    evaluateModule(mBaseModule);
   
    // Now load the syntax module so that quotations and metaprogramming work.
    mSyntaxModule = importModule("magpie.syntax");
  }
 
  public void interpret(SourceFile info) {
    evaluateModule(new Module("", info, this));
  }

  public Obj interpret(Expr expression) {
    return evaluate(expression, mBaseModule, mBaseModule.getScope());
  }
 
  public Obj evaluate(Expr expr, Module module, Scope scope) {
    ExprEvaluator evaluator = new ExprEvaluator(module);
    return evaluator.evaluate(expr, scope);
  }
 
  public String evaluateToString(Obj value) {
    Multimethod multimethod = mBaseModule.getScope().lookUpMultimethod(
        Name.TO_STRING);
    return multimethod.invoke(Name.TO_STRING,
        mBaseModule, value, mNothing).asString();
  }
 
  public Obj invoke(Obj leftArg, String method, Obj rightArg) {
    Multimethod multimethod = mBaseModule.getScope().lookUpMultimethod(method);
    return multimethod.invoke(method,
        mBaseModule, leftArg, rightArg);
  }
 
  public boolean objectsEqual(Obj a, Obj b) {
    // Shortcuts to avoid infinite regress. Identical values always match, and
    // "true" and "false" never match each other. This lets us match on values
    // before truthiness or "==" have been bootstrapped.
    if (a == b) return true;

    if (a == mTrue && b == mFalse) return false;
    if (a == mFalse && b == mTrue) return false;
   
    // Recursion base case. If we're in the middle of dispatching a call to
    // "==", don't call it again, just default to identity.
    if (mInObjectsEqual) return a == b;

    Multimethod equals = mBaseModule.getScope().lookUpMultimethod(Name.EQEQ);  
   
    // Bootstrap short-cut. If we haven't defined "==" yet, default to identity.
    if (equals == null) return a == b;
   
    mInObjectsEqual = true;
    Obj result = equals.invoke(Name.EQEQ, mBaseModule, a, b);
    mInObjectsEqual = false;
   
    return result.asBool();
  }
 
  public Module importModule(String name) {
    // TODO(bob): Check for circular references.
   
    // If it's a relative name, fully expand it.
    if (name.startsWith(".")) {
      name = mLoadingModules.peek().getName() + name;
    }
   
    Module module = mModules.get(name);
   
    // Only load it once.
    if (module == null) {
      SourceFile info = mHost.loadModule(name);
      module = new Module(name, info, this);
      mModules.put(name, module);
     
      evaluateModule(module);
    }
   
    return module;
  }
 
  public ErrorException error(String errorClassName, String message) {
    // Look up the error class.
    ClassObj classObj = mBaseModule.getScope().get(errorClassName).asClass();

    // TODO(bob): Hackish.
    Context context = mBaseModule;
   
    // TODO(bob): Putting the message in here as the value is kind of hackish,
    // but it ensures we can display an error message even if we aren't able
    // to evaluate any code (like calling "string" on the error).
    Obj error = context.instantiate(classObj, message);
   
    error.setValue(message);
   
    throw new ErrorException(error);
  }
 
  /**
   * Gets the single value "nothing" of type Nothing.
   * @return
   */
  public Obj nothing() { return mNothing; }

  public ClassObj getArrayClass() { return mArrayClass; }
  public ClassObj getBoolClass() { return mBoolClass; }
  public ClassObj getClassClass() { return mClass; }
  public ClassObj getFnClass() { return mFnClass; }
  public ClassObj getIntClass() { return mIntClass; }
  public ClassObj getListClass() { return mListClass; }
  public ClassObj getRecordClass() { return mRecordClass; }
  public ClassObj getStringClass() { return mStringClass; }
 
  public Module getBaseModule() { return mBaseModule; }
  public Module getSyntaxModule() { return mSyntaxModule; }
 
  public Obj getBool(boolean value) {
    return value ? mTrue : mFalse;
  }
 
  public ClassObj createClass(String name, List<ClassObj> parents,
      Map<String, Field> fields, Scope scope, String doc) {
   
    // Translate the fields.
    Map<String, FieldObj> fieldObjs = new HashMap<String, FieldObj>();
    for (Entry<String, Field> field : fields.entrySet()) {
      Callable initializer = null;
      if (field.getValue().getInitializer() != null) {
        FnExpr fn = Expr.fn(field.getValue().getInitializer(), "");
        initializer = new Function(fn, scope);
      }
     
      FieldObj fieldObj = new FieldObj(initializer,
          field.getValue().getPattern());
      fieldObjs.put(field.getKey(), fieldObj);
    }
   
    // Create the class.
    ClassObj classObj = new ClassObj(mClass, name, parents, fieldObjs, doc);
   
    ClassObj colliding = classObj.checkForCollisions();
    if (colliding != null) {
      error(Name.PARENT_COLLISION_ERROR, "Class \"" + name +
          "\" is trying to inherit from \"" + colliding.getName() +
          "\" more than once.");
    }
   
    // Add the constructor.
    Multimethod init = scope.define(Name.INIT, new ClassInit(classObj, scope));
    classObj.bindInitMultimethod(init);
   
    // Add getters and setters for the fields.
    for (Entry<String, Field> entry : fields.entrySet()) {
      // Getter.
      scope.define(entry.getKey(),
          new FieldGetter(classObj, entry.getKey(), scope));

      // Setter, if the field is mutable ("var" instead of "val").
      if (entry.getValue().isMutable()) {
        scope.define(Name.makeAssigner(entry.getKey()),
            new FieldSetter(classObj, entry.getKey(), entry.getValue(), scope));
      }
    }
   
    return classObj;
  }
   
  public MagpieHost getHost() {
    return mHost;
  }
 
  public Map<String, Multimethod> getMultimethods() {
    return mMultimethods;
  }
 
  public Obj getConstructingObject() { return mConstructing.peek(); }
 
  public Obj constructNewObject(Context context, ClassObj classObj, Obj initArg) {
    Obj newObj = context.instantiate(classObj, null);
   
    mConstructing.push(newObj);

    // Call the init() multimethod.
    initializeNewObject(context, classObj, initArg);

    mConstructing.pop();
   
    return newObj;
  }
 
  public void initializeNewObject(Context context, ClassObj classObj, Obj arg) {
    // Keep track of how many times we reach the canonical initializer so that
    // we can generate an error if an init() call fails to bottom out to it.
    int expected = mInitializingCount++;
   
    Multimethod init = classObj.getInitMethod();
   
    // Note: the receiver for init() is the class itself, not the new instance
    // which is considered to be in a hidden state since it isn't initialized
    // yet.
    init.invoke(Name.INIT, context, classObj, arg);

    // Make sure the canonical initializer was called.
    if (mInitializingCount > expected) {
      // Just decrement it so the error doesn't cascade.
      mInitializingCount--;

      error(Name.INITIALIZATION_ERROR,
          "Instance of class " + classObj.getName() + " was not initialized.");
    }
  }
 
  public void finishInitialization() {
    mInitializingCount--;
  }
 
  private void evaluateModule(Module module) {
    MagpieParser parser = new MagpieParser(module.readSource());
   
    mLoadingModules.push(module);
    try {
      // Copy the base stuff in first.
      if (module != mBaseModule) {
        Scope scope = module.getScope();
        for (String name : mBaseModule.getExportedNames()) {
          scope.importName(name, name, mBaseModule, false);
        }
      }
     
      // Evaluate the module.
      try {
        List<Expr> exprs = parser.parseModule();
        for (Expr expr : exprs) {
          evaluate(expr, module, module.getScope());
        }
      } catch (ParseException e) {
        String message = String.format("Syntax error at %s: %s",
            e.getPosition(), e.getMessage());
        mHost.showSyntaxError(message);
      }
    } finally {
      mLoadingModules.pop();
    }
  }
 
  private final MagpieHost mHost;
 
  private final Map<String, Module> mModules = new HashMap<String, Module>();
  private final Map<String, Multimethod> mMultimethods = new HashMap<String, Multimethod>();
 
  private final ClassObj mClass;
  private final ClassObj mArrayClass;
  private final ClassObj mBoolClass;
  private final ClassObj mFnClass;
  private final ClassObj mIntClass;
  private final ClassObj mListClass;
  private final ClassObj mNothingClass;
  private final ClassObj mRecordClass;
  private final ClassObj mStringClass;
 
  private final Obj mNothing;
  private final Obj mTrue;
  private final Obj mFalse;
 
  private final Stack<Module> mLoadingModules = new Stack<Module>();
  private final Module mBaseModule;
  private final Module mSyntaxModule;
 
  private final Stack<Obj> mConstructing = new Stack<Obj>();
  private int mInitializingCount = 0;

  private boolean mInObjectsEqual = false;
}
TOP

Related Classes of com.stuffwithstuff.magpie.interpreter.Interpreter

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.