Revision: 3105 http://trac.macosforge.org/projects/ruby/changeset/3105 Author: emoy@apple.com Date: 2009-12-11 18:07:37 -0800 (Fri, 11 Dec 2009) Log Message: ----------- Branch ticket159: ported the ruby 1.9.1 version of sprintf.c; added %@ and %a; fixed float literal support in compiler (but needs more work to remove Fixfloat dependencies); switch to libSystem's version of strtod. Modified Paths: -------------- MacRuby/branches/ticket159/array.c MacRuby/branches/ticket159/compiler.cpp MacRuby/branches/ticket159/dispatcher.cpp MacRuby/branches/ticket159/include/ruby/intern.h MacRuby/branches/ticket159/include/ruby/ruby.h MacRuby/branches/ticket159/include/ruby/util.h MacRuby/branches/ticket159/io.c MacRuby/branches/ticket159/marshal.c MacRuby/branches/ticket159/numeric.c MacRuby/branches/ticket159/parse.y MacRuby/branches/ticket159/util.c MacRuby/branches/ticket159/vm_method.c Added Paths: ----------- MacRuby/branches/ticket159/sprintf.c Removed Paths: ------------- MacRuby/branches/ticket159/sprintf.cpp Modified: MacRuby/branches/ticket159/array.c =================================================================== --- MacRuby/branches/ticket159/array.c 2009-12-11 17:09:10 UTC (rev 3104) +++ MacRuby/branches/ticket159/array.c 2009-12-12 02:07:37 UTC (rev 3105) @@ -1923,9 +1923,9 @@ } return 0; } - else if (FIXFLOAT_P(a) && FIXFLOAT_P(b)) { - const double fa = FIXFLOAT2DBL(a); - const double fb = FIXFLOAT2DBL(b); + else if (TYPE(a) == T_FLOAT && TYPE(b) == T_FLOAT) { + const double fa = RFLOAT_VALUE(a); + const double fb = RFLOAT_VALUE(b); if (fa > fb) { return 1; } @@ -2935,8 +2935,8 @@ VALUE item1 = rary_elt(RARY(ary1), i); VALUE item2 = rary_elt(RARY(ary2), i); - if ((FIXFLOAT_P(item1) && isnan(FIXFLOAT2DBL(item1))) - || FIXFLOAT_P(item2) && isnan(FIXFLOAT2DBL(item2))) { + if ((TYPE(item1) == T_FLOAT && isnan(RFLOAT_VALUE(item1))) + || TYPE(item2) == T_FLOAT && isnan(RFLOAT_VALUE(item2))) { return Qfalse; } Modified: MacRuby/branches/ticket159/compiler.cpp =================================================================== --- MacRuby/branches/ticket159/compiler.cpp 2009-12-11 17:09:10 UTC (rev 3104) +++ MacRuby/branches/ticket159/compiler.cpp 2009-12-12 02:07:37 UTC (rev 3105) @@ -2017,9 +2017,9 @@ val->v.l = FIX2LONG(rval); return true; } - else if (FIXFLOAT_P(rval)) { + else if (TYPE(rval) == T_FLOAT) { val->type = T_FLOAT; - val->v.d = FIXFLOAT2DBL(rval); + val->v.d = RFLOAT_VALUE(rval); return true; } } @@ -2307,7 +2307,7 @@ } else { res_val = ConstantInt::get(RubyObjTy, - DBL2FIXFLOAT(res)); + DOUBLE2NUM(res)); } } } @@ -5446,6 +5446,10 @@ cast<Function>(module->getOrInsertFunction("rb_bignum_new_retained", RubyObjTy, PtrTy, NULL)); + Function *newFloatFunc = + cast<Function>(module->getOrInsertFunction("rb_float_from_astr_retained", + RubyObjTy, PtrTy, NULL)); + Function *getClassFunc = cast<Function>(module->getOrInsertFunction("objc_getClass", RubyObjTy, PtrTy, NULL)); @@ -5578,6 +5582,33 @@ } break; + case T_FLOAT: + { + const char *astr = RSTRING_PTR(rb_float_to_astr(val)); + + GlobalVariable *floatstr_gvar = + compile_const_global_string(astr); + + std::vector<Value *> idxs; + idxs.push_back(ConstantInt::get(Int32Ty, 0)); + idxs.push_back(ConstantInt::get(Int32Ty, 0)); + Instruction *load = GetElementPtrInst::Create(floatstr_gvar, + idxs.begin(), idxs.end(), ""); + + std::vector<Value *> params; + params.push_back(load); + + Instruction *call = CallInst::Create(newFloatFunc, + params.begin(), params.end(), ""); + + Instruction *assign = new StoreInst(call, gvar, ""); + + list.insert(list.begin(), assign); + list.insert(list.begin(), call); + list.insert(list.begin(), load); + } + break; + default: if (rb_obj_is_kind_of(val, rb_cRange)) { VALUE beg = 0, end = 0; @@ -5863,7 +5894,7 @@ case T_SYMBOL: return rb_sym2name(rval); } - return StringValueCStr(rval); + return rb_str_cstr(rb_obj_as_string(rval)); } } @@ -6555,15 +6586,6 @@ func_name = "rb_vm_ulong_long_to_rval"; break; -#if __LP64__ - case _C_FLT: - val = new FPExtInst(val, DoubleTy, "", bb); - // fall through - case _C_DBL: - val = new BitCastInst(val, RubyObjTy, "", bb); - val = BinaryOperator::CreateOr(val, threeVal, "", bb); - return val; -#else // TODO inline the right code for the 32-bit fixfloat optimization case _C_FLT: func_name = "rb_vm_float_to_rval"; @@ -6572,7 +6594,6 @@ case _C_DBL: func_name = "rb_vm_double_to_rval"; break; -#endif case _C_SEL: func_name = "rb_vm_sel_to_rval"; Modified: MacRuby/branches/ticket159/dispatcher.cpp =================================================================== --- MacRuby/branches/ticket159/dispatcher.cpp 2009-12-11 17:09:10 UTC (rev 3104) +++ MacRuby/branches/ticket159/dispatcher.cpp 2009-12-12 02:07:37 UTC (rev 3105) @@ -1812,6 +1812,7 @@ } extern IMP basic_respond_to_imp; // vm_method.c +extern IMP basicobject_respond_to_imp; // vm_method.c bool RoxorCore::respond_to(VALUE obj, VALUE klass, SEL sel, bool priv, @@ -1824,9 +1825,11 @@ assert(!check_override); } + IMP respond_to_imp = class_getMethodImplementation((Class)klass, + selRespondTo); const bool overriden = check_override - ? (class_getMethodImplementation((Class)klass, selRespondTo) - != basic_respond_to_imp) + ? (respond_to_imp != basic_respond_to_imp + && respond_to_imp != basicobject_respond_to_imp) : false; if (!overriden) { Modified: MacRuby/branches/ticket159/include/ruby/intern.h =================================================================== --- MacRuby/branches/ticket159/include/ruby/intern.h 2009-12-11 17:09:10 UTC (rev 3104) +++ MacRuby/branches/ticket159/include/ruby/intern.h 2009-12-12 02:07:37 UTC (rev 3105) @@ -418,6 +418,7 @@ VALUE rb_objc_num_coerce_cmp(VALUE, VALUE, SEL sel); VALUE rb_num_coerce_relop(VALUE, VALUE, SEL); VALUE rb_float_new(double); +VALUE rb_float_to_astr(VALUE); VALUE rb_num2fix(VALUE); VALUE rb_fix2str(VALUE, int); VALUE rb_fix_minus(VALUE x, VALUE y); Modified: MacRuby/branches/ticket159/include/ruby/ruby.h =================================================================== --- MacRuby/branches/ticket159/include/ruby/ruby.h 2009-12-11 17:09:10 UTC (rev 3104) +++ MacRuby/branches/ticket159/include/ruby/ruby.h 2009-12-12 02:07:37 UTC (rev 3105) @@ -1386,6 +1386,12 @@ if (k == (Class)rb_cFixnum) { return T_FIXNUM; } + if (k == (Class)rb_cBignum) { + return T_BIGNUM; + } + if (k == (Class)rb_cFloat) { + return T_FLOAT; + } const long v = RCLASS_VERSION(k); if ((v & RCLASS_IS_STRING_SUBCLASS) == RCLASS_IS_STRING_SUBCLASS) { return T_STRING; Modified: MacRuby/branches/ticket159/include/ruby/util.h =================================================================== --- MacRuby/branches/ticket159/include/ruby/util.h 2009-12-11 17:09:10 UTC (rev 3104) +++ MacRuby/branches/ticket159/include/ruby/util.h 2009-12-12 02:07:37 UTC (rev 3105) @@ -68,9 +68,11 @@ VALUE ruby_getcwd(void); #define my_getcwd() ruby_getcwd() +#ifndef __APPLE__ double ruby_strtod(const char *, char **); #undef strtod #define strtod(s,e) ruby_strtod(s,e) +#endif void ruby_each_words(const char *, void (*)(const char*, int, void*), void *); Modified: MacRuby/branches/ticket159/io.c =================================================================== --- MacRuby/branches/ticket159/io.c 2009-12-11 17:09:10 UTC (rev 3104) +++ MacRuby/branches/ticket159/io.c 2009-12-12 02:07:37 UTC (rev 3105) @@ -3394,7 +3394,7 @@ } tv->tv_sec = FIX2LONG(num); } - else if (FIXFLOAT_P(num)) { + else if (TYPE(num) == T_FLOAT) { double quantity = RFLOAT_VALUE(num); if (quantity < 0.0) { rb_raise(rb_eArgError, "select() does not accept negative timeouts."); Modified: MacRuby/branches/ticket159/marshal.c =================================================================== --- MacRuby/branches/ticket159/marshal.c 2009-12-11 17:09:10 UTC (rev 3104) +++ MacRuby/branches/ticket159/marshal.c 2009-12-12 02:07:37 UTC (rev 3105) @@ -759,6 +759,11 @@ } break; + case T_FLOAT: + w_byte(TYPE_FLOAT, arg); + w_float(RFLOAT_VALUE(obj), arg); + break; + case T_STRING: #if WITH_OBJC w_uclass(obj, rb_objc_str_is_pure(obj), arg); Modified: MacRuby/branches/ticket159/numeric.c =================================================================== --- MacRuby/branches/ticket159/numeric.c 2009-12-11 17:09:10 UTC (rev 3104) +++ MacRuby/branches/ticket159/numeric.c 2009-12-12 02:07:37 UTC (rev 3105) @@ -586,7 +586,18 @@ VALUE rb_float_new(double d) { - return DBL2FIXFLOAT(d); +#ifdef __LP64__ + union { + VALUE v; + double d; + } x = {.d = d}; + if (IMMEDIATE_P(x.v) == 0) return DBL2FIXFLOAT(d); +#endif + NEWOBJ(flt, struct RFloat); + OBJSETUP(flt, rb_cFloat, T_FLOAT); + + flt->float_value = d; + return (VALUE)flt; } /* @@ -1451,6 +1462,31 @@ return LONG2FIX(val); } +// used to serialize/deserialize literal floats + +VALUE +rb_float_to_astr(VALUE num) +{ + char buf[32]; // should be big enough for any %a string + if (TYPE(num) != T_FLOAT) { + rb_raise(rb_eArgError, + "rb_float_to_astr called with argument that is not a float"); + } + double d = RFLOAT_VALUE(num); + snprintf(buf, sizeof(buf), "%a", d); + return rb_str_new2(buf); +} + +VALUE +rb_float_from_astr_retained(const char *s) +{ + double d = 0.0; + sscanf(s, "%la", &d); + VALUE v = DOUBLE2NUM(d); + GC_RETAIN(v); + return v; +} + /* * call-seq: * num.floor => integer Modified: MacRuby/branches/ticket159/parse.y =================================================================== --- MacRuby/branches/ticket159/parse.y 2009-12-11 17:09:10 UTC (rev 3104) +++ MacRuby/branches/ticket159/parse.y 2009-12-12 02:07:37 UTC (rev 3105) @@ -8751,7 +8751,7 @@ case T_FLOAT: { double v = -RFLOAT_VALUE(node->nd_lit); - node->nd_lit = DBL2FIXFLOAT(v); + node->nd_lit = DOUBLE2NUM(v); } break; default: Copied: MacRuby/branches/ticket159/sprintf.c (from rev 3102, MacRuby/branches/ticket159/sprintf.cpp) =================================================================== --- MacRuby/branches/ticket159/sprintf.c (rev 0) +++ MacRuby/branches/ticket159/sprintf.c 2009-12-12 02:07:37 UTC (rev 3105) @@ -0,0 +1,1107 @@ +/* + * MacRuby implementation of Ruby 1.9's sprintf.c. + * + * This file is covered by the Ruby license. See COPYING for more details. + * + * Copyright (C) 2007-2009, Apple Inc. All rights reserved. + */ +/********************************************************************** + + sprintf.c - + + $Author: yugui $ + created at: Fri Oct 15 10:39:26 JST 1993 + + Copyright (C) 1993-2007 Yukihiro Matsumoto + Copyright (C) 2000 Network Applied Communication Laboratory, Inc. + Copyright (C) 2000 Information-technology Promotion Agency, Japan + +**********************************************************************/ + +#include "ruby/ruby.h" +#include "ruby/re.h" +#include "ruby/encoding.h" +#include <math.h> +#include <stdarg.h> +#include <ctype.h> + +#ifdef HAVE_IEEEFP_H +#include <ieeefp.h> +#endif + +#define BIT_DIGITS(N) (((N)*146)/485 + 1) /* log2(10) =~ 146/485 */ +#define BITSPERDIG (SIZEOF_BDIGITS*CHAR_BIT) +#define EXTENDSIGN(n, l) (((~0 << (n)) >> (((n)*(l)) % BITSPERDIG)) & ~(~0 << (n))) + +static void fmt_setup(char*,size_t,int,int,int,int); + +static char* +remove_sign_bits(char *str, int base) +{ + char *s, *t; + + s = t = str; + + if (base == 16) { + while (*t == 'f') { + t++; + } + } + else if (base == 8) { + *t |= EXTENDSIGN(3, strlen(t)); + while (*t == '7') { + t++; + } + } + else if (base == 2) { + while (*t == '1') { + t++; + } + } + + return t; +} + +#define FNONE 0 +#define FSHARP 1 +#define FMINUS 2 +#define FPLUS 4 +#define FZERO 8 +#define FSPACE 16 +#define FWIDTH 32 +#define FPREC 64 +#define FPREC0 128 + +#define PUSH(s, l) CFStringAppendCharacters((CFMutableStringRef)result, s, l) + +#define PUSH_CSTR(s) CFStringAppendCString((CFMutableStringRef)result, s, kCFStringEncodingUTF8) + +#define PUSH_CSTR_LEN(s, l) do { \ + char save = 0; \ + if (l < strlen(s)) { \ + save = s[l]; \ + s[l] = 0; \ + } \ + CFStringAppendCString((CFMutableStringRef)result, s, kCFStringEncodingUTF8); \ + if (save) s[l] = save; \ +} while(0) + + +#define FILL(c, l) do {if ((l) > 0) CFStringPad((CFMutableStringRef)result, CFSTR(c), CFStringGetLength((CFMutableStringRef)result) + (l), 0);} while(0) + +#define FILL_SIGN_BITS(base, p, l) \ + switch (base) { \ + case 16: \ + if (*p == 'X') FILL("F", (l)); \ + else FILL("f", (l)); \ + break; \ + case 8: \ + FILL("7", (l)); \ + break; \ + case 2: \ + FILL("1", (l)); \ + break; \ + default: \ + FILL(".", (l)); \ + break; \ + } + +#define GETARG() (nextvalue != Qundef ? nextvalue : \ + posarg == -1 ? \ + (rb_raise(rb_eArgError, "unnumbered(%d) mixed with numbered", nextarg), 0) : \ + posarg == -2 ? \ + (rb_raise(rb_eArgError, "unnumbered(%d) mixed with named", nextarg), 0) : \ + (posarg = nextarg++, GETNTHARG(posarg))) + +#define GETPOSARG(n) (posarg > 0 ? \ + (rb_raise(rb_eArgError, "numbered(%d) after unnumbered(%d)", n, posarg), 0) : \ + posarg == -2 ? \ + (rb_raise(rb_eArgError, "numbered(%d) after named", n), 0) : \ + ((n < 1) ? (rb_raise(rb_eArgError, "invalid index - %d$", n), 0) : \ + (posarg = -1, GETNTHARG(n)))) + +#define GETNTHARG(nth) \ + ((nth >= argc) ? (rb_raise(rb_eArgError, "too few arguments"), 0) : argv[nth]) + +#define GETNAMEARG(id) (posarg > 0 ? \ + (rb_raise(rb_eArgError, "named after unnumbered(%d)", posarg), 0) : \ + posarg == -1 ? \ + (rb_raise(rb_eArgError, "named after numbered"), 0) : \ + rb_hash_lookup(get_hash(&hash, argc, argv), id)) + +#define GETNUM(n, val) \ + for (; p < end && isdigit(*p); p++) { \ + int next_n = 10 * n + (*p - '0'); \ + if (next_n / 10 != n) {\ + rb_raise(rb_eArgError, #val " too big"); \ + } \ + n = next_n; \ + } \ + if (p >= end) { \ + rb_raise(rb_eArgError, "malformed format string - %%*[0-9]"); \ + } + +#define GETASTER(val) do { \ + t = p++; \ + n = 0; \ + GETNUM(n, val); \ + if (*p == '$') { \ + tmp = GETPOSARG(n); \ + } \ + else { \ + tmp = GETARG(); \ + p = t; \ + } \ + val = NUM2INT(tmp); \ +} while (0) + +static VALUE +get_hash(volatile VALUE *hash, int argc, const VALUE *argv) +{ + VALUE tmp; + + if (*hash != Qundef) return *hash; + if (argc != 2) { + rb_raise(rb_eArgError, "one hash required"); + } + tmp = rb_check_convert_type(argv[1], T_HASH, "Hash", "to_hash"); + if (NIL_P(tmp)) { + rb_raise(rb_eArgError, "one hash required"); + } + return (*hash = tmp); +} + +/* + * call-seq: + * format(format_string [, arguments...] ) => string + * sprintf(format_string [, arguments...] ) => string + * + * Returns the string resulting from applying <i>format_string</i> to + * any additional arguments. Within the format string, any characters + * other than format sequences are copied to the result. + * + * The syntax of a format sequence is follows. + * + * %[flags][width][.precision]type + * + * A format + * sequence consists of a percent sign, followed by optional flags, + * width, and precision indicators, then terminated with a field type + * character. The field type controls how the corresponding + * <code>sprintf</code> argument is to be interpreted, while the flags + * modify that interpretation. + * + * The field type characters are: + * + * Field | Integer Format + * ------+-------------------------------------------------------------- + * b | Convert argument as a binary number. + * | Negative numbers will be displayed as a two's complement + * | prefixed with `..1'. + * B | Equivalent to `b', but uses an uppercase 0B for prefix + * | in the alternative format by #. + * d | Convert argument as a decimal number. + * i | Identical to `d'. + * o | Convert argument as an octal number. + * | Negative numbers will be displayed as a two's complement + * | prefixed with `..7'. + * u | Identical to `d'. + * x | Convert argument as a hexadecimal number. + * | Negative numbers will be displayed as a two's complement + * | prefixed with `..f' (representing an infinite string of + * | leading 'ff's). + * X | Equivalent to `x', but uses uppercase letters. + * + * Field | Float Format + * ------+-------------------------------------------------------------- + * e | Convert floating point argument into exponential notation + * | with one digit before the decimal point as [-]d.dddddde[+-]dd. + * | The precision specifies the number of digits after the decimal + * | point (defaulting to six). + * E | Equivalent to `e', but uses an uppercase E to indicate + * | the exponent. + * f | Convert floating point argument as [-]ddd.dddddd, + * | where the precision specifies the number of digits after + * | the decimal point. + * g | Convert a floating point number using exponential form + * | if the exponent is less than -4 or greater than or + * | equal to the precision, or in dd.dddd form otherwise. + * | The precision specifies the number of significant digits. + * G | Equivalent to `g', but use an uppercase `E' in exponent form. + * + * Field | Other Format + * ------+-------------------------------------------------------------- + * c | Argument is the numeric code for a single character or + * | a single character string itself. + * p | The valuing of argument.inspect. + * s | Argument is a string to be substituted. If the format + * | sequence contains a precision, at most that many characters + * | will be copied. + * % | A percent sign itself will be displayed. No argument taken. + * + * The flags modifies the behavior of the formats. + * The flag characters are: + * + * Flag | Applies to | Meaning + * ---------+---------------+----------------------------------------- + * space | bBdiouxX | Leave a space at the start of + * | eEfgG | non-negative numbers. + * | (numeric fmt) | For `o', `x', `X', `b' and `B', use + * | | a minus sign with absolute value for + * | | negative values. + * ---------+---------------+----------------------------------------- + * (digit)$ | all | Specifies the absolute argument number + * | | for this field. Absolute and relative + * | | argument numbers cannot be mixed in a + * | | sprintf string. + * ---------+---------------+----------------------------------------- + * # | bBoxX | Use an alternative format. + * | eEfgG | For the conversions `o', increase the precision + * | | until the first digit will be `0' if + * | | it is not formatted as complements. + * | | For the conversions `x', `X', `b' and `B' + * | | on non-zero, prefix the result with ``0x'', + * | | ``0X'', ``0b'' and ``0B'', respectively. + * | | For `e', `E', `f', `g', and 'G', + * | | force a decimal point to be added, + * | | even if no digits follow. + * | | For `g' and 'G', do not remove trailing zeros. + * ---------+---------------+----------------------------------------- + * + | bBdiouxX | Add a leading plus sign to non-negative + * | eEfgG | numbers. + * | (numeric fmt) | For `o', `x', `X', `b' and `B', use + * | | a minus sign with absolute value for + * | | negative values. + * ---------+---------------+----------------------------------------- + * - | all | Left-justify the result of this conversion. + * ---------+---------------+----------------------------------------- + * 0 (zero) | bBdiouxX | Pad with zeros, not spaces. + * | eEfgG | For `o', `x', `X', `b' and `B', radix-1 + * | (numeric fmt) | is used for negative numbers formatted as + * | | complements. + * ---------+---------------+----------------------------------------- + * * | all | Use the next argument as the field width. + * | | If negative, left-justify the result. If the + * | | asterisk is followed by a number and a dollar + * | | sign, use the indicated argument as the width. + * + * Examples of flags: + * + * # `+' and space flag specifies the sign of non-negative numbers. + * sprintf("%d", 123) #=> "123" + * sprintf("%+d", 123) #=> "+123" + * sprintf("% d", 123) #=> " 123" + * + * # `#' flag for `o' increases number of digits to show `0'. + * # `+' and space flag changes format of negative numbers. + * sprintf("%o", 123) #=> "173" + * sprintf("%#o", 123) #=> "0173" + * sprintf("%+o", -123) #=> "-173" + * sprintf("%o", -123) #=> "..7605" + * sprintf("%#o", -123) #=> "..7605" + * + * # `#' flag for `x' add a prefix `0x' for non-zero numbers. + * # `+' and space flag disables complements for negative numbers. + * sprintf("%x", 123) #=> "7b" + * sprintf("%#x", 123) #=> "0x7b" + * sprintf("%+x", -123) #=> "-7b" + * sprintf("%x", -123) #=> "..f85" + * sprintf("%#x", -123) #=> "0x..f85" + * sprintf("%#x", 0) #=> "0" + * + * # `#' for `X' uses the prefix `0X'. + * sprintf("%X", 123) #=> "7B" + * sprintf("%#X", 123) #=> "0X7B" + * + * # `#' flag for `b' add a prefix `0b' for non-zero numbers. + * # `+' and space flag disables complements for negative numbers. + * sprintf("%b", 123) #=> "1111011" + * sprintf("%#b", 123) #=> "0b1111011" + * sprintf("%+b", -123) #=> "-1111011" + * sprintf("%b", -123) #=> "..10000101" + * sprintf("%#b", -123) #=> "0b..10000101" + * sprintf("%#b", 0) #=> "0" + * + * # `#' for `B' uses the prefix `0B'. + * sprintf("%B", 123) #=> "1111011" + * sprintf("%#B", 123) #=> "0B1111011" + * + * # `#' for `e' forces to show the decimal point. + * sprintf("%.0e", 1) #=> "1e+00" + * sprintf("%#.0e", 1) #=> "1.e+00" + * + * # `#' for `f' forces to show the decimal point. + * sprintf("%.0f", 1234) #=> "1234" + * sprintf("%#.0f", 1234) #=> "1234." + * + * # `#' for `g' forces to show the decimal point. + * # It also disables stripping lowest zeros. + * sprintf("%g", 123.4) #=> "123.4" + * sprintf("%#g", 123.4) #=> "123.400" + * sprintf("%g", 123456) #=> "123456" + * sprintf("%#g", 123456) #=> "123456." + * + * The field width is an optional integer, followed optionally by a + * period and a precision. The width specifies the minimum number of + * characters that will be written to the result for this field. + * + * Examples of width: + * + * # padding is done by spaces, width=20 + * # 0 or radix-1. <------------------> + * sprintf("%20d", 123) #=> " 123" + * sprintf("%+20d", 123) #=> " +123" + * sprintf("%020d", 123) #=> "00000000000000000123" + * sprintf("%+020d", 123) #=> "+0000000000000000123" + * sprintf("% 020d", 123) #=> " 0000000000000000123" + * sprintf("%-20d", 123) #=> "123 " + * sprintf("%-+20d", 123) #=> "+123 " + * sprintf("%- 20d", 123) #=> " 123 " + * sprintf("%020x", -123) #=> "..ffffffffffffffff85" + * + * For + * numeric fields, the precision controls the number of decimal places + * displayed. For string fields, the precision determines the maximum + * number of characters to be copied from the string. (Thus, the format + * sequence <code>%10.10s</code> will always contribute exactly ten + * characters to the result.) + * + * Examples of precisions: + * + * # precision for `d', 'o', 'x' and 'b' is + * # minimum number of digits <------> + * sprintf("%20.8d", 123) #=> " 00000123" + * sprintf("%20.8o", 123) #=> " 00000173" + * sprintf("%20.8x", 123) #=> " 0000007b" + * sprintf("%20.8b", 123) #=> " 01111011" + * sprintf("%20.8d", -123) #=> " -00000123" + * sprintf("%20.8o", -123) #=> " ..777605" + * sprintf("%20.8x", -123) #=> " ..ffff85" + * sprintf("%20.8b", -11) #=> " ..110101" + * + * # "0x" and "0b" for `#x' and `#b' is not counted for + * # precision but "0" for `#o' is counted. <------> + * sprintf("%#20.8d", 123) #=> " 00000123" + * sprintf("%#20.8o", 123) #=> " 00000173" + * sprintf("%#20.8x", 123) #=> " 0x0000007b" + * sprintf("%#20.8b", 123) #=> " 0b01111011" + * sprintf("%#20.8d", -123) #=> " -00000123" + * sprintf("%#20.8o", -123) #=> " ..777605" + * sprintf("%#20.8x", -123) #=> " 0x..ffff85" + * sprintf("%#20.8b", -11) #=> " 0b..110101" + * + * # precision for `e' is number of + * # digits after the decimal point <------> + * sprintf("%20.8e", 1234.56789) #=> " 1.23456789e+03" + * + * # precision for `f' is number of + * # digits after the decimal point <------> + * sprintf("%20.8f", 1234.56789) #=> " 1234.56789000" + * + * # precision for `g' is number of + * # significant digits <-------> + * sprintf("%20.8g", 1234.56789) #=> " 1234.5679" + * + * # <-------> + * sprintf("%20.8g", 123456789) #=> " 1.2345679e+08" + * + * # precision for `s' is + * # maximum number of characters <------> + * sprintf("%20.8s", "string test") #=> " string t" + * + * Examples: + * + * sprintf("%d %04x", 123, 123) #=> "123 007b" + * sprintf("%08b '%4s'", 123, 123) #=> "01111011 ' 123'" + * sprintf("%1$*2$s %2$d %1$s", "hello", 8) #=> " hello 8 hello" + * sprintf("%1$*2$s %2$d", "hello", -8) #=> "hello -8" + * sprintf("%+g:% g:%-g", 1.23, 1.23, 1.23) #=> "+1.23: 1.23:1.23" + * sprintf("%u", -123) #=> "-123" + */ + +VALUE +rb_f_sprintf(int argc, const VALUE *argv) +{ + return rb_str_format(argc - 1, argv + 1, GETNTHARG(0)); +} + +VALUE rb_hash_lookup(VALUE, VALUE); +VALUE rb_str_new_empty(void); + +VALUE +rb_str_format(int argc, const VALUE *argv, VALUE fmt) +{ + const UniChar *p, *end; + VALUE result; + + int width, prec, flags = FNONE; + int nextarg = 1; + int posarg = 0; + int tainted = 0; + VALUE nextvalue; + VALUE tmp; + VALUE str; + volatile VALUE hash = Qundef; + + CFIndex leng; + +#define CHECK_FOR_WIDTH(f) \ + if ((f) & FWIDTH) { \ + rb_raise(rb_eArgError, "width given twice"); \ + } \ + if ((f) & FPREC0) { \ + rb_raise(rb_eArgError, "width after precision"); \ + } +#define CHECK_FOR_FLAGS(f) \ + if ((f) & FWIDTH) { \ + rb_raise(rb_eArgError, "flag after width"); \ + } \ + if ((f) & FPREC0) { \ + rb_raise(rb_eArgError, "flag after precision"); \ + } + + ++argc; + --argv; + if (OBJ_TAINTED(fmt)) tainted = 1; + StringValue(fmt); + leng = CFStringGetLength((CFStringRef)fmt); + if ((p = CFStringGetCharactersPtr((CFStringRef)fmt)) == NULL) { + CFIndex maxlen = CFStringGetMaximumSizeForEncoding(leng, + kCFStringEncodingUnicode); + UniChar *pp = (UniChar *)alloca(sizeof(UniChar) * (maxlen + 1)); + if (!pp) { + rb_raise(rb_eRuntimeError, + "out of memory converting format to Unicode"); + } + CFStringGetCharacters((CFStringRef)fmt, CFRangeMake(0, leng), pp); + pp[leng] = 0; + p = pp; + } + end = p + leng; + result = rb_str_new_empty(); + + for (; p < end; p++) { + const UniChar *t; + int n; + + for (t = p; t < end && *t != '%'; t++) ; + PUSH(p, t - p); + if (t >= end) { + /* end of fmt string */ + goto sprint_exit; + } + p = t + 1; /* skip `%' */ + + width = prec = -1; + nextvalue = Qundef; + retry: + switch (*p) { + default: + if (isprint(*p)) + rb_raise(rb_eArgError, "malformed format string - %%%c", *p); + else + rb_raise(rb_eArgError, "malformed format string"); + break; + + case ' ': + CHECK_FOR_FLAGS(flags); + flags |= FSPACE; + p++; + goto retry; + + case '#': + CHECK_FOR_FLAGS(flags); + flags |= FSHARP; + p++; + goto retry; + + case '+': + CHECK_FOR_FLAGS(flags); + flags |= FPLUS; + p++; + goto retry; + + case '-': + CHECK_FOR_FLAGS(flags); + flags |= FMINUS; + p++; + goto retry; + + case '0': + CHECK_FOR_FLAGS(flags); + flags |= FZERO; + p++; + goto retry; + + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = 0; + GETNUM(n, width); + if (*p == '$') { + if (nextvalue != Qundef) { + rb_raise(rb_eArgError, "value given twice - %d$", n); + } + nextvalue = GETPOSARG(n); + p++; + goto retry; + } + CHECK_FOR_WIDTH(flags); + width = n; + flags |= FWIDTH; + goto retry; + + case '<': + case '{': + { + const UniChar *start = p; + char term = (*p == '<') ? '>' : '}'; + VALUE key; + + for (; p < end && *p != term; ) { + p++; + } + if (p >= end) { + rb_raise(rb_eArgError, "malformed name - unmatched parenthesis"); + } + CFStringRef sname = CFStringCreateWithCharactersNoCopy(NULL, + start + 1, p - start - 1, kCFAllocatorNull); + if (!sname) { + rb_raise(rb_eRuntimeError, + "can't create CFStringRef of variable name"); + } + const char *utf8name = CFStringGetCStringPtr((CFStringRef)sname, + kCFStringEncodingUTF8); + if (!utf8name) { + CFIndex maxlen = CFStringGetMaximumSizeForEncoding( + CFStringGetLength(sname), + kCFStringEncodingUTF8); + utf8name = (const char *)alloca(sizeof(char) * (maxlen + 1)); + if (!utf8name) { + rb_raise(rb_eRuntimeError, + "out of memory converting variable name"); + } + if (!CFStringGetCString(sname, (char *)utf8name, maxlen + 1, + kCFStringEncodingUTF8)) { + rb_raise(rb_eRuntimeError, + "can't get c string from Unicode name"); + } + } + key = rb_str_new2(utf8name); + nextvalue = GETNAMEARG(key); + CFRelease(sname); + if (term == '}') goto format_s; + p++; + goto retry; + } + + case '*': + CHECK_FOR_WIDTH(flags); + flags |= FWIDTH; + GETASTER(width); + if (width < 0) { + flags |= FMINUS; + width = -width; + } + p++; + goto retry; + + case '.': + if (flags & FPREC0) { + rb_raise(rb_eArgError, "precision given twice"); + } + flags |= FPREC|FPREC0; + + prec = 0; + p++; + if (*p == '*') { + GETASTER(prec); + if (prec < 0) { /* ignore negative precision */ + flags &= ~FPREC; + } + p++; + goto retry; + } + + GETNUM(prec, precision); + goto retry; + + case '\n': + case '\0': + p--; + case '%': + if (flags != FNONE) { + rb_raise(rb_eArgError, "invalid format character - %%"); + } + PUSH_CSTR("%"); + break; + + case 'c': + { + VALUE val = GETARG(); + VALUE tmp; + UniChar c; + + tmp = rb_check_string_type(val); + if (!NIL_P(tmp)) { + if (CFStringGetLength((CFStringRef)tmp) != 1) { + rb_raise(rb_eArgError, "%%c requires a character"); + } + c = CFStringGetCharacterAtIndex((CFStringRef)tmp, 0); + } + else { + c = NUM2INT(val); + } + n = 1; + if (!(flags & FWIDTH)) width = 1; + CFStringAppendFormat((CFMutableStringRef)result, NULL, + (flags & FMINUS) ? CFSTR("%-*c") : CFSTR("%*c"), + width, c); + } + break; + + // %@ ignores all flags, widths and precisions + case '@': + { + VALUE arg = GETARG(); + CFStringAppendFormat((CFMutableStringRef)result, NULL, + CFSTR("%@"), arg); + } + break; + + case 's': + case 'p': + format_s: + { + VALUE arg = GETARG(); + CFIndex len; + + if (*p == 'p') arg = rb_inspect(arg); + str = rb_obj_as_string(arg); + if (OBJ_TAINTED(str)) tainted = 1; + len = CFStringGetLength((CFStringRef)str); + if (!(flags&FPREC) || prec > len) prec = len; + if (!(flags&FWIDTH)) width = prec; + if (!(flags&FMINUS)) FILL(" ", width - prec); + CFStringRef sub = NULL; + if (prec > 0) { + if (prec < len) { + sub = CFStringCreateWithSubstring(NULL, + (CFStringRef)str, CFRangeMake(0, prec)); + if (sub == NULL) { + rb_raise(rb_eRuntimeError, + "can't create substring"); + } + str = (VALUE)sub; + } + CFStringAppend((CFMutableStringRef)result, + (CFStringRef)str); + if (sub) CFRelease(sub); + } + if (flags&FMINUS) FILL(" ", width - prec); + } + break; + + case 'd': + case 'i': + case 'o': + case 'x': + case 'X': + case 'b': + case 'B': + case 'u': + { + volatile VALUE tmp1; + volatile VALUE val = GETARG(); + char fbuf[32], nbuf[64], *s; + const char *prefix = 0; + int sign = 0, dots = 0; + const char *sc = NULL; + long v = 0; + int base, bignum = 0; + int len, pos; + + switch (*p) { + case 'd': + case 'i': + case 'u': + sign = 1; break; + case 'o': + case 'x': + case 'X': + case 'b': + case 'B': + if (flags&(FPLUS|FSPACE)) sign = 1; + break; + } + if (flags & FSHARP) { + switch (*p) { + case 'o': + prefix = "0"; break; + case 'x': + prefix = "0x"; break; + case 'X': + prefix = "0X"; break; + case 'b': + prefix = "0b"; break; + case 'B': + prefix = "0B"; break; + } + } + + bin_retry: + switch (TYPE(val)) { + case T_FLOAT: + if (FIXABLE(RFLOAT_VALUE(val))) { + val = LONG2FIX((long)RFLOAT_VALUE(val)); + goto bin_retry; + } + val = rb_dbl2big(RFLOAT_VALUE(val)); + if (FIXNUM_P(val)) goto bin_retry; + bignum = 1; + break; + case T_STRING: + val = rb_str_to_inum(val, 0, Qtrue); + goto bin_retry; + case T_BIGNUM: + bignum = 1; + break; + case T_FIXNUM: + v = FIX2LONG(val); + break; + default: + val = rb_Integer(val); + goto bin_retry; + } + + switch (*p) { + case 'o': + base = 8; break; + case 'x': + case 'X': + base = 16; break; + case 'b': + case 'B': + base = 2; break; + case 'u': + case 'd': + case 'i': + default: + base = 10; break; + } + + if (!bignum) { + if (base == 2) { + val = rb_int2big(v); + goto bin_retry; + } + if (sign) { + char c = *p; + if (c == 'i') c = 'd'; /* %d and %i are identical */ + if (v < 0) { + v = -v; + sc = "-"; + width--; + } + else if (flags & FPLUS) { + sc = "+"; + width--; + } + else if (flags & FSPACE) { + sc = " "; + width--; + } + snprintf(fbuf, sizeof(fbuf), "%%l%c", c); + snprintf(nbuf, sizeof(nbuf), fbuf, v); + s = nbuf; + } + else { + s = nbuf; + if (v < 0) { + dots = 1; + } + snprintf(fbuf, sizeof(fbuf), "%%l%c", *p == 'X' ? 'x' : *p); + snprintf(++s, sizeof(nbuf) - 1, fbuf, v); + if (v < 0) { + char d = 0; + + s = remove_sign_bits(s, base); + switch (base) { + case 16: + d = 'f'; break; + case 8: + d = '7'; break; + } + if (d && *s != d) { + *--s = d; + } + } + } + } + else { + if (sign) { + tmp = rb_big2str(val, base); + s = (char *)RSTRING_PTR(tmp); + if (s[0] == '-') { + s++; + sc = "-"; + width--; + } + else if (flags & FPLUS) { + sc = "+"; + width--; + } + else if (flags & FSPACE) { + sc = " "; + width--; + } + } + else { + if (!RBIGNUM_SIGN(val)) { + val = rb_big_clone(val); + rb_big_2comp(val); + } + tmp1 = tmp = rb_big2str0(val, base, RBIGNUM_SIGN(val)); + s = (char *)RSTRING_PTR(tmp); + if (*s == '-') { + dots = 1; + if (base == 10) { + rb_warning("negative number for %%u specifier"); + } + s = remove_sign_bits(++s, base); + switch (base) { + case 16: + if (s[0] != 'f') *--s = 'f'; break; + case 8: + if (s[0] != '7') *--s = '7'; break; + case 2: + if (s[0] != '1') *--s = '1'; break; + } + } + } + } + + pos = -1; + len = strlen(s); + if (dots) { + prec -= 2; + width -= 2; + } + + if (*p == 'X') { + char *pp = s; + int c; + while ((c = (int)(unsigned char)*pp) != 0) { + *pp = toupper(c); + pp++; + } + } + if (prefix && !prefix[1]) { /* octal */ + if (dots) { + prefix = 0; + } + else if (len == 1 && *s == '0') { + len = 0; + if (flags & FPREC) prec--; + } + else if ((flags & FPREC) && (prec > len)) { + prefix = 0; + } + } + else if (len == 1 && *s == '0') { + prefix = 0; + } + if (prefix) { + width -= strlen(prefix); + } + if ((flags & (FZERO|FMINUS|FPREC)) == FZERO) { + prec = width; + width = 0; + } + else { + if (prec < len) { + if (!prefix && prec == 0 && len == 1 && *s == '0') len = 0; + prec = len; + } + width -= prec; + } + if (!(flags&FMINUS) && width > 0) { + FILL(" ", width); + width = 0; + } + if (sc) PUSH_CSTR(sc); + if (prefix) { + PUSH_CSTR(prefix); + } + if (dots) PUSH_CSTR(".."); + if (!bignum && v < 0) { + FILL_SIGN_BITS(base, p, prec - len); + } + else if ((flags & (FMINUS|FPREC)) != FMINUS) { + if (!sign && bignum && !RBIGNUM_SIGN(val)) { + FILL_SIGN_BITS(base, p, prec - len); + } else { + FILL("0", prec - len); + } + } + PUSH_CSTR_LEN(s, len); + FILL(" ", width); + } + break; + + case 'a': + case 'A': + case 'f': + case 'g': + case 'G': + case 'e': + case 'E': + { + VALUE val = GETARG(); + double fval; + int need = 6; + char fbuf[32]; + + fval = RFLOAT_VALUE(rb_Float(val)); + if (isnan(fval) || isinf(fval)) { + const char *expr; + + if (isnan(fval)) { + expr = "NaN"; + } + else { + expr = "Inf"; + } + need = strlen(expr); + if ((!isnan(fval) && fval < 0.0) || (flags & (FPLUS|FSPACE))) + need++; + if ((flags & FWIDTH) && need < width) + need = width; + + if (flags & FMINUS) { + if (!isnan(fval) && fval < 0.0) { + PUSH_CSTR("-"); + need--; + } else if (flags & FPLUS) { + PUSH_CSTR("+"); + need--; + } else if (flags & FSPACE) { + PUSH_CSTR(" "); + need--; + } + PUSH_CSTR(expr); + need -= strlen(expr); + FILL(" ", need); + } + else { + FILL(" ", need - strlen(expr) - 1); + if (!isnan(fval) && fval < 0.0) + PUSH_CSTR("-"); + else if (flags & FPLUS) + PUSH_CSTR("+"); + else if ((flags & FSPACE) && need > width) + PUSH_CSTR(" "); + PUSH_CSTR(expr); + } + break; + } + + fmt_setup(fbuf, sizeof(fbuf), *p, flags, width, prec); + + char *fout; + asprintf(&fout, fbuf, fval); + if (!fout) { + rb_raise(rb_eRuntimeError, + "out of memory converting floating point value"); + } + PUSH_CSTR(fout); + free(fout); + } + break; + } + flags = FNONE; + } + + sprint_exit: + /* XXX - We cannot validate the number of arguments if (digit)$ style used. + */ + if (posarg >= 0 && nextarg < argc) { + const char *mesg = "too many arguments for format string"; + if (RTEST(ruby_debug)) rb_raise(rb_eArgError, "%s", mesg); + if (RTEST(ruby_verbose)) rb_warn("%s", mesg); + } + + if (tainted) OBJ_TAINT(result); + return result; +} + +static void +fmt_setup(char *buf, size_t size, int c, int flags, int width, int prec) +{ + char *end = buf + size; + *buf++ = '%'; + if (flags & FSHARP) *buf++ = '#'; + if (flags & FPLUS) *buf++ = '+'; + if (flags & FMINUS) *buf++ = '-'; + if (flags & FZERO) *buf++ = '0'; + if (flags & FSPACE) *buf++ = ' '; + + if (flags & FWIDTH) { + snprintf(buf, end - buf, "%d", width); + buf += strlen(buf); + } + + if (flags & FPREC) { + snprintf(buf, end - buf, ".%d", prec); + buf += strlen(buf); + } + + *buf++ = c; + *buf = '\0'; +} + +VALUE +rb_f_sprintf_imp(VALUE recv, SEL sel, int argc, VALUE *argv) +{ + return rb_str_format(argc - 1, argv + 1, GETNTHARG(0)); +} + +VALUE +rb_enc_vsprintf(rb_encoding *enc, const char *fmt, va_list ap) +{ + char buffer[512]; + int n; + n = vsnprintf(buffer, sizeof buffer, fmt, ap); + return rb_enc_str_new(buffer, n, enc); +} + +VALUE +rb_enc_sprintf(rb_encoding *enc, const char *format, ...) +{ + VALUE result; + va_list ap; + + va_start(ap, format); + result = rb_enc_vsprintf(enc, format, ap); + va_end(ap); + + return result; +} + +VALUE +rb_vsprintf(const char *fmt, va_list ap) +{ + return rb_enc_vsprintf(NULL, fmt, ap); +} + +VALUE +rb_sprintf(const char *format, ...) +{ + VALUE result; + va_list ap; + + va_start(ap, format); + result = rb_vsprintf(format, ap); + va_end(ap); + + return result; +} Deleted: MacRuby/branches/ticket159/sprintf.cpp =================================================================== --- MacRuby/branches/ticket159/sprintf.cpp 2009-12-11 17:09:10 UTC (rev 3104) +++ MacRuby/branches/ticket159/sprintf.cpp 2009-12-12 02:07:37 UTC (rev 3105) @@ -1,498 +0,0 @@ -/* - * MacRuby implementation of Ruby 1.9's sprintf.c. - * - * This file is covered by the Ruby license. See COPYING for more details. - * - * Copyright (C) 2007-2009, Apple Inc. All rights reserved. - * Copyright (C) 1993-2007 Yukihiro Matsumoto - * Copyright (C) 2000 Network Applied Communication Laboratory, Inc. - * Copyright (C) 2000 Information-technology Promotion Agency, Japan - */ - -#include <stdarg.h> - -#include "llvm.h" -#include "ruby/ruby.h" -#include "ruby/node.h" -#include "ruby/encoding.h" -#include "vm.h" -#include "compiler.h" - -/* - * call-seq: - * format(format_string [, arguments...] ) => string - * sprintf(format_string [, arguments...] ) => string - * - * Returns the string resulting from applying <i>format_string</i> to - * any additional arguments. Within the format string, any characters - * other than format sequences are copied to the result. - * - * The syntax of a format sequence is follows. - * - * %[flags][width][.precision]type - * - * A format - * sequence consists of a percent sign, followed by optional flags, - * width, and precision indicators, then terminated with a field type - * character. The field type controls how the corresponding - * <code>sprintf</code> argument is to be interpreted, while the flags - * modify that interpretation. - * - * The field type characters are: - * - * Field | Integer Format - * ------+-------------------------------------------------------------- - * b | Convert argument as a binary number. - * | Negative numbers will be displayed as a two's complement - * | prefixed with `..1'. - * B | Equivalent to `b', but uses an uppercase 0B for prefix - * | in the alternative format by #. - * d | Convert argument as a decimal number. - * i | Identical to `d'. - * o | Convert argument as an octal number. - * | Negative numbers will be displayed as a two's complement - * | prefixed with `..7'. - * u | Identical to `d'. - * x | Convert argument as a hexadecimal number. - * | Negative numbers will be displayed as a two's complement - * | prefixed with `..f' (representing an infinite string of - * | leading 'ff's). - * X | Equivalent to `x', but uses uppercase letters. - * - * Field | Float Format - * ------+-------------------------------------------------------------- - * e | Convert floating point argument into exponential notation - * | with one digit before the decimal point as [-]d.dddddde[+-]dd. - * | The precision specifies the number of digits after the decimal - * | point (defaulting to six). - * E | Equivalent to `e', but uses an uppercase E to indicate - * | the exponent. - * f | Convert floating point argument as [-]ddd.dddddd, - * | where the precision specifies the number of digits after - * | the decimal point. - * g | Convert a floating point number using exponential form - * | if the exponent is less than -4 or greater than or - * | equal to the precision, or in dd.dddd form otherwise. - * | The precision specifies the number of significant digits. - * G | Equivalent to `g', but use an uppercase `E' in exponent form. - * - * Field | Other Format - * ------+-------------------------------------------------------------- - * c | Argument is the numeric code for a single character or - * | a single character string itself. - * p | The valuing of argument.inspect. - * s | Argument is a string to be substituted. If the format - * | sequence contains a precision, at most that many characters - * | will be copied. - * - * The flags modifies the behavior of the formats. - * The flag characters are: - * - * Flag | Applies to | Meaning - * ---------+---------------+----------------------------------------- - * space | bBdiouxX | Leave a space at the start of - * | eEfgG | non-negative numbers. - * | (numeric fmt) | For `o', `x', `X', `b' and `B', use - * | | a minus sign with absolute value for - * | | negative values. - * ---------+---------------+----------------------------------------- - * (digit)$ | all | Specifies the absolute argument number - * | | for this field. Absolute and relative - * | | argument numbers cannot be mixed in a - * | | sprintf string. - * ---------+---------------+----------------------------------------- - * # | bBoxX | Use an alternative format. - * | eEfgG | For the conversions `o', increase the precision - * | | until the first digit will be `0' if - * | | it is not formatted as complements. - * | | For the conversions `x', `X', `b' and `B' - * | | on non-zero, prefix the result with ``0x'', - * | | ``0X'', ``0b'' and ``0B'', respectively. - * | | For `e', `E', `f', `g', and 'G', - * | | force a decimal point to be added, - * | | even if no digits follow. - * | | For `g' and 'G', do not remove trailing zeros. - * ---------+---------------+----------------------------------------- - * + | bBdiouxX | Add a leading plus sign to non-negative - * | eEfgG | numbers. - * | (numeric fmt) | For `o', `x', `X', `b' and `B', use - * | | a minus sign with absolute value for - * | | negative values. - * ---------+---------------+----------------------------------------- - * - | all | Left-justify the result of this conversion. - * ---------+---------------+----------------------------------------- - * 0 (zero) | bBdiouxX | Pad with zeros, not spaces. - * | eEfgG | For `o', `x', `X', `b' and `B', radix-1 - * | (numeric fmt) | is used for negative numbers formatted as - * | | complements. - * ---------+---------------+----------------------------------------- - * * | all | Use the next argument as the field width. - * | | If negative, left-justify the result. If the - * | | asterisk is followed by a number and a dollar - * | | sign, use the indicated argument as the width. - * - * Examples of flags: - * - * # `+' and space flag specifies the sign of non-negative numbers. - * sprintf("%d", 123) #=> "123" - * sprintf("%+d", 123) #=> "+123" - * sprintf("% d", 123) #=> " 123" - * - * # `#' flag for `o' increases number of digits to show `0'. - * # `+' and space flag changes format of negative numbers. - * sprintf("%o", 123) #=> "173" - * sprintf("%#o", 123) #=> "0173" - * sprintf("%+o", -123) #=> "-173" - * sprintf("%o", -123) #=> "..7605" - * sprintf("%#o", -123) #=> "..7605" - * - * # `#' flag for `x' add a prefix `0x' for non-zero numbers. - * # `+' and space flag disables complements for negative numbers. - * sprintf("%x", 123) #=> "7b" - * sprintf("%#x", 123) #=> "0x7b" - * sprintf("%+x", -123) #=> "-7b" - * sprintf("%x", -123) #=> "..f85" - * sprintf("%#x", -123) #=> "0x..f85" - * sprintf("%#x", 0) #=> "0" - * - * # `#' for `X' uses the prefix `0X'. - * sprintf("%X", 123) #=> "7B" - * sprintf("%#X", 123) #=> "0X7B" - * - * # `#' flag for `b' add a prefix `0b' for non-zero numbers. - * # `+' and space flag disables complements for negative numbers. - * sprintf("%b", 123) #=> "1111011" - * sprintf("%#b", 123) #=> "0b1111011" - * sprintf("%+b", -123) #=> "-1111011" - * sprintf("%b", -123) #=> "..10000101" - * sprintf("%#b", -123) #=> "0b..10000101" - * sprintf("%#b", 0) #=> "0" - * - * # `#' for `B' uses the prefix `0B'. - * sprintf("%B", 123) #=> "1111011" - * sprintf("%#B", 123) #=> "0B1111011" - * - * # `#' for `e' forces to show the decimal point. - * sprintf("%.0e", 1) #=> "1e+00" - * sprintf("%#.0e", 1) #=> "1.e+00" - * - * # `#' for `f' forces to show the decimal point. - * sprintf("%.0f", 1234) #=> "1234" - * sprintf("%#.0f", 1234) #=> "1234." - * - * # `#' for `g' forces to show the decimal point. - * # It also disables stripping lowest zeros. - * sprintf("%g", 123.4) #=> "123.4" - * sprintf("%#g", 123.4) #=> "123.400" - * sprintf("%g", 123456) #=> "123456" - * sprintf("%#g", 123456) #=> "123456." - * - * The field width is an optional integer, followed optionally by a - * period and a precision. The width specifies the minimum number of - * characters that will be written to the result for this field. - * - * Examples of width: - * - * # padding is done by spaces, width=20 - * # 0 or radix-1. <------------------> - * sprintf("%20d", 123) #=> " 123" - * sprintf("%+20d", 123) #=> " +123" - * sprintf("%020d", 123) #=> "00000000000000000123" - * sprintf("%+020d", 123) #=> "+0000000000000000123" - * sprintf("% 020d", 123) #=> " 0000000000000000123" - * sprintf("%-20d", 123) #=> "123 " - * sprintf("%-+20d", 123) #=> "+123 " - * sprintf("%- 20d", 123) #=> " 123 " - * sprintf("%020x", -123) #=> "..ffffffffffffffff85" - * - * For - * numeric fields, the precision controls the number of decimal places - * displayed. For string fields, the precision determines the maximum - * number of characters to be copied from the string. (Thus, the format - * sequence <code>%10.10s</code> will always contribute exactly ten - * characters to the result.) - * - * Examples of precisions: - * - * # precision for `d', 'o', 'x' and 'b' is - * # minimum number of digits <------> - * sprintf("%20.8d", 123) #=> " 00000123" - * sprintf("%20.8o", 123) #=> " 00000173" - * sprintf("%20.8x", 123) #=> " 0000007b" - * sprintf("%20.8b", 123) #=> " 01111011" - * sprintf("%20.8d", -123) #=> " -00000123" - * sprintf("%20.8o", -123) #=> " ..777605" - * sprintf("%20.8x", -123) #=> " ..ffff85" - * sprintf("%20.8b", -11) #=> " ..110101" - * - * # "0x" and "0b" for `#x' and `#b' is not counted for - * # precision but "0" for `#o' is counted. <------> - * sprintf("%#20.8d", 123) #=> " 00000123" - * sprintf("%#20.8o", 123) #=> " 00000173" - * sprintf("%#20.8x", 123) #=> " 0x0000007b" - * sprintf("%#20.8b", 123) #=> " 0b01111011" - * sprintf("%#20.8d", -123) #=> " -00000123" - * sprintf("%#20.8o", -123) #=> " ..777605" - * sprintf("%#20.8x", -123) #=> " 0x..ffff85" - * sprintf("%#20.8b", -11) #=> " 0b..110101" - * - * # precision for `e' is number of - * # digits after the decimal point <------> - * sprintf("%20.8e", 1234.56789) #=> " 1.23456789e+03" - * - * # precision for `f' is number of - * # digits after the decimal point <------> - * sprintf("%20.8f", 1234.56789) #=> " 1234.56789000" - * - * # precision for `g' is number of - * # significant digits <-------> - * sprintf("%20.8g", 1234.56789) #=> " 1234.5679" - * - * # <-------> - * sprintf("%20.8g", 123456789) #=> " 1.2345679e+08" - * - * # precision for `s' is - * # maximum number of characters <------> - * sprintf("%20.8s", "string test") #=> " string t" - * - * Examples: - * - * sprintf("%d %04x", 123, 123) #=> "123 007b" - * sprintf("%08b '%4s'", 123, 123) #=> "01111011 ' 123'" - * sprintf("%1$*2$s %2$d %1$s", "hello", 8) #=> " hello 8 hello" - * sprintf("%1$*2$s %2$d", "hello", -8) #=> "hello -8" - * sprintf("%+g:% g:%-g", 1.23, 1.23, 1.23) #=> "+1.23: 1.23:1.23" - * sprintf("%u", -123) #=> "-123" - */ - -#define GETNTHARG(nth) \ - ((nth >= argc) ? (rb_raise(rb_eArgError, "too few arguments"), 0) : argv[nth]) - -extern "C" { - -VALUE -rb_f_sprintf_imp(VALUE recv, SEL sel, int argc, VALUE *argv) -{ - return rb_str_format(argc - 1, argv + 1, GETNTHARG(0)); -} - -VALUE -rb_f_sprintf(int argc, const VALUE *argv) -{ - return rb_str_format(argc - 1, argv + 1, GETNTHARG(0)); -} - -VALUE -rb_enc_vsprintf(rb_encoding *enc, const char *fmt, va_list ap) -{ - char buffer[512]; - int n; - n = vsnprintf(buffer, sizeof buffer, fmt, ap); - return rb_enc_str_new(buffer, n, enc); -} - -VALUE -rb_enc_sprintf(rb_encoding *enc, const char *format, ...) -{ - VALUE result; - va_list ap; - - va_start(ap, format); - result = rb_enc_vsprintf(enc, format, ap); - va_end(ap); - - return result; -} - -VALUE -rb_vsprintf(const char *fmt, va_list ap) -{ - return rb_enc_vsprintf(NULL, fmt, ap); -} - -VALUE -rb_sprintf(const char *format, ...) -{ - VALUE result; - va_list ap; - - va_start(ap, format); - result = rb_vsprintf(format, ap); - va_end(ap); - - return result; -} - -static void -get_types_for_format_str(std::string &octypes, const unsigned int len, - VALUE *args, const char *format_str, char **new_fmt) -{ - size_t format_str_len = strlen(format_str); - unsigned int i = 0, j = 0; - - while (i < format_str_len) { - bool sharp_modifier = false; - bool star_modifier = false; - if (format_str[i++] != '%') { - continue; - } - if (i < format_str_len && format_str[i] == '%') { - i++; - continue; - } - while (i < format_str_len) { - char type = 0; - switch (format_str[i]) { - case '#': - sharp_modifier = true; - break; - - case '*': - star_modifier = true; - type = _C_INT; - break; - - case 'd': - case 'i': - case 'o': - case 'u': - case 'x': - case 'X': - type = _C_INT; - break; - - case 'c': - case 'C': - type = _C_CHR; - break; - - case 'D': - case 'O': - case 'U': - type = _C_LNG; - break; - - case 'f': - case 'F': - case 'e': - case 'E': - case 'g': - case 'G': - case 'a': - case 'A': - type = _C_DBL; - break; - - case 's': - case 'S': - { - if (i - 1 > 0) { - unsigned long k = i - 1; - while (k > 0 && format_str[k] == '0') { - k--; - } - if (k < i && format_str[k] == '.') { - args[j] = (VALUE)CFSTR(""); - } - } -#if 1 - // In Ruby, '%s' is supposed to convert the argument - // as a string, calling #to_s on it. In order to - // support this behavior we are changing the format - // to '@' which sends the -[NSObject description] - // message, exactly what we want. - if (*new_fmt == NULL) { - *new_fmt = strdup(format_str); - } - (*new_fmt)[i] = '@'; - type = _C_ID; -#else - type = _C_CHARPTR; -#endif - } - break; - - case 'p': - case '@': - type = _C_ID; - break; - - case 'B': - case 'b': - { - VALUE arg = args[j]; - switch (TYPE(arg)) { - case T_STRING: - arg = rb_str_to_inum(arg, 0, Qtrue); - break; - } - arg = rb_big2str(arg, 2); - if (sharp_modifier) { - VALUE prefix = format_str[i] == 'B' - ? (VALUE)CFSTR("0B") : (VALUE)CFSTR("0b"); - rb_str_update(arg, 0, 0, prefix); - } - if (*new_fmt == NULL) { - *new_fmt = strdup(format_str); - } - (*new_fmt)[i] = '@'; - args[j] = arg; - type = _C_ID; - } - break; - } - - i++; - - if (type != 0) { - if (len == 0 || j >= len) { - rb_raise(rb_eArgError, - "Too much tokens in the format string `%s' "\ - "for the given %d argument(s)", format_str, len); - } - octypes.push_back(type); - j++; - if (!star_modifier) { - break; - } - } - } - } - for (; j < len; j++) { - octypes.push_back(_C_ID); - } -} - -VALUE -rb_str_format(int argc, const VALUE *argv, VALUE fmt) -{ - if (argc == 0) { - return fmt; - } - - char *new_fmt = NULL; - std::string types("@@@@"); - get_types_for_format_str(types, (unsigned int)argc, (VALUE *)argv, - RSTRING_PTR(fmt), &new_fmt); - - if (new_fmt != NULL) { - fmt = rb_str_new2(new_fmt); - } - - VALUE *stub_args = (VALUE *)alloca(sizeof(VALUE) * argc + 4); - stub_args[0] = Qnil; // allocator - stub_args[1] = Qnil; // format options - stub_args[2] = fmt; // format string - for (int i = 0; i < argc; i++) { - stub_args[3 + i] = argv[i]; - } - - rb_vm_c_stub_t *stub = (rb_vm_c_stub_t *)GET_CORE()->gen_stub(types, - true, 3, false); - - VALUE str = (*stub)((IMP)&CFStringCreateWithFormat, argc + 3, stub_args); - CFMakeCollectable((void *)str); - return str; -} - -} // extern "C" Modified: MacRuby/branches/ticket159/util.c =================================================================== --- MacRuby/branches/ticket159/util.c 2009-12-11 17:09:10 UTC (rev 3104) +++ MacRuby/branches/ticket159/util.c 2009-12-12 02:07:37 UTC (rev 3105) @@ -759,6 +759,7 @@ return (VALUE)str; } +#ifndef __APPLE__ /**************************************************************** * * The author of this software is David M. Gay. @@ -3925,6 +3926,7 @@ *rve = s; return s0; } +#endif /* !__APPLE__ */ void ruby_each_words(const char *str, void (*func)(const char*, int, void*), void *arg) Modified: MacRuby/branches/ticket159/vm_method.c =================================================================== --- MacRuby/branches/ticket159/vm_method.c 2009-12-11 17:09:10 UTC (rev 3104) +++ MacRuby/branches/ticket159/vm_method.c 2009-12-12 02:07:37 UTC (rev 3105) @@ -783,12 +783,14 @@ } IMP basic_respond_to_imp = NULL; +IMP basicobject_respond_to_imp = NULL; void Init_eval_method(void) { rb_objc_define_method(rb_mKernel, "respond_to?", obj_respond_to, -1); basic_respond_to_imp = class_getMethodImplementation((Class)rb_mKernel, selRespondTo); + basicobject_respond_to_imp = class_getMethodImplementation((Class)rb_cBasicObject, selRespondTo); //basic_respond_to = rb_method_node(rb_cObject, idRespond_to); //rb_register_mark_object((VALUE)basic_respond_to);