Skip to content

Commit

Permalink
shape: fix name binding, with a lot of garbage code
Browse files Browse the repository at this point in the history
  • Loading branch information
HoshinoTented committed Sep 25, 2023
1 parent 61b2bcc commit 14cd918
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 62 deletions.
4 changes: 2 additions & 2 deletions base/src/main/java/org/aya/core/repr/AyaShape.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ enum AyaPlusFnShape implements AyaShape {
// _ : Nat -> Nat -> Nat
ImmutableSeq.of(
anyLicit(new CodeShape.TermShape.ShapeCall(AyaIntShape.DATA_NAT, ImmutableSeq.empty()).named(NAT)),
anyLicit(new CodeShape.TermShape.NameRef(NAT))
anyLicit(new CodeShape.TermShape.NameCall(NAT, ImmutableSeq.empty()))
),
new CodeShape.TermShape.NameRef(NAT),
new CodeShape.TermShape.NameCall(NAT, ImmutableSeq.empty()),
Either.right(ImmutableSeq.of(
// | a, 0 => a
new CodeShape.ClauseShape(ImmutableSeq.of(
Expand Down
188 changes: 138 additions & 50 deletions base/src/main/java/org/aya/core/repr/ShapeMatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.aya.core.term.RefTerm;
import org.aya.core.term.SortTerm;
import org.aya.core.term.Term;
import org.aya.generic.Modifier;
import org.aya.ref.AnyVar;
import org.aya.ref.DefVar;
import org.aya.util.Arg;
Expand All @@ -26,6 +27,7 @@
import java.util.function.BiFunction;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
import java.util.function.Supplier;

/**
* @author kiva
Expand All @@ -39,12 +41,16 @@ public record ShapeMatcher(
@NotNull MutableList<String> names,
@NotNull MutableMap<String, AnyVar> resolved
) {

public ShapeMatcher() {
this(MutableLinkedList.create(), MutableMap.create(), MutableMap.create(), ImmutableMap.empty(), MutableList.create(), MutableMap.create());
}

public static Option<ShapeRecognition> match(@NotNull AyaShape shape, @NotNull GenericDef def) {
var matcher = new ShapeMatcher();
public ShapeMatcher(@NotNull ImmutableMap<GenericDef, ShapeRecognition> discovered) {
this(MutableLinkedList.create(), MutableMap.create(), MutableMap.create(), discovered, MutableList.create(), MutableMap.create());
}

public static Option<ShapeRecognition> match(@NotNull ShapeMatcher matcher, @NotNull AyaShape shape, @NotNull GenericDef def) {
var success = matcher.matchDecl(shape.codeShape(), def);

if (success) {
Expand All @@ -54,10 +60,14 @@ public static Option<ShapeRecognition> match(@NotNull AyaShape shape, @NotNull G
return Option.none();
}

public static Option<ShapeRecognition> match(@NotNull AyaShape shape, @NotNull GenericDef def) {
return match(new ShapeMatcher(), shape, def);
}

private boolean matchDecl(@NotNull CodeShape shape, @NotNull GenericDef def) {
if (shape instanceof CodeShape.Named named) {
names.append(named.name());
return matchDecl(shape, def);
return matchDecl(named.shape(), def);
}

if (shape instanceof CodeShape.DataShape dataShape && def instanceof DataDef data) {
Expand All @@ -72,6 +82,8 @@ private boolean matchDecl(@NotNull CodeShape shape, @NotNull GenericDef def) {
}

private boolean matchFn(@NotNull CodeShape.FnShape shape, @NotNull FnDef def) {
var names = acquireName();

// match signature
var teleResult = matchTele(shape.tele(), def.telescope)
&& matchTerm(shape.result(), def.result);
Expand All @@ -82,75 +94,86 @@ private boolean matchFn(@NotNull CodeShape.FnShape shape, @NotNull FnDef def) {
termShape -> {
if (!def.body.isLeft()) return false;
var term = def.body.getLeftValue();
return matchTerm(termShape, term);
return matchInside(def.ref, names, () -> matchTerm(termShape, term));

Check warning on line 97 in base/src/main/java/org/aya/core/repr/ShapeMatcher.java

View check run for this annotation

Codecov / codecov/patch

base/src/main/java/org/aya/core/repr/ShapeMatcher.java#L96-L97

Added lines #L96 - L97 were not covered by tests
},
clauseShapes -> {
if (!def.body.isRight()) return false;
var clauses = def.body.getRightValue();
return matchMany(false, clauseShapes, clauses, (cs, m) ->
// inside multiple times in order to reset the state
matchInside(def.ref, () -> matchClause(cs, m))
);
var mode = def.modifiers.contains(Modifier.Overlap) ? MatchMode.Sub : MatchMode.Eq;
return matchMany(mode, clauseShapes, clauses,
(cs, m) -> matchInside(def.ref, names, () -> matchClause(cs, m)));
}
);
}

private boolean matchClause(@NotNull CodeShape.ClauseShape shape, @NotNull Term.Matching clause) {
// match pats
var patsResult = matchMany(true, shape.pats(), clause.patterns(), (ps, ap) -> matchPat(ps, ap.term()));
var patsResult = matchMany(MatchMode.Ordered, shape.pats(), clause.patterns(), (ps, ap) -> matchPat(ps, ap.term()));
if (!patsResult) return false;
return matchTerm(shape.body(), clause.body());
}

private boolean matchPat(@NotNull CodeShape.PatShape shape, @NotNull Pat pat) {
if (shape == CodeShape.PatShape.Any.INSTANCE) return true;
if (shape instanceof CodeShape.PatShape.Named named) {
names.append(named.name());
return matchPat(named.pat(), pat);
}

if (shape instanceof CodeShape.PatShape.ShapedCtor shapedCtor && pat instanceof Pat.Ctor ctor) {
var data = resolved.getOrNull(shapedCtor.name());
if (!(data instanceof DefVar<?, ?> defVar && defVar.core instanceof DataDef dataDef)) {
throw new InternalException("Invalid name: " + shapedCtor.name());
}
return doMatch(names -> {
if (shape == CodeShape.PatShape.Any.INSTANCE) return MatchResult.MatchedAny.INSTANCE;

var recognition = discovered.getOrNull(dataDef);
if (recognition == null) {
throw new InternalException("Not a shaped data");
}
if (pat instanceof Pat.Ctor ctor) {
boolean matched = false;
@Nullable ImmutableSeq<CodeShape.PatShape> inside = null;

var realShapedCtor = recognition.captures().getOrNull(shapedCtor.id());
if (realShapedCtor == null) {
throw new InternalException("Invalid moment id: " + shapedCtor.id() + " in recognition" + recognition);
}
if (shape instanceof CodeShape.PatShape.ShapedCtor shapedCtor) {
inside = shapedCtor.innerPats();

var data = resolved.getOrNull(shapedCtor.name());
if (!(data instanceof DefVar<?, ?> defVar && defVar.core instanceof DataDef dataDef)) {
throw new InternalException("Invalid name: " + shapedCtor.name());

Check warning on line 134 in base/src/main/java/org/aya/core/repr/ShapeMatcher.java

View check run for this annotation

Codecov / codecov/patch

base/src/main/java/org/aya/core/repr/ShapeMatcher.java#L134

Added line #L134 was not covered by tests
}

var recognition = discovered.getOrNull(dataDef);
if (recognition == null) {
throw new InternalException("Not a shaped data");

Check warning on line 139 in base/src/main/java/org/aya/core/repr/ShapeMatcher.java

View check run for this annotation

Codecov / codecov/patch

base/src/main/java/org/aya/core/repr/ShapeMatcher.java#L139

Added line #L139 was not covered by tests
}

var realShapedCtor = recognition.captures().getOrNull(shapedCtor.id());
if (realShapedCtor == null) {
throw new InternalException("Invalid moment id: " + shapedCtor.id() + " in recognition" + recognition);

Check warning on line 144 in base/src/main/java/org/aya/core/repr/ShapeMatcher.java

View check run for this annotation

Codecov / codecov/patch

base/src/main/java/org/aya/core/repr/ShapeMatcher.java#L144

Added line #L144 was not covered by tests
}

matched = realShapedCtor == ctor.ref();
} else if (shape instanceof CodeShape.PatShape.Ctor shapedCtor) {
inside = shapedCtor.innerPats();
matched = true;

Check warning on line 150 in base/src/main/java/org/aya/core/repr/ShapeMatcher.java

View check run for this annotation

Codecov / codecov/patch

base/src/main/java/org/aya/core/repr/ShapeMatcher.java#L149-L150

Added lines #L149 - L150 were not covered by tests
}

if (realShapedCtor == ctor.ref()) {
// resolve inner
return matchInside(ctor.ref(), () ->
if (matched) {
// TODO: licit
matchMany(true, shapedCtor.innerPats(), ctor.params().view().map(Arg::term), this::matchPat));
// We don't use `matchInside` here, because the context doesn't need to reset.
// TODO: But `outCtor (outCtor xxx)` is unsupported now, since we doesn't bind `names` to `ctor.ref`
var success = matchMany(MatchMode.Ordered, inside, ctor.params().view().map(Arg::term), this::matchPat);
if (success) {
return new MatchResult.Matched(ctor.ref());
}
}
}
}

if (shape instanceof CodeShape.PatShape.Ctor shapedCtor && pat instanceof Pat.Ctor ctor) {
// TODO: fix duplicated
return matchInside(ctor.ref(), () ->
// TODO: licit
matchMany(true, shapedCtor.innerPats(), ctor.params().view().map(Arg::term), this::matchPat));
}

if (shape == CodeShape.PatShape.Bind.INSTANCE && pat instanceof Pat.Bind bind) {
bind(bind.bind());
return true;
}
if (shape == CodeShape.PatShape.Bind.INSTANCE && pat instanceof Pat.Bind bind) {
return new MatchResult.Matched(bind.bind());
}

return false;
return MatchResult.Failed.INSTANCE;

Check warning on line 168 in base/src/main/java/org/aya/core/repr/ShapeMatcher.java

View check run for this annotation

Codecov / codecov/patch

base/src/main/java/org/aya/core/repr/ShapeMatcher.java#L168

Added line #L168 was not covered by tests
});
}

private boolean matchData(@NotNull CodeShape.DataShape shape, @NotNull DataDef data) {
var names = acquireName();

return matchTele(shape.tele(), data.telescope)
&& matchInside(data.ref, () -> matchMany(false, shape.ctors(), data.body,
&& matchInside(data.ref, names, () -> matchMany(MatchMode.Eq, shape.ctors(), data.body,
(s, c) -> captured(s, c, this::matchCtor, CtorDef::ref)));
}

Expand All @@ -167,14 +190,17 @@ private boolean matchTerm(@NotNull CodeShape.TermShape shape, @NotNull Term term
return matchTerm(named.shape(), term);
}

var names = acquireName();
@Nullable AnyVar result = null;

// TODO[hoshino]: For now, we are unable to match `| Ctor (Data {Im} Ex)` and `| Ctor (Data Ex)`
// by only one `Shape`, I think the solution is
// constructing a Term by Shape and unify them.
if (shape instanceof CodeShape.TermShape.Call call && term instanceof Callable callable) {
var superLevel = def.getOrNull(call.superLevel());
if (superLevel != callable.ref()) return false; // implies null check
// TODO[hoshino]: do we also match implicit arguments when size mismatch?
return matchMany(true, call.args(), callable.args(),
return matchMany(MatchMode.Ordered, call.args(), callable.args(),
(l, r) -> matchTerm(l, r.term()));
}
if (shape instanceof CodeShape.TermShape.Callable call && term instanceof Callable callable) {
Expand All @@ -189,8 +215,11 @@ private boolean matchTerm(@NotNull CodeShape.TermShape shape, @NotNull Term term

if (!success) return false;

return matchMany(true, call.args(), callable.args(),
success = matchMany(MatchMode.Ordered, call.args(), callable.args(),
(l, r) -> matchTerm(l, r.term()));

if (!success) return false;
result = callable.ref();
}
if (shape instanceof CodeShape.TermShape.TeleRef ref && term instanceof RefTerm refTerm) {
var superLevel = def.getOrNull(ref.superLevel());
Expand All @@ -201,7 +230,9 @@ private boolean matchTerm(@NotNull CodeShape.TermShape shape, @NotNull Term term
return teleVar == refTerm.var() || tele.ref() == refTerm.var();
}
if (shape instanceof CodeShape.TermShape.NameRef ref && term instanceof RefTerm refTerm) {
return resolve(ref.name()) == refTerm.var();
var success = resolve(ref.name()) == refTerm.var();
if (!success) return false;
result = refTerm.var();
}
if (shape instanceof CodeShape.TermShape.Sort sort && term instanceof SortTerm sortTerm) {
// kind is null -> any sort
Expand All @@ -210,6 +241,12 @@ private boolean matchTerm(@NotNull CodeShape.TermShape shape, @NotNull Term term
// TODO[hoshino]: match kind, but I don't know how to do.
throw new UnsupportedOperationException("TODO");
}

if (result != null) {
bind(names, result);
return true;
}

return false;
}

Expand Down Expand Up @@ -245,10 +282,22 @@ private boolean matchLicit(@NotNull CodeShape.ParamShape.Licit.Kind xlicit, bool
|| (xlicit == CodeShape.ParamShape.Licit.Kind.Ex) == isExplicit;
}

private boolean matchInside(@NotNull DefVar<? extends Def, ? extends TeleDecl<?>> defVar, @NotNull BooleanSupplier matcher) {
private <T> T subscoped(@NotNull Supplier<T> block) {
var snapshot = resolved.toImmutableMap();

Check warning on line 286 in base/src/main/java/org/aya/core/repr/ShapeMatcher.java

View check run for this annotation

Codecov / codecov/patch

base/src/main/java/org/aya/core/repr/ShapeMatcher.java#L286

Added line #L286 was not covered by tests

var result = block.get();

Check warning on line 288 in base/src/main/java/org/aya/core/repr/ShapeMatcher.java

View check run for this annotation

Codecov / codecov/patch

base/src/main/java/org/aya/core/repr/ShapeMatcher.java#L288

Added line #L288 was not covered by tests

resolved.clear();
resolved.putAll(snapshot);

Check warning on line 291 in base/src/main/java/org/aya/core/repr/ShapeMatcher.java

View check run for this annotation

Codecov / codecov/patch

base/src/main/java/org/aya/core/repr/ShapeMatcher.java#L290-L291

Added lines #L290 - L291 were not covered by tests

return result;

Check warning on line 293 in base/src/main/java/org/aya/core/repr/ShapeMatcher.java

View check run for this annotation

Codecov / codecov/patch

base/src/main/java/org/aya/core/repr/ShapeMatcher.java#L293

Added line #L293 was not covered by tests
}

private boolean matchInside(@NotNull DefVar<? extends Def, ? extends TeleDecl<?>> defVar, @NotNull ImmutableSeq<String> names, @NotNull BooleanSupplier matcher) {
var snapshot = resolved.toImmutableMap();

bind(defVar);
bind(names, defVar);

def.push(defVar);
var result = matcher.getAsBoolean();
def.pop();
Expand All @@ -260,19 +309,34 @@ private boolean matchInside(@NotNull DefVar<? extends Def, ? extends TeleDecl<?>
}

private static <S, C> boolean matchMany(
boolean ordered,
@NotNull MatchMode mode,
@NotNull SeqLike<S> shapes,
@NotNull SeqLike<C> cores,
@NotNull BiFunction<S, C, Boolean> matcher) {
if (!shapes.sizeEquals(cores)) return false;
if (ordered) return shapes.allMatchWith(cores, matcher::apply);
if (mode == MatchMode.Ordered) return shapes.allMatchWith(cores, matcher::apply);
var remainingShapes = MutableLinkedList.from(shapes);
for (var core : cores) {
if (remainingShapes.isEmpty()) return mode == MatchMode.Sup;
var index = remainingShapes.indexWhere(shape -> matcher.apply(shape, core));
if (index == -1) return false;
remainingShapes.removeAt(index);
}
return true;
return remainingShapes.isEmpty() || mode == MatchMode.Sub;
}

private boolean doMatch(@NotNull Function<ImmutableSeq<String>, MatchResult> block) {
var names = acquireName();
var result = block.apply(names);

return switch (result) {
case MatchResult.Failed failed -> false;
case MatchResult.MatchedAny matchedAny -> true;

Check warning on line 334 in base/src/main/java/org/aya/core/repr/ShapeMatcher.java

View check run for this annotation

Codecov / codecov/patch

base/src/main/java/org/aya/core/repr/ShapeMatcher.java#L333-L334

Added lines #L333 - L334 were not covered by tests
case MatchResult.Matched matched -> {
bind(names, matched.capture);
yield true;
}
};
}

private <S extends CodeShape.Moment, C> boolean captured(
Expand All @@ -285,10 +349,16 @@ private <S extends CodeShape.Moment, C> boolean captured(
return matched;
}

private void bind(@NotNull AnyVar someVar) {
private void bind(@NotNull ImmutableSeq<String> names, @NotNull AnyVar someVar) {
names.forEach(name -> resolved.put(name, someVar));
}

private @NotNull ImmutableSeq<String> acquireName() {
var result = names.toImmutableSeq();
names.clear();
return result;
}

private @NotNull AnyVar resolve(@NotNull String name) {
var resolved = this.resolved.getOrNull(name);
if (resolved == null) {
Expand Down Expand Up @@ -316,4 +386,22 @@ private void bind(@NotNull AnyVar someVar) {

return ctor;
}

public enum MatchMode {
Ordered,
Sub, Eq, Sup
}

private sealed interface MatchResult {

enum Failed implements MatchResult {
INSTANCE

Check warning on line 398 in base/src/main/java/org/aya/core/repr/ShapeMatcher.java

View check run for this annotation

Codecov / codecov/patch

base/src/main/java/org/aya/core/repr/ShapeMatcher.java#L397-L398

Added lines #L397 - L398 were not covered by tests
}

enum MatchedAny implements MatchResult {
INSTANCE

Check warning on line 402 in base/src/main/java/org/aya/core/repr/ShapeMatcher.java

View check run for this annotation

Codecov / codecov/patch

base/src/main/java/org/aya/core/repr/ShapeMatcher.java#L401-L402

Added lines #L401 - L402 were not covered by tests
}

record Matched(@NotNull AnyVar capture) implements MatchResult {}
}
}
Loading

0 comments on commit 14cd918

Please sign in to comment.