diff graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/StringFormatter.java @ 13514:0fbee3eb71f0

Ruby: import project.
author Chris Seaton <chris.seaton@oracle.com>
date Mon, 06 Jan 2014 17:12:09 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/StringFormatter.java	Mon Jan 06 17:12:09 2014 +0000
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This
+ * code is released under a tri EPL/GPL/LGPL license. You can use it,
+ * redistribute it and/or modify it under the terms of the:
+ *
+ * Eclipse Public License version 1.0
+ * GNU General Public License version 2
+ * GNU Lesser General Public License version 2.1
+ */
+package com.oracle.truffle.ruby.runtime.core;
+
+import java.io.*;
+import java.util.*;
+
+import com.oracle.truffle.ruby.runtime.core.array.*;
+
+public class StringFormatter {
+
+    public static String format(String format, List<Object> values) {
+        final ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
+        final PrintStream printStream = new PrintStream(byteArray);
+
+        format(printStream, format, values);
+
+        return byteArray.toString();
+    }
+
+    public static void format(PrintStream stream, String format, List<Object> values) {
+        /*
+         * See http://www.ruby-doc.org/core-1.9.3/Kernel.html#method-i-sprintf.
+         * 
+         * At the moment we just do the basics that we need. We will need a proper lexer later on.
+         * Or better than that we could compile to Truffle nodes if the format string is constant! I
+         * don't think we can easily translate to Java's format syntax, otherwise JRuby would do
+         * that and they don't.
+         */
+
+        // I'm not using a for loop, because Checkstyle won't let me modify the control variable
+
+        int n = 0;
+        int v = 0;
+
+        while (n < format.length()) {
+            final char c = format.charAt(n);
+            n++;
+
+            if (c == '%') {
+                // %[flags][width][.precision]type
+
+                final String flagChars = "0";
+
+                boolean zeroPad = false;
+
+                while (n < format.length() && flagChars.indexOf(format.charAt(n)) != -1) {
+                    switch (format.charAt(n)) {
+                        case '0':
+                            zeroPad = true;
+                            break;
+                    }
+
+                    n++;
+                }
+
+                int width;
+
+                if (n < format.length() && Character.isDigit(format.charAt(n))) {
+                    final int widthStart = n;
+
+                    while (Character.isDigit(format.charAt(n))) {
+                        n++;
+                    }
+
+                    width = Integer.parseInt(format.substring(widthStart, n));
+                } else {
+                    width = 0;
+                }
+
+                int precision;
+
+                if (format.charAt(n) == '.') {
+                    n++;
+
+                    final int precisionStart = n;
+
+                    while (Character.isDigit(format.charAt(n))) {
+                        n++;
+                    }
+
+                    precision = Integer.parseInt(format.substring(precisionStart, n));
+                } else {
+                    precision = 5;
+                }
+
+                final char type = format.charAt(n);
+                n++;
+
+                final StringBuilder formatBuilder = new StringBuilder();
+
+                formatBuilder.append("%");
+
+                if (width > 0) {
+                    if (zeroPad) {
+                        formatBuilder.append("0");
+                    }
+
+                    formatBuilder.append(width);
+                }
+
+                switch (type) {
+                    case 'd': {
+                        formatBuilder.append("d");
+                        final int value = GeneralConversions.toFixnum(values.get(v));
+                        stream.printf(formatBuilder.toString(), value);
+                        break;
+                    }
+
+                    case 'f': {
+                        formatBuilder.append(".");
+                        formatBuilder.append(precision);
+                        formatBuilder.append("f");
+                        final double value = GeneralConversions.toFloat(values.get(v));
+                        stream.printf(formatBuilder.toString(), value);
+                        break;
+                    }
+
+                    default:
+                        throw new RuntimeException("Kernel#sprintf error");
+                }
+
+                v++;
+            } else {
+                stream.print(c);
+            }
+        }
+    }
+
+    public static void formatPuts(PrintStream stream, List<Object> args) {
+        if (args.size() > 0) {
+            formatPutsInner(stream, args);
+        } else {
+            stream.println();
+        }
+    }
+
+    public static void formatPutsInner(PrintStream stream, List<Object> args) {
+        if (args.size() > 0) {
+            for (Object arg : args) {
+                if (arg instanceof RubyArray) {
+                    final RubyArray array = (RubyArray) arg;
+                    formatPutsInner(stream, array.asList());
+                } else {
+                    stream.println(arg);
+                }
+            }
+        }
+    }
+}