diff --git a/base/build.gradle.kts b/base/build.gradle.kts index 271488448..21be23930 100644 --- a/base/build.gradle.kts +++ b/base/build.gradle.kts @@ -8,6 +8,7 @@ dependencies { // implementation(libs.manifold.delegate.runtime) // annotationProcessor(libs.manifold.delegate.codegen) testImplementation(project(":producer")) + testImplementation(project(":jit-compiler")) testImplementation(libs.junit.params) testImplementation(libs.junit.jupiter) testImplementation(libs.hamcrest) diff --git a/base/src/main/java/org/aya/normalize/Normalizer.java b/base/src/main/java/org/aya/normalize/Normalizer.java index 94819cf76..193f40458 100644 --- a/base/src/main/java/org/aya/normalize/Normalizer.java +++ b/base/src/main/java/org/aya/normalize/Normalizer.java @@ -6,7 +6,6 @@ import kala.collection.immutable.ImmutableSeq; import kala.collection.immutable.ImmutableSet; import kala.control.Either; -import kala.control.Option; import kala.control.Result; import org.aya.generic.Modifier; import org.aya.syntax.compile.JitFn; @@ -26,6 +25,7 @@ import org.aya.tyck.tycker.Stateful; import org.aya.util.error.WithPos; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.function.UnaryOperator; @@ -88,8 +88,8 @@ case FnCall(var fn, int ulift, var args) -> { var result = tryUnfoldClauses(clauses.view().map(WithPos::data), args, ulift, core.is(Modifier.Overlap)); // we may get stuck - if (result.isEmpty()) return defaultValue; - term = result.get(); + if (result == null) return defaultValue; + term = result; continue; } } @@ -156,10 +156,10 @@ case ConCall(var head, _) when !head.ref().hasEq() -> { } } } - case MatchTerm matchTerm -> { - var result = tryUnfoldClauses(matchTerm.clauses().view(), matchTerm.discriminant(), 0, false); - if (result.isEmpty()) return defaultValue; - term = result.get(); + case MatchTerm(var discr, _, var clauses) -> { + var result = tryUnfoldClauses(clauses.view(), discr, 0, false); + if (result == null) return defaultValue; + term = result; continue; } default -> { @@ -173,7 +173,7 @@ private boolean isOpaque(@NotNull FnDef fn) { return opaque.contains(fn.ref()) || fn.is(Modifier.Opaque) || fn.is(Modifier.Partial); } - public @NotNull Option tryUnfoldClauses( + public @Nullable Term tryUnfoldClauses( @NotNull SeqView clauses, @NotNull ImmutableSeq args, int ulift, boolean orderIndependent ) { @@ -181,14 +181,14 @@ private boolean isOpaque(@NotNull FnDef fn) { var matcher = new PatMatcher(false, this); switch (matcher.apply(matchy.patterns(), args)) { case Result.Err(var st) -> { - if (!orderIndependent && st == Stuck) return Option.none(); + if (!orderIndependent && st == Stuck) return null; } case Result.Ok(var subst) -> { - return Option.some(matchy.body().elevate(ulift).instantiateTele(subst.view())); + return matchy.body().elevate(ulift).instantiateTele(subst.view()); } } } - return Option.none(); + return null; } private class Full implements UnaryOperator { diff --git a/base/src/main/java/org/aya/tyck/ExprTycker.java b/base/src/main/java/org/aya/tyck/ExprTycker.java index f4e4460a1..4ede1238d 100644 --- a/base/src/main/java/org/aya/tyck/ExprTycker.java +++ b/base/src/main/java/org/aya/tyck/ExprTycker.java @@ -367,7 +367,7 @@ case DepTypeTerm(var kind, var param, var body) when kind == DTKind.Sigma -> { yield fail(expr.data(), new NoRuleError(expr, null)); } if (defs.sizeGreaterThan(1)) { - var type = freshMeta("_ty" + integer + "'", expr.sourcePos(), MetaVar.Misc.IsType, false); + var type = freshMeta(integer + "_ty", expr.sourcePos(), MetaVar.Misc.IsType, false); yield new Jdg.Default(new MetaLitTerm(expr.sourcePos(), integer, defs, type), type); } var match = defs.getFirst(); diff --git a/base/src/main/java/org/aya/tyck/tycker/Contextful.java b/base/src/main/java/org/aya/tyck/tycker/Contextful.java index c224db586..5a68f2de8 100644 --- a/base/src/main/java/org/aya/tyck/tycker/Contextful.java +++ b/base/src/main/java/org/aya/tyck/tycker/Contextful.java @@ -2,7 +2,6 @@ // Use of this source code is governed by the MIT license that can be found in the LICENSE.md file. package org.aya.tyck.tycker; -import org.aya.generic.Constants; import org.aya.generic.term.DTKind; import org.aya.syntax.concrete.Expr; import org.aya.syntax.core.Closure; @@ -18,8 +17,6 @@ import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; -import java.util.function.Supplier; - /** * Indicating something is {@link LocalCtx}ful.
* Whenever you want to introduce some bind, make sure you are modifying @@ -60,15 +57,15 @@ public interface Contextful { return new MetaCall(new MetaVar(name, pos, args.size(), req.bind(vars.view()), isUser), args); } + /** @see org.aya.syntax.ref.MetaVar#asDt */ default @NotNull Term generatePi(Expr.@NotNull Lambda expr, SourcePos sourcePos) { var param = expr.param(); return generatePi(sourcePos, param.ref().name()); } private @NotNull Term generatePi(@NotNull SourcePos pos, @NotNull String name) { - var genName = name + Constants.GENERATED_POSTFIX; - var domain = freshMeta(genName + "ty", pos, MetaVar.Misc.IsType, false); - var codomain = freshMeta(genName + "ret", pos, MetaVar.Misc.IsType, false); + var domain = freshMeta(name + "ty", pos, MetaVar.Misc.IsType, false); + var codomain = freshMeta(name + "ret", pos, MetaVar.Misc.IsType, false); return new DepTypeTerm(DTKind.Pi, domain, Closure.mkConst(codomain)); } } diff --git a/cli-impl/src/main/java/org/aya/cli/library/incremental/DiskCompilerAdvisor.java b/cli-impl/src/main/java/org/aya/cli/library/incremental/DiskCompilerAdvisor.java index 5741d3fdd..4c3dab259 100644 --- a/cli-impl/src/main/java/org/aya/cli/library/incremental/DiskCompilerAdvisor.java +++ b/cli-impl/src/main/java/org/aya/cli/library/incremental/DiskCompilerAdvisor.java @@ -8,9 +8,9 @@ import org.aya.cli.library.source.LibrarySource; import org.aya.cli.utils.CompilerUtil; import org.aya.compiler.CompiledModule; -import org.aya.compiler.FileSerializer; -import org.aya.compiler.ModuleSerializer; -import org.aya.compiler.NameSerializer; +import org.aya.compiler.free.morphism.SourceFreeJavaBuilder; +import org.aya.compiler.serializers.ModuleSerializer; +import org.aya.compiler.serializers.NameSerializer; import org.aya.primitive.PrimFactory; import org.aya.resolve.ResolveInfo; import org.aya.resolve.context.EmptyContext; @@ -99,7 +99,7 @@ public void addURL(Path url) throws MalformedURLException { var context = new EmptyContext(reporter, sourcePath).derive(mod); var coreDir = computeBaseDir(libraryRoot); cl.addURL(coreDir); - cl.loadClass(NameSerializer.getModuleReference(QPath.fileLevel(mod))); + cl.loadClass(NameSerializer.getModuleClassName(QPath.fileLevel(mod))); return compiledAya.toResolveInfo(recurseLoader, context, cl, primFactory); } @@ -128,11 +128,10 @@ public void addURL(Path url) throws MalformedURLException { @NotNull ImmutableSeq defs, @NotNull ModuleLoader recurseLoader ) throws IOException, ClassNotFoundException { - var javaCode = new FileSerializer(resolveInfo.shapeFactory()) - .serialize(new ModuleSerializer.ModuleResult( + var javaCode = new ModuleSerializer(resolveInfo.shapeFactory()) + .serialize(SourceFreeJavaBuilder.create(), new ModuleSerializer.ModuleResult( QPath.fileLevel(file.moduleName()), - defs.filterIsInstance(TopLevelDef.class))) - .result(); + defs.filterIsInstance(TopLevelDef.class))); var libraryRoot = file.owner().outDir(); var baseDir = computeBaseDir(libraryRoot).toAbsolutePath(); var relativePath = NameSerializer.getReference(QPath.fileLevel(file.moduleName()), null, diff --git a/jit-compiler/src/main/java/module-info.java b/jit-compiler/src/main/java/module-info.java index 2fa4c2a25..3a26ce8ed 100644 --- a/jit-compiler/src/main/java/module-info.java +++ b/jit-compiler/src/main/java/module-info.java @@ -6,4 +6,8 @@ requires static org.jetbrains.annotations; exports org.aya.compiler; + exports org.aya.compiler.free; + exports org.aya.compiler.free.data; + exports org.aya.compiler.free.morphism; + exports org.aya.compiler.serializers; } diff --git a/jit-compiler/src/main/java/org/aya/compiler/AbstractExprializer.java b/jit-compiler/src/main/java/org/aya/compiler/AbstractExprializer.java deleted file mode 100644 index 6c7376d71..000000000 --- a/jit-compiler/src/main/java/org/aya/compiler/AbstractExprializer.java +++ /dev/null @@ -1,25 +0,0 @@ -// 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.compiler; - -import kala.collection.immutable.ImmutableSeq; -import org.jetbrains.annotations.NotNull; - -public abstract class AbstractExprializer { - protected final @NotNull NameGenerator nameGen; - - protected AbstractExprializer(@NotNull NameGenerator nameGen) { this.nameGen = nameGen; } - - @SafeVarargs protected final @NotNull String makeAppNew(@NotNull String className, T... terms) { - return ImmutableSeq.from(terms).joinToString(ExprializeUtils.SEP, - "new " + className + "(", ").make()", this::doSerialize); - } - - protected @NotNull String serializeToImmutableSeq(@NotNull String typeName, @NotNull ImmutableSeq terms) { - return ExprializeUtils.makeImmutableSeq(typeName, terms.map(this::doSerialize)); - } - - protected abstract @NotNull String doSerialize(@NotNull T term); - - public abstract @NotNull String serialize(T unit); -} diff --git a/jit-compiler/src/main/java/org/aya/compiler/AbstractSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/AbstractSerializer.java deleted file mode 100644 index e6f743050..000000000 --- a/jit-compiler/src/main/java/org/aya/compiler/AbstractSerializer.java +++ /dev/null @@ -1,47 +0,0 @@ -// 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.compiler; - -import kala.collection.immutable.ImmutableSeq; -import org.aya.syntax.core.term.Term; -import org.jetbrains.annotations.NotNull; - -public abstract class AbstractSerializer implements SourceBuilder { - public record JitParam(@NotNull String name, @NotNull String type) { } - - protected final @NotNull SourceBuilder sourceBuilder; - - protected AbstractSerializer(@NotNull SourceBuilder builder) { - assert builder != this : "Dont do this"; - this.sourceBuilder = builder; - } - - @Override public @NotNull StringBuilder builder() { return sourceBuilder.builder(); } - @Override public @NotNull NameGenerator nameGen() { return sourceBuilder.nameGen(); } - @Override public int indent() { - return sourceBuilder.indent(); - } - - @Override public void runInside(@NotNull Runnable runnable) { - sourceBuilder.runInside(runnable); - } - /** - * the implementation should keep {@link SourceBuilder#indent} after invocation. - */ - public abstract AbstractSerializer serialize(T unit); - - public String result() { return builder().toString(); } - - protected @NotNull String serializeTermUnderTele(@NotNull Term term, @NotNull String argsTerm, int size) { - return serializeTermUnderTele(term, SourceBuilder.fromSeq(argsTerm, size)); - } - - protected @NotNull String serializeTermUnderTele(@NotNull Term term, @NotNull ImmutableSeq argTerms) { - return new TermExprializer(sourceBuilder.nameGen(), argTerms) - .serialize(term); - } - - protected @NotNull String serializeTerm(@NotNull Term term) { - return serializeTermUnderTele(term, ImmutableSeq.empty()); - } -} diff --git a/jit-compiler/src/main/java/org/aya/compiler/AyaSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/AyaSerializer.java deleted file mode 100644 index f4305cbf6..000000000 --- a/jit-compiler/src/main/java/org/aya/compiler/AyaSerializer.java +++ /dev/null @@ -1,71 +0,0 @@ -// 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.compiler; - -import kala.collection.Seq; -import kala.collection.immutable.ImmutableSeq; -import kala.collection.immutable.ImmutableTreeSeq; -import kala.collection.mutable.MutableSeq; -import kala.control.Result; -import org.aya.syntax.core.pat.Pat; -import org.aya.syntax.core.term.Term; -import org.aya.syntax.core.term.TupTerm; -import org.aya.syntax.core.term.call.*; -import org.aya.util.error.Panic; -import org.intellij.lang.annotations.Language; - -import java.util.function.Supplier; - -import static org.aya.compiler.ExprializeUtils.getJavaRef; - -public interface AyaSerializer { - String PACKAGE_BASE = "AYA"; - String STATIC_FIELD_INSTANCE = "INSTANCE"; - String FIELD_INSTANCE = "ref"; - String FIELD_EMPTYCALL = "ourCall"; - String CLASS_CONCALL = getJavaRef(ConCall.class); - String CLASS_CONCALLLIKE = getJavaRef(ConCallLike.class); - String CLASS_TUPLE = getJavaRef(TupTerm.class); - String CLASS_FNCALL = getJavaRef(FnCall.class); - String CLASS_DATACALL = getJavaRef(DataCall.class); - String CLASS_PRIMCALL = getJavaRef(PrimCall.class); - String CLASS_IMMSEQ = getJavaRef(ImmutableSeq.class); - String CLASS_PIMMSEQ = getJavaRef(ImmutableTreeSeq.class); - String CLASS_MUTSEQ = getJavaRef(MutableSeq.class); - String CLASS_SEQ = getJavaRef(Seq.class); - String CLASS_TERM = getJavaRef(Term.class); - String CLASS_PAT = getJavaRef(Pat.class); - String CLASS_PANIC = getJavaRef(Panic.class); - - String CLASS_SUPPLIER = getJavaRef(Supplier.class); - String CLASS_RESULT = getJavaRef(Result.class); - String TYPE_IMMTERMSEQ = CLASS_IMMSEQ + "<" + CLASS_TERM + ">"; - - @Language("Java") String IMPORT_BLOCK = """ - import org.aya.generic.term.SortKind; - import org.aya.generic.term.DTKind; - import org.aya.generic.State; - import org.aya.generic.Modifier; - import org.aya.syntax.compile.*; - import org.aya.syntax.compile.CompiledAya; - import org.aya.syntax.ref.LocalVar; - import org.aya.syntax.core.*; - import org.aya.syntax.core.Closure.Jit; - import org.aya.syntax.core.pat.Pat; - import org.aya.syntax.core.pat.PatMatcher; - import org.aya.syntax.core.repr.*; - import org.aya.syntax.core.term.*; - import org.aya.syntax.core.term.repr.*; - import org.aya.syntax.core.term.call.*; - import org.aya.syntax.core.term.xtt.*; - import org.aya.util.error.Panic; - import org.aya.util.binop.Assoc; - - import java.util.function.Supplier; - import kala.collection.immutable.ImmutableSeq; - import kala.collection.immutable.ImmutableTreeSeq; - import kala.collection.mutable.MutableSeq; - import kala.collection.Seq; - import kala.control.Result; - """; -} diff --git a/jit-compiler/src/main/java/org/aya/compiler/ClassSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/ClassSerializer.java deleted file mode 100644 index 54559a907..000000000 --- a/jit-compiler/src/main/java/org/aya/compiler/ClassSerializer.java +++ /dev/null @@ -1,45 +0,0 @@ -// 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.compiler; - -import kala.collection.immutable.ImmutableSeq; -import org.aya.syntax.compile.JitClass; -import org.aya.syntax.compile.JitMember; -import org.aya.syntax.core.def.ClassDef; -import org.aya.syntax.core.term.call.ClassCall; -import org.jetbrains.annotations.NotNull; - -import static org.aya.compiler.ExprializeUtils.getJavaRef; -import static org.aya.compiler.NameSerializer.getClassRef; - -public final class ClassSerializer extends JitDefSerializer { - public static final String CLASS_JITMEMBERS = getJavaRef(JitMember.class); - public static final String CLASS_CLASSCALL = getJavaRef(ClassCall.class); - public static final String FIELD_MEMBERS = "members"; - public static final String METHOD_MEMBARS = "membars"; - - public ClassSerializer(@NotNull SourceBuilder builder) { super(builder, JitClass.class); } - @Override protected @NotNull String callClass() { return CLASS_CLASSCALL; } - @Override protected void buildConstructor(ClassDef unit) { buildSuperCall(ImmutableSeq.empty()); } - - @Override protected boolean shouldBuildEmptyCall(@NotNull ClassDef unit) { - return true; - } - - private void buildMembers(ClassDef unit) { - buildIf(FIELD_MEMBERS + " == null", () -> - buildUpdate(FIELD_MEMBERS, ExprializeUtils.makeArrayFrom(CLASS_JITMEMBERS, unit.members().map(mem -> - ExprializeUtils.getInstance(getClassRef(mem.ref()))) - ))); - - buildReturn(FIELD_MEMBERS); - } - - @Override public AbstractSerializer serialize(ClassDef unit) { - buildFramework(unit, () -> - buildMethod(METHOD_MEMBARS, ImmutableSeq.empty(), CLASS_JITMEMBERS + "[]", true, - () -> buildMembers(unit))); - - return this; - } -} diff --git a/jit-compiler/src/main/java/org/aya/compiler/CompiledModule.java b/jit-compiler/src/main/java/org/aya/compiler/CompiledModule.java index 27e5eeaf5..5ee248b55 100644 --- a/jit-compiler/src/main/java/org/aya/compiler/CompiledModule.java +++ b/jit-compiler/src/main/java/org/aya/compiler/CompiledModule.java @@ -7,6 +7,8 @@ import kala.collection.immutable.ImmutableSet; import kala.collection.mutable.MutableMap; import kala.tuple.Tuple; +import org.aya.compiler.serializers.AyaSerializer; +import org.aya.compiler.serializers.NameSerializer; import org.aya.primitive.PrimFactory; import org.aya.primitive.ShapeFactory; import org.aya.resolve.ResolveInfo; @@ -50,7 +52,7 @@ public record CompiledModule( public record DeState(@NotNull ClassLoader loader) { public @NotNull Class topLevelClass(@NotNull ModulePath name) { try { - return loader.loadClass(NameSerializer.getModuleReference(QPath.fileLevel(name))); + return loader.loadClass(NameSerializer.getModuleClassName(QPath.fileLevel(name))); } catch (ClassNotFoundException e) { throw new Panic(e); } diff --git a/jit-compiler/src/main/java/org/aya/compiler/ConSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/ConSerializer.java deleted file mode 100644 index d3b6d2f50..000000000 --- a/jit-compiler/src/main/java/org/aya/compiler/ConSerializer.java +++ /dev/null @@ -1,82 +0,0 @@ -// 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.compiler; - -import kala.collection.Seq; -import kala.collection.immutable.ImmutableSeq; -import kala.function.BooleanConsumer; -import org.aya.generic.State; -import org.aya.syntax.compile.JitCon; -import org.aya.syntax.core.def.ConDef; -import org.aya.syntax.core.def.ConDefLike; -import org.aya.syntax.core.pat.PatMatcher; -import org.jetbrains.annotations.NotNull; - -import static org.aya.compiler.AyaSerializer.*; -import static org.aya.compiler.NameSerializer.getClassRef; - -public final class ConSerializer extends JitTeleSerializer { - public static final String CLASS_STATE = ExprializeUtils.getJavaRef(State.class); - public static final String CLASS_PATMATCHER = ExprializeUtils.getJavaRef(PatMatcher.class); - - public ConSerializer(@NotNull SourceBuilder other) { - super(other, JitCon.class); - } - - @Override protected @NotNull String callClass() { return CLASS_CONCALL; } - @Override protected void buildConstructor(ConDef unit) { - var hasEq = unit.equality != null; - buildConstructor(unit, ImmutableSeq.of( - ExprializeUtils.getInstance(NameSerializer.getClassRef(unit.dataRef)), - Integer.toString(unit.selfTele.size()), - Boolean.toString(hasEq))); - } - - private void buildIsAvailable(ConDef unit, @NotNull String argsTerm) { - String matchResult; - var termSeq = argsTerm + ".toImmutableSeq()"; - if (unit.pats.isEmpty()) { - // not indexed data type, this constructor is always available - matchResult = CLASS_RESULT + ".ok(" + termSeq + ")"; - } else { - var patsTerm = unit.pats.map(x -> new PatternExprializer(nameGen(), true).serialize(x)); - var patsSeq = ExprializeUtils.makeImmutableSeq(CLASS_PAT, patsTerm, CLASS_IMMSEQ); - var matcherTerm = ExprializeUtils.makeNew(CLASS_PATMATCHER, "true", "x -> x"); - matchResult = matcherTerm + ".apply(" + patsSeq + ", " + termSeq + ")"; - } - - buildReturn(matchResult); - } - - /** - * @see ConDefLike#equality(Seq, boolean) - */ - private void buildEquality(ConDef unit, @NotNull String argsTerm, @NotNull String is0Term) { - var eq = unit.equality; - assert eq != null; - BooleanConsumer continuation = b -> { - var side = b ? eq.a() : eq.b(); - buildReturn(serializeTermUnderTele(side, argsTerm, unit.telescope().size())); - }; - - buildIfElse(is0Term, () -> continuation.accept(true), () -> continuation.accept(false)); - } - - @Override public ConSerializer serialize(ConDef unit) { - var argsTerm = "args"; - var is0Term = "is0"; - - buildFramework(unit, () -> { - buildMethod("isAvailable", - ImmutableSeq.of(new JitParam(argsTerm, TYPE_TERMSEQ)), - CLASS_RESULT + "<" + TYPE_IMMTERMSEQ + ", " + CLASS_STATE + ">", true, - () -> buildIsAvailable(unit, argsTerm)); - appendLine(); - if (unit.equality != null) buildMethod("equality", - ImmutableSeq.of(new JitParam(argsTerm, TYPE_TERMSEQ), new JitParam(is0Term, "boolean")), - CLASS_TERM, true, () -> buildEquality(unit, argsTerm, is0Term)); - }); - - return this; - } -} diff --git a/jit-compiler/src/main/java/org/aya/compiler/DataSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/DataSerializer.java deleted file mode 100644 index a322dbe9d..000000000 --- a/jit-compiler/src/main/java/org/aya/compiler/DataSerializer.java +++ /dev/null @@ -1,72 +0,0 @@ -// 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.compiler; - -import kala.collection.immutable.ImmutableMap; -import kala.collection.immutable.ImmutableSeq; -import kala.tuple.Tuple; -import org.aya.primitive.ShapeFactory; -import org.aya.syntax.compile.JitData; -import org.aya.syntax.core.def.DataDef; -import org.aya.syntax.core.def.TyckAnyDef; -import org.aya.syntax.core.repr.CodeShape; -import org.aya.syntax.ref.DefVar; -import org.jetbrains.annotations.NotNull; - -import static org.aya.compiler.AyaSerializer.CLASS_DATACALL; -import static org.aya.compiler.NameSerializer.getClassRef; - -// You should compile this with its constructors -public final class DataSerializer extends JitTeleSerializer { - private final @NotNull ShapeFactory shapeFactory; - - public DataSerializer(@NotNull SourceBuilder builder, @NotNull ShapeFactory shapeFactory) { - super(builder, JitData.class); - this.shapeFactory = shapeFactory; - } - - @Override public DataSerializer serialize(DataDef unit) { - buildFramework(unit, () -> buildMethod("constructors", ImmutableSeq.empty(), - CLASS_JITCON + "[]", true, - () -> buildConstructors(unit))); - return this; - } - - @Override protected @NotNull String callClass() { return CLASS_DATACALL; } - @Override protected void buildShape(DataDef unit) { - var maybe = shapeFactory.find(TyckAnyDef.make(unit)); - if (maybe.isEmpty()) { - super.buildShape(unit); - return; - } - var recog = maybe.get(); - appendMetadataRecord("shape", Integer.toString(recog.shape().ordinal()), false); - - // The capture is one-to-one - var flipped = ImmutableMap.from(recog.captures().view() - .map((k, v) -> Tuple., CodeShape.GlobalId>of(((TyckAnyDef) v).ref, k))); - var capture = unit.body.map(x -> ExprializeUtils.makeSub(CLASS_GLOBALID, flipped.get(x.ref).toString())); - appendMetadataRecord("recognition", ExprializeUtils.makeHalfArrayFrom(capture), false); - } - - @Override protected void buildConstructor(DataDef unit) { - buildConstructor(unit, ImmutableSeq.of(Integer.toString(unit.body.size()))); - } - - /** - * @see JitData#constructors() - */ - private void buildConstructors(DataDef unit) { - var cRef = "this.constructors"; - if (unit.body.isEmpty()) { - buildReturn(cRef); - return; - } - - buildIf(ExprializeUtils.isNull(cRef + "[0]"), () -> - unit.body.forEachIndexed((idx, con) -> - buildUpdate(cRef + "[" + idx + "]", ExprializeUtils.getInstance(getClassRef(con.ref))))); - - buildReturn(cRef); - } -} diff --git a/jit-compiler/src/main/java/org/aya/compiler/ExprializeUtils.java b/jit-compiler/src/main/java/org/aya/compiler/ExprializeUtils.java deleted file mode 100644 index 98601de81..000000000 --- a/jit-compiler/src/main/java/org/aya/compiler/ExprializeUtils.java +++ /dev/null @@ -1,77 +0,0 @@ -// 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.compiler; - -import com.intellij.openapi.util.text.StringUtil; -import kala.collection.SeqLike; -import kala.collection.immutable.ImmutableSeq; -import org.jetbrains.annotations.NotNull; - -public interface ExprializeUtils { - String SEP = ", "; - - static @NotNull String makeNew(@NotNull String className, String... terms) { - return ImmutableSeq.from(terms).joinToString(SEP, "new " + className + "(", ")"); - } - - static @NotNull String makeImmutableSeq( - @NotNull String typeName, @NotNull ImmutableSeq terms, @NotNull String seqName - ) { - if (terms.isEmpty()) { - return seqName + ".empty()"; - } else { - return terms.joinToString(SEP, seqName + ".<" + typeName + ">of(", ")"); - } - } - - static @NotNull String makeImmutableSeq(@NotNull String typeName, @NotNull ImmutableSeq terms) { - return makeImmutableSeq(typeName, terms, AyaSerializer.CLASS_IMMSEQ); - } - - static @NotNull String makeThunk(@NotNull String value) { - return "() -> " + value; - } - - static @NotNull String makeArrayFrom(@NotNull String type, @NotNull ImmutableSeq elements) { - return "new " + type + "[] " + makeHalfArrayFrom(elements); - } - - static @NotNull String makeHalfArrayFrom(@NotNull SeqLike elements) { - return elements.joinToString(", ", "{ ", " }"); - } - - static @NotNull String makeSub(@NotNull String superClass, @NotNull String sub) { - return superClass + "." + sub; - } - - static @NotNull String makeEnum(@NotNull String enumClass, @NotNull Enum value) { - return makeSub(enumClass, value.toString()); - } - - static @NotNull String makeString(@NotNull String raw) { - return "\"" + StringUtil.escapeStringCharacters(raw) + "\""; - } - - static @NotNull String isNull(@NotNull String term) { - return term + " == null"; - } - - static @NotNull String getInstance(@NotNull String defName) { - return defName + "." + AyaSerializer.STATIC_FIELD_INSTANCE; - } - - static @NotNull String getCallInstance(@NotNull String term) { - return term + "." + AyaSerializer.FIELD_INSTANCE + "()"; - } - - static @NotNull String getEmptyCallTerm(@NotNull String term) { - return term + "." + AyaSerializer.FIELD_EMPTYCALL; - } - - /** - * Get the reference to {@param clazz}, it should be imported to current file. - */ - static @NotNull String getJavaRef(@NotNull Class clazz) { - return clazz.getSimpleName(); - } -} diff --git a/jit-compiler/src/main/java/org/aya/compiler/FileSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/FileSerializer.java deleted file mode 100644 index e7c4d6298..000000000 --- a/jit-compiler/src/main/java/org/aya/compiler/FileSerializer.java +++ /dev/null @@ -1,36 +0,0 @@ -// 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.compiler; - -import org.aya.primitive.ShapeFactory; -import org.aya.syntax.ref.ModulePath; -import org.jetbrains.annotations.NotNull; - -import static org.aya.compiler.NameSerializer.PACKAGE_SEPARATOR; -import static org.aya.compiler.NameSerializer.getModulePackageReference; - -public class FileSerializer extends AbstractSerializer { - private final @NotNull ShapeFactory shapeFactory; - - public FileSerializer(@NotNull ShapeFactory factory) { - super(new SourceBuilder.Default()); - this.shapeFactory = factory; - } - - private void buildPackage(@NotNull ModulePath path) { - appendLine("package " + getModulePackageReference(path, PACKAGE_SEPARATOR) + ";"); - } - - @Override public @NotNull FileSerializer serialize(ModuleSerializer.ModuleResult unit) { - assert unit.name().isFileModule(); - buildPackage(unit.name().module()); - appendLine(); - appendLine(AyaSerializer.IMPORT_BLOCK); - appendLine(); - - new ModuleSerializer(this, shapeFactory) - .serialize(unit); - - return this; - } -} diff --git a/jit-compiler/src/main/java/org/aya/compiler/FnSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/FnSerializer.java deleted file mode 100644 index 9f9fd56e0..000000000 --- a/jit-compiler/src/main/java/org/aya/compiler/FnSerializer.java +++ /dev/null @@ -1,113 +0,0 @@ -// 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.compiler; - -import kala.collection.Seq; -import kala.collection.immutable.ImmutableSeq; -import kala.collection.mutable.MutableList; -import kala.control.Either; -import org.aya.generic.Modifier; -import org.aya.primitive.ShapeFactory; -import org.aya.syntax.compile.JitFn; -import org.aya.syntax.core.def.FnDef; -import org.aya.syntax.core.def.TyckAnyDef; -import org.aya.util.error.WithPos; -import org.jetbrains.annotations.NotNull; - -import java.util.EnumSet; -import java.util.function.Consumer; - -import static org.aya.compiler.AyaSerializer.*; - -public final class FnSerializer extends JitTeleSerializer { - public static final String TYPE_STUCK = CLASS_SUPPLIER + "<" + CLASS_TERM + ">"; - - private final @NotNull ShapeFactory shapeFactory; - public FnSerializer(@NotNull SourceBuilder builder, @NotNull ShapeFactory shapeFactory) { - super(builder, JitFn.class); - this.shapeFactory = shapeFactory; - } - - public static int modifierFlags(@NotNull EnumSet modies) { - var flag = 0; - - for (var mody : modies) { - flag |= 1 << mody.ordinal(); - } - - return flag; - } - - @Override protected void buildConstructor(FnDef unit) { - super.buildConstructor(unit, ImmutableSeq.of(Integer.toString(modifierFlags(unit.modifiers())))); - } - - /** - * Build fixed argument `invoke` - */ - private void buildInvoke(FnDef unit, @NotNull String onStuckTerm, @NotNull ImmutableSeq argTerms) { - Consumer onStuckCon = s -> s.buildReturn(onStuckTerm + ".get()"); - - if (unit.is(Modifier.Opaque)) { - onStuckCon.accept(this); - return; - } - - switch (unit.body()) { - case Either.Left(var expr) -> buildReturn(serializeTermUnderTele(expr, argTerms)); - case Either.Right(var clauses) -> { - var ser = new PatternSerializer(this.sourceBuilder, argTerms, onStuckCon, onStuckCon); - ser.serialize(clauses.view() - .map(WithPos::data) - .map(matching -> new PatternSerializer.Matching( - matching.bindCount(), matching.patterns(), (s, bindSize) -> - s.buildReturn(serializeTermUnderTele(matching.body(), PatternSerializer.VARIABLE_RESULT, bindSize)) - )) - .toImmutableSeq()); - } - } - } - - /** - * Build vararg `invoke` - */ - private void buildInvoke(FnDef unit, @NotNull String onStuckTerm, @NotNull String argsTerm) { - var teleSize = unit.telescope().size(); - - buildReturn(SourceBuilder.fromSeq(argsTerm, teleSize).view() - .prepended(onStuckTerm) - .joinToString(", ", "this.invoke(", ")")); - } - - @Override protected @NotNull String callClass() { return CLASS_FNCALL; } - @Override protected void buildShape(FnDef unit) { - var maybe = shapeFactory.find(TyckAnyDef.make(unit)); - if (maybe.isEmpty()) { - super.buildShape(unit); - } else { - var recog = maybe.get(); - appendMetadataRecord("shape", Integer.toString(recog.shape().ordinal()), false); - appendMetadataRecord("recognition", ExprializeUtils.makeHalfArrayFrom(Seq.empty()), false); - } - } - - @Override public FnSerializer serialize(FnDef unit) { - var argsTerm = "args"; - var onStuckTerm = "onStuck"; - var onStuckParam = new JitParam(onStuckTerm, TYPE_STUCK); - var names = ImmutableSeq.fill(unit.telescope().size(), _ -> nameGen().nextName()); - var fixedParams = MutableList.create(); - fixedParams.append(onStuckParam); - fixedParams.appendAll(names.view().map(x -> new JitParam(x, CLASS_TERM))); - - buildFramework(unit, () -> { - buildMethod("invoke", fixedParams.toImmutableSeq(), - CLASS_TERM, false, () -> buildInvoke(unit, onStuckTerm, names)); - appendLine(); - buildMethod("invoke", ImmutableSeq.of(onStuckParam, new JitParam(argsTerm, TYPE_TERMSEQ)), - CLASS_TERM, true, () -> buildInvoke(unit, onStuckTerm, argsTerm)); - }); - - return this; - } -} diff --git a/jit-compiler/src/main/java/org/aya/compiler/JitDefSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/JitDefSerializer.java deleted file mode 100644 index 4a75283a2..000000000 --- a/jit-compiler/src/main/java/org/aya/compiler/JitDefSerializer.java +++ /dev/null @@ -1,89 +0,0 @@ -// 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.compiler; - -import kala.collection.immutable.ImmutableSeq; -import org.aya.syntax.compile.CompiledAya; -import org.aya.syntax.core.def.TyckDef; -import org.jetbrains.annotations.NotNull; - -import static org.aya.compiler.AyaSerializer.FIELD_EMPTYCALL; -import static org.aya.compiler.AyaSerializer.STATIC_FIELD_INSTANCE; -import static org.aya.compiler.NameSerializer.javifyClassName; - -public abstract class JitDefSerializer extends AbstractSerializer { - public static final String CLASS_METADATA = ExprializeUtils.getJavaRef(CompiledAya.class); - - protected final @NotNull Class superClass; - - protected JitDefSerializer(@NotNull SourceBuilder builder, @NotNull Class superClass) { - super(builder); - this.superClass = superClass; - } - - /** - * @see CompiledAya - */ - protected void buildMetadata(@NotNull T unit) { - var ref = unit.ref(); - var module = ref.module; - var assoc = ref.assoc(); - var assocIdx = assoc == null ? -1 : assoc.ordinal(); - assert module != null; - appendLine("@" + CLASS_METADATA + "("); - var modPath = module.module().module(); - appendMetadataRecord("module", ExprializeUtils.makeHalfArrayFrom(modPath.view().map(ExprializeUtils::makeString)), true); - // Assumption: module.take(fileModule.size).equals(fileModule) - appendMetadataRecord("fileModuleSize", Integer.toString(module.fileModuleSize()), false); - appendMetadataRecord("name", ExprializeUtils.makeString(ref.name()), false); - appendMetadataRecord("assoc", Integer.toString(assocIdx), false); - buildShape(unit); - - appendLine(")"); - } - - protected void buildShape(T unit) { - appendMetadataRecord("shape", "-1", false); - appendMetadataRecord("recognition", ExprializeUtils.makeHalfArrayFrom(ImmutableSeq.empty()), false); - } - - protected void appendMetadataRecord(@NotNull String name, @NotNull String value, boolean isFirst) { - var prepend = isFirst ? "" : ", "; - appendLine(prepend + name + " = " + value); - } - - public void buildInstance(@NotNull String className) { - buildConstantField(className, STATIC_FIELD_INSTANCE, ExprializeUtils.makeNew(className)); - } - - public void buildSuperCall(@NotNull ImmutableSeq args) { - appendLine("super(" + args.joinToString(", ") + ");"); - } - - protected abstract boolean shouldBuildEmptyCall(@NotNull T unit); - - protected void buildFramework(@NotNull T unit, @NotNull Runnable continuation) { - var className = javifyClassName(unit.ref()); - buildMetadata(unit); - buildInnerClass(className, superClass, () -> { - buildInstance(className); - appendLine(); - // empty return type for constructor - buildMethod(className, ImmutableSeq.empty(), "/*constructor*/", false, () -> buildConstructor(unit)); - appendLine(); - if (shouldBuildEmptyCall(unit)) { - buildConstantField(callClass(), FIELD_EMPTYCALL, ExprializeUtils.makeNew( - callClass(), ExprializeUtils.getInstance(className))); - } - appendLine(); - continuation.run(); - }); - } - - protected abstract @NotNull String callClass(); - - /** - * @see org.aya.syntax.compile.JitDef - */ - protected abstract void buildConstructor(T unit); -} diff --git a/jit-compiler/src/main/java/org/aya/compiler/JitTeleSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/JitTeleSerializer.java deleted file mode 100644 index 5c7bde67d..000000000 --- a/jit-compiler/src/main/java/org/aya/compiler/JitTeleSerializer.java +++ /dev/null @@ -1,81 +0,0 @@ -// 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.compiler; - -import kala.collection.immutable.ImmutableSeq; -import kala.range.primitive.IntRange; -import org.aya.syntax.compile.JitCon; -import org.aya.syntax.core.def.TyckDef; -import org.aya.syntax.core.repr.CodeShape; -import org.aya.syntax.core.term.Param; -import org.aya.syntax.core.term.Term; -import org.aya.syntax.telescope.JitTele; -import org.jetbrains.annotations.NotNull; - -import static org.aya.compiler.AyaSerializer.CLASS_SEQ; -import static org.aya.compiler.AyaSerializer.CLASS_TERM; - -public abstract class JitTeleSerializer extends JitDefSerializer { - public static final String CLASS_JITCON = ExprializeUtils.getJavaRef(JitCon.class); - public static final String CLASS_GLOBALID = ExprializeUtils.makeSub(ExprializeUtils.getJavaRef(CodeShape.class), ExprializeUtils.getJavaRef(CodeShape.GlobalId.class)); - public static final String METHOD_TELESCOPE = "telescope"; - public static final String METHOD_RESULT = "result"; - public static final String TYPE_TERMSEQ = CLASS_SEQ + "<" + CLASS_TERM + ">"; - - protected JitTeleSerializer( - @NotNull SourceBuilder builder, - @NotNull Class superClass - ) { - super(builder, superClass); - } - - protected void buildFramework(@NotNull T unit, @NotNull Runnable continuation) { - super.buildFramework(unit, () -> { - var iTerm = "i"; - var teleArgsTerm = "teleArgs"; - buildMethod(METHOD_TELESCOPE, ImmutableSeq.of( - new JitParam(iTerm, "int"), - new JitParam(teleArgsTerm, TYPE_TERMSEQ) - ), CLASS_TERM, true, () -> buildTelescope(unit, iTerm, teleArgsTerm)); - appendLine(); - buildMethod(METHOD_RESULT, ImmutableSeq.of( - new JitParam(teleArgsTerm, TYPE_TERMSEQ) - ), CLASS_TERM, true, () -> buildResult(unit, teleArgsTerm)); - appendLine(); - continuation.run(); - }); - } - - @Override - protected boolean shouldBuildEmptyCall(@NotNull T unit) { - return unit.telescope().isEmpty(); - } - protected void buildConstructor(@NotNull T def, @NotNull ImmutableSeq ext) { - var tele = def.telescope(); - var size = tele.size(); - var licit = tele.view().map(Param::explicit).map(Object::toString); - var names = tele.view().map(Param::name).map(x -> "\"" + x + "\""); - - buildSuperCall(ImmutableSeq.of( - Integer.toString(size), - ExprializeUtils.makeArrayFrom("boolean", licit.toImmutableSeq()), - ExprializeUtils.makeArrayFrom("java.lang.String", names.toImmutableArray()) - ).appendedAll(ext)); - } - - /** - * @see JitTele#telescope(int, Term...) - */ - protected void buildTelescope(@NotNull T unit, @NotNull String iTerm, @NotNull String teleArgsTerm) { - var tele = unit.telescope(); - buildSwitch(iTerm, IntRange.closedOpen(0, tele.size()).collect(ImmutableSeq.factory()), kase -> - buildReturn(serializeTermUnderTele(tele.get(kase).type(), teleArgsTerm, kase)), () -> buildPanic(null)); - } - - /** - * @see JitTele#result - */ - protected void buildResult(@NotNull T unit, @NotNull String teleArgsTerm) { - buildReturn(serializeTermUnderTele(unit.result(), teleArgsTerm, unit.telescope().size())); - } -} diff --git a/jit-compiler/src/main/java/org/aya/compiler/MemberSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/MemberSerializer.java deleted file mode 100644 index c8f856b13..000000000 --- a/jit-compiler/src/main/java/org/aya/compiler/MemberSerializer.java +++ /dev/null @@ -1,28 +0,0 @@ -// 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.compiler; - -import kala.collection.immutable.ImmutableSeq; -import org.aya.syntax.compile.JitMember; -import org.aya.syntax.core.def.MemberDef; -import org.jetbrains.annotations.NotNull; - -import static org.aya.compiler.NameSerializer.getClassRef; - -public final class MemberSerializer extends JitTeleSerializer { - public MemberSerializer(@NotNull SourceBuilder builder) { super(builder, JitMember.class); } - @Override protected @NotNull String callClass() { return TermExprializer.CLASS_MEMCALL; } - - @Override protected void buildConstructor(MemberDef unit) { - buildConstructor(unit, ImmutableSeq.of( - ExprializeUtils.getInstance(getClassRef(unit.classRef())), - Integer.toString(unit.index()), - serializeTerm(unit.type()) - )); - } - - @Override public AbstractSerializer serialize(MemberDef unit) { - buildFramework(unit, () -> { }); - return this; - } -} diff --git a/jit-compiler/src/main/java/org/aya/compiler/ModuleSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/ModuleSerializer.java deleted file mode 100644 index 8624b7868..000000000 --- a/jit-compiler/src/main/java/org/aya/compiler/ModuleSerializer.java +++ /dev/null @@ -1,72 +0,0 @@ -// 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.compiler; - -import kala.collection.immutable.ImmutableSeq; -import org.aya.primitive.ShapeFactory; -import org.aya.syntax.core.def.*; -import org.aya.syntax.ref.QPath; -import org.aya.util.IterableUtil; -import org.jetbrains.annotations.NotNull; - -import static org.aya.compiler.NameSerializer.javifyClassName; - -/** - * Serializing a module, note that it may not a file module, so we need not to make importing. - */ -public final class ModuleSerializer extends AbstractSerializer { - public record ModuleResult( - @NotNull QPath name, - @NotNull ImmutableSeq defs - ) { } - - private final @NotNull ShapeFactory shapeFactory; - - public ModuleSerializer(@NotNull SourceBuilder builder, @NotNull ShapeFactory shapeFactory) { - super(builder); - this.shapeFactory = shapeFactory; - } - - private void serializeCons(@NotNull DataDef dataDef, @NotNull SourceBuilder serializer) { - var ser = new ConSerializer(serializer); - IterableUtil.forEach(dataDef.body, ser::appendLine, ser::serialize); - } - - private void serializeMems(@NotNull ClassDef classDef, @NotNull SourceBuilder serializer) { - var ser = new MemberSerializer(serializer); - IterableUtil.forEach(classDef.members(), ser::appendLine, ser::serialize); - } - - private void doSerialize(@NotNull TyckDef unit) { - switch (unit) { - case FnDef teleDef -> new FnSerializer(this, shapeFactory) - .serialize(teleDef); - case DataDef dataDef -> { - new DataSerializer(this, shapeFactory).serialize(dataDef); - serializeCons(dataDef, this); - } - case ConDef conDef -> new ConSerializer(this) - .serialize(conDef); - case PrimDef primDef -> new PrimSerializer(this) - .serialize(primDef); - case ClassDef classDef -> { - new ClassSerializer(this) - .serialize(classDef); - serializeMems(classDef, this); - } - case MemberDef memberDef -> new MemberSerializer(this) - .serialize(memberDef); - } - } - - private void doSerialize(ModuleResult unit) { - buildClass(javifyClassName(unit.name, null), null, false, () -> - IterableUtil.forEach(unit.defs, this::appendLine, this::doSerialize)); - } - - @Override public ModuleSerializer serialize(ModuleResult unit) { - doSerialize(unit); - - return this; - } -} diff --git a/jit-compiler/src/main/java/org/aya/compiler/PatternExprializer.java b/jit-compiler/src/main/java/org/aya/compiler/PatternExprializer.java deleted file mode 100644 index 5adf52d00..000000000 --- a/jit-compiler/src/main/java/org/aya/compiler/PatternExprializer.java +++ /dev/null @@ -1,69 +0,0 @@ -// 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.compiler; - -import kala.collection.immutable.ImmutableSeq; -import org.aya.syntax.core.pat.Pat; -import org.aya.syntax.core.term.Term; -import org.aya.syntax.core.term.call.ConCallLike; -import org.aya.syntax.ref.LocalVar; -import org.aya.util.error.Panic; -import org.jetbrains.annotations.NotNull; - -import static org.aya.compiler.AyaSerializer.CLASS_PAT; -import static org.aya.compiler.AyaSerializer.CLASS_TERM; - -public final class PatternExprializer extends AbstractExprializer { - public static final @NotNull String CLASS_PAT_MISC = ExprializeUtils.makeSub(CLASS_PAT, ExprializeUtils.getJavaRef(Pat.Misc.class)); - public static final @NotNull String CLASS_PAT_BIND = ExprializeUtils.makeSub(CLASS_PAT, ExprializeUtils.getJavaRef(Pat.Bind.class)); - public static final @NotNull String CLASS_PAT_CON = ExprializeUtils.makeSub(CLASS_PAT, ExprializeUtils.getJavaRef(Pat.Con.class)); - public static final @NotNull String CLASS_PAT_INT = ExprializeUtils.makeSub(CLASS_PAT, ExprializeUtils.getJavaRef(Pat.ShapedInt.class)); - public static final @NotNull String CLASS_LOCALVAR = ExprializeUtils.getJavaRef(LocalVar.class); - public static final @NotNull String CLASS_CONHEAD = ExprializeUtils.makeSub(ExprializeUtils.getJavaRef(ConCallLike.class), ExprializeUtils.getJavaRef(ConCallLike.Head.class)); - public static final @NotNull String CLASS_PAT_TUPLE = ExprializeUtils.makeSub(CLASS_PAT, ExprializeUtils.getJavaRef(Pat.Tuple.class)); - - private final boolean allowLocalTerm; - - PatternExprializer(@NotNull NameGenerator nameGen, boolean allowLocalTerm) { - super(nameGen); - this.allowLocalTerm = allowLocalTerm; - } - - private @NotNull String serializeTerm(@NotNull Term term) { - return new TermExprializer(this.nameGen, ImmutableSeq.empty(), allowLocalTerm) - .serialize(term); - } - - private @NotNull String serializeConHead(@NotNull ConCallLike.Head head) { - return ExprializeUtils.makeNew(CLASS_CONHEAD, - ExprializeUtils.getInstance(NameSerializer.getClassRef(head.ref())), - Integer.toString(head.ulift()), - ExprializeUtils.makeImmutableSeq(CLASS_TERM, head.ownerArgs().map(this::serializeTerm))); - } - - @Override protected @NotNull String doSerialize(@NotNull Pat term) { - return switch (term) { - case Pat.Misc misc -> ExprializeUtils.makeEnum(CLASS_PAT_MISC, misc); - // it is safe to new a LocalVar, this method will be called when meta solving only, - // but the meta solver will eat all LocalVar so that it will be happy. - case Pat.Bind bind -> ExprializeUtils.makeNew(CLASS_PAT_BIND, - ExprializeUtils.makeNew(CLASS_LOCALVAR, ExprializeUtils.makeString(bind.bind().name())), - serializeTerm(bind.type()) - ); - case Pat.Con con -> ExprializeUtils.makeNew(CLASS_PAT_CON, - ExprializeUtils.getInstance(NameSerializer.getClassRef(con.ref())), - serializeToImmutableSeq(CLASS_PAT, con.args()), - serializeConHead(con.head())); - case Pat.ShapedInt shapedInt -> ExprializeUtils.makeNew(CLASS_PAT_INT, - Integer.toString(shapedInt.repr()), - ExprializeUtils.getInstance(NameSerializer.getClassRef(shapedInt.zero())), - ExprializeUtils.getInstance(NameSerializer.getClassRef(shapedInt.suc())), - serializeTerm(shapedInt.type())); - case Pat.Meta _ -> Panic.unreachable(); - case Pat.Tuple(var l, var r) -> ExprializeUtils.makeNew(CLASS_PAT_TUPLE, - doSerialize(l), doSerialize(r)); - }; - } - - @Override public @NotNull String serialize(Pat unit) { return doSerialize(unit); } -} diff --git a/jit-compiler/src/main/java/org/aya/compiler/PatternSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/PatternSerializer.java deleted file mode 100644 index 4bb01c689..000000000 --- a/jit-compiler/src/main/java/org/aya/compiler/PatternSerializer.java +++ /dev/null @@ -1,230 +0,0 @@ -// 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.compiler; - -import kala.collection.SeqView; -import kala.collection.immutable.ImmutableSeq; -import kala.collection.immutable.primitive.ImmutableIntSeq; -import kala.range.primitive.IntRange; -import org.aya.generic.State; -import org.aya.syntax.core.pat.Pat; -import org.aya.util.error.Panic; -import org.jetbrains.annotations.NotNull; - -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -import static org.aya.compiler.AyaSerializer.*; - -/** - * We do not serialize meta solve, it is annoying - */ -public final class PatternSerializer extends AbstractSerializer> { - @FunctionalInterface - public interface SuccessContinuation extends BiConsumer { - } - - public final static class Once implements Runnable { - public static @NotNull Once of(@NotNull Runnable run) { return new Once(run); } - private final @NotNull Runnable run; - private boolean dirty = false; - - public Once(@NotNull Runnable run) { this.run = run; } - - @Override public void run() { - if (dirty) throw new Panic("Once"); - dirty = true; - this.run.run(); - } - } - - public record Matching( - int bindCount, @NotNull ImmutableSeq patterns, - @NotNull SuccessContinuation onSucc - ) { } - - public static final @NotNull String VARIABLE_RESULT = "result"; - public static final @NotNull String VARIABLE_STATE = "matchState"; - public static final @NotNull String VARIABLE_SUBSTATE = "subMatchState"; - - private final @NotNull ImmutableSeq argNames; - private final @NotNull Consumer onStuck; - private final @NotNull Consumer onMismatch; - private int bindCount = 0; - - public PatternSerializer( - @NotNull SourceBuilder builder, - @NotNull ImmutableSeq argNames, - @NotNull Consumer onStuck, - @NotNull Consumer onMismatch - ) { - super(builder); - this.argNames = argNames; - this.onStuck = onStuck; - this.onMismatch = onMismatch; - } - - /// region Serializing - - private void doSerialize(@NotNull Pat pat, @NotNull String term, @NotNull Once continuation) { - switch (pat) { - case Pat.Misc misc -> { - switch (misc) { - case Absurd -> buildIfElse("Panic.unreachable()", State.Stuck, continuation); - case UntypedBind -> { - onMatchBind(term); - continuation.run(); - } - } - } - case Pat.Bind _ -> { - onMatchBind(term); - continuation.run(); - } - // TODO: match IntegerTerm / ListTerm first - case Pat.Con con -> multiStage(term, ImmutableSeq.of( - // mTerm -> solveMeta(con, mTerm), - mTerm -> buildIfInstanceElse(mTerm, CLASS_CONCALLLIKE, State.Stuck, mmTerm -> - buildIfElse(ExprializeUtils.getCallInstance(mmTerm) + " == " + ExprializeUtils.getInstance(NameSerializer.getClassRef(con.ref())), - State.Mismatch, () -> { - var conArgsTerm = buildLocalVar(TYPE_IMMTERMSEQ, - nameGen().nextName(), mmTerm + ".conArgs()"); - doSerialize(con.args().view(), SourceBuilder.fromSeq(conArgsTerm, con.args().size()).view(), - Once.of(() -> buildUpdate(VARIABLE_SUBSTATE, "true"))); - })) - ), continuation); - case Pat.Meta _ -> Panic.unreachable(); - case Pat.ShapedInt shapedInt -> multiStage(term, ImmutableSeq.of( - // mTerm -> solveMeta(shapedInt, mTerm), - mTerm -> matchInt(shapedInt, mTerm), - // do nothing on success, [doSerialize] sets subMatchState, and we will invoke [continuation] when [subMatchState = true] - mTerm -> doSerialize(shapedInt.constructorForm(), mTerm, Once.of(() -> { })) - ), continuation); - case Pat.Tuple(var l, var r) -> multiStage(term, ImmutableSeq.of( - // mTerm -> solveMeta(tuple, mTerm), - mTerm -> buildIfInstanceElse(mTerm, CLASS_TUPLE, State.Stuck, mmTerm -> - doSerialize(l, mmTerm + ".lhs()", Once.of(() -> - doSerialize(r, mmTerm + ".rhs()", Once.of(() -> { }))))) - ), continuation); - } - } - - /** - * Generate multi case matching, these local variable are available: - *
    - *
  • {@link #VARIABLE_SUBSTATE}: the state of multi case matching, false means last check failed
  • - *
  • {@code tmpName}: this name is generated, they are the first argument of continuation. - * {@param preContinuation} may change the term be matched - *
  • - *
- *

- * Note that {@param preContinuation}s should not invoke {@param continuation}! - * - * @param term the expression be matched, not always a variable reference - * @param preContinuation matching cases - * @param continuation on match success - */ - private void multiStage( - @NotNull String term, - @NotNull ImmutableSeq> preContinuation, - @NotNull Once continuation - ) { - var tmpName = nameGen().nextName(); - buildUpdate(VARIABLE_SUBSTATE, "false"); - buildLocalVar(CLASS_TERM, tmpName, term); - - for (var pre : preContinuation) { - buildIf("! " + VARIABLE_SUBSTATE, () -> pre.accept(tmpName)); - } - - buildIf(VARIABLE_SUBSTATE, continuation); - } - - private void matchInt(@NotNull Pat.ShapedInt pat, @NotNull String term) { - buildIfInstanceElse(term, TermExprializer.CLASS_INTEGER, intTerm -> - buildIf(pat.repr() + " == " + intTerm + ".repr()", () -> - // Pat.ShapedInt provides no binds - buildUpdate(VARIABLE_SUBSTATE, "true")), null); - } - - /** - * @apiNote {@code pats.sizeEquals(terms)} - */ - private void doSerialize(@NotNull SeqView pats, @NotNull SeqView terms, @NotNull Once continuation) { - if (pats.isEmpty()) { - continuation.run(); - return; - } - - var pat = pats.getFirst(); - var term = terms.getFirst(); - doSerialize(pat, term, Once.of(() -> doSerialize(pats.drop(1), terms.drop(1), continuation))); - } - - /// endregion Serializing - - /// region Java Source Code Generate API - - private void buildIfInstanceElse( - @NotNull String term, - @NotNull String type, - @NotNull State state, - @NotNull Consumer continuation - ) { - buildIfInstanceElse(term, type, continuation, () -> updateState(-state.ordinal())); - } - - private void buildIfElse(@NotNull String condition, @NotNull State state, @NotNull Runnable continuation) { - buildIfElse(condition, continuation, () -> updateState(-state.ordinal())); - } - - private void updateState(int state) { - buildUpdate(VARIABLE_STATE, Integer.toString(state)); - } - - private void onMatchBind(@NotNull String term) { - appendLine(VARIABLE_RESULT + ".set(" + bindCount++ + ", " + term + ");"); - } - - /// endregion Java Source Code Generate API - - @Override public PatternSerializer serialize(@NotNull ImmutableSeq unit) { - if (unit.isEmpty()) { - onMismatch.accept(this); - return this; - } - var bindSize = unit.mapToInt(ImmutableIntSeq.factory(), Matching::bindCount); - int maxBindSize = bindSize.max(); - - buildLocalVar(CLASS_MUTSEQ + "<" + CLASS_TERM + ">", VARIABLE_RESULT, CLASS_MUTSEQ + ".fill(" + maxBindSize + ", (" + CLASS_TERM + ") null)"); - buildLocalVar("int", VARIABLE_STATE, "0"); - buildLocalVar("boolean", VARIABLE_SUBSTATE, "false"); - - buildGoto(() -> unit.forEachIndexed((idx, clause) -> { - var jumpCode = idx + 1; - bindCount = 0; - doSerialize( - clause.patterns().view(), - argNames.view(), - Once.of(() -> updateState(jumpCode))); - - buildIf(VARIABLE_STATE + " > 0", this::buildBreak); - })); - - // -1 ..= unit.size() - var range = IntRange.closed(-1, unit.size()).collect(ImmutableSeq.factory()); - buildSwitch(VARIABLE_STATE, range, state -> { - switch (state) { - case -1 -> onMismatch.accept(this); - case 0 -> onStuck.accept(this); - default -> { - assert state > 0; - var realIdx = state - 1; - unit.get(realIdx).onSucc.accept(this, bindSize.get(realIdx)); - } - } - }, () -> buildPanic(null)); - - return this; - } -} diff --git a/jit-compiler/src/main/java/org/aya/compiler/PrimSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/PrimSerializer.java deleted file mode 100644 index 844c98780..000000000 --- a/jit-compiler/src/main/java/org/aya/compiler/PrimSerializer.java +++ /dev/null @@ -1,24 +0,0 @@ -// 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.compiler; - -import kala.collection.immutable.ImmutableSeq; -import org.aya.syntax.compile.JitPrim; -import org.aya.syntax.core.def.PrimDef; -import org.jetbrains.annotations.NotNull; - -import static org.aya.compiler.AyaSerializer.CLASS_PRIMCALL; - -public final class PrimSerializer extends JitTeleSerializer { - public PrimSerializer(@NotNull AbstractSerializer parent) { - super(parent, JitPrim.class); - } - @Override protected @NotNull String callClass() { return CLASS_PRIMCALL; } - @Override protected void buildConstructor(PrimDef unit) { - super.buildConstructor(unit, ImmutableSeq.of("org.aya.syntax.core.def.PrimDef.ID." + unit.id.name())); - } - @Override public PrimSerializer serialize(PrimDef unit) { - buildFramework(unit, () -> { }); - return this; - } -} diff --git a/jit-compiler/src/main/java/org/aya/compiler/SourceBuilder.java b/jit-compiler/src/main/java/org/aya/compiler/SourceBuilder.java index 3f7ee66f8..07c4afb1a 100644 --- a/jit-compiler/src/main/java/org/aya/compiler/SourceBuilder.java +++ b/jit-compiler/src/main/java/org/aya/compiler/SourceBuilder.java @@ -3,66 +3,67 @@ package org.aya.compiler; import kala.collection.immutable.ImmutableSeq; +import kala.collection.immutable.primitive.ImmutableIntSeq; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.function.Consumer; +import java.util.function.IntConsumer; -import static org.aya.compiler.AyaSerializer.CLASS_PANIC; -import static org.aya.compiler.ExprializeUtils.getJavaRef; -import static org.aya.compiler.ExprializeUtils.makeString; +import static org.aya.compiler.serializers.AyaSerializer.CLASS_PANIC; +import static org.aya.compiler.serializers.ExprializeUtil.makeString; -public interface SourceBuilder { - final class Default implements SourceBuilder { - private final @NotNull StringBuilder builder; - private int indent; - private final @NotNull NameGenerator nameGenerator; +public final class SourceBuilder { + public final @NotNull StringBuilder builder; + public final @NotNull NameGenerator nameGen; + private int indent; + private boolean isLineBegin; + + public SourceBuilder() { + this.builder = new StringBuilder(); + this.nameGen = new NameGenerator(); + this.indent = 0; + this.isLineBegin = true; + } + + public record JitParam(@NotNull String name, @NotNull String type) { } - public Default() { - this(new StringBuilder(), 0, new NameGenerator()); - } + private void assertLineBegin() { + assert isLineBegin; + } - public Default(@NotNull StringBuilder builder, int indent, @NotNull NameGenerator nameGenerator) { - this.builder = builder; - this.indent = indent; - this.nameGenerator = nameGenerator; - } - @Override public @NotNull StringBuilder builder() { return builder; } - @Override public int indent() { return indent; } - @Override public @NotNull NameGenerator nameGen() { return nameGenerator; } - @Override public void runInside(@NotNull Runnable runnable) { - indent++; - runnable.run(); - indent--; - } + public void runInside(@NotNull Runnable runnable) { + indent++; + runnable.run(); + indent--; } - @NotNull StringBuilder builder(); - int indent(); - @NotNull NameGenerator nameGen(); - default void fillIndent() { - if (indent() == 0) return; - builder().append(" ".repeat(indent())); + public int indent() { + return this.indent; } - void runInside(@NotNull Runnable runnable); + private void fillIndent() { + assertLineBegin(); + if (indent() == 0) return; + builder.append(" ".repeat(indent())); + } - default @NotNull String buildLocalVar(@NotNull String type, @NotNull String name, @Nullable String initial) { + public @NotNull String buildLocalVar(@NotNull String type, @NotNull String name, @Nullable String initial) { var update = initial == null ? "" : " = " + initial; appendLine(type + " " + name + update + ";"); return name; } - default void buildUpdate(@NotNull String lhs, @NotNull String rhs) { + public void buildUpdate(@NotNull String lhs, @NotNull String rhs) { appendLine(lhs + " = " + rhs + ";"); } - default void buildIf(@NotNull String condition, @NotNull Runnable onSucc) { + public void buildIf(@NotNull String condition, @NotNull Runnable onSucc) { buildIfElse(condition, onSucc, null); } - default void buildIfElse(@NotNull String condition, @NotNull Runnable onSucc, @Nullable Runnable onFailed) { + public void buildIfElse(@NotNull String condition, @NotNull Runnable onSucc, @Nullable Runnable onFailed) { appendLine("if (" + condition + ") {"); runInside(onSucc); if (onFailed == null) appendLine("}"); @@ -78,42 +79,39 @@ default void buildIfElse(@NotNull String condition, @NotNull Runnable onSucc, @N * * @param onSucc the argument is a local variable that has type {@param type} and identical equal to {@param term}; */ - default void buildIfInstanceElse( + public void buildIfInstanceElse( @NotNull String term, @NotNull String type, @NotNull Consumer onSucc, @Nullable Runnable onFailed ) { - String name = nameGen().nextName(); + String name = nameGen.nextName(); buildIfElse(term + " instanceof " + type + " " + name, () -> onSucc.accept(name), onFailed); } - default void buildGoto(@NotNull Runnable continuation) { + public void buildGoto(@NotNull Runnable continuation) { appendLine("do {"); runInside(continuation); appendLine("} while (false);"); } - default void buildBreak() { appendLine("break;"); } - default void buildReturn(@NotNull String retWith) { appendLine("return " + retWith + ";"); } - default void buildComment(@NotNull String comment) { appendLine("// " + comment); } - default void buildPanic(@Nullable String message) { + + public void buildBreak() { appendLine("break;"); } + public void buildReturn(@NotNull String retWith) { appendLine("return " + retWith + ";"); } + public void buildComment(@NotNull String comment) { appendLine("// " + comment); } + public void buildPanic(@Nullable String message) { message = message == null ? "" : makeString(message); appendLine("throw new " + CLASS_PANIC + "(" + message + ");"); } - default void buildInnerClass(@NotNull String className, @Nullable Class superClass, @NotNull Runnable continuation) { - buildClass(className, superClass, true, continuation); - } - - default void buildClass( + public void buildClass( @NotNull String className, - @Nullable Class superClass, + @Nullable String superClass, boolean isStatic, @NotNull Runnable continuation ) { - var ext = superClass == null ? "" : "extends " + getJavaRef(superClass); + var ext = superClass == null ? "" : "extends " + superClass; appendLine("public " + (isStatic ? "static" : "") + " final class " + className + " " + ext + " {"); runInside(continuation); @@ -124,48 +122,65 @@ default void buildClass( return ImmutableSeq.fill(size, idx -> term + ".get(" + idx + ")"); } - default void appendLine(@NotNull String string) { + public void appendLine(@NotNull String string) { fillIndent(); - builder().append(string); - builder().append('\n'); + builder.append(string); + appendLine(); } - default void appendLine() { builder().append('\n'); } - default void buildConstantField( + + public void append(@NotNull String string) { + if (isLineBegin) fillIndent(); + isLineBegin = false; + builder.append(string); + } + + public void appendLine() { + builder.append('\n'); + isLineBegin = true; + } + + public void buildConstantField( @NotNull String type, @NotNull String name, - @NotNull String value + @Nullable String value ) { - appendLine("public static final " + type + " " + name + " = " + value + ";"); + if (value != null) { + value = " = " + value; + } else { + value = ""; + } + + appendLine("public static final " + type + " " + name + value + ";"); } - default void buildSwitch( + public void buildSwitch( @NotNull String term, - @NotNull ImmutableSeq cases, - @NotNull Consumer continuation, - @NotNull Runnable defaultCase + @NotNull ImmutableIntSeq cases, + @NotNull IntConsumer continuation, + @NotNull Runnable publicCase ) { if (cases.isEmpty()) { - defaultCase.run(); + publicCase.run(); return; } appendLine("switch (" + term + ") {"); runInside(() -> { - for (var kase : cases) { + cases.forEach(kase -> { appendLine("case " + kase + " -> {"); runInside(() -> continuation.accept(kase)); appendLine("}"); - } + }); appendLine("default -> {"); - runInside(defaultCase); + runInside(publicCase); appendLine("}"); }); appendLine("}"); } - default void buildMethod( + public void buildMethod( @NotNull String name, - @NotNull ImmutableSeq params, + @NotNull ImmutableSeq params, @NotNull String returnType, boolean override, @NotNull Runnable continuation diff --git a/jit-compiler/src/main/java/org/aya/compiler/TermExprializer.java b/jit-compiler/src/main/java/org/aya/compiler/TermExprializer.java deleted file mode 100644 index 6bdf621b3..000000000 --- a/jit-compiler/src/main/java/org/aya/compiler/TermExprializer.java +++ /dev/null @@ -1,325 +0,0 @@ -// 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.compiler; - -import com.intellij.openapi.util.text.StringUtil; -import kala.collection.SeqView; -import kala.collection.immutable.ImmutableSeq; -import kala.collection.mutable.MutableMap; -import org.aya.generic.stmt.Shaped; -import org.aya.generic.term.DTKind; -import org.aya.generic.term.SortKind; -import org.aya.prettier.FindUsage; -import org.aya.syntax.compile.JitFn; -import org.aya.syntax.core.Closure; -import org.aya.syntax.core.def.FnDef; -import org.aya.syntax.core.term.*; -import org.aya.syntax.core.term.call.*; -import org.aya.syntax.core.term.marker.TyckInternal; -import org.aya.syntax.core.term.repr.*; -import org.aya.syntax.core.term.xtt.*; -import org.aya.syntax.ref.LocalVar; -import org.aya.util.error.Panic; -import org.jetbrains.annotations.NotNull; - -import java.util.function.Function; - -import static org.aya.compiler.AyaSerializer.*; -import static org.aya.compiler.ExprializeUtils.*; -import static org.aya.compiler.NameSerializer.getClassRef; - -/** - * Build the "constructor form" of {@link Term}, but in Java. - */ -public final class TermExprializer extends AbstractExprializer { - public static final String CLASS_LAMTERM = ExprializeUtils.getJavaRef(LamTerm.class); - public static final String CLASS_JITLAMTERM = ExprializeUtils.getJavaRef(Closure.Jit.class); - public static final String CLASS_APPTERM = ExprializeUtils.getJavaRef(AppTerm.class); - public static final String CLASS_SORTKIND = ExprializeUtils.getJavaRef(SortKind.class); - public static final String CLASS_INTOPS = ExprializeUtils.getJavaRef(IntegerOps.class); - public static final String CLASS_LISTOPS = ExprializeUtils.getJavaRef(ListOps.class); - public static final String CLASS_INTEGER = ExprializeUtils.getJavaRef(IntegerTerm.class); - public static final String CLASS_LIST = ExprializeUtils.getJavaRef(ListTerm.class); - public static final String CLASS_STRING = ExprializeUtils.getJavaRef(StringTerm.class); - public static final String CLASS_LOCALTERM = ExprializeUtils.getJavaRef(LocalTerm.class); - public static final String CLASS_INT_CONRULE = ExprializeUtils.makeSub(CLASS_INTOPS, ExprializeUtils.getJavaRef(IntegerOps.ConRule.class)); - public static final String CLASS_INT_FNRULE = ExprializeUtils.makeSub(CLASS_INTOPS, ExprializeUtils.getJavaRef(IntegerOps.FnRule.class)); - public static final String CLASS_LIST_CONRULE = ExprializeUtils.makeSub(CLASS_LISTOPS, ExprializeUtils.getJavaRef(ListOps.ConRule.class)); - public static final String CLASS_FNRULE_KIND = ExprializeUtils.makeSub(CLASS_INT_FNRULE, ExprializeUtils.getJavaRef(IntegerOps.FnRule.Kind.class)); - public static final String CLASS_RULEREDUCER = ExprializeUtils.getJavaRef(RuleReducer.class); - public static final String CLASS_RULE_CON = ExprializeUtils.makeSub(CLASS_RULEREDUCER, ExprializeUtils.getJavaRef(RuleReducer.Con.class)); - public static final String CLASS_RULE_FN = ExprializeUtils.makeSub(CLASS_RULEREDUCER, ExprializeUtils.getJavaRef(RuleReducer.Fn.class)); - public static final String CLASS_NEW = ExprializeUtils.getJavaRef(NewTerm.class); - public static final String CLASS_MEMCALL = ExprializeUtils.getJavaRef(MemberCall.class); - public static final String CLASS_CASTTERM = ExprializeUtils.getJavaRef(ClassCastTerm.class); - public static final String CLASS_CLSCALL = ExprializeUtils.getJavaRef(ClassCall.class); - public static final String CLASS_CLOSURE = ExprializeUtils.getJavaRef(Closure.class); - public static final String CLASS_MATCHTERM = ExprializeUtils.getJavaRef(MatchTerm.class); - public static final String CLASS_MATCHING = ExprializeUtils.makeSub(CLASS_TERM, getJavaRef(Term.Matching.class)); - - /** - * Terms that should be instantiated - */ - private final @NotNull ImmutableSeq instantiates; - private final @NotNull MutableMap binds; - - /** - * Whether allow LocalTerm, false in default (in order to report unexpected LocalTerm) - */ - private final boolean allowLocalTerm; - - public TermExprializer(@NotNull NameGenerator nameGen, @NotNull ImmutableSeq instantiates) { - this(nameGen, instantiates, false); - } - - public TermExprializer(@NotNull NameGenerator nameGen, @NotNull ImmutableSeq instantiates, boolean allowLocalTer) { - super(nameGen); - this.instantiates = instantiates; - this.allowLocalTerm = allowLocalTer; - this.binds = MutableMap.create(); - } - - private @NotNull String serializeApplicable(@NotNull Shaped.Applicable applicable) { - return switch (applicable) { - case IntegerOps.ConRule conRule -> - ExprializeUtils.makeNew(CLASS_INT_CONRULE, ExprializeUtils.getInstance(NameSerializer.getClassRef(conRule.ref())), - doSerialize(conRule.zero()) - ); - case IntegerOps.FnRule fnRule -> ExprializeUtils.makeNew(CLASS_INT_FNRULE, - ExprializeUtils.getInstance(NameSerializer.getClassRef(fnRule.ref())), - ExprializeUtils.makeSub(CLASS_FNRULE_KIND, fnRule.kind().toString()) - ); - case ListOps.ConRule conRule -> ExprializeUtils.makeNew(CLASS_LIST_CONRULE, - ExprializeUtils.getInstance(NameSerializer.getClassRef(conRule.ref())), - doSerialize(conRule.empty()) - ); - default -> Panic.unreachable(); - }; - } - - /** - * This code requires that {@link FnCall}, {@link RuleReducer.Fn} and {@link RuleReducer.Con} - * {@code ulift} is the second parameter, {@code args.get(i)} is the {@code i + 3}th parameter - * - * @param fixed whether {@param reducible} has fixed `invoke` - */ - private @NotNull String buildReducibleCall( - @NotNull String reducible, - @NotNull String callName, - int ulift, - @NotNull ImmutableSeq> args, - boolean fixed - ) { - var seredArgs = args.map(x -> x.map(this::doSerialize)); - var seredSeq = seredArgs.map(x -> ExprializeUtils.makeImmutableSeq(CLASS_TERM, x)); - var flatArgs = seredArgs.flatMap(x -> x); - - var callArgs = new String[seredSeq.size() + 2]; - callArgs[0] = reducible; - callArgs[1] = "0"; // elevate later - for (var i = 0; i < seredSeq.size(); ++i) { - callArgs[i + 2] = seredSeq.get(i); - } - - var elevate = ulift > 0 ? ".elevate(" + ulift + ")" : ""; - var onStuck = makeThunk(ExprializeUtils.makeNew(callName, callArgs)); - var finalArgs = fixed - ? flatArgs.view().prepended(onStuck).joinToString() - : onStuck + ", " + ExprializeUtils.makeImmutableSeq(CLASS_TERM, flatArgs); - - return reducible + ".invoke(" + finalArgs + ")" + elevate; - } - - @Override protected @NotNull String doSerialize(@NotNull Term term) { - return switch (term) { - case FreeTerm(var bind) -> { - // It is possible that we meet bind here, - // the serializer will instantiate some variable while serializing LamTerm - var subst = binds.getOrNull(bind); - if (subst == null) { - throw new Panic("No substitution for " + bind + " during serialization"); - } - - yield subst; - } - case TyckInternal i -> throw new Panic(i.getClass().toString()); - case Callable.SharableCall call when call.ulift() == 0 && call.args().isEmpty() -> - ExprializeUtils.getEmptyCallTerm(NameSerializer.getClassRef(call.ref())); - case ClassCall(var ref, var ulift, var args) -> ExprializeUtils.makeNew(CLASS_CLSCALL, - getInstance(NameSerializer.getClassRef(ref)), - Integer.toString(ulift), - serializeClosureToImmutableSeq(args) - ); - case MemberCall(var of, var ref, var ulift, var args) -> ExprializeUtils.makeNew(CLASS_MEMCALL, - doSerialize(of), - ExprializeUtils.getInstance(NameSerializer.getClassRef(ref)), - Integer.toString(ulift), - serializeToImmutableSeq(CLASS_TERM, args) - ); - case AppTerm appTerm -> makeAppNew(CLASS_APPTERM, appTerm.fun(), appTerm.arg()); - case LocalTerm _ when !allowLocalTerm -> throw new Panic("LocalTerm"); - case LocalTerm(var index) -> ExprializeUtils.makeNew(CLASS_LOCALTERM, Integer.toString(index)); - case LamTerm lamTerm -> ExprializeUtils.makeNew(CLASS_LAMTERM, serializeClosure(lamTerm.body())); - case DataCall(var ref, var ulift, var args) -> ExprializeUtils.makeNew(CLASS_DATACALL, - ExprializeUtils.getInstance(NameSerializer.getClassRef(ref)), - Integer.toString(ulift), - serializeToImmutableSeq(CLASS_TERM, args) - ); - case ConCall(var head, var args) -> ExprializeUtils.makeNew(CLASS_CONCALL, - ExprializeUtils.getInstance(NameSerializer.getClassRef(head.ref())), - serializeToImmutableSeq(CLASS_TERM, head.ownerArgs()), - Integer.toString(head.ulift()), - serializeToImmutableSeq(CLASS_TERM, args) - ); - case FnCall call -> { - var ref = switch (call.ref()) { - case JitFn jit -> ExprializeUtils.getInstance(NameSerializer.getClassRef(jit)); - case FnDef.Delegate def -> ExprializeUtils.getInstance(getClassRef(def.ref)); - }; - - var args = call.args(); - yield buildReducibleCall(ref, CLASS_FNCALL, call.ulift(), ImmutableSeq.of(args), true); - } - case RuleReducer.Con conRuler -> buildReducibleCall( - serializeApplicable(conRuler.rule()), - CLASS_RULE_CON, conRuler.ulift(), - ImmutableSeq.of(conRuler.ownerArgs(), conRuler.conArgs()), - false - ); - case RuleReducer.Fn fnRuler -> buildReducibleCall( - serializeApplicable(fnRuler.rule()), - CLASS_RULE_FN, fnRuler.ulift(), - ImmutableSeq.of(fnRuler.args()), - false - ); - case SortTerm sort when sort.equals(SortTerm.Type0) -> ExprializeUtils.makeSub( - ExprializeUtils.getJavaRef(SortTerm.class), "Type0"); - case SortTerm sort when sort.equals(SortTerm.ISet) -> ExprializeUtils.makeSub( - ExprializeUtils.getJavaRef(SortTerm.class), "ISet"); - case SortTerm(var kind, var ulift) -> ExprializeUtils.makeNew(ExprializeUtils.getJavaRef(SortTerm.class), - ExprializeUtils.makeEnum(CLASS_SORTKIND, kind), - Integer.toString(ulift)); - case DepTypeTerm(var kind, var param, var body) -> ExprializeUtils.makeNew( - ExprializeUtils.getJavaRef(DepTypeTerm.class), - ExprializeUtils.makeEnum(ExprializeUtils.getJavaRef(DTKind.class), kind), - doSerialize(param), - serializeClosure(body) - ); - case CoeTerm(var type, var r, var s) -> ExprializeUtils.makeNew(ExprializeUtils.getJavaRef(CoeTerm.class), - serializeClosure(type), - doSerialize(r), - doSerialize(s) - ); - case ProjTerm(var of, var fst) -> ExprializeUtils.makeNew(ExprializeUtils.getJavaRef(ProjTerm.class), - doSerialize(of), - Boolean.toString(fst) - ); - case PAppTerm(var fun, var arg, var a, var b) -> makeAppNew(ExprializeUtils.getJavaRef(PAppTerm.class), - fun, arg, a, b - ); - case EqTerm(var A, var a, var b) -> ExprializeUtils.makeNew(ExprializeUtils.getJavaRef(EqTerm.class), - serializeClosure(A), - doSerialize(a), doSerialize(b) - ); - case DimTyTerm _ -> ExprializeUtils.getInstance(ExprializeUtils.getJavaRef(DimTyTerm.class)); - case DimTerm dim -> ExprializeUtils.makeSub(ExprializeUtils.getJavaRef(DimTerm.class), dim.name()); - case TupTerm(var l, var r) -> ExprializeUtils.makeNew(ExprializeUtils.getJavaRef(TupTerm.class), - doSerialize(l), doSerialize(r) - ); - case PrimCall(var ref, var ulift, var args) -> ExprializeUtils.makeNew(CLASS_PRIMCALL, - ExprializeUtils.getInstance(NameSerializer.getClassRef(ref)), - Integer.toString(ulift), - serializeToImmutableSeq(CLASS_TERM, args) - ); - case IntegerTerm(var repr, var zero, var suc, var type) -> ExprializeUtils.makeNew(CLASS_INTEGER, - Integer.toString(repr), - ExprializeUtils.getInstance(NameSerializer.getClassRef(zero)), - ExprializeUtils.getInstance(NameSerializer.getClassRef(suc)), - doSerialize(type) - ); - case ListTerm(var repr, var nil, var cons, var type) -> ExprializeUtils.makeNew(CLASS_LIST, - ExprializeUtils.makeImmutableSeq(CLASS_TERM, repr.map(this::doSerialize), CLASS_PIMMSEQ), - ExprializeUtils.getInstance(NameSerializer.getClassRef(nil)), - ExprializeUtils.getInstance(NameSerializer.getClassRef(cons)), - doSerialize(type) - ); - case StringTerm stringTerm -> ExprializeUtils.makeNew(CLASS_STRING, - ExprializeUtils.makeString(StringUtil.escapeStringCharacters(stringTerm.string()))); - case ClassCastTerm(var classRef, var subterm, var rember, var forgor) -> makeNew(CLASS_CASTTERM, - getInstance(NameSerializer.getClassRef(classRef)), - serialize(subterm), - serializeClosureToImmutableSeq(rember), - serializeClosureToImmutableSeq(forgor) - ); - case MatchTerm(var discr, var type, var clauses) -> ExprializeUtils.makeNew(CLASS_MATCHTERM, - serializeToImmutableSeq(CLASS_TERM, discr), - doSerialize(type), - serializeMatching(clauses) - ); - case NewTerm(var classCall) -> ExprializeUtils.makeNew(CLASS_NEW, doSerialize(classCall)); - }; - } - - private @NotNull String serializeMatching(@NotNull ImmutableSeq matchings) { - var serializer = new PatternExprializer(this.nameGen, false); - return makeImmutableSeq(CLASS_MATCHING, - matchings.map(x -> { - var pats = serializer.serializeToImmutableSeq(CLASS_PAT, x.patterns()); - var bindCount = Integer.toString(x.bindCount()); - var localTerms = ImmutableSeq.fill(x.bindCount(), LocalTerm::new); - var tmpSerializer = new TermExprializer(nameGen, ImmutableSeq.empty(), true); - var serLocalTerms = localTerms.map(tmpSerializer::serialize); - var body = withMany(serLocalTerms, vars -> { - // 0th term for index 0, so it is de bruijn index order instead of telescope order - var freeBody = x.body().instantiateAll(SeqView.narrow(vars.view())); - return doSerialize(freeBody); - }); - - return makeNew(CLASS_MATCHING, pats, bindCount, body); - })); - } - - // def f (A : Type) : Fn (a : A) -> A - // (A : Type) : Pi(^0, IdxClosure(^1)) - // (A : Type) : Pi(^0, JitClosure(_ -> ^1)) - - private @NotNull String withMany(@NotNull ImmutableSeq subst, @NotNull Function, String> continuation) { - var binds = subst.map(LocalVar::new); - binds.forEachWith(subst, this.binds::put); - var result = continuation.apply(binds.map(FreeTerm::new)); - binds.forEach(this.binds::remove); - return result; - } - - private @NotNull String with(@NotNull String subst, @NotNull Function continuation) { - return withMany(ImmutableSeq.of(subst), xs -> continuation.apply(xs.getFirst())); - } - - private @NotNull String serializeClosureToImmutableSeq(@NotNull ImmutableSeq cls) { - return makeImmutableSeq(CLASS_CLOSURE, cls.map(this::serializeClosure)); - } - - private @NotNull String serializeClosure(@NotNull Closure body) { - var param = nameGen.nextName(); - return with(param, t -> { - if (body instanceof Closure.Const(var inside)) return serializeConst(inside); - var appliedBody = body.apply(t); - if (FindUsage.free(appliedBody, t.name()) > 0) - return makeNew(CLASS_JITLAMTERM, param + " -> " + doSerialize(appliedBody)); - else return serializeConst(appliedBody); - }); - } - - private @NotNull String serializeConst(Term appliedBody) { - return CLASS_CLOSURE + ".mkConst(" + doSerialize(appliedBody) + ")"; - } - - @Override public @NotNull String serialize(Term unit) { - binds.clear(); - var vars = ImmutableSeq.fill(instantiates.size(), i -> new LocalVar("arg" + i)); - unit = unit.instantiateTeleVar(vars.view()); - vars.forEachWith(instantiates, binds::put); - - return doSerialize(unit); - } -} diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/ArgumentProvider.java b/jit-compiler/src/main/java/org/aya/compiler/free/ArgumentProvider.java new file mode 100644 index 000000000..1017812fc --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/free/ArgumentProvider.java @@ -0,0 +1,14 @@ +// 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.compiler.free; + +import org.aya.compiler.free.data.LocalVariable; +import org.jetbrains.annotations.NotNull; + +public interface ArgumentProvider { + interface Lambda extends ArgumentProvider { + @NotNull FreeJavaExpr capture(int nth); + } + + @NotNull LocalVariable arg(int nth); +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/Constants.java b/jit-compiler/src/main/java/org/aya/compiler/free/Constants.java new file mode 100644 index 000000000..9af68dc0b --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/free/Constants.java @@ -0,0 +1,248 @@ +// 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.compiler.free; + +import kala.collection.Seq; +import kala.collection.immutable.ImmutableSeq; +import kala.collection.immutable.ImmutableTreeSeq; +import kala.collection.mutable.MutableSeq; +import kala.control.Result; +import org.aya.compiler.free.data.FieldRef; +import org.aya.compiler.free.data.MethodRef; +import org.aya.generic.stmt.Reducible; +import org.aya.syntax.compile.JitClass; +import org.aya.syntax.compile.JitCon; +import org.aya.syntax.compile.JitData; +import org.aya.syntax.compile.JitMember; +import org.aya.syntax.core.Closure; +import org.aya.syntax.core.pat.PatMatcher; +import org.aya.syntax.core.term.Term; +import org.aya.syntax.core.term.TupTerm; +import org.aya.syntax.core.term.call.ConCallLike; +import org.aya.syntax.core.term.marker.BetaRedex; +import org.aya.syntax.core.term.repr.IntegerTerm; +import org.aya.util.error.Panic; +import org.jetbrains.annotations.NotNull; + +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; + +public final class Constants { + private Constants() { } + + public static final @NotNull ClassDesc CD_Term = FreeUtil.fromClass(Term.class); + public static final @NotNull ClassDesc CD_Seq = FreeUtil.fromClass(Seq.class); + public static final @NotNull ClassDesc CD_ImmutableSeq = FreeUtil.fromClass(ImmutableSeq.class); + public static final @NotNull ClassDesc CD_MutableSeq = FreeUtil.fromClass(MutableSeq.class); + public static final @NotNull ClassDesc CD_Thunk = FreeUtil.fromClass(Supplier.class); + public static final @NotNull ClassDesc CD_Result = FreeUtil.fromClass(Result.class); + + // Term -> Term + public static final @NotNull MethodRef CLOSURE = new MethodRef.Default( + FreeUtil.fromClass(UnaryOperator.class), + "apply", + CD_Term, ImmutableSeq.of(CD_Term), + true + ); + + // () -> Term + public static final @NotNull MethodRef THUNK = new MethodRef.Default( + FreeUtil.fromClass(Supplier.class), + "get", + CD_Term, ImmutableSeq.empty(), + true + ); + + // ImmutableSeq from(Object[]) + public static final @NotNull MethodRef IMMSEQ = new MethodRef.Default( + CD_ImmutableSeq, + "from", + CD_ImmutableSeq, ImmutableSeq.of(ConstantDescs.CD_Object.arrayType()), + true + ); + + /** + * @see MutableSeq#fill(int, Object) + */ + public static final @NotNull MethodRef MUTSEQ = new MethodRef.Default( + CD_MutableSeq, + "fill", + CD_MutableSeq, ImmutableSeq.of(ConstantDescs.CD_int, ConstantDescs.CD_Object), + true + ); + + /** + * @see MutableSeq#set(int, Object) + */ + public static final @NotNull MethodRef MUTSEQ_SET = new MethodRef.Default( + CD_MutableSeq, "set", ConstantDescs.CD_void, + ImmutableSeq.of(ConstantDescs.CD_int, ConstantDescs.CD_Object), + true + ); + + /** + * Remember to {@code checkcast} the result value!! + * + * @see Seq#get(int) + */ + public static final @NotNull MethodRef SEQ_GET = new MethodRef.Default( + CD_Seq, "get", ConstantDescs.CD_Object, + ImmutableSeq.of(ConstantDescs.CD_int), + true + ); + + /** + * @see Seq#toImmutableSeq() + */ + public static final @NotNull MethodRef SEQ_TOIMMSEQ = new MethodRef.Default( + CD_Seq, "toImmutableSeq", CD_ImmutableSeq, ImmutableSeq.empty(), true + ); + + public static final @NotNull MethodRef IMMTREESEQ = new MethodRef.Default( + FreeUtil.fromClass(ImmutableTreeSeq.class), + "from", + FreeUtil.fromClass(ImmutableTreeSeq.class), + ImmutableSeq.of(ConstantDescs.CD_Object.arrayType()), + false + ); + + public static final @NotNull MethodRef BETAMAKE = new MethodRef.Default( + FreeUtil.fromClass(BetaRedex.class), + "make", + CD_Term, ImmutableSeq.empty(), + true + ); + + /** + * @see Term#elevate(int) + */ + public static final @NotNull MethodRef ELEVATE = new MethodRef.Default( + CD_Term, "elevate", CD_Term, ImmutableSeq.of(ConstantDescs.CD_int), true + ); + + /** + * @see Reducible#invoke(Supplier, Seq) + */ + public static final @NotNull MethodRef REDUCIBLE_INVOKE = new MethodRef.Default( + FreeUtil.fromClass(Reducible.class), "invoke", + CD_Term, ImmutableSeq.of(FreeUtil.fromClass(Supplier.class), CD_Seq), true + ); + + /** + * @see Closure#mkConst(Term) + */ + public static final @NotNull MethodRef CLOSURE_MKCONST = new MethodRef.Default( + FreeUtil.fromClass(Closure.class), + "mkConst", + FreeUtil.fromClass(Closure.class), + ImmutableSeq.of(CD_Term), + true + ); + + /** + * @see Panic#unreachable() + */ + public static final @NotNull MethodRef PANIC = new MethodRef.Default( + FreeUtil.fromClass(Panic.class), + "unreachable", + ConstantDescs.CD_Object, + ImmutableSeq.empty(), + true + ); + + public static final @NotNull MethodRef INT_REPR = new MethodRef.Default( + FreeUtil.fromClass(IntegerTerm.class), + "repr", + ConstantDescs.CD_int, + ImmutableSeq.empty(), + false + ); + + /** + * @see ConCallLike#conArgs() + */ + public static final @NotNull MethodRef CONARGS = new MethodRef.Default( + FreeUtil.fromClass(ConCallLike.class), + "conArgs", + CD_ImmutableSeq, + ImmutableSeq.empty(), + true + ); + + /** + * @see TupTerm#lhs() + */ + public static final @NotNull MethodRef TUP_LHS = new MethodRef.Default( + FreeUtil.fromClass(TupTerm.class), + "lhs", + CD_Term, + ImmutableSeq.empty(), + false + ); + + /** + * @see TupTerm#rhs() + */ + public static final @NotNull MethodRef TUP_RHS = new MethodRef.Default( + FreeUtil.fromClass(TupTerm.class), + "rhs", + CD_Term, + ImmutableSeq.empty(), + false + ); + + /** + * @see Result#ok(Object) + */ + public static final @NotNull MethodRef RESULT_OK = new MethodRef.Default( + CD_Result, "ok", + CD_Result, ImmutableSeq.of(ConstantDescs.CD_Object), + true + ); + + public static final @NotNull MethodRef RESULT_ERR = new MethodRef.Default( + CD_Result, "err", + CD_Result, ImmutableSeq.of(ConstantDescs.CD_Object), + true + ); + + /** + * @see org.aya.syntax.telescope.JitTele#JitTele(int, boolean[], String[]) + */ + public static final @NotNull ImmutableSeq JIT_TELE_CON_PARAMS = ImmutableSeq.of( + ConstantDescs.CD_int, ConstantDescs.CD_boolean.arrayType(), ConstantDescs.CD_String.arrayType() + ); + + public static final @NotNull FieldRef JITDATA_CONS = new FieldRef.Default( + FreeUtil.fromClass(JitData.class), + FreeUtil.fromClass(JitCon.class).arrayType(), + "constructors" + ); + + public static final @NotNull FieldRef JITCLASS_MEMS = new FieldRef.Default( + FreeUtil.fromClass(JitClass.class), + FreeUtil.fromClass(JitMember.class).arrayType(), + "members" + ); + + /** + * @see UnaryOperator#identity() + */ + public static final @NotNull MethodRef CLOSURE_ID = new MethodRef.Default( + FreeUtil.fromClass(UnaryOperator.class), + "identity", + FreeUtil.fromClass(UnaryOperator.class), + ImmutableSeq.empty(), + true + ); + + /** + * @see PatMatcher#apply(ImmutableSeq, ImmutableSeq) + */ + public static final @NotNull MethodRef PATMATCHER_APPLY = new MethodRef.Default( + FreeUtil.fromClass(PatMatcher.class), "apply", + CD_Result, ImmutableSeq.of(CD_ImmutableSeq, CD_ImmutableSeq), false + ); +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/FreeClassBuilder.java b/jit-compiler/src/main/java/org/aya/compiler/free/FreeClassBuilder.java new file mode 100644 index 000000000..0e1e77526 --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/free/FreeClassBuilder.java @@ -0,0 +1,48 @@ +// 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.compiler.free; + +import kala.collection.immutable.ImmutableSeq; +import org.aya.compiler.free.data.FieldRef; +import org.aya.compiler.free.data.MethodRef; +import org.aya.syntax.compile.CompiledAya; +import org.jetbrains.annotations.NotNull; + +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +public interface FreeClassBuilder { + static @NotNull MethodRef makeConstructorRef(@NotNull ClassDesc owner, @NotNull ImmutableSeq parameterTypes) { + return new MethodRef.Default(owner, ConstantDescs.INIT_NAME, ConstantDescs.CD_void, parameterTypes, false); + } + + void buildNestedClass( + CompiledAya compiledAya, + @NotNull String name, + @NotNull Class superclass, + @NotNull Consumer builder + ); + + @NotNull MethodRef buildMethod( + @NotNull ClassDesc returnType, + @NotNull String name, + @NotNull ImmutableSeq paramTypes, + @NotNull BiConsumer builder + ); + + @NotNull MethodRef buildConstructor( + @NotNull ImmutableSeq paramTypes, + @NotNull BiConsumer builder + ); + + @NotNull FieldRef buildConstantField( + @NotNull ClassDesc returnType, + @NotNull String name, + @NotNull Function initializer + ); + + @NotNull FreeJavaResolver resolver(); +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/FreeCodeBuilder.java b/jit-compiler/src/main/java/org/aya/compiler/free/FreeCodeBuilder.java new file mode 100644 index 000000000..e7e599df1 --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/free/FreeCodeBuilder.java @@ -0,0 +1,68 @@ +// 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.compiler.free; + +import kala.collection.immutable.ImmutableSeq; +import kala.collection.immutable.primitive.ImmutableIntSeq; +import org.aya.compiler.free.data.FieldRef; +import org.aya.compiler.free.data.LocalVariable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.constant.ClassDesc; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.ObjIntConsumer; + +public interface FreeCodeBuilder extends FreeExprBuilder { + @NotNull LocalVariable makeVar(@NotNull ClassDesc type, @Nullable FreeJavaExpr initializer); + + default LocalVariable makeVar(@NotNull Class type, @Nullable FreeJavaExpr initializer) { + return makeVar(FreeUtil.fromClass(type), initializer); + } + + void invokeSuperCon(@NotNull ImmutableSeq superConParams, @NotNull ImmutableSeq superConArgs); + + void updateVar(@NotNull LocalVariable var, @NotNull FreeJavaExpr update); + + void updateArray(@NotNull FreeJavaExpr array, int idx, @NotNull FreeJavaExpr update); + + void updateField(@NotNull FieldRef field, @NotNull FreeJavaExpr update); + + void updateField(@NotNull FieldRef field, @NotNull FreeJavaExpr owner, @NotNull FreeJavaExpr update); + + void ifNotTrue(@NotNull LocalVariable notTrue, @NotNull Consumer thenBlock, @Nullable Consumer elseBlock); + + void ifTrue(@NotNull LocalVariable theTrue, @NotNull Consumer thenBlock, @Nullable Consumer elseBlock); + + void ifInstanceOf(@NotNull FreeJavaExpr lhs, @NotNull ClassDesc rhs, @NotNull BiConsumer thenBlock, @Nullable Consumer elseBlock); + + void ifIntEqual(@NotNull FreeJavaExpr lhs, int rhs, @NotNull Consumer thenBlock, @Nullable Consumer elseBlock); + + void ifRefEqual(@NotNull FreeJavaExpr lhs, @NotNull FreeJavaExpr rhs, @NotNull Consumer thenBlock, @Nullable Consumer elseBlock); + + void ifNull(@NotNull FreeJavaExpr isNull, @NotNull Consumer thenBlock, @Nullable Consumer elseBlock); + + /** + * Construct a code block that can jump out + */ + void breakable(@NotNull Consumer innerBlock); + void breakOut(); + + /** + * Turns an expression to a statement + */ + void exec(@NotNull FreeJavaExpr expr); + + /** + * Build a switch statement on int + */ + void switchCase( + @NotNull LocalVariable elim, + @NotNull ImmutableIntSeq cases, + @NotNull ObjIntConsumer branch, + @NotNull Consumer defaultCase + ); + + void returnWith(@NotNull FreeJavaExpr expr); +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/FreeExprBuilder.java b/jit-compiler/src/main/java/org/aya/compiler/free/FreeExprBuilder.java new file mode 100644 index 000000000..c51252ef2 --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/free/FreeExprBuilder.java @@ -0,0 +1,98 @@ +// 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.compiler.free; + +import kala.collection.immutable.ImmutableSeq; +import org.aya.compiler.free.data.FieldRef; +import org.aya.compiler.free.data.LocalVariable; +import org.aya.compiler.free.data.MethodRef; +import org.jetbrains.annotations.NotNull; + +import java.lang.constant.ClassDesc; +import java.util.Arrays; +import java.util.function.BiConsumer; + +/** + * the result only depends on the {@link FreeCodeBuilder} that this builder derived from + */ +public interface FreeExprBuilder { + @NotNull FreeJavaResolver resolver(); + + /** + * A {@code new} expression on specified constructor. + */ + @NotNull FreeJavaExpr mkNew(@NotNull MethodRef conRef, @NotNull ImmutableSeq args); + + /** + * A {@code new} expression, the class should have only one (public) constructor with parameter count {@code args.size()}. + */ + default @NotNull FreeJavaExpr mkNew(@NotNull Class className, @NotNull ImmutableSeq args) { + var first = Arrays.stream(className.getConstructors()) + .filter(c -> c.getParameterCount() == args.size()) + .findFirst().get(); + + var desc = FreeUtil.fromClass(className); + var conRef = FreeClassBuilder.makeConstructorRef(desc, + Arrays.stream(first.getParameterTypes()) + .map(FreeUtil::fromClass) + .collect(ImmutableSeq.factory())); + return mkNew(conRef, args); + } + + @NotNull FreeJavaExpr refVar(@NotNull LocalVariable name); + + /** + * Invoke a (non-interface) method on {@param owner}. + * Remember to {@link FreeCodeBuilder#exec(FreeJavaExpr)} if you do not need the result! + */ + @NotNull FreeJavaExpr invoke(@NotNull MethodRef method, @NotNull FreeJavaExpr owner, @NotNull ImmutableSeq args); + + /** Invoke a static method */ + @NotNull FreeJavaExpr invoke(@NotNull MethodRef method, @NotNull ImmutableSeq args); + + @NotNull FreeJavaExpr refField(@NotNull FieldRef field); + + @NotNull FreeJavaExpr refField(@NotNull FieldRef field, @NotNull FreeJavaExpr owner); + + @NotNull FreeJavaExpr refEnum(@NotNull ClassDesc enumClass, @NotNull String enumName); + + default @NotNull FreeJavaExpr refEnum(@NotNull Enum value) { + var cd = FreeUtil.fromClass(value.getClass()); + var name = value.name(); + return refEnum(cd, name); + } + + @NotNull FreeJavaExpr mkLambda( + @NotNull ImmutableSeq captures, + @NotNull MethodRef method, + @NotNull BiConsumer builder + ); + + @NotNull FreeJavaExpr iconst(int i); + + @NotNull FreeJavaExpr iconst(boolean b); + + @NotNull FreeJavaExpr aconst(@NotNull String value); + + @NotNull FreeJavaExpr aconstNull(@NotNull ClassDesc type); + + @NotNull FreeJavaExpr thisRef(); + + /** + * Construct an array with given type and have length {@param length} + * + * @param initializer the initializer, the size is either {@code 0} or {@param length}, 0-length means don't initialize + */ + @NotNull FreeJavaExpr mkArray( + @NotNull ClassDesc type, int length, + @NotNull ImmutableSeq initializer + ); + + @NotNull FreeJavaExpr getArray(@NotNull FreeJavaExpr array, int index); + + @NotNull FreeJavaExpr checkcast(@NotNull FreeJavaExpr obj, @NotNull ClassDesc as); + + default FreeJavaExpr checkcast(@NotNull FreeJavaExpr obj, @NotNull Class as) { + return checkcast(obj, FreeUtil.fromClass(as)); + } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/FreeJavaBuilder.java b/jit-compiler/src/main/java/org/aya/compiler/free/FreeJavaBuilder.java new file mode 100644 index 000000000..ec227bd4a --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/free/FreeJavaBuilder.java @@ -0,0 +1,18 @@ +// 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.compiler.free; + +import org.aya.syntax.compile.CompiledAya; +import org.jetbrains.annotations.NotNull; + +import java.lang.constant.ClassDesc; +import java.util.function.Consumer; + +@SuppressWarnings("unused") +public interface FreeJavaBuilder { + @NotNull Carrier buildClass( + @NotNull ClassDesc className, + @NotNull Class superclass, + @NotNull Consumer builder + ); +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/FreeJavaExpr.java b/jit-compiler/src/main/java/org/aya/compiler/free/FreeJavaExpr.java new file mode 100644 index 000000000..d0522e153 --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/free/FreeJavaExpr.java @@ -0,0 +1,8 @@ +// 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.compiler.free; + +/** + * An expression representation of each morphism from free java + */ +public interface FreeJavaExpr { } diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/FreeJavaResolver.java b/jit-compiler/src/main/java/org/aya/compiler/free/FreeJavaResolver.java new file mode 100644 index 000000000..fff9ed8ed --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/free/FreeJavaResolver.java @@ -0,0 +1,69 @@ +// 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.compiler.free; + +import kala.collection.immutable.ImmutableSeq; +import org.aya.compiler.free.data.FieldRef; +import org.aya.compiler.free.data.MethodRef; +import org.aya.util.error.Panic; +import org.jetbrains.annotations.NotNull; + +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.util.Arrays; + +public interface FreeJavaResolver { + /** + * Find a method with given information + */ + @NotNull MethodRef resolve( + @NotNull ClassDesc owner, + @NotNull String name, + @NotNull ClassDesc returnType, + @NotNull ImmutableSeq paramType, + boolean isInterface + ); + + @NotNull FieldRef resolve( + @NotNull ClassDesc owner, + @NotNull String name, + @NotNull ClassDesc returnType + ); + + default @NotNull FieldRef resolve( + @NotNull Class owner, + @NotNull String name + ) { + try { + var field = owner.getField(name); + return resolve(FreeUtil.fromClass(owner), name, FreeUtil.fromClass(field.getType())); + } catch (NoSuchFieldException e) { + throw new Panic(e); + } + } + + /** + * Find the only method with given name + */ + default @NotNull MethodRef resolve(@NotNull Class owner, @NotNull String name, int paramSize) { + if (name.equals(ConstantDescs.INIT_NAME)) { + throw new Panic("use ExprBuilder#newObject instead"); + } + + var found = Arrays.stream(owner.getMethods()) + .filter(m -> m.getName().equals(name) && m.getParameterCount() == paramSize) + .toList(); + + assert found.size() == 1; + + var reallyFound = found.getFirst(); + + return resolve( + FreeUtil.fromClass(owner), + name, + FreeUtil.fromClass(reallyFound.getReturnType()), + ImmutableSeq.from(reallyFound.getParameterTypes()).map(FreeUtil::fromClass), + reallyFound.getDeclaringClass().isInterface() + ); + } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/FreeUtil.java b/jit-compiler/src/main/java/org/aya/compiler/free/FreeUtil.java new file mode 100644 index 000000000..ef5ce32bb --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/free/FreeUtil.java @@ -0,0 +1,15 @@ +// 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.compiler.free; + +import org.jetbrains.annotations.NotNull; + +import java.lang.constant.ClassDesc; + +public final class FreeUtil { + private FreeUtil() { } + + public static @NotNull ClassDesc fromClass(@NotNull Class clazz) { + return ClassDesc.ofDescriptor(clazz.descriptorString()); + } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/data/FieldRef.java b/jit-compiler/src/main/java/org/aya/compiler/free/data/FieldRef.java new file mode 100644 index 000000000..1b6bd24b8 --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/free/data/FieldRef.java @@ -0,0 +1,20 @@ +// 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.compiler.free.data; + +import org.jetbrains.annotations.NotNull; + +import java.lang.constant.ClassDesc; + +public interface FieldRef { + record Default( + @NotNull ClassDesc owner, + @NotNull ClassDesc returnType, + @NotNull String name + ) implements FieldRef { + } + + @NotNull ClassDesc owner(); + @NotNull ClassDesc returnType(); + @NotNull String name(); +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/data/LocalVariable.java b/jit-compiler/src/main/java/org/aya/compiler/free/data/LocalVariable.java new file mode 100644 index 000000000..93858669a --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/free/data/LocalVariable.java @@ -0,0 +1,10 @@ +// 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.compiler.free.data; + +import org.aya.compiler.free.FreeJavaExpr; +import org.jetbrains.annotations.NotNull; + +public interface LocalVariable { + @NotNull FreeJavaExpr ref(); +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/data/MethodRef.java b/jit-compiler/src/main/java/org/aya/compiler/free/data/MethodRef.java new file mode 100644 index 000000000..f8ca330af --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/free/data/MethodRef.java @@ -0,0 +1,26 @@ +// 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.compiler.free.data; + +import kala.collection.immutable.ImmutableSeq; +import org.jetbrains.annotations.NotNull; + +import java.lang.constant.ClassDesc; + +public interface MethodRef { + record Default( + @Override @NotNull ClassDesc owner, + @Override @NotNull String name, + @Override @NotNull ClassDesc returnType, + @Override @NotNull ImmutableSeq paramTypes, + @Override boolean isInterface + ) implements MethodRef { + } + + @NotNull ClassDesc owner(); + @NotNull String name(); + @NotNull ClassDesc returnType(); + @NotNull ImmutableSeq paramTypes(); + + boolean isInterface(); +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/morphism/SourceArgumentProvider.java b/jit-compiler/src/main/java/org/aya/compiler/free/morphism/SourceArgumentProvider.java new file mode 100644 index 000000000..d56fb4edf --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/free/morphism/SourceArgumentProvider.java @@ -0,0 +1,23 @@ +// 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.compiler.free.morphism; + +import kala.collection.immutable.ImmutableSeq; +import org.aya.compiler.free.ArgumentProvider; +import org.aya.compiler.free.FreeJavaExpr; +import org.jetbrains.annotations.NotNull; + +public record SourceArgumentProvider(@NotNull ImmutableSeq names) implements ArgumentProvider { + @Override public @NotNull SourceFreeJavaExpr.BlackBox arg(int nth) { + return new SourceFreeJavaExpr.BlackBox(names.get(nth)); + } + + record Lambda(@NotNull ImmutableSeq captures, + @NotNull ImmutableSeq names) implements ArgumentProvider.Lambda { + @Override public @NotNull FreeJavaExpr capture(int nth) { return captures.get(nth); } + + @Override public @NotNull SourceFreeJavaExpr.BlackBox arg(int nth) { + return new SourceFreeJavaExpr.BlackBox(names.get(nth)); + } + } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/morphism/SourceClassBuilder.java b/jit-compiler/src/main/java/org/aya/compiler/free/morphism/SourceClassBuilder.java new file mode 100644 index 000000000..37aa74d99 --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/free/morphism/SourceClassBuilder.java @@ -0,0 +1,133 @@ +// 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.compiler.free.morphism; + +import kala.collection.immutable.ImmutableSeq; +import org.aya.compiler.SourceBuilder; +import org.aya.compiler.free.*; +import org.aya.compiler.free.data.FieldRef; +import org.aya.compiler.free.data.MethodRef; +import org.aya.compiler.serializers.ExprializeUtil; +import org.aya.syntax.compile.CompiledAya; +import org.aya.syntax.core.repr.CodeShape; +import org.jetbrains.annotations.NotNull; + +import java.lang.constant.ClassDesc; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +import static org.aya.compiler.free.morphism.SourceFreeJavaBuilder.toClassName; +import static org.aya.compiler.free.morphism.SourceFreeJavaBuilder.toClassRef; + +public record SourceClassBuilder( + @NotNull SourceFreeJavaBuilder parent, @NotNull ClassDesc owner, + @NotNull SourceBuilder sourceBuilder) + implements FreeClassBuilder, FreeJavaResolver { + @Override public @NotNull FreeJavaResolver resolver() { return this; } + + private void buildMetadataRecord(@NotNull String name, @NotNull String value, boolean isFirst) { + var prepend = isFirst ? "" : ", "; + sourceBuilder.appendLine(prepend + name + " = " + value); + } + + private void buildMetadata(@NotNull CompiledAya compiledAya) { + sourceBuilder.appendLine("@" + toClassRef(FreeUtil.fromClass(CompiledAya.class)) + "("); + sourceBuilder.runInside(() -> { + buildMetadataRecord("module", SourceCodeBuilder.mkHalfArray( + ImmutableSeq.from(compiledAya.module()).map(ExprializeUtil::makeString) + ), true); + buildMetadataRecord("fileModuleSize", Integer.toString(compiledAya.fileModuleSize()), false); + buildMetadataRecord("name", ExprializeUtil.makeString(compiledAya.name()), false); + buildMetadataRecord("assoc", Integer.toString(compiledAya.assoc()), false); + buildMetadataRecord("shape", Integer.toString(compiledAya.shape()), false); + buildMetadataRecord("recognition", SourceCodeBuilder.mkHalfArray( + ImmutableSeq.from(compiledAya.recognition()).map(x -> + SourceCodeBuilder.makeRefEnum(FreeUtil.fromClass(CodeShape.GlobalId.class), x.name()) + ) + ), false); + }); + sourceBuilder.appendLine(")"); + } + + @Override + public void buildNestedClass( + CompiledAya compiledAya, + @NotNull String name, + @NotNull Class superclass, + @NotNull Consumer builder + ) { + buildMetadata(compiledAya); + this.sourceBuilder.buildClass(name, toClassRef(FreeUtil.fromClass(superclass)), true, () -> + builder.accept(new SourceClassBuilder(parent, owner.nested(name), sourceBuilder))); + } + + private void buildMethod( + @NotNull String returnType, + @NotNull String name, + @NotNull ImmutableSeq paramTypes, + @NotNull BiConsumer builder + ) { + var params = paramTypes.map(x -> + new SourceBuilder.JitParam(sourceBuilder.nameGen.nextName(), toClassRef(x)) + ); + + sourceBuilder.buildMethod(name, params, returnType, false, () -> builder.accept( + new SourceArgumentProvider(params.map(SourceBuilder.JitParam::name)), + new SourceCodeBuilder(this, sourceBuilder) + )); + } + + @Override public @NotNull MethodRef buildMethod( + @NotNull ClassDesc returnType, + @NotNull String name, + @NotNull ImmutableSeq paramTypes, + @NotNull BiConsumer builder + ) { + buildMethod(toClassRef(returnType), name, paramTypes, builder); + return new MethodRef.Default(this.owner, name, returnType, paramTypes, false); + } + + @Override public @NotNull MethodRef buildConstructor( + @NotNull ImmutableSeq paramTypes, + @NotNull BiConsumer builder + ) { + buildMethod( + "/* constructor */", + toClassName(this.owner), + paramTypes, + builder); + + return FreeClassBuilder.makeConstructorRef(this.owner, paramTypes); + } + + @Override public @NotNull FieldRef buildConstantField( + @NotNull ClassDesc returnType, + @NotNull String name, + @NotNull Function initializer + ) { + sourceBuilder.append("public static final " + toClassRef(returnType) + " " + name + " = "); + var codeBuilder = new SourceCodeBuilder(this, sourceBuilder); + var initValue = initializer.apply(codeBuilder); + codeBuilder.appendExpr(initValue); + sourceBuilder.append(";"); + sourceBuilder.appendLine(); + + return new FieldRef.Default(this.owner, returnType, name); + } + + @Override public @NotNull MethodRef resolve( + @NotNull ClassDesc owner, + @NotNull String name, + @NotNull ClassDesc returnType, + @NotNull ImmutableSeq paramType, + boolean isInterface + ) { + return new MethodRef.Default(owner, name, returnType, paramType, isInterface); + } + + @Override + public @NotNull FieldRef resolve(@NotNull ClassDesc owner, @NotNull String name, @NotNull ClassDesc returnType) { + return new FieldRef.Default(owner, returnType, name); + } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/morphism/SourceCodeBuilder.java b/jit-compiler/src/main/java/org/aya/compiler/free/morphism/SourceCodeBuilder.java new file mode 100644 index 000000000..4d2df6822 --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/free/morphism/SourceCodeBuilder.java @@ -0,0 +1,367 @@ +// 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.compiler.free.morphism; + +import kala.collection.immutable.ImmutableSeq; +import kala.collection.immutable.primitive.ImmutableIntSeq; +import org.aya.compiler.SourceBuilder; +import org.aya.compiler.free.*; +import org.aya.compiler.free.data.FieldRef; +import org.aya.compiler.free.data.LocalVariable; +import org.aya.compiler.free.data.MethodRef; +import org.aya.compiler.serializers.ExprializeUtil; +import org.aya.util.IterableUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.constant.ClassDesc; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.ObjIntConsumer; + +import static org.aya.compiler.free.morphism.SourceFreeJavaBuilder.toClassRef; + +public record SourceCodeBuilder( + @NotNull SourceClassBuilder parent, + @NotNull SourceBuilder sourceBuilder +) implements FreeCodeBuilder { + public void appendArgs(@NotNull ImmutableSeq args) { + IterableUtil.forEach(args, () -> sourceBuilder.append(", "), this::appendExpr); + } + + public void appendExpr(@NotNull FreeJavaExpr expr) { + switch ((SourceFreeJavaExpr) expr) { + case SourceFreeJavaExpr.BlackBox blackBox -> sourceBuilder.append(blackBox.expr()); + case SourceFreeJavaExpr.Cont cont -> cont.run(); + } + } + + public static @NotNull String getExpr(@NotNull LocalVariable expr) { + return ((SourceFreeJavaExpr.BlackBox) expr).expr(); + } + + @Override public @NotNull FreeJavaResolver resolver() { return parent; } + + @Override + public void invokeSuperCon(@NotNull ImmutableSeq superConParams, @NotNull ImmutableSeq superConArgs) { + sourceBuilder.append("super("); + appendArgs(superConArgs); + sourceBuilder.append(");"); + sourceBuilder.appendLine(); + } + + @Override + public @NotNull SourceFreeJavaExpr.BlackBox makeVar(@NotNull ClassDesc type, @Nullable FreeJavaExpr initializer) { + var name = sourceBuilder.nameGen.nextName(); + + sourceBuilder.append(toClassRef(type) + " " + name); + + if (initializer != null) { + sourceBuilder.append(" = "); + appendExpr(initializer); + } + + sourceBuilder.append(";"); + sourceBuilder.appendLine(); + + return new SourceFreeJavaExpr.BlackBox(name); + } + + @Override public void updateVar(@NotNull LocalVariable var, @NotNull FreeJavaExpr update) { + sourceBuilder.append(getExpr(var) + " = "); + appendExpr(update); + sourceBuilder.append(";"); + sourceBuilder.appendLine(); + } + + // (() -> something).get()[0] = 114; + @Override public void updateArray(@NotNull FreeJavaExpr array, int idx, @NotNull FreeJavaExpr update) { + appendExpr(array); + sourceBuilder.append("[" + idx + "]"); + sourceBuilder.append(" = "); + appendExpr(update); + sourceBuilder.append(";"); + sourceBuilder.appendLine(); + } + + private void buildUpdate(@NotNull String lhs, @NotNull FreeJavaExpr rhs) { + sourceBuilder.append(lhs); + sourceBuilder.append(" = "); + appendExpr(rhs); + sourceBuilder.append(";"); + sourceBuilder.appendLine(); + } + + @Override public void updateField(@NotNull FieldRef field, @NotNull FreeJavaExpr update) { + var fieldRef = toClassRef(field.owner()) + "." + field.name(); + buildUpdate(fieldRef, update); + } + + @Override + public void updateField(@NotNull FieldRef field, @NotNull FreeJavaExpr owner, @NotNull FreeJavaExpr update) { + appendExpr(owner); + sourceBuilder.append("." + field.name() + " = "); + appendExpr(update); + sourceBuilder.append(";"); + sourceBuilder.appendLine(); + } + + private void buildIf( + @NotNull Runnable condition, + @NotNull Consumer thenBlock, + @Nullable Consumer elseBlock + ) { + sourceBuilder.append("if ("); + condition.run(); + sourceBuilder.append(") {"); + sourceBuilder.appendLine(); + sourceBuilder.runInside(() -> thenBlock.accept(this)); + sourceBuilder.append("}"); + + if (elseBlock != null) { + sourceBuilder.append(" else {"); + sourceBuilder.appendLine(); + sourceBuilder.runInside(() -> elseBlock.accept(this)); + sourceBuilder.appendLine("}"); + } else { + sourceBuilder.appendLine(); + } + } + + private void buildIf( + @NotNull String condition, + @NotNull Consumer thenBlock, + @Nullable Consumer elseBlock + ) { + buildIf(() -> sourceBuilder.append(condition), thenBlock, elseBlock); + } + + @Override public void ifNotTrue( + @NotNull LocalVariable notTrue, + @NotNull Consumer thenBlock, + @Nullable Consumer elseBlock + ) { + buildIf("! (" + getExpr(notTrue) + ")", thenBlock, elseBlock); + } + + @Override public void ifTrue( + @NotNull LocalVariable theTrue, + @NotNull Consumer thenBlock, + @Nullable Consumer elseBlock + ) { + buildIf(getExpr(theTrue), thenBlock, elseBlock); + } + + @Override public void ifInstanceOf( + @NotNull FreeJavaExpr lhs, + @NotNull ClassDesc rhs, + @NotNull BiConsumer thenBlock, + @Nullable Consumer elseBlock + ) { + var name = sourceBuilder.nameGen.nextName(); + buildIf(() -> { + appendExpr(lhs); + sourceBuilder.append(" instanceof " + toClassRef(rhs) + " " + name); + }, cb -> thenBlock.accept(cb, new SourceFreeJavaExpr.BlackBox(name)), + elseBlock); + } + + @Override public void ifIntEqual( + @NotNull FreeJavaExpr lhs, + int rhs, + @NotNull Consumer thenBlock, + @Nullable Consumer elseBlock + ) { + buildIf(() -> { + appendExpr(lhs); + sourceBuilder.append(" == " + rhs); + }, thenBlock, elseBlock); + } + + @Override public void ifRefEqual( + @NotNull FreeJavaExpr lhs, + @NotNull FreeJavaExpr rhs, + @NotNull Consumer thenBlock, + @Nullable Consumer elseBlock + ) { + buildIf(() -> { + appendExpr(lhs); + sourceBuilder.append(" == "); + appendExpr(rhs); + }, thenBlock, elseBlock); + } + + @Override public void ifNull( + @NotNull FreeJavaExpr isNull, + @NotNull Consumer thenBlock, + @Nullable Consumer elseBlock + ) { + buildIf(() -> { + appendExpr(isNull); + sourceBuilder.append(" == null"); + }, thenBlock, elseBlock); + } + + @Override public void breakable(@NotNull Consumer innerBlock) { + sourceBuilder.appendLine("do {"); + sourceBuilder.runInside(() -> innerBlock.accept(this)); + sourceBuilder.appendLine("} while (false);"); + } + + @Override public void breakOut() { sourceBuilder.buildBreak(); } + + @Override public void exec(@NotNull FreeJavaExpr expr) { + appendExpr(expr); + sourceBuilder.append(";"); + sourceBuilder.appendLine(); + } + + @Override public void switchCase( + @NotNull LocalVariable elim, + @NotNull ImmutableIntSeq cases, + @NotNull ObjIntConsumer branch, + @NotNull Consumer defaultCase + ) { + sourceBuilder.buildSwitch(getExpr(elim), cases, + i -> branch.accept(this, i), + () -> defaultCase.accept(this)); + } + + @Override public void returnWith(@NotNull FreeJavaExpr expr) { + sourceBuilder.append("return "); + appendExpr(expr); + sourceBuilder.append(";"); + sourceBuilder.appendLine(); + } + + private @NotNull SourceFreeJavaExpr.Cont mkNew(@NotNull ClassDesc className, @NotNull ImmutableSeq args) { + return () -> { + sourceBuilder.append("new " + toClassRef(className) + "("); + appendArgs(args); + sourceBuilder.append(")"); + }; + } + + @Override public @NotNull FreeJavaExpr mkNew(@NotNull MethodRef conRef, @NotNull ImmutableSeq args) { + return mkNew(conRef.owner(), args); + } + + @Override public @NotNull FreeJavaExpr mkNew(@NotNull Class className, @NotNull ImmutableSeq args) { + return mkNew(FreeUtil.fromClass(className), args); + } + + @Override public @NotNull FreeJavaExpr refVar(@NotNull LocalVariable name) { + return name.ref(); + } + + @Override + public @NotNull SourceFreeJavaExpr.Cont invoke(@NotNull MethodRef method, @NotNull FreeJavaExpr owner, @NotNull ImmutableSeq args) { + return () -> { + appendExpr(owner); + sourceBuilder.append("." + method.name() + "("); + appendArgs(args); + sourceBuilder.append(")"); + }; + } + + @Override + public @NotNull SourceFreeJavaExpr.Cont invoke(@NotNull MethodRef method, @NotNull ImmutableSeq args) { + return () -> { + sourceBuilder.append(toClassRef(method.owner()) + "." + method.name() + "("); + appendArgs(args); + sourceBuilder.append(")"); + }; + } + + @Override public @NotNull SourceFreeJavaExpr.BlackBox refField(@NotNull FieldRef field) { + return new SourceFreeJavaExpr.BlackBox(toClassRef(field.owner()) + "." + field.name()); + } + + @Override public @NotNull SourceFreeJavaExpr.Cont refField(@NotNull FieldRef field, @NotNull FreeJavaExpr owner) { + return () -> { + appendExpr(owner); + sourceBuilder.append("." + field.name()); + }; + } + + public static @NotNull String makeRefEnum(@NotNull ClassDesc enumClass, @NotNull String enumName) { + return toClassRef(enumClass) + "." + enumName; + } + + @Override + public @NotNull SourceFreeJavaExpr.BlackBox refEnum(@NotNull ClassDesc enumClass, @NotNull String enumName) { + return new SourceFreeJavaExpr.BlackBox(makeRefEnum(enumClass, enumName)); + } + + @Override public @NotNull SourceFreeJavaExpr.Cont mkLambda( + @NotNull ImmutableSeq captures, + @NotNull MethodRef method, + @NotNull BiConsumer builder + ) { + var name = ImmutableSeq.fill(method.paramTypes().size(), _ -> sourceBuilder.nameGen.nextName()); + var ap = new SourceArgumentProvider.Lambda(captures, name); + return () -> { + sourceBuilder.append("(" + name.joinToString(", ") + ") -> {"); + sourceBuilder.appendLine(); + sourceBuilder.runInside(() -> { + builder.accept(ap, this); + }); + sourceBuilder.append("}"); + }; + } + + @Override public @NotNull SourceFreeJavaExpr.BlackBox iconst(int i) { + return new SourceFreeJavaExpr.BlackBox(Integer.toString(i)); + } + + @Override public @NotNull SourceFreeJavaExpr.BlackBox iconst(boolean b) { + return new SourceFreeJavaExpr.BlackBox(Boolean.toString(b)); + } + + @Override public @NotNull SourceFreeJavaExpr.BlackBox aconst(@NotNull String value) { + return new SourceFreeJavaExpr.BlackBox(ExprializeUtil.makeString(value)); + } + + @Override public @NotNull SourceFreeJavaExpr.BlackBox aconstNull(@NotNull ClassDesc type) { + return new SourceFreeJavaExpr.BlackBox("((" + toClassRef(type) + ") null)"); + } + + @Override public @NotNull SourceFreeJavaExpr.BlackBox thisRef() { + return new SourceFreeJavaExpr.BlackBox("this"); + } + + public static @NotNull String mkHalfArray(@NotNull ImmutableSeq elems) { + return elems.joinToString(", ", "{ ", " }"); + } + + @Override + public @NotNull SourceFreeJavaExpr.Cont mkArray(@NotNull ClassDesc type, int length, @Nullable ImmutableSeq initializer) { + assert initializer == null || initializer.sizeEquals(length); + var hasInit = initializer != null; + var arrayIndicator = hasInit ? "[]" : "[" + length + "]"; + + return () -> { + sourceBuilder.append("new " + toClassRef(type) + arrayIndicator); + if (initializer != null) { + sourceBuilder.append(" { "); + appendArgs(initializer); + sourceBuilder.append(" }"); + } + }; + } + + @Override public @NotNull SourceFreeJavaExpr.Cont getArray(@NotNull FreeJavaExpr array, int index) { + return () -> { + appendExpr(array); + sourceBuilder.append("[" + index + "]"); + }; + } + + @Override public @NotNull SourceFreeJavaExpr.Cont checkcast(@NotNull FreeJavaExpr obj, @NotNull ClassDesc as) { + return () -> { + sourceBuilder.append("((" + toClassRef(as) + ") "); + appendExpr(obj); + sourceBuilder.append(")"); + }; + } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/morphism/SourceFreeJavaBuilder.java b/jit-compiler/src/main/java/org/aya/compiler/free/morphism/SourceFreeJavaBuilder.java new file mode 100644 index 000000000..d030bc0d8 --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/free/morphism/SourceFreeJavaBuilder.java @@ -0,0 +1,53 @@ +// 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.compiler.free.morphism; + +import org.aya.compiler.SourceBuilder; +import org.aya.compiler.free.FreeClassBuilder; +import org.aya.compiler.free.FreeJavaBuilder; +import org.aya.compiler.free.FreeUtil; +import org.jetbrains.annotations.NotNull; + +import java.lang.constant.ClassDesc; +import java.util.function.Consumer; + +public record SourceFreeJavaBuilder(@NotNull SourceBuilder sourceBuilder) + implements FreeJavaBuilder { + public static @NotNull SourceFreeJavaBuilder create() { + return new SourceFreeJavaBuilder(new SourceBuilder()); + } + + // convert "Ljava/lang/Object;" to "java.lang.Object" + public static @NotNull String toClassRef(@NotNull ClassDesc className) { + var arrayDepth = 0; + ClassDesc baseType = className; + while (baseType.isArray()) { + baseType = baseType.componentType(); + arrayDepth += 1; + } + + var arrayPostfix = "[]".repeat(arrayDepth); + var name = baseType.displayName(); + var packageName = baseType.packageName(); + var prefix = packageName.isEmpty() ? "" : packageName + "."; + return prefix + name.replace('$', '.') + arrayPostfix; + } + + // convert "Ljava/lang/Object;" to "Object" + public static @NotNull String toClassName(@NotNull ClassDesc className) { + var name = className.displayName(); + return name.substring(name.lastIndexOf('$') + 1); + } + + @Override + public @NotNull String buildClass( + @NotNull ClassDesc className, + @NotNull Class superclass, + @NotNull Consumer builder + ) { + sourceBuilder.appendLine("package " + className.packageName() + ";"); + sourceBuilder.buildClass(className.displayName(), toClassRef(FreeUtil.fromClass(superclass)), false, () -> + builder.accept(new SourceClassBuilder(this, className, sourceBuilder))); + return sourceBuilder.builder.toString(); + } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/morphism/SourceFreeJavaExpr.java b/jit-compiler/src/main/java/org/aya/compiler/free/morphism/SourceFreeJavaExpr.java new file mode 100644 index 000000000..7b89418b4 --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/free/morphism/SourceFreeJavaExpr.java @@ -0,0 +1,20 @@ +// 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.compiler.free.morphism; + +import org.aya.compiler.free.FreeJavaExpr; +import org.aya.compiler.free.data.LocalVariable; +import org.jetbrains.annotations.NotNull; + +public sealed interface SourceFreeJavaExpr extends FreeJavaExpr { + record BlackBox(@NotNull String expr) implements SourceFreeJavaExpr, LocalVariable { + @Override public @NotNull FreeJavaExpr ref() { + return this; + } + } + + // A {@link Cont} should be used in a {@link SourceCodeBuilder} who constructs it. + @FunctionalInterface + non-sealed interface Cont extends SourceFreeJavaExpr, Runnable { + } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/serializers/AbstractExprializer.java b/jit-compiler/src/main/java/org/aya/compiler/serializers/AbstractExprializer.java new file mode 100644 index 000000000..7c076baef --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/serializers/AbstractExprializer.java @@ -0,0 +1,115 @@ +// 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.compiler.serializers; + +import kala.collection.immutable.ImmutableSeq; +import org.aya.compiler.free.Constants; +import org.aya.compiler.free.FreeExprBuilder; +import org.aya.compiler.free.FreeJavaExpr; +import org.aya.compiler.free.FreeUtil; +import org.aya.compiler.free.data.MethodRef; +import org.aya.syntax.core.def.AnyDef; +import org.aya.syntax.core.def.TyckDef; +import org.jetbrains.annotations.NotNull; + +import java.lang.constant.ClassDesc; + +public abstract class AbstractExprializer { + protected final @NotNull FreeExprBuilder builder; + + protected AbstractExprializer(@NotNull FreeExprBuilder builder) { this.builder = builder; } + + public @NotNull FreeJavaExpr makeImmutableSeq( + @NotNull Class typeName, + @NotNull ImmutableSeq terms + ) { + return makeImmutableSeq(builder, typeName, terms); + } + + public @NotNull FreeJavaExpr serializeToImmutableSeq( + @NotNull Class typeName, + @NotNull ImmutableSeq terms + ) { + var sered = terms.map(this::doSerialize); + return makeImmutableSeq(typeName, sered); + } + + public static @NotNull FreeJavaExpr makeImmutableSeq( + @NotNull FreeExprBuilder builder, + @NotNull Class typeName, + @NotNull ImmutableSeq terms + ) { + return makeImmutableSeq(builder, Constants.IMMSEQ, typeName, terms); + } + + public static @NotNull FreeJavaExpr makeImmutableSeq( + @NotNull FreeExprBuilder builder, + @NotNull MethodRef con, + @NotNull Class typeName, + @NotNull ImmutableSeq terms + ) { + var args = builder.mkArray(FreeUtil.fromClass(typeName), terms.size(), terms); + return builder.invoke(con, ImmutableSeq.of(args)); + } + + /** + * Return the reference to the {@code INSTANCE} field of the compiled class to {@param def} + */ + public final @NotNull FreeJavaExpr getInstance(@NotNull AnyDef def) { + return getInstance(builder, def); + } + + public static @NotNull FreeJavaExpr getInstance(@NotNull FreeExprBuilder builder, @NotNull TyckDef def) { + return getInstance(builder, AnyDef.fromVar(def.ref())); + } + + public static @NotNull FreeJavaExpr getInstance(@NotNull FreeExprBuilder builder, @NotNull AnyDef def) { + var desc = NameSerializer.getClassDesc(def); + return builder.refField(builder.resolver().resolve(desc, AyaSerializer.STATIC_FIELD_INSTANCE, desc)); + } + + public static @NotNull FreeJavaExpr getRef(@NotNull FreeExprBuilder builder, @NotNull CallKind callType, @NotNull FreeJavaExpr call) { + return builder.invoke(builder.resolver().resolve( + callType.callType, AyaSerializer.FIELD_INSTANCE, + callType.refType, ImmutableSeq.empty(), true + ), call, ImmutableSeq.empty()); + } + + public final @NotNull FreeJavaExpr getCallInstance(@NotNull CallKind callType, @NotNull AnyDef def) { + return builder.refField(builder.resolver().resolve( + NameSerializer.getClassDesc(def), + AyaSerializer.FIELD_EMPTYCALL, + callType.callType) + ); + } + + public static @NotNull ImmutableSeq fromSeq( + @NotNull FreeExprBuilder builder, + @NotNull ClassDesc elementType, + @NotNull FreeJavaExpr theSeq, + int size + ) { + return ImmutableSeq.fill(size, idx -> makeSeqGet(builder, elementType, theSeq, idx)); + } + + public static @NotNull FreeJavaExpr makeSeqGet( + @NotNull FreeExprBuilder builder, + @NotNull ClassDesc elementType, + @NotNull FreeJavaExpr theSeq, + int size + ) { + var result = builder.invoke(Constants.SEQ_GET, theSeq, ImmutableSeq.of(builder.iconst(size))); + return builder.checkcast(result, elementType); + } + + /** + * Actually perform serialization, unlike {@link #serialize} + * which will perform some initialization after a {@code T} is obtained. + */ + protected abstract @NotNull FreeJavaExpr doSerialize(@NotNull T term); + + /** + * Prepare and perform {@link #doSerialize} + */ + public abstract @NotNull FreeJavaExpr serialize(T unit); +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/serializers/AyaSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/serializers/AyaSerializer.java new file mode 100644 index 000000000..d1b950038 --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/serializers/AyaSerializer.java @@ -0,0 +1,43 @@ +// 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.compiler.serializers; + +import kala.collection.immutable.ImmutableSeq; +import org.aya.compiler.free.Constants; +import org.aya.compiler.free.FreeCodeBuilder; +import org.aya.compiler.free.FreeExprBuilder; +import org.aya.compiler.free.FreeJavaExpr; +import org.aya.syntax.core.term.Term; +import org.aya.util.error.Panic; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Supplier; + +import static org.aya.compiler.serializers.ExprializeUtil.getJavaRef; + +public interface AyaSerializer { + String PACKAGE_BASE = "AYA"; + String STATIC_FIELD_INSTANCE = "INSTANCE"; + String FIELD_INSTANCE = "ref"; + String FIELD_EMPTYCALL = "ourCall"; + String CLASS_PANIC = getJavaRef(Panic.class); + + static void returnPanic(@NotNull FreeCodeBuilder builder) { + builder.returnWith(buildPanic(builder)); + } + + static void execPanic(@NotNull FreeCodeBuilder builder) { + builder.exec(buildPanic(builder)); + } + + static @NotNull FreeJavaExpr buildPanic(@NotNull FreeExprBuilder builder) { + return builder.invoke(Constants.PANIC, ImmutableSeq.empty()); + } + + /** + * Build a type safe {@link Supplier#get()}, note that this method assume the result is {@link Term} + */ + static @NotNull FreeJavaExpr getThunk(@NotNull FreeExprBuilder builder, @NotNull FreeJavaExpr thunkExpr) { + return builder.checkcast(builder.invoke(Constants.THUNK, thunkExpr, ImmutableSeq.empty()), Term.class); + } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/serializers/CallKind.java b/jit-compiler/src/main/java/org/aya/compiler/serializers/CallKind.java new file mode 100644 index 000000000..e2f5f84cf --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/serializers/CallKind.java @@ -0,0 +1,38 @@ +// 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.compiler.serializers; + +import org.aya.compiler.free.FreeUtil; +import org.aya.syntax.core.def.ConDefLike; +import org.aya.syntax.core.def.DataDefLike; +import org.aya.syntax.core.def.FnDefLike; +import org.aya.syntax.core.def.PrimDefLike; +import org.aya.syntax.core.term.call.*; +import org.jetbrains.annotations.NotNull; + +import java.lang.constant.ClassDesc; + +public enum CallKind { + Fn(FnCall.class, FnDefLike.class), + Data(DataCall.class, DataDefLike.class), + Con(ConCall.class, ConDefLike.class), + Prim(PrimCall.class, PrimDefLike.class); + + public final @NotNull ClassDesc callType; + public final @NotNull ClassDesc refType; + + CallKind(@NotNull Class callType, @NotNull Class refType) { + this.callType = FreeUtil.fromClass(callType); + this.refType = FreeUtil.fromClass(refType); + } + + public static @NotNull CallKind from(@NotNull Callable.Tele call) { + return switch (call) { + case FnCall _ -> CallKind.Fn; + case ConCall _ -> CallKind.Con; + case DataCall _ -> CallKind.Data; + case PrimCall _ -> CallKind.Prim; + default -> throw new UnsupportedOperationException("TODO"); + }; + } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/serializers/ClassSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/serializers/ClassSerializer.java new file mode 100644 index 000000000..410818a3a --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/serializers/ClassSerializer.java @@ -0,0 +1,60 @@ +// 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.compiler.serializers; + +import kala.collection.immutable.ImmutableSeq; +import org.aya.compiler.free.Constants; +import org.aya.compiler.free.FreeClassBuilder; +import org.aya.compiler.free.FreeCodeBuilder; +import org.aya.compiler.free.FreeUtil; +import org.aya.compiler.free.data.MethodRef; +import org.aya.syntax.compile.JitClass; +import org.aya.syntax.compile.JitMember; +import org.aya.syntax.core.def.ClassDef; +import org.aya.syntax.core.term.call.ClassCall; +import org.jetbrains.annotations.NotNull; + +public final class ClassSerializer extends JitDefSerializer { + public ClassSerializer() { super(JitClass.class); } + + @Override protected @NotNull Class callClass() { return ClassCall.class; } + @Override protected boolean shouldBuildEmptyCall(@NotNull ClassDef unit) { + return true; + } + + // TODO: unify with DataSerializer#buildConstructors + private void buildMembers(@NotNull FreeCodeBuilder builder, ClassDef unit) { + var mems = Constants.JITCLASS_MEMS; + var memsRef = builder.refField(mems, builder.thisRef()); + + if (unit.members().isEmpty()) { + builder.returnWith(memsRef); + return; + } + + builder.ifNull(builder.getArray(memsRef, 0), cb -> { + unit.members().forEachIndexed((idx, con) -> { + cb.updateArray(memsRef, idx, AbstractExprializer.getInstance(builder, con)); + }); + }, null); + + builder.returnWith(memsRef); + } + + @Override public @NotNull ClassSerializer serialize(@NotNull FreeClassBuilder builder, ClassDef unit) { + buildFramework(builder, unit, builder0 -> { + builder0.buildMethod( + FreeUtil.fromClass(JitMember.class).arrayType(), + "membars", + ImmutableSeq.empty(), + (ap, cb) -> buildMembers(cb, unit)); + }); + + return this; + } + + @Override protected @NotNull MethodRef buildConstructor(@NotNull FreeClassBuilder builder, ClassDef unit) { + return builder.buildConstructor(ImmutableSeq.empty(), (ap, cb) -> + cb.invokeSuperCon(ImmutableSeq.empty(), ImmutableSeq.empty())); + } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/serializers/ConSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/serializers/ConSerializer.java new file mode 100644 index 000000000..8255d9c2d --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/serializers/ConSerializer.java @@ -0,0 +1,116 @@ +// 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.compiler.serializers; + +import kala.collection.Seq; +import kala.collection.immutable.ImmutableSeq; +import kala.control.Result; +import org.aya.compiler.free.*; +import org.aya.compiler.free.data.LocalVariable; +import org.aya.syntax.compile.JitCon; +import org.aya.syntax.compile.JitData; +import org.aya.syntax.core.def.ConDef; +import org.aya.syntax.core.def.ConDefLike; +import org.aya.syntax.core.pat.Pat; +import org.aya.syntax.core.pat.PatMatcher; +import org.aya.syntax.core.term.call.ConCall; +import org.jetbrains.annotations.NotNull; + +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.util.function.BiConsumer; + +public final class ConSerializer extends JitTeleSerializer { + public ConSerializer() { + super(JitCon.class); + } + + @Override protected @NotNull Class callClass() { return ConCall.class; } + + @Override protected @NotNull ImmutableSeq superConParams() { + return super.superConParams().appendedAll(ImmutableSeq.of( + FreeUtil.fromClass(JitData.class), + ConstantDescs.CD_int, ConstantDescs.CD_boolean + )); + } + + @Override protected @NotNull ImmutableSeq superConArgs(@NotNull FreeCodeBuilder builder, ConDef unit) { + return super.superConArgs(builder, unit).appendedAll(ImmutableSeq.of( + AbstractExprializer.getInstance(builder, unit.dataRef), + builder.iconst(unit.selfTele.size()), + builder.iconst(unit.equality != null) + )); + } + + /** + * @see JitCon#isAvailable(Seq) + */ + private void buildIsAvailable(@NotNull FreeCodeBuilder builder, ConDef unit, @NotNull LocalVariable argsTerm) { + FreeJavaExpr matchResult; + var termSeq = builder.invoke(Constants.SEQ_TOIMMSEQ, argsTerm.ref(), ImmutableSeq.empty()); + if (unit.pats.isEmpty()) { + // not indexed data type, this constructor is always available + matchResult = builder.invoke(Constants.RESULT_OK, ImmutableSeq.of(termSeq)); + } else { + // It is too stupid to serialize pat meta solving, so we just call PatMatcher + var patsTerm = unit.pats.map(x -> new PatternExprializer(builder, true).serialize(x)); + var patsSeq = AbstractExprializer.makeImmutableSeq(builder, Pat.class, patsTerm); + var id = builder.invoke(Constants.CLOSURE_ID, ImmutableSeq.empty()); + var matcherTerm = builder.mkNew(PatMatcher.class, ImmutableSeq.of( + builder.iconst(true), id + )); + + matchResult = builder.invoke(Constants.PATMATCHER_APPLY, matcherTerm, ImmutableSeq.of( + patsSeq, termSeq + )); + } + + builder.returnWith(matchResult); + } + + /** + * @see ConDefLike#equality(Seq, boolean) + */ + private void buildEquality( + @NotNull FreeCodeBuilder builder, + ConDef unit, + @NotNull LocalVariable argsTerm, + @NotNull LocalVariable is0Term + ) { + var eq = unit.equality; + assert eq != null; + BiConsumer continuation = (cb, b) -> { + var side = b ? eq.a() : eq.b(); + cb.returnWith(serializeTermUnderTele(cb, side, argsTerm.ref(), unit.telescope().size())); + }; + + builder.ifTrue(is0Term, + then -> continuation.accept(then, true), + otherwise -> continuation.accept(otherwise, false)); + } + + @Override public @NotNull ConSerializer serialize(@NotNull FreeClassBuilder builder0, ConDef unit) { + buildFramework(builder0, unit, builder -> { + builder.buildMethod( + FreeUtil.fromClass(Result.class), + "isAvailable", + ImmutableSeq.of(Constants.CD_Seq), + (ap, builder1) -> + buildIsAvailable(builder1, unit, ap.arg(0))); + + if (unit.equality != null) { + builder.buildMethod( + Constants.CD_Term, + "equality", + ImmutableSeq.of(Constants.CD_Seq, ConstantDescs.CD_boolean), + (ap, cb) -> { + var argsTerm = ap.arg(0); + var is0Term = ap.arg(1); + buildEquality(cb, unit, argsTerm, is0Term); + }); + } + }); + + return this; + } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/serializers/DataSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/serializers/DataSerializer.java new file mode 100644 index 000000000..f3a2fa907 --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/serializers/DataSerializer.java @@ -0,0 +1,100 @@ +// 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.compiler.serializers; + +import kala.collection.immutable.ImmutableMap; +import kala.collection.immutable.ImmutableSeq; +import kala.tuple.Tuple; +import org.aya.compiler.free.*; +import org.aya.primitive.ShapeFactory; +import org.aya.syntax.compile.JitCon; +import org.aya.syntax.compile.JitData; +import org.aya.syntax.core.def.DataDef; +import org.aya.syntax.core.def.TyckAnyDef; +import org.aya.syntax.core.repr.CodeShape; +import org.aya.syntax.core.term.call.DataCall; +import org.aya.syntax.ref.DefVar; +import org.jetbrains.annotations.NotNull; + +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; + +// You should compile this with its constructors +public final class DataSerializer extends JitTeleSerializer { + private final @NotNull ShapeFactory shapeFactory; + + public DataSerializer(@NotNull ShapeFactory shapeFactory) { + super(JitData.class); + this.shapeFactory = shapeFactory; + } + + @Override public @NotNull DataSerializer serialize(@NotNull FreeClassBuilder builder, DataDef unit) { + buildFramework(builder, unit, builder0 -> { + builder0.buildMethod( + FreeUtil.fromClass(JitCon.class).arrayType(), + "constructors", + ImmutableSeq.empty(), (_, cb) -> { + buildConstructors(cb, unit); + }); + }); + + return this; + } + + @Override protected @NotNull Class callClass() { return DataCall.class; } + @Override protected int buildShape(DataDef unit) { + var maybe = shapeFactory.find(TyckAnyDef.make(unit)); + if (maybe.isEmpty()) { + return super.buildShape(unit); + } + + return maybe.get().shape().ordinal(); + } + + @Override + protected CodeShape.GlobalId[] buildRecognition(DataDef unit) { + var maybe = shapeFactory.find(TyckAnyDef.make(unit)); + if (maybe.isEmpty()) { + return super.buildRecognition(unit); + } + + var recog = maybe.get(); + // The capture is one-to-one + var flipped = ImmutableMap.from(recog.captures().view() + .map((k, v) -> Tuple., CodeShape.GlobalId>of(((TyckAnyDef) v).ref, k))); + var capture = unit.body.map(x -> flipped.get(x.ref)); + return capture.toArray(CodeShape.GlobalId.class); + } + + @Override + protected @NotNull ImmutableSeq superConParams() { + return super.superConParams().appended(ConstantDescs.CD_int); + } + + @Override + protected @NotNull ImmutableSeq superConArgs(@NotNull FreeCodeBuilder builder, DataDef unit) { + return super.superConArgs(builder, unit).appended(builder.iconst(unit.body.size())); + } + + + /** + * @see JitData#constructors() + */ + private void buildConstructors(@NotNull FreeCodeBuilder builder, DataDef unit) { + var cons = Constants.JITDATA_CONS; + var consRef = builder.refField(cons, builder.thisRef()); + + if (unit.body.isEmpty()) { + builder.returnWith(consRef); + return; + } + + builder.ifNull(builder.getArray(consRef, 0), cb -> { + unit.body.forEachIndexed((idx, con) -> { + cb.updateArray(consRef, idx, AbstractExprializer.getInstance(builder, con)); + }); + }, null); + + builder.returnWith(consRef); + } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/serializers/ExprializeUtil.java b/jit-compiler/src/main/java/org/aya/compiler/serializers/ExprializeUtil.java new file mode 100644 index 000000000..8d36755c7 --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/serializers/ExprializeUtil.java @@ -0,0 +1,34 @@ +// 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.compiler.serializers; + +import com.intellij.openapi.util.text.StringUtil; +import kala.collection.immutable.ImmutableSeq; +import org.jetbrains.annotations.NotNull; + +public interface ExprializeUtil { + String SEP = ", "; + + static @NotNull String makeNew(@NotNull String className, String... terms) { + return ImmutableSeq.from(terms).joinToString(SEP, "new " + className + "(", ")"); + } + + static @NotNull String makeString(@NotNull String raw) { + return "\"" + StringUtil.escapeStringCharacters(raw) + "\""; + } + + static @NotNull String isNull(@NotNull String term) { + return term + " == null"; + } + + static @NotNull String getInstance(@NotNull String defName) { + return defName + "." + AyaSerializer.STATIC_FIELD_INSTANCE; + } + + /** + * Get the reference to {@param clazz}, it should be imported to current file. + */ + static @NotNull String getJavaRef(@NotNull Class clazz) { + return clazz.getSimpleName(); + } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/serializers/FnSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/serializers/FnSerializer.java new file mode 100644 index 000000000..e0aaac1d2 --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/serializers/FnSerializer.java @@ -0,0 +1,155 @@ +// 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.compiler.serializers; + +import kala.collection.immutable.ImmutableSeq; +import kala.collection.mutable.FreezableMutableList; +import kala.control.Either; +import org.aya.compiler.free.*; +import org.aya.compiler.free.data.LocalVariable; +import org.aya.compiler.free.data.MethodRef; +import org.aya.generic.Modifier; +import org.aya.primitive.ShapeFactory; +import org.aya.syntax.compile.JitFn; +import org.aya.syntax.core.def.FnDef; +import org.aya.syntax.core.def.TyckAnyDef; +import org.aya.syntax.core.term.call.FnCall; +import org.aya.util.error.WithPos; +import org.jetbrains.annotations.NotNull; + +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.util.EnumSet; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public final class FnSerializer extends JitTeleSerializer { + private final @NotNull ShapeFactory shapeFactory; + + public FnSerializer(@NotNull ShapeFactory shapeFactory) { + super(JitFn.class); + this.shapeFactory = shapeFactory; + } + + public static @NotNull MethodRef resolveInvoke(@NotNull ClassDesc owner, int argc) { + return new MethodRef.Default( + owner, "invoke", Constants.CD_Term, ImmutableSeq.of(Constants.CD_Thunk) + .appendedAll(ImmutableSeq.fill(argc, Constants.CD_Term)), false + ); + } + + public static int modifierFlags(@NotNull EnumSet modies) { + var flag = 0; + for (var mody : modies) flag |= 1 << mody.ordinal(); + return flag; + } + + @Override + protected @NotNull ImmutableSeq superConParams() { + return super.superConParams().appended(ConstantDescs.CD_int); + } + + @Override + protected @NotNull ImmutableSeq superConArgs(@NotNull FreeCodeBuilder builder, FnDef unit) { + return super.superConArgs(builder, unit) + .appended(builder.iconst(modifierFlags(unit.modifiers()))); + } + + /** + * Build fixed argument `invoke` + */ + private void buildInvoke( + @NotNull FreeCodeBuilder builder, + @NotNull FnDef unit, + @NotNull LocalVariable onStuckTerm, + @NotNull ImmutableSeq argTerms + ) { + Consumer onStuckCon = cb -> + cb.returnWith(AyaSerializer.getThunk(cb, onStuckTerm.ref())); + var argExprs = argTerms.map(LocalVariable::ref); + + if (unit.is(Modifier.Opaque)) { + onStuckCon.accept(builder); + return; + } + + switch (unit.body()) { + case Either.Left(var expr) -> { + var result = serializeTermUnderTele(builder, expr, argExprs); + builder.returnWith(result); + } + case Either.Right(var clauses) -> { + var ser = new PatternSerializer(argExprs, onStuckCon, unit.is(Modifier.Overlap)); + ser.serialize(builder, clauses.view() + .map(WithPos::data) + .map(matching -> new PatternSerializer.Matching( + matching.bindCount(), matching.patterns(), (patSer, builder0, bindSize) -> { + var result = serializeTermUnderTele( + builder0, + matching.body(), + patSer.result.ref(), + bindSize + ); + builder0.returnWith(result); + }) + ).toImmutableSeq()); + } + } + } + + /** + * Build vararg `invoke` + */ + private void buildInvoke(@NotNull FreeCodeBuilder builder, @NotNull FnDef unit, @NotNull MethodRef invokeMethod, @NotNull LocalVariable onStuckTerm, @NotNull LocalVariable argsTerm) { + var teleSize = unit.telescope().size(); + var args = AbstractExprializer.fromSeq(builder, Constants.CD_Term, argsTerm.ref(), teleSize); + var result = builder.invoke( + invokeMethod, + builder.thisRef(), + args.prepended(onStuckTerm.ref()) + ); + + builder.returnWith(result); + } + + @Override protected @NotNull Class callClass() { return FnCall.class; } + + @Override + protected int buildShape(FnDef unit) { + var shapeMaybe = shapeFactory.find(TyckAnyDef.make(unit)); + if (shapeMaybe.isEmpty()) return super.buildShape(unit); + return shapeMaybe.get().shape().ordinal(); + } + + @Override public @NotNull FnSerializer serialize(@NotNull FreeClassBuilder builder, FnDef unit) { + var onStuckParam = FreeUtil.fromClass(Supplier.class); + var fullParam = FreezableMutableList.create(); + fullParam.append(onStuckParam); + fullParam.appendAll(ImmutableSeq.fill(unit.telescope().size(), Constants.CD_Term)); + + buildFramework(builder, unit, builder0 -> { + var fixedInvoke = builder0.buildMethod( + Constants.CD_Term, + "invoke", + fullParam.freeze(), + (ap, cb) -> { + var onStuck = ap.arg(0); + var args = ImmutableSeq.fill(unit.telescope().size(), + i -> ap.arg(i + 1)); + buildInvoke(cb, unit, onStuck, args); + } + ); + + builder0.buildMethod( + Constants.CD_Term, + "invoke", + ImmutableSeq.of(onStuckParam, Constants.CD_Seq), + (ap, cb) -> + buildInvoke(cb, unit, fixedInvoke, + ap.arg(0), ap.arg(1)) + ); + }); + + return this; + } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/serializers/JitDefSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/serializers/JitDefSerializer.java new file mode 100644 index 000000000..20ac9afb1 --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/serializers/JitDefSerializer.java @@ -0,0 +1,105 @@ +// 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.compiler.serializers; + +import kala.collection.immutable.ImmutableSeq; +import org.aya.compiler.free.FreeClassBuilder; +import org.aya.compiler.free.FreeExprBuilder; +import org.aya.compiler.free.FreeJavaExpr; +import org.aya.compiler.free.FreeUtil; +import org.aya.compiler.free.data.FieldRef; +import org.aya.compiler.free.data.MethodRef; +import org.aya.syntax.compile.CompiledAya; +import org.aya.syntax.core.def.AnyDef; +import org.aya.syntax.core.def.TyckDef; +import org.aya.syntax.core.repr.CodeShape; +import org.jetbrains.annotations.NotNull; + +import java.lang.annotation.Annotation; +import java.util.function.Consumer; + +import static org.aya.compiler.serializers.AyaSerializer.FIELD_EMPTYCALL; +import static org.aya.compiler.serializers.AyaSerializer.STATIC_FIELD_INSTANCE; +import static org.aya.compiler.serializers.NameSerializer.javifyClassName; + +public abstract class JitDefSerializer { + protected final @NotNull Class superClass; + + protected JitDefSerializer(@NotNull Class superClass) { + this.superClass = superClass; + } + + private static @NotNull CompiledAya mkCompiledAya( + @NotNull String[] module, + int fileModuleSize, + @NotNull String name, + int assoc, + int shape, + @NotNull CodeShape.GlobalId[] recognition + ) { + return new CompiledAya() { + @Override public Class annotationType() { return CompiledAya.class; } + @Override public @NotNull String[] module() { return module; } + @Override public int fileModuleSize() { return fileModuleSize; } + @Override public @NotNull String name() { return name; } + @Override public int assoc() { return assoc; } + @Override public int shape() { return shape; } + @Override public @NotNull CodeShape.GlobalId[] recognition() { return recognition; } + }; + } + + /** + * @see CompiledAya + */ + protected @NotNull CompiledAya buildMetadata(@NotNull T unit) { + var ref = unit.ref(); + var module = ref.module; + var assoc = ref.assoc(); + var assocIdx = assoc == null ? -1 : assoc.ordinal(); + assert module != null; + return mkCompiledAya( + module.module().module().toArray(String.class), + module.fileModuleSize(), + ref.name(), + assocIdx, + buildShape(unit), + buildRecognition(unit) + ); + } + + protected int buildShape(T unit) { return -1; } + protected CodeShape.GlobalId[] buildRecognition(T unit) { return new CodeShape.GlobalId[0]; } + + protected @NotNull FieldRef buildInstance(@NotNull FreeClassBuilder builder, @NotNull MethodRef con) { + return builder.buildConstantField(con.owner(), STATIC_FIELD_INSTANCE, b -> + b.mkNew(con, ImmutableSeq.empty())); + } + + protected abstract boolean shouldBuildEmptyCall(@NotNull T unit); + + protected abstract @NotNull Class callClass(); + + protected abstract @NotNull MethodRef buildConstructor(@NotNull FreeClassBuilder builder, T unit); + + protected final FreeJavaExpr buildEmptyCall(@NotNull FreeExprBuilder builder, @NotNull AnyDef def) { + return builder.mkNew(callClass(), ImmutableSeq.of(AbstractExprializer.getInstance(builder, def))); + } + + protected void buildFramework(@NotNull FreeClassBuilder builder, @NotNull T unit, @NotNull Consumer continuation) { + var className = javifyClassName(unit.ref()); + var metadata = buildMetadata(unit); + builder.buildNestedClass(metadata, className, superClass, nestBuilder -> { + var def = AnyDef.fromVar(unit.ref()); + var con = buildConstructor(nestBuilder, unit); + buildInstance(nestBuilder, con); + if (shouldBuildEmptyCall(unit)) { + nestBuilder.buildConstantField(FreeUtil.fromClass(callClass()), FIELD_EMPTYCALL, cb -> + buildEmptyCall(cb, def)); + } + + continuation.accept(nestBuilder); + }); + } + + public abstract @NotNull JitDefSerializer serialize(@NotNull FreeClassBuilder builder, T unit); +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/serializers/JitTeleSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/serializers/JitTeleSerializer.java new file mode 100644 index 000000000..48acdc453 --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/serializers/JitTeleSerializer.java @@ -0,0 +1,140 @@ +// 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.compiler.serializers; + +import kala.collection.immutable.ImmutableSeq; +import kala.collection.immutable.primitive.ImmutableIntSeq; +import kala.range.primitive.IntRange; +import org.aya.compiler.free.*; +import org.aya.compiler.free.data.LocalVariable; +import org.aya.compiler.free.data.MethodRef; +import org.aya.syntax.core.def.TyckDef; +import org.aya.syntax.core.term.Param; +import org.aya.syntax.core.term.Term; +import org.aya.syntax.telescope.JitTele; +import org.jetbrains.annotations.NotNull; + +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.util.function.Consumer; + +public abstract class JitTeleSerializer extends JitDefSerializer { + protected JitTeleSerializer(@NotNull Class superClass) { + super(superClass); + } + + protected @NotNull ImmutableSeq superConParams() { + return Constants.JIT_TELE_CON_PARAMS; + } + + protected @NotNull ImmutableSeq superConArgs(@NotNull FreeCodeBuilder builder, T unit) { + var tele = unit.telescope(); + var size = tele.size(); + var sizeExpr = builder.iconst(size); + var licit = tele.view().map(Param::explicit) + .map(builder::iconst) + .toImmutableSeq(); + var licitExpr = builder.mkArray(ConstantDescs.CD_boolean, licit.size(), licit); + var names = tele.view().map(Param::name) + .map(builder::aconst) + .toImmutableSeq(); + var namesExpr = builder.mkArray(ConstantDescs.CD_String, names.size(), names); + return ImmutableSeq.of(sizeExpr, licitExpr, namesExpr); + } + + @Override protected @NotNull MethodRef buildConstructor(@NotNull FreeClassBuilder builder, T unit) { + return builder.buildConstructor(ImmutableSeq.empty(), (_, cb) -> + cb.invokeSuperCon(superConParams(), superConArgs(cb, unit))); + } + + @Override protected void buildFramework( + @NotNull FreeClassBuilder builder, + @NotNull T unit, + @NotNull Consumer continuation + ) { + super.buildFramework(builder, unit, nestBuilder -> { + nestBuilder.buildMethod( + Constants.CD_Term, + "telescope", + ImmutableSeq.of(ConstantDescs.CD_int, Constants.CD_Seq), + (ap, cb) -> { + var i = ap.arg(0); + var teleArgs = ap.arg(1); + buildTelescope(cb, unit, i, teleArgs); + }); + + nestBuilder.buildMethod( + Constants.CD_Term, + "result", + ImmutableSeq.of(Constants.CD_Seq), + (ap, cb) -> { + var teleArgs = ap.arg(0); + buildResult(cb, unit, teleArgs); + } + ); + + continuation.accept(nestBuilder); + }); + } + + @Override protected boolean shouldBuildEmptyCall(@NotNull T unit) { + return unit.telescope().isEmpty(); + } + + /** + * @see JitTele#telescope(int, Term...) + */ + protected void buildTelescope(@NotNull FreeCodeBuilder builder, @NotNull T unit, @NotNull LocalVariable iTerm, @NotNull LocalVariable teleArgsTerm) { + var tele = unit.telescope(); + + builder.switchCase( + iTerm, + IntRange.closedOpen(0, tele.size()).collect(ImmutableIntSeq.factory()), + (cb, kase) -> { + var result = serializeTermUnderTele( + cb, + tele.get(kase).type(), + teleArgsTerm.ref(), kase + ); + + cb.returnWith(result); + }, + AyaSerializer::returnPanic); + } + + /** + * @see JitTele#result + */ + protected void buildResult(@NotNull FreeCodeBuilder builder, @NotNull T unit, @NotNull LocalVariable teleArgsTerm) { + var result = serializeTermUnderTele( + builder, + unit.result(), + teleArgsTerm.ref(), + unit.telescope().size() + ); + + builder.returnWith(result); + } + + public static @NotNull FreeJavaExpr serializeTermUnderTele( + @NotNull FreeExprBuilder builder, + @NotNull Term term, + @NotNull FreeJavaExpr argsTerm, + int size + ) { + return serializeTermUnderTele(builder, term, AbstractExprializer.fromSeq(builder, Constants.CD_Term, argsTerm, size)); + } + + public static @NotNull FreeJavaExpr serializeTermUnderTele( + @NotNull FreeExprBuilder builder, + @NotNull Term term, + @NotNull ImmutableSeq argTerms + ) { + return new TermExprializer(builder, argTerms) + .serialize(term); + } + + public static @NotNull FreeJavaExpr serializeTerm(@NotNull FreeCodeBuilder builder, @NotNull Term term) { + return serializeTermUnderTele(builder, term, ImmutableSeq.empty()); + } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/serializers/MemberSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/serializers/MemberSerializer.java new file mode 100644 index 000000000..a5ff7d091 --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/serializers/MemberSerializer.java @@ -0,0 +1,47 @@ +// 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.compiler.serializers; + +import kala.collection.immutable.ImmutableSeq; +import org.aya.compiler.free.FreeClassBuilder; +import org.aya.compiler.free.FreeCodeBuilder; +import org.aya.compiler.free.FreeJavaExpr; +import org.aya.compiler.free.FreeUtil; +import org.aya.syntax.compile.JitClass; +import org.aya.syntax.compile.JitMember; +import org.aya.syntax.core.def.AnyDef; +import org.aya.syntax.core.def.MemberDef; +import org.aya.syntax.core.term.SortTerm; +import org.aya.syntax.core.term.call.MemberCall; +import org.jetbrains.annotations.NotNull; + +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; + +public final class MemberSerializer extends JitTeleSerializer { + public MemberSerializer() { super(JitMember.class); } + @Override protected @NotNull Class callClass() { return MemberCall.class; } + + @Override + protected @NotNull ImmutableSeq superConParams() { + return super.superConParams().appendedAll(ImmutableSeq.of( + FreeUtil.fromClass(JitClass.class), + ConstantDescs.CD_int, + FreeUtil.fromClass(SortTerm.class) + )); + } + + @Override + protected @NotNull ImmutableSeq superConArgs(@NotNull FreeCodeBuilder builder, MemberDef unit) { + return super.superConArgs(builder, unit).appendedAll(ImmutableSeq.of( + AbstractExprializer.getInstance(builder, AnyDef.fromVar(unit.classRef())), + builder.iconst(unit.index()), + serializeTerm(builder, unit.type()) + )); + } + + @Override public @NotNull MemberSerializer serialize(@NotNull FreeClassBuilder builder, MemberDef unit) { + buildFramework(builder, unit, _ -> { }); + return this; + } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/serializers/ModuleSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/serializers/ModuleSerializer.java new file mode 100644 index 000000000..22c757b9d --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/serializers/ModuleSerializer.java @@ -0,0 +1,71 @@ +// 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.compiler.serializers; + +import kala.collection.immutable.ImmutableSeq; +import org.aya.compiler.free.FreeClassBuilder; +import org.aya.compiler.free.FreeJavaBuilder; +import org.aya.primitive.ShapeFactory; +import org.aya.syntax.core.def.*; +import org.aya.syntax.ref.QPath; +import org.jetbrains.annotations.NotNull; + +import java.lang.constant.ClassDesc; + +import static org.aya.compiler.serializers.NameSerializer.getReference; + +/** + * Serializing a module, note that it may not a file module, so we need not to make importing. + */ +public final class ModuleSerializer { + public record ModuleResult( + @NotNull QPath name, + @NotNull ImmutableSeq defs + ) { } + + private final @NotNull ShapeFactory shapeFactory; + + public ModuleSerializer(@NotNull ShapeFactory shapeFactory) { + this.shapeFactory = shapeFactory; + } + + private void serializeCons(@NotNull FreeClassBuilder builder, @NotNull DataDef dataDef) { + var ser = new ConSerializer(); + dataDef.body.forEach(con -> ser.serialize(builder, con)); + } + + private void serializeMems(@NotNull FreeClassBuilder builder, @NotNull ClassDef classDef) { + var ser = new MemberSerializer(); + classDef.members().forEach(mem -> ser.serialize(builder, mem)); + } + + private void doSerialize(@NotNull FreeClassBuilder builder, @NotNull TyckDef unit) { + switch (unit) { + case FnDef teleDef -> new FnSerializer(shapeFactory) + .serialize(builder, teleDef); + case DataDef dataDef -> { + new DataSerializer(shapeFactory).serialize(builder, dataDef); + serializeCons(builder, dataDef); + } + case ConDef conDef -> new ConSerializer() + .serialize(builder, conDef); + case PrimDef primDef -> new PrimSerializer() + .serialize(builder, primDef); + case ClassDef classDef -> { + new ClassSerializer() + .serialize(builder, classDef); + serializeMems(builder, classDef); + } + case MemberDef memberDef -> new MemberSerializer() + .serialize(builder, memberDef); + } + } + + public Carrier serialize(@NotNull FreeJavaBuilder builder, ModuleResult unit) { + var desc = ClassDesc.of(getReference(unit.name, null, NameSerializer.NameType.ClassName)); + + return builder.buildClass(desc, Object.class, cb -> { + unit.defs.forEach(def -> doSerialize(cb, def)); + }); + } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/NameSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/serializers/NameSerializer.java similarity index 73% rename from jit-compiler/src/main/java/org/aya/compiler/NameSerializer.java rename to jit-compiler/src/main/java/org/aya/compiler/serializers/NameSerializer.java index 4f51e7a0f..1310da976 100644 --- a/jit-compiler/src/main/java/org/aya/compiler/NameSerializer.java +++ b/jit-compiler/src/main/java/org/aya/compiler/serializers/NameSerializer.java @@ -1,11 +1,10 @@ // 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.compiler; +package org.aya.compiler.serializers; import kala.collection.SeqView; import kala.collection.immutable.ImmutableSeq; import org.aya.syntax.core.def.AnyDef; -import org.aya.syntax.core.def.TyckAnyDef; import org.aya.syntax.ref.DefVar; import org.aya.syntax.ref.ModulePath; import org.aya.syntax.ref.QName; @@ -14,24 +13,22 @@ import org.jetbrains.annotations.Nullable; import java.io.File; +import java.lang.constant.ClassDesc; import java.util.Objects; import java.util.stream.IntStream; -import static org.aya.compiler.AyaSerializer.PACKAGE_BASE; +import static org.aya.compiler.serializers.AyaSerializer.PACKAGE_BASE; public interface NameSerializer { - String PATH_SEPARATOR = File.separator; - String PACKAGE_SEPARATOR = "."; - String NEST_CLASS_SEPARATOR = "."; - String CLASS_NAME_SEPARATOR = "$"; + char MAGIC_CHAR = '_'; enum NameType { // class reference in java source code, i.e. "foo.bar.nestClass" - ClassReference(PACKAGE_SEPARATOR, NEST_CLASS_SEPARATOR), + ClassReference(".", "."), // class name that used for loading class, i.e. "foo.bar$nestClass" - ClassName(PACKAGE_SEPARATOR, CLASS_NAME_SEPARATOR), + ClassName(".", "$"), // class path that used for finding class file, i.e. "foo/bar$nestClass" - ClassPath(PATH_SEPARATOR, CLASS_NAME_SEPARATOR); + ClassPath(File.separator, "$"); public final @NotNull String packageSeparator; public final @NotNull String classNameSeparator; @@ -64,10 +61,6 @@ enum NameType { return prefix + type.classNameSeparator + javifyClassName(module, name); } - static @NotNull String getClassRef(@NotNull QPath module, @Nullable String name) { - return getReference(module, name, NameType.ClassReference); - } - static @NotNull String getClassName(@NotNull QName name) { return getClassName(name.module(), name.name()); } @@ -76,25 +69,16 @@ enum NameType { return getReference(module, name, NameType.ClassName); } - static @NotNull String getModuleReference(@NotNull QPath module) { - return getClassRef(module, null); - } - - static @NotNull String getClassRef(@NotNull QName name) { - return getClassRef(name.module(), name.name()); + static @NotNull String getModuleClassName(@NotNull QPath module) { + return getClassName(module, null); } - static @NotNull String getClassRef(@NotNull DefVar ref) { - return getClassRef(TyckAnyDef.make(ref.core)); + static @NotNull String getClassName(@NotNull AnyDef def) { + return getClassName(def.qualifiedName()); } - /** - * Obtain the java qualified name of certain {@link AnyDef def} - * - * @see #getReference(QPath, String, NameType) - */ - static @NotNull String getClassRef(@NotNull AnyDef def) { - return getClassRef(def.qualifiedName()); + static @NotNull ClassDesc getClassDesc(@NotNull AnyDef def) { + return ClassDesc.of(getClassName(def)); } static @NotNull String javifyClassName(@NotNull QPath path, @Nullable String name) { @@ -110,16 +94,16 @@ enum NameType { } /** - * Generate a java friendly class name of {@param ids}, this function should be one-to-one + * Generate a java friendly class name of {@param ids} * * @param ids the qualified id that may refer to a {@link org.aya.syntax.concrete.stmt.ModuleName module} - * or {@link org.aya.syntax.concrete.stmt.QualifiedID definition}, + * or a {@link org.aya.syntax.concrete.stmt.QualifiedID definition}, * note that {@link org.aya.syntax.concrete.stmt.ModuleName.ThisRef} should * be replaced with the name of the file level module. */ static @NotNull String javifyClassName(@NotNull SeqView ids) { return ids.map(NameSerializer::javify) - .joinToString("$", "$", ""); + .joinToString(String.valueOf(MAGIC_CHAR), String.valueOf(MAGIC_CHAR), ""); } ImmutableSeq keywords = ImmutableSeq.of( @@ -135,11 +119,13 @@ enum NameType { * Note that the result may not be used for class name, see {@link #javifyClassName} */ static @NotNull String javify(String name) { - if (keywords.contains(name)) return "_$" + name; + if (keywords.contains(name)) return MAGIC_CHAR + name; return name.codePoints().flatMap(x -> - x == '$' ? "$$".chars() - : Character.isJavaIdentifierPart(x) ? IntStream.of(x) - : ("$" + x).chars()) + x == MAGIC_CHAR + ? String.valueOf(MAGIC_CHAR).repeat(2).chars() + : Character.isJavaIdentifierPart(x) + ? IntStream.of(x) + : (String.valueOf(MAGIC_CHAR) + x).chars()) .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) .toString(); } diff --git a/jit-compiler/src/main/java/org/aya/compiler/serializers/PatternExprializer.java b/jit-compiler/src/main/java/org/aya/compiler/serializers/PatternExprializer.java new file mode 100644 index 000000000..a26e608a4 --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/serializers/PatternExprializer.java @@ -0,0 +1,66 @@ +// 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.compiler.serializers; + +import kala.collection.immutable.ImmutableSeq; +import org.aya.compiler.free.FreeExprBuilder; +import org.aya.compiler.free.FreeJavaExpr; +import org.aya.syntax.core.pat.Pat; +import org.aya.syntax.core.term.Term; +import org.aya.syntax.core.term.call.ConCallLike; +import org.aya.syntax.ref.LocalVar; +import org.aya.util.error.Panic; +import org.jetbrains.annotations.NotNull; + +public final class PatternExprializer extends AbstractExprializer { + private final boolean allowLocalTerm; + + PatternExprializer(@NotNull FreeExprBuilder builder, boolean allowLocalTerm) { + super(builder); + this.allowLocalTerm = allowLocalTerm; + } + + private @NotNull FreeJavaExpr serializeTerm(@NotNull Term term) { + return new TermExprializer(builder, ImmutableSeq.empty(), allowLocalTerm) + .serialize(term); + } + + private @NotNull FreeJavaExpr serializeConHead(@NotNull ConCallLike.Head head) { + var termSer = new TermExprializer(builder, ImmutableSeq.empty(), allowLocalTerm); + + return builder.mkNew(ConCallLike.Head.class, ImmutableSeq.of( + getInstance(head.ref()), + builder.iconst(head.ulift()), + makeImmutableSeq(Term.class, head.ownerArgs().map(termSer::serialize)) + )); + } + + @Override protected @NotNull FreeJavaExpr doSerialize(@NotNull Pat term) { + return switch (term) { + case Pat.Misc misc -> builder.refEnum(misc); + // it is safe to new a LocalVar, this method will be called when meta solving only, + // but the meta solver will eat all LocalVar so that it will be happy. + case Pat.Bind bind -> builder.mkNew(Pat.Bind.class, ImmutableSeq.of( + builder.mkNew(LocalVar.class, ImmutableSeq.of(builder.aconst(bind.bind().name()))), + serializeTerm(bind.type()) + )); + case Pat.Con con -> builder.mkNew(Pat.Con.class, ImmutableSeq.of( + getInstance(con.ref()), + serializeToImmutableSeq(Pat.class, con.args()), + serializeConHead(con.head()) + )); + case Pat.ShapedInt shapedInt -> builder.mkNew(Pat.ShapedInt.class, ImmutableSeq.of( + builder.iconst(shapedInt.repr()), + getInstance(shapedInt.zero()), + getInstance(shapedInt.suc()), + serializeTerm(shapedInt.type()) + )); + case Pat.Meta _ -> Panic.unreachable(); + case Pat.Tuple(var l, var r) -> builder.mkNew(Pat.Tuple.class, ImmutableSeq.of( + doSerialize(l), doSerialize(r) + )); + }; + } + + @Override public @NotNull FreeJavaExpr serialize(Pat unit) { return doSerialize(unit); } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/serializers/PatternSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/serializers/PatternSerializer.java new file mode 100644 index 000000000..b42b6204f --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/serializers/PatternSerializer.java @@ -0,0 +1,281 @@ +// 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.compiler.serializers; + +import kala.collection.SeqView; +import kala.collection.immutable.ImmutableSeq; +import kala.collection.immutable.primitive.ImmutableIntSeq; +import kala.function.TriConsumer; +import kala.range.primitive.IntRange; +import org.aya.compiler.free.Constants; +import org.aya.compiler.free.FreeCodeBuilder; +import org.aya.compiler.free.FreeJavaExpr; +import org.aya.compiler.free.FreeUtil; +import org.aya.compiler.free.data.LocalVariable; +import org.aya.syntax.core.pat.Pat; +import org.aya.syntax.core.term.Term; +import org.aya.syntax.core.term.TupTerm; +import org.aya.syntax.core.term.call.ConCallLike; +import org.aya.syntax.core.term.repr.IntegerTerm; +import org.aya.util.error.Panic; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.UnknownNullability; + +import java.lang.constant.ConstantDescs; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +/** + * We do not serialize meta solve, it is annoying + */ +public final class PatternSerializer { + @FunctionalInterface + public interface SuccessContinuation extends TriConsumer { + } + + // Just for checking + public final static class Once implements Consumer { + public static @NotNull Once of(@NotNull Consumer run) { return new Once(run); } + private final @NotNull Consumer run; + private boolean dirty = false; + + public Once(@NotNull Consumer run) { this.run = run; } + + @Override + public void accept(FreeCodeBuilder freeClassBuilder) { + if (dirty) throw new Panic("Once"); + dirty = true; + this.run.accept(freeClassBuilder); + } + } + + public record Matching( + int bindCount, @NotNull ImmutableSeq patterns, + @NotNull SuccessContinuation onSucc + ) { } + + @UnknownNullability LocalVariable result; + @UnknownNullability LocalVariable matchState; + @UnknownNullability LocalVariable subMatchState; + + private final @NotNull ImmutableSeq argNames; + private final @NotNull Consumer onFailed; + private final boolean isOverlap; + private int bindCount = 0; + + public PatternSerializer( + @NotNull ImmutableSeq argNames, + @NotNull Consumer onFailed, + boolean isOverlap + ) { + this.argNames = argNames; + this.onFailed = onFailed; + this.isOverlap = isOverlap; + } + + /// region Serializing + + private void doSerialize( + @NotNull FreeCodeBuilder builder, + @NotNull Pat pat, + @NotNull FreeJavaExpr term, + @NotNull Once onMatchSucc + ) { + switch (pat) { + case Pat.Misc misc -> { + switch (misc) { + case Absurd -> AyaSerializer.execPanic(builder); + case UntypedBind -> { + onMatchBind(builder, term); + onMatchSucc.accept(builder); + } + } + } + case Pat.Bind _ -> { + onMatchBind(builder, term); + onMatchSucc.accept(builder); + } + + // TODO: match IntegerTerm / ListTerm first + case Pat.Con con -> builder.ifInstanceOf(term, FreeUtil.fromClass(ConCallLike.class), + (builder1, conTerm) -> { + builder1.ifRefEqual( + AbstractExprializer.getRef(builder1, CallKind.Con, conTerm.ref()), + AbstractExprializer.getInstance(builder1, con.ref()), + builder2 -> { + var conArgsTerm = builder2.invoke(Constants.CONARGS, conTerm.ref(), ImmutableSeq.empty()); + var conArgs = AbstractExprializer.fromSeq( + builder2, + Constants.CD_Term, + conArgsTerm, + con.args().size() + ); + + doSerialize(builder2, con.args().view(), conArgs.view(), onMatchSucc); + }, null /* mismatch, do nothing */ + ); + }, this::onStuck); + case Pat.Meta _ -> Panic.unreachable(); + case Pat.ShapedInt shapedInt -> multiStage(builder, term, ImmutableSeq.of( + // mTerm -> solveMeta(shapedInt, mTerm), + (builder0, mTerm) -> + matchInt(builder0, shapedInt, mTerm), + (builder0, mTerm) -> + doSerialize(builder0, shapedInt.constructorForm(), builder.refVar(mTerm), + // There will a sequence of [subMatchState = true] if there are a lot of [Pat.ShapedInt], + // but our optimizer will fix them + Once.of(builder1 -> updateSubstate(builder1, true))) + ), onMatchSucc); + case Pat.Tuple(var l, var r) -> { + builder.ifInstanceOf(term, FreeUtil.fromClass(TupTerm.class), (builder0, tupTerm) -> { + var lhs = builder0.invoke(Constants.TUP_LHS, tupTerm.ref(), ImmutableSeq.empty()); + doSerialize(builder0, l, lhs, Once.of(builder1 -> { + var rhs = builder0.invoke(Constants.TUP_RHS, tupTerm.ref(), ImmutableSeq.empty()); + doSerialize(builder1, r, rhs, onMatchSucc); + })); + }, this::onStuck); + } + } + } + + /** + * Generate multi case matching, these local variable are available: + *

+ * Note that {@param preContinuation}s should not invoke {@param continuation}! + * + * @param term the expression be matched, not always a variable reference + * @param preContinuation matching cases, only the last one can invoke multiStage + * @param continuation on match success + */ + private void multiStage( + @NotNull FreeCodeBuilder builder, + @NotNull FreeJavaExpr term, + @NotNull ImmutableSeq> preContinuation, + @NotNull Once continuation + ) { + updateSubstate(builder, false); + var tmpName = builder.makeVar(Term.class, term); + + for (var pre : preContinuation) { + builder.ifNotTrue(subMatchState, builder0 -> { + pre.accept(builder0, tmpName); + }, null); + } + + builder.ifTrue(subMatchState, continuation, null); + } + + private void matchInt(@NotNull FreeCodeBuilder builder, @NotNull Pat.ShapedInt pat, @NotNull LocalVariable term) { + builder.ifInstanceOf(builder.refVar(term), FreeUtil.fromClass(IntegerTerm.class), (builder0, intTerm) -> { + var intTermRepr = builder0.invoke( + Constants.INT_REPR, + builder0.refVar(intTerm), + ImmutableSeq.empty() + ); + + builder0.ifIntEqual(intTermRepr, pat.repr(), builder1 -> { + // Pat.ShapedInt provides no binds + updateSubstate(builder1, true); + }, null); + }, null); + } + + /** + * @apiNote {@code pats.sizeEquals(terms)} + */ + private void doSerialize( + @NotNull FreeCodeBuilder builder, + @NotNull SeqView pats, + @NotNull SeqView terms, + @NotNull Once continuation + ) { + if (pats.isEmpty()) { + continuation.accept(builder); + return; + } + + var pat = pats.getFirst(); + var term = terms.getFirst(); + doSerialize(builder, pat, term, + Once.of(builder0 -> doSerialize(builder0, pats.drop(1), terms.drop(1), continuation))); + } + + /// endregion Serializing + + /// region Java Source Code Generate API + + private void onStuck(@NotNull FreeCodeBuilder builder) { + if (!isOverlap) builder.breakOut(); + } + + private void updateSubstate(@NotNull FreeCodeBuilder builder, boolean state) { + builder.updateVar(subMatchState, builder.iconst(state)); + } + + private void updateState(@NotNull FreeCodeBuilder builder, int state) { + builder.updateVar(matchState, builder.iconst(state)); + } + + private void onMatchBind(@NotNull FreeCodeBuilder builder, @NotNull FreeJavaExpr term) { + builder.exec( + builder.invoke(Constants.MUTSEQ_SET, result.ref(), ImmutableSeq.of( + builder.iconst(bindCount++), + term + )) + ); + } + + /// endregion Java Source Code Generate API + + public PatternSerializer serialize(@NotNull FreeCodeBuilder builder, @NotNull ImmutableSeq unit) { + if (unit.isEmpty()) { + onFailed.accept(builder); + return this; + } + + var bindSize = unit.mapToInt(ImmutableIntSeq.factory(), Matching::bindCount); + int maxBindSize = bindSize.max(); + + // var result = MutableSeq.fill(maxBindCount, null); + result = builder.makeVar(Constants.CD_MutableSeq, + builder.invoke(Constants.MUTSEQ, ImmutableSeq.of( + builder.iconst(maxBindSize), + builder.aconstNull(Constants.CD_Term) + )) + ); + // whether the match success or mismatch, 0 implies mismatch + matchState = builder.makeVar(ConstantDescs.CD_int, builder.iconst(0)); + subMatchState = builder.makeVar(ConstantDescs.CD_Boolean, builder.iconst(false)); + + builder.breakable(mBuilder -> { + unit.forEachIndexed((idx, clause) -> { + var jumpCode = idx + 1; + bindCount = 0; + doSerialize( + mBuilder, + clause.patterns.view(), + argNames.view(), + Once.of(builder1 -> { + updateState(builder1, jumpCode); + builder1.breakOut(); + }) + ); + }); + }); + + // 0 ..= unit.size() + var range = IntRange.closed(0, unit.size()).collect(ImmutableIntSeq.factory()); + builder.switchCase(matchState, range, (mBuilder, i) -> { + if (i == 0) { + onFailed.accept(mBuilder); + return; + } + + assert i > 0; + var realIdx = i - 1; + unit.get(realIdx).onSucc.accept(this, mBuilder, bindSize.get(realIdx)); + }, AyaSerializer::returnPanic); + + return this; + } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/serializers/PrimSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/serializers/PrimSerializer.java new file mode 100644 index 000000000..09e6da30f --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/serializers/PrimSerializer.java @@ -0,0 +1,37 @@ +// 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.compiler.serializers; + +import kala.collection.immutable.ImmutableSeq; +import org.aya.compiler.free.FreeClassBuilder; +import org.aya.compiler.free.FreeCodeBuilder; +import org.aya.compiler.free.FreeJavaExpr; +import org.aya.compiler.free.FreeUtil; +import org.aya.syntax.compile.JitPrim; +import org.aya.syntax.core.def.PrimDef; +import org.aya.syntax.core.term.call.PrimCall; +import org.jetbrains.annotations.NotNull; + +import java.lang.constant.ClassDesc; + +public final class PrimSerializer extends JitTeleSerializer { + public PrimSerializer() { + super(JitPrim.class); + } + @Override protected @NotNull Class callClass() { return PrimCall.class; } + + @Override + protected @NotNull ImmutableSeq superConParams() { + return super.superConParams().appended(FreeUtil.fromClass(PrimDef.ID.class)); + } + + @Override + protected @NotNull ImmutableSeq superConArgs(@NotNull FreeCodeBuilder builder, PrimDef unit) { + return super.superConArgs(builder, unit).appended(builder.refEnum(unit.id)); + } + + @Override public @NotNull PrimSerializer serialize(@NotNull FreeClassBuilder builder, PrimDef unit) { + buildFramework(builder, unit, _ -> { }); + return this; + } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/serializers/TermExprializer.java b/jit-compiler/src/main/java/org/aya/compiler/serializers/TermExprializer.java new file mode 100644 index 000000000..fe6b2839e --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/serializers/TermExprializer.java @@ -0,0 +1,337 @@ +// 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.compiler.serializers; + +import kala.collection.immutable.ImmutableArray; +import kala.collection.immutable.ImmutableSeq; +import kala.collection.mutable.MutableLinkedHashMap; +import kala.tuple.Tuple; +import kala.tuple.Tuple2; +import org.aya.compiler.free.ArgumentProvider; +import org.aya.compiler.free.Constants; +import org.aya.compiler.free.FreeExprBuilder; +import org.aya.compiler.free.FreeJavaExpr; +import org.aya.compiler.free.data.MethodRef; +import org.aya.generic.stmt.Shaped; +import org.aya.prettier.FindUsage; +import org.aya.syntax.compile.JitFn; +import org.aya.syntax.core.Closure; +import org.aya.syntax.core.def.AnyDef; +import org.aya.syntax.core.def.FnDef; +import org.aya.syntax.core.term.*; +import org.aya.syntax.core.term.call.*; +import org.aya.syntax.core.term.marker.TyckInternal; +import org.aya.syntax.core.term.repr.*; +import org.aya.syntax.core.term.xtt.*; +import org.aya.syntax.ref.LocalVar; +import org.aya.util.error.Panic; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.constant.ClassDesc; +import java.util.Objects; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * Build the "constructor form" of {@link Term}, but in Java. + */ +public final class TermExprializer extends AbstractExprializer { + /** + * Terms that should be instantiated + */ + private final @NotNull ImmutableSeq instantiates; + private final @NotNull MutableLinkedHashMap binds; + + /** + * Whether allow LocalTerm, false in default (in order to report unexpected LocalTerm) + */ + private final boolean allowLocalTerm; + + public TermExprializer(@NotNull FreeExprBuilder builder, @NotNull ImmutableSeq instantiates) { + this(builder, instantiates, false); + } + + public TermExprializer(@NotNull FreeExprBuilder builder, @NotNull ImmutableSeq instantiates, boolean allowLocalTer) { + super(builder); + this.instantiates = instantiates; + this.allowLocalTerm = allowLocalTer; + this.binds = MutableLinkedHashMap.of(); + } + + private TermExprializer( + @NotNull FreeExprBuilder builder, + @NotNull MutableLinkedHashMap newBinds, + boolean allowLocalTerm + ) { + super(builder); + this.instantiates = ImmutableSeq.empty(); + this.binds = newBinds; + this.allowLocalTerm = allowLocalTerm; + } + + private @NotNull FreeJavaExpr serializeApplicable(@NotNull Shaped.Applicable applicable) { + return switch (applicable) { + case IntegerOps.ConRule conRule -> builder.mkNew(IntegerOps.ConRule.class, ImmutableSeq.of( + getInstance(conRule.ref()), + doSerialize(conRule.zero()) + )); + case IntegerOps.FnRule fnRule -> builder.mkNew(IntegerOps.FnRule.class, ImmutableSeq.of( + getInstance(fnRule.ref()), + builder.refEnum(fnRule.kind()) + )); + case ListOps.ConRule conRule -> builder.mkNew(ListOps.ConRule.class, ImmutableSeq.of( + getInstance(conRule.ref()), + doSerialize(conRule.empty()) + )); + default -> Panic.unreachable(); + }; + } + + /** + * TODO: remove {@param fixed}, it is used by FnCall only + *

+ * This code requires that {@link FnCall}, {@link RuleReducer.Fn} and {@link RuleReducer.Con} + * {@code ulift} is the second parameter, {@code args.get(i)} is the {@code i + 3}th parameter + * + * @param reducibleType the ref class of {@param reducible}, used when {@param fixed} is true + * @param fixed whether {@param reducible} has fixed `invoke`, + * i.e. {@link JitFn} does but {@link org.aya.generic.stmt.Shaped.Applicable} doesn't. + */ + private @NotNull FreeJavaExpr buildReducibleCall( + @Nullable ClassDesc reducibleType, + @NotNull FreeJavaExpr reducible, + @NotNull Class callName, + int ulift, + @NotNull ImmutableSeq> args, + boolean fixed + ) { + var seredArgs = args.map(x -> x.map(this::doSerialize)); + var seredSeq = seredArgs.map(x -> makeImmutableSeq(Term.class, x)); + var flatArgs = seredArgs.flatMap(x -> x); + + var callArgs = new FreeJavaExpr[seredSeq.size() + 2]; + callArgs[0] = reducible; + callArgs[1] = builder.iconst(0); // elevate later + for (var i = 0; i < seredSeq.size(); ++i) { + callArgs[i + 2] = seredSeq.get(i); + } + + var onStuck = makeThunk(te -> te.builder.mkNew(callName, ImmutableArray.Unsafe.wrap(callArgs))); + var finalInvocation = fixed + ? builder.invoke( + FnSerializer.resolveInvoke(Objects.requireNonNull(reducibleType), flatArgs.size()), + reducible, + flatArgs.view().prepended(onStuck).toImmutableSeq()) + : builder.invoke(Constants.REDUCIBLE_INVOKE, reducible, ImmutableSeq.of( + onStuck, + makeImmutableSeq(Term.class, flatArgs) + )); + + return ulift == 0 + ? finalInvocation + : builder.invoke(Constants.ELEVATE, finalInvocation, ImmutableSeq.of(builder.iconst(ulift))); + } + + @Override protected @NotNull FreeJavaExpr doSerialize(@NotNull Term term) { + return switch (term) { + case FreeTerm(var bind) -> { + // It is possible that we meet bind here, + // the serializer will instantiate some variable while serializing LamTerm + var subst = binds.getOrNull(bind); + if (subst == null) { + throw new Panic("No substitution for " + bind + " during serialization"); + } + + yield subst; + } + case TyckInternal i -> throw new Panic(i.getClass().toString()); + case Callable.SharableCall call when call.ulift() == 0 && call.args().isEmpty() -> + getCallInstance(CallKind.from(call), call.ref()); + case ClassCall(var ref, var ulift, var args) -> builder.mkNew(ClassCall.class, ImmutableSeq.of( + getInstance(ref), + builder.iconst(ulift), + serializeClosureToImmutableSeq(args) + )); + case MemberCall(var of, var ref, var ulift, var args) -> builder.mkNew(MemberCall.class, ImmutableSeq.of( + doSerialize(of), + getInstance(ref), + builder.iconst(ulift), + serializeToImmutableSeq(Term.class, args) + )); + case AppTerm appTerm -> makeAppNew(AppTerm.class, appTerm.fun(), appTerm.arg()); + case LocalTerm _ when !allowLocalTerm -> throw new Panic("LocalTerm"); + case LocalTerm(var index) -> builder.mkNew(LocalTerm.class, ImmutableSeq.of(builder.iconst(index))); + case LamTerm lamTerm -> builder.mkNew(LamTerm.class, ImmutableSeq.of(serializeClosure(lamTerm.body()))); + case DataCall(var ref, var ulift, var args) -> builder.mkNew(DataCall.class, ImmutableSeq.of( + getInstance(ref), + builder.iconst(ulift), + serializeToImmutableSeq(Term.class, args) + )); + case ConCall(var head, var args) -> builder.mkNew(ConCall.class, ImmutableSeq.of( + getInstance(head.ref()), + serializeToImmutableSeq(Term.class, head.ownerArgs()), + builder.iconst(head.ulift()), + serializeToImmutableSeq(Term.class, args) + )); + case FnCall call -> { + var anyDef = switch (call.ref()) { + case JitFn jit -> jit; + case FnDef.Delegate def -> AnyDef.fromVar(def.ref); + }; + + var ref = getInstance(anyDef); + var args = call.args(); + yield buildReducibleCall(NameSerializer.getClassDesc(anyDef), ref, FnCall.class, call.ulift(), ImmutableSeq.of(args), true); + } + case RuleReducer.Con conRuler -> buildReducibleCall( + null, + serializeApplicable(conRuler.rule()), + RuleReducer.Con.class, conRuler.ulift(), + ImmutableSeq.of(conRuler.ownerArgs(), conRuler.conArgs()), + false + ); + case RuleReducer.Fn fnRuler -> buildReducibleCall( + null, + serializeApplicable(fnRuler.rule()), + RuleReducer.Fn.class, fnRuler.ulift(), + ImmutableSeq.of(fnRuler.args()), + false + ); + // TODO: make the resolving const + case SortTerm sort when sort.equals(SortTerm.Type0) -> + builder.refField(builder.resolver().resolve(SortTerm.class, "Type0")); + case SortTerm sort when sort.equals(SortTerm.ISet) -> + builder.refField(builder.resolver().resolve(SortTerm.class, "ISet")); + case SortTerm(var kind, var ulift) -> + builder.mkNew(SortTerm.class, ImmutableSeq.of(builder.refEnum(kind), builder.iconst(ulift))); + case DepTypeTerm(var kind, var param, var body) -> builder.mkNew(DepTypeTerm.class, ImmutableSeq.of( + builder.refEnum(kind), + doSerialize(param), + serializeClosure(body) + )); + case CoeTerm(var type, var r, var s) -> builder.mkNew(CoeTerm.class, ImmutableSeq.of( + serializeClosure(type), + doSerialize(r), + doSerialize(s) + )); + case ProjTerm(var of, var fst) -> builder.mkNew(ProjTerm.class, ImmutableSeq.of( + doSerialize(of), + builder.iconst(fst) + )); + case PAppTerm(var fun, var arg, var a, var b) -> makeAppNew(PAppTerm.class, + fun, arg, a, b + ); + case EqTerm(var A, var a, var b) -> builder.mkNew(EqTerm.class, ImmutableSeq.of( + serializeClosure(A), + doSerialize(a), doSerialize(b) + )); + case DimTyTerm _ -> builder.refEnum(DimTyTerm.INSTANCE); + case DimTerm dim -> builder.refEnum(dim); + case TupTerm(var l, var r) -> builder.mkNew(TupTerm.class, ImmutableSeq.of( + doSerialize(l), doSerialize(r) + )); + case PrimCall(var ref, var ulift, var args) -> builder.mkNew(PrimCall.class, ImmutableSeq.of( + getInstance(ref), + builder.iconst(ulift), + serializeToImmutableSeq(Term.class, args) + )); + case IntegerTerm(var repr, var zero, var suc, var type) -> builder.mkNew(IntegerTerm.class, ImmutableSeq.of( + builder.iconst(repr), + getInstance(zero), + getInstance(suc), + doSerialize(type) + )); + case ListTerm(var repr, var nil, var cons, var type) -> builder.mkNew(ListTerm.class, ImmutableSeq.of( + makeImmutableSeq(builder, Constants.IMMTREESEQ, Term.class, repr.map(this::doSerialize)), + getInstance(nil), + getInstance(cons), + doSerialize(type) + )); + case StringTerm stringTerm -> builder.mkNew(StringTerm.class, ImmutableSeq.of( + builder.aconst(stringTerm.string()) + )); + case ClassCastTerm(var classRef, var subterm, var rember, var forgor) -> + builder.mkNew(ClassCastTerm.class, ImmutableSeq.of( + getInstance(classRef), + serialize(subterm), + serializeClosureToImmutableSeq(rember), + serializeClosureToImmutableSeq(forgor) + )); + case MatchTerm(var discr, var ty, var clauses) -> + // TODO + builder.aconstNull(Constants.CD_Term); + + case NewTerm(var classCall) -> builder.mkNew(NewTerm.class, ImmutableSeq.of(doSerialize(classCall))); + }; + } + + private @NotNull FreeJavaExpr makeLambda( + @NotNull MethodRef lambdaType, + @NotNull BiFunction cont + ) { + var binds = MutableLinkedHashMap.from(this.binds); + var entries = binds.toImmutableSeq(); + return builder.mkLambda(entries.map(Tuple2::component2), lambdaType, (ap, builder) -> { + var captured = entries.mapIndexed((i, tup) -> Tuple.of(tup.component1(), ap.capture(i))); + var result = cont.apply(ap, new TermExprializer(this.builder, MutableLinkedHashMap.from(captured), this.allowLocalTerm)); + builder.returnWith(result); + }); + } + + // TODO: unify with makeClosure + private @NotNull FreeJavaExpr makeThunk(@NotNull Function cont) { + return makeLambda(Constants.THUNK, (_, te) -> cont.apply(te)); + } + + private @NotNull FreeJavaExpr makeClosure(@NotNull BiFunction cont) { + return makeLambda(Constants.CLOSURE, (ap, te) -> cont.apply(te, ap.arg(0).ref())); + } + + private @NotNull FreeJavaExpr serializeClosureToImmutableSeq(@NotNull ImmutableSeq cls) { + return makeImmutableSeq(Closure.class, cls.map(this::serializeClosure)); + } + + private @NotNull FreeJavaExpr with( + @NotNull LocalVar var, + @NotNull FreeJavaExpr subst, + @NotNull Supplier continuation + ) { + this.binds.put(var, subst); + var result = continuation.get(); + this.binds.remove(var); + return result; + } + + private @NotNull FreeJavaExpr serializeClosure(@NotNull Closure body) { + if (body instanceof Closure.Const(var inside)) return serializeConst(inside); + + var var = new LocalVar(""); + var appliedBody = body.apply(var); + if (FindUsage.free(appliedBody, var) == 0) return serializeConst(appliedBody); + + var closure = makeClosure((te, arg) -> + te.with(var, arg, () -> te.doSerialize(appliedBody))); + + return builder.mkNew(Closure.Jit.class, ImmutableSeq.of(closure)); + } + + private @NotNull FreeJavaExpr serializeConst(Term appliedBody) { + return builder.invoke(Constants.CLOSURE_MKCONST, ImmutableSeq.of(doSerialize(appliedBody))); + } + + private @NotNull FreeJavaExpr makeAppNew(@NotNull Class className, Term... terms) { + var obj = builder.mkNew(className, ImmutableSeq.from(terms).map(this::doSerialize)); + return builder.invoke(Constants.BETAMAKE, obj, ImmutableSeq.empty()); + } + + @Override public @NotNull FreeJavaExpr serialize(Term unit) { + binds.clear(); + var vars = ImmutableSeq.fill(instantiates.size(), i -> new LocalVar("arg" + i)); + unit = unit.instantiateTeleVar(vars.view()); + vars.forEachWith(instantiates, binds::put); + + return doSerialize(unit); + } +} diff --git a/jit-compiler/src/test/java/CompileTest.java b/jit-compiler/src/test/java/CompileTest.java index 1ede4a8d3..e14a85be7 100644 --- a/jit-compiler/src/test/java/CompileTest.java +++ b/jit-compiler/src/test/java/CompileTest.java @@ -2,10 +2,11 @@ // Use of this source code is governed by the MIT license that can be found in the LICENSE.md file. import kala.collection.immutable.ImmutableSeq; -import org.aya.compiler.FileSerializer; -import org.aya.compiler.ModuleSerializer; -import org.aya.compiler.NameGenerator; -import org.aya.compiler.TermExprializer; +import org.aya.compiler.free.morphism.SourceClassBuilder; +import org.aya.compiler.free.morphism.SourceCodeBuilder; +import org.aya.compiler.free.morphism.SourceFreeJavaBuilder; +import org.aya.compiler.serializers.ModuleSerializer; +import org.aya.compiler.serializers.TermExprializer; import org.aya.prettier.AyaPrettierOptions; import org.aya.producer.AyaParserImpl; import org.aya.resolve.ResolveInfo; @@ -28,9 +29,10 @@ import org.junit.jupiter.api.Test; import java.io.IOException; +import java.lang.constant.ConstantDescs; import java.nio.file.Path; -import static org.aya.compiler.NameSerializer.getClassName; +import static org.aya.compiler.serializers.NameSerializer.getClassName; public class CompileTest { @Test public void test0() { @@ -43,6 +45,8 @@ open inductive Vec (n : Nat) Type def plus (a b : Nat) : Nat elim a | O => b | S n => S (plus n b) + + def what : Nat -> Nat => fn n => plus n 1 """); // .filter(x -> x instanceof FnDef || x instanceof DataDef); var code = serializeFrom(result); @@ -83,9 +87,11 @@ def plus (a b : Nat) : Nat elim a } @Test public void serLam() { + var fjb = SourceFreeJavaBuilder.create(); + var dummy = new SourceCodeBuilder(new SourceClassBuilder(fjb, ConstantDescs.CD_Object, fjb.sourceBuilder()), fjb.sourceBuilder()); // \ t. (\0. 0 t) var lam = new LamTerm(new Closure.Jit(t -> new LamTerm(new Closure.Locns(new AppTerm(new LocalTerm(0), t))))); - var out = new TermExprializer(new NameGenerator(), ImmutableSeq.empty()) + var out = new TermExprializer(dummy, ImmutableSeq.empty()) .serialize(lam); System.out.println(out); @@ -97,10 +103,9 @@ public record TyckResult(@NotNull ImmutableSeq defs, @NotNull ResolveIn public static final ThrowingReporter REPORTER = new ThrowingReporter(AyaPrettierOptions.pretty()); public static @NotNull String serializeFrom(@NotNull TyckResult result) { - return new FileSerializer(result.info.shapeFactory()) - .serialize(new ModuleSerializer.ModuleResult( - DumbModuleLoader.DUMB_MODULE_NAME, result.defs.filterIsInstance(TopLevelDef.class))) - .result(); + return new ModuleSerializer(result.info.shapeFactory()) + .serialize(SourceFreeJavaBuilder.create(), new ModuleSerializer.ModuleResult( + DumbModuleLoader.DUMB_MODULE_NAME, result.defs.filterIsInstance(TopLevelDef.class))); } public static TyckResult tyck(@Language("Aya") @NotNull String code) { diff --git a/jit-compiler/src/test/java/CompileTester.java b/jit-compiler/src/test/java/CompileTester.java index 46ca39737..3cdda07df 100644 --- a/jit-compiler/src/test/java/CompileTester.java +++ b/jit-compiler/src/test/java/CompileTester.java @@ -1,8 +1,8 @@ // 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. -import org.aya.compiler.AyaSerializer; -import org.aya.compiler.NameSerializer; +import org.aya.compiler.serializers.AyaSerializer; +import org.aya.compiler.serializers.NameSerializer; import org.aya.resolve.module.DumbModuleLoader; import org.aya.syntax.compile.JitDef; import org.aya.util.FileUtil; @@ -24,7 +24,7 @@ public class CompileTester { public CompileTester(@NotNull String code) throws IOException { var root = Paths.get("build/tmp/testGenerated"); var genDir = root.resolve(AyaSerializer.PACKAGE_BASE); - FileUtil.writeString(baka = genDir.resolve("$baka.java"), code); + FileUtil.writeString(baka = genDir.resolve("_baka.java"), code); cl = new URLClassLoader(new URL[]{root.toUri().toURL()}); } @@ -36,7 +36,7 @@ public void compile() { var options = List.of("--enable-preview", "--release", "21"); var task = compiler.getTask(null, fileManager, null, options, null, compilationUnits); task.call(); - var fqName = NameSerializer.getClassRef(DumbModuleLoader.DUMB_MODULE_NAME, null); + var fqName = NameSerializer.getModuleClassName(DumbModuleLoader.DUMB_MODULE_NAME); cl.loadClass(fqName); } catch (ClassNotFoundException e) { throw new RuntimeException(e); diff --git a/jit-compiler/src/test/java/MiscTest.java b/jit-compiler/src/test/java/MiscTest.java deleted file mode 100644 index a5879d016..000000000 --- a/jit-compiler/src/test/java/MiscTest.java +++ /dev/null @@ -1,30 +0,0 @@ -// 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. -import org.aya.compiler.AyaSerializer; -import org.aya.compiler.NameSerializer; -import org.aya.syntax.ref.ModulePath; -import org.aya.syntax.ref.QName; -import org.aya.syntax.ref.QPath; -import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class MiscTest { - public static final @NotNull QPath TOP = new QPath(ModulePath.of("baka", "114514"), 2); - public static final @NotNull QPath SUB = TOP.derive("hentai"); - public static final @NotNull QName NAME = new QName(SUB, "urusai"); - - @Test public void test0() { - assertEquals(AyaSerializer.PACKAGE_BASE + ".baka.$114514", - NameSerializer.getClassRef(TOP, null)); - assertEquals(AyaSerializer.PACKAGE_BASE + ".baka.$114514.$114514$shinji", - NameSerializer.getClassRef(TOP, "shinji")); - assertEquals(AyaSerializer.PACKAGE_BASE + ".baka.$114514", - NameSerializer.getClassRef(SUB, null)); - assertEquals(AyaSerializer.PACKAGE_BASE + ".baka.$114514.$114514$hentai$shinji", - NameSerializer.getClassRef(SUB, "shinji")); - assertEquals(AyaSerializer.PACKAGE_BASE + ".baka.$114514.$114514$hentai$urusai", - NameSerializer.getClassRef(NAME.module(), NAME.name())); - } -} diff --git a/jit-compiler/src/test/java/RedBlackTreeTest.java b/jit-compiler/src/test/java/RedBlackTreeTest.java index 68c332186..d160c4c50 100644 --- a/jit-compiler/src/test/java/RedBlackTreeTest.java +++ b/jit-compiler/src/test/java/RedBlackTreeTest.java @@ -22,7 +22,7 @@ import java.util.function.Function; import java.util.function.IntFunction; -import static org.aya.compiler.NameSerializer.getClassName; +import static org.aya.compiler.serializers.NameSerializer.getClassName; import static org.junit.jupiter.api.Assertions.assertNotNull; public class RedBlackTreeTest { diff --git a/note/jit.md b/note/jit.md index 71d12636f..9c2630c41 100644 --- a/note/jit.md +++ b/note/jit.md @@ -9,9 +9,9 @@ Jit Unit 是一个需要被编译的单位, 它包括: ## Class Structure -所有 Jit Unit 都会被编译成一个 class, 它们之间的包含结构通过嵌套类的方式保留. +所有 Jit Unit 都会被编译成一个 class. -示例: `class-structure.aya` +示例: `test/class-structure.aya` ```aya inductive Nat @@ -21,24 +21,32 @@ inductive Nat def inc (n : Nat) : Nat => S n ``` -编译输出: `class$45structure.java` +> 注意这里的 test 是一个 aya 级别的目录, 也就是说需要用 `test::class-structor` 来引用这个 aya 模块. + +编译输出: `AYA/test/class_45structure.java` ```java -public final class class$45structure { - public static final class class$45structure$Nat extends JitData { - public static final class class$45structure$Nat$O extends JitCon { /* ... */ - } +package AYA.test; - public static final class class$45structure$Nat$S extends JitCon { /* ... */ - } +public final class _class_45structure { + public static final class _class_45structure_Nat extends JitData { // ... } - public static final class class$45structure$inc extends JitFn { /* ... */ + public static final class _class_45structure_Nat_O extends JitCon { /* ... */ + } + + public static final class _class_45structure_Nat_S extends JitCon { /* ... */ + } + + public static final class _class_45structure_inc extends JitFn { /* ... */ } } ``` +注意到 `Nat` 的构造子在序列化在外层, 并且所有 Jit Unit 的类名都有一个所在模块的前缀. +这是由于某些技术问题, 所有子模块的内容都会序列化到文件模块内. + > 关于编译结果中的类名部分,参见 [Name Mapping](#name-mapping) 章节 任何 Def 对应的类都需要继承对应的基类, 如 `JitData` 对应归纳数据类型, `JitCon` 对应构造子. @@ -55,13 +63,14 @@ public final class class$45structure { ## Name Mapping > ![WARNING] -> 这个章节仅适用于编译到 Java 源代码的编译器实现. +> 这个章节仅适用于编译到 Java 源代码的编译器实现. +> 编译到 Bytecode 的编译器实现需要处理的情况更少, 仅需要处理属于非法文件字符(如路径分隔符)的情况. 由于需要在 Java 编译器的限制, 并不是所有合法的 aya 符号名在 Java 中都合法, 因此需要对符号名进行转义. -* 对于是关键字的符号名, 将它转义成 `_$`. 如 `new` 转义成 `_$new` -* 对于符号名中的每个 `$`, 将它转义成 `$$`. -* 对于符号名中的每个不合法的 Java 符号名 code point, 将它转义成 `$`. 如 `+` 转义成 `$43`. +* 对于是关键字的符号名, 将它转义成 `_`. 如 `new` 转义成 `_new` +* 对于符号名中的每个 `_`, 将它转义成 `__`. +* 对于符号名中的每个不合法的 Java 符号名 code point, 将它转义成 `_`. 如 `+` 转义成 `_43`. ## 模式匹配 @@ -106,7 +115,7 @@ public Term invoke(Supplier onStuck, Term arg0, Term arg1) { if (!subMatchState) { if (arg0 instanceof ConCallLike var0) { - if (Nat$O.INSTANCE.ref() == var0.ref()) { + if (Nat_O.INSTANCE.ref() == var0.ref()) { // 匹配: b // b 是一个 Pat.Bind, 匹配总是成功 result[0] = var1; @@ -141,7 +150,7 @@ public Term invoke(Supplier onStuck, Term arg0, Term arg1) { } case 2 -> { // | S a, b => S (plus a b) - return new ConCall(Nat$S.INSTANCE, 0, ImmutableSeq.of(plus.invoke( + return new ConCall(Nat_S.INSTANCE, 0, ImmutableSeq.of(plus.invoke( () -> new FnCall(/* ... */), result[0], result[1] ))); diff --git a/syntax/src/main/java/org/aya/generic/Constants.java b/syntax/src/main/java/org/aya/generic/Constants.java index 1cb0ef597..d4b9ccd27 100644 --- a/syntax/src/main/java/org/aya/generic/Constants.java +++ b/syntax/src/main/java/org/aya/generic/Constants.java @@ -15,7 +15,6 @@ public interface Constants { @NotNull @NonNls String ANONYMOUS_PREFIX = "_"; - @NotNull @NonNls String GENERATED_POSTFIX = "'"; @NotNull @NonNls String SCOPE_SEPARATOR = "::"; @NotNull Pattern SCOPE_SEPARATOR_PATTERN = Pattern.compile(SCOPE_SEPARATOR); @NotNull @NonNls String AYA_POSTFIX = ".aya"; diff --git a/syntax/src/main/java/org/aya/syntax/compile/JitClass.java b/syntax/src/main/java/org/aya/syntax/compile/JitClass.java index 3e672d306..4a6e37821 100644 --- a/syntax/src/main/java/org/aya/syntax/compile/JitClass.java +++ b/syntax/src/main/java/org/aya/syntax/compile/JitClass.java @@ -24,12 +24,11 @@ protected JitClass() { return ImmutableArray.Unsafe.wrap(membars()); } - @Override - public final @NotNull Term telescope(int i, @NotNull Seq teleArgs) { + @Override public final @NotNull Term telescope(int i, @NotNull Seq teleArgs) { return ClassDefLike.super.telescope(i, teleArgs); } @Override public final @NotNull Term result(Seq teleArgs) { - return result(teleArgs.size()); + return ClassDefLike.super.result(teleArgs.size()); } } diff --git a/syntax/src/main/java/org/aya/syntax/compile/JitDef.java b/syntax/src/main/java/org/aya/syntax/compile/JitDef.java index cac5b3888..9e2a86e72 100644 --- a/syntax/src/main/java/org/aya/syntax/compile/JitDef.java +++ b/syntax/src/main/java/org/aya/syntax/compile/JitDef.java @@ -3,6 +3,7 @@ package org.aya.syntax.compile; import kala.collection.immutable.ImmutableArray; +import kala.collection.immutable.ImmutableSeq; import org.aya.syntax.core.def.AnyDef; import org.aya.syntax.ref.ModulePath; import org.aya.syntax.ref.QName; diff --git a/syntax/src/main/java/org/aya/syntax/core/def/TopLevelDef.java b/syntax/src/main/java/org/aya/syntax/core/def/TopLevelDef.java index c180a6d1d..7d236dd4c 100644 --- a/syntax/src/main/java/org/aya/syntax/core/def/TopLevelDef.java +++ b/syntax/src/main/java/org/aya/syntax/core/def/TopLevelDef.java @@ -13,13 +13,13 @@ public sealed interface TopLevelDef extends TyckDef permits ClassDef, DataDef, FnDef, PrimDef { @Override default @NotNull ImmutableSeq telescope() { var signature = ref().signature; - assert signature != null; + assert signature != null : ref().name(); return signature.params(); } @Override default @NotNull Term result() { var signature = ref().signature; - assert signature != null; + assert signature != null : ref().name(); return signature.result(); } } diff --git a/syntax/src/main/java/org/aya/syntax/core/term/MatchTerm.java b/syntax/src/main/java/org/aya/syntax/core/term/MatchTerm.java index 8ce893040..b3131531c 100644 --- a/syntax/src/main/java/org/aya/syntax/core/term/MatchTerm.java +++ b/syntax/src/main/java/org/aya/syntax/core/term/MatchTerm.java @@ -6,8 +6,6 @@ import kala.function.IndexedFunction; import org.jetbrains.annotations.NotNull; -import java.util.function.UnaryOperator; - public record MatchTerm( @NotNull ImmutableSeq discriminant, @NotNull Term returns, @NotNull ImmutableSeq clauses @@ -19,17 +17,14 @@ public record MatchTerm( return this.discriminant.sameElements(discriminant, true) && this.returns == returns && this.clauses.sameElements(clauses, true) - ? this - : new MatchTerm(discriminant, returns, clauses); + ? this : new MatchTerm(discriminant, returns, clauses); } @Override public @NotNull Term descent(@NotNull IndexedFunction f) { return update( discriminant.map(x -> f.apply(0, x)), f.apply(0, returns), clauses.map(clause -> - clause.descent( - t -> f.apply(clause.bindCount(), t), - UnaryOperator.identity()) + clause.descent(t -> f.apply(clause.bindCount(), t)) )); } } diff --git a/syntax/src/main/java/org/aya/syntax/core/term/Term.java b/syntax/src/main/java/org/aya/syntax/core/term/Term.java index a70f6e4f9..c0736e9d1 100644 --- a/syntax/src/main/java/org/aya/syntax/core/term/Term.java +++ b/syntax/src/main/java/org/aya/syntax/core/term/Term.java @@ -177,17 +177,13 @@ public sealed interface Term extends Serializable, AyaDocile return descent(t -> t.doElevate(level)); } - record Matching( - @NotNull ImmutableSeq patterns, - int bindCount, @NotNull Term body - ) { - public @NotNull Matching update(@NotNull ImmutableSeq patterns, @NotNull Term body) { - return body == body() && patterns.sameElements(patterns(), true) ? this - : new Matching(patterns, bindCount, body); + record Matching(@NotNull ImmutableSeq patterns, int bindCount, @NotNull Term body) { + public @NotNull Matching update(@NotNull Term body) { + return body == body() ? this : new Matching(patterns, bindCount, body); } - public @NotNull Matching descent(@NotNull UnaryOperator f, @NotNull UnaryOperator g) { - return update(patterns.map(g), f.apply(body)); + public @NotNull Matching descent(@NotNull UnaryOperator f) { + return update(f.apply(body)); } public void forEach(@NotNull Consumer f, @NotNull Consumer g) {