All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aya.resolve.visitor.ExprResolver Maven / Gradle / Ivy

There is a newer version: 0.36.0
Show newest version
// Copyright (c) 2020-2024 Tesla (Yinsen) Zhang.
// Use of this source code is governed by the MIT license that can be found in the LICENSE.md file.
package org.aya.resolve.visitor;

import kala.collection.immutable.ImmutableSeq;
import kala.collection.mutable.MutableLinkedHashMap;
import kala.collection.mutable.MutableList;
import kala.collection.mutable.MutableMap;
import kala.collection.mutable.MutableStack;
import kala.value.MutableValue;
import org.aya.generic.stmt.TyckOrder;
import org.aya.generic.stmt.TyckUnit;
import org.aya.resolve.context.Context;
import org.aya.resolve.context.ModuleContext;
import org.aya.resolve.context.NoExportContext;
import org.aya.resolve.error.GeneralizedNotAvailableError;
import org.aya.syntax.concrete.Expr;
import org.aya.syntax.concrete.Pattern;
import org.aya.syntax.concrete.stmt.QualifiedID;
import org.aya.syntax.concrete.stmt.Stmt;
import org.aya.syntax.concrete.stmt.decl.DataCon;
import org.aya.syntax.ref.AnyVar;
import org.aya.syntax.ref.DefVar;
import org.aya.syntax.ref.GeneralizedVar;
import org.aya.syntax.ref.LocalVar;
import org.aya.tyck.error.ClassError;
import org.aya.util.error.Panic;
import org.aya.util.error.PosedUnaryOperator;
import org.aya.util.error.SourcePos;
import org.aya.util.error.WithPos;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;

/**
 * Resolves bindings.
 *
 * @param allowedGeneralizes will be filled with generalized vars if {@param allowGeneralizing},
 *                           and represents the allowed generalized level vars otherwise
 * @param allowGeneralizing  allow new generalized vars to be introduced
 * @author re-xyr, ice1000
 * @implSpec allowedGeneralizes must be linked map
 * @see StmtResolver
 */
