aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/zwobble
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2016-07-15 14:23:47 +0100
committerMichael Williamson <mike@zwobble.org>2016-07-15 14:23:47 +0100
commitbc06840e00425db342bc5a021c0900d9e63b71f0 (patch)
tree0aa45a4ea475804a36a5ee33f9a726656f45bc1f /src/main/java/org/zwobble
parent3be68289f34a37ac7f9e1e95fa0d95dfc1bb3836 (diff)
Split switch handling off to desugar step
Diffstat (limited to 'src/main/java/org/zwobble')
-rw-r--r--src/main/java/org/zwobble/couscous/ast/NodeTypes.java2
-rw-r--r--src/main/java/org/zwobble/couscous/ast/Operations.java6
-rw-r--r--src/main/java/org/zwobble/couscous/ast/sugar/SwitchCaseNode.java36
-rw-r--r--src/main/java/org/zwobble/couscous/ast/sugar/SwitchNode.java48
-rw-r--r--src/main/java/org/zwobble/couscous/ast/visitors/NodeTransformer.java28
-rw-r--r--src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java6
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java6
-rw-r--r--src/main/java/org/zwobble/couscous/frontends/java/JavaStatementReader.java44
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/JavaProject.java7
-rw-r--r--src/main/java/org/zwobble/couscous/transforms/DesugarSwitchToIfElse.java90
-rw-r--r--src/main/java/org/zwobble/couscous/util/ExtraIterables.java39
-rw-r--r--src/main/java/org/zwobble/couscous/util/ExtraLists.java4
12 files changed, 271 insertions, 45 deletions
diff --git a/src/main/java/org/zwobble/couscous/ast/NodeTypes.java b/src/main/java/org/zwobble/couscous/ast/NodeTypes.java
index a82cd1c..86703ff 100644
--- a/src/main/java/org/zwobble/couscous/ast/NodeTypes.java
+++ b/src/main/java/org/zwobble/couscous/ast/NodeTypes.java
@@ -30,6 +30,8 @@ public class NodeTypes {
public static final int OPERATION = 10021;
public static final int RETURN = 10022;
public static final int STATIC_RECEIVER = 10023;
+ public static final int SWITCH = 10032;
+ public static final int SWITCH_CASE = 10033;
public static final int TERNARY_CONDITIONAL = 10024;
public static final int THIS_REFERENCE = 10025;
public static final int THROW = 10026;
diff --git a/src/main/java/org/zwobble/couscous/ast/Operations.java b/src/main/java/org/zwobble/couscous/ast/Operations.java
index 0ef9b88..308d070 100644
--- a/src/main/java/org/zwobble/couscous/ast/Operations.java
+++ b/src/main/java/org/zwobble/couscous/ast/Operations.java
@@ -4,7 +4,9 @@ import org.zwobble.couscous.types.ScalarType;
import org.zwobble.couscous.types.Types;
import org.zwobble.couscous.values.InternalCouscousValue;
+import static org.zwobble.couscous.ast.MethodCallNode.methodCall;
import static org.zwobble.couscous.ast.MethodCallNode.staticMethodCall;
+import static org.zwobble.couscous.ast.TypeCoercionNode.coerce;
import static org.zwobble.couscous.ast.TypeCoercionNode.typeCoercion;
import static org.zwobble.couscous.util.ExtraLists.list;
@@ -94,4 +96,8 @@ public class Operations {
public static ExpressionNode instanceOf(ExpressionNode value, ScalarType type) {
return new InstanceOfNode(value, type);
}
+
+ public static ExpressionNode methodEquals(ExpressionNode left, ExpressionNode right) {
+ return methodCall(left, "equals", list(coerce(right, Types.OBJECT)), Types.BOOLEAN);
+ }
}
diff --git a/src/main/java/org/zwobble/couscous/ast/sugar/SwitchCaseNode.java b/src/main/java/org/zwobble/couscous/ast/sugar/SwitchCaseNode.java
index 534f244..3aa9bdc 100644
--- a/src/main/java/org/zwobble/couscous/ast/sugar/SwitchCaseNode.java
+++ b/src/main/java/org/zwobble/couscous/ast/sugar/SwitchCaseNode.java
@@ -1,12 +1,25 @@
package org.zwobble.couscous.ast.sugar;
+import com.google.common.collect.Iterables;
import org.zwobble.couscous.ast.ExpressionNode;
+import org.zwobble.couscous.ast.Node;
+import org.zwobble.couscous.ast.NodeTypes;
import org.zwobble.couscous.ast.StatementNode;
+import org.zwobble.couscous.ast.visitors.NodeTransformer;
+import org.zwobble.couscous.util.ExtraIterables;
import java.util.List;
import java.util.Optional;
-public class SwitchCaseNode {
+public class SwitchCaseNode implements Node {
+ public static SwitchCaseNode switchCase(ExpressionNode value, List<StatementNode> statements) {
+ return switchCase(Optional.of(value), statements);
+ }
+
+ public static SwitchCaseNode switchCase(List<StatementNode> statements) {
+ return switchCase(Optional.empty(), statements);
+ }
+
public static SwitchCaseNode switchCase(Optional<ExpressionNode> value, List<StatementNode> statements) {
return new SwitchCaseNode(value, statements);
}
@@ -30,4 +43,25 @@ public class SwitchCaseNode {
public List<StatementNode> getStatements() {
return statements;
}
+
+ @Override
+ public int type() {
+ return NodeTypes.SWITCH_CASE;
+ }
+
+ @Override
+ public Iterable<? extends Node> childNodes() {
+ return Iterables.concat(
+ ExtraIterables.iterable(value),
+ statements
+ );
+ }
+
+ @Override
+ public SwitchCaseNode transform(NodeTransformer transformer) {
+ return new SwitchCaseNode(
+ value.map(transformer::transformExpression),
+ transformer.transformStatements(statements)
+ );
+ }
}
diff --git a/src/main/java/org/zwobble/couscous/ast/sugar/SwitchNode.java b/src/main/java/org/zwobble/couscous/ast/sugar/SwitchNode.java
new file mode 100644
index 0000000..8e1e226
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/ast/sugar/SwitchNode.java
@@ -0,0 +1,48 @@
+package org.zwobble.couscous.ast.sugar;
+
+import org.zwobble.couscous.ast.ExpressionNode;
+import org.zwobble.couscous.ast.Node;
+import org.zwobble.couscous.ast.NodeTypes;
+import org.zwobble.couscous.ast.StatementNode;
+import org.zwobble.couscous.ast.visitors.NodeTransformer;
+
+import java.util.List;
+
+import static org.zwobble.couscous.util.ExtraIterables.lazyCons;
+import static org.zwobble.couscous.util.ExtraLists.eagerMap;
+
+public class SwitchNode implements StatementNode {
+ private final ExpressionNode value;
+ private final List<SwitchCaseNode> cases;
+
+ public SwitchNode(ExpressionNode value, List<SwitchCaseNode> cases) {
+ this.value = value;
+ this.cases = cases;
+ }
+
+ public ExpressionNode getValue() {
+ return value;
+ }
+
+ public List<SwitchCaseNode> getCases() {
+ return cases;
+ }
+
+ @Override
+ public int type() {
+ return NodeTypes.SWITCH;
+ }
+
+ @Override
+ public Iterable<? extends Node> childNodes() {
+ return lazyCons(value, cases);
+ }
+
+ @Override
+ public StatementNode transform(NodeTransformer transformer) {
+ return new SwitchNode(
+ transformer.transformExpression(value),
+ eagerMap(cases, caseNode -> caseNode.transform(transformer))
+ );
+ }
+}
diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/NodeTransformer.java b/src/main/java/org/zwobble/couscous/ast/visitors/NodeTransformer.java
index 721ed8e..78bd6ef 100644
--- a/src/main/java/org/zwobble/couscous/ast/visitors/NodeTransformer.java
+++ b/src/main/java/org/zwobble/couscous/ast/visitors/NodeTransformer.java
@@ -14,7 +14,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
-import static org.zwobble.couscous.util.ExtraLists.eagerMap;
+import static org.zwobble.couscous.util.ExtraLists.*;
public class NodeTransformer {
public static NodeTransformer replaceExpressions(Map<ExpressionNode, ExpressionNode> replacements) {
@@ -29,6 +29,7 @@ public class NodeTransformer {
}
public static class Builder {
+ private Function<StatementNode, Optional<List<StatementNode>>> transformStatement = statement -> Optional.empty();
private Function<ExpressionNode, Optional<ExpressionNode>> transformExpression = expression -> Optional.empty();
private Function<Type, Type> transformType = type -> type;
private Function<MethodSignature, String> transformMethodName = MethodSignature::getName;
@@ -41,6 +42,13 @@ public class NodeTransformer {
return this;
}
+ public Builder transformStatement(
+ Function<StatementNode, Optional<List<StatementNode>>> transformStatement)
+ {
+ this.transformStatement = transformStatement;
+ return this;
+ }
+
public Builder transformType(
Function<Type, Type> transformType)
{
@@ -59,22 +67,31 @@ public class NodeTransformer {
}
public NodeTransformer build() {
- return new NodeTransformer(transformExpression, transformType, transformMethodName, transformFieldName);
+ return new NodeTransformer(
+ transformExpression,
+ transformStatement,
+ transformType,
+ transformMethodName,
+ transformFieldName
+ );
}
}
private final Function<ExpressionNode, Optional<ExpressionNode>> transformExpression;
+ private final Function<StatementNode, Optional<List<StatementNode>>> transformStatement;
private final Function<Type, Type> transformType;
private final Function<MethodSignature, String> transformMethodName;
private final Function<String, String> transformFieldName;
private NodeTransformer(
Function<ExpressionNode, Optional<ExpressionNode>> transformExpression,
+ Function<StatementNode, Optional<List<StatementNode>>> transformStatement,
Function<Type, Type> transformType,
Function<MethodSignature, String> transformMethodName,
Function<String, String> transformFieldName)
{
this.transformExpression = transformExpression;
+ this.transformStatement = transformStatement;
this.transformType = transformType;
this.transformMethodName = transformMethodName;
this.transformFieldName = transformFieldName;
@@ -121,8 +138,9 @@ public class NodeTransformer {
return VariableDeclaration.var(declaration.getId(), declaration.getName(), transform(declaration.getType()));
}
- public final StatementNode transformStatement(StatementNode statement) {
- return statement.transform(this);
+ public final Iterable<StatementNode> transformStatement(StatementNode statement) {
+ return transformStatement.apply(statement)
+ .orElseGet(() -> list(statement.transform(this)));
}
public FormalTypeParameterNode transformFormalTypeParameter(FormalTypeParameterNode typeParameter) {
@@ -178,7 +196,7 @@ public class NodeTransformer {
}
public List<StatementNode> transformStatements(List<StatementNode> body) {
- return transformList(body, this::transformStatement);
+ return eagerFlatMap(body, this::transformStatement);
}
public List<ExpressionNode> transformExpressions(List<? extends ExpressionNode> expressions) {
diff --git a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java
index 537184c..23743d0 100644
--- a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java
+++ b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java
@@ -7,6 +7,8 @@ import org.zwobble.couscous.ast.Node;
import org.zwobble.couscous.ast.Receiver;
import org.zwobble.couscous.ast.visitors.NodeTransformer;
import org.zwobble.couscous.backends.naming.Naming;
+import org.zwobble.couscous.frontends.java.Scope;
+import org.zwobble.couscous.transforms.DesugarSwitchToIfElse;
import org.zwobble.couscous.types.*;
import java.util.Map;
@@ -48,7 +50,9 @@ public class CsharpCodeGenerator {
}
private Node generateCode(Node node) {
- return node.transform(nodeTransformer);
+ return node
+ .transform(DesugarSwitchToIfElse.transformer(Scope.create()))
+ .transform(nodeTransformer);
}
private Type transformType(Type type) {
diff --git a/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java b/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java
index 7b016bf..949506a 100644
--- a/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java
+++ b/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java
@@ -9,6 +9,8 @@ import org.zwobble.couscous.ast.structure.NodeStructure;
import org.zwobble.couscous.ast.visitors.DynamicNodeMapper;
import org.zwobble.couscous.backends.naming.Names;
import org.zwobble.couscous.backends.python.ast.*;
+import org.zwobble.couscous.frontends.java.Scope;
+import org.zwobble.couscous.transforms.DesugarSwitchToIfElse;
import org.zwobble.couscous.types.ScalarType;
import org.zwobble.couscous.types.Type;
import org.zwobble.couscous.types.Types;
@@ -56,7 +58,9 @@ import static org.zwobble.couscous.util.ExtraIterables.*;
import static org.zwobble.couscous.util.ExtraLists.*;
public class PythonCodeGenerator {
- public static PythonModuleNode generateCode(TypeNode typeNode) {
+ public static PythonModuleNode generateCode(TypeNode sugaredTypeNode) {
+ TypeNode typeNode = sugaredTypeNode.transform(DesugarSwitchToIfElse.transformer(Scope.create()));
+
Iterator<PythonImportNode> imports = generateImports(typeNode).iterator();
PythonImportNode internalsImport = pythonImport(
diff --git a/src/main/java/org/zwobble/couscous/frontends/java/JavaStatementReader.java b/src/main/java/org/zwobble/couscous/frontends/java/JavaStatementReader.java
index 512030d..5692f8a 100644
--- a/src/main/java/org/zwobble/couscous/frontends/java/JavaStatementReader.java
+++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaStatementReader.java
@@ -4,6 +4,7 @@ import com.google.common.collect.Lists;
import org.eclipse.jdt.core.dom.*;
import org.zwobble.couscous.ast.*;
import org.zwobble.couscous.ast.sugar.SwitchCaseNode;
+import org.zwobble.couscous.ast.sugar.SwitchNode;
import org.zwobble.couscous.types.Type;
import org.zwobble.couscous.types.Types;
@@ -11,24 +12,20 @@ import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
-import static com.google.common.collect.Iterables.filter;
-import static com.google.common.collect.Iterables.tryFind;
import static org.zwobble.couscous.ast.ExceptionHandlerNode.exceptionHandler;
import static org.zwobble.couscous.ast.ExpressionStatementNode.expressionStatement;
import static org.zwobble.couscous.ast.IfStatementNode.ifStatement;
import static org.zwobble.couscous.ast.MethodCallNode.methodCall;
import static org.zwobble.couscous.ast.TryNode.tryStatement;
-import static org.zwobble.couscous.ast.TypeCoercionNode.coerce;
import static org.zwobble.couscous.ast.VariableReferenceNode.reference;
import static org.zwobble.couscous.ast.WhileNode.whileLoop;
import static org.zwobble.couscous.ast.sugar.SwitchCaseNode.switchCase;
import static org.zwobble.couscous.frontends.java.JavaTypes.typeOf;
import static org.zwobble.couscous.types.BoundTypeParameter.boundTypeParameter;
+import static org.zwobble.couscous.util.ExtraIterables.takeUntil;
import static org.zwobble.couscous.util.ExtraLists.*;
-import static org.zwobble.couscous.util.Fold.foldRight;
import static org.zwobble.couscous.util.Tails.tail;
import static org.zwobble.couscous.util.Tails.tails;
-import static org.zwobble.couscous.util.UpToAndIncludingIterable.upToAndIncluding;
class JavaStatementReader {
private final Scope scope;
@@ -120,8 +117,6 @@ class JavaStatementReader {
private List<StatementNode> readSwitchStatement(SwitchStatement switchStatement) {
ExpressionNode switchValue = readExpressionWithoutBoxing(switchStatement.getExpression());
- LocalVariableDeclarationNode switchValueAssignment = scope.temporaryVariable(switchValue);
-
@SuppressWarnings("unchecked")
List<Statement> statements = switchStatement.statements();
List<SwitchCaseNode> cases = eagerFlatMap(
@@ -134,42 +129,19 @@ class JavaStatementReader {
return list();
}
});
-
- List<StatementNode> handleDefault = tryFind(cases, currentCase -> currentCase.isDefault())
- .transform(currentCase -> currentCase.getStatements())
- .or(list());
-
- return cons(
- switchValueAssignment,
- foldRight(cases, handleDefault, (handle, currentCase) ->
- currentCase.getValue()
- .map(value -> list(ifStatement(
- methodCall(
- reference(switchValueAssignment),
- "equals",
- list(coerce(value, Types.OBJECT)),
- Types.BOOLEAN),
- currentCase.getStatements(), handle)))
- .orElse(handle)));
+ return list(new SwitchNode(switchValue, cases));
}
- private SwitchCaseNode readSwitchCase(SwitchCase caseStatement, List<Statement> statements)
- {
- Iterable<Statement> statementsForCase = upToAndIncluding(
- filter(
- statements,
- statement -> statement.getNodeType() != ASTNode.SWITCH_CASE),
- JavaStatementReader::isEndOfCase);
+ private SwitchCaseNode readSwitchCase(SwitchCase caseStatement, List<Statement> statements) {
+ Iterable<Statement> statementsForCase = takeUntil(
+ statements,
+ statement -> statement.getNodeType() == ASTNode.SWITCH_CASE
+ );
return switchCase(
Optional.ofNullable(caseStatement.getExpression()).map(this::readExpressionWithoutBoxing),
readStatements(statementsForCase));
}
- private static boolean isEndOfCase(Statement statement) {
- // TODO: do this on transformed nodes instead?
- return statement.getNodeType() == ASTNode.RETURN_STATEMENT;
- }
-
private List<StatementNode> readTryStatement(TryStatement statement) {
@SuppressWarnings("unchecked")
List<VariableDeclarationExpression> resources = statement.resources();
diff --git a/src/main/java/org/zwobble/couscous/interpreter/JavaProject.java b/src/main/java/org/zwobble/couscous/interpreter/JavaProject.java
index f96f0c4..42f0d83 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/JavaProject.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/JavaProject.java
@@ -2,11 +2,13 @@ package org.zwobble.couscous.interpreter;
import com.google.common.collect.Iterables;
import org.zwobble.couscous.ast.TypeNode;
+import org.zwobble.couscous.frontends.java.Scope;
import org.zwobble.couscous.interpreter.types.InterpreterType;
import org.zwobble.couscous.interpreter.types.IntrinsicInterpreterType;
import org.zwobble.couscous.interpreter.types.UserDefinedInterpreterType;
import org.zwobble.couscous.interpreter.values.BoxedIntegerInterpreterValue;
import org.zwobble.couscous.interpreter.values.InternalCouscousInterpreterValue;
+import org.zwobble.couscous.transforms.DesugarSwitchToIfElse;
import java.util.List;
@@ -22,7 +24,10 @@ public class JavaProject {
}
public static Project of(List<TypeNode> classNodes) {
- Iterable<InterpreterType> concreteTypes = Iterables.transform(classNodes, UserDefinedInterpreterType::new);
+ Iterable<InterpreterType> concreteTypes = Iterables.transform(
+ classNodes,
+ classNode -> new UserDefinedInterpreterType(classNode.transform(DesugarSwitchToIfElse.transformer(Scope.create())))
+ );
return builder()
.addClasses(concreteTypes)
.build();
diff --git a/src/main/java/org/zwobble/couscous/transforms/DesugarSwitchToIfElse.java b/src/main/java/org/zwobble/couscous/transforms/DesugarSwitchToIfElse.java
new file mode 100644
index 0000000..3ffaad5
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/transforms/DesugarSwitchToIfElse.java
@@ -0,0 +1,90 @@
+package org.zwobble.couscous.transforms;
+
+import org.zwobble.couscous.ast.LocalVariableDeclarationNode;
+import org.zwobble.couscous.ast.NodeTypes;
+import org.zwobble.couscous.ast.StatementNode;
+import org.zwobble.couscous.ast.sugar.SwitchCaseNode;
+import org.zwobble.couscous.ast.sugar.SwitchNode;
+import org.zwobble.couscous.ast.visitors.NodeTransformer;
+import org.zwobble.couscous.frontends.java.Scope;
+import org.zwobble.couscous.types.Types;
+import org.zwobble.couscous.util.ExtraLists;
+
+import java.util.List;
+import java.util.Optional;
+
+import static com.google.common.collect.Iterables.tryFind;
+import static org.zwobble.couscous.ast.IfStatementNode.ifStatement;
+import static org.zwobble.couscous.ast.MethodCallNode.methodCall;
+import static org.zwobble.couscous.ast.TypeCoercionNode.coerce;
+import static org.zwobble.couscous.ast.VariableReferenceNode.reference;
+import static org.zwobble.couscous.ast.sugar.SwitchCaseNode.switchCase;
+import static org.zwobble.couscous.util.ExtraIterables.lazyFlatMap;
+import static org.zwobble.couscous.util.ExtraLists.*;
+import static org.zwobble.couscous.util.Fold.foldRight;
+import static org.zwobble.couscous.util.Tails.tails;
+import static org.zwobble.couscous.util.UpToAndIncludingIterable.upToAndIncluding;
+
+public class DesugarSwitchToIfElse {
+ // TODO: ensure globally unique IDs, and locally unique variable names
+
+ public static NodeTransformer transformer(Scope scope) {
+ return NodeTransformer.builder()
+ .transformStatement(statement -> {
+ if (statement.type() == NodeTypes.SWITCH) {
+ return Optional.of(desugar(scope, ((SwitchNode) statement)));
+ } else {
+ return Optional.empty();
+ }
+ })
+ .build();
+ }
+
+ public static List<StatementNode> desugar(Scope scope, SwitchNode switchNode) {
+ LocalVariableDeclarationNode switchValueAssignment = scope.temporaryVariable(switchNode.getValue());
+
+ List<StatementNode> handleDefault = tryFind(switchNode.getCases(), SwitchCaseNode::isDefault)
+ .transform(SwitchCaseNode::getStatements)
+ .or(list());
+
+ List<SwitchCaseNode> cases = eagerMap(
+ tails(switchNode.getCases()),
+ remainingCases -> readSwitchCase(remainingCases.get(0), remainingCases)
+ );
+
+ return cons(
+ switchValueAssignment,
+ foldRight(cases, handleDefault, (handle, currentCase) ->
+ currentCase.getValue()
+ .map(value -> list(ifStatement(
+ methodCall(
+ reference(switchValueAssignment),
+ "equals",
+ list(coerce(value, Types.OBJECT)),
+ Types.BOOLEAN
+ ),
+ currentCase.getStatements(),
+ handle
+ )))
+ .orElse(handle)
+ )
+ );
+ }
+
+ private static SwitchCaseNode readSwitchCase(SwitchCaseNode caseStatement, List<SwitchCaseNode> cases)
+ {
+ Iterable<StatementNode> statementsForCase = upToAndIncluding(
+ lazyFlatMap(cases, SwitchCaseNode::getStatements),
+ DesugarSwitchToIfElse::isEndOfCase
+ );
+ return switchCase(
+ caseStatement.getValue(),
+ ExtraLists.copyOf(statementsForCase)
+ );
+ }
+
+ private static boolean isEndOfCase(StatementNode statement) {
+ // TODO: add attribute onto statement node to detect exit
+ return statement.type() == NodeTypes.RETURN;
+ }
+}
diff --git a/src/main/java/org/zwobble/couscous/util/ExtraIterables.java b/src/main/java/org/zwobble/couscous/util/ExtraIterables.java
index 1140462..3881d29 100644
--- a/src/main/java/org/zwobble/couscous/util/ExtraIterables.java
+++ b/src/main/java/org/zwobble/couscous/util/ExtraIterables.java
@@ -2,8 +2,11 @@ package org.zwobble.couscous.util;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.PeekingIterator;
import java.util.Iterator;
+import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -162,4 +165,40 @@ public class ExtraIterables {
public static <T> Iterable<T> of(T e1, T e2, T e3) {
return ImmutableList.of(e1, e2, e3);
}
+
+ public static <T> Iterable<T> iterable(Optional<T> optional) {
+ if (optional.isPresent()) {
+ return of(optional.get());
+ } else {
+ return empty();
+ }
+ }
+
+ public static <T> Iterable<T> takeUntil(Iterable<T> iterable, Predicate<T> predicate) {
+ return new Iterable<T>() {
+ @Override
+ public Iterator<T> iterator() {
+ return takeUntil(iterable.iterator(), predicate);
+ }
+ };
+ }
+
+ public static <T> Iterator<T> takeUntil(Iterator<T> iterator, Predicate<T> predicate) {
+ PeekingIterator<T> peekingIterator = Iterators.peekingIterator(iterator);
+ return new Iterator<T>() {
+ @Override
+ public boolean hasNext() {
+ return peekingIterator.hasNext() && !predicate.test(peekingIterator.peek());
+ }
+
+ @Override
+ public T next() {
+ if (hasNext()) {
+ return peekingIterator.next();
+ } else {
+ throw new NoSuchElementException();
+ }
+ }
+ };
+ }
}
diff --git a/src/main/java/org/zwobble/couscous/util/ExtraLists.java b/src/main/java/org/zwobble/couscous/util/ExtraLists.java
index 76d3de7..d52c099 100644
--- a/src/main/java/org/zwobble/couscous/util/ExtraLists.java
+++ b/src/main/java/org/zwobble/couscous/util/ExtraLists.java
@@ -143,4 +143,8 @@ public class ExtraLists {
this.exception = exception;
}
}
+
+ public static <T> List<T> copyOf(Iterable<T> iterable) {
+ return ImmutableList.copyOf(iterable);
+ }
}