From e06407cf7d8647a66c3d514014b71170d54ddca3 Mon Sep 17 00:00:00 2001 From: mrkn Date: Thu, 1 Aug 2013 14:58:54 +0000 Subject: * rational.c (rb_flt_rationalize_with_prec): new public C function to rationalize a Float instance with a precision. * rational.c (rb_flt_rationalize): new public C function to rationalize a Float instance. A precision is calculated from the given float number. * include/ruby/intern.h: Add rb_flt_rationalize_with_prec and rb_flt_rationalize. * parse.y: implement number literal suffixes, 'r' and 'i'. [ruby-core:55096] [Feature #8430] * bootstraptest/test_literal_suffix.rb: add tests for parser to scan number literals with the above tsuffixes. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@42311 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 18 +++++ bootstraptest/test_literal_suffix.rb | 46 +++++++++++ include/ruby/intern.h | 2 + parse.y | 151 ++++++++++++++++++++++++++++------- rational.c | 96 +++++++++++++--------- 5 files changed, 244 insertions(+), 69 deletions(-) create mode 100644 bootstraptest/test_literal_suffix.rb diff --git a/ChangeLog b/ChangeLog index 22907dd9c1..0cabb99b18 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +Thu Aug 1 23:45:00 2013 Kenta Murata + + * rational.c (rb_flt_rationalize_with_prec): new public C function + to rationalize a Float instance with a precision. + + * rational.c (rb_flt_rationalize): new public C function to + rationalize a Float instance. A precision is calculated from + the given float number. + + * include/ruby/intern.h: Add rb_flt_rationalize_with_prec and + rb_flt_rationalize. + + * parse.y: implement number literal suffixes, 'r' and 'i'. + [ruby-core:55096] [Feature #8430] + + * bootstraptest/test_literal_suffix.rb: add tests for parser to scan + number literals with the above tsuffixes. + Thu Aug 1 23:55:08 2013 Tanaka Akira * bignum.c (rb_big2str1): Remove a local variable. diff --git a/bootstraptest/test_literal_suffix.rb b/bootstraptest/test_literal_suffix.rb new file mode 100644 index 0000000000..e466cfc5d2 --- /dev/null +++ b/bootstraptest/test_literal_suffix.rb @@ -0,0 +1,46 @@ +# numbers with suffix +assert_equal '0/1', '0r' +assert_equal 'Rational', '0r.class' +assert_equal '1/1', '1r' +assert_equal 'Rational', '1r.class' +assert_equal '1/1', '0x1r' +assert_equal 'Rational', '0x1r.class' +assert_equal '1/1', '0b1r' +assert_equal 'Rational', '0b1r.class' +assert_equal '1/1', '0d1r' +assert_equal 'Rational', '0d1r.class' +assert_equal '1/1', '0o1r' +assert_equal 'Rational', '0o1r.class' +assert_equal '1/1', '01r' +assert_equal 'Rational', '01r.class' +assert_equal '6/5', '1.2r' +assert_equal 'Rational', '1.2r.class' +assert_equal '0+0i', '0i' +assert_equal 'Complex', '0i.class' +assert_equal '0+1i', '1i' +assert_equal 'Complex', '1i.class' +assert_equal '0+1i', '0x1i' +assert_equal 'Complex', '0x1i.class' +assert_equal '0+1i', '0b1i' +assert_equal 'Complex', '0b1i.class' +assert_equal '0+1i', '0d1i' +assert_equal 'Complex', '0d1i.class' +assert_equal '0+1i', '0o1i' +assert_equal 'Complex', '0o1i.class' +assert_equal '0+1i', '01i' +assert_equal 'Complex', '01i.class' +assert_equal '0+1.2i', '1.2i' +assert_equal 'Complex', '1.2i.class' +assert_equal '0+1/1i', '1ri' +assert_equal 'Complex', '1ri.class' +assert_equal '0+6/5i', '1.2ri' +assert_equal 'Complex', '1.2ri.class' +assert_equal '0+10.0i', '1e1i' +assert_equal 'Complex', '1e1i.class' + +assert_equal 'syntax error, unexpected tIDENTIFIER, expecting end-of-input', + %q{begin eval('1ir', nil, '', 0); rescue SyntaxError => e; e.message[/\A:(?:\d+:)? (.*)/, 1] end} +assert_equal 'syntax error, unexpected tIDENTIFIER, expecting end-of-input', + %q{begin eval('1.2ir', nil, '', 0); rescue SyntaxError => e; e.message[/\A:(?:\d+:)? (.*)/, 1] end} +assert_equal 'syntax error, unexpected tIDENTIFIER, expecting end-of-input', + %q{begin eval('1e1r', nil, '', 0); rescue SyntaxError => e; e.message[/\A:(?:\d+:)? (.*)/, 1] end} diff --git a/include/ruby/intern.h b/include/ruby/intern.h index 4393246744..02213c9072 100644 --- a/include/ruby/intern.h +++ b/include/ruby/intern.h @@ -171,6 +171,8 @@ VALUE rb_rational_new(VALUE, VALUE); VALUE rb_Rational(VALUE, VALUE); #define rb_Rational1(x) rb_Rational((x), INT2FIX(1)) #define rb_Rational2(x,y) rb_Rational((x), (y)) +VALUE rb_flt_rationalize_with_prec(VALUE, VALUE); +VALUE rb_flt_rationalize(VALUE); /* complex.c */ VALUE rb_complex_raw(VALUE, VALUE); #define rb_complex_raw1(x) rb_complex_raw((x), INT2FIX(0)) diff --git a/parse.y b/parse.y index 87029eb2aa..0d11c17dae 100644 --- a/parse.y +++ b/parse.y @@ -754,7 +754,7 @@ static void token_info_pop(struct parser_params*, const char *token); keyword__ENCODING__ %token tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL -%token tINTEGER tFLOAT tSTRING_CONTENT tCHAR +%token tINTEGER tFLOAT tRATIONAL tIMAGINARY tSTRING_CONTENT tCHAR %token tNTH_REF tBACK_REF %token tREGEXP_END @@ -2128,6 +2128,24 @@ arg : lhs '=' arg $$ = dispatch2(unary, ripper_intern("-@"), $$); %*/ } + | tUMINUS_NUM tRATIONAL tPOW arg + { + /*%%%*/ + $$ = NEW_CALL(call_bin_op($2, tPOW, $4), tUMINUS, 0); + /*% + $$ = dispatch3(binary, $2, ripper_intern("**"), $4); + $$ = dispatch2(unary, ripper_intern("-@"), $$); + %*/ + } + | tUMINUS_NUM tIMAGINARY tPOW arg + { + /*%%%*/ + $$ = NEW_CALL(call_bin_op($2, tPOW, $4), tUMINUS, 0); + /*% + $$ = dispatch3(binary, $2, ripper_intern("**"), $4); + $$ = dispatch2(unary, ripper_intern("-@"), $$); + %*/ + } | tUPLUS arg { /*%%%*/ @@ -4294,6 +4312,8 @@ dsym : tSYMBEG xstring_contents tSTRING_END numeric : tINTEGER | tFLOAT + | tRATIONAL + | tIMAGINARY | tUMINUS_NUM tINTEGER %prec tLOWEST { /*%%%*/ @@ -4310,6 +4330,22 @@ numeric : tINTEGER $$ = dispatch2(unary, ripper_intern("-@"), $2); %*/ } + | tUMINUS_NUM tRATIONAL %prec tLOWEST + { + /*%%%*/ + $$ = negate_lit($2); + /*% + $$ = dispatch2(unary, ripper_intern("-@"), $2); + %*/ + } + | tUMINUS_NUM tIMAGINARY %prec tLOWEST + { + /*%%%*/ + $$ = negate_lit($2); + /*% + $$ = dispatch2(unary, ripper_intern("-@"), $2); + %*/ + } ; user_variable : tIDENTIFIER @@ -5020,22 +5056,23 @@ static int parser_parse_string(struct parser_params*,NODE*); static int parser_here_document(struct parser_params*,NODE*); -# define nextc() parser_nextc(parser) -# define pushback(c) parser_pushback(parser, (c)) -# define newtok() parser_newtok(parser) -# define tokspace(n) parser_tokspace(parser, (n)) -# define tokadd(c) parser_tokadd(parser, (c)) -# define tok_hex(numlen) parser_tok_hex(parser, (numlen)) -# define read_escape(flags,e) parser_read_escape(parser, (flags), (e)) -# define tokadd_escape(e) parser_tokadd_escape(parser, (e)) -# define regx_options() parser_regx_options(parser) -# define tokadd_string(f,t,p,n,e) parser_tokadd_string(parser,(f),(t),(p),(n),(e)) -# define parse_string(n) parser_parse_string(parser,(n)) -# define tokaddmbc(c, enc) parser_tokaddmbc(parser, (c), (enc)) -# define here_document(n) parser_here_document(parser,(n)) -# define heredoc_identifier() parser_heredoc_identifier(parser) -# define heredoc_restore(n) parser_heredoc_restore(parser,(n)) -# define whole_match_p(e,l,i) parser_whole_match_p(parser,(e),(l),(i)) +# define nextc() parser_nextc(parser) +# define pushback(c) parser_pushback(parser, (c)) +# define newtok() parser_newtok(parser) +# define tokspace(n) parser_tokspace(parser, (n)) +# define tokadd(c) parser_tokadd(parser, (c)) +# define tok_hex(numlen) parser_tok_hex(parser, (numlen)) +# define read_escape(flags,e) parser_read_escape(parser, (flags), (e)) +# define tokadd_escape(e) parser_tokadd_escape(parser, (e)) +# define regx_options() parser_regx_options(parser) +# define tokadd_string(f,t,p,n,e) parser_tokadd_string(parser,(f),(t),(p),(n),(e)) +# define parse_string(n) parser_parse_string(parser,(n)) +# define tokaddmbc(c, enc) parser_tokaddmbc(parser, (c), (enc)) +# define here_document(n) parser_here_document(parser,(n)) +# define heredoc_identifier() parser_heredoc_identifier(parser) +# define heredoc_restore(n) parser_heredoc_restore(parser,(n)) +# define whole_match_p(e,l,i) parser_whole_match_p(parser,(e),(l),(i)) +# define number_literal_suffix(v, f) parser_number_literal_suffix(parser, (v), (f)) #ifndef RIPPER # define set_yylval_str(x) (yylval.node = NEW_STR(x)) @@ -6388,6 +6425,58 @@ parser_whole_match_p(struct parser_params *parser, return strncmp(eos, p, len) == 0; } +#define NUM_SUFFIX_R (1<<0) +#define NUM_SUFFIX_I (1<<1) +#define NUM_SUFFIX_ALL 3 + +static int +parser_number_literal_suffix(struct parser_params *parser, VALUE v, int const flag) +{ + int c = nextc(); + if ((flag & NUM_SUFFIX_R) > 0 && c == 'r') { + c = nextc(); + if (c != 'i' && (ISALNUM(c) || c == '_')) { + pushback(c); + pushback('r'); + goto finish; + } + + if (RB_TYPE_P(v, T_FLOAT)) { + v = rb_flt_rationalize(v); + } + else { + v = rb_rational_new(v, INT2FIX(1)); + } + } + if ((flag & NUM_SUFFIX_I) > 0 && c == 'i') { + c = nextc(); + if (ISALNUM(c) || c == '_') { + pushback(c); + pushback('i'); + goto finish; + } + + v = rb_complex_new(INT2FIX(0), v); + } + pushback(c); + +finish: + set_yylval_literal(v); + switch (TYPE(v)) { + case T_FIXNUM: case T_BIGNUM: + return tINTEGER; + case T_FLOAT: + return tFLOAT; + case T_RATIONAL: + return tRATIONAL; + case T_COMPLEX: + return tIMAGINARY; + default: + break; + } + UNREACHABLE; +} + #ifdef RIPPER static void ripper_dispatch_heredoc_end(struct parser_params *parser) @@ -7384,6 +7473,7 @@ parser_yylex(struct parser_params *parser) case '5': case '6': case '7': case '8': case '9': { int is_float, seen_point, seen_e, nondigit; + VALUE v; is_float = seen_point = seen_e = nondigit = 0; lex_state = EXPR_END; @@ -7417,8 +7507,8 @@ parser_yylex(struct parser_params *parser) no_digits(); } else if (nondigit) goto trailing_uc; - set_yylval_literal(rb_cstr_to_inum(tok(), 16, FALSE)); - return tINTEGER; + v = rb_cstr_to_inum(tok(), 16, FALSE); + return number_literal_suffix(v, NUM_SUFFIX_ALL); } if (c == 'b' || c == 'B') { /* binary */ @@ -7441,8 +7531,8 @@ parser_yylex(struct parser_params *parser) no_digits(); } else if (nondigit) goto trailing_uc; - set_yylval_literal(rb_cstr_to_inum(tok(), 2, FALSE)); - return tINTEGER; + v = rb_cstr_to_inum(tok(), 2, FALSE); + return number_literal_suffix(v, NUM_SUFFIX_ALL); } if (c == 'd' || c == 'D') { /* decimal */ @@ -7465,8 +7555,8 @@ parser_yylex(struct parser_params *parser) no_digits(); } else if (nondigit) goto trailing_uc; - set_yylval_literal(rb_cstr_to_inum(tok(), 10, FALSE)); - return tINTEGER; + v = rb_cstr_to_inum(tok(), 10, FALSE); + return number_literal_suffix(v, NUM_SUFFIX_ALL); } if (c == '_') { /* 0_0 */ @@ -7497,8 +7587,8 @@ parser_yylex(struct parser_params *parser) pushback(c); tokfix(); if (nondigit) goto trailing_uc; - set_yylval_literal(rb_cstr_to_inum(tok(), 8, FALSE)); - return tINTEGER; + v = rb_cstr_to_inum(tok(), 8, FALSE); + return number_literal_suffix(v, NUM_SUFFIX_ALL); } if (nondigit) { pushback(c); @@ -7514,8 +7604,7 @@ parser_yylex(struct parser_params *parser) } else { pushback(c); - set_yylval_literal(INT2FIX(0)); - return tINTEGER; + return number_literal_suffix(INT2FIX(0), NUM_SUFFIX_ALL); } } @@ -7597,11 +7686,11 @@ parser_yylex(struct parser_params *parser) rb_warningS("Float %s out of range", tok()); errno = 0; } - set_yylval_literal(DBL2NUM(d)); - return tFLOAT; + v = DBL2NUM(d); + return number_literal_suffix(v, seen_e ? NUM_SUFFIX_I : NUM_SUFFIX_ALL); } - set_yylval_literal(rb_cstr_to_inum(tok(), 10, FALSE)); - return tINTEGER; + v = rb_cstr_to_inum(tok(), 10, FALSE); + return number_literal_suffix(v, NUM_SUFFIX_ALL); } case ')': diff --git a/rational.c b/rational.c index 6cd31e119a..ee0e1689e9 100644 --- a/rational.c +++ b/rational.c @@ -2004,6 +2004,60 @@ float_to_r(VALUE self) #endif } +VALUE +rb_flt_rationalize_with_prec(VALUE flt, VALUE prec) +{ + VALUE e, a, b, p, q; + + e = f_abs(prec); + a = f_sub(flt, e); + b = f_add(flt, e); + + if (f_eqeq_p(a, b)) + return f_to_r(flt); + + nurat_rationalize_internal(a, b, &p, &q); + return rb_rational_new2(p, q); +} + +VALUE +rb_flt_rationalize(VALUE flt) +{ + VALUE a, b, f, n, p, q; + + float_decode_internal(flt, &f, &n); + if (f_zero_p(f) || f_positive_p(n)) + return rb_rational_new1(f_lshift(f, n)); + +#if FLT_RADIX == 2 + { + VALUE two_times_f, den; + + two_times_f = f_mul(TWO, f); + den = f_lshift(ONE, f_sub(ONE, n)); + + a = rb_rational_new2(f_sub(two_times_f, ONE), den); + b = rb_rational_new2(f_add(two_times_f, ONE), den); + } +#else + { + VALUE radix_times_f, den; + + radix_times_f = f_mul(INT2FIX(FLT_RADIX), f); + den = f_expt(INT2FIX(FLT_RADIX), f_sub(ONE, n)); + + a = rb_rational_new2(f_sub(radix_times_f, INT2FIX(FLT_RADIX - 1)), den); + b = rb_rational_new2(f_add(radix_times_f, INT2FIX(FLT_RADIX - 1)), den); + } +#endif + + if (f_eqeq_p(a, b)) + return f_to_r(flt); + + nurat_rationalize_internal(a, b, &p, &q); + return rb_rational_new2(p, q); +} + /* * call-seq: * flt.rationalize([eps]) -> rational @@ -2021,53 +2075,19 @@ float_to_r(VALUE self) static VALUE float_rationalize(int argc, VALUE *argv, VALUE self) { - VALUE e, a, b, p, q; + VALUE e; if (f_negative_p(self)) - return f_negate(float_rationalize(argc, argv, f_abs(self))); + return f_negate(float_rationalize(argc, argv, f_abs(self))); rb_scan_args(argc, argv, "01", &e); if (argc != 0) { - e = f_abs(e); - a = f_sub(self, e); - b = f_add(self, e); + return rb_flt_rationalize_with_prec(self, e); } else { - VALUE f, n; - - float_decode_internal(self, &f, &n); - if (f_zero_p(f) || f_positive_p(n)) - return rb_rational_new1(f_lshift(f, n)); - -#if FLT_RADIX == 2 - { - VALUE two_times_f, den; - - two_times_f = f_mul(TWO, f); - den = f_lshift(ONE, f_sub(ONE, n)); - - a = rb_rational_new2(f_sub(two_times_f, ONE), den); - b = rb_rational_new2(f_add(two_times_f, ONE), den); - } -#else - { - VALUE radix_times_f, den; - - radix_times_f = f_mul(INT2FIX(FLT_RADIX), f); - den = f_expt(INT2FIX(FLT_RADIX), f_sub(ONE, n)); - - a = rb_rational_new2(f_sub(radix_times_f, INT2FIX(FLT_RADIX - 1)), den); - b = rb_rational_new2(f_add(radix_times_f, INT2FIX(FLT_RADIX - 1)), den); - } -#endif + return rb_flt_rationalize(self); } - - if (f_eqeq_p(a, b)) - return f_to_r(self); - - nurat_rationalize_internal(a, b, &p, &q); - return rb_rational_new2(p, q); } #include -- cgit v1.2.3