public class BuiltinNodes {
  private HBLanguage language;

  private HashMap<String, MethodTargets> classes = new HashMap<>();

  private BuiltinNodes(HBLanguage language) {
    this.language = language;
    this.addNodeFactories(GlobalNodesFactory.getFactories());
    this.addNodeFactories(IntegerNodesFactory.getFactories());
    this.addNodeFactories(StringNodesFactory.getFactories());
  }

  public static BuiltinNodes bootstrap(HBLanguage language) {
    return new BuiltinNodes(language);
  }

  private void addNodeFactories(List<? extends NodeFactory<? extends HBNode>> factories) {
    for (NodeFactory<? extends HBNode> factory : factories) {
      Class<?> nodeClass = factory.getClass().getAnnotation(GeneratedBy.class).value();
      BuiltinMethod methodAnnotation = this.getMethodAnnotation(nodeClass);
      BuiltinClass classAnnotation = this.getClassAnnotation(nodeClass);

      String methodName = methodAnnotation.value();
      String className = classAnnotation.value();
      MethodTargets methodTargets = this.getOrCreateMethodTargets(className);

      HBNode bodyNode = this.createNode(factory, methodAnnotation);

      HBBuiltinRootNode rootNode = new HBBuiltinRootNode(
        this.language,
        new FrameDescriptor(),
        bodyNode
      );
      CallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode);
      methodTargets.put(methodName, callTarget);
    }
  }

  private HBNode createNode(NodeFactory<? extends HBNode> factory, BuiltinMethod methodAnnotation) {
    List<List<Class<?>>> signatures = factory.getNodeSignatures();
    assert signatures.size() == 1;

    List<HBNode> argumentsNodes = new ArrayList<>();
    if (methodAnnotation.usesThis()) {
      argumentsNodes.add(new GetThisNode());
    }
    int argumentsCount = methodAnnotation.required();
    for (int index = 0; index < argumentsCount; index++) {
      argumentsNodes.add(new GetArgumentNode(index));
    }

    Object[] arguments = argumentsNodes.toArray(new HBNode[argumentsNodes.size()]);
    return factory.createNode(arguments);
  }

  /**
   * Helper to assist in easily creating a `MethodType`s with a builtin
   * method node as a call target (using `BuiltinMethod` annotation to
   * derive the name).
   */
  public MethodType createMethodType(Type receiverType, Type[] parameterTypes, Type returnType, Class<?> nodeClass) {
    String name = this.getMethodAnnotation(nodeClass).value();
    CallTarget callTarget = this.getCallTarget(nodeClass);
    return new MethodType(receiverType, parameterTypes, returnType, name, callTarget);
  }

  public FunctionType createFunctionType(Type[] parameterTypes, Type returnType, Class<?> nodeClass) {
    String name = this.getMethodAnnotation(nodeClass).value();
    CallTarget callTarget = this.getCallTarget(nodeClass);
    return new FunctionType(parameterTypes, returnType, name, callTarget);
  }

  public CallTarget getCallTarget(Class<?> nodeClass) {
    String className = this.getClassAnnotation(nodeClass).value();
    String methodName = this.getMethodAnnotation(nodeClass).value();
    return this.getCallTarget(className, methodName);
  }

  public CallTarget getCallTarget(String className, String methodName) {
    if (!this.classes.containsKey(className)) {
      throw new Error("Missing built-in class: " + className);
    }

    MethodTargets methodTargets = this.classes.get(className);
    if (!methodTargets.contains(methodName)) {
      throw new Error("Missing built-in method: " + className + "." + methodName);
    }
    return methodTargets.get(methodName);
  }

  private MethodTargets getOrCreateMethodTargets(String name) {
    if (this.classes.containsKey(name)) {
      return this.classes.get(name);
    } else {
      MethodTargets methodTargets = new MethodTargets();
      this.classes.put(name, methodTargets);
      return methodTargets;
    }
  }

  private BuiltinMethod getMethodAnnotation(Class<?> nodeClass) {
    return nodeClass.getAnnotation(BuiltinMethod.class);
  }

  private BuiltinClass getClassAnnotation(Class<?> nodeClass) {
    return nodeClass.getEnclosingClass().getAnnotation(BuiltinClass.class);
  }

  public class MethodTargets {
    private HashMap<String, CallTarget> map = new HashMap<>();

    public MethodTargets() {
    }

    public boolean contains(String methodName) {
      return this.map.containsKey(methodName);
    }

    public CallTarget get(String methodName) {
      return this.map.get(methodName);
    }

    public CallTarget put(String methodName, CallTarget callTarget) {
      return this.map.put(methodName, callTarget);
    }
  }
} @BuiltinClass("Global")
public abstract class GlobalNodes {
  @BuiltinMethod(value = "println", usesThis = false, required = 1)
  @NodeChild(value = "object", type = HBNode.class)
  public abstract static class PrintlnNode extends BuiltinNode {
    @Specialization
    public Object println(Object object) {
      System.out.println(object);
      return null;
    }
  }
} public final class FunctionType extends ConcreteType {
  private final Type[] parameterTypes;
  private final Type returnType;
  private final String name;
  private final CallTarget callTarget;
  // Scope where the function was declared.
  private Scope declarationScope;
  // Scope inside the function (ie. of its block).
  private Scope ownScope;

  public FunctionType(
    Type[] parameterTypes,
    Type returnType,
    String name,
    CallTarget callTarget
  ) {
    this.parameterTypes = parameterTypes;
    this.returnType = returnType;
    this.name = name;
    this.callTarget = callTarget;
  }

  public Type[] getParameterTypes() {
    return this.parameterTypes;
  }

  public Type getReturnType() {
    return this.returnType;
  }

  public String getName() {
    return this.name;
  }

  public CallTarget getCallTarget() {
    return this.callTarget;
  }

  public Scope getOwnScope() {
    if (this.ownScope != null && !this.ownScope.isClosed()) {
      throw new RuntimeException("Scope not yet closed");
    }
    return this.ownScope;
  }

  public void setOwnScope(Scope scope) {
    if (this.ownScope != null) {
      throw new RuntimeException("Cannot re-set scope");
    }
    this.ownScope = scope;
  }

  public Scope getDeclarationScope() {
    if (this.declarationScope != null && !this.declarationScope.isClosed()) {
      throw new RuntimeException("Scope not yet closed");
    }
    return this.declarationScope;
  }

  public void setDeclarationScope(Scope declarationScope) {
    if (this.declarationScope != null) {
      throw new RuntimeException("Cannot re-set scope");
    }
    this.declarationScope = declarationScope;
  }

  @Override
  public Property getProperty(String name) throws PropertyNotFoundException {
    throw new PropertyNotFoundException("Not yet implemented");
  }
} public abstract class Type {
  public abstract Property getProperty(String name) throws PropertyNotFoundException;

  public void assertEquals(Type otherType) throws TypeMismatchException {
    // Rudimentary pointer comparison by default.
    if (this != otherType) {
      throw new TypeMismatchException(this, otherType);
    }
  }

  /**
   * Returns whether or not this type is equal to another type.
   */
  public boolean equals(Type otherType) {
    try {
      this.assertEquals(otherType);
    } catch (TypeMismatchException exception) {
      return false;
    }
    return true;
  }
} public class UnknownType extends Type {
  @Override
  public Property getProperty(String name) throws PropertyNotFoundException {
    throw new PropertyNotFoundException("Unknown type cannot have property: " + name);
  }
} public class SumType extends CompositeType {
  public final Type[] subTypes;

  public SumType(Type[] subTypes) {
    this.subTypes = subTypes;
  }

  @Override
  public Property getProperty(String name) throws PropertyNotFoundException {
    throw new PropertyNotFoundException("Not yet implemented");
  }

  /**
   * Other type must be equal to or smaller than this union for equality
   * to hold.
   */
  @Override
  public boolean equals(Type otherType) {
    // Equality check first.
    if (this == otherType) {
      return true;
    }
    // Then check if we contain the other type.
    for (Type subType : this.subTypes) {
      if (subType.equals(otherType)) {
        return true;
      }
    }
    return false;
  }

  @Override
  public String toString() {
    StringBuilder result = new StringBuilder();
    boolean bar = false;
    for (Type subType : this.subTypes) {
      if (bar) {
        result.append(" | ");
      } else {
        bar = true;
      }
      result.append(subType.toString());
    }
    return result.toString();
  }
} public class TypeReferenceType extends CompositeType {
  private Type type;

  public TypeReferenceType(Type type) {
    this.type = type;
  }

  public Type getType() {
    return this.type;
  }

  @Override
  public Property getProperty(String name) throws PropertyNotFoundException {
    throw new PropertyNotFoundException("Not yet implemented");
  }

  @Override
  public String toString() {
    return "'" + this.type.toString();
  }
} public abstract class BootstrappableConcreteType extends ConcreteType {
  /**
   * Name that the type will be bootstrapped into the index module as. Should
   * be "String", "Integer", etc.
   */
  public abstract String getBootstrapName();

  /**
   * Called at the start of bootstrapping so that the type can fetch call
   * targets from the `BuiltinNodes`.
   */
  public abstract void bootstrapBuiltins(BuiltinNodes builtins);

  /**
   * Called at the end of bootstrapping so that it can fill in references
   * to other built-in types.
   */
  public abstract void bootstrapTypes(Module builtinModule);
} public final class IntegerType extends BootstrappableConcreteType {
  public static String BUILTIN_NAME = "Integer";

  private MethodType toString;

  @Override
  public String getBootstrapName() {
    return IntegerType.BUILTIN_NAME;
  }

  @Override
  public void bootstrapBuiltins(BuiltinNodes builtins) {
    this.toString = builtins.createMethodType(this, new Type[]{}, new UnknownType(), IntegerNodes.ToStringNode.class);
  }

  @Override
  public void bootstrapTypes(Module builtinModule) {
    this.toString = this.toString.cloneWithReturnType(builtinModule.getType(StringType.BUILTIN_NAME));
  }

  @Override
  public Property getProperty(String name) throws PropertyNotFoundException {
    switch (name) {
      case "toString":
        return MethodProperty.fromType(this.toString);
      default:
        throw new PropertyNotFoundException(this, name);
    }
  }

  @Override
  public String toString() {
    return "$Integer";
  }
} public final class NullType extends ConcreteType {
  public static final NullType SINGLETON = new NullType();

  private NullType() {
  }

  @Override
  public Property getProperty(String name) throws PropertyNotFoundException {
    throw new PropertyNotFoundException("Not yet implemented");
  }
} public final class StringType extends BootstrappableConcreteType {
  public static String BUILTIN_NAME = "String";

  private MethodType toUpperCase;

  @Override
  public String getBootstrapName() {
    return StringType.BUILTIN_NAME;
  }

  @Override
  public void bootstrapBuiltins(BuiltinNodes builtins) {
    this.toUpperCase = builtins.createMethodType(this, new Type[]{}, this, StringNodes.ToUpperCaseNode.class);
  }

  @Override
  public void bootstrapTypes(Module builtinModule) {
    return;
  }

  @Override
  public Property getProperty(String name) throws PropertyNotFoundException {
    switch (name) {
      case "toUpperCase":
        return MethodProperty.fromType(this.toUpperCase);
      default:
        throw new PropertyNotFoundException(this, name);
    }
  }

  @Override
  public String toString() {
    // NOTE: Concrete types' string representations are prefixed with "$"
    //   to indicate their internal, concrete nature (for now).
    return "$String";
  }
}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import org.hummingbirdlang.nodes.builtins.BuiltinNodes;
import org.hummingbirdlang.nodes.builtins.GlobalNodes;
import org.hummingbirdlang.types.FunctionType;
import org.hummingbirdlang.types.Type;
import org.hummingbirdlang.types.UnknownType;
import org.hummingbirdlang.types.composite.SumType;
import org.hummingbirdlang.types.composite.TypeReferenceType;
import org.hummingbirdlang.types.concrete.BootstrappableConcreteType;
import org.hummingbirdlang.types.concrete.IntegerType;
import org.hummingbirdlang.types.concrete.NullType;
import org.hummingbirdlang.types.concrete.StringType;
package org.hummingbirdlang.types.realize;



/**
 * Maps exportable fully-qualified names (either a file-system path or
 * "builtin") to types. Also reponsible for bootstrapping concrete types.
 */
public final class Index {
  public static final String BUILTIN = "builtin";

  private final HashMap<String, Module> modules = new HashMap<>();

  private Index(BuiltinNodes builtins) {
    // Instantiate all the bootstrappable types.
    ArrayList<BootstrappableConcreteType> bootstrappableTypes = new ArrayList<>();
    bootstrappableTypes.add(new IntegerType());
    bootstrappableTypes.add(new StringType());