Last active
August 21, 2022 14:16
-
-
Save ice1000/a20bf0ffec2a91ac36c443d0ae000ff8 to your computer and use it in GitHub Desktop.
Cubical code in guest0x0
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/base/src/main/java/org/aya/guest0x0/syntax/BdryData.java b/base/src/main/java/org/aya/guest0x0/syntax/BdryData.java | |
new file mode 100644 | |
index 0000000..8ef920c | |
--- /dev/null | |
+++ b/base/src/main/java/org/aya/guest0x0/syntax/BdryData.java | |
@@ -0,0 +1,32 @@ | |
+package org.aya.guest0x0.syntax; | |
+ | |
+import kala.collection.immutable.ImmutableSeq; | |
+import kala.collection.mutable.MutableList; | |
+import org.aya.guest0x0.cubical.Restr; | |
+import org.aya.guest0x0.util.LocalVar; | |
+import org.aya.pretty.doc.Doc; | |
+import org.aya.pretty.doc.Docile; | |
+import org.jetbrains.annotations.NotNull; | |
+ | |
+import java.util.function.Function; | |
+ | |
+public record BdryData<E extends Docile & Restr.TermLike<E>>( | |
+ @NotNull ImmutableSeq<LocalVar> dims, | |
+ @NotNull E type, | |
+ @NotNull ImmutableSeq<Restr.Side<E>> boundaries | |
+) implements Docile { | |
+ public @NotNull BdryData<E> fmap(@NotNull Function<E, E> f, @NotNull ImmutableSeq<LocalVar> newDims) { | |
+ return new BdryData<>(newDims, f.apply(type), boundaries.map(b -> b.rename(f))); | |
+ } | |
+ | |
+ public @NotNull BdryData<E> fmap(@NotNull Function<E, E> f) { | |
+ return fmap(f, dims); | |
+ } | |
+ | |
+ @Override public @NotNull Doc toDoc() { | |
+ var head = MutableList.of(Doc.symbol("[|")); | |
+ dims.forEach(d -> head.append(Doc.plain(d.name()))); | |
+ head.appendAll(new Doc[]{Doc.symbol("|]"), type.toDoc()}); | |
+ return Doc.cblock(Doc.sep(head), 2, Doc.vcat(boundaries.map(Restr.Side::toDoc))); | |
+ } | |
+} | |
diff --git a/base/src/main/java/org/aya/guest0x0/syntax/Expr.java b/base/src/main/java/org/aya/guest0x0/syntax/Expr.java | |
index 44a6cb1..68f9e43 100644 | |
--- a/base/src/main/java/org/aya/guest0x0/syntax/Expr.java | |
+++ b/base/src/main/java/org/aya/guest0x0/syntax/Expr.java | |
@@ -2,6 +2,7 @@ package org.aya.guest0x0.syntax; | |
import kala.collection.immutable.ImmutableSeq; | |
import kala.collection.mutable.MutableList; | |
+import org.aya.guest0x0.cubical.Formula; | |
import org.aya.guest0x0.cubical.Restr; | |
import org.aya.guest0x0.util.Distiller; | |
import org.aya.guest0x0.util.LocalVar; | |
@@ -36,4 +37,18 @@ public sealed interface Expr extends Docile, Restr.TermLike<Expr> { | |
record Hole(@Override @NotNull SourcePos pos, ImmutableSeq<LocalVar> accessible) implements Expr {} | |
/** @param isPi it's a sigma if false */ | |
record DT(boolean isPi, @Override @NotNull SourcePos pos, Param<Expr> param, Expr cod) implements Expr {} | |
+ record Path(@Override @NotNull SourcePos pos, @NotNull BdryData<Expr> data) implements Expr {} | |
+ record Mula(@Override @NotNull SourcePos pos, @Override @NotNull Formula<Expr> asFormula) implements Expr {} | |
+ record Transp(@Override @NotNull SourcePos pos, @NotNull Expr cover, @NotNull Expr restr) implements Expr {} | |
+ record Cof(@Override @NotNull SourcePos pos, @NotNull Restr<Expr> data) implements Expr {} | |
+ record PartEl(@Override @NotNull SourcePos pos, @NotNull ImmutableSeq<Restr.Side<Expr>> clauses) implements Expr {} | |
+ record PartTy(@Override @NotNull SourcePos pos, @NotNull Expr ty, @NotNull Expr restr) implements Expr {} | |
+ /** "Proper" cubical subtypes */ | |
+ record Sub(@Override @NotNull SourcePos pos, @NotNull Expr ty, @NotNull PartEl par) implements Expr {} | |
+ /** | |
+ * inS/outS, the introduction/elimination rules for the subtype relation | |
+ * | |
+ * @param isIntro true if inS | |
+ */ | |
+ record SubEl(@Override @NotNull SourcePos pos, @NotNull Expr e, boolean isIntro) implements Expr {} | |
} | |
diff --git a/base/src/main/java/org/aya/guest0x0/syntax/Keyword.java b/base/src/main/java/org/aya/guest0x0/syntax/Keyword.java | |
index 17f99f0..5e431eb 100644 | |
--- a/base/src/main/java/org/aya/guest0x0/syntax/Keyword.java | |
+++ b/base/src/main/java/org/aya/guest0x0/syntax/Keyword.java | |
@@ -1,5 +1,5 @@ | |
package org.aya.guest0x0.syntax; | |
public enum Keyword { | |
- U | |
+ U, I, F | |
} | |
diff --git a/base/src/main/java/org/aya/guest0x0/syntax/Term.java b/base/src/main/java/org/aya/guest0x0/syntax/Term.java | |
index fad3f41..71eb5df 100644 | |
--- a/base/src/main/java/org/aya/guest0x0/syntax/Term.java | |
+++ b/base/src/main/java/org/aya/guest0x0/syntax/Term.java | |
@@ -4,6 +4,7 @@ import kala.collection.SeqView; | |
import kala.collection.immutable.ImmutableSeq; | |
import kala.collection.mutable.MutableList; | |
import kala.collection.mutable.MutableMap; | |
+import org.aya.guest0x0.cubical.Formula; | |
import org.aya.guest0x0.cubical.Restr; | |
import org.aya.guest0x0.tyck.Normalizer; | |
import org.aya.guest0x0.util.Distiller; | |
@@ -76,6 +77,24 @@ public sealed interface Term extends Docile, Restr.TermLike<Term> { | |
static @NotNull Term mkPi(@NotNull Term dom, @NotNull Term cod) { | |
return new Term.DT(true, new Param<>(new LocalVar("_"), dom), cod); | |
} | |
- @NotNull Term U = new UI(Keyword.U); | |
+ @NotNull Term U = new UI(Keyword.U), I = new UI(Keyword.I), F = new UI(Keyword.F); | |
record UI(@NotNull Keyword keyword) implements Term {} | |
+ record Path(@NotNull BdryData<Term> data) implements Term {} | |
+ record PLam(@NotNull ImmutableSeq<LocalVar> dims, @NotNull Term fill) implements Term {} | |
+ record PCall(@NotNull Term p, @NotNull ImmutableSeq<Term> i, @NotNull PartEl b) implements Term {} | |
+ record Mula(@Override @NotNull Formula<Term> asFormula) implements Term {} | |
+ static @NotNull Term end(boolean isLeft) {return new Mula(new Formula.Lit<>(isLeft));} | |
+ static @NotNull Term neg(@NotNull Term term) {return new Mula(new Formula.Inv<>(term));} | |
+ static @NotNull Term conn(boolean isAnd, @NotNull Term l, @NotNull Term r) {return new Mula(new Formula.Conn<>(isAnd, l, r));} | |
+ static @NotNull Term and(@NotNull Term l, @NotNull Term r) {return conn(true, l, r);} | |
+ static @NotNull Term or(@NotNull Term l, @NotNull Term r) {return conn(false, l, r);} | |
+ record Transp(@NotNull Term cover, @NotNull Cof restr) implements Term {} | |
+ record Cof(@NotNull Restr<Term> restr) implements Term { | |
+ public @NotNull Cof fmap(@NotNull Function<Term, Term> f) { | |
+ return new Cof(restr.fmap(f)); | |
+ } | |
+ } | |
+ record PartTy(@NotNull Term ty, @NotNull Term restr) implements Term {} | |
+ record PartEl(@NotNull ImmutableSeq<Restr.Side<Term>> clauses) implements Term {} | |
+ record Sub(@NotNull Term ty, @NotNull PartEl par) implements Term {} | |
} | |
diff --git a/base/src/main/java/org/aya/guest0x0/tyck/Elaborator.java b/base/src/main/java/org/aya/guest0x0/tyck/Elaborator.java | |
index b187aae..5b4e12b 100644 | |
--- a/base/src/main/java/org/aya/guest0x0/tyck/Elaborator.java | |
+++ b/base/src/main/java/org/aya/guest0x0/tyck/Elaborator.java | |
@@ -5,10 +5,16 @@ import kala.collection.immutable.ImmutableSeq; | |
import kala.collection.mutable.MutableArrayList; | |
import kala.collection.mutable.MutableList; | |
import kala.collection.mutable.MutableMap; | |
+import kala.control.Option; | |
import kala.tuple.Tuple; | |
+import org.aya.guest0x0.cubical.CofThy; | |
+import org.aya.guest0x0.cubical.Formula; | |
+import org.aya.guest0x0.cubical.Restr; | |
+import org.aya.guest0x0.syntax.BdryData; | |
import org.aya.guest0x0.syntax.Def; | |
import org.aya.guest0x0.syntax.Expr; | |
import org.aya.guest0x0.syntax.Term; | |
+import org.aya.guest0x0.util.AltF7; | |
import org.aya.guest0x0.util.LocalVar; | |
import org.aya.guest0x0.util.Param; | |
import org.aya.guest0x0.util.SPE; | |
@@ -36,12 +42,32 @@ public record Elaborator( | |
public Term inherit(Expr expr, Term type) { | |
return switch (expr) { | |
- case Expr.Lam lam -> { | |
- if (normalize(type) instanceof Term.DT dt && dt.isPi()) | |
- yield new Term.Lam(lam.x(), hof(lam.x(), dt.param().type(), () -> | |
- inherit(lam.a(), dt.codomain(new Term.Ref(lam.x()))))); | |
- else throw new SPE(lam.pos(), | |
+ case Expr.Lam lam -> switch (normalize(type)) { | |
+ case Term.DT dt && dt.isPi() -> new Term.Lam(lam.x(), | |
+ hof(lam.x(), dt.param().type(), () -> inherit(lam.a(), dt.codomain(new Term.Ref(lam.x()))))); | |
+ // Overloaded lambda | |
+ case Term.Path path -> { | |
+ var tyDims = path.data().dims(); | |
+ var lamDims = MutableArrayList.<LocalVar>create(tyDims.size()); | |
+ var unlam = Expr.unlam(lamDims, tyDims.size(), lam); | |
+ if (unlam == null) throw new SPE(lam.pos(), Doc.english("Expected path lambda")); | |
+ yield boundaries(lamDims, () -> inherit(unlam, | |
+ normalizer(lamDims, tyDims).term(path.data().type()) | |
+ ), unlam.pos(), path.data(), normalizer(tyDims, lamDims)); | |
+ } | |
+ default -> throw new SPE(lam.pos(), | |
Doc.english("Expects a right adjoint for"), expr, Doc.plain("got"), type); | |
+ }; | |
+ case Expr.PartEl el -> { | |
+ if (!(normalize(type) instanceof Term.PartTy par)) throw new SPE(el.pos(), | |
+ Doc.english("Expects partial type for partial elements"), expr, Doc.plain("got"), type); | |
+ var cof = cof(par.restr(), el.pos()); | |
+ var clauses = elaborateClauses(el, el.clauses(), par.ty()); | |
+ var face = new Restr.Vary<>(clauses.map(Restr.Side::cof)); | |
+ if (!CofThy.conv(cof.restr(), Normalizer.create(), norm -> CofThy.satisfied(norm.restr(face)))) | |
+ throw new SPE(el.pos(), Doc.english("The faces in the partial element"), face, | |
+ Doc.english("must cover the face(s) specified in type:"), cof); | |
+ yield new Term.PartEl(clauses); | |
} | |
case Expr.Two two && !two.isApp() -> { | |
if (!(normalize(type) instanceof Term.DT dt) || dt.isPi()) throw new SPE(two.pos(), | |
@@ -49,6 +75,17 @@ public record Elaborator( | |
var lhs = inherit(two.f(), dt.param().type()); | |
yield new Term.Two(false, lhs, inherit(two.a(), dt.codomain(lhs))); | |
} | |
+ case Expr.SubEl inS && inS.isIntro() -> { | |
+ if (!(normalize(type) instanceof Term.Sub sub)) throw new SPE(inS.pos(), | |
+ Doc.english("Expects cubical subtype, got"), type); | |
+ var arg = inherit(inS.e(), sub.ty()); | |
+ boundaries(inS.pos(), normalizer(), arg, sub.par().clauses()); | |
+ // yield new Term.SubEl(arg, true); | |
+ throw new UnsupportedOperationException("TODO"); | |
+ } | |
+ case Expr.SubEl outS /*&& !outS.isIntro()*/ -> { | |
+ throw new UnsupportedOperationException("TODO"); | |
+ } | |
case Expr.Hole hole -> { | |
var docs = MutableList.<Doc>create(); | |
gamma.forEach((k, v) -> { | |
@@ -69,8 +106,22 @@ public record Elaborator( | |
} | |
default -> { | |
var synth = synth(expr); | |
- unify(normalize(type), synth.wellTyped, synth.type, expr.pos()); | |
- yield synth.wellTyped; | |
+ yield switch (normalize(type)) { | |
+ case Term.Path path -> { | |
+ var exTyDims = path.data().dims(); | |
+ var lamDims = MutableArrayList.<LocalVar>create(exTyDims.size()); | |
+ var unlam = Term.unlam(lamDims, synth.wellTyped, exTyDims.size()); | |
+ var acTyDims = MutableArrayList.<LocalVar>create(exTyDims.size()); | |
+ var unpi = Term.unpi(acTyDims, synth.type, exTyDims.size()); | |
+ if (unlam == null || unpi == null) throw new SPE(expr.pos(), Doc.english("Expected (path) lambda")); | |
+ unify(unpi, unlam, normalizer(exTyDims, acTyDims).term(path.data().type()), expr.pos()); | |
+ yield boundaries(lamDims, () -> unlam, expr.pos(), path.data(), normalizer(exTyDims, lamDims)); | |
+ } | |
+ case Term ty -> { | |
+ unify(ty, synth.wellTyped, synth.type, expr.pos()); | |
+ yield synth.wellTyped; | |
+ } | |
+ }; | |
} | |
}; | |
} | |
@@ -81,6 +132,27 @@ public record Elaborator( | |
)); | |
} | |
+ private ImmutableSeq<Restr.Side<Term>> elaborateClauses( | |
+ Expr on, @NotNull ImmutableSeq<Restr.Side<Expr>> clauses, @NotNull Term ty | |
+ ) { | |
+ var sides = clauses.flatMap(cl -> clause(on.pos(), cl, ty)); | |
+ confluence(on, sides, on.pos()); | |
+ return sides; | |
+ } | |
+ | |
+ private void confluence(Docile on, ImmutableSeq<Restr.Side<Term>> clauses, @NotNull SourcePos pos) { | |
+ for (int i = 1; i < clauses.size(); i++) { | |
+ var lhs = clauses.get(i); | |
+ for (int j = 0; j < i; j++) { | |
+ var rhs = clauses.get(j); | |
+ CofThy.conv(lhs.cof().and(rhs.cof()), normalizer(), norm -> { | |
+ unify(norm.term(lhs.u()), on, norm.term(rhs.u()), pos, Doc.english("Boundaries disagree.")); | |
+ return true; | |
+ }); | |
+ } | |
+ } | |
+ } | |
+ | |
private void unify(Term ty, Docile on, @NotNull Term actual, SourcePos pos, Doc prefix) { | |
unify(ty, actual, pos, u -> Doc.vcat(prefix, unifyDoc(ty, on, actual, u))); | |
} | |
@@ -105,6 +177,35 @@ public record Elaborator( | |
return line1; | |
} | |
+ /** | |
+ * @param subst Must be purely variable-to-variable | |
+ * @param coreSupplier Already substituted with the variables in the type | |
+ * @param lamDims Bindings in the type | |
+ */ | |
+ private @NotNull Term boundaries( | |
+ @NotNull MutableList<LocalVar> lamDims, | |
+ @NotNull Supplier<Term> coreSupplier, | |
+ SourcePos pos, BdryData<Term> data, | |
+ Normalizer subst | |
+ ) { | |
+ lamDims.forEach(t -> gamma.put(t, Term.I)); | |
+ var core = coreSupplier.get(); | |
+ boundaries(pos, subst, core, data.boundaries()); | |
+ lamDims.forEach(gamma::remove); | |
+ return new Term.PLam(lamDims.toImmutableArray(), core); | |
+ } | |
+ | |
+ private void boundaries(SourcePos pos, Normalizer subst, Term core, ImmutableSeq<Restr.Side<Term>> boundaries) { | |
+ for (var boundary : boundaries) { | |
+ // Based on the very assumption as in the function's javadoc | |
+ CofThy.conv(boundary.cof().fmap(subst::term), subst, norm -> { | |
+ unify(norm.term(boundary.u()), boundary, norm.term(core), pos, | |
+ Doc.english("Boundary mismatch, oh no.")); | |
+ return true; | |
+ }); | |
+ } | |
+ } | |
+ | |
public Synth synth(Expr expr) { | |
var synth = switch (expr) { | |
case Expr.PrimTy u -> new Synth(new Term.UI(u.keyword()), Term.U); | |
@@ -146,10 +247,82 @@ public record Elaborator( | |
var cod = hof(x, param.wellTyped, () -> synth(dt.cod())); | |
yield new Synth(new Term.DT(dt.isPi(), new Param<>(x, param.wellTyped), cod.wellTyped), cod.type); | |
} | |
+ case Expr.Sub sub -> { | |
+ var ty = inherit(sub.ty(), Term.U); | |
+ var clauses = elaborateClauses(sub, sub.par().clauses(), ty); | |
+ yield new Synth(new Term.Sub(ty, new Term.PartEl(clauses)), ty); | |
+ } | |
+ case Expr.Path path -> { | |
+ var dims = path.data().dims(); | |
+ for (var dim : dims) gamma.put(dim, Term.I); | |
+ var ty = inherit(path.data().type(), Term.U); | |
+ var boundaries = elaborateClauses(expr, path.data().boundaries(), ty); | |
+ var data = new BdryData<>(dims, ty, boundaries); | |
+ for (var dim : dims) gamma.remove(dim); | |
+ yield new Synth(new Term.Path(data), Term.U); | |
+ } | |
+ case Expr.Mula f -> switch (f.asFormula()) { | |
+ case Formula.Inv<Expr> inv -> new Synth(Term.neg(inherit(inv.i(), Term.I)), Term.I); | |
+ case Formula.Conn<Expr> conn -> new Synth(new Term.Mula( | |
+ new Formula.Conn<>(conn.isAnd(), inherit(conn.l(), Term.I), inherit(conn.r(), Term.I))), Term.I); | |
+ case Formula.Lit<Expr> lit -> new Synth(Term.end(lit.isLeft()), Term.I); | |
+ }; | |
+ case Expr.Cof cof -> new Synth(new Term.Cof(cof.data().mapCond(this::condition)), Term.F); | |
+ case Expr.PartTy par -> new Synth(new Term.PartTy(inherit(par.ty(), Term.U), cof(par.restr())), Term.U); | |
+ case Expr.Transp transp -> { | |
+ var cover = inherit(transp.cover(), Term.mkPi(Term.I, Term.U)); | |
+ var detective = new AltF7(new LocalVar("?")); | |
+ var sample = cover.app(new Term.Ref(detective.var())); | |
+ var ty = Term.mkPi(cover.app(Term.end(true)), cover.app(Term.end(false))); | |
+ var cof = cof(transp.restr()); | |
+ // I believe find-usages is slightly more efficient than what Huber wrote in hcomp.pdf | |
+ var capture = new Object() { | |
+ Term under = sample; | |
+ }; | |
+ // I want it to have no references, so !detective.press(), and if it returns true, it means no problem | |
+ // So if it returns false then we're in trouble | |
+ if (!CofThy.conv(cof.restr(), normalizer(), n -> !detective.press(capture.under = n.term(sample)))) | |
+ throw new SPE(transp.pos(), Doc.english("The cover"), cover, | |
+ Doc.english("has to be constant under the cofibration"), cof, | |
+ Doc.english("but applying a variable `?` to it results in"), capture.under, | |
+ Doc.english("which contains a reference to `?`, oh no")); | |
+ yield new Synth(new Term.Transp(cover, cof), ty); | |
+ } | |
default -> throw new SPE(expr.pos(), Doc.english("Synthesis failed for"), expr); | |
}; | |
var type = normalize(synth.type); | |
- return new Synth(synth.wellTyped, type); | |
+ if (type instanceof Term.Path path) { | |
+ var dims = path.data().dims(); | |
+ return new Synth(Normalizer.rename(Term.mkLam(dims.view(), | |
+ new Term.PCall(synth.wellTyped, dims.map(Term.Ref::new), new Term.PartEl(path.data().boundaries())))), | |
+ Term.mkPi(dims.map(x -> new Param<>(x, Term.I)), path.data().type())); | |
+ } else return new Synth(synth.wellTyped, type); | |
+ } | |
+ | |
+ private @NotNull Restr.Cond<Term> condition(Restr.Cond<Expr> c) { | |
+ return new Restr.Cond<>(inherit(c.inst(), Term.I), c.isLeft()); | |
+ } | |
+ | |
+ private @NotNull Term.Cof cof(@NotNull Expr restr) { | |
+ return cof(inherit(restr, Term.F), restr.pos()); | |
+ } | |
+ | |
+ private static @NotNull Term.Cof cof(Term restr, @NotNull SourcePos pos) { | |
+ if (!(restr instanceof Term.Cof cof)) | |
+ throw new SPE(pos, Doc.english("Expects a cofibration literal, got"), restr); | |
+ return cof; | |
+ } | |
+ | |
+ private @NotNull Option<Restr.Side<Term>> clause( | |
+ @NotNull SourcePos pos, | |
+ @NotNull Restr.Side<Expr> clause, @NotNull Term ty | |
+ ) { | |
+ var cofib = new Restr.Cofib<>(clause.cof().ands().map(this::condition)); | |
+ var u = CofThy.vdash(cofib, normalizer(), norm -> inherit(clause.u(), norm.term(ty))); | |
+ if (u.isDefined() && u.get() == null) | |
+ throw new SPE(pos, Doc.english("The cofibration in"), cofib, | |
+ Doc.english("is not well-defined")); | |
+ return u.map(uu -> new Restr.Side<>(cofib, uu)); | |
} | |
private <T> T hof(@NotNull LocalVar x, @NotNull Term type, @NotNull Supplier<T> t) { | |
diff --git a/base/src/main/java/org/aya/guest0x0/tyck/HCompPDF.java b/base/src/main/java/org/aya/guest0x0/tyck/HCompPDF.java | |
new file mode 100644 | |
index 0000000..fff2236 | |
--- /dev/null | |
+++ b/base/src/main/java/org/aya/guest0x0/tyck/HCompPDF.java | |
@@ -0,0 +1,38 @@ | |
+package org.aya.guest0x0.tyck; | |
+ | |
+import org.aya.guest0x0.cubical.Restr; | |
+import org.aya.guest0x0.syntax.Term; | |
+import org.aya.guest0x0.util.LocalVar; | |
+import org.jetbrains.annotations.NotNull; | |
+ | |
+import static org.aya.guest0x0.syntax.Term.*; | |
+ | |
+/** | |
+ * References: | |
+ * <ul> | |
+ * <li><a href="https://github.com/molikto/mlang/blob/5110e18d20484a3f4ee57ee68e2793e5cf0e28e6/src-main/src/main/scala/mlang/compiler/semantic/10_value_fibrant.scala">10_value_fibrant.scala</a></li> | |
+ * <li><a href="https://www.cse.chalmers.se/~simonhu/misc/hcomp.pdf">hcomp.pdf</a></li> | |
+ * </ul> | |
+ */ | |
+public interface HCompPDF { | |
+ record Transps(@NotNull Term cover, @NotNull Term.Cof restr) { | |
+ public @NotNull Term inv() { | |
+ return new Transp(mkLam("i", i -> cover.app(neg(i))), restr); | |
+ } | |
+ | |
+ /** Marisa Kirisame!! */ | |
+ public @NotNull Term mk() {return new Transp(cover, restr);} | |
+ | |
+ public @NotNull Term fill(@NotNull LocalVar i) { | |
+ var ri = new Ref(i); | |
+ return new Transp(mkLam("j", j -> cover.app(and(ri, j))), | |
+ new Cof(restr.restr().or(new Restr.Cond<>(ri, true)))); | |
+ } | |
+ | |
+ public @NotNull Term invFill(@NotNull LocalVar i) { | |
+ var ri = new Ref(i); | |
+ return new Transp(mkLam("j", j -> cover.app(neg(and(neg(ri), j)))), | |
+ new Cof(restr.restr().or(new Restr.Cond<>(ri, false)))); | |
+ } | |
+ } | |
+} | |
diff --git a/base/src/main/java/org/aya/guest0x0/tyck/Normalizer.java b/base/src/main/java/org/aya/guest0x0/tyck/Normalizer.java | |
index d2a9371..557001e 100644 | |
--- a/base/src/main/java/org/aya/guest0x0/tyck/Normalizer.java | |
+++ b/base/src/main/java/org/aya/guest0x0/tyck/Normalizer.java | |
@@ -1,16 +1,32 @@ | |
package org.aya.guest0x0.tyck; | |
+import kala.collection.immutable.ImmutableSeq; | |
+import kala.collection.mutable.MutableArrayList; | |
import kala.collection.mutable.MutableMap; | |
+import kala.value.Var; | |
+import org.aya.guest0x0.cubical.CofThy; | |
+import org.aya.guest0x0.cubical.Formula; | |
+import org.aya.guest0x0.cubical.Restr; | |
+import org.aya.guest0x0.syntax.BdryData; | |
import org.aya.guest0x0.syntax.Def; | |
+import org.aya.guest0x0.syntax.Keyword; | |
import org.aya.guest0x0.syntax.Term; | |
+import org.aya.guest0x0.tyck.HCompPDF.Transps; | |
import org.aya.guest0x0.util.LocalVar; | |
import org.aya.guest0x0.util.Param; | |
import org.jetbrains.annotations.NotNull; | |
+import org.jetbrains.annotations.Nullable; | |
+ | |
+import java.util.function.Consumer; | |
public record Normalizer( | |
@NotNull MutableMap<LocalVar, Def<Term>> sigma, | |
@NotNull MutableMap<LocalVar, Term> rho | |
-) { | |
+) implements CofThy.SubstObj<Term, LocalVar, Normalizer> { | |
+ public static @NotNull Normalizer create() { | |
+ return new Normalizer(MutableMap.create(), MutableMap.create()); | |
+ } | |
+ | |
public static @NotNull Term rename(@NotNull Term term) { | |
return new Renamer(MutableMap.create()).term(term); | |
} | |
@@ -19,6 +35,24 @@ public record Normalizer( | |
return new Param<>(param.x(), term(param.type())); | |
} | |
+ @Override public void put(LocalVar i, boolean isLeft) { | |
+ rho.put(i, Term.end(isLeft)); | |
+ } | |
+ | |
+ @Override public boolean contradicts(LocalVar i, boolean newIsLeft) { | |
+ if (!rho.containsKey(i)) return false; | |
+ if (!(rho.get(i).asFormula() instanceof Formula.Lit<Term> lit)) return false; | |
+ return lit.isLeft() != newIsLeft; | |
+ } | |
+ | |
+ @Override public @Nullable LocalVar asRef(@NotNull Term term) { | |
+ return term instanceof Term.Ref ref ? ref.var() : null; | |
+ } | |
+ | |
+ @Override public @NotNull Normalizer derive() { | |
+ return new Normalizer(sigma, MutableMap.from(rho)); | |
+ } | |
+ | |
public Term term(Term term) { | |
return switch (term) { | |
case Term.Ref ref -> rho.getOption(ref.var()) | |
@@ -49,6 +83,110 @@ public record Normalizer( | |
fn.telescope().zip(call.args()).forEach(zip -> rho.put(zip._1.x(), term(zip._2))); | |
yield term(fn.body()); | |
} | |
+ case Term.Path path -> new Term.Path(path.data().fmap(this::term)); | |
+ case Term.PLam pLam -> new Term.PLam(pLam.dims(), term(pLam.fill())); | |
+ case Term.PCall pApp -> { | |
+ var i = pApp.i().map(this::term); | |
+ var p = term(pApp.p()); | |
+ if (p instanceof Term.PLam pLam) { | |
+ pLam.dims().zipView(i).forEach(rho::put); | |
+ var fill = term(pLam.fill()); | |
+ pLam.dims().forEach(rho::remove); | |
+ yield fill; | |
+ } | |
+ var b = pApp.b(); | |
+ var ur = new Var<Term>(); | |
+ // b.dims().zipView(i).forEach(zip -> rho.put(zip._1, zip._2)); | |
+ var clauses = clauses(b.clauses(), ur::set); | |
+ if (ur.value != null) yield ur.value; | |
+ // b.dims().forEach(rho::remove); | |
+ yield new Term.PCall(p, i, new Term.PartEl(clauses)); | |
+ } | |
+ case Term.Mula f -> formulae(f.asFormula().fmap(this::term)); | |
+ case Term.Cof cof -> new Term.Cof(restr(cof.restr())); | |
+ case Term.Transp transp -> { | |
+ var parkerLiu = restr(transp.restr().restr()); | |
+ // Because of his talk about lax 2-functors! | |
+ if (CofThy.satisfied(parkerLiu)) yield Term.id("x"); | |
+ yield transp(new LocalVar("i"), term(transp.cover()), new Term.Cof(parkerLiu)); | |
+ } | |
+ case Term.PartTy par -> new Term.PartTy(term(par.ty()), term(par.restr())); | |
+ case Term.PartEl par -> partial(par); | |
+ case Term.Sub sub -> new Term.Sub(term(sub.ty()), partial(sub.par())); | |
+ }; | |
+ } | |
+ | |
+ private @NotNull Term.PartEl partial(Term.PartEl par) { | |
+ // TODO: do nothing for now, need a new term for 'to reduce' partials | |
+ return new Term.PartEl(clauses(par.clauses(), u -> {})); | |
+ } | |
+ | |
+ private ImmutableSeq<Restr.Side<Term>> clauses( | |
+ @NotNull ImmutableSeq<Restr.Side<Term>> sides, | |
+ @NotNull Consumer<Term> truthHandler | |
+ ) { | |
+ var clauses = MutableArrayList.<Restr.Side<Term>>create(); | |
+ for (var clause : sides) { | |
+ var u = term(clause.u()); | |
+ if (CofThy.normalizeCof(clause.cof().fmap(this::term), clauses, cofib -> new Restr.Side<>(cofib, u))) { | |
+ truthHandler.accept(u); | |
+ } | |
+ } | |
+ return clauses.toImmutableArray(); | |
+ } | |
+ | |
+ public Restr<Term> restr(@NotNull Restr<Term> restr) { | |
+ return switch (restr.fmap(this::term)) { | |
+ case Restr.Vary<Term> vary -> CofThy.normalizeRestr(vary); | |
+ case Restr.Const<Term> c -> c; | |
+ }; | |
+ } | |
+ | |
+ private Term transp(LocalVar i, Term cover, Term.Cof cof) { | |
+ return switch (cover.app(new Term.Ref(i))) { | |
+ case Term.DT dt && dt.isPi() -> Term.mkLam("f", u0 -> Term.mkLam("x", v -> { | |
+ var laptop = new Transps(rename(new Term.Lam(i, dt.param().type())), cof); | |
+ var w = laptop.invFill(i).app(v); | |
+ // w0 = w.subst(i, 0), according to Minghao Liu | |
+ var w0 = laptop.inv().app(v); | |
+ var cod = rename(new Term.Lam(i, dt.codomain(w))); | |
+ return new Term.Transp(cod, cof).app(u0.app(w0)); | |
+ })); | |
+ case Term.UI u && u.keyword() == Keyword.U -> Term.id("u"); | |
+ case Term.DT dt /*&& !dt.isPi()*/ -> Term.mkLam("t", u0 -> { | |
+ var laptop = new Transps(rename(new Term.Lam(i, dt.param().type())), cof); | |
+ // Simon Huber wrote u0.1 both with and without parentheses, extremely confusing!! | |
+ var v = laptop.fill(i).app(u0.proj(true)); | |
+ return new Term.Two(false, laptop.mk().app(u0.proj(true)), | |
+ new Term.Transp(rename(new Term.Lam(i, dt.codomain(v))), cof).app(u0.proj(false))); | |
+ }); | |
+ default -> new Term.Transp(cover, cof); | |
+ }; | |
+ } | |
+ | |
+ // https://github.com/mortberg/cubicaltt/blob/a5c6f94bfc0da84e214641e0b87aa9649ea114ea/Connections.hs#L178-L197 | |
+ private Term formulae(Formula<Term> formula) { | |
+ return switch (formula) { // de Morgan laws | |
+ // ~ 1 = 0, ~ 0 = 1 | |
+ case Formula.Inv<Term> inv && inv.i().asFormula() instanceof Formula.Lit<Term> lit -> Term.end(!lit.isLeft()); | |
+ // ~ (~ a) = a | |
+ case Formula.Inv<Term> inv && inv.i().asFormula() instanceof Formula.Inv<Term> ii -> ii.i(); // DNE!! :fear: | |
+ // ~ (a /\ b) = (~ a \/ ~ b), ~ (a \/ b) = (~ a /\ ~ b) | |
+ case Formula.Inv<Term> inv && inv.i().asFormula() instanceof Formula.Conn<Term> conn -> | |
+ new Term.Mula(new Formula.Conn<>(!conn.isAnd(), | |
+ formulae(new Formula.Inv<>(conn.l())), | |
+ formulae(new Formula.Inv<>(conn.r())))); | |
+ // 0 /\ a = 0, 1 /\ a = a, 0 \/ a = a, 1 \/ a = 1 | |
+ case Formula.Conn<Term> conn && conn.l() instanceof Term.Mula lf | |
+ && lf.asFormula() instanceof Formula.Lit<Term> l -> l.isLeft() | |
+ ? (conn.isAnd() ? lf : conn.r()) | |
+ : (conn.isAnd() ? conn.r() : lf); | |
+ // a /\ 0 = 0, a /\ 1 = a, a \/ 0 = a, a \/ 1 = 1 | |
+ case Formula.Conn<Term> conn && conn.r() instanceof Term.Mula rf | |
+ && rf.asFormula() instanceof Formula.Lit<Term> r -> r.isLeft() | |
+ ? (conn.isAnd() ? rf : conn.l()) | |
+ : (conn.isAnd() ? conn.l() : rf); | |
+ default -> new Term.Mula(formula); | |
}; | |
} | |
@@ -69,9 +207,29 @@ public record Normalizer( | |
case Term.Two two -> new Term.Two(two.isApp(), term(two.f()), term(two.a())); | |
case Term.Proj proj -> new Term.Proj(term(proj.t()), proj.isOne()); | |
case Term.Call call -> new Term.Call(call.fn(), call.args().map(this::term)); | |
+ case Term.Path path -> new Term.Path(boundaries(path.data())); | |
+ case Term.PLam pLam -> { | |
+ var params = pLam.dims().map(this::param); | |
+ yield new Term.PLam(params, term(pLam.fill())); | |
+ } | |
+ case Term.PCall pApp -> new Term.PCall(term(pApp.p()), pApp.i().map(this::term), partEl(pApp.b())); | |
+ case Term.Mula f -> new Term.Mula(f.asFormula().fmap(this::term)); | |
+ case Term.Transp tr -> new Term.Transp(term(tr.cover()), tr.restr().fmap(this::term)); | |
+ case Term.Cof cof -> cof.fmap(this::term); | |
+ case Term.PartTy par -> new Term.PartTy(term(par.ty()), term(par.restr())); | |
+ case Term.PartEl par -> partEl(par); | |
+ case Term.Sub sub -> new Term.Sub(term(sub.ty()), partEl(sub.par())); | |
}; | |
} | |
+ private @NotNull Term.PartEl partEl(Term.PartEl par) { | |
+ return new Term.PartEl(par.clauses().map(clause -> clause.rename(this::term))); | |
+ } | |
+ | |
+ private @NotNull BdryData<Term> boundaries(@NotNull BdryData<Term> data) { | |
+ return data.fmap(this::term, data.dims().map(this::param)); | |
+ } | |
+ | |
private @NotNull LocalVar vv(@NotNull LocalVar var) { | |
return map.getOrDefault(var, var); | |
} | |
diff --git a/base/src/main/java/org/aya/guest0x0/tyck/Resolver.java b/base/src/main/java/org/aya/guest0x0/tyck/Resolver.java | |
index 1075ed8..cbe1868 100644 | |
--- a/base/src/main/java/org/aya/guest0x0/tyck/Resolver.java | |
+++ b/base/src/main/java/org/aya/guest0x0/tyck/Resolver.java | |
@@ -76,9 +76,28 @@ public record Resolver(@NotNull MutableMap<String, LocalVar> env) { | |
.getOrThrow(() -> new SPE(unresolved.pos(), Doc.english("Unresolved: " + unresolved.name()))); | |
case Expr.Resolved resolved -> resolved; | |
case Expr.Proj proj -> new Expr.Proj(proj.pos(), expr(proj.t()), proj.isOne()); | |
+ case Expr.Path path -> { | |
+ var dims = path.data().dims(); | |
+ var state = mkCache(dims.size()); | |
+ dims.forEach(state::add); | |
+ var data = path.data().fmap(this::expr); | |
+ state.purge(); | |
+ yield new Expr.Path(path.pos(), data); | |
+ } | |
+ case Expr.Mula f -> new Expr.Mula(f.pos(), f.asFormula().fmap(this::expr)); | |
+ case Expr.Transp transp -> new Expr.Transp(transp.pos(), expr(transp.cover()), expr(transp.restr())); | |
+ case Expr.Cof cof -> new Expr.Cof(cof.pos(), cof.data().fmap(this::expr)); | |
+ case Expr.PartEl par -> par(par); | |
+ case Expr.PartTy par -> new Expr.PartTy(par.pos(), expr(par.ty()), expr(par.restr())); | |
+ case Expr.Sub sub -> new Expr.Sub(sub.pos(), expr(sub.ty()), par(sub.par())); | |
+ case Expr.SubEl subEl -> new Expr.SubEl(subEl.pos(), expr(subEl.e()), subEl.isIntro()); | |
}; | |
} | |
+ private @NotNull Expr.PartEl par(Expr.PartEl par) { | |
+ return new Expr.PartEl(par.pos(), par.clauses().map(clause -> clause.rename(this::expr))); | |
+ } | |
+ | |
private @NotNull Expr bodied(LocalVar x, Expr expr) { | |
var old = put(x); | |
var e = expr(expr); | |
diff --git a/base/src/main/java/org/aya/guest0x0/tyck/Unifier.java b/base/src/main/java/org/aya/guest0x0/tyck/Unifier.java | |
index 5d3f20e..b9a81f9 100644 | |
--- a/base/src/main/java/org/aya/guest0x0/tyck/Unifier.java | |
+++ b/base/src/main/java/org/aya/guest0x0/tyck/Unifier.java | |
@@ -1,6 +1,11 @@ | |
package org.aya.guest0x0.tyck; | |
import kala.collection.immutable.ImmutableSeq; | |
+import kala.collection.mutable.MutableMap; | |
+import kala.tuple.Tuple; | |
+import org.aya.guest0x0.cubical.CofThy; | |
+import org.aya.guest0x0.cubical.Formula; | |
+import org.aya.guest0x0.cubical.Restr; | |
import org.aya.guest0x0.syntax.Term; | |
import org.aya.guest0x0.util.LocalVar; | |
import org.jetbrains.annotations.NotNull; | |
@@ -26,6 +31,28 @@ public class Unifier { | |
case Term.UI lu && r instanceof Term.UI ru -> lu.keyword() == ru.keyword(); | |
case Term.Call lcall && r instanceof Term.Call rcall -> lcall.fn() == rcall.fn() | |
&& lcall.args().sameElements(rcall.args(), true); | |
+ case Term.PLam plam && r instanceof Term.PLam pram && plam.dims().sizeEquals(pram.dims()) -> | |
+ untyped(plam.fill(), pram.fill().subst(MutableMap.from( | |
+ pram.dims().zip(plam.dims()).map(p -> Tuple.of(p._1, new Term.Ref(p._2)))))); | |
+ case Term.PCall lpcall && r instanceof Term.PCall rpcall -> | |
+ untyped(lpcall.p(), rpcall.p()) && unifySeq(lpcall.i(), rpcall.i()); | |
+ case Term.Mula lf && r instanceof Term.Mula rf -> formulae(lf.asFormula(), rf.asFormula()); | |
+ case Term.Transp ltp && r instanceof Term.Transp rtp -> | |
+ untyped(ltp.cover(), rtp.cover()) && untyped(ltp.restr(), rtp.restr()); | |
+ case Term.Cof lcof && r instanceof Term.Cof rcof -> { | |
+ var initial = Normalizer.create(); | |
+ var ll = lcof.restr(); | |
+ var rr = rcof.restr(); | |
+ yield CofThy.conv(ll, initial, normalizer -> CofThy.satisfied(normalizer.restr(rr))) | |
+ && CofThy.conv(rr, initial, normalizer -> CofThy.satisfied(normalizer.restr(ll))); | |
+ } | |
+ case Term.PartTy lpart && r instanceof Term.PartTy rpart -> | |
+ untyped(lpart.ty(), rpart.ty()) && untyped(lpart.restr(), rpart.restr()); | |
+ case Term.PartEl par -> par.clauses().allMatch(clause -> clause(clause, r)); | |
+ case Term ll && r instanceof Term.PartEl par -> par.clauses().allMatch(clause -> clause(clause, ll)); | |
+ case Term.Sub ll && r instanceof Term.Sub rr -> untyped(ll.ty(), rr.ty()) | |
+ && untyped(ll.par(), rr.par()); | |
+ // Cubical subtyping?? Are we ever gonna unify cubes? | |
default -> false; | |
}; | |
if (!happy && data == null) | |
@@ -33,10 +60,27 @@ public class Unifier { | |
return happy; | |
} | |
+ /** Daniel Gratzer used <code>N</code> when explaining these to me */ | |
+ private boolean clause(@NotNull Restr.Side<Term> clause, @NotNull Term n) { | |
+ return CofThy.conv(new Restr.Vary<>(ImmutableSeq.of(clause.cof())), | |
+ Normalizer.create(), subst -> untyped(clause.u(), subst.term(n))); | |
+ } | |
+ | |
private boolean unifySeq(@NotNull ImmutableSeq<Term> l, @NotNull ImmutableSeq<Term> r) { | |
return l.zipView(r).allMatch(p -> untyped(p._1, p._2)); | |
} | |
+ // Hopefully.... I don't know. :shrug: | |
+ private boolean formulae(Formula<Term> lf, Formula<Term> rf) { | |
+ return switch (lf) { | |
+ case Formula.Lit<Term> l && rf instanceof Formula.Lit<Term> r -> l.isLeft() == r.isLeft(); | |
+ case Formula.Inv<Term> l && rf instanceof Formula.Inv<Term> r -> untyped(l.i(), r.i()); | |
+ case Formula.Conn<Term> l && rf instanceof Formula.Conn<Term> r && l.isAnd() == r.isAnd() -> | |
+ untyped(l.l(), r.l()) && untyped(l.r(), r.r()); | |
+ default -> false; | |
+ }; | |
+ } | |
+ | |
private boolean eta(@NotNull Term r, Term.Lam lam) { | |
return untyped(lam.body(), r.app(new Term.Ref(lam.x()))); | |
} | |
diff --git a/base/src/main/java/org/aya/guest0x0/util/AltF7.java b/base/src/main/java/org/aya/guest0x0/util/AltF7.java | |
new file mode 100644 | |
index 0000000..1036c58 | |
--- /dev/null | |
+++ b/base/src/main/java/org/aya/guest0x0/util/AltF7.java | |
@@ -0,0 +1,46 @@ | |
+package org.aya.guest0x0.util; | |
+ | |
+import org.aya.guest0x0.cubical.Formula; | |
+import org.aya.guest0x0.cubical.Restr; | |
+import org.aya.guest0x0.syntax.BdryData; | |
+import org.aya.guest0x0.syntax.Term; | |
+import org.jetbrains.annotations.NotNull; | |
+ | |
+public record AltF7(@NotNull LocalVar var) { | |
+ public boolean press(@NotNull Term term) { | |
+ return switch (term) { | |
+ case Term.Ref r -> r.var() == var; | |
+ case Term.Lam lam -> press(lam.body()); | |
+ case Term.Transp transp -> press(transp.cover()) || press(transp.restr()); | |
+ case Term.PCall pCall -> press(pCall.p()) || pCall.i().anyMatch(this::press) || press(pCall.b()); | |
+ case Term.PLam pLam -> press(pLam.fill()); | |
+ case Term.Call call -> call.fn() == var || call.args().anyMatch(this::press); | |
+ case Term.Two two -> press(two.f()) || press(two.a()); | |
+ case Term.Proj proj -> press(proj.t()); | |
+ case Term.UI ignored -> false; | |
+ case Term.DT dt -> press(dt.param().type()) || press(dt.cod()); | |
+ case Term.Path path -> boundaries(path.data()); | |
+ case Term.Mula mula -> formula(mula.asFormula()); | |
+ case Term.Cof cof -> cof.restr().instView().anyMatch(this::press); | |
+ case Term.PartTy par -> press(par.ty()) || press(par.restr()); | |
+ case Term.PartEl par -> par.clauses().anyMatch(this::clause); | |
+ case Term.Sub sub -> press(sub.ty()) || press(sub.par()); | |
+ }; | |
+ } | |
+ | |
+ private boolean boundaries(BdryData<Term> data) { | |
+ return press(data.type()) || data.boundaries().anyMatch(this::clause); | |
+ } | |
+ | |
+ private boolean formula(Formula<Term> formula) { | |
+ return switch (formula) { | |
+ case Formula.Lit<?> ignored -> false; | |
+ case Formula.Inv<Term> inv -> press(inv.i()); | |
+ case Formula.Conn<Term> conn -> press(conn.l()) || press(conn.r()); | |
+ }; | |
+ } | |
+ | |
+ private boolean clause(Restr.Side<Term> clause) { | |
+ return press(clause.u()) || clause.cof().view().anyMatch(this::press); | |
+ } | |
+} | |
diff --git a/base/src/main/java/org/aya/guest0x0/util/Distiller.java b/base/src/main/java/org/aya/guest0x0/util/Distiller.java | |
index d134e72..68c51a6 100644 | |
--- a/base/src/main/java/org/aya/guest0x0/util/Distiller.java | |
+++ b/base/src/main/java/org/aya/guest0x0/util/Distiller.java | |
@@ -1,6 +1,10 @@ | |
package org.aya.guest0x0.util; | |
import kala.collection.Seq; | |
+import kala.collection.SeqView; | |
+import kala.collection.mutable.MutableList; | |
+import org.aya.guest0x0.cubical.Formula; | |
+import org.aya.guest0x0.cubical.Restr; | |
import org.aya.guest0x0.syntax.Expr; | |
import org.aya.guest0x0.syntax.Term; | |
import org.aya.pretty.doc.Doc; | |
@@ -32,6 +36,7 @@ public interface Distiller { | |
yield envPrec.ordinal() > Free.ordinal() ? Doc.parened(doc) : doc; | |
} | |
case Expr.Resolved resolved -> Doc.plain(resolved.ref().name()); | |
+ case Expr.Path path -> path.data().toDoc(); | |
case Expr.Unresolved unresolved -> Doc.plain(unresolved.name()); | |
case Expr.Proj proj -> Doc.cat(expr(proj.t(), ProjHead), Doc.plain("." + (proj.isOne() ? 1 : 2))); | |
case Expr.DT dt -> { | |
@@ -39,12 +44,44 @@ public interface Distiller { | |
yield envPrec.ordinal() > Cod.ordinal() ? Doc.parened(doc) : doc; | |
} | |
case Expr.Hole ignored -> Doc.symbol("_"); | |
+ case Expr.Mula e -> formulae(Distiller::expr, e.asFormula(), envPrec); | |
+ case Expr.Transp transp -> fibred("tr", transp.cover(), transp.restr()); | |
+ case Expr.Cof cof -> { | |
+ var doc = cof.data().toDoc(); | |
+ // Well, hopefully I guessed the precedence right. | |
+ yield envPrec.ordinal() > AppSpine.ordinal() ? Doc.parened(doc) : doc; | |
+ } | |
+ case Expr.PartEl par -> Doc.sep(Doc.symbol("\\"), partial(par)); | |
+ case Expr.PartTy par -> fibred("Partial", par.ty(), par.restr()); | |
+ case Expr.Sub sub -> Doc.sep(Doc.plain("Sub"), | |
+ expr(sub.ty(), Free), partial(sub.par())); | |
+ case Expr.SubEl subEl -> Doc.sep(Doc.plain(subEl.isIntro() ? "inS" : "outS"), expr(subEl.e(), Free)); | |
}; | |
} | |
+ @NotNull private static Doc partial(Expr.PartEl par) { | |
+ return Doc.wrap("[|", "|]", | |
+ Doc.join(Doc.symbol("|"), clauses(par.clauses()))); | |
+ } | |
+ private static @NotNull Doc fibred(String kw, Docile cover, Docile restr) { | |
+ return Doc.sep(Doc.plain(kw), cover.toDoc(), | |
+ Doc.symbol("#{"), restr.toDoc(), Doc.symbol("}")); | |
+ } | |
private static @NotNull Doc dependentType(boolean isPi, Param<?> param, Docile cod) { | |
return Doc.sep(Doc.plain(isPi ? "Pi" : "Sig"), | |
param.toDoc(), Doc.symbol(isPi ? "->" : "**"), cod.toDoc()); | |
} | |
+ private static @NotNull <E> Doc formulae(PP<E> f, Formula<E> formula, Prec envPrec) { | |
+ var doc = switch (formula) { | |
+ case Formula.Conn<E> conn -> Doc.sep(f.apply(conn.l(), IOp), | |
+ Doc.symbol(conn.isAnd() ? "/\\" : "\\/"), f.apply(conn.r(), IOp)); | |
+ case Formula.Inv<E> inv -> Doc.sep(Doc.plain("~"), f.apply(inv.i(), IOp)); | |
+ case Formula.Lit<E> lit -> { | |
+ envPrec = Free; // A hack to force no paren | |
+ yield Doc.symbol(lit.isLeft() ? "0" : "1"); | |
+ } | |
+ }; | |
+ return envPrec.ordinal() >= IOp.ordinal() ? Doc.parened(doc) : doc; | |
+ } | |
static @NotNull Doc term(@NotNull Term term, Prec envPrec) { | |
return switch (term) { | |
@@ -54,6 +91,7 @@ public interface Distiller { | |
} | |
case Term.UI ui -> Doc.plain(ui.keyword().name()); | |
case Term.Ref ref -> Doc.plain(ref.var().name()); | |
+ case Term.Path path -> path.data().toDoc(); | |
case Term.Lam lam -> { | |
var doc = Doc.sep(Doc.cat(Doc.plain("\\"), | |
Doc.symbol(lam.x().name()), Doc.plain(".")), term(lam.body(), Free)); | |
@@ -71,6 +109,38 @@ public interface Distiller { | |
.map(t -> term(t, AppSpine)).prepended(Doc.plain(call.fn().name()))); | |
yield envPrec.ordinal() > AppHead.ordinal() ? Doc.parened(doc) : doc; | |
} | |
+ case Term.PCall call -> { | |
+ var doc = Doc.sep(call.i().view() | |
+ .map(t -> term(t, AppSpine)).prepended(term(call.p(), AppHead))); | |
+ yield envPrec.ordinal() > AppHead.ordinal() ? Doc.parened(doc) : doc; | |
+ } | |
+ case Term.PLam pLam -> { | |
+ var docs = MutableList.of(Doc.plain("\\")); | |
+ pLam.dims().forEach(d -> docs.append(Doc.symbol(d.name()))); | |
+ docs.append(Doc.plain(".")); | |
+ docs.append(term(pLam.fill(), Free)); | |
+ var doc = Doc.sep(docs); | |
+ yield envPrec.ordinal() > Free.ordinal() ? Doc.parened(doc) : doc; | |
+ } | |
+ case Term.Mula f -> formulae(Distiller::term, f.asFormula(), envPrec); | |
+ case Term.Transp transp -> fibred("tr", transp.cover(), transp.restr()); | |
+ case Term.Cof cof -> { | |
+ var doc = cof.restr().toDoc(); | |
+ yield envPrec.ordinal() > AppSpine.ordinal() ? Doc.parened(doc) : doc; | |
+ } | |
+ case Term.PartTy par -> fibred("Partial", par.ty(), par.restr()); | |
+ case Term.PartEl par -> partial(par); | |
+ case Term.Sub sub -> Doc.sep(Doc.plain("Sub"), | |
+ term(sub.ty(), Free), partial(sub.par())); | |
}; | |
} | |
+ private static @NotNull Doc partial(Term.PartEl par) { | |
+ return Doc.wrap("[|", "|]", | |
+ Doc.join(Doc.symbol("|"), clauses(par.clauses()))); | |
+ } | |
+ static <T extends Restr.TermLike<T>> SeqView<Doc> clauses(@NotNull Seq<Restr.Side<T>> clauses) { | |
+ return clauses.view() | |
+ .map(Restr.Side::toDoc) | |
+ .map(Doc::spaced); | |
+ } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment