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

org.aya.primitive.PrimFactory Maven / Gradle / Ivy

The 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.primitive;

import kala.collection.Map;
import kala.collection.immutable.ImmutableMap;
import kala.collection.immutable.ImmutableSeq;
import kala.control.Option;
import kala.tuple.Tuple;
import org.aya.normalize.Normalizer;
import org.aya.syntax.compile.JitPrim;
import org.aya.syntax.concrete.stmt.decl.PrimDecl;
import org.aya.syntax.core.Closure;
import org.aya.syntax.core.def.PrimDef;
import org.aya.syntax.core.def.PrimDefLike;
import org.aya.syntax.core.term.*;
import org.aya.syntax.core.term.call.PrimCall;
import org.aya.syntax.core.term.repr.StringTerm;
import org.aya.syntax.core.term.xtt.CoeTerm;
import org.aya.syntax.core.term.xtt.DimTerm;
import org.aya.syntax.core.term.xtt.DimTyTerm;
import org.aya.syntax.core.term.xtt.EqTerm;
import org.aya.syntax.ref.DefVar;
import org.aya.syntax.ref.LocalVar;
import org.aya.tyck.TyckState;
import org.aya.util.ForLSP;
import org.jetbrains.annotations.NotNull;

import java.util.EnumMap;
import java.util.function.BiFunction;
import java.util.function.Function;

import static org.aya.syntax.core.def.PrimDef.*;
import static org.aya.syntax.core.term.SortTerm.Type0;

public class PrimFactory {
  private final @NotNull Map<@NotNull ID, @NotNull PrimSeed> seeds;
  private final @NotNull EnumMap<@NotNull ID, @NotNull PrimDefLike> defs = new EnumMap<>(ID.class);

  public PrimFactory() {
    seeds = ImmutableMap.from(ImmutableSeq.of(
      stringType,
      stringConcat,
      intervalType,
      pathType,
      coe
    ).map(seed -> Tuple.of(seed.name, seed)));
  }

  public void definePrim(PrimDefLike prim) {
    assert !isForbiddenRedefinition(prim.id(), prim instanceof JitPrim);
    defs.put(prim.id(), prim);
  }

  @FunctionalInterface
  public interface Unfolder extends BiFunction<@NotNull PrimCall, @NotNull TyckState, @NotNull Term> { }

  public record PrimSeed(
    @NotNull ID name,
    @NotNull Unfolder unfold,
    @NotNull Function<@NotNull DefVar, @NotNull PrimDef> supplier,
    @NotNull ImmutableSeq<@NotNull ID> dependency
  ) {
    public @NotNull PrimDef supply(@NotNull DefVar ref) {
      return supplier.apply(ref);
    }
  }

  final @NotNull PrimSeed coe = new PrimSeed(ID.COE, (prim, _) -> {
    var args = prim.args();
    return new CoeTerm(closureParam(args.get(2)), args.get(0), args.get(1));
  }, ref -> {
    // coe (r s : I) (A : I -> Type) : A r -> A s
    var telescope = ImmutableSeq.of(
      DimTyTerm.param("r"),
      DimTyTerm.param("s"),
      new Param("A", intervalToType, true));
    var r = LocalVar.generate("r");
    var s = LocalVar.generate("s");
    var A = LocalVar.generate("A");
    // Eta-expanded A
    var closureA = new Closure.Locns(new AppTerm(new FreeTerm(A), new LocalTerm(0)));
    var result = familyI2J(closureA, new FreeTerm(r), new FreeTerm(s))
      .bindTele(ImmutableSeq.of(r, s, A).view());

    return new PrimDef(ref, telescope, result, ID.COE);
  }, ImmutableSeq.of(ID.I));

  final @NotNull PrimSeed pathType = new PrimSeed(ID.PATH, (prim, _) -> {
    var args = prim.args();
    return new EqTerm(closureParam(args.get(0)), args.get(1), args.get(2));
  }, ref -> {
    // (A : I -> Type) (a : A 0) (b : A 1) : Type
    var paramA = new Param("A", intervalToType, true);
    var paramLeft = new Param("a", AppTerm.make(new LocalTerm(0), DimTerm.I0), true);
    var paramRight = new Param("b", AppTerm.make(new LocalTerm(1), DimTerm.I1), true);
    return new PrimDef(ref, ImmutableSeq.of(paramA, paramLeft, paramRight), Type0, ID.PATH);
  }, ImmutableSeq.of(ID.I));

  private static @NotNull Closure closureParam(@NotNull Term term) {
    var var = LocalVar.generate("disappearing");
    return AppTerm.make(term, new FreeTerm(var)).bind(var);
  }

