org.aya.util.binop.BinOpSet Maven / Gradle / Ivy
// 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.util.binop;
import kala.collection.immutable.ImmutableSeq;
import kala.collection.mutable.MutableSet;
import org.aya.util.error.Panic;
import org.aya.util.error.SourcePos;
import org.aya.util.terck.MutableGraph;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class BinOpSet {
public final @NotNull MutableGraph tighterGraph = MutableGraph.create();
public final @NotNull MutableSet ops = MutableSet.of(APP_ELEM);
public static final @NotNull BinOpSet.BinOP APP_ELEM = BinOP.from(SourcePos.NONE, OpDecl.APPLICATION);
public void bind(@NotNull OpDecl op, @NotNull OpDecl.BindPred pred, @NotNull OpDecl target, @NotNull SourcePos sourcePos) {
var opElem = ensureHasElem(op, sourcePos);
var targetElem = ensureHasElem(target, sourcePos);
if (opElem == targetElem) reportSelfBind(sourcePos);
switch (pred) {
case Tighter -> addTighter(opElem, targetElem);
case Looser -> addTighter(targetElem, opElem);
}
}
public PredCmp compare(@NotNull BinOpSet.BinOP lhs, @NotNull BinOpSet.BinOP rhs) {
// BinOp all have lower priority than application
if (lhs == APP_ELEM) return PredCmp.Tighter;
if (rhs == APP_ELEM) return PredCmp.Looser;
if (lhs == rhs) return PredCmp.Equal;
if (tighterGraph.hasPath(lhs, rhs)) return PredCmp.Tighter;
if (tighterGraph.hasPath(rhs, lhs)) return PredCmp.Looser;
return PredCmp.Undefined;
}
public Assoc assocOf(@Nullable OpDecl opDecl) {
if (isOperand(opDecl)) return Assoc.Unspecified;
return ensureHasElem(opDecl).assoc;
}
public abstract boolean equals(@NotNull OpDecl lhs, @NotNull OpDecl rhs);
public final boolean isOperand(@Nullable OpDecl opDecl) {
return opDecl == null || opDecl.opInfo() == null;
}
public BinOP ensureHasElem(@NotNull OpDecl opDecl) {
return ensureHasElem(opDecl, SourcePos.NONE);
}
public BinOP ensureHasElem(@NotNull OpDecl opDecl, @NotNull SourcePos sourcePos) {
var elem = ops.find(e -> equals(e.op, opDecl));
if (elem.isDefined()) return elem.get();
var newElem = BinOP.from(sourcePos, opDecl);
ops.add(newElem);
return newElem;
}
private void addTighter(@NotNull BinOpSet.BinOP from, @NotNull BinOpSet.BinOP to) {
tighterGraph.sucMut(to);
tighterGraph.sucMut(from).append(to);
}
public void reportIfCyclic() {
var cycles = tighterGraph.findCycles();
if (cycles.isNotEmpty()) {
reportCyclic(cycles);
}
}
abstract protected void reportSelfBind(@NotNull SourcePos sourcePos);
abstract protected void reportCyclic(ImmutableSeq> cycles);
public void importBind(@NotNull BinOpSet other, @NotNull SourcePos sourcePos) {
other.tighterGraph.E().view().forEach((from, tos) -> {
var fromOp = ensureHasElem(from.op, sourcePos);
tos.forEach(to -> {
var toOp = ensureHasElem(to.op, sourcePos);
addTighter(fromOp, toOp);
});
});
}
public record BinOP(
@NotNull SourcePos firstBind,
@NotNull OpDecl op,
@NotNull String name,
@NotNull Assoc assoc
) {
private static @NotNull OpDecl.OpInfo ensureOperator(@NotNull OpDecl opDecl) {
var op = opDecl.opInfo();
if (op == null) throw new Panic("Not an operator" + opDecl);
return op;
}
private static @NotNull BinOpSet.BinOP from(@NotNull SourcePos sourcePos, @NotNull OpDecl opDecl) {
var op = ensureOperator(opDecl);
return new BinOpSet.BinOP(sourcePos, opDecl, op.name(), op.assoc());
}
@Override public String toString() { return name; }
}
public enum PredCmp {
Looser,
Tighter,
Undefined,
Equal,
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy