summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog18
-rw-r--r--bootstraptest/test_literal_suffix.rb46
-rw-r--r--include/ruby/intern.h2
-rw-r--r--parse.y151
-rw-r--r--rational.c96
5 files changed, 244 insertions, 69 deletions
diff --git a/ChangeLog b/ChangeLog
index 22907dd..0cabb99 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+Thu Aug 1 23:45:00 2013 Kenta Murata <mrkn@mrkn.jp>
+
+ * 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 <akr@fsij.org>
* 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 0000000..e466cfc
--- /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 4393246..02213c9 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 87029eb..0d11c17 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 <id> tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL
-%token <node> tINTEGER tFLOAT tSTRING_CONTENT tCHAR
+%token <node> tINTEGER tFLOAT tRATIONAL tIMAGINARY tSTRING_CONTENT tCHAR
%token <node> tNTH_REF tBACK_REF
%token <num> 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 6cd31e1..ee0e168 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 <ctype.h>