  final @NotNull PrimSeed stringType =
    new PrimSeed(ID.STRING, (prim, _) -> prim,
      ref -> new PrimDef(ref, Type0, ID.STRING), ImmutableSeq.empty());

  final @NotNull PrimSeed stringConcat =
    new PrimSeed(ID.STRCONCAT, PrimFactory::concat, ref -> new PrimDef(
      ref,
      ImmutableSeq.of(
        new Param("str1", getCall(ID.STRING), true),
        new Param("str2", getCall(ID.STRING), true)
      ),
      getCall(ID.STRING),
      ID.STRCONCAT
    ), ImmutableSeq.of(ID.STRING));

  private static @NotNull Term concat(@NotNull PrimCall prim, @NotNull TyckState state) {
    var norm = new Normalizer(state);
    var first = norm.apply(prim.args().get(0));
    var second = norm.apply(prim.args().get(1));

    if (first instanceof StringTerm(var str1) && second instanceof StringTerm(var str2)) {
      return new StringTerm(str1 + str2);
    }

    return new PrimCall(prim.ref(), prim.ulift(), ImmutableSeq.of(first, second));
  }

  /*
  private final @NotNull PrimSeed hcomp = new PrimSeed(ID.HCOMP, this::hcomp, ref -> {
    var varA = new LocalVar("A");
    var paramA = new Term.Param(varA, Type0, false);
    var restr = IntervalTerm.paramImplicit("phi");
    var varU = new LocalVar("u");
    var paramFuncU = new Term.Param(varU,
      new PiTerm(
        IntervalTerm.param(LocalVar.IGNORED),
        new PartialTyTerm(new RefTerm(varA), AyaRestrSimplifier.INSTANCE.isOne(restr.toTerm()))),
      true);
    var varU0 = new LocalVar("u0");
    var paramU0 = new Term.Param(varU0, new RefTerm(varA), true);
    var result = new RefTerm(varA);
    return new PrimDef(
      ref,
      ImmutableSeq.of(paramA, restr, paramFuncU, paramU0),
      result,
      ID.HCOMP
    );
  }, ImmutableSeq.of(ID.I));

  private @NotNull Term hcomp(@NotNull PrimCall prim, @NotNull TyckState state) {
    var A = prim.args().get(0);
    var phi = prim.args().get(1);
    var u = prim.args().get(2);
    var u0 = prim.args().get(3);
    return new HCompTerm(A, AyaRestrSimplifier.INSTANCE.isOne(phi), u, u0);
  }
  */

  public final @NotNull PrimSeed intervalType = new PrimSeed(ID.I,
    ((_, _) -> DimTyTerm.INSTANCE),
    ref -> new PrimDef(ref, SortTerm.ISet, ID.I),
    ImmutableSeq.empty());

  public @NotNull PrimDefLike factory(@NotNull ID name, @NotNull DefVar ref) {
    var rst = new PrimDef.Delegate(seeds.get(name).supply(ref).ref);
    definePrim(rst);
    return rst;
  }

  public @NotNull PrimCall getCall(@NotNull ID id, @NotNull ImmutableSeq args) {
    return new PrimCall(getOption(id).get(), 0, args);
  }

  public @NotNull PrimCall getCall(@NotNull ID id) {
    return new PrimCall(getOption(id).get());
  }

  public @NotNull Option getOption(@NotNull ID name) {
    return Option.ofNullable(defs.get(name));
  }

  public boolean have(@NotNull ID name) {
    return defs.containsKey(name);
  }

  /**
   * Whether this definition is a redefinition that should be treated as error.
   * There are two cases where a redefinition is allowed:
   * 
    *
  • When we are working in an LSP, and users can reload a file to redefine things.
  • *
  • When we are serializing a file, which we will deserialize immediately, and this will * replace the existing PrimDefs with their JIT-compiled version.
  • *
* * @return true if redefinition is forbidden. */ @ForLSP public boolean isForbiddenRedefinition(@NotNull PrimDef.ID id, boolean isJit) { if (isJit) return have(id) && defs.get(id) instanceof JitPrim; else return have(id); } public @NotNull Option> checkDependency(@NotNull ID name) { return seeds.getOption(name).map(seed -> seed.dependency().filterNot(this::have)); } public @NotNull Term unfold(@NotNull PrimCall primCall, @NotNull TyckState state) { var id = primCall.ref().id(); return seeds.get(id).unfold.apply(primCall, state); } public void clear() { defs.clear(); } public void clear(@NotNull ID name) { defs.remove(name); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy