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

org.aya.resolve.context.Context 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.context;

import kala.collection.SeqLike;
import kala.collection.immutable.ImmutableSeq;
import kala.collection.mutable.MutableList;
import org.aya.generic.InterruptException;
import org.aya.resolve.error.NameProblem;
import org.aya.syntax.concrete.stmt.ModuleName;
import org.aya.syntax.concrete.stmt.QualifiedID;
import org.aya.syntax.ref.AnyVar;
import org.aya.syntax.ref.GenerateKind;
import org.aya.syntax.ref.LocalVar;
import org.aya.syntax.ref.ModulePath;
import org.aya.tyck.tycker.Problematic;
import org.aya.util.error.SourcePos;
import org.aya.util.reporter.Problem;
import org.aya.util.reporter.Reporter;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.nio.file.Path;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * @author re-xyr
 */
public interface Context extends Problematic {
  @Nullable Context parent();
  @NotNull Reporter reporter();
  @NotNull Path underlyingFile();

  default  @Nullable T iterate(@NotNull Function<@NotNull Context, @Nullable T> f) {
    var p = this;
    while (p != null) {
      var result = f.apply(p);
      if (result != null) return result;
      p = p.parent();
    }
    return null;
  }

  /**
   * The path of this module
   */
  default @NotNull ModulePath modulePath() {
    var p = parent();
    assert p != null;
    return p.modulePath();
  }

  @Contract("_ -> fail") default  @NotNull T reportAndThrow(@NotNull Problem problem) {
    fail(problem);
    throw new ResolvingInterruptedException();
  }

  @Contract("_ -> fail") default  @NotNull T reportAllAndThrow(@NotNull SeqLike problems) {
    reportAll(problems);
    throw new ResolvingInterruptedException();
  }

  default void reportAll(@NotNull SeqLike problems) {
    problems.forEach(this::fail);
  }

  /**
   * Getting a symbol by name {@param name}.
   *
   * @param name an id which probably unqualified
   */
  default @NotNull AnyVar get(@NotNull QualifiedID name) {
    return switch (name.component()) {
      case ModuleName.ThisRef _ -> getUnqualified(name.name(), name.sourcePos());
      case ModuleName.Qualified qualified -> getQualified(qualified, name.name(), name.sourcePos());
    };
  }

  /**
   * @see Context#get(QualifiedID)
   */
  default @Nullable AnyVar getMaybe(@NotNull QualifiedID name) {
    return switch (name.component()) {
      case ModuleName.ThisRef _ -> getUnqualifiedMaybe(name.name(), name.sourcePos());
      case ModuleName.Qualified qualified -> getQualifiedMaybe(qualified, name.name(), name.sourcePos());
    };
  }

  default MutableList collect(@NotNull MutableList container) {
    return container;
  }

  /**
   * Trying to get a symbol by unqualified name {@param name} in {@code this} context.
   */
  @Nullable AnyVar getUnqualifiedLocalMaybe(@NotNull String name, @NotNull SourcePos sourcePos);

  /**
   * Trying to get a symbol which can referred by unqualified name {@param name} in the whole context.
   *
   * @param name      the unqualified name
   * @param sourcePos the source pos for error reporting
   * @return null if not found
   * @see Context#getUnqualifiedLocalMaybe(String, SourcePos)
   */
  default @Nullable AnyVar getUnqualifiedMaybe(@NotNull String name, @NotNull SourcePos sourcePos) {
    return iterate(c -> c.getUnqualifiedLocalMaybe(name, sourcePos));
  }

  /**
   * @see Context#getUnqualified(String, SourcePos)
   */
  default @NotNull AnyVar getUnqualified(@NotNull String name, @NotNull SourcePos sourcePos) {
    var result = getUnqualifiedMaybe(name, sourcePos);
    if (result == null) reportAndThrow(new NameProblem.UnqualifiedNameNotFoundError(this, name, sourcePos));
    return result;
  }

  /**
   * Trying to get a symbol by qualified id {@code {modName}::{name}} in {@code this} context
   *
   * @return a symbol in component {@param modName}, even it is {@link ModuleName#This}; null if not found
   */
  @Nullable AnyVar getQualifiedLocalMaybe(
    @NotNull ModuleName.Qualified modName,
    @NotNull String name,
    @NotNull SourcePos sourcePos
  );

  /**
   * Trying to get a symbol by qualified id {@code {modName}::{name}} in the whole context with {@param accessibility}.
   *
   * @see Context#getQualifiedLocalMaybe(ModuleName.Qualified, String, SourcePos)
   */
  default @Nullable AnyVar getQualifiedMaybe(
    @NotNull ModuleName.Qualified modName,
    @NotNull String name,
    @NotNull SourcePos sourcePos
  ) {
    return iterate(c -> c.getQualifiedLocalMaybe(modName, name, sourcePos));
  }

  /**
   * @see Context#getQualifiedMaybe(ModuleName.Qualified, String, SourcePos)
   */
  default @NotNull AnyVar getQualified(
    @NotNull ModuleName.Qualified modName,
    @NotNull String name,
    @NotNull SourcePos sourcePos
  ) {
    var result = getQualifiedMaybe(modName, name, sourcePos);
    if (result == null)
      reportAndThrow(new NameProblem.QualifiedNameNotFoundError(modName, name, sourcePos));
    return result;
  }

  /**
   * Trying to get a {@link ModuleExport} by a module {@param modName} in {@code this} context.
   *
   * @param modName qualified module name
   * @return a ModuleExport of that module; null if no such module.
   */
  @Nullable ModuleExport getModuleLocalMaybe(@NotNull ModuleName.Qualified modName);

  /**
   * Trying to get a {@link ModuleExport} by a module {@param modName} in the whole context.
   *
   * @param modName qualified module name
   * @return a ModuleExport of that module; null if no such module.
   */
  default @Nullable ModuleExport getModuleMaybe(@NotNull ModuleName.Qualified modName) {
    return iterate(c -> c.getModuleLocalMaybe(modName));
  }

  default @NotNull Context bind(
    @NotNull LocalVar ref,
    @NotNull Predicate<@Nullable AnyVar> toWarn
  ) {
    return bind(ref.name(), ref, toWarn);
  }

  default @NotNull Context bind(@NotNull LocalVar ref) {
    return bind(ref, var -> var instanceof LocalVar);
  }

  default @NotNull Context bind(
    @NotNull String name, @NotNull LocalVar ref,
    @NotNull Predicate<@Nullable AnyVar> toWarn
  ) {
    // do not bind ignored var, and users should not try to use it
    if (ref == LocalVar.IGNORED) return this;
    var exists = getUnqualifiedMaybe(name, ref.definition());
    if (toWarn.test(exists) && (!(ref.generateKind() == GenerateKind.Basic.Anonymous))) {
      fail(new NameProblem.ShadowingWarn(name, ref.definition()));
    }
    return new BindContext(this, name, ref);
  }

  default @NotNull PhysicalModuleContext derive(@NotNull String extraName) {
    return derive(new ModulePath(ImmutableSeq.of(extraName)));
  }

  default @NotNull PhysicalModuleContext derive(@NotNull ModulePath extraName) {
    return new PhysicalModuleContext(this, this.modulePath().derive(extraName));
  }

  class ResolvingInterruptedException extends InterruptException {
    @Override public InterruptStage stage() {
      return InterruptStage.Resolving;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy