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

org.aya.compiler.SourceBuilder Maven / Gradle / Ivy

There is a newer version: 0.36.0
Show newest version
// Copyright (c) 2020-2024 Tesla (Yinsen) Zhang.
// Use of this source code is governed by the MIT license that can be found in the LICENSE.md file.
package org.aya.compiler;

import kala.collection.immutable.ImmutableSeq;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.function.Consumer;

import static org.aya.compiler.AyaSerializer.CLASS_PANIC;
import static org.aya.compiler.ExprializeUtils.getJavaReference;
import static org.aya.compiler.ExprializeUtils.makeString;

public interface SourceBuilder {
  final class Default implements SourceBuilder {
    private final @NotNull StringBuilder builder;
    private int indent;
    private final @NotNull NameGenerator nameGenerator;

    public Default() {
      this(new StringBuilder(), 0, new NameGenerator());
    }

    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--;
    }
  }

  @NotNull StringBuilder builder();
  int indent();
  @NotNull NameGenerator nameGen();

  default void fillIndent() {
    if (indent() == 0) return;
    builder().append("  ".repeat(indent()));
  }

  void runInside(@NotNull Runnable runnable);

  default @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) {
    appendLine(lhs + " = " + rhs + ";");
  }

  default void buildIf(@NotNull String condition, @NotNull Runnable onSucc) {
    buildIfElse(condition, onSucc, null);
  }

  default void buildIfElse(@NotNull String condition, @NotNull Runnable onSucc, @Nullable Runnable onFailed) {
    appendLine("if (" + condition + ") {");
    runInside(onSucc);
    if (onFailed == null) appendLine("}");
    else {
      appendLine("} else {");
      runInside(onFailed);
      appendLine("}");
    }
  }

  /**
   * Generate java code that check whether {@param term} is an instance of {@param type}
   *
   * @param onSucc the argument is a local variable that has type {@param type} and identical equal to {@param term};
   */
  default void buildIfInstanceElse(
    @NotNull String term,
    @NotNull String type,
    @NotNull Consumer onSucc,
    @Nullable Runnable onFailed
  ) {
    String name = nameGen().nextName();
    buildIfElse(term + " instanceof " + type + " " + name,
      () -> onSucc.accept(name),
      onFailed);
  }

  default 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) {
    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(
    @NotNull String className,
    @Nullable Class superClass,
    boolean isStatic,
    @NotNull Runnable continuation
  ) {
    var ext = superClass == null ? "" : "extends " + getJavaReference(superClass);

    appendLine("public " + (isStatic ? "static" : "") + " final class " + className + " " + ext + " {");
    runInside(continuation);
    appendLine("}");
  }

  static @NotNull ImmutableSeq fromSeq(@NotNull String term, int size) {
    return ImmutableSeq.fill(size, idx -> term + ".get(" + idx + ")");
  }

  default void appendLine(@NotNull String string) {
    fillIndent();
    builder().append(string);
    builder().append('\n');
  }
  default void appendLine() { builder().append('\n'); }
  default void buildConstantField(
    @NotNull String type,
    @NotNull String name,
    @NotNull String value
  ) {
    appendLine("public static final " + type + " " + name + " = " + value + ";");
  }

  default  void buildSwitch(
    @NotNull String term,
    @NotNull ImmutableSeq cases,
    @NotNull Consumer continuation,
    @NotNull Runnable defaultCase
  ) {
    if (cases.isEmpty()) {
      defaultCase.run();
      return;
    }
    appendLine("switch (" + term + ") {");
    runInside(() -> {
      for (var kase : cases) {
        appendLine("case " + kase + " -> {");
        runInside(() -> continuation.accept(kase));
        appendLine("}");
      }

      appendLine("default -> {");
      runInside(defaultCase);
      appendLine("}");
    });
    appendLine("}");
  }

  default void buildMethod(
    @NotNull String name,
    @NotNull ImmutableSeq params,
    @NotNull String returnType,
    boolean override,
    @NotNull Runnable continuation
  ) {
    var paramStr = params.joinToString(", ",
      (override ? "@Override " : "") + "public " + returnType + " " + name + "(", ") {",
      param -> param.type() + " " + param.name());
    appendLine(paramStr);
    runInside(continuation);
    appendLine("}");
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy