Skip to content

Commit

Permalink
Merge with main
Browse files Browse the repository at this point in the history
  • Loading branch information
fikovnik committed Feb 13, 2024
1 parent 7cdb25d commit 0dbe2c2
Show file tree
Hide file tree
Showing 9 changed files with 69 additions and 29 deletions.
25 changes: 20 additions & 5 deletions src/main/java/org/prlprg/bc/Compiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -1090,12 +1090,27 @@ private boolean inlineDotCall(LangSXP call) {

private boolean inlineMultiColon(LangSXP call) {
if (!dotsOrMissing(call.args()) && call.args().size() == 2) {
if (call.arg(0).value() instanceof StrOrRegSymSXP s1 &&
call.arg(1).value() instanceof StrOrRegSymSXP s2) {
var args = SEXPs.list(SEXPs.string(s1.reifyString()), SEXPs.string(s2.reifyString()));
compileCallSymFun((RegSymSXP) call.fun(), args, call);
return true;

// FIXME: ugly
String s1 = switch(call.arg(0).value()) {
case StrSXP s when s.size() == 1 -> s.get(0);
case RegSymSXP s -> s.name();
default -> null;
};

String s2 = switch(call.arg(1).value()) {
case StrSXP s when s.size() == 1 -> s.get(0);
case RegSymSXP s -> s.name();
default -> null;
};

if (s1 == null || s2 == null) {
return false;
}

var args = SEXPs.list(SEXPs.string(s1), SEXPs.string(s2));
compileCallSymFun((RegSymSXP) call.fun(), args, call);
return true;
}

return false;
Expand Down
31 changes: 14 additions & 17 deletions src/main/java/org/prlprg/bc/Context.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,30 +103,28 @@ public Set<String> findLocals(SEXP e) {
// the code is following compiler:::findLocals1 from R
while (!todo.isEmpty()) {
var elem = todo.removeFirst();
if (elem instanceof LangSXP l && l.fun() instanceof RegSymSXP s) {
if (elem instanceof LangSXP l && l.fun() instanceof RegSymSXP fun) {
var args = l.args().values();
var local =
switch (s.name()) {
switch (fun.name()) {
case "=", "<-" -> {
var args = l.args().values();
todo.addAll(args.subList(1, args.size()));
yield getAssignedVar(l);
}
case "for" -> {
var args = l.args().values();
todo.addAll(args.subList(1, args.size()));

var sym = (RegSymSXP) l.arg(0).value();
var sym = (RegSymSXP) args.getFirst();
yield Optional.of(sym.name());
}
case "assign", "delayedAssign" -> {
// The variable in assign and delayedAssign expressions is considered
// local if it is an explicit
// character string and there is no environment argument.
var args = l.args().values();
todo.addAll(args.subList(1, args.size()));

if (args.size() == 2 && args.getFirst() instanceof StrOrRegSymSXP v) {
yield Optional.of(v.reifyString());
yield v.reifyString();
} else {
yield Optional.<String>empty();
}
Expand All @@ -142,27 +140,26 @@ public Set<String> findLocals(SEXP e) {
case "~", "expression", "quote" -> {
// they do not evaluate their arguments and so do not contribute new local
// variables.
if (shadowed.contains(s.name()) || locals.contains(s.name())) {
todo.addAll(l.args().values());
if (shadowed.contains(fun.name()) || locals.contains(fun.name())) {
todo.addAll(args);
}
yield Optional.<String>empty();
}
case "local" -> {

// local calls without an environment argument create a new environment
// for evaluating their expression and do not add new local variables.
// If an environment argument is present then this might be the current
// environment and so assignments in the expression are considered to
// create possible local variables.
if (shadowed.contains(s.name())
|| locals.contains(s.name())
|| l.args().size() != 1) {
todo.addAll(l.args().values());
if (shadowed.contains(fun.name())
|| locals.contains(fun.name())
|| args.size() != 1) {
todo.addAll(args);
}
yield Optional.<String>empty();
}
default -> {
todo.addAll(l.args().values());
todo.addAll(args);
yield Optional.<String>empty();
}
};
Expand All @@ -180,7 +177,7 @@ private static Optional<String> getAssignedVar(LangSXP l) {
if (v == SEXPs.MISSING_ARG) {
throw new CompilerException("Bad assignment: " + l);
} else if (v instanceof StrOrRegSymSXP s) {
return Optional.of(s.reifyString());
return s.reifyString();
} else {
if (l.args().isEmpty()) {
throw new CompilerException("Bad assignment: " + l);
Expand All @@ -190,7 +187,7 @@ private static Optional<String> getAssignedVar(LangSXP l) {
return getAssignedVar(ll);
}
case StrOrRegSymSXP s -> {
return Optional.of(s.reifyString());
return s.reifyString();
}
default -> {
throw new CompilerException("Bad assignment: " + l);
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/prlprg/rds/RDSException.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.prlprg.rds;

/** Thrown when deserializing R objects from malformed RDS. */
public class RDSException extends Exception {
public class RDSException extends RuntimeException {
RDSException(String message) {
super(message);
}
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/org/prlprg/sexp/RegSymSXP.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import java.util.Optional;

/** Symbol which isn't "unbound value" or "missing arg" */
public final class RegSymSXP implements SymSXP, StrOrRegSymSXP {
Expand All @@ -27,6 +28,10 @@ public String name() {
return name;
}

public Optional<String> reifyString() {
return Optional.of(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);
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/org/prlprg/sexp/SEXP.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.prlprg.sexp;

import java.util.Objects;
import javax.annotation.Nullable;

/**
Expand All @@ -10,7 +11,7 @@
* suspect GNU-R SEXPs aren't actually S-expressions.
*/
public sealed interface SEXP
permits StrOrRegSymSXP, SymOrLangSXP, ListOrVectorSXP, CloSXP, EnvSXP, BCodeSXP {
permits StrOrRegSymSXP, SymOrLangSXP, ListOrVectorSXP, CloSXP, EnvSXP, BCodeSXP, PromSXP {
/**
* SEXPTYPE. It's important to distinguish these from the SEXP's class, because there's a class
* for every type but not vice versa due to subclasses (e.g. simple-scalar ints have the same
Expand Down Expand Up @@ -39,7 +40,7 @@ default SEXP withAttributes(Attributes attributes) {
}

default SEXP withClass(String name) {
var attrs = attributes().including("class", new SimpleStrSXPImpl(name));
var attrs = Objects.requireNonNull(attributes()).including("class", SEXPs.string(name));
return withAttributes(attrs);
}
}
7 changes: 7 additions & 0 deletions src/main/java/org/prlprg/sexp/SimpleStrSXP.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.prlprg.sexp;

import java.util.Optional;

/** Simple scalar string = vector of size 1 with no ALTERP, ATTRIB, or OBJECT. */
public final class SimpleStrSXP extends SimpleScalarSXPImpl<String> implements StrSXP {
SimpleStrSXP(String data) {
Expand All @@ -20,4 +22,9 @@ public String toString() {
public StrSXP withAttributes(Attributes attributes) {
return SEXPs.string(data, attributes);
}

@Override
public Optional<String> reifyString() {
return Optional.of(data);
}
}
7 changes: 6 additions & 1 deletion src/main/java/org/prlprg/sexp/StrOrRegSymSXP.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.prlprg.sexp;

import java.util.Optional;
import javax.annotation.concurrent.Immutable;

/**
Expand All @@ -11,4 +12,8 @@
* org.prlprg.util.Either} instead...
*/
@Immutable
public sealed interface StrOrRegSymSXP extends SEXP permits StrSXP, RegSymSXP {}
public sealed interface StrOrRegSymSXP extends SEXP permits StrSXP, RegSymSXP {
// FIXME: this is not great, but far the most often used pattern matching is
// on either symbol or a scalar string
public Optional<String> reifyString();
}
11 changes: 11 additions & 0 deletions src/main/java/org/prlprg/sexp/StrSXP.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.google.common.collect.UnmodifiableIterator;
import com.google.common.escape.Escaper;
import com.google.common.escape.Escapers;
import java.util.Optional;
import javax.annotation.concurrent.Immutable;
import org.prlprg.primitive.Constants;

Expand Down Expand Up @@ -49,6 +50,11 @@ public String toString() {
public StrSXP withAttributes(Attributes attributes) {
return SEXPs.string(data, attributes);
}

@Override
public Optional<String> reifyString() {
return size() == 1 ? Optional.of(get(0)) : Optional.empty();
}
}

/** Empty string vector with no ALTREP, ATTRIB, or OBJECT. */
Expand All @@ -61,6 +67,11 @@ private EmptyStrSXPImpl() {}
public StrSXP withAttributes(Attributes attributes) {
return SEXPs.string(ImmutableList.of(), attributes);
}

@Override
public Optional<String> reifyString() {
return Optional.empty();
}
}

final class StrSXPs {
Expand Down
5 changes: 2 additions & 3 deletions src/test/java/org/prlprg/rds/RDSReaderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

import java.util.Objects;
import org.junit.jupiter.api.Test;
import org.prlprg.RPlatform;
import org.prlprg.RSession;
import org.prlprg.primitive.Constants;
import org.prlprg.primitive.Logical;
Expand All @@ -27,12 +26,12 @@ public void testInts() throws Exception {

if (sexp instanceof IntSXP ints) {
assertEquals(6, ints.size());
assertEquals(-RPlatform.INT_MAX, ints.get(0));
assertEquals(Constants.INT_MIN, 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));
assertEquals(Integer.MAX_VALUE, ints.get(5));
} else {
fail("Expected IntSXP");
}
Expand Down

0 comments on commit 0dbe2c2

Please sign in to comment.