view 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 source

/*
 * 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);
                }
            }
        }
    }
}