diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 000000000..a9434528c --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Fix formatters so they apply to specific languages (apply) +5f4ba7cd2c1dbfadabb122b93b3011f0db56ae95 diff --git a/.githooks/pre-commit.sh b/.githooks/pre-commit.sh index 1e4a4111c..4dffac2f8 100755 --- a/.githooks/pre-commit.sh +++ b/.githooks/pre-commit.sh @@ -1,2 +1,18 @@ #!/bin/sh -mvn spotless:apply \ No newline at end of file + +everything_staged=$(git diff --name-only) + +if [ -z "$everything_staged" ]; then + # Don't commit because, after we reformat, the formatting will be unstaged, but it's too hard to determine (and maybe + # ambiguous) what are the formatting differences and what was originally unstaged. + mvn spotless:check || { + echo "*** ERROR ***" + echo "Detected a partial commit that doesn't have proper formatting." + echo "Run 'mvn spotless:apply', make sure every change you want to commit is added, and commit again." + exit 1 + } +else + # It's not ambiguous, just format and re-add everything tracked + mvn spotless:apply + git add -u +fi diff --git a/.githooks/pre-push.sh b/.githooks/pre-push.sh index 4808a05b0..09b2a4d0b 100755 --- a/.githooks/pre-push.sh +++ b/.githooks/pre-push.sh @@ -1,2 +1,2 @@ #!/bin/sh -mvn verify \ No newline at end of file +mvn verify diff --git a/.idea/settings.zip b/.idea/settings.zip index 77cfb941f..6edb8b2bb 100644 Binary files a/.idea/settings.zip and b/.idea/settings.zip differ diff --git a/.mvn/jvm.config b/.mvn/jvm.config new file mode 100644 index 000000000..a15bfa0e3 --- /dev/null +++ b/.mvn/jvm.config @@ -0,0 +1,6 @@ +--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED diff --git a/doc/design-decisions.md b/doc/design-decisions.md index 511362fe1..fea5316df 100644 --- a/doc/design-decisions.md +++ b/doc/design-decisions.md @@ -168,4 +168,3 @@ useful (`val`, `@Synchronized`, `@SneakyThrows`). - Also concerns with Lombok adding complexity, Lombok becoming unmaintained, Lombok not being supported by IDEs ( although at least IntelliJ supports it), Lombok being confusing. - diff --git a/pom.xml b/pom.xml index 06db261fd..22fa19cf1 100644 --- a/pom.xml +++ b/pom.xml @@ -1,252 +1,264 @@ - - 4.0.0 + + + 4.0.0 - org.prlprg - r-compile-server - 0.1-SNAPSHOT - jar + org.prlprg + r-compile-server + 0.1-SNAPSHOT + jar - r-jit-server - https://maven.apache.org + r-jit-server + https://maven.apache.org - - UTF-8 - 21 - ${maven.compiler.source} - 7.0.0-rc4 - - - - - - - org.junit - junit-bom - 5.10.1 - pom - import - - - + + UTF-8 + 21 + ${maven.compiler.source} + 7.0.0-rc4 + + - - - org.zeromq - jeromq - 0.5.4 - - - - com.google.guava - guava - 33.0.0-jre - - - - - com.github.spotbugs - spotbugs-annotations - 4.8.3 - true - - - - org.junit.jupiter - junit-jupiter - test - + + + org.junit + junit-bom + 5.10.1 + pom + import + - - - - - com.rudikershaw.gitbuildhook - git-build-hook-maven-plugin - 3.4.1 - - - .githooks/pre-commit.sh - .githooks/pre-push.sh - - - - - - install - - initialize - - - - - - com.diffplug.spotless - spotless-maven-plugin - 2.41.1 - - - - - check - - - - - - - - - src/**/* - tools/**/* - .gitignore - .gitattributes - Makefile - pom.xml - README.md - - - **/*.rds - - - - - - - - **/Makefile - - - target/**/* - - - true - 4 - - - - - - **/*.java - - - target/**/* - - - - - - - - - - **/*.md - - - target/**/* - - - true - 2 - - - - - - pom.xml - - - true - 2 - - - - - - **/*.yaml - **/*.yml - - - target/**/* - - - true - 2 - - - - - - - - com.github.spotbugs - spotbugs-maven-plugin - 4.8.2.0 - - - - - check - - - - - - - org.apache.maven.plugins - maven-pmd-plugin - 3.21.2 - - - - - check - cpd-check - - - - - - - net.sourceforge.pmd - pmd-compat6 - ${pmdVersion} - - - net.sourceforge.pmd - pmd-core - ${pmdVersion} - - - net.sourceforge.pmd - pmd-java - ${pmdVersion} - - - net.sourceforge.pmd - pmd-javascript - ${pmdVersion} - - - net.sourceforge.pmd - pmd-jsp - ${pmdVersion} - - - - - - - false - - - - - maven-surefire-plugin - 3.2.2 - - - - + + + + + + org.zeromq + jeromq + 0.5.4 + + + + com.google.guava + guava + 33.0.0-jre + + + + + com.github.spotbugs + spotbugs-annotations + 4.8.3 + true + + + + org.junit.jupiter + junit-jupiter + test + + + + + + + com.rudikershaw.gitbuildhook + git-build-hook-maven-plugin + 3.4.1 + + + .githooks/pre-commit.sh + .githooks/pre-push.sh + + + + + + install + + initialize + + + + + + com.diffplug.spotless + spotless-maven-plugin + 2.43.0 + + + + + + .gitignore + .gitattributes + .git-blame-ignore-revs + .githooks/** + + + + + + + + **/Makefile + + + target/**/* + + + true + 4 + + + + + + + + **/*.java + + + target/**/* + + + 2.18 + + + 1.19.2 + + true + + + + + + + + + + **/*.md + + + target/**/* + + + true + 2 + + + + + + + + pom.xml + + + true + 2 + + + false + + + + + + + **/*.yaml + **/*.yml + + + target/**/* + + + true + 2 + + + + + + + + + + + check + + + + + + + com.github.spotbugs + spotbugs-maven-plugin + 4.8.2.0 + + + + + check + + + + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.21.2 + + + + + false + + + + + net.sourceforge.pmd + pmd-compat6 + ${pmdVersion} + + + net.sourceforge.pmd + pmd-core + ${pmdVersion} + + + net.sourceforge.pmd + pmd-java + ${pmdVersion} + + + net.sourceforge.pmd + pmd-javascript + ${pmdVersion} + + + net.sourceforge.pmd + pmd-jsp + ${pmdVersion} + + + + + + + check + cpd-check + + + + + + + maven-surefire-plugin + 3.2.2 + + + + diff --git a/src/main/java/Main.java b/src/main/java/Main.java index 0d8ad53f1..ada450fe3 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -1,32 +1,31 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; - import org.prlprg.rds.RDSReader; import org.prlprg.sexp.SEXP; import org.prlprg.util.IO; public class Main { - public static void main(String[] args) throws Exception { - new Main().doMain(args); - } + public static void main(String[] args) throws Exception { + new Main().doMain(args); + } - private void doMain(String[] args) throws Exception { - compile(args[0]); - } + private void doMain(String[] args) throws Exception { + compile(args[0]); + } - private void compile(String file) throws IOException { - try (var input = new FileInputStream(file)) { - compile(input); - } + private void compile(String file) throws IOException { + try (var input = new FileInputStream(file)) { + compile(input); } + } - private void compile(InputStream input) throws IOException { - compile(RDSReader.readStream(IO.maybeDecompress(input))); - } + private void compile(InputStream input) throws IOException { + compile(RDSReader.readStream(IO.maybeDecompress(input))); + } - private void compile(SEXP sexp) { - System.out.println(sexp); - } + private void compile(SEXP sexp) { + System.out.println(sexp); + } } diff --git a/src/main/java/org/prlprg/App.java b/src/main/java/org/prlprg/App.java index b8abf69dd..1ecd28dda 100644 --- a/src/main/java/org/prlprg/App.java +++ b/src/main/java/org/prlprg/App.java @@ -1,10 +1,8 @@ package org.prlprg; -/** - * Hello world! - */ +/** Hello world! */ public class App { - public static void main(String[] args) { - System.out.println("Hello World!"); - } + public static void main(String[] args) { + System.out.println("Hello World!"); + } } diff --git a/src/main/java/org/prlprg/RPlatform.java b/src/main/java/org/prlprg/RPlatform.java index fbbb163d8..0557a65cd 100644 --- a/src/main/java/org/prlprg/RPlatform.java +++ b/src/main/java/org/prlprg/RPlatform.java @@ -1,8 +1,7 @@ package org.prlprg; public final class RPlatform { - public static final int INT_MAX = ~(1 << 31); + public static final int INT_MAX = ~(1 << 31); - private RPlatform() { - } + private RPlatform() {} } diff --git a/src/main/java/org/prlprg/bc/Bc.java b/src/main/java/org/prlprg/bc/Bc.java index 46e993c5b..517cfa686 100644 --- a/src/main/java/org/prlprg/bc/Bc.java +++ b/src/main/java/org/prlprg/bc/Bc.java @@ -2,76 +2,73 @@ import com.google.common.collect.ImmutableList; import com.google.common.primitives.ImmutableIntArray; -import org.prlprg.sexp.SEXP; - import java.util.*; +import org.prlprg.sexp.SEXP; -/** A complete R bytecode, consisting of a version, array of instructions and associated data, and constants. */ +/** + * A complete R bytecode, consisting of a version, array of instructions and associated data, and + * constants. + */ public record Bc(BcCode code, ConstPool consts) { - /** - * The only version of R bytecodes we support, which is also the latest version. - * The bytecode's version is denoted by the first integer in its code. - */ - public static final int R_BC_VERSION = 12; - - /** Create from the raw GNU-R representation, bytecodes not including the initial version number. */ - public static Bc fromRaw(ImmutableIntArray bytecodes, List consts) throws BcFromRawException { - var poolAndMakeIdx = ConstPool.fromRaw(consts); - var pool = poolAndMakeIdx.a(); - var makePoolIdx = poolAndMakeIdx.b(); - try { - return new Bc(BcCode.fromRaw(bytecodes, makePoolIdx), pool); - } catch (BcFromRawException e) { - throw new BcFromRawException("malformed bytecode\nConstants: " + pool, e); - } - } + /** + * The only version of R bytecodes we support, which is also the latest version. The bytecode's + * version is denoted by the first integer in its code. + */ + public static final int R_BC_VERSION = 12; - @Override - public String toString() { - return code() + "\n" + consts; + /** + * Create from the raw GNU-R representation, bytecodes not including the initial version number. + */ + public static Bc fromRaw(ImmutableIntArray bytecodes, List consts) + throws BcFromRawException { + var poolAndMakeIdx = ConstPool.fromRaw(consts); + var pool = poolAndMakeIdx.a(); + var makePoolIdx = poolAndMakeIdx.b(); + try { + return new Bc(BcCode.fromRaw(bytecodes, makePoolIdx), pool); + } catch (BcFromRawException e) { + throw new BcFromRawException("malformed bytecode\nConstants: " + pool, e); } + } - /** Equivalent to `CodeBuffer` in other projects */ - public static class Builder { - private final BcCode.Builder code = new BcCode.Builder(); - private final ConstPool.Builder consts = new ConstPool.Builder(); + @Override + public String toString() { + return code() + "\n" + consts; + } - /** - * Append a constant and return its index. - */ - public ConstPool.TypedIdx addConst(S c) { - return consts.add(c); - } + /** Equivalent to `CodeBuffer` in other projects */ + public static class Builder { + private final BcCode.Builder code = new BcCode.Builder(); + private final ConstPool.Builder consts = new ConstPool.Builder(); - /** - * Append an instruction. - */ - public void addInstr(BcInstr instr) { - code.add(instr); - } + /** Append a constant and return its index. */ + public ConstPool.TypedIdx addConst(S c) { + return consts.add(c); + } - /** - * Append a collection of constants and return its index. - */ - public ImmutableList> addAllConsts(Collection c) { - return consts.addAll(c); - } + /** Append an instruction. */ + public void addInstr(BcInstr instr) { + code.add(instr); + } - /** - * Append instructions. - */ - public void addAllInstrs(Collection c) { - code.addAll(c); - } + /** Append a collection of constants and return its index. */ + public ImmutableList> addAllConsts( + Collection c) { + return consts.addAll(c); + } - /** - * Finish building the bytecode. - * - * @return The bytecode. - */ - public Bc build() { - return new Bc(code.build(), consts.build()); - } + /** Append instructions. */ + public void addAllInstrs(Collection c) { + code.addAll(c); } + /** + * Finish building the bytecode. + * + * @return The bytecode. + */ + public Bc build() { + return new Bc(code.build(), consts.build()); + } + } } diff --git a/src/main/java/org/prlprg/bc/BcCode.java b/src/main/java/org/prlprg/bc/BcCode.java index 70da4b1be..eae3f307d 100644 --- a/src/main/java/org/prlprg/bc/BcCode.java +++ b/src/main/java/org/prlprg/bc/BcCode.java @@ -4,98 +4,90 @@ import com.google.common.collect.ImmutableList; import com.google.common.primitives.ImmutableIntArray; import com.google.errorprone.annotations.CanIgnoreReturnValue; - -import javax.annotation.concurrent.Immutable; import java.util.Collection; import java.util.List; +import javax.annotation.concurrent.Immutable; /** - * An array of bytecode instructions, which make up the code of a closure or promise. - * A complete bytecode is {@link Bc}, which also includes a constant pool. + * An array of bytecode instructions, which make up the code of a closure or promise. A complete + * bytecode is {@link Bc}, which also includes a constant pool. */ @Immutable public final class BcCode extends ForwardingList { - final ImmutableList instrs; + final ImmutableList instrs; - private BcCode(ImmutableList instrs) { - this.instrs = instrs; - } + private BcCode(ImmutableList instrs) { + this.instrs = instrs; + } - @Override - protected List delegate() { - return instrs; - } + @Override + protected List delegate() { + return instrs; + } - /** Create from the raw GNU-R representation, not including the initial version number. - * - * @param makePoolIdx A function to create pool indices from raw integers - */ - static BcCode fromRaw(ImmutableIntArray bytecodes, ConstPool.MakeIdx makePoolIdx) - throws BcFromRawException { - var builder = new Builder(); - int i = 0; - while (i < bytecodes.length()) { - try { - var instrAndI = BcInstrs.fromRaw(bytecodes, i, makePoolIdx); - builder.add(instrAndI.a()); - i = instrAndI.b(); - } catch (BcFromRawException e) { - throw new BcFromRawException( - "malformed bytecode at " + i + "\nBytecode up to this point: " + builder.build(), - e); - } - } - return builder.build(); + /** + * Create from the raw GNU-R representation, not including the initial version number. + * + * @param makePoolIdx A function to create pool indices from raw integers + */ + static BcCode fromRaw(ImmutableIntArray bytecodes, ConstPool.MakeIdx makePoolIdx) + throws BcFromRawException { + var builder = new Builder(); + int i = 0; + while (i < bytecodes.length()) { + try { + var instrAndI = BcInstrs.fromRaw(bytecodes, i, makePoolIdx); + builder.add(instrAndI.a()); + i = instrAndI.b(); + } catch (BcFromRawException e) { + throw new BcFromRawException( + "malformed bytecode at " + i + "\nBytecode up to this point: " + builder.build(), e); + } } + return builder.build(); + } - @Override - public String toString() { - StringBuilder sb = new StringBuilder("=== CODE ==="); - for (BcInstr instr : instrs) { - sb.append("\n").append(instr); - } - return sb.toString(); + @Override + public String toString() { + StringBuilder sb = new StringBuilder("=== CODE ==="); + for (BcInstr instr : instrs) { + sb.append("\n").append(instr); } + return sb.toString(); + } - /** - * A builder class for creating BcArray instances. - *

- * Not synchronized, so don't use from multiple threads. - */ - public static class Builder { - ImmutableList.Builder builder = ImmutableList.builder(); + /** + * A builder class for creating BcArray instances. + * + *

Not synchronized, so don't use from multiple threads. + */ + public static class Builder { + ImmutableList.Builder builder = ImmutableList.builder(); - /** - * Create a new builder. - */ - public Builder() { - } + /** Create a new builder. */ + public Builder() {} - /** - * Append an instruction. - */ - @CanIgnoreReturnValue - public Builder add(BcInstr instr) { - builder.add(instr); - return this; - } + /** Append an instruction. */ + @CanIgnoreReturnValue + public Builder add(BcInstr instr) { + builder.add(instr); + return this; + } - /** - * Append instructions. - */ - @CanIgnoreReturnValue - public Builder addAll(Collection c) { - builder.addAll(c); - return this; - } + /** Append instructions. */ + @CanIgnoreReturnValue + public Builder addAll(Collection c) { + builder.addAll(c); + return this; + } - /** - * Finish building the array. - * - * @return The array. - */ - public BcCode build() { - return new BcCode(builder.build()); - } + /** + * Finish building the array. + * + * @return The array. + */ + public BcCode build() { + return new BcCode(builder.build()); } + } } diff --git a/src/main/java/org/prlprg/bc/BcFromRawException.java b/src/main/java/org/prlprg/bc/BcFromRawException.java index 61d741098..040d8764a 100644 --- a/src/main/java/org/prlprg/bc/BcFromRawException.java +++ b/src/main/java/org/prlprg/bc/BcFromRawException.java @@ -1,11 +1,11 @@ package org.prlprg.bc; public class BcFromRawException extends RuntimeException { - public BcFromRawException(String message) { - super(message); - } + public BcFromRawException(String message) { + super(message); + } - public BcFromRawException(String message, Throwable cause) { - super(message, cause); - } + public BcFromRawException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/src/main/java/org/prlprg/bc/BcInstr.java b/src/main/java/org/prlprg/bc/BcInstr.java index eee480d6b..8e3e0e7c2 100644 --- a/src/main/java/org/prlprg/bc/BcInstr.java +++ b/src/main/java/org/prlprg/bc/BcInstr.java @@ -1,1095 +1,1166 @@ package org.prlprg.bc; import com.google.common.primitives.ImmutableIntArray; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; import org.prlprg.sexp.*; import org.prlprg.util.Either; import org.prlprg.util.Pair; -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; - /** - * A single bytecode instruction, consists of an operation and arguments. - * The operation is determined by its subtype, arguments are determined by its fields. + * A single bytecode instruction, consists of an operation and arguments. The operation is + * determined by its subtype, arguments are determined by its fields. */ @Immutable public sealed interface BcInstr { - /** The instruction's operation. */ - BcOp op(); + /** The instruction's operation. */ + BcOp op(); - record Return() implements BcInstr { - @Override - public BcOp op() { - return BcOp.RETURN; - } + record Return() implements BcInstr { + @Override + public BcOp op() { + return BcOp.RETURN; } + } - record Goto(BcLabel label) implements BcInstr { - @Override - public BcOp op() { - return BcOp.GOTO; - } + record Goto(BcLabel label) implements BcInstr { + @Override + public BcOp op() { + return BcOp.GOTO; } + } - record BrIfNot(ConstPool.TypedIdx ast, BcLabel label) implements BcInstr { - @Override - public BcOp op() { - return BcOp.BRIFNOT; - } + record BrIfNot(ConstPool.TypedIdx ast, BcLabel label) implements BcInstr { + @Override + public BcOp op() { + return BcOp.BRIFNOT; } + } - record Pop() implements BcInstr { - @Override - public BcOp op() { - return BcOp.POP; - } + record Pop() implements BcInstr { + @Override + public BcOp op() { + return BcOp.POP; } + } - record Dup() implements BcInstr { - @Override - public BcOp op() { - return BcOp.DUP; - } + record Dup() implements BcInstr { + @Override + public BcOp op() { + return BcOp.DUP; } + } - record PrintValue() implements BcInstr { - @Override - public BcOp op() { - return BcOp.PRINTVALUE; - } + record PrintValue() implements BcInstr { + @Override + public BcOp op() { + return BcOp.PRINTVALUE; } + } - record StartLoopCntxt(boolean isForLoop, BcLabel break_) implements BcInstr { - @Override - public BcOp op() { - return BcOp.STARTLOOPCNTXT; - } + record StartLoopCntxt(boolean isForLoop, BcLabel break_) implements BcInstr { + @Override + public BcOp op() { + return BcOp.STARTLOOPCNTXT; } + } - record EndLoopCntxt(boolean isForLoop) implements BcInstr { - @Override - public BcOp op() { - return BcOp.ENDLOOPCNTXT; - } + record EndLoopCntxt(boolean isForLoop) implements BcInstr { + @Override + public BcOp op() { + return BcOp.ENDLOOPCNTXT; } + } - record DoLoopNext() implements BcInstr { - @Override - public BcOp op() { - return BcOp.DOLOOPNEXT; - } + record DoLoopNext() implements BcInstr { + @Override + public BcOp op() { + return BcOp.DOLOOPNEXT; } + } - record DoLoopBreak() implements BcInstr { - @Override - public BcOp op() { - return BcOp.DOLOOPBREAK; - } + record DoLoopBreak() implements BcInstr { + @Override + public BcOp op() { + return BcOp.DOLOOPBREAK; } + } - record StartFor(ConstPool.TypedIdx ast, ConstPool.TypedIdx elemName, BcLabel end) implements BcInstr { - @Override - public BcOp op() { - return BcOp.STARTFOR; - } + record StartFor( + ConstPool.TypedIdx ast, ConstPool.TypedIdx elemName, BcLabel end) + implements BcInstr { + @Override + public BcOp op() { + return BcOp.STARTFOR; } + } - record StepFor(BcLabel end) implements BcInstr { - @Override - public BcOp op() { - return BcOp.STEPFOR; - } + record StepFor(BcLabel end) implements BcInstr { + @Override + public BcOp op() { + return BcOp.STEPFOR; } + } - record EndFor() implements BcInstr { - @Override - public BcOp op() { - return BcOp.ENDFOR; - } + record EndFor() implements BcInstr { + @Override + public BcOp op() { + return BcOp.ENDFOR; } + } - record SetLoopVal() implements BcInstr { - @Override - public BcOp op() { - return BcOp.SETLOOPVAL; - } + record SetLoopVal() implements BcInstr { + @Override + public BcOp op() { + return BcOp.SETLOOPVAL; } + } - record Invisible() implements BcInstr { - @Override - public BcOp op() { - return BcOp.INVISIBLE; - } + record Invisible() implements BcInstr { + @Override + public BcOp op() { + return BcOp.INVISIBLE; } + } - record LdConst(ConstPool.Idx constant) implements BcInstr { - @Override - public BcOp op() { - return BcOp.LDCONST; - } + record LdConst(ConstPool.Idx constant) implements BcInstr { + @Override + public BcOp op() { + return BcOp.LDCONST; } + } - record LdNull() implements BcInstr { - @Override - public BcOp op() { - return BcOp.LDNULL; - } + record LdNull() implements BcInstr { + @Override + public BcOp op() { + return BcOp.LDNULL; } + } - record LdTrue() implements BcInstr { - @Override - public BcOp op() { - return BcOp.LDTRUE; - } + record LdTrue() implements BcInstr { + @Override + public BcOp op() { + return BcOp.LDTRUE; } + } - record LdFalse() implements BcInstr { - @Override - public BcOp op() { - return BcOp.LDFALSE; - } + record LdFalse() implements BcInstr { + @Override + public BcOp op() { + return BcOp.LDFALSE; } + } - record GetVar(ConstPool.TypedIdx name) implements BcInstr { - @Override - public BcOp op() { - return BcOp.GETVAR; - } + record GetVar(ConstPool.TypedIdx name) implements BcInstr { + @Override + public BcOp op() { + return BcOp.GETVAR; } + } - record DdVal(ConstPool.TypedIdx name) implements BcInstr { - @Override - public BcOp op() { - return BcOp.DDVAL; - } + record DdVal(ConstPool.TypedIdx name) implements BcInstr { + @Override + public BcOp op() { + return BcOp.DDVAL; } + } - record SetVar(ConstPool.TypedIdx name) implements BcInstr { - @Override - public BcOp op() { - return BcOp.SETVAR; - } + record SetVar(ConstPool.TypedIdx name) implements BcInstr { + @Override + public BcOp op() { + return BcOp.SETVAR; } + } - record GetFun(ConstPool.TypedIdx name) implements BcInstr { - @Override - public BcOp op() { - return BcOp.GETFUN; - } + record GetFun(ConstPool.TypedIdx name) implements BcInstr { + @Override + public BcOp op() { + return BcOp.GETFUN; } + } - record GetGlobFun(ConstPool.TypedIdx name) implements BcInstr { - @Override - public BcOp op() { - return BcOp.GETGLOBFUN; - } + record GetGlobFun(ConstPool.TypedIdx name) implements BcInstr { + @Override + public BcOp op() { + return BcOp.GETGLOBFUN; } + } - record GetSymFun(ConstPool.TypedIdx name) implements BcInstr { - @Override - public BcOp op() { - return BcOp.GETSYMFUN; - } + record GetSymFun(ConstPool.TypedIdx name) implements BcInstr { + @Override + public BcOp op() { + return BcOp.GETSYMFUN; } + } - record GetBuiltin(ConstPool.TypedIdx name) implements BcInstr { - @Override - public BcOp op() { - return BcOp.GETBUILTIN; - } + record GetBuiltin(ConstPool.TypedIdx name) implements BcInstr { + @Override + public BcOp op() { + return BcOp.GETBUILTIN; } + } - record GetIntlBuiltin(ConstPool.TypedIdx name) implements BcInstr { - @Override - public BcOp op() { - return BcOp.GETINTLBUILTIN; - } + record GetIntlBuiltin(ConstPool.TypedIdx name) implements BcInstr { + @Override + public BcOp op() { + return BcOp.GETINTLBUILTIN; } + } - record CheckFun() implements BcInstr { - @Override - public BcOp op() { - return BcOp.CHECKFUN; - } + record CheckFun() implements BcInstr { + @Override + public BcOp op() { + return BcOp.CHECKFUN; } + } - /** {@code code} is usually but not always bytecode (see eval.c). */ - record MakeProm(ConstPool.Idx code) implements BcInstr { - @Override - public BcOp op() { - return BcOp.MAKEPROM; - } + /** {@code code} is usually but not always bytecode (see eval.c). */ + record MakeProm(ConstPool.Idx code) implements BcInstr { + @Override + public BcOp op() { + return BcOp.MAKEPROM; } + } - record DoMissing() implements BcInstr { - @Override - public BcOp op() { - return BcOp.DOMISSING; - } + record DoMissing() implements BcInstr { + @Override + public BcOp op() { + return BcOp.DOMISSING; } + } - record SetTag(@Nullable ConstPool.TypedIdx tag) implements BcInstr { - @Override - public BcOp op() { - return BcOp.SETTAG; - } + record SetTag(@Nullable ConstPool.TypedIdx tag) implements BcInstr { + @Override + public BcOp op() { + return BcOp.SETTAG; } + } - record DoDots() implements BcInstr { - @Override - public BcOp op() { - return BcOp.DODOTS; - } + record DoDots() implements BcInstr { + @Override + public BcOp op() { + return BcOp.DODOTS; } + } - record PushArg() implements BcInstr { - @Override - public BcOp op() { - return BcOp.PUSHARG; - } + record PushArg() implements BcInstr { + @Override + public BcOp op() { + return BcOp.PUSHARG; } + } - record PushConstArg(ConstPool.Idx constant) implements BcInstr { - @Override - public BcOp op() { - return BcOp.PUSHCONSTARG; - } + record PushConstArg(ConstPool.Idx constant) implements BcInstr { + @Override + public BcOp op() { + return BcOp.PUSHCONSTARG; } + } - record PushNullArg() implements BcInstr { - @Override - public BcOp op() { - return BcOp.PUSHNULLARG; - } + record PushNullArg() implements BcInstr { + @Override + public BcOp op() { + return BcOp.PUSHNULLARG; } + } - record PushTrueArg() implements BcInstr { - @Override - public BcOp op() { - return BcOp.PUSHTRUEARG; - } + record PushTrueArg() implements BcInstr { + @Override + public BcOp op() { + return BcOp.PUSHTRUEARG; } + } - record PushFalseArg() implements BcInstr { - @Override - public BcOp op() { - return BcOp.PUSHFALSEARG; - } + record PushFalseArg() implements BcInstr { + @Override + public BcOp op() { + return BcOp.PUSHFALSEARG; } + } - record Call(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.CALL; - } + record Call(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.CALL; } + } - record CallBuiltin(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.CALLBUILTIN; - } + record CallBuiltin(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.CALLBUILTIN; } + } - record CallSpecial(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.CALLSPECIAL; - } + record CallSpecial(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.CALLSPECIAL; } + } - record MakeClosure(ConstPool.TypedIdx formalsBodyAndMaybeSrcRef) implements BcInstr { - ListSXP formals(ConstPool pool) { - return (ListSXP)pool.get(this.formalsBodyAndMaybeSrcRef).get(0); - } + record MakeClosure(ConstPool.TypedIdx formalsBodyAndMaybeSrcRef) implements BcInstr { + ListSXP formals(ConstPool pool) { + return (ListSXP) pool.get(this.formalsBodyAndMaybeSrcRef).get(0); + } - SEXP body(ConstPool pool) { - return pool.get(formalsBodyAndMaybeSrcRef).get(1); - } + SEXP body(ConstPool pool) { + return pool.get(formalsBodyAndMaybeSrcRef).get(1); + } - SEXP srcRef(ConstPool pool) { - var formalsBodyAndMaybeSrcRef = pool.get(this.formalsBodyAndMaybeSrcRef); - return formalsBodyAndMaybeSrcRef.size() < 3 ? SEXPs.NULL : formalsBodyAndMaybeSrcRef.get(2); - } + SEXP srcRef(ConstPool pool) { + var formalsBodyAndMaybeSrcRef = pool.get(this.formalsBodyAndMaybeSrcRef); + return formalsBodyAndMaybeSrcRef.size() < 3 ? SEXPs.NULL : formalsBodyAndMaybeSrcRef.get(2); + } - @Override - public BcOp op() { - return BcOp.MAKECLOSURE; - } + @Override + public BcOp op() { + return BcOp.MAKECLOSURE; } + } - record UMinus(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.UMINUS; - } + record UMinus(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.UMINUS; } + } - record UPlus(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.UPLUS; - } + record UPlus(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.UPLUS; } + } - record Add(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.ADD; - } + record Add(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.ADD; } + } - record Sub(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.SUB; - } + record Sub(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.SUB; } + } - record Mul(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.MUL; - } + record Mul(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.MUL; } + } - record Div(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.DIV; - } + record Div(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.DIV; } + } - record Expt(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.EXPT; - } + record Expt(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.EXPT; } + } - record Sqrt(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.SQRT; - } + record Sqrt(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.SQRT; } + } - record Exp(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.EXP; - } + record Exp(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.EXP; } + } - record Eq(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.EQ; - } + record Eq(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.EQ; } + } - record Ne(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.NE; - } + record Ne(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.NE; } + } - record Lt(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.LT; - } + record Lt(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.LT; } + } - record Le(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.LE; - } + record Le(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.LE; } + } - record Ge(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.GE; - } + record Ge(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.GE; } + } - record Gt(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.GT; - } + record Gt(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.GT; } + } - record And(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.AND; - } + record And(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.AND; } + } - record Or(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.OR; - } + record Or(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.OR; } + } - record Not(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.NOT; - } + record Not(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.NOT; } + } - record DotsErr() implements BcInstr { - @Override - public BcOp op() { - return BcOp.DOTSERR; - } + record DotsErr() implements BcInstr { + @Override + public BcOp op() { + return BcOp.DOTSERR; } + } - record StartAssign(ConstPool.TypedIdx name) implements BcInstr { - @Override - public BcOp op() { - return BcOp.STARTASSIGN; - } + record StartAssign(ConstPool.TypedIdx name) implements BcInstr { + @Override + public BcOp op() { + return BcOp.STARTASSIGN; } + } - record EndAssign(ConstPool.TypedIdx name) implements BcInstr { - @Override - public BcOp op() { - return BcOp.ENDASSIGN; - } + record EndAssign(ConstPool.TypedIdx name) implements BcInstr { + @Override + public BcOp op() { + return BcOp.ENDASSIGN; } + } - record StartSubset(ConstPool.TypedIdx ast, BcLabel after) implements BcInstr { - @Override - public BcOp op() { - return BcOp.STARTSUBSET; - } + record StartSubset(ConstPool.TypedIdx ast, BcLabel after) implements BcInstr { + @Override + public BcOp op() { + return BcOp.STARTSUBSET; } + } - record DfltSubset() implements BcInstr { - @Override - public BcOp op() { - return BcOp.DFLTSUBSET; - } + record DfltSubset() implements BcInstr { + @Override + public BcOp op() { + return BcOp.DFLTSUBSET; } + } - record StartSubassign(ConstPool.TypedIdx ast, BcLabel after) implements BcInstr { - @Override - public BcOp op() { - return BcOp.STARTSUBASSIGN; - } + record StartSubassign(ConstPool.TypedIdx ast, BcLabel after) implements BcInstr { + @Override + public BcOp op() { + return BcOp.STARTSUBASSIGN; } + } - record DfltSubassign() implements BcInstr { - @Override - public BcOp op() { - return BcOp.DFLTSUBASSIGN; - } + record DfltSubassign() implements BcInstr { + @Override + public BcOp op() { + return BcOp.DFLTSUBASSIGN; } + } - record StartC(ConstPool.TypedIdx ast, BcLabel after) implements BcInstr { - @Override - public BcOp op() { - return BcOp.STARTC; - } + record StartC(ConstPool.TypedIdx ast, BcLabel after) implements BcInstr { + @Override + public BcOp op() { + return BcOp.STARTC; } + } - record DfltC() implements BcInstr { - @Override - public BcOp op() { - return BcOp.DFLTC; - } + record DfltC() implements BcInstr { + @Override + public BcOp op() { + return BcOp.DFLTC; } + } - record StartSubset2(ConstPool.TypedIdx ast, BcLabel after) implements BcInstr { - @Override - public BcOp op() { - return BcOp.STARTSUBSET2; - } + record StartSubset2(ConstPool.TypedIdx ast, BcLabel after) implements BcInstr { + @Override + public BcOp op() { + return BcOp.STARTSUBSET2; } + } - record DfltSubset2() implements BcInstr { - @Override - public BcOp op() { - return BcOp.DFLTSUBSET2; - } + record DfltSubset2() implements BcInstr { + @Override + public BcOp op() { + return BcOp.DFLTSUBSET2; } + } - record StartSubassign2(ConstPool.TypedIdx ast, BcLabel after) implements BcInstr { - @Override - public BcOp op() { - return BcOp.STARTSUBASSIGN2; - } + record StartSubassign2(ConstPool.TypedIdx ast, BcLabel after) implements BcInstr { + @Override + public BcOp op() { + return BcOp.STARTSUBASSIGN2; } + } - record DfltSubassign2() implements BcInstr { - @Override - public BcOp op() { - return BcOp.DFLTSUBASSIGN2; - } + record DfltSubassign2() implements BcInstr { + @Override + public BcOp op() { + return BcOp.DFLTSUBASSIGN2; } + } - record Dollar(ConstPool.TypedIdx ast, ConstPool.TypedIdx member) implements BcInstr { - @Override - public BcOp op() { - return BcOp.DOLLAR; - } + record Dollar(ConstPool.TypedIdx ast, ConstPool.TypedIdx member) + implements BcInstr { + @Override + public BcOp op() { + return BcOp.DOLLAR; } + } - record DollarGets(ConstPool.TypedIdx ast, ConstPool.TypedIdx member) implements BcInstr { - @Override - public BcOp op() { - return BcOp.DOLLARGETS; - } + record DollarGets(ConstPool.TypedIdx ast, ConstPool.TypedIdx member) + implements BcInstr { + @Override + public BcOp op() { + return BcOp.DOLLARGETS; } + } - record IsNull() implements BcInstr { - @Override - public BcOp op() { - return BcOp.ISNULL; - } + record IsNull() implements BcInstr { + @Override + public BcOp op() { + return BcOp.ISNULL; } + } - record IsLogical() implements BcInstr { - @Override - public BcOp op() { - return BcOp.ISLOGICAL; - } + record IsLogical() implements BcInstr { + @Override + public BcOp op() { + return BcOp.ISLOGICAL; } + } - record IsInteger() implements BcInstr { - @Override - public BcOp op() { - return BcOp.ISINTEGER; - } + record IsInteger() implements BcInstr { + @Override + public BcOp op() { + return BcOp.ISINTEGER; } + } - record IsDouble() implements BcInstr { - @Override - public BcOp op() { - return BcOp.ISDOUBLE; - } + record IsDouble() implements BcInstr { + @Override + public BcOp op() { + return BcOp.ISDOUBLE; } + } - record IsComplex() implements BcInstr { - @Override - public BcOp op() { - return BcOp.ISCOMPLEX; - } + record IsComplex() implements BcInstr { + @Override + public BcOp op() { + return BcOp.ISCOMPLEX; } + } - record IsCharacter() implements BcInstr { - @Override - public BcOp op() { - return BcOp.ISCHARACTER; - } + record IsCharacter() implements BcInstr { + @Override + public BcOp op() { + return BcOp.ISCHARACTER; } + } - record IsSymbol() implements BcInstr { - @Override - public BcOp op() { - return BcOp.ISSYMBOL; - } + record IsSymbol() implements BcInstr { + @Override + public BcOp op() { + return BcOp.ISSYMBOL; } + } - record IsObject() implements BcInstr { - @Override - public BcOp op() { - return BcOp.ISOBJECT; - } + record IsObject() implements BcInstr { + @Override + public BcOp op() { + return BcOp.ISOBJECT; } + } - record IsNumeric() implements BcInstr { - @Override - public BcOp op() { - return BcOp.ISNUMERIC; - } + record IsNumeric() implements BcInstr { + @Override + public BcOp op() { + return BcOp.ISNUMERIC; } + } - // ???: call-idx can be negative? We make TypedIdx null to support this case, but not sure if it's possible. - // This applies to every other `@Nullable call` in this file. - record VecSubset(@Nullable ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.VECSUBSET; - } + // ???: call-idx can be negative? We make TypedIdx null to support this case, but not sure if + // it's possible. + // This applies to every other `@Nullable call` in this file. + record VecSubset(@Nullable ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.VECSUBSET; } + } - record MatSubset(@Nullable ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.MATSUBSET; - } + record MatSubset(@Nullable ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.MATSUBSET; } + } - record VecSubassign(@Nullable ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.VECSUBASSIGN; - } + record VecSubassign(@Nullable ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.VECSUBASSIGN; } + } - record MatSubassign(@Nullable ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.MATSUBASSIGN; - } + record MatSubassign(@Nullable ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.MATSUBASSIGN; } + } - record And1st(ConstPool.TypedIdx ast, BcLabel shortCircuit) implements BcInstr { - @Override - public BcOp op() { - return BcOp.AND1ST; - } + record And1st(ConstPool.TypedIdx ast, BcLabel shortCircuit) implements BcInstr { + @Override + public BcOp op() { + return BcOp.AND1ST; } + } - record And2nd(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.AND2ND; - } + record And2nd(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.AND2ND; } + } - record Or1st(ConstPool.TypedIdx ast, BcLabel shortCircuit) implements BcInstr { - @Override - public BcOp op() { - return BcOp.OR1ST; - } + record Or1st(ConstPool.TypedIdx ast, BcLabel shortCircuit) implements BcInstr { + @Override + public BcOp op() { + return BcOp.OR1ST; } + } - record Or2nd(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.OR2ND; - } + record Or2nd(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.OR2ND; } + } - record GetVarMissOk(ConstPool.TypedIdx name) implements BcInstr { - @Override - public BcOp op() { - return BcOp.GETVAR_MISSOK; - } + record GetVarMissOk(ConstPool.TypedIdx name) implements BcInstr { + @Override + public BcOp op() { + return BcOp.GETVAR_MISSOK; } + } - record DdValMissOk(ConstPool.TypedIdx name) implements BcInstr { - @Override - public BcOp op() { - return BcOp.DDVAL_MISSOK; - } + record DdValMissOk(ConstPool.TypedIdx name) implements BcInstr { + @Override + public BcOp op() { + return BcOp.DDVAL_MISSOK; } + } - record Visible() implements BcInstr { - @Override - public BcOp op() { - return BcOp.VISIBLE; - } + record Visible() implements BcInstr { + @Override + public BcOp op() { + return BcOp.VISIBLE; } + } - record SetVar2(ConstPool.TypedIdx name) implements BcInstr { - @Override - public BcOp op() { - return BcOp.SETVAR2; - } + record SetVar2(ConstPool.TypedIdx name) implements BcInstr { + @Override + public BcOp op() { + return BcOp.SETVAR2; } + } - record StartAssign2(ConstPool.TypedIdx name) implements BcInstr { - @Override - public BcOp op() { - return BcOp.STARTASSIGN2; - } + record StartAssign2(ConstPool.TypedIdx name) implements BcInstr { + @Override + public BcOp op() { + return BcOp.STARTASSIGN2; } + } - record EndAssign2(ConstPool.TypedIdx name) implements BcInstr { - @Override - public BcOp op() { - return BcOp.ENDASSIGN2; - } + record EndAssign2(ConstPool.TypedIdx name) implements BcInstr { + @Override + public BcOp op() { + return BcOp.ENDASSIGN2; } + } - record SetterCall(ConstPool.TypedIdx ast, ConstPool.Idx valueExpr) implements BcInstr { - @Override - public BcOp op() { - return BcOp.SETTER_CALL; - } + record SetterCall(ConstPool.TypedIdx ast, ConstPool.Idx valueExpr) implements BcInstr { + @Override + public BcOp op() { + return BcOp.SETTER_CALL; } + } - record GetterCall(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.GETTER_CALL; - } + record GetterCall(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.GETTER_CALL; } + } - /** See eval.c for why this isn't just a regular swap instruction. */ - record SpecialSwap() implements BcInstr { - @Override - public BcOp op() { - return BcOp.SWAP; - } + /** See eval.c for why this isn't just a regular swap instruction. */ + record SpecialSwap() implements BcInstr { + @Override + public BcOp op() { + return BcOp.SWAP; } + } - record Dup2nd() implements BcInstr { - @Override - public BcOp op() { - return BcOp.DUP2ND; - } + record Dup2nd() implements BcInstr { + @Override + public BcOp op() { + return BcOp.DUP2ND; } + } - record Switch( - ConstPool.TypedIdx ast, - @Nullable Either, ConstPool.TypedIdx> names, - @Nullable ConstPool.TypedIdx cOffsets, - @Nullable ConstPool.TypedIdx iOffsets - ) implements BcInstr { - @Override - public BcOp op() { - return BcOp.SWITCH; - } + record Switch( + ConstPool.TypedIdx ast, + @Nullable Either, ConstPool.TypedIdx> names, + @Nullable ConstPool.TypedIdx cOffsets, + @Nullable ConstPool.TypedIdx iOffsets) + implements BcInstr { + @Override + public BcOp op() { + return BcOp.SWITCH; } + } - record ReturnJmp() implements BcInstr { - @Override - public BcOp op() { - return BcOp.RETURNJMP; - } + record ReturnJmp() implements BcInstr { + @Override + public BcOp op() { + return BcOp.RETURNJMP; } + } - record StartSubsetN(ConstPool.TypedIdx ast, BcLabel after) implements BcInstr { - @Override - public BcOp op() { - return BcOp.STARTSUBSET_N; - } + record StartSubsetN(ConstPool.TypedIdx ast, BcLabel after) implements BcInstr { + @Override + public BcOp op() { + return BcOp.STARTSUBSET_N; } + } - record StartSubassignN(ConstPool.TypedIdx ast, BcLabel after) implements BcInstr { - @Override - public BcOp op() { - return BcOp.STARTSUBASSIGN_N; - } + record StartSubassignN(ConstPool.TypedIdx ast, BcLabel after) implements BcInstr { + @Override + public BcOp op() { + return BcOp.STARTSUBASSIGN_N; } + } - record VecSubset2(@Nullable ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.VECSUBSET2; - } + record VecSubset2(@Nullable ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.VECSUBSET2; } + } - record MatSubset2(@Nullable ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.MATSUBSET2; - } + record MatSubset2(@Nullable ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.MATSUBSET2; } + } - record VecSubassign2(@Nullable ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.VECSUBASSIGN2; - } + record VecSubassign2(@Nullable ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.VECSUBASSIGN2; } + } - record MatSubassign2(@Nullable ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.MATSUBASSIGN2; - } + record MatSubassign2(@Nullable ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.MATSUBASSIGN2; } + } - record StartSubset2N(ConstPool.TypedIdx ast, BcLabel after) implements BcInstr { - @Override - public BcOp op() { - return BcOp.STARTSUBSET2_N; - } + record StartSubset2N(ConstPool.TypedIdx ast, BcLabel after) implements BcInstr { + @Override + public BcOp op() { + return BcOp.STARTSUBSET2_N; } + } - record StartSubassign2N(ConstPool.TypedIdx ast, BcLabel after) implements BcInstr { - @Override - public BcOp op() { - return BcOp.STARTSUBASSIGN2_N; - } + record StartSubassign2N(ConstPool.TypedIdx ast, BcLabel after) implements BcInstr { + @Override + public BcOp op() { + return BcOp.STARTSUBASSIGN2_N; } + } - record SubsetN(@Nullable ConstPool.TypedIdx ast, int n) implements BcInstr { - @Override - public BcOp op() { - return BcOp.SUBSET_N; - } + record SubsetN(@Nullable ConstPool.TypedIdx ast, int n) implements BcInstr { + @Override + public BcOp op() { + return BcOp.SUBSET_N; } + } - record Subset2N(@Nullable ConstPool.TypedIdx ast, int n) implements BcInstr { - @Override - public BcOp op() { - return BcOp.SUBSET2_N; - } + record Subset2N(@Nullable ConstPool.TypedIdx ast, int n) implements BcInstr { + @Override + public BcOp op() { + return BcOp.SUBSET2_N; } + } - record SubassignN(@Nullable ConstPool.TypedIdx ast, int n) implements BcInstr { - @Override - public BcOp op() { - return BcOp.SUBASSIGN_N; - } + record SubassignN(@Nullable ConstPool.TypedIdx ast, int n) implements BcInstr { + @Override + public BcOp op() { + return BcOp.SUBASSIGN_N; } + } - record Subassign2N(@Nullable ConstPool.TypedIdx ast, int n) implements BcInstr { - @Override - public BcOp op() { - return BcOp.SUBASSIGN2_N; - } + record Subassign2N(@Nullable ConstPool.TypedIdx ast, int n) implements BcInstr { + @Override + public BcOp op() { + return BcOp.SUBASSIGN2_N; } + } - record Log(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.LOG; - } + record Log(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.LOG; } + } - record LogBase(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.LOGBASE; - } + record LogBase(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.LOGBASE; } + } - record Math1(ConstPool.TypedIdx ast, int funId) implements BcInstr { - @Override - public BcOp op() { - return BcOp.MATH1; - } + record Math1(ConstPool.TypedIdx ast, int funId) implements BcInstr { + @Override + public BcOp op() { + return BcOp.MATH1; } + } - record DotCall(ConstPool.TypedIdx ast, int numArgs) implements BcInstr { - @Override - public BcOp op() { - return BcOp.DOTCALL; - } + record DotCall(ConstPool.TypedIdx ast, int numArgs) implements BcInstr { + @Override + public BcOp op() { + return BcOp.DOTCALL; } + } - record Colon(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.COLON; - } + record Colon(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.COLON; } + } - record SeqAlong(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.SEQALONG; - } + record SeqAlong(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.SEQALONG; } + } - record SeqLen(ConstPool.TypedIdx ast) implements BcInstr { - @Override - public BcOp op() { - return BcOp.SEQLEN; - } + record SeqLen(ConstPool.TypedIdx ast) implements BcInstr { + @Override + public BcOp op() { + return BcOp.SEQLEN; } + } - record BaseGuard(ConstPool.TypedIdx expr, BcLabel after) implements BcInstr { - @Override - public BcOp op() { - return BcOp.BASEGUARD; - } + record BaseGuard(ConstPool.TypedIdx expr, BcLabel after) implements BcInstr { + @Override + public BcOp op() { + return BcOp.BASEGUARD; } + } - record IncLnk() implements BcInstr { - @Override - public BcOp op() { - return BcOp.INCLNK; - } + record IncLnk() implements BcInstr { + @Override + public BcOp op() { + return BcOp.INCLNK; } + } - record DecLnk() implements BcInstr { - @Override - public BcOp op() { - return BcOp.DECLNK; - } + record DecLnk() implements BcInstr { + @Override + public BcOp op() { + return BcOp.DECLNK; } + } - record DeclnkN(int n) implements BcInstr { - @Override - public BcOp op() { - return BcOp.DECLNK_N; - } + record DeclnkN(int n) implements BcInstr { + @Override + public BcOp op() { + return BcOp.DECLNK_N; } + } - record IncLnkStk() implements BcInstr { - @Override - public BcOp op() { - return BcOp.INCLNKSTK; - } + record IncLnkStk() implements BcInstr { + @Override + public BcOp op() { + return BcOp.INCLNKSTK; } + } - record DecLnkStk() implements BcInstr { - @Override - public BcOp op() { - return BcOp.DECLNKSTK; - } + record DecLnkStk() implements BcInstr { + @Override + public BcOp op() { + return BcOp.DECLNKSTK; } + } } class BcInstrs { - /** Create from the raw GNU-R representation. - * - * @param bytecodes The full list of instruction bytecodes including ones before and after this one - * @param i The index in the list where this instruction starts - * @param makePoolIdx A function to create pool indices from raw integers - * @return The instruction and the index in the list where the next instruction starts - * @apiNote This has to be in a separate class because it's package-private but interface methods are public. - */ - static Pair fromRaw(ImmutableIntArray bytecodes, int i, ConstPool.MakeIdx makePoolIdx) { - BcOp op; - try { - op = BcOp.valueOf(bytecodes.get(i++)); - } catch (IllegalArgumentException e) { - throw new BcFromRawException("invalid opcode (instruction) " + bytecodes.get(i - 1)); - } - - try { - var instr = switch (op) { - case BCMISMATCH -> throw new BcFromRawException("invalid opcode " + BcOp.BCMISMATCH.value()); - case RETURN -> new BcInstr.Return(); - case GOTO -> new BcInstr.Goto(new BcLabel(bytecodes.get(i++))); - case BRIFNOT -> new BcInstr.BrIfNot(makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); - case POP -> new BcInstr.Pop(); - case DUP -> new BcInstr.Dup(); - case PRINTVALUE -> new BcInstr.PrintValue(); - case STARTLOOPCNTXT -> new BcInstr.StartLoopCntxt(bytecodes.get(i++) != 0, new BcLabel(bytecodes.get(i++))); - case ENDLOOPCNTXT -> new BcInstr.EndLoopCntxt(bytecodes.get(i++) != 0); - case DOLOOPNEXT -> new BcInstr.DoLoopNext(); - case DOLOOPBREAK -> new BcInstr.DoLoopBreak(); - case STARTFOR -> new BcInstr.StartFor(makePoolIdx.lang(bytecodes.get(i++)), makePoolIdx.sym(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); - case STEPFOR -> new BcInstr.StepFor(new BcLabel(bytecodes.get(i++))); - case ENDFOR -> new BcInstr.EndFor(); - case SETLOOPVAL -> new BcInstr.SetLoopVal(); - case INVISIBLE -> new BcInstr.Invisible(); - case LDCONST -> new BcInstr.LdConst(makePoolIdx.any(bytecodes.get(i++))); - case LDNULL -> new BcInstr.LdNull(); - case LDTRUE -> new BcInstr.LdTrue(); - case LDFALSE -> new BcInstr.LdFalse(); - case GETVAR -> new BcInstr.GetVar(makePoolIdx.sym(bytecodes.get(i++))); - case DDVAL -> new BcInstr.DdVal(makePoolIdx.sym(bytecodes.get(i++))); - case SETVAR -> new BcInstr.SetVar(makePoolIdx.sym(bytecodes.get(i++))); - case GETFUN -> new BcInstr.GetFun(makePoolIdx.sym(bytecodes.get(i++))); - case GETGLOBFUN -> new BcInstr.GetGlobFun(makePoolIdx.sym(bytecodes.get(i++))); - case GETSYMFUN -> new BcInstr.GetSymFun(makePoolIdx.sym(bytecodes.get(i++))); - case GETBUILTIN -> new BcInstr.GetBuiltin(makePoolIdx.sym(bytecodes.get(i++))); - case GETINTLBUILTIN -> new BcInstr.GetIntlBuiltin(makePoolIdx.sym(bytecodes.get(i++))); - case CHECKFUN -> new BcInstr.CheckFun(); - case MAKEPROM -> new BcInstr.MakeProm(makePoolIdx.any(bytecodes.get(i++))); - case DOMISSING -> new BcInstr.DoMissing(); - case SETTAG -> new BcInstr.SetTag(makePoolIdx.strOrSymOrNil(bytecodes.get(i++))); - case DODOTS -> new BcInstr.DoDots(); - case PUSHARG -> new BcInstr.PushArg(); - case PUSHCONSTARG -> new BcInstr.PushConstArg(makePoolIdx.any(bytecodes.get(i++))); - case PUSHNULLARG -> new BcInstr.PushNullArg(); - case PUSHTRUEARG -> new BcInstr.PushTrueArg(); - case PUSHFALSEARG -> new BcInstr.PushFalseArg(); - case CALL -> new BcInstr.Call(makePoolIdx.lang(bytecodes.get(i++))); - case CALLBUILTIN -> new BcInstr.CallBuiltin(makePoolIdx.lang(bytecodes.get(i++))); - case CALLSPECIAL -> new BcInstr.CallSpecial(makePoolIdx.lang(bytecodes.get(i++))); - case MAKECLOSURE -> new BcInstr.MakeClosure(makePoolIdx.formalsBodyAndMaybeSrcRef(bytecodes.get(i++))); - case UMINUS -> new BcInstr.UMinus(makePoolIdx.lang(bytecodes.get(i++))); - case UPLUS -> new BcInstr.UPlus(makePoolIdx.lang(bytecodes.get(i++))); - case ADD -> new BcInstr.Add(makePoolIdx.lang(bytecodes.get(i++))); - case SUB -> new BcInstr.Sub(makePoolIdx.lang(bytecodes.get(i++))); - case MUL -> new BcInstr.Mul(makePoolIdx.lang(bytecodes.get(i++))); - case DIV -> new BcInstr.Div(makePoolIdx.lang(bytecodes.get(i++))); - case EXPT -> new BcInstr.Expt(makePoolIdx.lang(bytecodes.get(i++))); - case SQRT -> new BcInstr.Sqrt(makePoolIdx.lang(bytecodes.get(i++))); - case EXP -> new BcInstr.Exp(makePoolIdx.lang(bytecodes.get(i++))); - case EQ -> new BcInstr.Eq(makePoolIdx.lang(bytecodes.get(i++))); - case NE -> new BcInstr.Ne(makePoolIdx.lang(bytecodes.get(i++))); - case LT -> new BcInstr.Lt(makePoolIdx.lang(bytecodes.get(i++))); - case LE -> new BcInstr.Le(makePoolIdx.lang(bytecodes.get(i++))); - case GE -> new BcInstr.Ge(makePoolIdx.lang(bytecodes.get(i++))); - case GT -> new BcInstr.Gt(makePoolIdx.lang(bytecodes.get(i++))); - case AND -> new BcInstr.And(makePoolIdx.lang(bytecodes.get(i++))); - case OR -> new BcInstr.Or(makePoolIdx.lang(bytecodes.get(i++))); - case NOT -> new BcInstr.Not(makePoolIdx.lang(bytecodes.get(i++))); - case DOTSERR -> new BcInstr.DotsErr(); - case STARTASSIGN -> new BcInstr.StartAssign(makePoolIdx.sym(bytecodes.get(i++))); - case ENDASSIGN -> new BcInstr.EndAssign(makePoolIdx.sym(bytecodes.get(i++))); - case STARTSUBSET -> new BcInstr.StartSubset(makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); - case DFLTSUBSET -> new BcInstr.DfltSubset(); - case STARTSUBASSIGN -> new BcInstr.StartSubassign(makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); - case DFLTSUBASSIGN -> new BcInstr.DfltSubassign(); - case STARTC -> new BcInstr.StartC(makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); - case DFLTC -> new BcInstr.DfltC(); - case STARTSUBSET2 -> new BcInstr.StartSubset2(makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); - case DFLTSUBSET2 -> new BcInstr.DfltSubset2(); - case STARTSUBASSIGN2 -> new BcInstr.StartSubassign2(makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); - case DFLTSUBASSIGN2 -> new BcInstr.DfltSubassign2(); - case DOLLAR -> new BcInstr.Dollar(makePoolIdx.lang(bytecodes.get(i++)), makePoolIdx.sym(bytecodes.get(i++))); - case DOLLARGETS -> new BcInstr.DollarGets(makePoolIdx.lang(bytecodes.get(i++)), makePoolIdx.sym(bytecodes.get(i++))); - case ISNULL -> new BcInstr.IsNull(); - case ISLOGICAL -> new BcInstr.IsLogical(); - case ISINTEGER -> new BcInstr.IsInteger(); - case ISDOUBLE -> new BcInstr.IsDouble(); - case ISCOMPLEX -> new BcInstr.IsComplex(); - case ISCHARACTER -> new BcInstr.IsCharacter(); - case ISSYMBOL -> new BcInstr.IsSymbol(); - case ISOBJECT -> new BcInstr.IsObject(); - case ISNUMERIC -> new BcInstr.IsNumeric(); - case VECSUBSET -> new BcInstr.VecSubset(makePoolIdx.langOrNegative(bytecodes.get(i++))); - case MATSUBSET -> new BcInstr.MatSubset(makePoolIdx.langOrNegative(bytecodes.get(i++))); - case VECSUBASSIGN -> new BcInstr.VecSubassign(makePoolIdx.langOrNegative(bytecodes.get(i++))); - case MATSUBASSIGN -> new BcInstr.MatSubassign(makePoolIdx.langOrNegative(bytecodes.get(i++))); - case AND1ST -> new BcInstr.And1st(makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); - case AND2ND -> new BcInstr.And2nd(makePoolIdx.lang(bytecodes.get(i++))); - case OR1ST -> new BcInstr.Or1st(makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); - case OR2ND -> new BcInstr.Or2nd(makePoolIdx.lang(bytecodes.get(i++))); - case GETVAR_MISSOK -> new BcInstr.GetVarMissOk(makePoolIdx.sym(bytecodes.get(i++))); - case DDVAL_MISSOK -> new BcInstr.DdValMissOk(makePoolIdx.sym(bytecodes.get(i++))); - case VISIBLE -> new BcInstr.Visible(); - case SETVAR2 -> new BcInstr.SetVar2(makePoolIdx.sym(bytecodes.get(i++))); - case STARTASSIGN2 -> new BcInstr.StartAssign2(makePoolIdx.sym(bytecodes.get(i++))); - case ENDASSIGN2 -> new BcInstr.EndAssign2(makePoolIdx.sym(bytecodes.get(i++))); - case SETTER_CALL -> new BcInstr.SetterCall(makePoolIdx.lang(bytecodes.get(i++)), makePoolIdx.any(bytecodes.get(i++))); - case GETTER_CALL -> new BcInstr.GetterCall(makePoolIdx.lang(bytecodes.get(i++))); - case SWAP -> new BcInstr.SpecialSwap(); - case DUP2ND -> new BcInstr.Dup2nd(); - case SWITCH -> new BcInstr.Switch(makePoolIdx.lang(bytecodes.get(i++)), makePoolIdx.strOrNilOrOther(bytecodes.get(i++)), makePoolIdx.intOrOther(bytecodes.get(i++)), makePoolIdx.intOrOther(bytecodes.get(i++))); - case RETURNJMP -> new BcInstr.ReturnJmp(); - case STARTSUBSET_N -> new BcInstr.StartSubsetN(makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); - case STARTSUBASSIGN_N -> new BcInstr.StartSubassignN(makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); - case VECSUBSET2 -> new BcInstr.VecSubset2(makePoolIdx.langOrNegative(bytecodes.get(i++))); - case MATSUBSET2 -> new BcInstr.MatSubset2(makePoolIdx.langOrNegative(bytecodes.get(i++))); - case VECSUBASSIGN2 -> new BcInstr.VecSubassign2(makePoolIdx.langOrNegative(bytecodes.get(i++))); - case MATSUBASSIGN2 -> new BcInstr.MatSubassign2(makePoolIdx.langOrNegative(bytecodes.get(i++))); - case STARTSUBSET2_N -> new BcInstr.StartSubset2N(makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); - case STARTSUBASSIGN2_N -> new BcInstr.StartSubassign2N(makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); - case SUBSET_N -> new BcInstr.SubsetN(makePoolIdx.langOrNegative(bytecodes.get(i++)), bytecodes.get(i++)); - case SUBSET2_N -> new BcInstr.Subset2N(makePoolIdx.langOrNegative(bytecodes.get(i++)), bytecodes.get(i++)); - case SUBASSIGN_N -> new BcInstr.SubassignN(makePoolIdx.langOrNegative(bytecodes.get(i++)), bytecodes.get(i++)); - case SUBASSIGN2_N -> new BcInstr.Subassign2N(makePoolIdx.langOrNegative(bytecodes.get(i++)), bytecodes.get(i++)); - case LOG -> new BcInstr.Log(makePoolIdx.lang(bytecodes.get(i++))); - case LOGBASE -> new BcInstr.LogBase(makePoolIdx.lang(bytecodes.get(i++))); - case MATH1 -> new BcInstr.Math1(makePoolIdx.lang(bytecodes.get(i++)), bytecodes.get(i++)); - case DOTCALL -> new BcInstr.DotCall(makePoolIdx.lang(bytecodes.get(i++)), bytecodes.get(i++)); - case COLON -> new BcInstr.Colon(makePoolIdx.lang(bytecodes.get(i++))); - case SEQALONG -> new BcInstr.SeqAlong(makePoolIdx.lang(bytecodes.get(i++))); - case SEQLEN -> new BcInstr.SeqLen(makePoolIdx.lang(bytecodes.get(i++))); - case BASEGUARD -> new BcInstr.BaseGuard(makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); - case INCLNK -> new BcInstr.IncLnk(); - case DECLNK -> new BcInstr.DecLnk(); - case DECLNK_N -> new BcInstr.DeclnkN(bytecodes.get(i++)); - case INCLNKSTK -> new BcInstr.IncLnkStk(); - case DECLNKSTK -> new BcInstr.DecLnkStk(); - }; - return new Pair<>(instr, i); - } catch (IllegalArgumentException e) { - throw new BcFromRawException("invalid opcode (arguments) " + op, e); - } catch (ArrayIndexOutOfBoundsException e) { - throw new BcFromRawException("invalid opcode (arguments, unexpected end of bytecode stream) " + op); - } - } + /** + * Create from the raw GNU-R representation. + * + * @param bytecodes The full list of instruction bytecodes including ones before and after this + * one + * @param i The index in the list where this instruction starts + * @param makePoolIdx A function to create pool indices from raw integers + * @return The instruction and the index in the list where the next instruction starts + * @apiNote This has to be in a separate class because it's package-private but interface methods + * are public. + */ + static Pair fromRaw( + ImmutableIntArray bytecodes, int i, ConstPool.MakeIdx makePoolIdx) { + BcOp op; + try { + op = BcOp.valueOf(bytecodes.get(i++)); + } catch (IllegalArgumentException e) { + throw new BcFromRawException("invalid opcode (instruction) " + bytecodes.get(i - 1)); + } + + try { + var instr = + switch (op) { + case BCMISMATCH -> + throw new BcFromRawException("invalid opcode " + BcOp.BCMISMATCH.value()); + case RETURN -> new BcInstr.Return(); + case GOTO -> new BcInstr.Goto(new BcLabel(bytecodes.get(i++))); + case BRIFNOT -> + new BcInstr.BrIfNot( + makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); + case POP -> new BcInstr.Pop(); + case DUP -> new BcInstr.Dup(); + case PRINTVALUE -> new BcInstr.PrintValue(); + case STARTLOOPCNTXT -> + new BcInstr.StartLoopCntxt( + bytecodes.get(i++) != 0, new BcLabel(bytecodes.get(i++))); + case ENDLOOPCNTXT -> new BcInstr.EndLoopCntxt(bytecodes.get(i++) != 0); + case DOLOOPNEXT -> new BcInstr.DoLoopNext(); + case DOLOOPBREAK -> new BcInstr.DoLoopBreak(); + case STARTFOR -> + new BcInstr.StartFor( + makePoolIdx.lang(bytecodes.get(i++)), + makePoolIdx.sym(bytecodes.get(i++)), + new BcLabel(bytecodes.get(i++))); + case STEPFOR -> new BcInstr.StepFor(new BcLabel(bytecodes.get(i++))); + case ENDFOR -> new BcInstr.EndFor(); + case SETLOOPVAL -> new BcInstr.SetLoopVal(); + case INVISIBLE -> new BcInstr.Invisible(); + case LDCONST -> new BcInstr.LdConst(makePoolIdx.any(bytecodes.get(i++))); + case LDNULL -> new BcInstr.LdNull(); + case LDTRUE -> new BcInstr.LdTrue(); + case LDFALSE -> new BcInstr.LdFalse(); + case GETVAR -> new BcInstr.GetVar(makePoolIdx.sym(bytecodes.get(i++))); + case DDVAL -> new BcInstr.DdVal(makePoolIdx.sym(bytecodes.get(i++))); + case SETVAR -> new BcInstr.SetVar(makePoolIdx.sym(bytecodes.get(i++))); + case GETFUN -> new BcInstr.GetFun(makePoolIdx.sym(bytecodes.get(i++))); + case GETGLOBFUN -> new BcInstr.GetGlobFun(makePoolIdx.sym(bytecodes.get(i++))); + case GETSYMFUN -> new BcInstr.GetSymFun(makePoolIdx.sym(bytecodes.get(i++))); + case GETBUILTIN -> new BcInstr.GetBuiltin(makePoolIdx.sym(bytecodes.get(i++))); + case GETINTLBUILTIN -> new BcInstr.GetIntlBuiltin(makePoolIdx.sym(bytecodes.get(i++))); + case CHECKFUN -> new BcInstr.CheckFun(); + case MAKEPROM -> new BcInstr.MakeProm(makePoolIdx.any(bytecodes.get(i++))); + case DOMISSING -> new BcInstr.DoMissing(); + case SETTAG -> new BcInstr.SetTag(makePoolIdx.strOrSymOrNil(bytecodes.get(i++))); + case DODOTS -> new BcInstr.DoDots(); + case PUSHARG -> new BcInstr.PushArg(); + case PUSHCONSTARG -> new BcInstr.PushConstArg(makePoolIdx.any(bytecodes.get(i++))); + case PUSHNULLARG -> new BcInstr.PushNullArg(); + case PUSHTRUEARG -> new BcInstr.PushTrueArg(); + case PUSHFALSEARG -> new BcInstr.PushFalseArg(); + case CALL -> new BcInstr.Call(makePoolIdx.lang(bytecodes.get(i++))); + case CALLBUILTIN -> new BcInstr.CallBuiltin(makePoolIdx.lang(bytecodes.get(i++))); + case CALLSPECIAL -> new BcInstr.CallSpecial(makePoolIdx.lang(bytecodes.get(i++))); + case MAKECLOSURE -> + new BcInstr.MakeClosure(makePoolIdx.formalsBodyAndMaybeSrcRef(bytecodes.get(i++))); + case UMINUS -> new BcInstr.UMinus(makePoolIdx.lang(bytecodes.get(i++))); + case UPLUS -> new BcInstr.UPlus(makePoolIdx.lang(bytecodes.get(i++))); + case ADD -> new BcInstr.Add(makePoolIdx.lang(bytecodes.get(i++))); + case SUB -> new BcInstr.Sub(makePoolIdx.lang(bytecodes.get(i++))); + case MUL -> new BcInstr.Mul(makePoolIdx.lang(bytecodes.get(i++))); + case DIV -> new BcInstr.Div(makePoolIdx.lang(bytecodes.get(i++))); + case EXPT -> new BcInstr.Expt(makePoolIdx.lang(bytecodes.get(i++))); + case SQRT -> new BcInstr.Sqrt(makePoolIdx.lang(bytecodes.get(i++))); + case EXP -> new BcInstr.Exp(makePoolIdx.lang(bytecodes.get(i++))); + case EQ -> new BcInstr.Eq(makePoolIdx.lang(bytecodes.get(i++))); + case NE -> new BcInstr.Ne(makePoolIdx.lang(bytecodes.get(i++))); + case LT -> new BcInstr.Lt(makePoolIdx.lang(bytecodes.get(i++))); + case LE -> new BcInstr.Le(makePoolIdx.lang(bytecodes.get(i++))); + case GE -> new BcInstr.Ge(makePoolIdx.lang(bytecodes.get(i++))); + case GT -> new BcInstr.Gt(makePoolIdx.lang(bytecodes.get(i++))); + case AND -> new BcInstr.And(makePoolIdx.lang(bytecodes.get(i++))); + case OR -> new BcInstr.Or(makePoolIdx.lang(bytecodes.get(i++))); + case NOT -> new BcInstr.Not(makePoolIdx.lang(bytecodes.get(i++))); + case DOTSERR -> new BcInstr.DotsErr(); + case STARTASSIGN -> new BcInstr.StartAssign(makePoolIdx.sym(bytecodes.get(i++))); + case ENDASSIGN -> new BcInstr.EndAssign(makePoolIdx.sym(bytecodes.get(i++))); + case STARTSUBSET -> + new BcInstr.StartSubset( + makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); + case DFLTSUBSET -> new BcInstr.DfltSubset(); + case STARTSUBASSIGN -> + new BcInstr.StartSubassign( + makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); + case DFLTSUBASSIGN -> new BcInstr.DfltSubassign(); + case STARTC -> + new BcInstr.StartC( + makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); + case DFLTC -> new BcInstr.DfltC(); + case STARTSUBSET2 -> + new BcInstr.StartSubset2( + makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); + case DFLTSUBSET2 -> new BcInstr.DfltSubset2(); + case STARTSUBASSIGN2 -> + new BcInstr.StartSubassign2( + makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); + case DFLTSUBASSIGN2 -> new BcInstr.DfltSubassign2(); + case DOLLAR -> + new BcInstr.Dollar( + makePoolIdx.lang(bytecodes.get(i++)), makePoolIdx.sym(bytecodes.get(i++))); + case DOLLARGETS -> + new BcInstr.DollarGets( + makePoolIdx.lang(bytecodes.get(i++)), makePoolIdx.sym(bytecodes.get(i++))); + case ISNULL -> new BcInstr.IsNull(); + case ISLOGICAL -> new BcInstr.IsLogical(); + case ISINTEGER -> new BcInstr.IsInteger(); + case ISDOUBLE -> new BcInstr.IsDouble(); + case ISCOMPLEX -> new BcInstr.IsComplex(); + case ISCHARACTER -> new BcInstr.IsCharacter(); + case ISSYMBOL -> new BcInstr.IsSymbol(); + case ISOBJECT -> new BcInstr.IsObject(); + case ISNUMERIC -> new BcInstr.IsNumeric(); + case VECSUBSET -> new BcInstr.VecSubset(makePoolIdx.langOrNegative(bytecodes.get(i++))); + case MATSUBSET -> new BcInstr.MatSubset(makePoolIdx.langOrNegative(bytecodes.get(i++))); + case VECSUBASSIGN -> + new BcInstr.VecSubassign(makePoolIdx.langOrNegative(bytecodes.get(i++))); + case MATSUBASSIGN -> + new BcInstr.MatSubassign(makePoolIdx.langOrNegative(bytecodes.get(i++))); + case AND1ST -> + new BcInstr.And1st( + makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); + case AND2ND -> new BcInstr.And2nd(makePoolIdx.lang(bytecodes.get(i++))); + case OR1ST -> + new BcInstr.Or1st( + makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); + case OR2ND -> new BcInstr.Or2nd(makePoolIdx.lang(bytecodes.get(i++))); + case GETVAR_MISSOK -> new BcInstr.GetVarMissOk(makePoolIdx.sym(bytecodes.get(i++))); + case DDVAL_MISSOK -> new BcInstr.DdValMissOk(makePoolIdx.sym(bytecodes.get(i++))); + case VISIBLE -> new BcInstr.Visible(); + case SETVAR2 -> new BcInstr.SetVar2(makePoolIdx.sym(bytecodes.get(i++))); + case STARTASSIGN2 -> new BcInstr.StartAssign2(makePoolIdx.sym(bytecodes.get(i++))); + case ENDASSIGN2 -> new BcInstr.EndAssign2(makePoolIdx.sym(bytecodes.get(i++))); + case SETTER_CALL -> + new BcInstr.SetterCall( + makePoolIdx.lang(bytecodes.get(i++)), makePoolIdx.any(bytecodes.get(i++))); + case GETTER_CALL -> new BcInstr.GetterCall(makePoolIdx.lang(bytecodes.get(i++))); + case SWAP -> new BcInstr.SpecialSwap(); + case DUP2ND -> new BcInstr.Dup2nd(); + case SWITCH -> + new BcInstr.Switch( + makePoolIdx.lang(bytecodes.get(i++)), + makePoolIdx.strOrNilOrOther(bytecodes.get(i++)), + makePoolIdx.intOrOther(bytecodes.get(i++)), + makePoolIdx.intOrOther(bytecodes.get(i++))); + case RETURNJMP -> new BcInstr.ReturnJmp(); + case STARTSUBSET_N -> + new BcInstr.StartSubsetN( + makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); + case STARTSUBASSIGN_N -> + new BcInstr.StartSubassignN( + makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); + case VECSUBSET2 -> + new BcInstr.VecSubset2(makePoolIdx.langOrNegative(bytecodes.get(i++))); + case MATSUBSET2 -> + new BcInstr.MatSubset2(makePoolIdx.langOrNegative(bytecodes.get(i++))); + case VECSUBASSIGN2 -> + new BcInstr.VecSubassign2(makePoolIdx.langOrNegative(bytecodes.get(i++))); + case MATSUBASSIGN2 -> + new BcInstr.MatSubassign2(makePoolIdx.langOrNegative(bytecodes.get(i++))); + case STARTSUBSET2_N -> + new BcInstr.StartSubset2N( + makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); + case STARTSUBASSIGN2_N -> + new BcInstr.StartSubassign2N( + makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); + case SUBSET_N -> + new BcInstr.SubsetN( + makePoolIdx.langOrNegative(bytecodes.get(i++)), bytecodes.get(i++)); + case SUBSET2_N -> + new BcInstr.Subset2N( + makePoolIdx.langOrNegative(bytecodes.get(i++)), bytecodes.get(i++)); + case SUBASSIGN_N -> + new BcInstr.SubassignN( + makePoolIdx.langOrNegative(bytecodes.get(i++)), bytecodes.get(i++)); + case SUBASSIGN2_N -> + new BcInstr.Subassign2N( + makePoolIdx.langOrNegative(bytecodes.get(i++)), bytecodes.get(i++)); + case LOG -> new BcInstr.Log(makePoolIdx.lang(bytecodes.get(i++))); + case LOGBASE -> new BcInstr.LogBase(makePoolIdx.lang(bytecodes.get(i++))); + case MATH1 -> + new BcInstr.Math1(makePoolIdx.lang(bytecodes.get(i++)), bytecodes.get(i++)); + case DOTCALL -> + new BcInstr.DotCall(makePoolIdx.lang(bytecodes.get(i++)), bytecodes.get(i++)); + case COLON -> new BcInstr.Colon(makePoolIdx.lang(bytecodes.get(i++))); + case SEQALONG -> new BcInstr.SeqAlong(makePoolIdx.lang(bytecodes.get(i++))); + case SEQLEN -> new BcInstr.SeqLen(makePoolIdx.lang(bytecodes.get(i++))); + case BASEGUARD -> + new BcInstr.BaseGuard( + makePoolIdx.lang(bytecodes.get(i++)), new BcLabel(bytecodes.get(i++))); + case INCLNK -> new BcInstr.IncLnk(); + case DECLNK -> new BcInstr.DecLnk(); + case DECLNK_N -> new BcInstr.DeclnkN(bytecodes.get(i++)); + case INCLNKSTK -> new BcInstr.IncLnkStk(); + case DECLNKSTK -> new BcInstr.DecLnkStk(); + }; + return new Pair<>(instr, i); + } catch (IllegalArgumentException e) { + throw new BcFromRawException("invalid opcode (arguments) " + op, e); + } catch (ArrayIndexOutOfBoundsException e) { + throw new BcFromRawException( + "invalid opcode (arguments, unexpected end of bytecode stream) " + op); + } + } } diff --git a/src/main/java/org/prlprg/bc/BcLabel.java b/src/main/java/org/prlprg/bc/BcLabel.java index 1685c628d..5dbdb7b6a 100644 --- a/src/main/java/org/prlprg/bc/BcLabel.java +++ b/src/main/java/org/prlprg/bc/BcLabel.java @@ -1,4 +1,3 @@ package org.prlprg.bc; -public record BcLabel(int id) { -} +public record BcLabel(int id) {} diff --git a/src/main/java/org/prlprg/bc/BcOp.java b/src/main/java/org/prlprg/bc/BcOp.java index dcbaf0b61..00bbdbe24 100644 --- a/src/main/java/org/prlprg/bc/BcOp.java +++ b/src/main/java/org/prlprg/bc/BcOp.java @@ -1,146 +1,144 @@ package org.prlprg.bc; -/** - * Bytecode operations. Synced with those in `gnur/src/eval.c`. - */ +/** Bytecode operations. Synced with those in `gnur/src/eval.c`. */ public enum BcOp { - BCMISMATCH, - RETURN, - GOTO, - BRIFNOT, - POP, - DUP, - PRINTVALUE, - STARTLOOPCNTXT, - ENDLOOPCNTXT, - DOLOOPNEXT, - DOLOOPBREAK, - STARTFOR, - STEPFOR, - ENDFOR, - SETLOOPVAL, - INVISIBLE, - LDCONST, - LDNULL, - LDTRUE, - LDFALSE, - GETVAR, - DDVAL, - SETVAR, - GETFUN, - GETGLOBFUN, - GETSYMFUN, - GETBUILTIN, - GETINTLBUILTIN, - CHECKFUN, - MAKEPROM, - DOMISSING, - SETTAG, - DODOTS, - PUSHARG, - PUSHCONSTARG, - PUSHNULLARG, - PUSHTRUEARG, - PUSHFALSEARG, - CALL, - CALLBUILTIN, - CALLSPECIAL, - MAKECLOSURE, - UMINUS, - UPLUS, - ADD, - SUB, - MUL, - DIV, - EXPT, - SQRT, - EXP, - EQ, - NE, - LT, - LE, - GE, - GT, - AND, - OR, - NOT, - DOTSERR, - STARTASSIGN, - ENDASSIGN, - STARTSUBSET, - DFLTSUBSET, - STARTSUBASSIGN, - DFLTSUBASSIGN, - STARTC, - DFLTC, - STARTSUBSET2, - DFLTSUBSET2, - STARTSUBASSIGN2, - DFLTSUBASSIGN2, - DOLLAR, - DOLLARGETS, - ISNULL, - ISLOGICAL, - ISINTEGER, - ISDOUBLE, - ISCOMPLEX, - ISCHARACTER, - ISSYMBOL, - ISOBJECT, - ISNUMERIC, - VECSUBSET, - MATSUBSET, - VECSUBASSIGN, - MATSUBASSIGN, - AND1ST, - AND2ND, - OR1ST, - OR2ND, - GETVAR_MISSOK, - DDVAL_MISSOK, - VISIBLE, - SETVAR2, - STARTASSIGN2, - ENDASSIGN2, - SETTER_CALL, - GETTER_CALL, - SWAP, - DUP2ND, - SWITCH, - RETURNJMP, - STARTSUBSET_N, - STARTSUBASSIGN_N, - VECSUBSET2, - MATSUBSET2, - VECSUBASSIGN2, - MATSUBASSIGN2, - STARTSUBSET2_N, - STARTSUBASSIGN2_N, - SUBSET_N, - SUBSET2_N, - SUBASSIGN_N, - SUBASSIGN2_N, - LOG, - LOGBASE, - MATH1, - DOTCALL, - COLON, - SEQALONG, - SEQLEN, - BASEGUARD, - INCLNK, - DECLNK, - DECLNK_N, - INCLNKSTK, - DECLNKSTK; + BCMISMATCH, + RETURN, + GOTO, + BRIFNOT, + POP, + DUP, + PRINTVALUE, + STARTLOOPCNTXT, + ENDLOOPCNTXT, + DOLOOPNEXT, + DOLOOPBREAK, + STARTFOR, + STEPFOR, + ENDFOR, + SETLOOPVAL, + INVISIBLE, + LDCONST, + LDNULL, + LDTRUE, + LDFALSE, + GETVAR, + DDVAL, + SETVAR, + GETFUN, + GETGLOBFUN, + GETSYMFUN, + GETBUILTIN, + GETINTLBUILTIN, + CHECKFUN, + MAKEPROM, + DOMISSING, + SETTAG, + DODOTS, + PUSHARG, + PUSHCONSTARG, + PUSHNULLARG, + PUSHTRUEARG, + PUSHFALSEARG, + CALL, + CALLBUILTIN, + CALLSPECIAL, + MAKECLOSURE, + UMINUS, + UPLUS, + ADD, + SUB, + MUL, + DIV, + EXPT, + SQRT, + EXP, + EQ, + NE, + LT, + LE, + GE, + GT, + AND, + OR, + NOT, + DOTSERR, + STARTASSIGN, + ENDASSIGN, + STARTSUBSET, + DFLTSUBSET, + STARTSUBASSIGN, + DFLTSUBASSIGN, + STARTC, + DFLTC, + STARTSUBSET2, + DFLTSUBSET2, + STARTSUBASSIGN2, + DFLTSUBASSIGN2, + DOLLAR, + DOLLARGETS, + ISNULL, + ISLOGICAL, + ISINTEGER, + ISDOUBLE, + ISCOMPLEX, + ISCHARACTER, + ISSYMBOL, + ISOBJECT, + ISNUMERIC, + VECSUBSET, + MATSUBSET, + VECSUBASSIGN, + MATSUBASSIGN, + AND1ST, + AND2ND, + OR1ST, + OR2ND, + GETVAR_MISSOK, + DDVAL_MISSOK, + VISIBLE, + SETVAR2, + STARTASSIGN2, + ENDASSIGN2, + SETTER_CALL, + GETTER_CALL, + SWAP, + DUP2ND, + SWITCH, + RETURNJMP, + STARTSUBSET_N, + STARTSUBASSIGN_N, + VECSUBSET2, + MATSUBSET2, + VECSUBASSIGN2, + MATSUBASSIGN2, + STARTSUBSET2_N, + STARTSUBASSIGN2_N, + SUBSET_N, + SUBSET2_N, + SUBASSIGN_N, + SUBASSIGN2_N, + LOG, + LOGBASE, + MATH1, + DOTCALL, + COLON, + SEQALONG, + SEQLEN, + BASEGUARD, + INCLNK, + DECLNK, + DECLNK_N, + INCLNKSTK, + DECLNKSTK; - /** Returns the operation represented by the given integer in GNU-R. */ - public static BcOp valueOf(int i) { - return values()[i]; - } + /** Returns the operation represented by the given integer in GNU-R. */ + public static BcOp valueOf(int i) { + return values()[i]; + } - /** Returns the integer representation of this operation in GNU-R. */ - public int value() { - return ordinal(); - } + /** Returns the integer representation of this operation in GNU-R. */ + public int value() { + return ordinal(); + } } diff --git a/src/main/java/org/prlprg/bc/ConstPool.java b/src/main/java/org/prlprg/bc/ConstPool.java index 194fcabde..7c8302a9b 100644 --- a/src/main/java/org/prlprg/bc/ConstPool.java +++ b/src/main/java/org/prlprg/bc/ConstPool.java @@ -4,341 +4,355 @@ import com.google.common.collect.ForwardingCollection; import com.google.common.collect.ImmutableList; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import java.util.*; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; import org.prlprg.sexp.*; import org.prlprg.util.Either; import org.prlprg.util.Pair; -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; -import java.util.*; - /** - * A pool (array) of constants. Underneath this is an immutable list, but the elements are only accessible with typed - * integers. + * A pool (array) of constants. Underneath this is an immutable list, but the elements are only + * accessible with typed integers. */ @SuppressFBWarnings( - value = "JCIP_FIELD_ISNT_FINAL_IN_IMMUTABLE_CLASS", - justification = "The class isn't technically immutable but is after we use the thread-unsafe builder, " + - "so practically we treat it as immutable" -) + value = "JCIP_FIELD_ISNT_FINAL_IN_IMMUTABLE_CLASS", + justification = + "The class isn't technically immutable but is after we use the thread-unsafe builder, " + + "so practically we treat it as immutable") @Immutable public final class ConstPool extends ForwardingCollection { - @Nullable - private ImmutableList consts; + @Nullable private ImmutableList consts; - private ConstPool() { - } + private ConstPool() {} - @Override - protected Collection delegate() { - if (consts == null) { - throw new IllegalStateException("ConstPool is not yet built"); - } - return consts; + @Override + protected Collection delegate() { + if (consts == null) { + throw new IllegalStateException("ConstPool is not yet built"); } - - /** Get the element at the given pool index - * @throws WrongPoolException if the index is for a different pool - * @throws IndexOutOfBoundsException if the index is out of bounds - */ - public SEXP get(Idx idx) { - if (consts == null) { - throw new IllegalStateException("ConstPool is not yet built"); - } - return consts.get(idx.unwrapIdx(this)); + return consts; + } + + /** + * Get the element at the given pool index + * + * @throws WrongPoolException if the index is for a different pool + * @throws IndexOutOfBoundsException if the index is out of bounds + */ + public SEXP get(Idx idx) { + if (consts == null) { + throw new IllegalStateException("ConstPool is not yet built"); } - - /** Get the element at the given pool index - * @throws WrongPoolException if the index is for a different pool - * @throws IndexOutOfBoundsException if the index is out of bounds - */ - public S get(TypedIdx idx) { - if (consts == null) { - throw new IllegalStateException("ConstPool is not yet built"); - } - assert idx.checkType(); - @SuppressWarnings("unchecked") - var res = (S)consts.get(idx.unwrapIdx(this)); - return res; + return consts.get(idx.unwrapIdx(this)); + } + + /** + * Get the element at the given pool index + * + * @throws WrongPoolException if the index is for a different pool + * @throws IndexOutOfBoundsException if the index is out of bounds + */ + public S get(TypedIdx idx) { + if (consts == null) { + throw new IllegalStateException("ConstPool is not yet built"); } - - - /** If the SEXP is a constant, returns its index. Otherwise returns null. */ - public @Nullable TypedIdx indexOf(S c) { - if (consts == null) { - throw new IllegalStateException("ConstPool is not yet built"); - } - var i = consts.indexOf(c); - if (i == -1) { - return null; - } - // This is only valid because TypedIdx is covariant, and only accepted because Java erases generics. - // The conversion from Class to Class changes the generic. - @SuppressWarnings("unchecked") var idx = new TypedIdx<>(this, i, (Class)c.getClass()); - assert idx.checkType(); - return idx; + assert idx.checkType(); + @SuppressWarnings("unchecked") + var res = (S) consts.get(idx.unwrapIdx(this)); + return res; + } + + /** If the SEXP is a constant, returns its index. Otherwise returns null. */ + public @Nullable TypedIdx indexOf(S c) { + if (consts == null) { + throw new IllegalStateException("ConstPool is not yet built"); } - - /** Iterate all indices */ - public Iterable indices() { - return () -> new Iterator<>() { - int i = 0; - - @Override - public boolean hasNext() { - if (consts == null) { - throw new IllegalStateException("ConstPool is not yet built"); - } - return i < consts.size(); + var i = consts.indexOf(c); + if (i == -1) { + return null; + } + // This is only valid because TypedIdx is covariant, and only accepted because Java erases + // generics. + // The conversion from Class to Class changes the generic. + @SuppressWarnings("unchecked") + var idx = new TypedIdx<>(this, i, (Class) c.getClass()); + assert idx.checkType(); + return idx; + } + + /** Iterate all indices */ + public Iterable indices() { + return () -> + new Iterator<>() { + int i = 0; + + @Override + public boolean hasNext() { + if (consts == null) { + throw new IllegalStateException("ConstPool is not yet built"); } + return i < consts.size(); + } - @Override - public Idx next() { - if (!hasNext()) { - throw new IndexOutOfBoundsException(); - } - return new Idx(ConstPool.this, i++); + @Override + public Idx next() { + if (!hasNext()) { + throw new IndexOutOfBoundsException(); } + return new Idx(ConstPool.this, i++); + } }; + } + + /** + * Create from the raw GNU-R representation. + * + * @return The pool and a function to create pool indices from raw integers, since that isn't + * ordinarily exposed. + */ + static Pair fromRaw(List consts) throws BcFromRawException { + var builder = new Builder(); + for (var c : consts) { + builder.add(c); + } + + var pool = builder.build(); + return new Pair<>(pool, new MakeIdx(pool)); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("=== CONSTS " + debugId() + " ==="); + var idx = 0; + for (var c : this) { + sb.append("\n").append(idx++).append(": ").append(c); + } + return sb.toString(); + } + + private String debugId() { + return "@" + hashCode(); + } + + /** + * A typed index into a bytecode pool. + * + *

It also contains a reference to the owner pool which is checked at runtime for extra safety. + */ + public static sealed class Idx permits TypedIdx { + protected final ConstPool pool; + protected final int idx; + + private Idx(ConstPool pool, int idx) { + this.pool = pool; + this.idx = idx; } - /** Create from the raw GNU-R representation. + /** + * Return the underlying index if the given pool is the one this was originally created with. * - * @return The pool and a function to create pool indices from raw integers, since that isn't ordinarily exposed. + * @throws WrongPoolException if the given pool is not the one this was originally created with */ - static Pair fromRaw(List consts) throws BcFromRawException { - var builder = new Builder(); - for (var c : consts) { - builder.add(c); - } - - var pool = builder.build(); - return new Pair<>(pool, new MakeIdx(pool)); + protected int unwrapIdx(ConstPool parent) { + if (parent != pool) { + throw new WrongPoolException(); + } + return idx; } @Override - public String toString() { - StringBuilder sb = new StringBuilder("=== CONSTS " + debugId() + " ==="); - var idx = 0; - for (var c : this) { - sb.append("\n").append(idx++).append(": ").append(c); - } - return sb.toString(); + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Idx idx1)) return false; + return idx == idx1.idx && Objects.equal(pool, idx1.pool); } - private String debugId() { - return "@" + hashCode(); + @Override + public int hashCode() { + return Objects.hashCode(pool, idx); } - /** A typed index into a bytecode pool. - *

- * It also contains a reference to the owner pool which is checked at runtime for extra safety. - */ - public sealed static class Idx permits TypedIdx { - protected final ConstPool pool; - protected final int idx; - - private Idx(ConstPool pool, int idx) { - this.pool = pool; - this.idx = idx; - } - - /** - * Return the underlying index if the given pool is the one this was originally created with. - * - * @throws WrongPoolException if the given pool is not the one this was originally created with - * */ - protected int unwrapIdx(ConstPool parent) { - if (parent != pool) { - throw new WrongPoolException(); - } - return idx; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Idx idx1)) return false; - return idx == idx1.idx && Objects.equal(pool, idx1.pool); - } - - @Override - public int hashCode() { - return Objects.hashCode(pool, idx); - } - - @Override - public String toString() { - return "Idx(" + idx + " of " + pool.debugId() + ")"; - } + @Override + public String toString() { + return "Idx(" + idx + " of " + pool.debugId() + ")"; + } + } + + @SuppressFBWarnings( + value = "EQ_DOESNT_OVERRIDE_EQUALS", + justification = + "Idx and TypedIdx only compare `pool` and `index`, we want different types to be equal") + public static final class TypedIdx extends Idx { + private final Class sexpInterface; + + private TypedIdx(ConstPool pool, int idx, Class sexpInterface) { + super(pool, idx); + if (!SEXP.class.isAssignableFrom(sexpInterface)) { + throw new IllegalArgumentException("sexpInterface must be inherit SEXP: " + sexpInterface); + } + this.sexpInterface = sexpInterface; } - @SuppressFBWarnings( - value = "EQ_DOESNT_OVERRIDE_EQUALS", - justification = "Idx and TypedIdx only compare `pool` and `index`, we want different types to be equal" - ) - public static final class TypedIdx extends Idx { - private final Class sexpInterface; - - private TypedIdx(ConstPool pool, int idx, Class sexpInterface) { - super(pool, idx); - if (!SEXP.class.isAssignableFrom(sexpInterface)) { - throw new IllegalArgumentException("sexpInterface must be inherit SEXP: " + sexpInterface); - } - this.sexpInterface = sexpInterface; - } - - private boolean checkType() { - // The cast to Idx is required because the TypedIdx version does an `assert`. - return sexpInterface.isInstance(pool.get((Idx)this)); - } - - @Override - public String toString() { - return "Idx(" + idx + " of " + pool.debugId() + " type " + sexpInterface.getSimpleName() + ")"; - } + private boolean checkType() { + // The cast to Idx is required because the TypedIdx version does an `assert`. + return sexpInterface.isInstance(pool.get((Idx) this)); } - final static class MakeIdx { - private final ConstPool pool; + @Override + public String toString() { + return "Idx(" + + idx + + " of " + + pool.debugId() + + " type " + + sexpInterface.getSimpleName() + + ")"; + } + } - private MakeIdx(ConstPool pool) { - this.pool = pool; - } + static final class MakeIdx { + private final ConstPool pool; - TypedIdx lang(int i) { - return of(i, LangSXP.class); - } + private MakeIdx(ConstPool pool) { + this.pool = pool; + } - TypedIdx sym(int i) { - return of(i, RegSymSXP.class); - } + TypedIdx lang(int i) { + return of(i, LangSXP.class); + } - @Nullable TypedIdx symOrNil(int i) { - return tryOf(i, RegSymSXP.class); - } + TypedIdx sym(int i) { + return of(i, RegSymSXP.class); + } - @Nullable TypedIdx langOrNegative(int i) { - return i >= 0 ? tryOf(i, LangSXP.class) : null; - } + @Nullable TypedIdx symOrNil(int i) { + return tryOf(i, RegSymSXP.class); + } - @Nullable TypedIdx intOrOther(int i) { - return tryOf(i, IntSXP.class); - } + @Nullable TypedIdx langOrNegative(int i) { + return i >= 0 ? tryOf(i, LangSXP.class) : null; + } - @Nullable TypedIdx strOrSymOrNil(int i) { - var asStrOrSymbol = tryOf(i, StrOrRegSymSXP.class); - if (asStrOrSymbol != null) { - return asStrOrSymbol; - } - var asNil = tryOf(i, NilSXP.class); - if (asNil != null) { - return null; - } else { - throw new IllegalArgumentException("Expected StrSXP, SymSXP or NilSXP, got " + pool.get(new Idx(pool, i))); - } - } + @Nullable TypedIdx intOrOther(int i) { + return tryOf(i, IntSXP.class); + } - @Nullable Either, TypedIdx> strOrNilOrOther(int i) { - var asSymbol = tryOf(i, StrSXP.class); - if (asSymbol != null) { - return Either.left(asSymbol); - } - var asNil = tryOf(i, NilSXP.class); - if (asNil != null) { - return Either.right(asNil); - } else { - return null; - } - } + @Nullable TypedIdx strOrSymOrNil(int i) { + var asStrOrSymbol = tryOf(i, StrOrRegSymSXP.class); + if (asStrOrSymbol != null) { + return asStrOrSymbol; + } + var asNil = tryOf(i, NilSXP.class); + if (asNil != null) { + return null; + } else { + throw new IllegalArgumentException( + "Expected StrSXP, SymSXP or NilSXP, got " + pool.get(new Idx(pool, i))); + } + } - TypedIdx formalsBodyAndMaybeSrcRef(int i) { - var idx = of(i, VecSXP.class); + @Nullable Either, TypedIdx> strOrNilOrOther(int i) { + var asSymbol = tryOf(i, StrSXP.class); + if (asSymbol != null) { + return Either.left(asSymbol); + } + var asNil = tryOf(i, NilSXP.class); + if (asNil != null) { + return Either.right(asNil); + } else { + return null; + } + } - // Check vector shape - var vec = pool.get(idx); - if (vec.size() != 2 && vec.size() != 3) { - throw new IllegalArgumentException("Malformed formals/body/srcref vector, expected length 2 or 3, got " + vec); - } - if (!(vec.get(0) instanceof ListSXP)) { - throw new IllegalArgumentException("Malformed formals/body/srcref vector, expected first element to be a list (formals), got " + vec.get(0)); - } + TypedIdx formalsBodyAndMaybeSrcRef(int i) { + var idx = of(i, VecSXP.class); + + // Check vector shape + var vec = pool.get(idx); + if (vec.size() != 2 && vec.size() != 3) { + throw new IllegalArgumentException( + "Malformed formals/body/srcref vector, expected length 2 or 3, got " + vec); + } + if (!(vec.get(0) instanceof ListSXP)) { + throw new IllegalArgumentException( + "Malformed formals/body/srcref vector, expected first element to be a list (formals), got " + + vec.get(0)); + } + + return idx; + } - return idx; - } + Idx any(int i) { + return new Idx(pool, i); + } - Idx any(int i) { - return new Idx(pool, i); - } + private TypedIdx of(int i, Class sexpInterface) { + var idx = new TypedIdx<>(pool, i, sexpInterface); + if (!idx.checkType()) { + // The cast to Idx is required because the TypedIdx version does an `assert`. + throw new IllegalArgumentException( + "Expected " + sexpInterface.getSimpleName() + ", got " + pool.get((Idx) idx)); + } + return idx; + } - private TypedIdx of(int i, Class sexpInterface) { - var idx = new TypedIdx<>(pool, i, sexpInterface); - if (!idx.checkType()) { - // The cast to Idx is required because the TypedIdx version does an `assert`. - throw new IllegalArgumentException("Expected " + sexpInterface.getSimpleName() + ", got " + pool.get((Idx)idx)); - } - return idx; - } + private @Nullable TypedIdx tryOf(int i, Class sexpInterface) { + var idx = new TypedIdx<>(pool, i, sexpInterface); + if (!idx.checkType()) { + return null; + } + return idx; + } + } - private @Nullable TypedIdx tryOf(int i, Class sexpInterface) { - var idx = new TypedIdx<>(pool, i, sexpInterface); - if (!idx.checkType()) { - return null; - } - return idx; - } + public static class WrongPoolException extends RuntimeException { + private WrongPoolException() { + super("Wrong pool"); + } + } + + /** + * A builder class for creating constant pools. + * + *

Not synchronized, so don't use from multiple threads. + */ + public static class Builder { + private final ConstPool pool = new ConstPool(); + private final LinkedHashMap consts = new LinkedHashMap<>(); + + /** Create a new builder. */ + public Builder() {} + + /** Append a constant and return the index. */ + public TypedIdx add(S c) { + // This only works because TypedIdx is covariant the and generic gets erased. + // We actually cast TypedIdx into TypedIdx. + @SuppressWarnings("unchecked") + var idx = + (TypedIdx) + consts.computeIfAbsent( + c, (ignored) -> new TypedIdx<>(pool, consts.size(), (Class) c.getClass())); + return idx; } - public static class WrongPoolException extends RuntimeException { - private WrongPoolException() { - super("Wrong pool"); - } + /** Append instructions. */ + public ImmutableList> addAll(Collection c) { + var builder = ImmutableList.>builder(); + for (var e : c) { + builder.add(add(e)); + } + return builder.build(); } /** - * A builder class for creating constant pools. - *

- * Not synchronized, so don't use from multiple threads. + * Finish building the pool. + * + * @return The pool. */ - public static class Builder { - private final ConstPool pool = new ConstPool(); - private final LinkedHashMap consts = new LinkedHashMap<>(); - - /** - * Create a new builder. - */ - public Builder() { - } - - /** - * Append a constant and return the index. - */ - public TypedIdx add(S c) { - // This only works because TypedIdx is covariant the and generic gets erased. - // We actually cast TypedIdx into TypedIdx. - @SuppressWarnings("unchecked") var idx = (TypedIdx)consts.computeIfAbsent(c, (ignored) -> new TypedIdx<>(pool, consts.size(), (Class)c.getClass())); - return idx; - } - - /** - * Append instructions. - */ - public ImmutableList> addAll(Collection c) { - var builder = ImmutableList.>builder(); - for (var e : c) { - builder.add(add(e)); - } - return builder.build(); - } - - /** - * Finish building the pool. - * - * @return The pool. - */ - public ConstPool build() { - pool.consts = ImmutableList.copyOf(consts.sequencedKeySet()); - return pool; - } + public ConstPool build() { + pool.consts = ImmutableList.copyOf(consts.sequencedKeySet()); + return pool; } + } } diff --git a/src/main/java/org/prlprg/bc/package-info.java b/src/main/java/org/prlprg/bc/package-info.java index 294819c09..805179475 100644 --- a/src/main/java/org/prlprg/bc/package-info.java +++ b/src/main/java/org/prlprg/bc/package-info.java @@ -3,7 +3,6 @@ @ReturnTypesAreNonNullByDefault package org.prlprg.bc; +import javax.annotation.ParametersAreNonnullByDefault; import org.prlprg.util.FieldsAreNonNullByDefault; import org.prlprg.util.ReturnTypesAreNonNullByDefault; - -import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/org/prlprg/compile/Compiler.java b/src/main/java/org/prlprg/compile/Compiler.java index e10461548..187bd935a 100644 --- a/src/main/java/org/prlprg/compile/Compiler.java +++ b/src/main/java/org/prlprg/compile/Compiler.java @@ -1,187 +1,186 @@ package org.prlprg.compile; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import java.util.*; +import javax.annotation.Nullable; import org.prlprg.bc.Bc; import org.prlprg.bc.BcInstr; import org.prlprg.sexp.*; -import javax.annotation.Nullable; -import java.util.*; - public class Compiler { - private static final Set MAYBE_NSE_SYMBOLS = Set.of("bquote"); - - private final Bc.Builder cb = new Bc.Builder(); - - public static Bc compileFun(CloSXP source) { - var body = source.body(); - - // FIXME: this is wrong -- need a special cmpFun to include the formals and others. EMPTY_ENV is also wrong - return compile(body, SEXPs.EMPTY_ENV); - } - - public static Bc compile(SEXP expr, EnvSXP env) { - // TODO: add local variables into the context environment? - return compile(expr, new Context(true, true, env)); - } - - private static Bc compile(SEXP expr, Context ctx) { - var compiler = new Compiler(); - compiler.compileExpr(expr, ctx); - return compiler.cb.build(); - } - - private void compileExpr(SEXP expr, Context ctx) { - // TODO: check we do not attempt to compile BCSXP or PROMSXP - switch (expr) { - case LangSXP e -> compileCall(e, ctx); - case SpecialSymSXP e -> throw new CompilerException("unhandled special symbol: " + e); - case RegSymSXP e -> compileRegSym(e, ctx); - default -> compileConst(expr, ctx); + private static final Set MAYBE_NSE_SYMBOLS = Set.of("bquote"); + + private final Bc.Builder cb = new Bc.Builder(); + + public static Bc compileFun(CloSXP source) { + var body = source.body(); + + // FIXME: this is wrong -- need a special cmpFun to include the formals and others. + // EMPTY_ENV is also wrong + return compile(body, SEXPs.EMPTY_ENV); + } + + public static Bc compile(SEXP expr, EnvSXP env) { + // TODO: add local variables into the context environment? + return compile(expr, new Context(true, true, env)); + } + + private static Bc compile(SEXP expr, Context ctx) { + var compiler = new Compiler(); + compiler.compileExpr(expr, ctx); + return compiler.cb.build(); + } + + private void compileExpr(SEXP expr, Context ctx) { + // TODO: check we do not attempt to compile BCSXP or PROMSXP + switch (expr) { + case LangSXP e -> compileCall(e, ctx); + case SpecialSymSXP e -> throw new CompilerException("unhandled special symbol: " + e); + case RegSymSXP e -> compileRegSym(e, ctx); + default -> compileConst(expr, ctx); + } + } + + private void compileRegSym(RegSymSXP e, Context ctx) { + compileRegSym(e, ctx, false); + } + + private void compileRegSym(RegSymSXP e, Context ctx, boolean missingOk) { + if (e.isEllipsis()) { + // notifyWrongDotsUse + cb.addInstr(new BcInstr.DotsErr()); + } else if (e.isDdSym()) { + // if (!findLocVar("...", ctx)) + // notifyWrongDotsUse + var idx = cb.addConst(e); + cb.addInstr(missingOk ? new BcInstr.DdValMissOk(idx) : new BcInstr.DdVal(idx)); + checkTailCall(ctx); + } else { + // if (!findVar(sym, ctx)) + // notifyUndefVar + var idx = cb.addConst(e); + cb.addInstr(missingOk ? new BcInstr.GetVarMissOk(idx) : new BcInstr.GetVar(idx)); + checkTailCall(ctx); + } + } + + @SuppressFBWarnings( + value = "DLS_DEAD_LOCAL_STORE", + justification = "False positive, probably because of ignored switch case") + private void compileConst(SEXP expr, Context ctx) { + if (expr.type() == SEXPType.PROM || expr.type() == SEXPType.BCODE) { + throw new CompilerException("Unexpected type: " + expr.type()); + } + + switch (expr) { + case NilSXP ignored -> cb.addInstr(new BcInstr.LdNull()); + case LglSXP x when x == SEXPs.TRUE -> cb.addInstr(new BcInstr.LdTrue()); + case LglSXP x when x == SEXPs.FALSE -> cb.addInstr(new BcInstr.LdFalse()); + default -> cb.addInstr(new BcInstr.LdConst(cb.addConst(expr))); + } + + checkTailCall(ctx); + } + + void compileCall(LangSXP call, Context ctx) { + var args = call.args(); + switch (call.fun()) { + case RegSymSXP fun -> compileCallSymFun(fun, args, call, ctx); + case SpecialSymSXP fun -> + throw new IllegalStateException("Trying to call special symbol: " + fun); + case LangSXP fun -> compileCallExprFun(fun, args, call, ctx); + } + } + + private void compileCallSymFun(RegSymSXP fun, ListSXP args, LangSXP call, Context ctx) { + cb.addInstr(new BcInstr.GetFun(cb.addConst(fun))); + var nse = MAYBE_NSE_SYMBOLS.contains(fun.name()); + compileArgs(args, nse, ctx); + cb.addInstr(new BcInstr.Call(cb.addConst(call))); + checkTailCall(ctx); + } + + private void compileCallExprFun(LangSXP fun, ListSXP args, LangSXP call, Context ctx) { + var ncntxt = ctx.makeNonTailContext(); + compileExpr(fun, ncntxt); + cb.addInstr(new BcInstr.CheckFun()); + compileArgs(args, false, ctx); + cb.addInstr(new BcInstr.Call(cb.addConst(call))); + checkTailCall(ctx); + } + + @SuppressFBWarnings( + value = "DLS_DEAD_LOCAL_STORE", + justification = "False positive, probably because of ignored switch case") + private void compileArgs(ListSXP args, boolean nse, Context ctx) { + for (var arg : args) { + var tag = arg.tag(); + var val = arg.value(); + + switch (val) { + case SymSXP x when x.isMissing() -> { + cb.addInstr(new BcInstr.DoMissing()); + compileTag(tag); } - } - - private void compileRegSym(RegSymSXP e, Context ctx) { - compileRegSym(e, ctx, false); - } - - private void compileRegSym(RegSymSXP e, Context ctx, boolean missingOk) { - if (e.isEllipsis()) { - // notifyWrongDotsUse - cb.addInstr(new BcInstr.DotsErr()); - } else if (e.isDdSym()) { + case SymSXP x when x.isEllipsis() -> // if (!findLocVar("...", ctx)) // notifyWrongDotsUse - var idx = cb.addConst(e); - cb.addInstr(missingOk ? new BcInstr.DdValMissOk(idx) : new BcInstr.DdVal(idx)); - checkTailCall(ctx); - } else { - // if (!findVar(sym, ctx)) - // notifyUndefVar - var idx = cb.addConst(e); - cb.addInstr(missingOk ? new BcInstr.GetVarMissOk(idx) : new BcInstr.GetVar(idx)); - checkTailCall(ctx); - } - } - - @SuppressFBWarnings( - value = "DLS_DEAD_LOCAL_STORE", - justification = "False positive, probably because of ignored switch case" - ) - private void compileConst(SEXP expr, Context ctx) { - if (expr.type() == SEXPType.PROM || expr.type() == SEXPType.BCODE) { - throw new CompilerException("Unexpected type: " + expr.type()); - } - - switch (expr) { - case NilSXP ignored -> cb.addInstr(new BcInstr.LdNull()); - case LglSXP x when x == SEXPs.TRUE -> cb.addInstr(new BcInstr.LdTrue()); - case LglSXP x when x == SEXPs.FALSE -> cb.addInstr(new BcInstr.LdFalse()); - default -> cb.addInstr(new BcInstr.LdConst(cb.addConst(expr))); + cb.addInstr(new BcInstr.DoDots()); + case SymSXP x -> { + compileNormArg(x, nse, ctx); + compileTag(tag); } - - checkTailCall(ctx); - } - - void compileCall(LangSXP call, Context ctx) { - var args = call.args(); - switch (call.fun()) { - case RegSymSXP fun -> compileCallSymFun(fun, args, call, ctx); - case SpecialSymSXP fun -> throw new IllegalStateException("Trying to call special symbol: " + fun); - case LangSXP fun -> compileCallExprFun(fun, args, call, ctx); + case LangSXP x -> { + compileNormArg(x, nse, ctx); + compileTag(tag); } - } - - private void compileCallSymFun(RegSymSXP fun, ListSXP args, LangSXP call, Context ctx) { - cb.addInstr(new BcInstr.GetFun(cb.addConst(fun))); - var nse = MAYBE_NSE_SYMBOLS.contains(fun.name()); - compileArgs(args, nse, ctx); - cb.addInstr(new BcInstr.Call(cb.addConst(call))); - checkTailCall(ctx); - } - - private void compileCallExprFun(LangSXP fun, ListSXP args, LangSXP call, Context ctx) { - var ncntxt = ctx.makeNonTailContext(); - compileExpr(fun, ncntxt); - cb.addInstr(new BcInstr.CheckFun()); - compileArgs(args, false, ctx); - cb.addInstr(new BcInstr.Call(cb.addConst(call))); - checkTailCall(ctx); - } - - @SuppressFBWarnings( - value = "DLS_DEAD_LOCAL_STORE", - justification = "False positive, probably because of ignored switch case" - ) - private void compileArgs(ListSXP args, boolean nse, Context ctx) { - for (var arg : args) { - var tag = arg.tag(); - var val = arg.value(); - - switch (val) { - case SymSXP x when x.isMissing() -> { - cb.addInstr(new BcInstr.DoMissing()); - compileTag(tag); - } - case SymSXP x when x.isEllipsis() -> - // if (!findLocVar("...", ctx)) - // notifyWrongDotsUse - cb.addInstr(new BcInstr.DoDots()); - case SymSXP x -> { - compileNormArg(x, nse, ctx); - compileTag(tag); - } - case LangSXP x -> { - compileNormArg(x, nse, ctx); - compileTag(tag); - } - case BCodeSXP ignored -> throw new CompilerException("can't compile byte code literals in code"); - default -> { - compileConstArg(val); - compileTag(tag); - } - } + case BCodeSXP ignored -> + throw new CompilerException("can't compile byte code literals in code"); + default -> { + compileConstArg(val); + compileTag(tag); } - } - - private void compileNormArg(SEXP arg, boolean nse, Context ctx) { - cb.addInstr(new BcInstr.MakeProm(cb.addConst( - nse ? arg : SEXPs.bcode(compile(arg, makePromiseContext(ctx))) - ))); - } - - @SuppressFBWarnings( - value = "DLS_DEAD_LOCAL_STORE", - justification = "False positive, probably because of ignored switch case" - ) - private void compileConstArg(SEXP arg) { - switch (arg) { - case NilSXP ignored -> cb.addInstr(new BcInstr.PushNullArg()); - case LglSXP x when x == SEXPs.TRUE -> cb.addInstr(new BcInstr.PushTrueArg()); - case LglSXP x when x == SEXPs.FALSE -> cb.addInstr(new BcInstr.PushFalseArg()); - default -> cb.addInstr(new BcInstr.PushConstArg(cb.addConst(arg))); - } - } - - private Context makePromiseContext(Context ctx) { - /* - * cntxt$needRETURNJMP <- TRUE - * if (! is.null(cntxt$loop)) - * cntxt$loop$gotoOK <- FALSE - */ - // TODO? - return new Context(false, true, ctx.cenv()); - } - - private void compileTag(@Nullable String tag) { - if (tag != null && !tag.isEmpty()) { - cb.addInstr(new BcInstr.SetTag(cb.addConst(SEXPs.string(tag)))); - } - } - - private void checkTailCall(Context ctx) { - if (ctx.tailCall()) { - cb.addInstr(new BcInstr.Return()); - } - } + } + } + } + + private void compileNormArg(SEXP arg, boolean nse, Context ctx) { + cb.addInstr( + new BcInstr.MakeProm( + cb.addConst(nse ? arg : SEXPs.bcode(compile(arg, makePromiseContext(ctx)))))); + } + + @SuppressFBWarnings( + value = "DLS_DEAD_LOCAL_STORE", + justification = "False positive, probably because of ignored switch case") + private void compileConstArg(SEXP arg) { + switch (arg) { + case NilSXP ignored -> cb.addInstr(new BcInstr.PushNullArg()); + case LglSXP x when x == SEXPs.TRUE -> cb.addInstr(new BcInstr.PushTrueArg()); + case LglSXP x when x == SEXPs.FALSE -> cb.addInstr(new BcInstr.PushFalseArg()); + default -> cb.addInstr(new BcInstr.PushConstArg(cb.addConst(arg))); + } + } + + private Context makePromiseContext(Context ctx) { + /* + * cntxt$needRETURNJMP <- TRUE + * if (! is.null(cntxt$loop)) + * cntxt$loop$gotoOK <- FALSE + */ + // TODO? + return new Context(false, true, ctx.cenv()); + } + + private void compileTag(@Nullable String tag) { + if (tag != null && !tag.isEmpty()) { + cb.addInstr(new BcInstr.SetTag(cb.addConst(SEXPs.string(tag)))); + } + } + + private void checkTailCall(Context ctx) { + if (ctx.tailCall()) { + cb.addInstr(new BcInstr.Return()); + } + } } diff --git a/src/main/java/org/prlprg/compile/CompilerException.java b/src/main/java/org/prlprg/compile/CompilerException.java index ff4206687..32249b1b5 100644 --- a/src/main/java/org/prlprg/compile/CompilerException.java +++ b/src/main/java/org/prlprg/compile/CompilerException.java @@ -1,7 +1,7 @@ package org.prlprg.compile; public class CompilerException extends RuntimeException { - public CompilerException(String message) { - super(message); - } + public CompilerException(String message) { + super(message); + } } diff --git a/src/main/java/org/prlprg/compile/Context.java b/src/main/java/org/prlprg/compile/Context.java index 3396fb08e..7d0fa4e5e 100644 --- a/src/main/java/org/prlprg/compile/Context.java +++ b/src/main/java/org/prlprg/compile/Context.java @@ -3,11 +3,11 @@ import org.prlprg.sexp.EnvSXP; record Context(boolean topLevel, boolean tailCall, EnvSXP cenv) { - Context makeNonTailContext() { - return new Context(false, true, cenv); - } + Context makeNonTailContext() { + return new Context(false, true, cenv); + } - Context makePromisecContext() { - return new Context(false, true, cenv); - } + Context makePromisecContext() { + return new Context(false, true, cenv); + } } diff --git a/src/main/java/org/prlprg/compile/package-info.java b/src/main/java/org/prlprg/compile/package-info.java index 15bc500c7..1358783b4 100644 --- a/src/main/java/org/prlprg/compile/package-info.java +++ b/src/main/java/org/prlprg/compile/package-info.java @@ -3,7 +3,6 @@ @ReturnTypesAreNonNullByDefault package org.prlprg.compile; +import javax.annotation.ParametersAreNonnullByDefault; import org.prlprg.util.FieldsAreNonNullByDefault; import org.prlprg.util.ReturnTypesAreNonNullByDefault; - -import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/org/prlprg/package-info.java b/src/main/java/org/prlprg/package-info.java index 8c9f6a687..3da83667f 100644 --- a/src/main/java/org/prlprg/package-info.java +++ b/src/main/java/org/prlprg/package-info.java @@ -3,7 +3,6 @@ @ReturnTypesAreNonNullByDefault package org.prlprg; +import javax.annotation.ParametersAreNonnullByDefault; import org.prlprg.util.FieldsAreNonNullByDefault; import org.prlprg.util.ReturnTypesAreNonNullByDefault; - -import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/org/prlprg/primitive/Complex.java b/src/main/java/org/prlprg/primitive/Complex.java index 80eb0594e..cc821e7f7 100644 --- a/src/main/java/org/prlprg/primitive/Complex.java +++ b/src/main/java/org/prlprg/primitive/Complex.java @@ -2,8 +2,8 @@ /** Complex number */ public record Complex(double real, double imaginary) { - @Override - public String toString() { - return real + "+" + imaginary + "i"; - } + @Override + public String toString() { + return real + "+" + imaginary + "i"; + } } diff --git a/src/main/java/org/prlprg/primitive/Constants.java b/src/main/java/org/prlprg/primitive/Constants.java index 886ea6288..7a7a63cb3 100644 --- a/src/main/java/org/prlprg/primitive/Constants.java +++ b/src/main/java/org/prlprg/primitive/Constants.java @@ -3,22 +3,20 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @SuppressFBWarnings( - value = {"DM_STRING_CTOR", "ES_COMPARING_PARAMETER_STRING_WITH_EQ"}, - justification = "NA_STRING has to be a unique string, and we compare via direct equality" -) - + value = {"DM_STRING_CTOR", "ES_COMPARING_PARAMETER_STRING_WITH_EQ"}, + justification = "NA_STRING has to be a unique string, and we compare via direct equality") public final class Constants { - // according to Arith.h - public static final int NA_INT = Integer.MIN_VALUE; - public static final double NA_REAL = Double.NaN; - @SuppressWarnings("StringOperationCanBeSimplified") - public static final String NA_STRING = new String("!!!NA_STRING!!!"); + // according to Arith.h + public static final int NA_INT = Integer.MIN_VALUE; + public static final double NA_REAL = Double.NaN; + + @SuppressWarnings("StringOperationCanBeSimplified") + public static final String NA_STRING = new String("!!!NA_STRING!!!"); - @SuppressWarnings("StringEquality") - public static boolean isNaString(String s) { - return s == NA_STRING; - } + @SuppressWarnings("StringEquality") + public static boolean isNaString(String s) { + return s == NA_STRING; + } - private Constants() { - } + private Constants() {} } diff --git a/src/main/java/org/prlprg/primitive/Logical.java b/src/main/java/org/prlprg/primitive/Logical.java index 70cf44c38..9d9e878da 100644 --- a/src/main/java/org/prlprg/primitive/Logical.java +++ b/src/main/java/org/prlprg/primitive/Logical.java @@ -2,24 +2,24 @@ /** GNU-R "logical": a boolean which also permits NA (trinary). */ public enum Logical { - FALSE(0), - TRUE(1), - NA(Integer.MIN_VALUE); + FALSE(0), + TRUE(1), + NA(Integer.MIN_VALUE); - /** The value in GNU-R */ - final int i; + /** The value in GNU-R */ + final int i; - /** Convert from GNU-R representation. */ - public static Logical valueOf(int i) { - return switch (i) { - case 0 -> FALSE; - case 1 -> TRUE; - case Integer.MIN_VALUE -> NA; - default -> throw new IllegalArgumentException("Integer is not a GNU-R logical: " + i); - }; - } + /** Convert from GNU-R representation. */ + public static Logical valueOf(int i) { + return switch (i) { + case 0 -> FALSE; + case 1 -> TRUE; + case Integer.MIN_VALUE -> NA; + default -> throw new IllegalArgumentException("Integer is not a GNU-R logical: " + i); + }; + } - Logical(int i) { - this.i = i; - } + Logical(int i) { + this.i = i; + } } diff --git a/src/main/java/org/prlprg/primitive/Names.java b/src/main/java/org/prlprg/primitive/Names.java index 9b3d4773e..ed155b746 100644 --- a/src/main/java/org/prlprg/primitive/Names.java +++ b/src/main/java/org/prlprg/primitive/Names.java @@ -3,35 +3,12 @@ import com.google.common.collect.ImmutableList; public final class Names { - public static final ImmutableList BINOPS = ImmutableList.of( - "+", "-", "*", "/", "^", "%%", "%/%", - "==", "!=", "<", "<=", ">", ">=", - "&", "|", "&&", "||", - ":", "::", ":::", - "$", "@", - "~", - "%%%", - "%%", - "%*%", - "%o%", - "%x%", - "%in%", - "%*%", - "%/%", - "%+%", - "%-%", - "%&%", - "%/%", - "%|%", - "%?%", - "%$%", - "%::%", - "%:::%", - "%>%", - "%<>%", - "%<>%" - ); + public static final ImmutableList BINOPS = + ImmutableList.of( + "+", "-", "*", "/", "^", "%%", "%/%", "==", "!=", "<", "<=", ">", ">=", "&", "|", "&&", + "||", ":", "::", ":::", "$", "@", "~", "%%%", "%%", "%*%", "%o%", "%x%", "%in%", "%*%", + "%/%", "%+%", "%-%", "%&%", "%/%", "%|%", "%?%", "%$%", "%::%", "%:::%", "%>%", "%<>%", + "%<>%"); - private Names() { - } + private Names() {} } diff --git a/src/main/java/org/prlprg/primitive/package-info.java b/src/main/java/org/prlprg/primitive/package-info.java index d5737bdbb..8fd3c5c4b 100644 --- a/src/main/java/org/prlprg/primitive/package-info.java +++ b/src/main/java/org/prlprg/primitive/package-info.java @@ -3,7 +3,6 @@ @ReturnTypesAreNonNullByDefault package org.prlprg.primitive; +import javax.annotation.ParametersAreNonnullByDefault; import org.prlprg.util.FieldsAreNonNullByDefault; import org.prlprg.util.ReturnTypesAreNonNullByDefault; - -import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/org/prlprg/rds/Flags.java b/src/main/java/org/prlprg/rds/Flags.java index 8542295bd..c59707ce3 100644 --- a/src/main/java/org/prlprg/rds/Flags.java +++ b/src/main/java/org/prlprg/rds/Flags.java @@ -1,54 +1,76 @@ package org.prlprg.rds; final class Flags { - private static final int UTF8_MASK = 1 << 3; - private static final int ATTR_MASK = 1 << 9; - private static final int TAG_MASK = 1 << 10; - - private final int flags; - - public Flags(int flags) { - this.flags = flags; - try { - this.getType(); - } catch (IllegalArgumentException e) { - throw new IllegalArgumentException("Invalid flags", e); - } - } + private static final int UTF8_MASK = 1 << 3; + private static final int ATTR_MASK = 1 << 9; + private static final int TAG_MASK = 1 << 10; - public Flags(RDSItemType type, int levels, boolean isUTF8, boolean hasAttributes, boolean hasTag, int refIndex) { - this.flags = type.i() | (levels << 12) | (isUTF8 ? UTF8_MASK : 0) | (hasAttributes ? ATTR_MASK : 0) - | (hasTag ? TAG_MASK : 0) | (refIndex << 8); - } + private final int flags; - public RDSItemType getType() { - return RDSItemType.valueOf(flags & 255); + public Flags(int flags) { + this.flags = flags; + try { + this.getType(); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Invalid flags", e); } + } - public int decodeLevels() { - return flags >> 12; - } + public Flags( + RDSItemType type, + int levels, + boolean isUTF8, + boolean hasAttributes, + boolean hasTag, + int refIndex) { + this.flags = + type.i() + | (levels << 12) + | (isUTF8 ? UTF8_MASK : 0) + | (hasAttributes ? ATTR_MASK : 0) + | (hasTag ? TAG_MASK : 0) + | (refIndex << 8); + } - public boolean isUTF8() { - return (decodeLevels() & UTF8_MASK) != 0; - } + public RDSItemType getType() { + return RDSItemType.valueOf(flags & 255); + } - public boolean hasAttributes() { - return (flags & ATTR_MASK) != 0; - } + public int decodeLevels() { + return flags >> 12; + } - public boolean hasTag() { - return (flags & TAG_MASK) != 0; - } + public boolean isUTF8() { + return (decodeLevels() & UTF8_MASK) != 0; + } - public int unpackRefIndex() { - return flags >> 8; - } + public boolean hasAttributes() { + return (flags & ATTR_MASK) != 0; + } - @Override - public String toString() { - return "Flags{" + "type=" + getType() + ", levels=" + decodeLevels() + ", isUTF8=" + isUTF8() - + ", hasAttributes=" + hasAttributes() + ", hasTag=" + hasTag() + ", refIndex=" + unpackRefIndex() - + '}'; - } + public boolean hasTag() { + return (flags & TAG_MASK) != 0; + } + + public int unpackRefIndex() { + return flags >> 8; + } + + @Override + public String toString() { + return "Flags{" + + "type=" + + getType() + + ", levels=" + + decodeLevels() + + ", isUTF8=" + + isUTF8() + + ", hasAttributes=" + + hasAttributes() + + ", hasTag=" + + hasTag() + + ", refIndex=" + + unpackRefIndex() + + '}'; + } } diff --git a/src/main/java/org/prlprg/rds/RDSException.java b/src/main/java/org/prlprg/rds/RDSException.java index a4677ddc1..ce5b29ae8 100644 --- a/src/main/java/org/prlprg/rds/RDSException.java +++ b/src/main/java/org/prlprg/rds/RDSException.java @@ -1,11 +1,11 @@ package org.prlprg.rds; public class RDSException extends RuntimeException { - public RDSException(String message) { - super(message); - } + public RDSException(String message) { + super(message); + } - public RDSException(String message, Throwable cause) { - super(message, cause); - } + public RDSException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/src/main/java/org/prlprg/rds/RDSInputStream.java b/src/main/java/org/prlprg/rds/RDSInputStream.java index d9b8bdc18..0386c84a0 100644 --- a/src/main/java/org/prlprg/rds/RDSInputStream.java +++ b/src/main/java/org/prlprg/rds/RDSInputStream.java @@ -7,54 +7,54 @@ import java.nio.charset.Charset; class RDSInputStream implements Closeable { - private final DataInputStream in; - - RDSInputStream(InputStream in) { - this.in = new DataInputStream(in); - } - - @Override - public void close() throws IOException { - in.close(); - } - - public boolean isAtEnd() throws IOException { - return in.available() == 0; - } - - public byte readByte() throws IOException { - return in.readByte(); - } - - public int readInt() throws IOException { - return in.readInt(); - } - - public double readDouble() throws IOException { - return in.readDouble(); - } - - public String readString(int natEncSize, Charset charset) throws IOException { - var buf = new byte[natEncSize]; - in.readFully(buf, 0, natEncSize); - return new String(buf, charset); - } - - public int[] readInts(int length) throws IOException { - int[] ints = new int[length]; - for (int i = 0; i < length; i++) { - var n = readInt(); - ints[i] = n; - } - return ints; - } - - public double[] readDoubles(int length) throws IOException { - double[] ints = new double[length]; - for (int i = 0; i < length; i++) { - var n = readDouble(); - ints[i] = n; - } - return ints; - } + private final DataInputStream in; + + RDSInputStream(InputStream in) { + this.in = new DataInputStream(in); + } + + @Override + public void close() throws IOException { + in.close(); + } + + public boolean isAtEnd() throws IOException { + return in.available() == 0; + } + + public byte readByte() throws IOException { + return in.readByte(); + } + + public int readInt() throws IOException { + return in.readInt(); + } + + public double readDouble() throws IOException { + return in.readDouble(); + } + + public String readString(int natEncSize, Charset charset) throws IOException { + var buf = new byte[natEncSize]; + in.readFully(buf, 0, natEncSize); + return new String(buf, charset); + } + + public int[] readInts(int length) throws IOException { + int[] ints = new int[length]; + for (int i = 0; i < length; i++) { + var n = readInt(); + ints[i] = n; + } + return ints; + } + + public double[] readDoubles(int length) throws IOException { + double[] ints = new double[length]; + for (int i = 0; i < length; i++) { + var n = readDouble(); + ints[i] = n; + } + return ints; + } } diff --git a/src/main/java/org/prlprg/rds/RDSItemType.java b/src/main/java/org/prlprg/rds/RDSItemType.java index 140dd2037..1e2fa842e 100644 --- a/src/main/java/org/prlprg/rds/RDSItemType.java +++ b/src/main/java/org/prlprg/rds/RDSItemType.java @@ -3,77 +3,78 @@ import org.prlprg.sexp.SEXPType; sealed interface RDSItemType { - int i(); + int i(); - default boolean isSexp(SEXPType type) { - return (this instanceof Sexp s && s.sexp() == type) || (this == Special.NILVALUE_SXP && type == SEXPType.NIL); - } - - static RDSItemType valueOf(int i) { - // These will never change so we don't have to worry about code synchronization - return switch (i) { - case 255 -> Special.REFSXP; - case 254 -> Special.NILVALUE_SXP; - case 253 -> Special.GLOBALENV_SXP; - case 252 -> Special.UNBOUNDVALUE_SXP; - case 251 -> Special.MISSINGARG_SXP; - case 250 -> Special.BASENAMESPACE_SXP; - case 249 -> Special.NAMESPACESXP; - case 248 -> Special.PACKAGESXP; - case 247 -> Special.PERSISTSXP; - case 246 -> Special.CLASSREFSXP; - case 245 -> Special.GENERICREFSXP; - case 244 -> Special.BCREPDEF; - case 243 -> Special.BCREPREF; - case 242 -> Special.EMPTYENV_SXP; - case 241 -> Special.BASEENV_SXP; - case 240 -> Special.ATTRLANGSXP; - case 239 -> Special.ATTRLISTSXP; - default -> { - try { - yield new Sexp(SEXPType.valueOf(i)); - } catch (IllegalArgumentException e) { - throw new IllegalArgumentException("Unknown RDS item type: " + i); - } - } - }; - } + default boolean isSexp(SEXPType type) { + return (this instanceof Sexp s && s.sexp() == type) + || (this == Special.NILVALUE_SXP && type == SEXPType.NIL); + } - record Sexp(SEXPType sexp) implements RDSItemType { - @Override - public int i() { - return sexp.i; + static RDSItemType valueOf(int i) { + // These will never change so we don't have to worry about code synchronization + return switch (i) { + case 255 -> Special.REFSXP; + case 254 -> Special.NILVALUE_SXP; + case 253 -> Special.GLOBALENV_SXP; + case 252 -> Special.UNBOUNDVALUE_SXP; + case 251 -> Special.MISSINGARG_SXP; + case 250 -> Special.BASENAMESPACE_SXP; + case 249 -> Special.NAMESPACESXP; + case 248 -> Special.PACKAGESXP; + case 247 -> Special.PERSISTSXP; + case 246 -> Special.CLASSREFSXP; + case 245 -> Special.GENERICREFSXP; + case 244 -> Special.BCREPDEF; + case 243 -> Special.BCREPREF; + case 242 -> Special.EMPTYENV_SXP; + case 241 -> Special.BASEENV_SXP; + case 240 -> Special.ATTRLANGSXP; + case 239 -> Special.ATTRLISTSXP; + default -> { + try { + yield new Sexp(SEXPType.valueOf(i)); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Unknown RDS item type: " + i); } + } + }; + } + + record Sexp(SEXPType sexp) implements RDSItemType { + @Override + public int i() { + return sexp.i; } + } - enum Special implements RDSItemType { - REFSXP(255), - NILVALUE_SXP(254), - GLOBALENV_SXP(253), - UNBOUNDVALUE_SXP(252), - MISSINGARG_SXP(251), - BASENAMESPACE_SXP(250), - NAMESPACESXP(249), - PACKAGESXP(248), - PERSISTSXP(247), - CLASSREFSXP(246), - GENERICREFSXP(245), - BCREPDEF(244), - BCREPREF(243), - EMPTYENV_SXP(242), - BASEENV_SXP(241), - ATTRLANGSXP(240), - ATTRLISTSXP(239); + enum Special implements RDSItemType { + REFSXP(255), + NILVALUE_SXP(254), + GLOBALENV_SXP(253), + UNBOUNDVALUE_SXP(252), + MISSINGARG_SXP(251), + BASENAMESPACE_SXP(250), + NAMESPACESXP(249), + PACKAGESXP(248), + PERSISTSXP(247), + CLASSREFSXP(246), + GENERICREFSXP(245), + BCREPDEF(244), + BCREPREF(243), + EMPTYENV_SXP(242), + BASEENV_SXP(241), + ATTRLANGSXP(240), + ATTRLISTSXP(239); - private final int i; + private final int i; - Special(int i) { - this.i = i; - } + Special(int i) { + this.i = i; + } - @Override - public int i() { - return i; - } + @Override + public int i() { + return i; } + } } diff --git a/src/main/java/org/prlprg/rds/RDSReader.java b/src/main/java/org/prlprg/rds/RDSReader.java index ae1d69f63..9ba05386d 100644 --- a/src/main/java/org/prlprg/rds/RDSReader.java +++ b/src/main/java/org/prlprg/rds/RDSReader.java @@ -1,14 +1,6 @@ package org.prlprg.rds; import com.google.common.collect.ImmutableList; -import org.prlprg.bc.Bc; -import org.prlprg.bc.BcFromRawException; -import org.prlprg.primitive.Constants; -import org.prlprg.primitive.Logical; -import org.prlprg.sexp.*; -import org.prlprg.util.NotImplementedException; - -import javax.annotation.Nullable; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; @@ -17,442 +9,467 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import javax.annotation.Nullable; +import org.prlprg.bc.Bc; +import org.prlprg.bc.BcFromRawException; +import org.prlprg.primitive.Constants; +import org.prlprg.primitive.Logical; +import org.prlprg.sexp.*; +import org.prlprg.util.NotImplementedException; public class RDSReader implements Closeable { - private final RDSInputStream in; - private final List refTable = new ArrayList<>(128); + private final RDSInputStream in; + private final List refTable = new ArrayList<>(128); - // FIXME: this should include the logic from platform.c - private Charset nativeEncoding = Charset.defaultCharset(); + // FIXME: this should include the logic from platform.c + private Charset nativeEncoding = Charset.defaultCharset(); - private RDSReader(InputStream in) { - this.in = new RDSInputStream(in); - } + private RDSReader(InputStream in) { + this.in = new RDSInputStream(in); + } - public static SEXP readStream(InputStream input) throws IOException { - try (var reader = new RDSReader(input)) { - return reader.read(); - } + public static SEXP readStream(InputStream input) throws IOException { + try (var reader = new RDSReader(input)) { + return reader.read(); } + } - private void readHeader() throws IOException { - var type = in.readByte(); - if (type != 'X') { - throw new RDSException("Unsupported type (possibly compressed)"); - } - var nl = in.readByte(); - assert nl == '\n'; - - // versions - var formatVersion = in.readInt(); - // writer version - in.readInt(); - // minimal reader version - in.readInt(); - - // native encoding for version == 3 - switch (formatVersion) { - case 2: - break; - case 3: - var natEncSize = in.readInt(); - nativeEncoding = Charset.forName(in.readString(natEncSize, StandardCharsets.US_ASCII)); - break; - default: - throw new RDSException("Unsupported version: " + formatVersion); - } + private void readHeader() throws IOException { + var type = in.readByte(); + if (type != 'X') { + throw new RDSException("Unsupported type (possibly compressed)"); } - - public SEXP read() throws IOException { - readHeader(); - var sexp = readItem(); - if (!in.isAtEnd()) { - throw new RDSException("Expected end of file"); - } - return sexp; + var nl = in.readByte(); + assert nl == '\n'; + + // versions + var formatVersion = in.readInt(); + // writer version + in.readInt(); + // minimal reader version + in.readInt(); + + // native encoding for version == 3 + switch (formatVersion) { + case 2: + break; + case 3: + var natEncSize = in.readInt(); + nativeEncoding = Charset.forName(in.readString(natEncSize, StandardCharsets.US_ASCII)); + break; + default: + throw new RDSException("Unsupported version: " + formatVersion); } + } - private SEXP readItem() throws IOException { - var flags = readFlags(); - - return switch (flags.getType()) { - case RDSItemType.Sexp s -> switch (s.sexp()) { - case NIL -> SEXPs.NULL; - case SYM -> readSymbol(); - case CLO -> readClosure(flags); - case LIST -> readList(flags); - case INT -> readInts(flags); - case REAL -> readReals(flags); - case LGL -> readLogicals(flags); - case VEC -> readVec(flags); - case ENV -> readEnv(); - case STR -> readStrs(flags); - case LANG -> readLang(flags); - case BCODE -> readByteCode(); - case EXPR -> readExpr(); - default -> throw new RDSException("Unsupported SEXP type: " + s.sexp()); - }; - case RDSItemType.Special s -> switch (s) { - case NILVALUE_SXP -> SEXPs.NULL; - case MISSINGARG_SXP -> SEXPs.MISSING_ARG; - case GLOBALENV_SXP -> SEXPs.GLOBAL_ENV; - case BASEENV_SXP -> SEXPs.BASE_ENV; - case EMPTYENV_SXP -> SEXPs.EMPTY_ENV; - case REFSXP -> readRef(flags); - case BCREPDEF, BCREPREF -> throw new RDSException("Unexpected bytecode reference here (not in bytecode)"); - case ATTRLANGSXP, ATTRLISTSXP -> throw new RDSException("Unexpected attr here"); - case UNBOUNDVALUE_SXP, GENERICREFSXP, BASENAMESPACE_SXP, NAMESPACESXP, PACKAGESXP, PERSISTSXP, CLASSREFSXP -> throw new NotImplementedException(); - }; - }; + public SEXP read() throws IOException { + readHeader(); + var sexp = readItem(); + if (!in.isAtEnd()) { + throw new RDSException("Expected end of file"); } - - private ExprSXP readExpr() throws IOException { - // ??? Should flags be used somewhere here? At least for sanity assertions? - var length = in.readInt(); - var sexps = new ArrayList(length); - for (int i = 0; i < length; i++) { - sexps.add(readItem()); - } - return SEXPs.expr(sexps); + return sexp; + } + + private SEXP readItem() throws IOException { + var flags = readFlags(); + + return switch (flags.getType()) { + case RDSItemType.Sexp s -> + switch (s.sexp()) { + case NIL -> SEXPs.NULL; + case SYM -> readSymbol(); + case CLO -> readClosure(flags); + case LIST -> readList(flags); + case INT -> readInts(flags); + case REAL -> readReals(flags); + case LGL -> readLogicals(flags); + case VEC -> readVec(flags); + case ENV -> readEnv(); + case STR -> readStrs(flags); + case LANG -> readLang(flags); + case BCODE -> readByteCode(); + case EXPR -> readExpr(); + default -> throw new RDSException("Unsupported SEXP type: " + s.sexp()); + }; + case RDSItemType.Special s -> + switch (s) { + case NILVALUE_SXP -> SEXPs.NULL; + case MISSINGARG_SXP -> SEXPs.MISSING_ARG; + case GLOBALENV_SXP -> SEXPs.GLOBAL_ENV; + case BASEENV_SXP -> SEXPs.BASE_ENV; + case EMPTYENV_SXP -> SEXPs.EMPTY_ENV; + case REFSXP -> readRef(flags); + case BCREPDEF, BCREPREF -> + throw new RDSException("Unexpected bytecode reference here (not in bytecode)"); + case ATTRLANGSXP, ATTRLISTSXP -> throw new RDSException("Unexpected attr here"); + case UNBOUNDVALUE_SXP, + GENERICREFSXP, + BASENAMESPACE_SXP, + NAMESPACESXP, + PACKAGESXP, + PERSISTSXP, + CLASSREFSXP -> + throw new NotImplementedException(); + }; + }; + } + + private ExprSXP readExpr() throws IOException { + // ??? Should flags be used somewhere here? At least for sanity assertions? + var length = in.readInt(); + var sexps = new ArrayList(length); + for (int i = 0; i < length; i++) { + sexps.add(readItem()); } + return SEXPs.expr(sexps); + } + + private BCodeSXP readByteCode() throws IOException { + var length = in.readInt(); + var reps = new SEXP[length]; + return readByteCode1(reps); + } + + private BCodeSXP readByteCode1(SEXP[] reps) throws IOException { + @SuppressWarnings("SwitchStatementWithTooFewBranches") + var code = + switch (readItem()) { + case IntSXP s -> s; + default -> throw new RDSException("Expected IntSXP"); + }; - private BCodeSXP readByteCode() throws IOException { - var length = in.readInt(); - var reps = new SEXP[length]; - return readByteCode1(reps); + if (code.get(0) != Bc.R_BC_VERSION) { + throw new RDSException("Unsupported byte code version: " + code.get(0)); } - private BCodeSXP readByteCode1(SEXP[] reps) throws IOException { - @SuppressWarnings("SwitchStatementWithTooFewBranches") var code = switch (readItem()) { - case IntSXP s -> s; - default -> throw new RDSException("Expected IntSXP"); - }; - - if (code.get(0) != Bc.R_BC_VERSION) { - throw new RDSException("Unsupported byte code version: " + code.get(0)); + var bytecode = code.subArray(1, code.size()); + var consts = readByteCodeConsts(reps); + try { + return SEXPs.bcode(Bc.fromRaw(bytecode, consts)); + } catch (BcFromRawException e) { + throw new RDSException("Error reading bytecode", e); + } + } + + private List readByteCodeConsts(SEXP[] reps) throws IOException { + var length = in.readInt(); + var consts = new ArrayList(length); + for (int i = 0; i < length; i++) { + var type = RDSItemType.valueOf(in.readInt()); + switch (type) { + case RDSItemType.Sexp s -> { + switch (s.sexp()) { + case BCODE -> consts.add(readByteCode1(reps)); + case LANG, LIST -> consts.add(readByteCodeLang(type, reps)); + default -> consts.add(readItem()); + } } - - var bytecode = code.subArray(1, code.size()); - var consts = readByteCodeConsts(reps); - try { - return SEXPs.bcode(Bc.fromRaw(bytecode, consts)); - } catch (BcFromRawException e) { - throw new RDSException("Error reading bytecode", e); + case RDSItemType.Special s -> { + switch (s) { + case ATTRLISTSXP, ATTRLANGSXP, BCREPREF, BCREPDEF -> + consts.add(readByteCodeLang(type, reps)); + default -> consts.add(readItem()); + } } + } } - - private List readByteCodeConsts(SEXP[] reps) throws IOException { - var length = in.readInt(); - var consts = new ArrayList(length); - for (int i = 0; i < length; i++) { - var type = RDSItemType.valueOf(in.readInt()); - switch (type) { - case RDSItemType.Sexp s -> { - switch (s.sexp()) { - case BCODE -> consts.add(readByteCode1(reps)); - case LANG, LIST -> consts.add(readByteCodeLang(type, reps)); - default -> consts.add(readItem()); - } - } - case RDSItemType.Special s -> { - switch (s) { - case ATTRLISTSXP, ATTRLANGSXP, BCREPREF, BCREPDEF -> consts.add(readByteCodeLang(type, reps)); - default -> consts.add(readItem()); - } - } - } - } - return consts; + return consts; + } + + private SEXP readByteCodeLang(RDSItemType type, SEXP[] reps) throws IOException { + return switch (type) { + case RDSItemType.Sexp s -> + switch (s.sexp()) { + case LANG, LIST -> readByteCodeLang1(type, reps); + default -> readItem(); + }; + case RDSItemType.Special s -> + switch (s) { + case BCREPREF -> reps[in.readInt()]; + case BCREPDEF, ATTRLISTSXP, ATTRLANGSXP -> readByteCodeLang1(type, reps); + default -> readItem(); + }; + }; + } + + private SEXP readByteCodeLang1(RDSItemType type, SEXP[] reps) throws IOException { + var pos = -1; + if (type == RDSItemType.Special.BCREPDEF) { + pos = in.readInt(); + type = RDSItemType.valueOf(in.readInt()); + } + var attributes = + (type == RDSItemType.Special.ATTRLANGSXP || type == RDSItemType.Special.ATTRLISTSXP) + ? readAttributes() + : Attributes.NONE; + + SEXP tagSexp; + SEXP ans; + + if (type.isSexp(SEXPType.LANG) || type == RDSItemType.Special.ATTRLANGSXP) { + tagSexp = readItem(); + if (tagSexp != SEXPs.NULL) { + throw new RDSException("Expected NULL tag"); + } + + var fun = readByteCodeLang(RDSItemType.valueOf(in.readInt()), reps); + if (!(fun instanceof SymOrLangSXP funSymOrLang)) { + throw new RDSException("Expected symbol or language, got: " + fun.type()); + } + + var args = readByteCodeLang(RDSItemType.valueOf(in.readInt()), reps); + if (!(args instanceof ListSXP argsList)) { + throw new RDSException("Expected list, got: " + args.type()); + } + + ans = SEXPs.lang(funSymOrLang, argsList, attributes); + } else if (type.isSexp(SEXPType.LIST) || type == RDSItemType.Special.ATTRLISTSXP) { + tagSexp = readItem(); + String tag; + + if (tagSexp instanceof RegSymSXP sym) { + tag = sym.name(); + } else if (tagSexp instanceof NilSXP) { + tag = null; + } else { + throw new RDSException("Expected regular symbol or nil"); + } + + var head = readByteCodeLang(RDSItemType.valueOf(in.readInt()), reps); + var tail = readByteCodeLang(RDSItemType.valueOf(in.readInt()), reps); + + var data = new ImmutableList.Builder(); + data.add(new TaggedElem(tag, head)); + + if (!(tail instanceof ListSXP tailList)) { + throw new RDSException("Expected list, got: " + tail.type()); + } + ListSXP.flatten(tailList, data); + + ans = SEXPs.list(data.build(), attributes); + } else { + throw new RDSException("Unexpected bclang type: " + type); } - private SEXP readByteCodeLang(RDSItemType type, SEXP[] reps) throws IOException { - return switch (type) { - case RDSItemType.Sexp s -> switch (s.sexp()) { - case LANG, LIST -> readByteCodeLang1(type, reps); - default -> readItem(); - }; - case RDSItemType.Special s -> switch (s) { - case BCREPREF -> reps[in.readInt()]; - case BCREPDEF, ATTRLISTSXP, ATTRLANGSXP -> readByteCodeLang1(type, reps); - default -> readItem(); - }; - }; + if (pos >= 0) { + reps[pos] = ans; } - private SEXP readByteCodeLang1(RDSItemType type, SEXP[] reps) throws IOException { - var pos = -1; - if (type == RDSItemType.Special.BCREPDEF) { - pos = in.readInt(); - type = RDSItemType.valueOf(in.readInt()); - } - var attributes = - (type == RDSItemType.Special.ATTRLANGSXP || type == RDSItemType.Special.ATTRLISTSXP) ? - readAttributes() : Attributes.NONE; - - SEXP tagSexp; - SEXP ans; - - if (type.isSexp(SEXPType.LANG) || type == RDSItemType.Special.ATTRLANGSXP) { - tagSexp = readItem(); - if (tagSexp != SEXPs.NULL) { - throw new RDSException("Expected NULL tag"); - } - - var fun = readByteCodeLang(RDSItemType.valueOf(in.readInt()), reps); - if (!(fun instanceof SymOrLangSXP funSymOrLang)) { - throw new RDSException("Expected symbol or language, got: " + fun.type()); - } - - var args = readByteCodeLang(RDSItemType.valueOf(in.readInt()), reps); - if (!(args instanceof ListSXP argsList)) { - throw new RDSException("Expected list, got: " + args.type()); - } - - ans = SEXPs.lang(funSymOrLang, argsList, attributes); - } else if (type.isSexp(SEXPType.LIST) || type == RDSItemType.Special.ATTRLISTSXP) { - tagSexp = readItem(); - String tag; - - if (tagSexp instanceof RegSymSXP sym) { - tag = sym.name(); - } else if (tagSexp instanceof NilSXP) { - tag = null; - } else { - throw new RDSException("Expected regular symbol or nil"); - } - - var head = readByteCodeLang(RDSItemType.valueOf(in.readInt()), reps); - var tail = readByteCodeLang(RDSItemType.valueOf(in.readInt()), reps); - - var data = new ImmutableList.Builder(); - data.add(new TaggedElem(tag, head)); - - if (!(tail instanceof ListSXP tailList)) { - throw new RDSException("Expected list, got: " + tail.type()); - } - ListSXP.flatten(tailList, data); - - ans = SEXPs.list(data.build(), attributes); - } else { - throw new RDSException("Unexpected bclang type: " + type); - } + return ans; + } - if (pos >= 0) { - reps[pos] = ans; - } + private SEXP readRef(Flags flags) throws IOException { + var index = flags.unpackRefIndex(); + if (index == 0) { + index = in.readInt(); + } + return refTable.get(index - 1); + } - return ans; + private LangSXP readLang(Flags flags) throws IOException { + var attributes = readAttributes(flags); + // FIXME: not sure what it is good for + readTag(flags); + if (!(readItem() instanceof SymOrLangSXP fun)) { + throw new RDSException("Expected symbol or language"); } - - private SEXP readRef(Flags flags) throws IOException { - var index = flags.unpackRefIndex(); - if (index == 0) { - index = in.readInt(); - } - return refTable.get(index - 1); + if (!(readItem() instanceof ListSXP args)) { + throw new RDSException("Expected list"); } + return SEXPs.lang(fun, args, attributes); + } - private LangSXP readLang(Flags flags) throws IOException { - var attributes = readAttributes(flags); - // FIXME: not sure what it is good for - readTag(flags); - - if (!(readItem() instanceof SymOrLangSXP fun)) { - throw new RDSException("Expected symbol or language"); - } - if (!(readItem() instanceof ListSXP args)) { - throw new RDSException("Expected list"); - } - return SEXPs.lang(fun, args, attributes); + private String readChars() throws IOException { + var flags = readFlags(); + if (!flags.getType().isSexp(SEXPType.CHAR)) { + throw new RDSException("Expected CHAR"); } - - private String readChars() throws IOException { - var flags = readFlags(); - if (!flags.getType().isSexp(SEXPType.CHAR)) { - throw new RDSException("Expected CHAR"); - } - var length = in.readInt(); - if (length == -1) { - return Constants.NA_STRING; - } else { - return in.readString(length, nativeEncoding); - } + var length = in.readInt(); + if (length == -1) { + return Constants.NA_STRING; + } else { + return in.readString(length, nativeEncoding); } + } - private StrSXP readStrs(Flags flags) throws IOException { - var length = in.readInt(); - var strings = ImmutableList.builderWithExpectedSize(length); + private StrSXP readStrs(Flags flags) throws IOException { + var length = in.readInt(); + var strings = ImmutableList.builderWithExpectedSize(length); - for (int i = 0; i < length; i++) { - strings.add(readChars()); - } - - var attributes = readAttributes(flags); - return SEXPs.string(strings.build(), attributes); + for (int i = 0; i < length; i++) { + strings.add(readChars()); } - private LocalEnvSXP readEnv() throws IOException { - // ??? Should flags be used somewhere here? At least for sanity assertions? - var item = RegEnvSXP.uninitialized(); - refTable.add(item); + var attributes = readAttributes(flags); + return SEXPs.string(strings.build(), attributes); + } - var locked = in.readInt(); - if (locked != 0 && locked != 1) { - throw new RDSException("Expected 0 or 1 (LOCKED)"); - } - if (!(readItem() instanceof EnvSXP enclos)) { - throw new RDSException("Expected environment (ENCLOS)"); - } - if (!(readItem() instanceof ListSXP frame)) { - throw new RDSException("Expected list (FRAME)"); - } - // We read the hash table, - // but immediately discard it because we represent environments differently and it's redundant. - readItem(); - var attributes = readAttributes(); + private LocalEnvSXP readEnv() throws IOException { + // ??? Should flags be used somewhere here? At least for sanity assertions? + var item = RegEnvSXP.uninitialized(); + refTable.add(item); - item.init(locked != 0, enclos, frame, attributes); - return item; + var locked = in.readInt(); + if (locked != 0 && locked != 1) { + throw new RDSException("Expected 0 or 1 (LOCKED)"); } - - private VecSXP readVec(Flags flags) throws IOException { - var length = in.readInt(); - var data = ImmutableList.builderWithExpectedSize(length); - for (int i = 0; i < length; i++) { - data.add(readItem()); - } - var attributes = readAttributes(flags); - return SEXPs.vec(data.build(), attributes); - } - - private LglSXP readLogicals(Flags flags) throws IOException { - var length = in.readInt(); - var data = in.readInts(length); - var attributes = readAttributes(flags); - return SEXPs.logical(Arrays.stream(data).mapToObj(Logical::valueOf).collect(ImmutableList.toImmutableList()), attributes); + if (!(readItem() instanceof EnvSXP enclos)) { + throw new RDSException("Expected environment (ENCLOS)"); } - - private RealSXP readReals(Flags flags) throws IOException { - var length = in.readInt(); - var data = in.readDoubles(length); - var attributes = readAttributes(flags); - return SEXPs.real(data, attributes); + if (!(readItem() instanceof ListSXP frame)) { + throw new RDSException("Expected list (FRAME)"); } - - private IntSXP readInts(Flags flags) throws IOException { - var length = in.readInt(); - var data = in.readInts(length); - var attributes = readAttributes(flags); - return SEXPs.integer(data, attributes); + // We read the hash table, + // but immediately discard it because we represent environments differently and it's + // redundant. + readItem(); + var attributes = readAttributes(); + + item.init(locked != 0, enclos, frame, attributes); + return item; + } + + private VecSXP readVec(Flags flags) throws IOException { + var length = in.readInt(); + var data = ImmutableList.builderWithExpectedSize(length); + for (int i = 0; i < length; i++) { + data.add(readItem()); } - - private ListSXP readList(Flags flags) throws IOException { - var data = ImmutableList.builder(); - Attributes attributes = null; - - while (!flags.getType().isSexp(SEXPType.NIL)) { - if (!flags.getType().isSexp(SEXPType.LIST)) { - throw new RDSException("Expected list (reading in the middle or at the end of a list)"); - } - if (attributes == null) { - attributes = readAttributes(flags); - } else { - var discard = readAttributes(flags); - if (!discard.isEmpty()) { - throw new RDSException("Unexpected attributes in the middle of a list"); - } - } - - var tag = readTag(flags); - var item = readItem(); - data.add(new TaggedElem(tag, item)); - - flags = readFlags(); + var attributes = readAttributes(flags); + return SEXPs.vec(data.build(), attributes); + } + + private LglSXP readLogicals(Flags flags) throws IOException { + var length = in.readInt(); + var data = in.readInts(length); + var attributes = readAttributes(flags); + return SEXPs.logical( + Arrays.stream(data).mapToObj(Logical::valueOf).collect(ImmutableList.toImmutableList()), + attributes); + } + + private RealSXP readReals(Flags flags) throws IOException { + var length = in.readInt(); + var data = in.readDoubles(length); + var attributes = readAttributes(flags); + return SEXPs.real(data, attributes); + } + + private IntSXP readInts(Flags flags) throws IOException { + var length = in.readInt(); + var data = in.readInts(length); + var attributes = readAttributes(flags); + return SEXPs.integer(data, attributes); + } + + private ListSXP readList(Flags flags) throws IOException { + var data = ImmutableList.builder(); + Attributes attributes = null; + + while (!flags.getType().isSexp(SEXPType.NIL)) { + if (!flags.getType().isSexp(SEXPType.LIST)) { + throw new RDSException("Expected list (reading in the middle or at the end of a list)"); + } + if (attributes == null) { + attributes = readAttributes(flags); + } else { + var discard = readAttributes(flags); + if (!discard.isEmpty()) { + throw new RDSException("Unexpected attributes in the middle of a list"); } + } - return SEXPs.list(data.build()); + var tag = readTag(flags); + var item = readItem(); + data.add(new TaggedElem(tag, item)); + + flags = readFlags(); } - private CloSXP readClosure(Flags flags) throws IOException { - var attributes = readAttributes(flags); - if (!(readItem() instanceof EnvSXP env)) { - throw new RDSException("Expected CLOENV to be environment"); - } - if (!(readItem() instanceof ListSXP formals)) { - throw new RDSException("Expected closure FORMALS to be list"); - } - var body = readItem(); + return SEXPs.list(data.build()); + } - return SEXPs.closure(formals, body, env, attributes); + private CloSXP readClosure(Flags flags) throws IOException { + var attributes = readAttributes(flags); + if (!(readItem() instanceof EnvSXP env)) { + throw new RDSException("Expected CLOENV to be environment"); } - - private @Nullable String readTag(Flags flags) throws IOException { - if (flags.hasTag()) { - if (readItem() instanceof RegSymSXP s) { - return s.name(); - } else { - throw new RDSException("Expected tag to be a symbol"); - } - } else { - return null; - } + if (!(readItem() instanceof ListSXP formals)) { + throw new RDSException("Expected closure FORMALS to be list"); } - - private Attributes readAttributes(Flags flags) throws IOException { - if (flags.hasAttributes()) { - return readAttributes(); - } else { - return Attributes.NONE; - } + var body = readItem(); + + return SEXPs.closure(formals, body, env, attributes); + } + + private @Nullable String readTag(Flags flags) throws IOException { + if (flags.hasTag()) { + if (readItem() instanceof RegSymSXP s) { + return s.name(); + } else { + throw new RDSException("Expected tag to be a symbol"); + } + } else { + return null; } + } - private Attributes readAttributes() throws IOException { - if (readItem() instanceof ListSXP xs) { - var attrs = new Attributes.Builder(); - - for (var x : xs) { - var attr = x.tag(); - if (attr == null) { - throw new RDSException("Expected tag"); - } - attrs.put(attr, x.value()); - } - - return attrs.build(); - } else { - throw new RDSException("Expected list"); - } + private Attributes readAttributes(Flags flags) throws IOException { + if (flags.hasAttributes()) { + return readAttributes(); + } else { + return Attributes.NONE; } + } - private RegSymSXP readSymbol() throws IOException { - var flags = readFlags(); - var s = readString(flags); - var item = SEXPs.symbol(s); + private Attributes readAttributes() throws IOException { + if (readItem() instanceof ListSXP xs) { + var attrs = new Attributes.Builder(); - refTable.add(item); + for (var x : xs) { + var attr = x.tag(); + if (attr == null) { + throw new RDSException("Expected tag"); + } + attrs.put(attr, x.value()); + } - return item; + return attrs.build(); + } else { + throw new RDSException("Expected list"); } + } - private String readString(Flags flags) throws IOException { - var len = in.readInt(); - var charset = StandardCharsets.US_ASCII; + private RegSymSXP readSymbol() throws IOException { + var flags = readFlags(); + var s = readString(flags); + var item = SEXPs.symbol(s); - if (flags.isUTF8()) { - charset = StandardCharsets.UTF_8; - } + refTable.add(item); - return in.readString(len, charset); - } + return item; + } - private Flags readFlags() throws IOException { - var raw = in.readInt(); - return new Flags(raw); - } + private String readString(Flags flags) throws IOException { + var len = in.readInt(); + var charset = StandardCharsets.US_ASCII; - @Override - public void close() throws IOException { - in.close(); + if (flags.isUTF8()) { + charset = StandardCharsets.UTF_8; } + + return in.readString(len, charset); + } + + private Flags readFlags() throws IOException { + var raw = in.readInt(); + return new Flags(raw); + } + + @Override + public void close() throws IOException { + in.close(); + } } diff --git a/src/main/java/org/prlprg/rds/package-info.java b/src/main/java/org/prlprg/rds/package-info.java index 6c4f98d43..71645c59d 100644 --- a/src/main/java/org/prlprg/rds/package-info.java +++ b/src/main/java/org/prlprg/rds/package-info.java @@ -3,7 +3,6 @@ @ReturnTypesAreNonNullByDefault package org.prlprg.rds; +import javax.annotation.ParametersAreNonnullByDefault; import org.prlprg.util.FieldsAreNonNullByDefault; import org.prlprg.util.ReturnTypesAreNonNullByDefault; - -import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/org/prlprg/sexp/Attributes.java b/src/main/java/org/prlprg/sexp/Attributes.java index f9bd0c1c4..44e03a6d6 100644 --- a/src/main/java/org/prlprg/sexp/Attributes.java +++ b/src/main/java/org/prlprg/sexp/Attributes.java @@ -4,56 +4,55 @@ import com.google.common.collect.ForwardingMap; import com.google.common.collect.ImmutableMap; import com.google.errorprone.annotations.CanIgnoreReturnValue; - -import javax.annotation.concurrent.Immutable; import java.util.Map; +import javax.annotation.concurrent.Immutable; /** Attributes on an {@link SEXP}. */ @Immutable public final class Attributes extends ForwardingMap { - private final ImmutableMap attrs; - - public static final Attributes NONE = new Attributes(); - - private Attributes() { - attrs = ImmutableMap.of(); - } - - private Attributes(ImmutableMap attrs) { - this.attrs = attrs; - } - - @Override - protected Map delegate() { - return attrs; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Attributes that)) return false; - return Objects.equal(attrs, that.attrs); - } - - @Override - public int hashCode() { - return Objects.hashCode(super.hashCode(), attrs); + private final ImmutableMap attrs; + + public static final Attributes NONE = new Attributes(); + + private Attributes() { + attrs = ImmutableMap.of(); + } + + private Attributes(ImmutableMap attrs) { + this.attrs = attrs; + } + + @Override + protected Map delegate() { + return attrs; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Attributes that)) return false; + return Objects.equal(attrs, that.attrs); + } + + @Override + public int hashCode() { + return Objects.hashCode(super.hashCode(), attrs); + } + + /** Build an {@link Attributes} instance. */ + public static class Builder { + private final ImmutableMap.Builder attrs = ImmutableMap.builder(); + + /** Add an attribute. */ + @CanIgnoreReturnValue + public Builder put(String key, SEXP value) { + attrs.put(key, value); + return this; } - /** Build an {@link Attributes} instance. */ - public static class Builder { - private final ImmutableMap.Builder attrs = ImmutableMap.builder(); - - /** Add an attribute. */ - @CanIgnoreReturnValue - public Builder put(String key, SEXP value) { - attrs.put(key, value); - return this; - } - - /** Finish building the {@link Attributes} instance. */ - public Attributes build() { - return new Attributes(attrs.build()); - } + /** Finish building the {@link Attributes} instance. */ + public Attributes build() { + return new Attributes(attrs.build()); } + } } diff --git a/src/main/java/org/prlprg/sexp/BCodeSXP.java b/src/main/java/org/prlprg/sexp/BCodeSXP.java index 0766660eb..d19934a30 100644 --- a/src/main/java/org/prlprg/sexp/BCodeSXP.java +++ b/src/main/java/org/prlprg/sexp/BCodeSXP.java @@ -1,22 +1,21 @@ package org.prlprg.sexp; -import org.prlprg.bc.Bc; - import javax.annotation.concurrent.Immutable; +import org.prlprg.bc.Bc; @Immutable public sealed interface BCodeSXP extends SEXP { - Bc bc(); + Bc bc(); - @Override - default SEXPType type() { - return SEXPType.BCODE; - } + @Override + default SEXPType type() { + return SEXPType.BCODE; + } } record BCodeSXPImpl(Bc bc) implements BCodeSXP { - @Override - public String toString() { - return SEXPs.toString(this, bc()); - } + @Override + public String toString() { + return SEXPs.toString(this, bc()); + } } diff --git a/src/main/java/org/prlprg/sexp/CloSXP.java b/src/main/java/org/prlprg/sexp/CloSXP.java index b6f7099a0..4a3ea4b30 100644 --- a/src/main/java/org/prlprg/sexp/CloSXP.java +++ b/src/main/java/org/prlprg/sexp/CloSXP.java @@ -1,31 +1,33 @@ package org.prlprg.sexp; public sealed interface CloSXP extends SEXP { - ListSXP formals(); + ListSXP formals(); - SEXP body(); + SEXP body(); - EnvSXP env(); + EnvSXP env(); - @Override - default SEXPType type() { - return SEXPType.CLO; - } + @Override + default SEXPType type() { + return SEXPType.CLO; + } - @Override Attributes attributes(); + @Override + Attributes attributes(); - @Override - CloSXP withAttributes(Attributes attributes); + @Override + CloSXP withAttributes(Attributes attributes); } -record CloSXPImpl(ListSXP formals, SEXP body, EnvSXP env, @Override Attributes attributes) implements CloSXP { - @Override - public String toString() { - return SEXPs.toString(this, env(), formals(), "\n → ", body()); - } - - @Override - public CloSXP withAttributes(Attributes attributes) { - return SEXPs.closure(formals, body, env, attributes); - } +record CloSXPImpl(ListSXP formals, SEXP body, EnvSXP env, @Override Attributes attributes) + implements CloSXP { + @Override + public String toString() { + return SEXPs.toString(this, env(), formals(), "\n → ", body()); + } + + @Override + public CloSXP withAttributes(Attributes attributes) { + return SEXPs.closure(formals, body, env, attributes); + } } diff --git a/src/main/java/org/prlprg/sexp/ComplexSXP.java b/src/main/java/org/prlprg/sexp/ComplexSXP.java index a13ab6284..d380e95ef 100644 --- a/src/main/java/org/prlprg/sexp/ComplexSXP.java +++ b/src/main/java/org/prlprg/sexp/ComplexSXP.java @@ -2,70 +2,71 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.UnmodifiableIterator; -import org.prlprg.primitive.Complex; - import javax.annotation.concurrent.Immutable; +import org.prlprg.primitive.Complex; @Immutable public sealed interface ComplexSXP extends VectorSXP { - @Override - default SEXPType type() { - return SEXPType.CPLX; - } + @Override + default SEXPType type() { + return SEXPType.CPLX; + } - @Override Attributes attributes(); + @Override + Attributes attributes(); - @Override - ComplexSXP withAttributes(Attributes attributes); + @Override + ComplexSXP withAttributes(Attributes attributes); } -record ComplexSXPImpl(ImmutableList data, @Override Attributes attributes) implements ComplexSXP { - @Override - public UnmodifiableIterator iterator() { - return data.iterator(); - } +record ComplexSXPImpl(ImmutableList data, @Override Attributes attributes) + implements ComplexSXP { + @Override + public UnmodifiableIterator iterator() { + return data.iterator(); + } - @Override - public Complex get(int i) { - return data.get(i); - } + @Override + public Complex get(int i) { + return data.get(i); + } - @Override - public int size() { - return data.size(); - } + @Override + public int size() { + return data.size(); + } - @Override - public String toString() { - return VectorSXPs.toString(this, data().stream()); - } + @Override + public String toString() { + return VectorSXPs.toString(this, data().stream()); + } - @Override - public ComplexSXP withAttributes(Attributes attributes) { - return new ComplexSXPImpl(data, attributes); - } + @Override + public ComplexSXP withAttributes(Attributes attributes) { + return new ComplexSXPImpl(data, attributes); + } } final class SimpleComplexSXPImpl extends SimpleScalarSXPImpl implements ComplexSXP { - SimpleComplexSXPImpl(Complex data) { - super(data); - } + SimpleComplexSXPImpl(Complex data) { + super(data); + } - @Override - public ComplexSXP withAttributes(Attributes attributes) { - return SEXPs.complex(data, attributes); - } + @Override + public ComplexSXP withAttributes(Attributes attributes) { + return SEXPs.complex(data, attributes); + } } final class EmptyComplexSXPImpl extends EmptyVectorSXPImpl implements ComplexSXP { - static final EmptyComplexSXPImpl INSTANCE = new EmptyComplexSXPImpl(); + static final EmptyComplexSXPImpl INSTANCE = new EmptyComplexSXPImpl(); - private EmptyComplexSXPImpl() { - super(); - } + private EmptyComplexSXPImpl() { + super(); + } - @Override - public ComplexSXP withAttributes(Attributes attributes) { - return SEXPs.complex(ImmutableList.of(), attributes); - } + @Override + public ComplexSXP withAttributes(Attributes attributes) { + return SEXPs.complex(ImmutableList.of(), attributes); + } } diff --git a/src/main/java/org/prlprg/sexp/EmptyVectorSXPImpl.java b/src/main/java/org/prlprg/sexp/EmptyVectorSXPImpl.java index 81c3d0ddd..20e396eb4 100644 --- a/src/main/java/org/prlprg/sexp/EmptyVectorSXPImpl.java +++ b/src/main/java/org/prlprg/sexp/EmptyVectorSXPImpl.java @@ -2,37 +2,35 @@ import com.google.common.collect.Iterators; import com.google.common.collect.UnmodifiableIterator; - import javax.annotation.concurrent.Immutable; /** Class for representing a scalar SEXP of a primitive type with no attributes. */ @Immutable abstract non-sealed class EmptyVectorSXPImpl implements VectorSXP { - protected EmptyVectorSXPImpl() { - } - - @Override - public UnmodifiableIterator iterator() { - return Iterators.forArray(); - } - - @Override - public T get(int i) { - throw new IndexOutOfBoundsException(); - } - - @Override - public int size() { - return 0; - } - - @Override - public String toString() { - return ""; - } - - @Override - public Attributes attributes() { - return Attributes.NONE; - } + protected EmptyVectorSXPImpl() {} + + @Override + public UnmodifiableIterator iterator() { + return Iterators.forArray(); + } + + @Override + public T get(int i) { + throw new IndexOutOfBoundsException(); + } + + @Override + public int size() { + return 0; + } + + @Override + public String toString() { + return ""; + } + + @Override + public Attributes attributes() { + return Attributes.NONE; + } } diff --git a/src/main/java/org/prlprg/sexp/EnvSXP.java b/src/main/java/org/prlprg/sexp/EnvSXP.java index 95b4c207d..e83d8ffa4 100644 --- a/src/main/java/org/prlprg/sexp/EnvSXP.java +++ b/src/main/java/org/prlprg/sexp/EnvSXP.java @@ -2,23 +2,29 @@ import javax.annotation.Nullable; -/** A scope containing name-value bindings and parent environment. However, global environments we abstract the bindings - * so that we can compile in agnostic context (see {@link SpecialEnvSXP}. Environments are also special in that they can - * be mutable and equality is defined by their identity, while most R objects are immutable and equality is defined by - * their structure. */ +/** + * A scope containing name-value bindings and parent environment. However, global environments we + * abstract the bindings so that we can compile in agnostic context (see {@link SpecialEnvSXP}. + * Environments are also special in that they can be mutable and equality is defined by their + * identity, while most R objects are immutable and equality is defined by their structure. + */ // TODO: Also namespaces and packages public sealed interface EnvSXP extends SEXP permits LocalEnvSXP, SpecialEnvSXP { - /** @return {@code null} if unknown or different in different contexts. */ - @Nullable EnvSXP knownParent(); + /** + * @return {@code null} if unknown or different in different contexts. + */ + @Nullable EnvSXP knownParent(); - /** @return {@code null} if not present, unknown, or different in different contexts */ - @Nullable SEXP get(String name); + /** + * @return {@code null} if not present, unknown, or different in different contexts + */ + @Nullable SEXP get(String name); - @Override - default SEXPType type() { - return SEXPType.ENV; - } + @Override + default SEXPType type() { + return SEXPType.ENV; + } - @Override - EnvSXP withAttributes(Attributes attributes); + @Override + EnvSXP withAttributes(Attributes attributes); } diff --git a/src/main/java/org/prlprg/sexp/ExprSXP.java b/src/main/java/org/prlprg/sexp/ExprSXP.java index 97ac212db..34a2ec4fe 100644 --- a/src/main/java/org/prlprg/sexp/ExprSXP.java +++ b/src/main/java/org/prlprg/sexp/ExprSXP.java @@ -2,48 +2,50 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.UnmodifiableIterator; - import javax.annotation.concurrent.Immutable; -/** R expression vector. +/** + * R expression vector. * * @apiNote Immutable because we assume it can't contain environments or any other mutable SEXPs. */ @Immutable public sealed interface ExprSXP extends VectorSXP { - @Override - default SEXPType type() { - return SEXPType.EXPR; - } + @Override + default SEXPType type() { + return SEXPType.EXPR; + } - @Override Attributes attributes(); + @Override + Attributes attributes(); - @Override ExprSXP withAttributes(Attributes attributes); + @Override + ExprSXP withAttributes(Attributes attributes); } record ExprSXPImpl(ImmutableList data, @Override Attributes attributes) implements ExprSXP { - @Override - public UnmodifiableIterator iterator() { - return data.iterator(); - } - - @Override - public SEXP get(int i) { - return data.get(i); - } - - @Override - public int size() { - return data.size(); - } - - @Override - public String toString() { - return VectorSXPs.toString(this, data().stream()); - } - - @Override - public ExprSXP withAttributes(Attributes attributes) { - return SEXPs.expr(data, attributes); - } + @Override + public UnmodifiableIterator iterator() { + return data.iterator(); + } + + @Override + public SEXP get(int i) { + return data.get(i); + } + + @Override + public int size() { + return data.size(); + } + + @Override + public String toString() { + return VectorSXPs.toString(this, data().stream()); + } + + @Override + public ExprSXP withAttributes(Attributes attributes) { + return SEXPs.expr(data, attributes); + } } diff --git a/src/main/java/org/prlprg/sexp/IntSXP.java b/src/main/java/org/prlprg/sexp/IntSXP.java index 44b3d19b5..7986c6c78 100644 --- a/src/main/java/org/prlprg/sexp/IntSXP.java +++ b/src/main/java/org/prlprg/sexp/IntSXP.java @@ -1,98 +1,99 @@ package org.prlprg.sexp; import com.google.common.primitives.ImmutableIntArray; - -import javax.annotation.concurrent.Immutable; import java.util.PrimitiveIterator; +import javax.annotation.concurrent.Immutable; @Immutable public sealed interface IntSXP extends VectorSXP { - ImmutableIntArray subArray(int startIndex, int endIndex); + ImmutableIntArray subArray(int startIndex, int endIndex); - @Override - default SEXPType type() { - return SEXPType.INT; - } + @Override + default SEXPType type() { + return SEXPType.INT; + } - @Override Attributes attributes(); + @Override + Attributes attributes(); - @Override - IntSXP withAttributes(Attributes attributes); + @Override + IntSXP withAttributes(Attributes attributes); } record IntSXPImpl(ImmutableIntArray data, @Override Attributes attributes) implements IntSXP { - @Override - public ImmutableIntArray subArray(int startIndex, int endIndex) { - return data.subArray(startIndex, endIndex); - } - - @Override - public PrimitiveIterator.OfInt iterator() { - return data.stream().iterator(); - } - - @Override - public Integer get(int i) { - return data.get(i); - } - - @Override - public int size() { - return data.length(); - } - - @Override - public String toString() { - return VectorSXPs.toString(this, data().stream()); - } - - @Override - public IntSXP withAttributes(Attributes attributes) { - return SEXPs.integer(data, attributes); - } + @Override + public ImmutableIntArray subArray(int startIndex, int endIndex) { + return data.subArray(startIndex, endIndex); + } + + @Override + public PrimitiveIterator.OfInt iterator() { + return data.stream().iterator(); + } + + @Override + public Integer get(int i) { + return data.get(i); + } + + @Override + public int size() { + return data.length(); + } + + @Override + public String toString() { + return VectorSXPs.toString(this, data().stream()); + } + + @Override + public IntSXP withAttributes(Attributes attributes) { + return SEXPs.integer(data, attributes); + } } final class SimpleIntSXPImpl extends SimpleScalarSXPImpl implements IntSXP { - SimpleIntSXPImpl(int data) { - super(data); + SimpleIntSXPImpl(int data) { + super(data); + } + + @Override + public ImmutableIntArray subArray(int startIndex, int endIndex) { + if (startIndex == endIndex && (startIndex == 0 || startIndex == 1)) { + return ImmutableIntArray.of(); + } else if (startIndex == 0 && endIndex == 1) { + return ImmutableIntArray.of(data); + } else { + throw new IndexOutOfBoundsException( + "subArray of simple scalar; startIndex=" + startIndex + ", endIndex=" + endIndex); } + } - @Override - public ImmutableIntArray subArray(int startIndex, int endIndex) { - if (startIndex == endIndex && (startIndex == 0 || startIndex == 1)) { - return ImmutableIntArray.of(); - } else if (startIndex == 0 && endIndex == 1) { - return ImmutableIntArray.of(data); - } else { - throw new IndexOutOfBoundsException("subArray of simple scalar; startIndex=" + startIndex + ", endIndex=" + endIndex); - } - } - - @Override - public IntSXP withAttributes(Attributes attributes) { - return SEXPs.integer(data, attributes); - } + @Override + public IntSXP withAttributes(Attributes attributes) { + return SEXPs.integer(data, attributes); + } } - final class EmptyIntSXPImpl extends EmptyVectorSXPImpl implements IntSXP { - static final EmptyIntSXPImpl INSTANCE = new EmptyIntSXPImpl(); - - private EmptyIntSXPImpl() { - super(); + static final EmptyIntSXPImpl INSTANCE = new EmptyIntSXPImpl(); + + private EmptyIntSXPImpl() { + super(); + } + + @Override + public ImmutableIntArray subArray(int startIndex, int endIndex) { + if (startIndex == 0 && endIndex == 0) { + return ImmutableIntArray.of(); + } else { + throw new IndexOutOfBoundsException( + "subArray of empty vector; startIndex=" + startIndex + ", endIndex=" + endIndex); } + } - @Override - public ImmutableIntArray subArray(int startIndex, int endIndex) { - if (startIndex == 0 && endIndex == 0) { - return ImmutableIntArray.of(); - } else { - throw new IndexOutOfBoundsException("subArray of empty vector; startIndex=" + startIndex + ", endIndex=" + endIndex); - } - } - - @Override - public IntSXP withAttributes(Attributes attributes) { - return SEXPs.integer(ImmutableIntArray.of(), attributes); - } + @Override + public IntSXP withAttributes(Attributes attributes) { + return SEXPs.integer(ImmutableIntArray.of(), attributes); + } } diff --git a/src/main/java/org/prlprg/sexp/LangSXP.java b/src/main/java/org/prlprg/sexp/LangSXP.java index dced17b2d..a28f088d0 100644 --- a/src/main/java/org/prlprg/sexp/LangSXP.java +++ b/src/main/java/org/prlprg/sexp/LangSXP.java @@ -1,45 +1,46 @@ package org.prlprg.sexp; -import org.prlprg.primitive.Names; - import javax.annotation.concurrent.Immutable; +import org.prlprg.primitive.Names; @Immutable public sealed interface LangSXP extends SymOrLangSXP { - SymOrLangSXP fun(); + SymOrLangSXP fun(); - ListSXP args(); + ListSXP args(); - @Override - default SEXPType type() { - return SEXPType.LANG; - } - @Override Attributes attributes(); + @Override + default SEXPType type() { + return SEXPType.LANG; + } - @Override - LangSXP withAttributes(Attributes attributes); -} + @Override + Attributes attributes(); -record LangSXPImpl(SymOrLangSXP fun, ListSXP args, @Override Attributes attributes) implements LangSXP { - @Override - public String toString() { - return attributes().isEmpty() ? deparse() : - SEXPs.toString(this, deparse(), attributes()); - } + @Override + LangSXP withAttributes(Attributes attributes); +} - // TODO: Special print `{`, `if`, `while`, `for`, ... - private String deparse() { - if (fun instanceof RegSymSXP funSym) { - var funName = funSym.name(); - if (Names.BINOPS.contains(funName) && args.size() == 2) { - return args.get(0) + " " + funName + " " + args.get(1); - } - } - return "" + fun() + args(); +record LangSXPImpl(SymOrLangSXP fun, ListSXP args, @Override Attributes attributes) + implements LangSXP { + @Override + public String toString() { + return attributes().isEmpty() ? deparse() : SEXPs.toString(this, deparse(), attributes()); + } + + // TODO: Special print `{`, `if`, `while`, `for`, ... + private String deparse() { + if (fun instanceof RegSymSXP funSym) { + var funName = funSym.name(); + if (Names.BINOPS.contains(funName) && args.size() == 2) { + return args.get(0) + " " + funName + " " + args.get(1); + } } + return "" + fun() + args(); + } - @Override - public LangSXP withAttributes(Attributes attributes) { - return SEXPs.lang(fun, args, attributes); - } + @Override + public LangSXP withAttributes(Attributes attributes) { + return SEXPs.lang(fun, args, attributes); + } } diff --git a/src/main/java/org/prlprg/sexp/LglSXP.java b/src/main/java/org/prlprg/sexp/LglSXP.java index cc7401b60..8be76f4c3 100644 --- a/src/main/java/org/prlprg/sexp/LglSXP.java +++ b/src/main/java/org/prlprg/sexp/LglSXP.java @@ -2,75 +2,74 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.UnmodifiableIterator; -import org.prlprg.primitive.Logical; - import javax.annotation.concurrent.Immutable; +import org.prlprg.primitive.Logical; @Immutable public sealed interface LglSXP extends VectorSXP { - @Override - default SEXPType type() { - return SEXPType.LGL; - } + @Override + default SEXPType type() { + return SEXPType.LGL; + } - @Override Attributes attributes(); + @Override + Attributes attributes(); - @Override - LglSXP withAttributes(Attributes attributes); + @Override + LglSXP withAttributes(Attributes attributes); } record LglSXPImpl(ImmutableList data, @Override Attributes attributes) implements LglSXP { - @Override - public UnmodifiableIterator iterator() { - return data.iterator(); - } - - @Override - public Logical get(int i) { - return data.get(i); - } - - @Override - public int size() { - return data.size(); - } - - @Override - public String toString() { - return VectorSXPs.toString(this, data.stream()); - } - - @Override - public LglSXP withAttributes(Attributes attributes) { - return SEXPs.logical(data, attributes); - } + @Override + public UnmodifiableIterator iterator() { + return data.iterator(); + } + + @Override + public Logical get(int i) { + return data.get(i); + } + + @Override + public int size() { + return data.size(); + } + + @Override + public String toString() { + return VectorSXPs.toString(this, data.stream()); + } + + @Override + public LglSXP withAttributes(Attributes attributes) { + return SEXPs.logical(data, attributes); + } } - final class SimpleLglSXPImpl extends SimpleScalarSXPImpl implements LglSXP { - static final SimpleLglSXPImpl TRUE = new SimpleLglSXPImpl(Logical.TRUE); - static final SimpleLglSXPImpl FALSE = new SimpleLglSXPImpl(Logical.FALSE); - static final SimpleLglSXPImpl NA = new SimpleLglSXPImpl(Logical.NA); - - private SimpleLglSXPImpl(Logical data) { - super(data); - } - - @Override - public LglSXP withAttributes(Attributes attributes) { - return SEXPs.logical(data, attributes); - } + static final SimpleLglSXPImpl TRUE = new SimpleLglSXPImpl(Logical.TRUE); + static final SimpleLglSXPImpl FALSE = new SimpleLglSXPImpl(Logical.FALSE); + static final SimpleLglSXPImpl NA = new SimpleLglSXPImpl(Logical.NA); + + private SimpleLglSXPImpl(Logical data) { + super(data); + } + + @Override + public LglSXP withAttributes(Attributes attributes) { + return SEXPs.logical(data, attributes); + } } final class EmptyLglSXPImpl extends EmptyVectorSXPImpl implements LglSXP { - static final EmptyLglSXPImpl INSTANCE = new EmptyLglSXPImpl(); + static final EmptyLglSXPImpl INSTANCE = new EmptyLglSXPImpl(); - private EmptyLglSXPImpl() { - super(); - } + private EmptyLglSXPImpl() { + super(); + } - @Override - public LglSXP withAttributes(Attributes attributes) { - return SEXPs.logical(ImmutableList.of(), attributes); - } + @Override + public LglSXP withAttributes(Attributes attributes) { + return SEXPs.logical(ImmutableList.of(), attributes); + } } diff --git a/src/main/java/org/prlprg/sexp/ListOrVectorSXP.java b/src/main/java/org/prlprg/sexp/ListOrVectorSXP.java index 819f2143b..508a2f3ea 100644 --- a/src/main/java/org/prlprg/sexp/ListOrVectorSXP.java +++ b/src/main/java/org/prlprg/sexp/ListOrVectorSXP.java @@ -2,14 +2,14 @@ /** A list or vector SEXP (2 main collection types in GNU-R) */ public sealed interface ListOrVectorSXP extends SEXP, Iterable permits ListSXP, VectorSXP { - T get(int i); + T get(int i); - int size(); + int size(); - default boolean isEmpty() { - return size() == 0; - } + default boolean isEmpty() { + return size() == 0; + } - @Override - ListOrVectorSXP withAttributes(Attributes attributes); + @Override + ListOrVectorSXP withAttributes(Attributes attributes); } diff --git a/src/main/java/org/prlprg/sexp/ListSXP.java b/src/main/java/org/prlprg/sexp/ListSXP.java index a28758b88..a396b15af 100644 --- a/src/main/java/org/prlprg/sexp/ListSXP.java +++ b/src/main/java/org/prlprg/sexp/ListSXP.java @@ -1,53 +1,53 @@ package org.prlprg.sexp; import com.google.common.collect.ImmutableList; - import java.util.Iterator; import java.util.stream.Collectors; public sealed interface ListSXP extends ListOrVectorSXP permits NilSXP, ListSXPImpl { - static void flatten(ListSXP src, ImmutableList.Builder target) { - for (var i : src) { - if (i.value() instanceof ListSXP lst) { - flatten(lst, target); - } else { - target.add(i); - } - } + static void flatten(ListSXP src, ImmutableList.Builder target) { + for (var i : src) { + if (i.value() instanceof ListSXP lst) { + flatten(lst, target); + } else { + target.add(i); + } } + } - @Override - ListSXP withAttributes(Attributes attributes); + @Override + ListSXP withAttributes(Attributes attributes); } -record ListSXPImpl(ImmutableList data, @Override Attributes attributes) implements ListSXP { - @Override - public SEXPType type() { - return data.isEmpty() ? SEXPType.NIL : SEXPType.LIST; - } - - @Override - public Iterator iterator() { - return data.iterator(); - } - - @Override - public TaggedElem get(int i) { - return data.get(i); - } - - @Override - public int size() { - return data.size(); - } - - @Override - public String toString() { - return "(" + data.stream().map(TaggedElem::toString).collect(Collectors.joining(", ")) + ")"; - } - - @Override - public ListSXPImpl withAttributes(Attributes attributes) { - return new ListSXPImpl(data, attributes); - } +record ListSXPImpl(ImmutableList data, @Override Attributes attributes) + implements ListSXP { + @Override + public SEXPType type() { + return data.isEmpty() ? SEXPType.NIL : SEXPType.LIST; + } + + @Override + public Iterator iterator() { + return data.iterator(); + } + + @Override + public TaggedElem get(int i) { + return data.get(i); + } + + @Override + public int size() { + return data.size(); + } + + @Override + public String toString() { + return "(" + data.stream().map(TaggedElem::toString).collect(Collectors.joining(", ")) + ")"; + } + + @Override + public ListSXPImpl withAttributes(Attributes attributes) { + return new ListSXPImpl(data, attributes); + } } diff --git a/src/main/java/org/prlprg/sexp/LocalEnvSXP.java b/src/main/java/org/prlprg/sexp/LocalEnvSXP.java index 1b09d6cec..a123f8dfc 100644 --- a/src/main/java/org/prlprg/sexp/LocalEnvSXP.java +++ b/src/main/java/org/prlprg/sexp/LocalEnvSXP.java @@ -1,40 +1,39 @@ package org.prlprg.sexp; -import javax.annotation.Nullable; import java.util.SequencedMap; +import javax.annotation.Nullable; /** Environment which has ENCLOS, FRAME, HASHTAB, ATTR. */ // May permit a locked or empty special environment later. // If we do that we must remember to keep equals for identity in the new environment, // and we don't need to change equals in RegEnvSxp. public sealed interface LocalEnvSXP extends EnvSXP permits RegEnvSXP { - /** The environment's parent. */ - EnvSXP enclos(); - - @Override - default EnvSXP knownParent() { - return enclos(); - } - - /** The environment's frame. */ - SequencedMap frame(); - - /** Whether the environment is locked (immutable). */ - - boolean isLocked(); - - /** Lookup a binding in the environment or parents. - * - * @return {@code null} if not found. - */ - @Nullable - SEXP get(String name); - - /** The environment's attributes. */ - @Override - Attributes attributes(); - - /** Create a copy of this environment with the given attributes. */ - @Override - LocalEnvSXP withAttributes(Attributes attributes); + /** The environment's parent. */ + EnvSXP enclos(); + + @Override + default EnvSXP knownParent() { + return enclos(); + } + + /** The environment's frame. */ + SequencedMap frame(); + + /** Whether the environment is locked (immutable). */ + boolean isLocked(); + + /** + * Lookup a binding in the environment or parents. + * + * @return {@code null} if not found. + */ + @Nullable SEXP get(String name); + + /** The environment's attributes. */ + @Override + Attributes attributes(); + + /** Create a copy of this environment with the given attributes. */ + @Override + LocalEnvSXP withAttributes(Attributes attributes); } diff --git a/src/main/java/org/prlprg/sexp/NilSXP.java b/src/main/java/org/prlprg/sexp/NilSXP.java index 2056eb28c..dce5a8c3e 100644 --- a/src/main/java/org/prlprg/sexp/NilSXP.java +++ b/src/main/java/org/prlprg/sexp/NilSXP.java @@ -1,52 +1,53 @@ package org.prlprg.sexp; -import org.prlprg.util.EmptyIterator; - import javax.annotation.concurrent.Immutable; +import org.prlprg.util.EmptyIterator; @Immutable public final class NilSXP implements ListSXP { - static final NilSXP INSTANCE = new NilSXP(); - - private NilSXP() { - } - - @Override - public SEXPType type() { - return SEXPType.NIL; - } - - @Override - public String toString() { - return "NULL"; - } - - /** In GNU-R, nil is equivalent to an empty list, so this is always {@code true}. */ - @Override - public boolean isEmpty() { - return true; - } - - /** In GNU-R, nil is equivalent to an empty list, so this always throws {@link UnsupportedOperationException}. */ - @Override - public TaggedElem get(int i) { - throw new UnsupportedOperationException("NULL is empty"); - } - - /** In GNU-R, nil is equivalent to an empty list, so this always returns {@code 0}. */ - @Override - public int size() { - return 0; - } - - /** In GNU-R, nil is equivalent to an empty list, so this always returns an empty iterator. */ - @Override - public EmptyIterator iterator() { - return new EmptyIterator<>(); - } - - @Override - public ListSXP withAttributes(Attributes attributes) { - throw new UnsupportedOperationException("Cannot set attributes on NULL"); - } + static final NilSXP INSTANCE = new NilSXP(); + + private NilSXP() {} + + @Override + public SEXPType type() { + return SEXPType.NIL; + } + + @Override + public String toString() { + return "NULL"; + } + + /** In GNU-R, nil is equivalent to an empty list, so this is always {@code true}. */ + @Override + public boolean isEmpty() { + return true; + } + + /** + * In GNU-R, nil is equivalent to an empty list, so this always throws {@link + * UnsupportedOperationException}. + */ + @Override + public TaggedElem get(int i) { + throw new UnsupportedOperationException("NULL is empty"); + } + + /** In GNU-R, nil is equivalent to an empty list, so this always returns {@code 0}. */ + @Override + public int size() { + return 0; + } + + /** In GNU-R, nil is equivalent to an empty list, so this always returns an empty iterator. */ + @Override + public EmptyIterator iterator() { + return new EmptyIterator<>(); + } + + @Override + public ListSXP withAttributes(Attributes attributes) { + throw new UnsupportedOperationException("Cannot set attributes on NULL"); + } } diff --git a/src/main/java/org/prlprg/sexp/RealSXP.java b/src/main/java/org/prlprg/sexp/RealSXP.java index cf9aa72d2..2cdd08094 100644 --- a/src/main/java/org/prlprg/sexp/RealSXP.java +++ b/src/main/java/org/prlprg/sexp/RealSXP.java @@ -1,71 +1,70 @@ package org.prlprg.sexp; import com.google.common.primitives.ImmutableDoubleArray; - -import javax.annotation.concurrent.Immutable; import java.util.PrimitiveIterator; +import javax.annotation.concurrent.Immutable; @Immutable public sealed interface RealSXP extends VectorSXP { - @Override - default SEXPType type() { - return SEXPType.REAL; - } + @Override + default SEXPType type() { + return SEXPType.REAL; + } - @Override Attributes attributes(); + @Override + Attributes attributes(); - @Override - RealSXP withAttributes(Attributes attributes); + @Override + RealSXP withAttributes(Attributes attributes); } record RealSXPImpl(ImmutableDoubleArray data, @Override Attributes attributes) implements RealSXP { - @Override - public PrimitiveIterator.OfDouble iterator() { - return data.stream().iterator(); - } - - @Override - public Double get(int i) { - return data.get(i); - } - - @Override - public int size() { - return data.length(); - } - - @Override - public String toString() { - return VectorSXPs.toString(this, data().stream()); - } - - @Override - public RealSXP withAttributes(Attributes attributes) { - return SEXPs.real(data, attributes); - } + @Override + public PrimitiveIterator.OfDouble iterator() { + return data.stream().iterator(); + } + + @Override + public Double get(int i) { + return data.get(i); + } + + @Override + public int size() { + return data.length(); + } + + @Override + public String toString() { + return VectorSXPs.toString(this, data().stream()); + } + + @Override + public RealSXP withAttributes(Attributes attributes) { + return SEXPs.real(data, attributes); + } } - final class SimpleRealSXPImpl extends SimpleScalarSXPImpl implements RealSXP { - SimpleRealSXPImpl(double data) { - super(data); - } - - @Override - public RealSXP withAttributes(Attributes attributes) { - return SEXPs.real(data, attributes); - } + SimpleRealSXPImpl(double data) { + super(data); + } + + @Override + public RealSXP withAttributes(Attributes attributes) { + return SEXPs.real(data, attributes); + } } final class EmptyRealSXPImpl extends EmptyVectorSXPImpl implements RealSXP { - static final EmptyRealSXPImpl INSTANCE = new EmptyRealSXPImpl(); + static final EmptyRealSXPImpl INSTANCE = new EmptyRealSXPImpl(); - private EmptyRealSXPImpl() { - super(); - } + private EmptyRealSXPImpl() { + super(); + } - @Override - public RealSXP withAttributes(Attributes attributes) { - return SEXPs.real(ImmutableDoubleArray.of(), attributes); - } + @Override + public RealSXP withAttributes(Attributes attributes) { + return SEXPs.real(ImmutableDoubleArray.of(), attributes); + } } diff --git a/src/main/java/org/prlprg/sexp/RegEnvSXP.java b/src/main/java/org/prlprg/sexp/RegEnvSXP.java index 0299f51a6..930b31051 100644 --- a/src/main/java/org/prlprg/sexp/RegEnvSXP.java +++ b/src/main/java/org/prlprg/sexp/RegEnvSXP.java @@ -1,161 +1,174 @@ package org.prlprg.sexp; -import javax.annotation.Nullable; import java.util.*; import java.util.stream.Collectors; +import javax.annotation.Nullable; /** * A mutable {@link LocalEnvSXP}: a mutable environment which has ENCLOS, FRAME, HASHTAB, and ATTR. - *

- * Most other SEXPs are immutable. The reason these are mutable is because we preserve the mutability of SEXPs in R. - * In R, if you assign an SEXP to a variable and then modify it, for other SEXP types the variable won't change, but for - * environments it will. + * + *

Most other SEXPs are immutable. The reason these are mutable is because we preserve the + * mutability of SEXPs in R. In R, if you assign an SEXP to a variable and then modify it, for other + * SEXP types the variable won't change, but for environments it will. */ public final class RegEnvSXP implements LocalEnvSXP { - private boolean isInitialized; - private boolean isLocked; - private @Nullable EnvSXP enclos; - private final SequencedMap frame; - private @Nullable Attributes attributes; - - /** Create an uninitialized environment. You must then call {@link #init} before any other methods. */ - public static RegEnvSXP uninitialized() { - return new RegEnvSXP(); - } - - private RegEnvSXP() { - this.isInitialized = false; - this.isLocked = false; - this.enclos = null; - this.frame = new LinkedHashMap<>(); - this.attributes = null; - } - - /** Create a new empty (initialized, unlocked) environment. */ - RegEnvSXP(EnvSXP enclos) { - this.isInitialized = true; - this.isLocked = false; - this.enclos = enclos; - this.frame = new LinkedHashMap<>(); - this.attributes = Attributes.NONE; - } - - /** Initialize an uninitialized environment with data. */ - public void init(boolean isLocked, EnvSXP enclos, ListSXP frame, Attributes attributes) { - if (isInitialized) { - throw new IllegalStateException("Already initialized"); - } - this.isInitialized = true; - this.isLocked = isLocked; - this.enclos = enclos; - for (var elem : frame) { - this.frame.put(Objects.requireNonNull(elem.tag()), elem.value()); - } - this.attributes = attributes; - } - - @Override - public EnvSXP enclos() { - checkInitialized(); - return Objects.requireNonNull(enclos); - } - - @Override - public SequencedMap frame() { - checkInitialized(); - return Collections.unmodifiableSequencedMap(Objects.requireNonNull(frame)); - } - - @Override - public Attributes attributes() { - checkInitialized(); - return Objects.requireNonNull(attributes); - } - - @Override - public boolean isLocked() { - checkInitialized(); - return isLocked; - } - - /** Lookup a binding in the environment or parents. - * - * @return {@code null} if not found. */ - @Override - public @Nullable SEXP get(String name) { - checkInitialized(); - var frame = Objects.requireNonNull(this.frame); - var inFrame = frame.get(name); - if (inFrame != null) { - return inFrame; - } - var enclos = Objects.requireNonNull(this.enclos); - return enclos.get(name); - } - - - /** Change the environment's parent. */ - public void setEnclos(EnvSXP enclos) { - checkInitialized(); - this.enclos = enclos; - } - - /** Change the environment's attributes. */ - public void setAttributes(Attributes attributes) { - checkInitialized(); - this.attributes = attributes; - } - - /** Lock the environment. Trying to mutate it afterward will throw {@link IllegalStateException}. - * - * @throws IllegalStateException If the environment is already locked. - */ - public void lock() { - checkInitialized(); - if (isLocked) { - throw new IllegalStateException("Environment is already locked"); - } - isLocked = true; - } - - /** Modify a binding in the environment. Call with {@code null} to remove. - * - * @throws IllegalStateException if the environment is locked. - */ - public void set(String name, @Nullable SEXP value) { - checkInitialized(); - if (isLocked) { - throw new IllegalStateException("Environment is locked"); - } - var frame = Objects.requireNonNull(this.frame); - if (value == null) { - frame.remove(name); - } else { - frame.put(name, value); - } - } - - @Override - public String toString() { - return SEXPs.toString(this, "isInitialized=" + isInitialized, "enclos=" + enclos, - frame.isEmpty() ? "" : "\n ; " + frame.sequencedEntrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("\n , ")) - ); - } - - /** Create a copy of this environment with the given attributes. - *

- * This does not mutate the environment and change its own attributes. - * Call {@link #setAttributes} to do that. - */ - @Override - public LocalEnvSXP withAttributes(Attributes attributes) { - this.setAttributes(attributes); - return this; - } - - private void checkInitialized() { - if (!isInitialized) { - throw new IllegalStateException("Not initialized"); - } - } + private boolean isInitialized; + private boolean isLocked; + private @Nullable EnvSXP enclos; + private final SequencedMap frame; + private @Nullable Attributes attributes; + + /** + * Create an uninitialized environment. You must then call {@link #init} before any other methods. + */ + public static RegEnvSXP uninitialized() { + return new RegEnvSXP(); + } + + private RegEnvSXP() { + this.isInitialized = false; + this.isLocked = false; + this.enclos = null; + this.frame = new LinkedHashMap<>(); + this.attributes = null; + } + + /** Create a new empty (initialized, unlocked) environment. */ + RegEnvSXP(EnvSXP enclos) { + this.isInitialized = true; + this.isLocked = false; + this.enclos = enclos; + this.frame = new LinkedHashMap<>(); + this.attributes = Attributes.NONE; + } + + /** Initialize an uninitialized environment with data. */ + public void init(boolean isLocked, EnvSXP enclos, ListSXP frame, Attributes attributes) { + if (isInitialized) { + throw new IllegalStateException("Already initialized"); + } + this.isInitialized = true; + this.isLocked = isLocked; + this.enclos = enclos; + for (var elem : frame) { + this.frame.put(Objects.requireNonNull(elem.tag()), elem.value()); + } + this.attributes = attributes; + } + + @Override + public EnvSXP enclos() { + checkInitialized(); + return Objects.requireNonNull(enclos); + } + + @Override + public SequencedMap frame() { + checkInitialized(); + return Collections.unmodifiableSequencedMap(Objects.requireNonNull(frame)); + } + + @Override + public Attributes attributes() { + checkInitialized(); + return Objects.requireNonNull(attributes); + } + + @Override + public boolean isLocked() { + checkInitialized(); + return isLocked; + } + + /** + * Lookup a binding in the environment or parents. + * + * @return {@code null} if not found. + */ + @Override + public @Nullable SEXP get(String name) { + checkInitialized(); + var frame = Objects.requireNonNull(this.frame); + var inFrame = frame.get(name); + if (inFrame != null) { + return inFrame; + } + var enclos = Objects.requireNonNull(this.enclos); + return enclos.get(name); + } + + /** Change the environment's parent. */ + public void setEnclos(EnvSXP enclos) { + checkInitialized(); + this.enclos = enclos; + } + + /** Change the environment's attributes. */ + public void setAttributes(Attributes attributes) { + checkInitialized(); + this.attributes = attributes; + } + + /** + * Lock the environment. Trying to mutate it afterward will throw {@link IllegalStateException}. + * + * @throws IllegalStateException If the environment is already locked. + */ + public void lock() { + checkInitialized(); + if (isLocked) { + throw new IllegalStateException("Environment is already locked"); + } + isLocked = true; + } + + /** + * Modify a binding in the environment. Call with {@code null} to remove. + * + * @throws IllegalStateException if the environment is locked. + */ + public void set(String name, @Nullable SEXP value) { + checkInitialized(); + if (isLocked) { + throw new IllegalStateException("Environment is locked"); + } + var frame = Objects.requireNonNull(this.frame); + if (value == null) { + frame.remove(name); + } else { + frame.put(name, value); + } + } + + @Override + public String toString() { + return SEXPs.toString( + this, + "isInitialized=" + isInitialized, + "enclos=" + enclos, + frame.isEmpty() + ? "" + : "\n ; " + + frame.sequencedEntrySet().stream() + .map(e -> e.getKey() + "=" + e.getValue()) + .collect(Collectors.joining("\n , "))); + } + + /** + * Create a copy of this environment with the given attributes. + * + *

This does not mutate the environment and change its own attributes. Call {@link + * #setAttributes} to do that. + */ + @Override + public LocalEnvSXP withAttributes(Attributes attributes) { + this.setAttributes(attributes); + return this; + } + + private void checkInitialized() { + if (!isInitialized) { + throw new IllegalStateException("Not initialized"); + } + } } diff --git a/src/main/java/org/prlprg/sexp/RegSymSXP.java b/src/main/java/org/prlprg/sexp/RegSymSXP.java index 40c6ef836..4dcfff922 100644 --- a/src/main/java/org/prlprg/sexp/RegSymSXP.java +++ b/src/main/java/org/prlprg/sexp/RegSymSXP.java @@ -5,51 +5,52 @@ /** Symbol which isn't "unbound value" or "missing arg" */ public final class RegSymSXP implements SymSXP, StrOrRegSymSXP { - private static final ImmutableList LITERAL_NAMES = ImmutableList.of("TRUE", "FALSE", "NULL", "NA", "Inf", "NaN"); - - private final String name; - private final boolean isEscaped; - - RegSymSXP(String name) { - if (name.isBlank()) { - throw new IllegalArgumentException("Symbol name cannot be blank"); - } - if (LITERAL_NAMES.contains(name)) { - throw new IllegalArgumentException("Symbol name reserved by literal: " + name); - } - this.name = name; - isEscaped = name.chars().anyMatch(c -> !Character.isAlphabetic(c) && c != '.' && c != '_'); - } + private static final ImmutableList LITERAL_NAMES = + ImmutableList.of("TRUE", "FALSE", "NULL", "NA", "Inf", "NaN"); - /** Returns the name of this symbol. */ - public String name() { - return name; - } + private final String name; + private final boolean isEscaped; - /** Whether this is a double-dot symbol ({@code ..0}, {@code ..1}, ...). */ - public boolean isDdSym() { - return name.startsWith("..") && name.substring(2).chars().allMatch(Character::isDigit); + RegSymSXP(String name) { + if (name.isBlank()) { + throw new IllegalArgumentException("Symbol name cannot be blank"); } - - @Override - public String reifyString() { - return name; - } - - @Override - public String toString() { - return isEscaped ? "`" + name + "`" : name; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof RegSymSXP regSymSXP)) return false; - return Objects.equal(name, regSymSXP.name); - } - - @Override - public int hashCode() { - return Objects.hashCode(name); + if (LITERAL_NAMES.contains(name)) { + throw new IllegalArgumentException("Symbol name reserved by literal: " + name); } + this.name = name; + isEscaped = name.chars().anyMatch(c -> !Character.isAlphabetic(c) && c != '.' && c != '_'); + } + + /** Returns the name of this symbol. */ + public String name() { + return name; + } + + /** Whether this is a double-dot symbol ({@code ..0}, {@code ..1}, ...). */ + public boolean isDdSym() { + return name.startsWith("..") && name.substring(2).chars().allMatch(Character::isDigit); + } + + @Override + public String reifyString() { + return name; + } + + @Override + public String toString() { + return isEscaped ? "`" + name + "`" : name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof RegSymSXP regSymSXP)) return false; + return Objects.equal(name, regSymSXP.name); + } + + @Override + public int hashCode() { + return Objects.hashCode(name); + } } diff --git a/src/main/java/org/prlprg/sexp/SEXP.java b/src/main/java/org/prlprg/sexp/SEXP.java index 5169609f4..cb92adb2f 100644 --- a/src/main/java/org/prlprg/sexp/SEXP.java +++ b/src/main/java/org/prlprg/sexp/SEXP.java @@ -2,17 +2,22 @@ import javax.annotation.Nullable; -public sealed interface SEXP permits StrOrRegSymSXP, SymOrLangSXP, ListOrVectorSXP, CloSXP, EnvSXP, BCodeSXP { - SEXPType type(); +public sealed interface SEXP + permits StrOrRegSymSXP, SymOrLangSXP, ListOrVectorSXP, CloSXP, EnvSXP, BCodeSXP { + SEXPType type(); - /** @return {@code null} if the SEXP doesn't support attributes ({@link #withAttributes} throws an exception) - * and {@code Attributes.NONE} if it does but there are none. */ - default @Nullable Attributes attributes() { - return null; - } + /** + * @return {@code null} if the SEXP doesn't support attributes ({@link #withAttributes} throws an + * exception) and {@code Attributes.NONE} if it does but there are none. + */ + default @Nullable Attributes attributes() { + return null; + } - /** @throws UnsupportedOperationException if the SEXP doesn't support attributes. */ - default SEXP withAttributes(Attributes attributes) { - throw new UnsupportedOperationException("Cannot set attributes on " + type()); - } + /** + * @throws UnsupportedOperationException if the SEXP doesn't support attributes. + */ + default SEXP withAttributes(Attributes attributes) { + throw new UnsupportedOperationException("Cannot set attributes on " + type()); + } } diff --git a/src/main/java/org/prlprg/sexp/SEXPType.java b/src/main/java/org/prlprg/sexp/SEXPType.java index fd73bfe3e..e0bfff69e 100644 --- a/src/main/java/org/prlprg/sexp/SEXPType.java +++ b/src/main/java/org/prlprg/sexp/SEXPType.java @@ -3,104 +3,107 @@ /** * SEXP type you can expect to see in regular code (no unknown types, no GC types, etc.) * - * @apiNote If you want to represent an arbitrary (AKA possibly malformed) type just use {@code int}. + * @apiNote If you want to represent an arbitrary (AKA possibly malformed) type just use {@code + * int}. */ public enum SEXPType { - /** nil = NULL */ - NIL(0), - /** symbols */ - SYM(1), - /** lists of dotted pairs */ - LIST(2), - /** closures */ - CLO(3), - /** environments */ - ENV(4), - /** promises: [un]evaluated closure arguments */ - PROM(5), - /** language constructs (special lists) */ - LANG(6), - /** special forms */ - SPECIAL(7), - /** builtin non-special forms */ - BUILTIN(8), - /** "scalar" string type (internal only) */ - CHAR(9), - /** logical vectors */ - LGL(10), - /** integer vectors */ - INT(13), - /** real variables */ - REAL(14), - /** complex variables */ - CPLX(15), - /** string vectors */ - STR(16), - /** dot-dot-dot object */ - DOT(17), - /** make "any" args work */ - ANY(18), - /** generic vectors */ - VEC(19), - /** expressions vectors */ - EXPR(20), - /** byte code */ - BCODE(21), - /** external pointer */ - EXTPTR(22), - /** weak reference */ - WEAKREF(23), - /** raw bytes */ - RAW(24), - /** S4 non-vector */ - S4(25); - // These types are in GNU-R but they should never actually show up unless something's seriously wrong. - // /** fresh node created in new page */ - // NEW(30), - // /** node released by GC */ - // FREE(31), - // /** closure, builtin, special */ - // FUN(99); + /** nil = NULL */ + NIL(0), + /** symbols */ + SYM(1), + /** lists of dotted pairs */ + LIST(2), + /** closures */ + CLO(3), + /** environments */ + ENV(4), + /** promises: [un]evaluated closure arguments */ + PROM(5), + /** language constructs (special lists) */ + LANG(6), + /** special forms */ + SPECIAL(7), + /** builtin non-special forms */ + BUILTIN(8), + /** "scalar" string type (internal only) */ + CHAR(9), + /** logical vectors */ + LGL(10), + /** integer vectors */ + INT(13), + /** real variables */ + REAL(14), + /** complex variables */ + CPLX(15), + /** string vectors */ + STR(16), + /** dot-dot-dot object */ + DOT(17), + /** make "any" args work */ + ANY(18), + /** generic vectors */ + VEC(19), + /** expressions vectors */ + EXPR(20), + /** byte code */ + BCODE(21), + /** external pointer */ + EXTPTR(22), + /** weak reference */ + WEAKREF(23), + /** raw bytes */ + RAW(24), + /** S4 non-vector */ + S4(25); - /** The integer which represents this type in GNU-R */ - public final int i; + // These types are in GNU-R but they should never actually show up unless something's seriously + // wrong. + // /** fresh node created in new page */ + // NEW(30), + // /** node released by GC */ + // FREE(31), + // /** closure, builtin, special */ + // FUN(99); - SEXPType(int i) { - this.i = i; - } + /** The integer which represents this type in GNU-R */ + public final int i; - /** Return the SEXP type represented by the given integer value. */ - public static SEXPType valueOf(int i) { - // These will never change so we don't have to worry about code synchronization - return switch (i) { - case 0 -> NIL; - case 1 -> SYM; - case 2 -> LIST; - case 3 -> CLO; - case 4 -> ENV; - case 5 -> PROM; - case 6 -> LANG; - case 7 -> SPECIAL; - case 8 -> BUILTIN; - case 9 -> CHAR; - case 10 -> LGL; - case 13 -> INT; - case 14 -> REAL; - case 15 -> CPLX; - case 16 -> STR; - case 17 -> DOT; - case 18 -> ANY; - case 19 -> VEC; - case 20 -> EXPR; - case 21 -> BCODE; - case 22 -> EXTPTR; - case 23 -> WEAKREF; - case 24 -> RAW; - case 25 -> S4; - // case 30 -> NEW; - // case 31 -> FREE; - // case 99 -> FUN; - default -> throw new IllegalArgumentException("Unknown SEXP type: " + i); - }; - } + SEXPType(int i) { + this.i = i; + } + + /** Return the SEXP type represented by the given integer value. */ + public static SEXPType valueOf(int i) { + // These will never change so we don't have to worry about code synchronization + return switch (i) { + case 0 -> NIL; + case 1 -> SYM; + case 2 -> LIST; + case 3 -> CLO; + case 4 -> ENV; + case 5 -> PROM; + case 6 -> LANG; + case 7 -> SPECIAL; + case 8 -> BUILTIN; + case 9 -> CHAR; + case 10 -> LGL; + case 13 -> INT; + case 14 -> REAL; + case 15 -> CPLX; + case 16 -> STR; + case 17 -> DOT; + case 18 -> ANY; + case 19 -> VEC; + case 20 -> EXPR; + case 21 -> BCODE; + case 22 -> EXTPTR; + case 23 -> WEAKREF; + case 24 -> RAW; + case 25 -> S4; + // case 30 -> NEW; + // case 31 -> FREE; + // case 99 -> FUN; + default -> throw new IllegalArgumentException("Unknown SEXP type: " + i); + }; + } } diff --git a/src/main/java/org/prlprg/sexp/SEXPs.java b/src/main/java/org/prlprg/sexp/SEXPs.java index 06eedfc89..041404745 100644 --- a/src/main/java/org/prlprg/sexp/SEXPs.java +++ b/src/main/java/org/prlprg/sexp/SEXPs.java @@ -3,384 +3,400 @@ import com.google.common.collect.ImmutableList; import com.google.common.primitives.ImmutableDoubleArray; import com.google.common.primitives.ImmutableIntArray; -import org.prlprg.bc.Bc; -import org.prlprg.primitive.Complex; -import org.prlprg.primitive.Constants; -import org.prlprg.primitive.Logical; - import java.util.Arrays; import java.util.Collection; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.prlprg.bc.Bc; +import org.prlprg.primitive.Complex; +import org.prlprg.primitive.Constants; +import org.prlprg.primitive.Logical; public final class SEXPs { - // region constants - public static final NilSXP NULL = NilSXP.INSTANCE; - public static final LglSXP TRUE = SimpleLglSXPImpl.TRUE; - public static final LglSXP FALSE = SimpleLglSXPImpl.FALSE; - public static final LglSXP NA_LOGICAL = SimpleLglSXPImpl.NA; - public static final IntSXP NA_INTEGER = new SimpleIntSXPImpl(Constants.NA_INT); - public static final RealSXP NA_REAL = new SimpleRealSXPImpl(Constants.NA_REAL); - public static final StrSXP NA_STRING = new SimpleStrSXPImpl(Constants.NA_STRING); - public static final LglSXP EMPTY_LOGICAL = EmptyLglSXPImpl.INSTANCE; - public static final IntSXP EMPTY_INTEGER = EmptyIntSXPImpl.INSTANCE; - public static final RealSXP EMPTY_REAL = EmptyRealSXPImpl.INSTANCE; - public static final StrSXP EMPTY_STRING = EmptyStrSXPImpl.INSTANCE; - public static final ComplexSXP EMPTY_COMPLEX = EmptyComplexSXPImpl.INSTANCE; - public static final SpecialSymSXP UNBOUND_VALUE = new SpecialSymSXP("UNBOUND_VALUE"); - - public static final SpecialSymSXP MISSING_ARG = new SpecialSymSXP("MISSING_ARG"); - - public static final RegSymSXP ELLIPSIS = new RegSymSXP("..."); - - public static final SpecialEnvSXP GLOBAL_ENV = new SpecialEnvSXP("GLOBAL_ENV"); - - public static final SpecialEnvSXP BASE_ENV = new SpecialEnvSXP("BASE_ENV"); - - public static final SpecialEnvSXP BASE_NAMESPACE = new SpecialEnvSXP("BASE_NAMESPACE"); - - public static final SpecialEnvSXP EMPTY_ENV = new SpecialEnvSXP("EMPTY_ENV"); - // endregion - - // region constructors - public static LglSXP logical(Logical data) { - return switch (data) { - case TRUE -> TRUE; - case FALSE -> FALSE; - case NA -> NA_LOGICAL; - }; - } - - public static IntSXP integer(int data) { - return new SimpleIntSXPImpl(data); - } - - public static RealSXP real(double data) { - return new SimpleRealSXPImpl(data); - } - - public static StrSXP string(String data) { - return new SimpleStrSXPImpl(data); - } - - public static ComplexSXP complex(Complex data) { - return new SimpleComplexSXPImpl(data); - } - - public static IntSXP integer(int first, int... rest) { - return integer(ImmutableIntArray.of(first, rest)); - } - - public static RealSXP real(double first, double... rest) { - return real(ImmutableDoubleArray.of(first, rest)); - } - - public static StrSXP string(String first, String... rest) { - return string(ImmutableList.builderWithExpectedSize(rest.length + 1).add(first).add(rest).build()); - } - - public static ComplexSXP complex(Complex first, Complex... rest) { - return complex(ImmutableList.builderWithExpectedSize(rest.length + 1).add(first).add(rest).build()); - } - - public static LglSXP logical(Logical[] data) { - return logical(ImmutableList.copyOf(data)); - } - - public static IntSXP integer(int[] data) { - return integer(ImmutableIntArray.copyOf(data)); - } - - public static RealSXP real(double[] data) { - return real(ImmutableDoubleArray.copyOf(data)); - } - - public static StrSXP string(String[] data) { - return string(ImmutableList.copyOf(data)); - } - - public static ComplexSXP complex(Complex[] data) { - return complex(ImmutableList.copyOf(data)); - } - - public static VecSXP vec(SEXP... data) { - return vec(ImmutableList.copyOf(data)); - } + // region constants + public static final NilSXP NULL = NilSXP.INSTANCE; + public static final LglSXP TRUE = SimpleLglSXPImpl.TRUE; + public static final LglSXP FALSE = SimpleLglSXPImpl.FALSE; + public static final LglSXP NA_LOGICAL = SimpleLglSXPImpl.NA; + public static final IntSXP NA_INTEGER = new SimpleIntSXPImpl(Constants.NA_INT); + public static final RealSXP NA_REAL = new SimpleRealSXPImpl(Constants.NA_REAL); + public static final StrSXP NA_STRING = new SimpleStrSXPImpl(Constants.NA_STRING); + public static final LglSXP EMPTY_LOGICAL = EmptyLglSXPImpl.INSTANCE; + public static final IntSXP EMPTY_INTEGER = EmptyIntSXPImpl.INSTANCE; + public static final RealSXP EMPTY_REAL = EmptyRealSXPImpl.INSTANCE; + public static final StrSXP EMPTY_STRING = EmptyStrSXPImpl.INSTANCE; + public static final ComplexSXP EMPTY_COMPLEX = EmptyComplexSXPImpl.INSTANCE; + public static final SpecialSymSXP UNBOUND_VALUE = new SpecialSymSXP("UNBOUND_VALUE"); + + public static final SpecialSymSXP MISSING_ARG = new SpecialSymSXP("MISSING_ARG"); + + public static final RegSymSXP ELLIPSIS = new RegSymSXP("..."); + + public static final SpecialEnvSXP GLOBAL_ENV = new SpecialEnvSXP("GLOBAL_ENV"); + + public static final SpecialEnvSXP BASE_ENV = new SpecialEnvSXP("BASE_ENV"); + + public static final SpecialEnvSXP BASE_NAMESPACE = new SpecialEnvSXP("BASE_NAMESPACE"); + + public static final SpecialEnvSXP EMPTY_ENV = new SpecialEnvSXP("EMPTY_ENV"); + + // endregion + + // region constructors + public static LglSXP logical(Logical data) { + return switch (data) { + case TRUE -> TRUE; + case FALSE -> FALSE; + case NA -> NA_LOGICAL; + }; + } + + public static IntSXP integer(int data) { + return new SimpleIntSXPImpl(data); + } + + public static RealSXP real(double data) { + return new SimpleRealSXPImpl(data); + } + + public static StrSXP string(String data) { + return new SimpleStrSXPImpl(data); + } + + public static ComplexSXP complex(Complex data) { + return new SimpleComplexSXPImpl(data); + } + + public static IntSXP integer(int first, int... rest) { + return integer(ImmutableIntArray.of(first, rest)); + } + + public static RealSXP real(double first, double... rest) { + return real(ImmutableDoubleArray.of(first, rest)); + } + + public static StrSXP string(String first, String... rest) { + return string( + ImmutableList.builderWithExpectedSize(rest.length + 1) + .add(first) + .add(rest) + .build()); + } + + public static ComplexSXP complex(Complex first, Complex... rest) { + return complex( + ImmutableList.builderWithExpectedSize(rest.length + 1) + .add(first) + .add(rest) + .build()); + } + + public static LglSXP logical(Logical[] data) { + return logical(ImmutableList.copyOf(data)); + } + + public static IntSXP integer(int[] data) { + return integer(ImmutableIntArray.copyOf(data)); + } + + public static RealSXP real(double[] data) { + return real(ImmutableDoubleArray.copyOf(data)); + } + + public static StrSXP string(String[] data) { + return string(ImmutableList.copyOf(data)); + } + + public static ComplexSXP complex(Complex[] data) { + return complex(ImmutableList.copyOf(data)); + } + + public static VecSXP vec(SEXP... data) { + return vec(ImmutableList.copyOf(data)); + } + + public static ExprSXP expr(SEXP... data) { + return expr(ImmutableList.copyOf(data)); + } + + public static ComplexSXP complex(double real, double imaginary) { + return new SimpleComplexSXPImpl(new Complex(real, imaginary)); + } - public static ExprSXP expr(SEXP... data) { - return expr(ImmutableList.copyOf(data)); - } - - public static ComplexSXP complex(double real, double imaginary) { - return new SimpleComplexSXPImpl(new Complex(real, imaginary)); - } - - public static LglSXP logical(Logical data, Attributes attributes) { - return logical(ImmutableList.of(data), attributes); - } - - public static IntSXP integer(int data, Attributes attributes) { - return integer(ImmutableIntArray.of(data), attributes); - } - - public static RealSXP real(double data, Attributes attributes) { - return real(ImmutableDoubleArray.of(data), attributes); - } - - public static StrSXP string(String data, Attributes attributes) { - return string(ImmutableList.of(data), attributes); - } - - public static ComplexSXP complex(Complex data, Attributes attributes) { - return complex(ImmutableList.of(data), attributes); - } - - public static LglSXP logical(ImmutableList data) { - return logical(data, Attributes.NONE); - } - - public static IntSXP integer(ImmutableIntArray data) { - return integer(data, Attributes.NONE); - } - - public static RealSXP real(ImmutableDoubleArray data) { - return real(data, Attributes.NONE); - } + public static LglSXP logical(Logical data, Attributes attributes) { + return logical(ImmutableList.of(data), attributes); + } - public static StrSXP string(ImmutableList data) { - return string(data, Attributes.NONE); - } - - public static ComplexSXP complex(ImmutableList data) { - return complex(data, Attributes.NONE); - } - - public static VecSXP vec(ImmutableList data) { - return vec(data, Attributes.NONE); - } - - public static ExprSXP expr(ImmutableList data) { - return expr(data, Attributes.NONE); - } - - public static LglSXP logical(Collection data) { - return logical(ImmutableList.copyOf(data), Attributes.NONE); - } - - public static IntSXP integer(Collection data) { - return integer(ImmutableIntArray.copyOf(data), Attributes.NONE); - } - - public static RealSXP real(Collection data) { - return real(ImmutableDoubleArray.copyOf(data), Attributes.NONE); - } - - public static StrSXP string(Collection data) { - return string(ImmutableList.copyOf(data), Attributes.NONE); - } - - public static ComplexSXP complex(Collection data) { - return complex(ImmutableList.copyOf(data), Attributes.NONE); - } - - public static VecSXP vec(Collection data) { - return vec(ImmutableList.copyOf(data), Attributes.NONE); - } - - public static ExprSXP expr(Collection data) { - return expr(ImmutableList.copyOf(data), Attributes.NONE); - } - - public static LglSXP logical(Logical[] data, Attributes attributes) { - return logical(ImmutableList.copyOf(data), attributes); - } - - public static IntSXP integer(int[] data, Attributes attributes) { - return integer(ImmutableIntArray.copyOf(data), attributes); - } - - public static RealSXP real(double[] data, Attributes attributes) { - return real(ImmutableDoubleArray.copyOf(data), attributes); - } - - public static StrSXP string(String[] data, Attributes attributes) { - return string(ImmutableList.copyOf(data), attributes); - } - - public static ComplexSXP complex(Complex[] data, Attributes attributes) { - return complex(ImmutableList.copyOf(data), attributes); - } - - public static VecSXP vec(SEXP[] data, Attributes attributes) { - return vec(ImmutableList.copyOf(data), attributes); - } - - public static ExprSXP expr(SEXP[] data, Attributes attributes) { - return expr(ImmutableList.copyOf(data), attributes); - } - - public static LglSXP logical(ImmutableList data, Attributes attributes) { - if (attributes.isEmpty()) { - return switch (data.size()) { - case 0 -> EMPTY_LOGICAL; - case 1 -> logical(data.getFirst()); - default -> new LglSXPImpl(data, attributes); - }; - } - return new LglSXPImpl(data, attributes); - } - - public static IntSXP integer(ImmutableIntArray data, Attributes attributes) { - if (attributes.isEmpty()) { - return switch (data.length()) { - case 0 -> EMPTY_INTEGER; - case 1 -> integer(data.get(0)); - default -> new IntSXPImpl(data, attributes); - }; - } - return new IntSXPImpl(data, attributes); - } - - public static RealSXP real(ImmutableDoubleArray data, Attributes attributes) { - if (attributes.isEmpty()) { - return switch (data.length()) { - case 0 -> EMPTY_REAL; - case 1 -> real(data.get(0)); - default -> new RealSXPImpl(data, attributes); - }; - } - return new RealSXPImpl(data, attributes); - } - - public static StrSXP string(ImmutableList data, Attributes attributes) { - if (attributes.isEmpty()) { - return switch (data.size()) { - case 0 -> EMPTY_STRING; - case 1 -> string(data.getFirst()); - default -> new StrSXPImpl(data, attributes); - }; - } - return new StrSXPImpl(data, attributes); - } - - public static ComplexSXP complex(ImmutableList data, Attributes attributes) { - if (attributes.isEmpty()) { - return switch (data.size()) { - case 0 -> EMPTY_COMPLEX; - case 1 -> complex(data.getFirst()); - default -> new ComplexSXPImpl(data, attributes); - }; - } - return new ComplexSXPImpl(data, attributes); - } - - public static VecSXP vec(ImmutableList data, Attributes attributes) { - return new VecSXPImpl(data, attributes); - } - - public static ExprSXP expr(ImmutableList data, Attributes attributes) { - return new ExprSXPImpl(data, attributes); - } - - public static LglSXP logical(Collection data, Attributes attributes) { - return logical(ImmutableList.copyOf(data), attributes); - } - - public static IntSXP integer(Collection data, Attributes attributes) { - return integer(ImmutableIntArray.copyOf(data), attributes); - } - - public static RealSXP real(Collection data, Attributes attributes) { - return real(ImmutableDoubleArray.copyOf(data), attributes); - } - - public static StrSXP string(Collection data, Attributes attributes) { - return string(ImmutableList.copyOf(data), attributes); - } - - public static ComplexSXP complex(Collection data, Attributes attributes) { - return complex(ImmutableList.copyOf(data), attributes); - } - - public static VecSXP vec(Collection data, Attributes attributes) { - return vec(ImmutableList.copyOf(data), attributes); - } - - public static ExprSXP expr(Collection data, Attributes attributes) { - return expr(ImmutableList.copyOf(data), attributes); - } - - public static ListSXP list(SEXP... data) { - return list(Arrays.stream(data).map(TaggedElem::new).collect(Collectors.toList())); - } - - public static ListSXP list(TaggedElem... data) { - return list(ImmutableList.copyOf(data)); - } - - public static ListSXP list(ImmutableList data) { - return list(data, Attributes.NONE); - } - - public static ListSXP list(Collection data) { - return list(ImmutableList.copyOf(data)); - } - - public static ListSXP list(TaggedElem[] data, Attributes attributes) { - return list(ImmutableList.copyOf(data), attributes); - } - - public static ListSXP list(ImmutableList data, Attributes attributes) { - if (data.isEmpty() && !attributes.isEmpty()) { - throw new IllegalArgumentException("Cannot create a list with attributes but no elements"); - } - return data.isEmpty() ? NULL : new ListSXPImpl(data, attributes); - } - - public static ListSXP list(Collection data, Attributes attributes) { - return list(ImmutableList.copyOf(data), attributes); - } - - public static BCodeSXP bcode(Bc bc) { - return new BCodeSXPImpl(bc); - } - - public static CloSXP closure(ListSXP formals, SEXP body, EnvSXP environment) { - return closure(formals, body, environment, Attributes.NONE); - } - - public static CloSXP closure(ListSXP formals, SEXP body, EnvSXP environment, Attributes attributes) { - return new CloSXPImpl(formals, body, environment, attributes); - } - - public static RegEnvSXP environment(EnvSXP enclos) { - return new RegEnvSXP(enclos); - } - - public static LangSXP lang(SymOrLangSXP fun, ListSXP args) { - return lang(fun, args, Attributes.NONE); - } - - public static LangSXP lang(SymOrLangSXP fun, ListSXP args, Attributes attributes) { - return new LangSXPImpl(fun, args, attributes); - } - - public static RegSymSXP symbol(String name) { - if (name.equals("...")) { - return ELLIPSIS; - } - return new RegSymSXP(name); - } - // endregion - - static String toString(SEXP sexp, Object... data) { - var attributes = sexp.attributes(); - return "<" + sexp.type().name().toLowerCase() + " " + - Stream.of(data).map(Object::toString).collect(Collectors.joining(" ")) + - (attributes == null || attributes.isEmpty() ? "" : "\n | " + attributes.entrySet().stream().map(e -> e.getKey() + " = " + e.getValue()).collect(Collectors.joining("\n , "))) + - ">"; - } - - private SEXPs() { - } + public static IntSXP integer(int data, Attributes attributes) { + return integer(ImmutableIntArray.of(data), attributes); + } + + public static RealSXP real(double data, Attributes attributes) { + return real(ImmutableDoubleArray.of(data), attributes); + } + + public static StrSXP string(String data, Attributes attributes) { + return string(ImmutableList.of(data), attributes); + } + + public static ComplexSXP complex(Complex data, Attributes attributes) { + return complex(ImmutableList.of(data), attributes); + } + + public static LglSXP logical(ImmutableList data) { + return logical(data, Attributes.NONE); + } + + public static IntSXP integer(ImmutableIntArray data) { + return integer(data, Attributes.NONE); + } + + public static RealSXP real(ImmutableDoubleArray data) { + return real(data, Attributes.NONE); + } + + public static StrSXP string(ImmutableList data) { + return string(data, Attributes.NONE); + } + + public static ComplexSXP complex(ImmutableList data) { + return complex(data, Attributes.NONE); + } + + public static VecSXP vec(ImmutableList data) { + return vec(data, Attributes.NONE); + } + + public static ExprSXP expr(ImmutableList data) { + return expr(data, Attributes.NONE); + } + + public static LglSXP logical(Collection data) { + return logical(ImmutableList.copyOf(data), Attributes.NONE); + } + + public static IntSXP integer(Collection data) { + return integer(ImmutableIntArray.copyOf(data), Attributes.NONE); + } + + public static RealSXP real(Collection data) { + return real(ImmutableDoubleArray.copyOf(data), Attributes.NONE); + } + + public static StrSXP string(Collection data) { + return string(ImmutableList.copyOf(data), Attributes.NONE); + } + + public static ComplexSXP complex(Collection data) { + return complex(ImmutableList.copyOf(data), Attributes.NONE); + } + + public static VecSXP vec(Collection data) { + return vec(ImmutableList.copyOf(data), Attributes.NONE); + } + + public static ExprSXP expr(Collection data) { + return expr(ImmutableList.copyOf(data), Attributes.NONE); + } + + public static LglSXP logical(Logical[] data, Attributes attributes) { + return logical(ImmutableList.copyOf(data), attributes); + } + + public static IntSXP integer(int[] data, Attributes attributes) { + return integer(ImmutableIntArray.copyOf(data), attributes); + } + + public static RealSXP real(double[] data, Attributes attributes) { + return real(ImmutableDoubleArray.copyOf(data), attributes); + } + + public static StrSXP string(String[] data, Attributes attributes) { + return string(ImmutableList.copyOf(data), attributes); + } + + public static ComplexSXP complex(Complex[] data, Attributes attributes) { + return complex(ImmutableList.copyOf(data), attributes); + } + + public static VecSXP vec(SEXP[] data, Attributes attributes) { + return vec(ImmutableList.copyOf(data), attributes); + } + + public static ExprSXP expr(SEXP[] data, Attributes attributes) { + return expr(ImmutableList.copyOf(data), attributes); + } + + public static LglSXP logical(ImmutableList data, Attributes attributes) { + if (attributes.isEmpty()) { + return switch (data.size()) { + case 0 -> EMPTY_LOGICAL; + case 1 -> logical(data.getFirst()); + default -> new LglSXPImpl(data, attributes); + }; + } + return new LglSXPImpl(data, attributes); + } + + public static IntSXP integer(ImmutableIntArray data, Attributes attributes) { + if (attributes.isEmpty()) { + return switch (data.length()) { + case 0 -> EMPTY_INTEGER; + case 1 -> integer(data.get(0)); + default -> new IntSXPImpl(data, attributes); + }; + } + return new IntSXPImpl(data, attributes); + } + + public static RealSXP real(ImmutableDoubleArray data, Attributes attributes) { + if (attributes.isEmpty()) { + return switch (data.length()) { + case 0 -> EMPTY_REAL; + case 1 -> real(data.get(0)); + default -> new RealSXPImpl(data, attributes); + }; + } + return new RealSXPImpl(data, attributes); + } + + public static StrSXP string(ImmutableList data, Attributes attributes) { + if (attributes.isEmpty()) { + return switch (data.size()) { + case 0 -> EMPTY_STRING; + case 1 -> string(data.getFirst()); + default -> new StrSXPImpl(data, attributes); + }; + } + return new StrSXPImpl(data, attributes); + } + + public static ComplexSXP complex(ImmutableList data, Attributes attributes) { + if (attributes.isEmpty()) { + return switch (data.size()) { + case 0 -> EMPTY_COMPLEX; + case 1 -> complex(data.getFirst()); + default -> new ComplexSXPImpl(data, attributes); + }; + } + return new ComplexSXPImpl(data, attributes); + } + + public static VecSXP vec(ImmutableList data, Attributes attributes) { + return new VecSXPImpl(data, attributes); + } + + public static ExprSXP expr(ImmutableList data, Attributes attributes) { + return new ExprSXPImpl(data, attributes); + } + + public static LglSXP logical(Collection data, Attributes attributes) { + return logical(ImmutableList.copyOf(data), attributes); + } + + public static IntSXP integer(Collection data, Attributes attributes) { + return integer(ImmutableIntArray.copyOf(data), attributes); + } + + public static RealSXP real(Collection data, Attributes attributes) { + return real(ImmutableDoubleArray.copyOf(data), attributes); + } + + public static StrSXP string(Collection data, Attributes attributes) { + return string(ImmutableList.copyOf(data), attributes); + } + + public static ComplexSXP complex(Collection data, Attributes attributes) { + return complex(ImmutableList.copyOf(data), attributes); + } + + public static VecSXP vec(Collection data, Attributes attributes) { + return vec(ImmutableList.copyOf(data), attributes); + } + + public static ExprSXP expr(Collection data, Attributes attributes) { + return expr(ImmutableList.copyOf(data), attributes); + } + + public static ListSXP list(SEXP... data) { + return list(Arrays.stream(data).map(TaggedElem::new).collect(Collectors.toList())); + } + + public static ListSXP list(TaggedElem... data) { + return list(ImmutableList.copyOf(data)); + } + + public static ListSXP list(ImmutableList data) { + return list(data, Attributes.NONE); + } + + public static ListSXP list(Collection data) { + return list(ImmutableList.copyOf(data)); + } + + public static ListSXP list(TaggedElem[] data, Attributes attributes) { + return list(ImmutableList.copyOf(data), attributes); + } + + public static ListSXP list(ImmutableList data, Attributes attributes) { + if (data.isEmpty() && !attributes.isEmpty()) { + throw new IllegalArgumentException("Cannot create a list with attributes but no elements"); + } + return data.isEmpty() ? NULL : new ListSXPImpl(data, attributes); + } + + public static ListSXP list(Collection data, Attributes attributes) { + return list(ImmutableList.copyOf(data), attributes); + } + + public static BCodeSXP bcode(Bc bc) { + return new BCodeSXPImpl(bc); + } + + public static CloSXP closure(ListSXP formals, SEXP body, EnvSXP environment) { + return closure(formals, body, environment, Attributes.NONE); + } + + public static CloSXP closure( + ListSXP formals, SEXP body, EnvSXP environment, Attributes attributes) { + return new CloSXPImpl(formals, body, environment, attributes); + } + + public static RegEnvSXP environment(EnvSXP enclos) { + return new RegEnvSXP(enclos); + } + + public static LangSXP lang(SymOrLangSXP fun, ListSXP args) { + return lang(fun, args, Attributes.NONE); + } + + public static LangSXP lang(SymOrLangSXP fun, ListSXP args, Attributes attributes) { + return new LangSXPImpl(fun, args, attributes); + } + + public static RegSymSXP symbol(String name) { + if (name.equals("...")) { + return ELLIPSIS; + } + return new RegSymSXP(name); + } + + // endregion + + static String toString(SEXP sexp, Object... data) { + var attributes = sexp.attributes(); + return "<" + + sexp.type().name().toLowerCase() + + " " + + Stream.of(data).map(Object::toString).collect(Collectors.joining(" ")) + + (attributes == null || attributes.isEmpty() + ? "" + : "\n | " + + attributes.entrySet().stream() + .map(e -> e.getKey() + " = " + e.getValue()) + .collect(Collectors.joining("\n , "))) + + ">"; + } + + private SEXPs() {} } diff --git a/src/main/java/org/prlprg/sexp/SimpleScalarSXPImpl.java b/src/main/java/org/prlprg/sexp/SimpleScalarSXPImpl.java index 4f0801249..078d170ab 100644 --- a/src/main/java/org/prlprg/sexp/SimpleScalarSXPImpl.java +++ b/src/main/java/org/prlprg/sexp/SimpleScalarSXPImpl.java @@ -3,51 +3,50 @@ import com.google.common.base.Objects; import com.google.common.collect.Iterators; import com.google.common.collect.UnmodifiableIterator; - import javax.annotation.concurrent.Immutable; /** Class for representing a scalar SEXP of a primitive type with no attributes. */ @Immutable abstract class SimpleScalarSXPImpl { - final T data; - - protected SimpleScalarSXPImpl(T data) { - this.data = data; - } - - public UnmodifiableIterator iterator() { - return Iterators.forArray(data); - } - - public T get(int i) { - if (i != 0) { - throw new IndexOutOfBoundsException(); - } - return data; - } - - public int size() { - return 1; - } - - @Override - public String toString() { - return data.toString(); - } - - public Attributes attributes() { - return Attributes.NONE; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof SimpleScalarSXPImpl that)) return false; - return Objects.equal(data, that.data); - } - - @Override - public int hashCode() { - return Objects.hashCode(data); - } + final T data; + + protected SimpleScalarSXPImpl(T data) { + this.data = data; + } + + public UnmodifiableIterator iterator() { + return Iterators.forArray(data); + } + + public T get(int i) { + if (i != 0) { + throw new IndexOutOfBoundsException(); + } + return data; + } + + public int size() { + return 1; + } + + @Override + public String toString() { + return data.toString(); + } + + public Attributes attributes() { + return Attributes.NONE; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SimpleScalarSXPImpl that)) return false; + return Objects.equal(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hashCode(data); + } } diff --git a/src/main/java/org/prlprg/sexp/SpecialEnvSXP.java b/src/main/java/org/prlprg/sexp/SpecialEnvSXP.java index 3118449ac..11dd7b6a8 100644 --- a/src/main/java/org/prlprg/sexp/SpecialEnvSXP.java +++ b/src/main/java/org/prlprg/sexp/SpecialEnvSXP.java @@ -4,36 +4,36 @@ /** * Top-level environment (global, base, or empty). - *

- * We don't track its contents because 1) it may be shared across compilations and we compile SEXPs in a pure context - * (we don't want compiling one SEXP to produce side-effects which affect compiling others), 2) we want to compile SEXPs - * in a more general context than their exact top-level environment, so they can be reused. + * + *

We don't track its contents because 1) it may be shared across compilations and we compile + * SEXPs in a pure context (we don't want compiling one SEXP to produce side-effects which affect + * compiling others), 2) we want to compile SEXPs in a more general context than their exact + * top-level environment, so they can be reused. */ public final class SpecialEnvSXP implements EnvSXP { - private final String label; + private final String label; - SpecialEnvSXP(String label) { - this.label = label; - } + SpecialEnvSXP(String label) { + this.label = label; + } - @Override - public @Nullable EnvSXP knownParent() { - return null; - } + @Override + public @Nullable EnvSXP knownParent() { + return null; + } - @Nullable - @Override - public SEXP get(String name) { - return null; - } + @Nullable @Override + public SEXP get(String name) { + return null; + } - @Override - public String toString() { - return "<" + label + ">"; - } + @Override + public String toString() { + return "<" + label + ">"; + } - @Override - public EnvSXP withAttributes(Attributes attributes) { - throw new UnsupportedOperationException("Cannot set attributes on special environment"); - } + @Override + public EnvSXP withAttributes(Attributes attributes) { + throw new UnsupportedOperationException("Cannot set attributes on special environment"); + } } diff --git a/src/main/java/org/prlprg/sexp/SpecialSymSXP.java b/src/main/java/org/prlprg/sexp/SpecialSymSXP.java index 45abc3f05..728107c93 100644 --- a/src/main/java/org/prlprg/sexp/SpecialSymSXP.java +++ b/src/main/java/org/prlprg/sexp/SpecialSymSXP.java @@ -2,14 +2,14 @@ /** Unique symbol (equality by identity). */ public final class SpecialSymSXP implements SymSXP { - private final String label; + private final String label; - SpecialSymSXP(String label) { - this.label = label; - } + SpecialSymSXP(String label) { + this.label = label; + } - @Override - public String toString() { - return "<" + label + ">"; - } + @Override + public String toString() { + return "<" + label + ">"; + } } diff --git a/src/main/java/org/prlprg/sexp/StrOrRegSymSXP.java b/src/main/java/org/prlprg/sexp/StrOrRegSymSXP.java index a8d55198d..db99ca72d 100644 --- a/src/main/java/org/prlprg/sexp/StrOrRegSymSXP.java +++ b/src/main/java/org/prlprg/sexp/StrOrRegSymSXP.java @@ -4,10 +4,10 @@ @Immutable public sealed interface StrOrRegSymSXP extends SEXP permits StrSXP, RegSymSXP { - /** This is a weird method. If the SEXP is a symbol, it returns the name. - * If the SEXP is a string, it returns the first element or (if empty) empty string. - * It's used in the compiler in CreateTag to convert a symbol or string into a tag. - * Maybe it should be moved into the compiler directly. - */ - String reifyString(); + /** + * This is a weird method. If the SEXP is a symbol, it returns the name. If the SEXP is a string, + * it returns the first element or (if empty) empty string. It's used in the compiler in CreateTag + * to convert a symbol or string into a tag. Maybe it should be moved into the compiler directly. + */ + String reifyString(); } diff --git a/src/main/java/org/prlprg/sexp/StrSXP.java b/src/main/java/org/prlprg/sexp/StrSXP.java index d0a1a4968..af1ed27b2 100644 --- a/src/main/java/org/prlprg/sexp/StrSXP.java +++ b/src/main/java/org/prlprg/sexp/StrSXP.java @@ -4,96 +4,95 @@ import com.google.common.collect.UnmodifiableIterator; import com.google.common.escape.Escaper; import com.google.common.escape.Escapers; -import org.prlprg.primitive.Constants; - import javax.annotation.concurrent.Immutable; +import org.prlprg.primitive.Constants; @Immutable public sealed interface StrSXP extends VectorSXP, StrOrRegSymSXP { - @Override - default SEXPType type() { - return SEXPType.STR; - } + @Override + default SEXPType type() { + return SEXPType.STR; + } - @Override - default String reifyString() { - return isEmpty() ? "" : get(0); - } + @Override + default String reifyString() { + return isEmpty() ? "" : get(0); + } - @Override Attributes attributes(); + @Override + Attributes attributes(); - @Override - StrSXP withAttributes(Attributes attributes); + @Override + StrSXP withAttributes(Attributes attributes); } record StrSXPImpl(ImmutableList data, @Override Attributes attributes) implements StrSXP { - @Override - public UnmodifiableIterator iterator() { - return data.iterator(); - } - - @Override - public String get(int i) { - return data.get(i); - } - - @Override - public int size() { - return data.size(); - } - - @Override - public String toString() { - return VectorSXPs.toString(this, data.stream().map(StrSXPs::quoteString)); - } - - @Override - public StrSXP withAttributes(Attributes attributes) { - return SEXPs.string(data, attributes); - } + @Override + public UnmodifiableIterator iterator() { + return data.iterator(); + } + + @Override + public String get(int i) { + return data.get(i); + } + + @Override + public int size() { + return data.size(); + } + + @Override + public String toString() { + return VectorSXPs.toString(this, data.stream().map(StrSXPs::quoteString)); + } + + @Override + public StrSXP withAttributes(Attributes attributes) { + return SEXPs.string(data, attributes); + } } final class SimpleStrSXPImpl extends SimpleScalarSXPImpl implements StrSXP { - SimpleStrSXPImpl(String data) { - super(data); - } - - @Override - public String toString() { - return StrSXPs.quoteString(data); - } - - @Override - public StrSXP withAttributes(Attributes attributes) { - return SEXPs.string(data, attributes); - } + SimpleStrSXPImpl(String data) { + super(data); + } + + @Override + public String toString() { + return StrSXPs.quoteString(data); + } + + @Override + public StrSXP withAttributes(Attributes attributes) { + return SEXPs.string(data, attributes); + } } final class EmptyStrSXPImpl extends EmptyVectorSXPImpl implements StrSXP { - static final EmptyStrSXPImpl INSTANCE = new EmptyStrSXPImpl(); + static final EmptyStrSXPImpl INSTANCE = new EmptyStrSXPImpl(); - private EmptyStrSXPImpl() { - } + private EmptyStrSXPImpl() {} - @Override - public StrSXP withAttributes(Attributes attributes) { - return SEXPs.string(ImmutableList.of(), attributes); - } + @Override + public StrSXP withAttributes(Attributes attributes) { + return SEXPs.string(ImmutableList.of(), attributes); + } } final class StrSXPs { - private static final Escaper rEscaper = Escapers.builder() - .addEscape('"', "\\\"") - .addEscape('\\', "\\\\") - .addEscape('\n', "\\n") - .addEscape('\r', "\\r") - .addEscape('\t', "\\t") - .build(); - - static String quoteString(String s) { - return Constants.isNaString(s) ? "NA" : "\"" + rEscaper.escape(s) + "\""; - } - - private StrSXPs() { - } + private static final Escaper rEscaper = + Escapers.builder() + .addEscape('"', "\\\"") + .addEscape('\\', "\\\\") + .addEscape('\n', "\\n") + .addEscape('\r', "\\r") + .addEscape('\t', "\\t") + .build(); + + static String quoteString(String s) { + return Constants.isNaString(s) ? "NA" : "\"" + rEscaper.escape(s) + "\""; + } + + private StrSXPs() {} } diff --git a/src/main/java/org/prlprg/sexp/SymOrLangSXP.java b/src/main/java/org/prlprg/sexp/SymOrLangSXP.java index 5f0d75249..47459ff0f 100644 --- a/src/main/java/org/prlprg/sexp/SymOrLangSXP.java +++ b/src/main/java/org/prlprg/sexp/SymOrLangSXP.java @@ -3,5 +3,4 @@ import javax.annotation.concurrent.Immutable; @Immutable -public sealed interface SymOrLangSXP extends SEXP permits SymSXP, LangSXP { -} +public sealed interface SymOrLangSXP extends SEXP permits SymSXP, LangSXP {} diff --git a/src/main/java/org/prlprg/sexp/SymSXP.java b/src/main/java/org/prlprg/sexp/SymSXP.java index 9e8d776db..b83d37a41 100644 --- a/src/main/java/org/prlprg/sexp/SymSXP.java +++ b/src/main/java/org/prlprg/sexp/SymSXP.java @@ -4,23 +4,23 @@ @Immutable public sealed interface SymSXP extends SymOrLangSXP permits RegSymSXP, SpecialSymSXP { - @Override - default SEXPType type() { - return SEXPType.SYM; - } + @Override + default SEXPType type() { + return SEXPType.SYM; + } - /** Whether this is the ellipsis symbol. */ - default boolean isEllipsis() { - return this == SEXPs.ELLIPSIS; - } + /** Whether this is the ellipsis symbol. */ + default boolean isEllipsis() { + return this == SEXPs.ELLIPSIS; + } - /** Whether this is the missing symbol. */ - default boolean isMissing() { - return this == SEXPs.MISSING_ARG; - } + /** Whether this is the missing symbol. */ + default boolean isMissing() { + return this == SEXPs.MISSING_ARG; + } - /** Whether this is the unbound value symbol. */ - default boolean isUnbound() { - return this == SEXPs.UNBOUND_VALUE; - } + /** Whether this is the unbound value symbol. */ + default boolean isUnbound() { + return this == SEXPs.UNBOUND_VALUE; + } } diff --git a/src/main/java/org/prlprg/sexp/TaggedElem.java b/src/main/java/org/prlprg/sexp/TaggedElem.java index 2c7cc87e0..3f35f205b 100644 --- a/src/main/java/org/prlprg/sexp/TaggedElem.java +++ b/src/main/java/org/prlprg/sexp/TaggedElem.java @@ -3,20 +3,20 @@ import javax.annotation.Nullable; public record TaggedElem(@Nullable String tag, SEXP value) { - public TaggedElem { - if (tag != null && tag.isEmpty()) { - throw new IllegalArgumentException("Tag cannot be empty, should be null instead"); - } + public TaggedElem { + if (tag != null && tag.isEmpty()) { + throw new IllegalArgumentException("Tag cannot be empty, should be null instead"); } + } - public TaggedElem(SEXP value) { - this(null, value); - } + public TaggedElem(SEXP value) { + this(null, value); + } - @Override - public String toString() { - return tag == null ? value.toString() : - value == SEXPs.MISSING_ARG ? tag + "=" : - tag + "=" + value; - } + @Override + public String toString() { + return tag == null + ? value.toString() + : value == SEXPs.MISSING_ARG ? tag + "=" : tag + "=" + value; + } } diff --git a/src/main/java/org/prlprg/sexp/VecSXP.java b/src/main/java/org/prlprg/sexp/VecSXP.java index cd5dc24c8..690cda4f6 100644 --- a/src/main/java/org/prlprg/sexp/VecSXP.java +++ b/src/main/java/org/prlprg/sexp/VecSXP.java @@ -4,40 +4,41 @@ import com.google.common.collect.UnmodifiableIterator; public sealed interface VecSXP extends VectorSXP { - @Override - default SEXPType type() { - return SEXPType.VEC; - } + @Override + default SEXPType type() { + return SEXPType.VEC; + } - @Override Attributes attributes(); + @Override + Attributes attributes(); - @Override - VecSXP withAttributes(Attributes attributes); + @Override + VecSXP withAttributes(Attributes attributes); } record VecSXPImpl(ImmutableList data, @Override Attributes attributes) implements VecSXP { - @Override - public UnmodifiableIterator iterator() { - return data.iterator(); - } - - @Override - public SEXP get(int i) { - return data.get(i); - } - - @Override - public int size() { - return data.size(); - } - - @Override - public String toString() { - return VectorSXPs.toString(this, data.stream()); - } - - @Override - public VecSXP withAttributes(Attributes attributes) { - return SEXPs.vec(data, attributes); - } + @Override + public UnmodifiableIterator iterator() { + return data.iterator(); + } + + @Override + public SEXP get(int i) { + return data.get(i); + } + + @Override + public int size() { + return data.size(); + } + + @Override + public String toString() { + return VectorSXPs.toString(this, data.stream()); + } + + @Override + public VecSXP withAttributes(Attributes attributes) { + return SEXPs.vec(data, attributes); + } } diff --git a/src/main/java/org/prlprg/sexp/VectorSXP.java b/src/main/java/org/prlprg/sexp/VectorSXP.java index eca55d0ee..9aa5eac05 100644 --- a/src/main/java/org/prlprg/sexp/VectorSXP.java +++ b/src/main/java/org/prlprg/sexp/VectorSXP.java @@ -2,32 +2,33 @@ import java.util.stream.BaseStream; -public sealed interface VectorSXP extends ListOrVectorSXP permits ComplexSXP, ExprSXP, IntSXP, LglSXP, RealSXP, StrSXP, VecSXP, EmptyVectorSXPImpl { - @Override Attributes attributes(); +public sealed interface VectorSXP extends ListOrVectorSXP + permits ComplexSXP, ExprSXP, IntSXP, LglSXP, RealSXP, StrSXP, VecSXP, EmptyVectorSXPImpl { + @Override + Attributes attributes(); - @Override - VectorSXP withAttributes(Attributes attributes); + @Override + VectorSXP withAttributes(Attributes attributes); } final class VectorSXPs { - static final int VECTOR_TRUNCATE_SIZE = 100; + static final int VECTOR_TRUNCATE_SIZE = 100; - static String toString(SEXP sexp, BaseStream data) { - var dataString = new StringBuilder(); - var dataIter = data.iterator(); - while (dataIter.hasNext()) { - dataString.append(dataIter.next()); - if (dataIter.hasNext()) { - dataString.append(","); - if (dataString.length() >= VECTOR_TRUNCATE_SIZE) { - dataString.append("..."); - break; - } - } + static String toString(SEXP sexp, BaseStream data) { + var dataString = new StringBuilder(); + var dataIter = data.iterator(); + while (dataIter.hasNext()) { + dataString.append(dataIter.next()); + if (dataIter.hasNext()) { + dataString.append(","); + if (dataString.length() >= VECTOR_TRUNCATE_SIZE) { + dataString.append("..."); + break; } - return SEXPs.toString(sexp, dataString); + } } + return SEXPs.toString(sexp, dataString); + } - private VectorSXPs() { - } + private VectorSXPs() {} } diff --git a/src/main/java/org/prlprg/sexp/package-info.java b/src/main/java/org/prlprg/sexp/package-info.java index 7745eb418..81d89a94c 100644 --- a/src/main/java/org/prlprg/sexp/package-info.java +++ b/src/main/java/org/prlprg/sexp/package-info.java @@ -3,7 +3,6 @@ @ReturnTypesAreNonNullByDefault package org.prlprg.sexp; +import javax.annotation.ParametersAreNonnullByDefault; import org.prlprg.util.FieldsAreNonNullByDefault; import org.prlprg.util.ReturnTypesAreNonNullByDefault; - -import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/org/prlprg/util/Either.java b/src/main/java/org/prlprg/util/Either.java index 30a57921d..76bb87dd7 100644 --- a/src/main/java/org/prlprg/util/Either.java +++ b/src/main/java/org/prlprg/util/Either.java @@ -1,11 +1,11 @@ package org.prlprg.util; public sealed interface Either permits Left, Right { - static Either left(L left) { - return new Left<>(left); - } + static Either left(L left) { + return new Left<>(left); + } - static Either right(R right) { - return new Right<>(right); - } + static Either right(R right) { + return new Right<>(right); + } } diff --git a/src/main/java/org/prlprg/util/EmptyIterator.java b/src/main/java/org/prlprg/util/EmptyIterator.java index 4611b00d8..3b9abd8a1 100644 --- a/src/main/java/org/prlprg/util/EmptyIterator.java +++ b/src/main/java/org/prlprg/util/EmptyIterator.java @@ -4,13 +4,13 @@ /** An iterator which has no elements. */ public record EmptyIterator() implements Iterator { - @Override - public boolean hasNext() { - return false; - } + @Override + public boolean hasNext() { + return false; + } - @Override - public T next() { - throw new UnsupportedOperationException("EmptyIterator has no elements"); - } + @Override + public T next() { + throw new UnsupportedOperationException("EmptyIterator has no elements"); + } } diff --git a/src/main/java/org/prlprg/util/FieldsAreNonNullByDefault.java b/src/main/java/org/prlprg/util/FieldsAreNonNullByDefault.java index 96fb63282..68b8ec953 100644 --- a/src/main/java/org/prlprg/util/FieldsAreNonNullByDefault.java +++ b/src/main/java/org/prlprg/util/FieldsAreNonNullByDefault.java @@ -1,13 +1,12 @@ package org.prlprg.util; -import javax.annotation.Nonnull; -import javax.annotation.meta.TypeQualifierDefault; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import javax.annotation.Nonnull; +import javax.annotation.meta.TypeQualifierDefault; @Nonnull @TypeQualifierDefault(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) -public @interface FieldsAreNonNullByDefault { -} +public @interface FieldsAreNonNullByDefault {} diff --git a/src/main/java/org/prlprg/util/IO.java b/src/main/java/org/prlprg/util/IO.java index 35c31f438..42f2a94bd 100644 --- a/src/main/java/org/prlprg/util/IO.java +++ b/src/main/java/org/prlprg/util/IO.java @@ -6,23 +6,22 @@ import java.util.zip.GZIPInputStream; public final class IO { - private IO() { - } + private IO() {} - public static InputStream maybeDecompress(InputStream input) throws IOException { - final PushbackInputStream pb = new PushbackInputStream(input, 2); - byte[] buff = pb.readNBytes(2); - pb.unread(buff); - if (buff.length != 2) { - return pb; - } + public static InputStream maybeDecompress(InputStream input) throws IOException { + final PushbackInputStream pb = new PushbackInputStream(input, 2); + byte[] buff = pb.readNBytes(2); + pb.unread(buff); + if (buff.length != 2) { + return pb; + } - int magick = (buff[0] & 0xFF) | ((buff[1] & 0xFF) << 8); + int magick = (buff[0] & 0xFF) | ((buff[1] & 0xFF) << 8); - if (magick == GZIPInputStream.GZIP_MAGIC) { - return new GZIPInputStream(pb); - } else { - return pb; - } + if (magick == GZIPInputStream.GZIP_MAGIC) { + return new GZIPInputStream(pb); + } else { + return pb; } + } } diff --git a/src/main/java/org/prlprg/util/Left.java b/src/main/java/org/prlprg/util/Left.java index 5f35efcfd..7a542dfad 100644 --- a/src/main/java/org/prlprg/util/Left.java +++ b/src/main/java/org/prlprg/util/Left.java @@ -1,4 +1,3 @@ package org.prlprg.util; -public record Left(L left) implements Either { -} +public record Left(L left) implements Either {} diff --git a/src/main/java/org/prlprg/util/NotImplementedException.java b/src/main/java/org/prlprg/util/NotImplementedException.java index bba0cddc5..c82879ff5 100644 --- a/src/main/java/org/prlprg/util/NotImplementedException.java +++ b/src/main/java/org/prlprg/util/NotImplementedException.java @@ -1,11 +1,11 @@ package org.prlprg.util; public class NotImplementedException extends UnsupportedOperationException { - public NotImplementedException(Object switchCase) { - super("Sorry, this case is not yet implemented: " + switchCase); - } + public NotImplementedException(Object switchCase) { + super("Sorry, this case is not yet implemented: " + switchCase); + } - public NotImplementedException() { - super("Sorry, this code is not yet implemented"); - } + public NotImplementedException() { + super("Sorry, this code is not yet implemented"); + } } diff --git a/src/main/java/org/prlprg/util/Nothing.java b/src/main/java/org/prlprg/util/Nothing.java index b1106c0ae..83b4ac4af 100644 --- a/src/main/java/org/prlprg/util/Nothing.java +++ b/src/main/java/org/prlprg/util/Nothing.java @@ -1,12 +1,13 @@ package org.prlprg.util; -/** A type with no instances, it can't be constructed. - *

- * Useful in generics and other compound types, e.g. `@Nullable Nothing` is only `null`, and a function which returns - * `Nothing` is guaranteed not to return +/** + * A type with no instances, it can't be constructed. + * + *

Useful in generics and other compound types, e.g. `@Nullable Nothing` is only `null`, and a + * function which returns `Nothing` is guaranteed not to return */ public final class Nothing { - private Nothing() { - throw new UnsupportedOperationException("Nothing can't be constructed"); - } + private Nothing() { + throw new UnsupportedOperationException("Nothing can't be constructed"); + } } diff --git a/src/main/java/org/prlprg/util/PackagePrivate.java b/src/main/java/org/prlprg/util/PackagePrivate.java index b0bfe8749..747c21222 100644 --- a/src/main/java/org/prlprg/util/PackagePrivate.java +++ b/src/main/java/org/prlprg/util/PackagePrivate.java @@ -1,8 +1,7 @@ package org.prlprg.util; /** - * Explicitly denotes that a member is package-private. - * Otherwise, pmd will warn in case you forgot to add a visibility. + * Explicitly denotes that a member is package-private. Otherwise, pmd will warn in case you forgot + * to add a visibility. */ -public @interface PackagePrivate { -} +public @interface PackagePrivate {} diff --git a/src/main/java/org/prlprg/util/Pair.java b/src/main/java/org/prlprg/util/Pair.java index 72a3d593e..c3620b6c5 100644 --- a/src/main/java/org/prlprg/util/Pair.java +++ b/src/main/java/org/prlprg/util/Pair.java @@ -1,7 +1,7 @@ package org.prlprg.util; public record Pair(A a, B b) { - public static Pair of(A a, B b) { - return new Pair<>(a, b); - } + public static Pair of(A a, B b) { + return new Pair<>(a, b); + } } diff --git a/src/main/java/org/prlprg/util/ReturnTypesAreNonNullByDefault.java b/src/main/java/org/prlprg/util/ReturnTypesAreNonNullByDefault.java index b4f7450ef..78959d0fc 100644 --- a/src/main/java/org/prlprg/util/ReturnTypesAreNonNullByDefault.java +++ b/src/main/java/org/prlprg/util/ReturnTypesAreNonNullByDefault.java @@ -1,13 +1,12 @@ package org.prlprg.util; -import javax.annotation.Nonnull; -import javax.annotation.meta.TypeQualifierDefault; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import javax.annotation.Nonnull; +import javax.annotation.meta.TypeQualifierDefault; @Nonnull @TypeQualifierDefault(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) -public @interface ReturnTypesAreNonNullByDefault { -} +public @interface ReturnTypesAreNonNullByDefault {} diff --git a/src/main/java/org/prlprg/util/Right.java b/src/main/java/org/prlprg/util/Right.java index 15dc87177..65b0a4b32 100644 --- a/src/main/java/org/prlprg/util/Right.java +++ b/src/main/java/org/prlprg/util/Right.java @@ -1,4 +1,3 @@ package org.prlprg.util; -public record Right(R right) implements Either { -} +public record Right(R right) implements Either {} diff --git a/src/test/java/org/prlprg/DumbTests.java b/src/test/java/org/prlprg/DumbTests.java index 53e70e714..dbd0be165 100644 --- a/src/test/java/org/prlprg/DumbTests.java +++ b/src/test/java/org/prlprg/DumbTests.java @@ -1,31 +1,26 @@ package org.prlprg; +import static org.junit.jupiter.api.Assertions.assertEquals; + import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * Unit test for simple App. - */ +/** Unit test for simple App. */ public class DumbTests { - @Test - @DisplayName("2 + 2 = 4") - void addsTwoNumbers() { - assertEquals(4, 2 + 2, "2 + 2 should equal 4"); - } + @Test + @DisplayName("2 + 2 = 4") + void addsTwoNumbers() { + assertEquals(4, 2 + 2, "2 + 2 should equal 4"); + } - @ParameterizedTest(name = "{0} + {1} = {2}") - @CsvSource({ - "0, 1, 1", - "1, 2, 3", - "49, 51, 100", - "1, 100, 101" - }) - void add(int first, int second, int expectedResult) { - assertEquals(expectedResult, first + second, - () -> first + " + " + second + " should equal " + expectedResult); - } + @ParameterizedTest(name = "{0} + {1} = {2}") + @CsvSource({"0, 1, 1", "1, 2, 3", "49, 51, 100", "1, 100, 101"}) + void add(int first, int second, int expectedResult) { + assertEquals( + expectedResult, + first + second, + () -> first + " + " + second + " should equal " + expectedResult); + } } diff --git a/src/test/java/org/prlprg/bc/BcTests.java b/src/test/java/org/prlprg/bc/BcTests.java index 003bf916a..a9b469f67 100644 --- a/src/test/java/org/prlprg/bc/BcTests.java +++ b/src/test/java/org/prlprg/bc/BcTests.java @@ -1,44 +1,44 @@ package org.prlprg.bc; +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.prlprg.sexp.SEXPs; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; - public class BcTests { - @Test - @DisplayName("Create bytecode array") - void createBcArray() { - var bcBuilder = new Bc.Builder(); - var ast = SEXPs.lang(SEXPs.symbol("+"), SEXPs.list(SEXPs.integer(1), SEXPs.integer(2))); - // It doesn't make sense to implement SEXP#clone because you'd just reuse the SEXP since they are immutable. - // Only SEXP.withXYZ(...) methods. - var astClone = SEXPs.lang(SEXPs.symbol("+"), SEXPs.list(SEXPs.integer(1), SEXPs.integer(2))); - bcBuilder.addAllInstrs(List.of( - new BcInstr.LdConst(bcBuilder.addConst(SEXPs.integer(1))), - new BcInstr.LdConst(bcBuilder.addConst(SEXPs.integer(2))), - new BcInstr.Add(bcBuilder.addConst(ast)), - new BcInstr.Return() - )); - var bc = bcBuilder.build(); - assertEquals(4, bc.code().size()); - assertEquals(BcOp.LDCONST, bc.code().get(0).op()); - assertEquals(BcOp.LDCONST, bc.code().get(1).op()); - assertEquals(BcOp.ADD, bc.code().get(2).op()); - assertEquals(BcOp.RETURN, bc.code().get(3).op()); - var consts = bc.consts().stream().toList(); - assertEquals(3, consts.size()); - assertEquals(SEXPs.integer(1), consts.get(0)); - assertEquals(SEXPs.integer(2), consts.get(1)); - assertEquals(astClone, consts.get(2)); - assertEquals(0, ((BcInstr.LdConst)bc.code().get(0)).constant().idx); - assertEquals(bc.consts(), ((BcInstr.LdConst)bc.code().get(0)).constant().pool); - assertEquals(1, ((BcInstr.LdConst)bc.code().get(1)).constant().idx); - assertEquals(bc.consts(), ((BcInstr.LdConst)bc.code().get(1)).constant().pool); - assertEquals(2, ((BcInstr.Add)bc.code().get(2)).ast().idx); - assertEquals(bc.consts(), ((BcInstr.Add)bc.code().get(2)).ast().pool); - } + @Test + @DisplayName("Create bytecode array") + void createBcArray() { + var bcBuilder = new Bc.Builder(); + var ast = SEXPs.lang(SEXPs.symbol("+"), SEXPs.list(SEXPs.integer(1), SEXPs.integer(2))); + // It doesn't make sense to implement SEXP#clone because you'd just reuse the SEXP since + // they are immutable. + // Only SEXP.withXYZ(...) methods. + var astClone = SEXPs.lang(SEXPs.symbol("+"), SEXPs.list(SEXPs.integer(1), SEXPs.integer(2))); + bcBuilder.addAllInstrs( + List.of( + new BcInstr.LdConst(bcBuilder.addConst(SEXPs.integer(1))), + new BcInstr.LdConst(bcBuilder.addConst(SEXPs.integer(2))), + new BcInstr.Add(bcBuilder.addConst(ast)), + new BcInstr.Return())); + var bc = bcBuilder.build(); + assertEquals(4, bc.code().size()); + assertEquals(BcOp.LDCONST, bc.code().get(0).op()); + assertEquals(BcOp.LDCONST, bc.code().get(1).op()); + assertEquals(BcOp.ADD, bc.code().get(2).op()); + assertEquals(BcOp.RETURN, bc.code().get(3).op()); + var consts = bc.consts().stream().toList(); + assertEquals(3, consts.size()); + assertEquals(SEXPs.integer(1), consts.get(0)); + assertEquals(SEXPs.integer(2), consts.get(1)); + assertEquals(astClone, consts.get(2)); + assertEquals(0, ((BcInstr.LdConst) bc.code().get(0)).constant().idx); + assertEquals(bc.consts(), ((BcInstr.LdConst) bc.code().get(0)).constant().pool); + assertEquals(1, ((BcInstr.LdConst) bc.code().get(1)).constant().idx); + assertEquals(bc.consts(), ((BcInstr.LdConst) bc.code().get(1)).constant().pool); + assertEquals(2, ((BcInstr.Add) bc.code().get(2)).ast().idx); + assertEquals(bc.consts(), ((BcInstr.Add) bc.code().get(2)).ast().pool); + } } diff --git a/src/test/java/org/prlprg/bc/CompilerTest.java b/src/test/java/org/prlprg/bc/CompilerTest.java index 132b91cdb..0a85881ef 100644 --- a/src/test/java/org/prlprg/bc/CompilerTest.java +++ b/src/test/java/org/prlprg/bc/CompilerTest.java @@ -6,13 +6,11 @@ import org.prlprg.sexp.CloSXP; import org.prlprg.util.Tests; -import java.util.Objects; - public class CompilerTest implements Tests { - @Test - public void testBasic() throws Exception { - var source = (CloSXP) RDSReader.readStream(getResourceAsStream("f1.rds")); - var bc = Compiler.compileFun(source); - System.out.println(bc); - } + @Test + public void testBasic() throws Exception { + var source = (CloSXP) RDSReader.readStream(getResourceAsStream("f1.rds")); + var bc = Compiler.compileFun(source); + System.out.println(bc); + } } diff --git a/src/test/java/org/prlprg/rds/RDSReaderTest.java b/src/test/java/org/prlprg/rds/RDSReaderTest.java index 9aa18efdf..45aa3eed0 100644 --- a/src/test/java/org/prlprg/rds/RDSReaderTest.java +++ b/src/test/java/org/prlprg/rds/RDSReaderTest.java @@ -3,123 +3,120 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; +import java.util.Objects; import org.junit.jupiter.api.Test; - import org.prlprg.RPlatform; import org.prlprg.primitive.Constants; import org.prlprg.primitive.Logical; import org.prlprg.sexp.*; import org.prlprg.util.Tests; -import java.util.Objects; - public class RDSReaderTest implements Tests { - @Test - public void testInts() throws Exception { - var sexp = RDSReader.readStream(getResourceAsStream("ints.rds")); - if (sexp instanceof IntSXP ints) { - assertEquals(6, ints.size()); - assertEquals(-RPlatform.INT_MAX, ints.get(0)); - assertEquals(-1, ints.get(1)); - assertEquals(0, ints.get(2)); - assertEquals(Constants.NA_INT, ints.get(3)); - assertEquals(1, ints.get(4)); - assertEquals(RPlatform.INT_MAX, ints.get(5)); - } else { - fail("Expected IntSXP"); - } + @Test + public void testInts() throws Exception { + var sexp = RDSReader.readStream(getResourceAsStream("ints.rds")); + if (sexp instanceof IntSXP ints) { + assertEquals(6, ints.size()); + assertEquals(-RPlatform.INT_MAX, ints.get(0)); + assertEquals(-1, ints.get(1)); + assertEquals(0, ints.get(2)); + assertEquals(Constants.NA_INT, ints.get(3)); + assertEquals(1, ints.get(4)); + assertEquals(RPlatform.INT_MAX, ints.get(5)); + } else { + fail("Expected IntSXP"); } - - @Test - public void testLgls() throws Exception { - var sexp = RDSReader.readStream(getResourceAsStream("lgls.rds")); - if (sexp instanceof LglSXP logs) { - assertEquals(3, logs.size()); - assertEquals(Logical.TRUE, logs.get(0)); - assertEquals(Logical.FALSE, logs.get(1)); - assertEquals(Logical.NA, logs.get(2)); - } else { - fail("Expected LglSXP"); - } + } + + @Test + public void testLgls() throws Exception { + var sexp = RDSReader.readStream(getResourceAsStream("lgls.rds")); + if (sexp instanceof LglSXP logs) { + assertEquals(3, logs.size()); + assertEquals(Logical.TRUE, logs.get(0)); + assertEquals(Logical.FALSE, logs.get(1)); + assertEquals(Logical.NA, logs.get(2)); + } else { + fail("Expected LglSXP"); } - - @Test - public void testReals() throws Exception { - var sexp = RDSReader.readStream(getResourceAsStream("reals.rds")); - if (sexp instanceof RealSXP reals) { - assertEquals(6, reals.size()); - // TODO: R double min are different - should be all be based on RPlatform - // assertEquals(nums[0], Double.MIN_VALUE); - assertEquals(-1.0, reals.get(1)); - assertEquals(.0, reals.get(2)); - assertEquals(Constants.NA_REAL, reals.get(3)); - assertEquals(1.0, reals.get(4)); - assertEquals(Double.MAX_VALUE, reals.get(5)); - } else { - fail("Expected RealSXP"); - } + } + + @Test + public void testReals() throws Exception { + var sexp = RDSReader.readStream(getResourceAsStream("reals.rds")); + if (sexp instanceof RealSXP reals) { + assertEquals(6, reals.size()); + // TODO: R double min are different - should be all be based on RPlatform + // assertEquals(nums[0], Double.MIN_VALUE); + assertEquals(-1.0, reals.get(1)); + assertEquals(.0, reals.get(2)); + assertEquals(Constants.NA_REAL, reals.get(3)); + assertEquals(1.0, reals.get(4)); + assertEquals(Double.MAX_VALUE, reals.get(5)); + } else { + fail("Expected RealSXP"); } - - @Test - public void testFakeList() throws Exception { - var sexp = RDSReader.readStream(getResourceAsStream("list.rds")); - if (sexp instanceof ListSXP list) { - assertEquals(3, list.size()); - assertEquals(new TaggedElem("a", SEXPs.integer(4)), list.get(0)); - assertEquals(new TaggedElem(SEXPs.real(3.5)), list.get(1)); - assertEquals(new TaggedElem("c", SEXPs.logical(Logical.TRUE)), list.get(2)); - } else if (sexp instanceof VecSXP fakeList) { - // ???: R reads and writes the lists as a vector with the "names" attr? - assertEquals(3, fakeList.size()); - assertEquals(SEXPs.integer(4), fakeList.get(0)); - assertEquals(SEXPs.real(3.5), fakeList.get(1)); - assertEquals(SEXPs.logical(Logical.TRUE), fakeList.get(2)); - var names = Objects.requireNonNull(fakeList.attributes()).get("names"); - if (names instanceof StrSXP namesStrs) { - assertEquals(3, namesStrs.size()); - assertEquals("a", namesStrs.get(0)); - assertEquals("", namesStrs.get(1)); - assertEquals("c", namesStrs.get(2)); - } else { - fail("Expected names attribute in \"fake list\" VecSXP"); - } - } else { - fail("Expected ListSXP or \"fake list\" VecSXP with names"); - } + } + + @Test + public void testFakeList() throws Exception { + var sexp = RDSReader.readStream(getResourceAsStream("list.rds")); + if (sexp instanceof ListSXP list) { + assertEquals(3, list.size()); + assertEquals(new TaggedElem("a", SEXPs.integer(4)), list.get(0)); + assertEquals(new TaggedElem(SEXPs.real(3.5)), list.get(1)); + assertEquals(new TaggedElem("c", SEXPs.logical(Logical.TRUE)), list.get(2)); + } else if (sexp instanceof VecSXP fakeList) { + // ???: R reads and writes the lists as a vector with the "names" attr? + assertEquals(3, fakeList.size()); + assertEquals(SEXPs.integer(4), fakeList.get(0)); + assertEquals(SEXPs.real(3.5), fakeList.get(1)); + assertEquals(SEXPs.logical(Logical.TRUE), fakeList.get(2)); + var names = Objects.requireNonNull(fakeList.attributes()).get("names"); + if (names instanceof StrSXP namesStrs) { + assertEquals(3, namesStrs.size()); + assertEquals("a", namesStrs.get(0)); + assertEquals("", namesStrs.get(1)); + assertEquals("c", namesStrs.get(2)); + } else { + fail("Expected names attribute in \"fake list\" VecSXP"); + } + } else { + fail("Expected ListSXP or \"fake list\" VecSXP with names"); } + } - @Test - public void testList2() throws Exception { - var sexp = RDSReader.readStream(getResourceAsStream("list2.rds")); - - System.out.println(sexp); - } + @Test + public void testList2() throws Exception { + var sexp = RDSReader.readStream(getResourceAsStream("list2.rds")); - @Test - public void testClosure() throws Exception { - // function(x, y) "abc" + x + length(y) - var sexp = RDSReader.readStream(getResourceAsStream("closure.rds")); + System.out.println(sexp); + } - System.out.println(sexp); - } + @Test + public void testClosure() throws Exception { + // function(x, y) "abc" + x + length(y) + var sexp = RDSReader.readStream(getResourceAsStream("closure.rds")); - @Test - public void testClosureBC() throws Exception { - // function(x, y) "abc" + x + length(y) - var sexp = RDSReader.readStream(getResourceAsStream("closure-bc.rds")); + System.out.println(sexp); + } - System.out.println(sexp); + @Test + public void testClosureBC() throws Exception { + // function(x, y) "abc" + x + length(y) + var sexp = RDSReader.readStream(getResourceAsStream("closure-bc.rds")); - } + System.out.println(sexp); + } - @Test - public void testClosureBC2() throws Exception { - // function(a,b,c) f(a+b,length(b),c) - var sexp = RDSReader.readStream(getResourceAsStream("closure-bc2.rds")); + @Test + public void testClosureBC2() throws Exception { + // function(a,b,c) f(a+b,length(b),c) + var sexp = RDSReader.readStream(getResourceAsStream("closure-bc2.rds")); - System.out.println(sexp); - } + System.out.println(sexp); + } - // TODO: "closure-install.packages.rds" + // TODO: "closure-install.packages.rds" } diff --git a/src/test/java/org/prlprg/util/Tests.java b/src/test/java/org/prlprg/util/Tests.java index 6c1c0f4b1..b383b8875 100644 --- a/src/test/java/org/prlprg/util/Tests.java +++ b/src/test/java/org/prlprg/util/Tests.java @@ -1,13 +1,12 @@ package org.prlprg.util; -import com.google.common.io.Resources; - import java.io.InputStream; import java.util.Objects; -import java.util.stream.Collectors; public interface Tests { - default InputStream getResourceAsStream(String path) { - return Objects.requireNonNull(getClass().getResourceAsStream(path), "Resource not found in " + getClass().getPackageName() + ": " + path); - } + default InputStream getResourceAsStream(String path) { + return Objects.requireNonNull( + getClass().getResourceAsStream(path), + "Resource not found in " + getClass().getPackageName() + ": " + path); + } }