public record ExprResolver(
  @NotNull Context ctx,
  boolean allowGeneralizing,
  @NotNull MutableMap allowedGeneralizes,
  @NotNull MutableList reference,
  @NotNull MutableStack where
) implements PosedUnaryOperator {
  public record LiterateResolved(
    ImmutableSeq params,
    WithPos expr
  ) {
    public @NotNull LiterateResolved descent(PosedUnaryOperator desalt) {
      return new LiterateResolved(params.map(p -> p.descent(desalt)), expr.descent(desalt));
    }
  }
  /**
   * Do !!!NOT!!! use in the type checker.
   * This is solely for cosmetic features, such as literate mode inline expressions, or repl.
   */
  @Contract(pure = true)
  public static LiterateResolved resolveLax(@NotNull ModuleContext context, @NotNull WithPos expr) {
    var resolver = new ExprResolver(context, true);
    resolver.enter(Where.FnBody);
    var inner = expr.descent(resolver);
    var view = resolver.allowedGeneralizes().valuesView().toImmutableSeq();
    return new LiterateResolved(view, inner);
  }

  public ExprResolver(@NotNull Context ctx, boolean allowGeneralizing) {
    this(ctx, allowGeneralizing, MutableLinkedHashMap.of(), MutableList.create(), MutableStack.create());
  }

  public void resetRefs() { reference.clear(); }
  public void enter(Where loc) { where.push(loc); }
  public void exit() { where.pop(); }

  public @NotNull ExprResolver enter(Context ctx) {
    return ctx == ctx() ? this : new ExprResolver(ctx, allowGeneralizing, allowedGeneralizes, reference, where);
  }

  /**
   * The intended usage is to create an {@link ExprResolver}
   * that resolves the body/bodies of something.
   */
  public @NotNull ExprResolver deriveRestrictive() {
    return new ExprResolver(ctx, false, allowedGeneralizes, reference, where);
  }

  public @NotNull Expr pre(@NotNull Expr expr) {
    return switch (expr) {
      case Expr.Proj(var tup, var ix, _, var theCore) -> {
        if (ix.isLeft()) yield expr;
        var projName = ix.getRightValue();
        var resolvedIx = ctx.getMaybe(projName);
        if (resolvedIx == null) ctx.reportAndThrow(new ClassError.UnknownMember(projName.sourcePos(), projName.join()));
        yield new Expr.Proj(tup, ix, resolvedIx, theCore);
      }
      case Expr.Hole(var expl, var fill, var core, var local) -> {
        assert local.isEmpty();
        yield new Expr.Hole(expl, fill, core,
          ctx.collect(MutableList.create()).toImmutableSeq());
      }
      default -> expr;
    };
  }

  /**
   * Special handling of terms with binding structure.
   * We need to invoke a resolver with a different context under the binders.
   */
  @Override public @NotNull Expr apply(@NotNull SourcePos pos, @NotNull Expr expr) {
    return switch (pre(expr)) {
      case Expr.Do doExpr ->
        doExpr.update(apply(SourcePos.NONE, doExpr.bindName()), bind(doExpr.binds(), MutableValue.create(ctx)));
//      case Expr.Match match -> {
//        var clauses = match.clauses().map(this::apply);
//        yield match.update(match.discriminant().map(this), clauses);
//      }
      case Expr.Lambda lam -> {
        var mCtx = MutableValue.create(ctx);
        mCtx.update(ctx -> bindAs(lam.ref(), ctx));
        yield lam.update(lam.body().descent(enter(mCtx.get())));
      }
      case Expr.DepType depType -> {
        var mCtx = MutableValue.create(ctx);
        var param = bind(depType.param(), mCtx);
        yield depType.update(param, depType.last().descent(enter(mCtx.get())));
      }
      case Expr.Array array -> array.update(array.arrayBlock().map(
        left -> {
          var mCtx = MutableValue.create(ctx);
          var binds = bind(left.binds(), mCtx);
          var generator = left.generator().descent(enter(mCtx.get()));
          return left.update(generator, binds, left.names().fmap(this::forceApply));
        },
        right -> right.descent(this)
      ));
      case Expr.Unresolved(var name) -> {
        var resolved = resolve(name);
        AnyVar finalVar = switch (resolved) {
          case GeneralizedVar generalized -> {
            // a "resolved" GeneralizedVar is not in [allowedGeneralizes]
            if (allowGeneralizing) {
              // Ordered set semantics. Do not expect too many generalized vars.
              var owner = generalized.owner;
              assert owner != null : "Sanity check";
              var param = owner.toExpr(false, generalized.toLocal());
              allowedGeneralizes.put(generalized, param);
              addReference(owner);
              yield param.ref();
            } else {
              yield ctx.reportAndThrow(new GeneralizedNotAvailableError(pos, generalized));
            }
          }
          case DefVar defVar -> {
            addReference(defVar);
            yield defVar;
          }
          case AnyVar var -> var;
        };

        yield new Expr.Ref(finalVar);
      }
      case Expr.Let let -> {
        // resolve letBind
        var letBind = let.bind();

        var mCtx = MutableValue.create(ctx);
        // visit telescope
        var telescope = letBind.telescope().map(param -> bind(param, mCtx));
        // for things that can refer the telescope (like result and definedAs)
        var resolver = enter(mCtx.get());
        // visit result
        var result = letBind.result().descent(resolver);
        // visit definedAs
        var definedAs = letBind.definedAs().descent(resolver);
        // end resolve letBind

        // resolve body
        var newBody = let.body().descent(enter(ctx.bind(letBind.bindName())));

        yield let.update(
          letBind.update(telescope, result, definedAs),
          newBody
        );
      }
      case Expr.LetOpen letOpen -> {
        var context = new NoExportContext(ctx);
        // open module
        context.openModule(letOpen.componentName(), Stmt.Accessibility.Private,
          letOpen.sourcePos(), letOpen.useHide());
        yield letOpen.update(letOpen.body().descent(enter(context)));
      }
      case Expr.Match match -> {
        var discriminant = match.discriminant().map(x -> x.descent(this));
        // FIXME: #clause enters Where.FnPattern and Where.FnBody, does it matter?
        var clauses = match.clauses().map(this::clause);
        yield match.update(discriminant, clauses);
      }

      case Expr newExpr -> newExpr.descent(this);
    };
  }

  private void addReference(@NotNull TyckUnit unit) {
    if (where.isEmpty()) throw new Panic("where am I?");
    switch (where.peek()) {
      case FnPattern -> {
        reference.append(new TyckOrder.Body(unit));
        if (unit instanceof DataCon con) {
          reference.append(new TyckOrder.Body(con.dataRef.concrete));
        }
      }
      default -> reference.append(new TyckOrder.Head(unit));
    }
  }

  private void addReference(@NotNull DefVar defVar) {
    addReference(defVar.concrete);
  }

  public @NotNull Pattern.Clause clause(@NotNull Pattern.Clause clause) {
    var mCtx = MutableValue.create(ctx);
    enter(Where.FnPattern);
    var pats = clause.patterns.map(pa -> pa.descent(pat -> resolvePattern(pat, mCtx)));
    exit();
    enter(Where.FnBody);
    var body = clause.expr.map(x -> x.descent(enter(mCtx.get())));
    exit();
    return clause.update(pats, body);
  }

  public @NotNull WithPos resolvePattern(@NotNull WithPos pattern, MutableValue ctx) {
    var resolver = new PatternResolver(ctx.get(), this::addReference);
    var result = pattern.descent(resolver);
    ctx.set(resolver.context());
    return result;
  }

  private static Context bindAs(@NotNull LocalVar as, @NotNull Context ctx) { return ctx.bind(as); }

  @Contract(mutates = "param2")
  public @NotNull Expr.Param bind(@NotNull Expr.Param param, @NotNull MutableValue ctx) {
    var p = param.descent(enter(ctx.get()));
    ctx.set(ctx.get().bind(param.ref()));
    return p;
  }

  public @NotNull ImmutableSeq
  bind(@NotNull ImmutableSeq binds, @NotNull MutableValue ctx) {
    return binds.map(bind -> {
      var b = bind.descent(enter(ctx.get()));
      ctx.set(ctx.get().bind(bind.var()));
      return b;
    });
  }

  public @NotNull AnyVar resolve(@NotNull QualifiedID name) {
    var result = ctx.get(name);
    if (result instanceof GeneralizedVar gvar) {
      var gened = allowedGeneralizes.getOrNull(gvar);
      if (gened != null) return gened.ref();
    }

    return result;
  }

  public @NotNull ExprResolver member(@NotNull TyckUnit decl, Where initial) {
    var resolver = new ExprResolver(ctx, false, allowedGeneralizes,
      MutableList.of(new TyckOrder.Head(decl)),
      MutableStack.create());
    resolver.enter(initial);
    return resolver;
  }

  public enum Where {
    // Data head & Fn head
    Head,
    // Con patterns
    ConPattern,
    // Functions with just a body
    FnSimple,
    // Fn patterns
    FnPattern,
    // Body of non-simple functions
    FnBody
  }
  public record Options(boolean allowIntroduceGeneralized) { }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy