diff --git a/src/main/java/io/github/spencerpark/ijava/runtime/Interpolation.java b/src/main/java/io/github/spencerpark/ijava/runtime/Interpolation.java new file mode 100644 index 0000000..46fab44 --- /dev/null +++ b/src/main/java/io/github/spencerpark/ijava/runtime/Interpolation.java @@ -0,0 +1,115 @@ +package io.github.spencerpark.ijava.runtime; + + +import java.util.ArrayList; +import java.util.List; + +public class Interpolation { + + private String value; + private int index; + + public static List parse(String value) { + return new Interpolation(value).parse(); + } + + private Interpolation(String stringValue) { + value = stringValue; + } + + private List parse() { + List list = new ArrayList<>(); + StringBuilder sb = new StringBuilder(); + int length = value.length(); + int offset = 0; + for (index = 0; index < length; ++index) { + char c = value.charAt(index); + if (c == '$') { + Expression expr = parseExpr(); + if (expr != null) { + if (sb.length() > 0) { + list.add(new Expression(sb.toString(), true)); + sb.setLength(0); + } + list.add(expr); + continue; + } + } + sb.append(c); + } + + if (list.isEmpty() || sb.length() > 0) { + list.add(new Expression(sb.toString(), true)); + } + return list; + } + + private Expression parseExpr() { + if (index + 1 == value.length()) { + return null; + } + + if (value.charAt(index + 1) == '{') { + return parseBraceExpr(); + } + return parseSimpleExpr(); + } + + private Expression parseBraceExpr() { + int length = value.length(); + StringBuilder sb = new StringBuilder(); + for (int pos = index + 2; pos < length; ++pos) { + char c = value.charAt(pos); + if (c != '}') { + sb.append(c); + } else { + if (sb.length() > 0) { + index = pos; + return new Expression(sb.toString(), false); + } + break; + } + } + return null; + } + + private Expression parseSimpleExpr() { + int length = value.length(); + StringBuilder sb = new StringBuilder(); + for (int pos = index + 1; pos < length; pos++) { + char c = value.charAt(pos); + if (sb.length() == 0) { + if (c != '$' && Character.isJavaIdentifierStart(c)) { + sb.append(c); + } else { + return null; + } + } else if (c != '$' && Character.isJavaIdentifierPart(c)) { + sb.append(c); + } else { + break; + } + index = pos; + } + return sb.length() > 0 ? new Expression(sb.toString(), false) : null; + } + + public static final class Expression { + + private String expression; + private boolean constant; + + Expression(String expression, boolean constant) { + this.expression = expression; + this.constant = constant; + } + + public String getExpression() { + return expression; + } + + public boolean isConstant() { + return constant; + } + } +} diff --git a/src/main/java/io/github/spencerpark/ijava/runtime/Magics.java b/src/main/java/io/github/spencerpark/ijava/runtime/Magics.java index 384d7ae..c7f095e 100644 --- a/src/main/java/io/github/spencerpark/ijava/runtime/Magics.java +++ b/src/main/java/io/github/spencerpark/ijava/runtime/Magics.java @@ -4,6 +4,7 @@ import io.github.spencerpark.ijava.JavaKernel; import io.github.spencerpark.jupyter.kernel.magic.registry.UndefinedMagicException; +import java.util.ArrayList; import java.util.List; public class Magics { @@ -12,7 +13,11 @@ public static T lineMagic(String name, List args) { if (kernel != null) { try { - return kernel.getMagics().applyLineMagic(name, args); + List realArgs = new ArrayList<>(args.size()); + for (String arg : args) { + realArgs.add(kernel.evalRaw(interpolate(arg)).toString()); + } + return kernel.getMagics().applyLineMagic(name, realArgs); } catch (UndefinedMagicException e) { throw e; } catch (Exception e) { @@ -28,7 +33,12 @@ public static T cellMagic(String name, List args, String body) { if (kernel != null) { try { - return kernel.getMagics().applyCellMagic(name, args, body); + List realArgs = new ArrayList<>(args.size()); + for (String arg : args) { + realArgs.add(kernel.evalRaw(interpolate(arg)).toString()); + } + String realBody = kernel.evalRaw(interpolate(body)).toString(); + return kernel.getMagics().applyCellMagic(name, realArgs, realBody); } catch (UndefinedMagicException e) { throw e; } catch (Exception e) { @@ -38,4 +48,25 @@ public static T cellMagic(String name, List args, String body) { throw new RuntimeException("No IJava kernel running"); } } + + private static String interpolate(String input) { + List expressions = Interpolation.parse(input); + StringBuilder sb = new StringBuilder(); + sb.append("String.format(\""); + StringBuilder parameters = new StringBuilder(); + for (Interpolation.Expression expression : expressions) { + if (expression.isConstant()) { + sb.append(expression.getExpression().replace("%", "%%").replace("\n", "\\n").replace("\r", "\\r")); + } else { + if (parameters.length() == 0) { + parameters.append(", "); + } + parameters.append("String.valueOf(").append(expression.getExpression()).append(')'); + sb.append("%s"); + } + } + sb.append('"'); + sb.append(parameters).append(");"); + return sb.toString(); + } }