Initial suffix support
MODIFY
include/mrbconf.h
include/mrbconf.h
# endif
#endif
+#define MRB_COMPLEX_NUMBERS
+#define MRB_RATIONAL_NUMBERS
+
/* define on big endian machines; used by MRB_NAN_BOXING, etc. */
#ifndef MRB_ENDIAN_BIG
# if (defined(BYTE_ORDER) && defined(BIG_ENDIAN) && BYTE_ORDER == BIG_ENDIAN) || \

MODIFY
mrbgems/default.gembox
mrbgems/default.gembox
@@ -68,6 +68,12 @@ MRuby::GemBox.new do |conf|
# Use Enumerator::Lazy class (require mruby-enumerator)
conf.gem :core => "mruby-enum-lazy"
+ # Use Complex class
+ #conf.gem :core => "mruby-complex"
+
+ # Use Rational class
+ #conf.gem :core => "mruby-rational"
+
# Use toplevel object (main) methods extension
conf.gem :core => "mruby-toplevel-ext"

MODIFY
mrbgems/mruby-compiler/core/parse.y
mrbgems/mruby-compiler/core/parse.y
@@ -76,6 +76,24 @@ typedef unsigned int stack_type;
#define nint(x) ((node*)(intptr_t)(x))
#define intn(x) ((int)(intptr_t)(x))
+#if defined(MRB_COMPLEX_NUMBERS) || defined(MRB_RATIONAL_NUMBERS)
+ #define MRB_SUFFIX_SUPPORT
+
+ #ifdef MRB_RATIONAL_NUMBERS
+ #define NUM_SUFFIX_R (1<<0)
+ #else
+ #define NUM_SUFFIX_R 0
+ #endif
+
+ #ifdef MRB_COMPLEX_NUMBERS
+ #define NUM_SUFFIX_I (1<<1)
+ #else
+ #define NUM_SUFFIX_I 0
+ #endif
+
+ #define NUM_SUFFIX_ALL (NUM_SUFFIX_R | NUM_SUFFIX_I)
+#endif
+
static inline mrb_sym
intern_cstr_gen(parser_state *p, const char *s)
{
@@ -842,19 +860,62 @@ new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b)
return list4((node*)NODE_OP_ASGN, a, nsym(op), b);
}
+#ifdef MRB_COMPLEX_NUMBERS
+static node*
+new_imaginary(parser_state *p, node *imaginary);
+#endif
+
+#ifdef MRB_RATIONAL_NUMBERS
+static node*
+new_rational(parser_state *p, node *rational)
+{
+ return new_call(p, new_const(p, intern_cstr("Rational")), intern_cstr("new"), list1(list1(rational)), 1);
+}
+#endif
+
/* (:int . i) */
static node*
-new_int(parser_state *p, const char *s, int base)
+new_int(parser_state *p, const char *s, int base, int suffix)
{
- return list3((node*)NODE_INT, (node*)strdup(s), nint(base));
+ node* result = list3((node*)NODE_INT, (node*)strdup(s), nint(base));
+#ifdef MRB_RATIONAL_NUMBERS
+ if (suffix & NUM_SUFFIX_R) {
+ result = new_rational(p, result);
+ }
+#endif
+#ifdef MRB_COMPLEX_NUMBERS
+ if (suffix & NUM_SUFFIX_I) {
+ result = new_imaginary(p, result);
+ }
+#endif
+ return result;
}
#ifndef MRB_WITHOUT_FLOAT
/* (:float . i) */
static node*
-new_float(parser_state *p, const char *s)
+new_float(parser_state *p, const char *s, int suffix)
+{
+ node* result = cons((node*)NODE_FLOAT, (node*)strdup(s));
+#ifdef MRB_RATIONAL_NUMBERS
+ if (suffix & NUM_SUFFIX_R) {
+ result = new_rational(p, result);
+ }
+#endif
+#ifdef MRB_COMPLEX_NUMBERS
+ if (suffix & NUM_SUFFIX_I) {
+ result = new_imaginary(p, result);
+ }
+#endif
+ return result;
+}
+#endif
+
+#ifdef MRB_COMPLEX_NUMBERS
+static node*
+new_imaginary(parser_state *p, node *imaginary)
{
- return cons((node*)NODE_FLOAT, (node*)strdup(s));
+ return new_call(p, new_const(p, intern_cstr("Complex")), intern_cstr("new"), list1(list2(new_int(p, "0", 10, 0), imaginary)), 1);
}
#endif
@@ -3192,7 +3253,7 @@ var_ref : variable
char buf[16];
dump_int(p->lineno, buf);
- $$ = new_int(p, buf, 10);
+ $$ = new_int(p, buf, 10, 0);
}
| keyword__ENCODING__
{
@@ -4520,6 +4581,45 @@ parse_string(parser_state *p)
return tSTRING;
}
+#ifdef MRB_SUFFIX_SUPPORT
+static int
+number_literal_suffix(parser_state *p, int mask)
+{
+ int c, result = 0;
+ node *list = 0;
+ int column = p->column;
+
+ while ((c = nextc(p)) != -1) {
+ list = push(list, (node*)(intptr_t)c);
+
+ if ((mask & NUM_SUFFIX_I) && c == 'i') {
+ result |= (mask & NUM_SUFFIX_I);
+ mask &= ~NUM_SUFFIX_I;
+ /* r after i, rational of complex is disallowed */
+ mask &= ~NUM_SUFFIX_R;
+ continue;
+ }
+ if ((mask & NUM_SUFFIX_R) && c == 'r') {
+ result |= (mask & NUM_SUFFIX_R);
+ mask &= ~NUM_SUFFIX_R;
+ continue;
+ }
+ if (!ISASCII(c) || ISALPHA(c) || c == '_') {
+ p->column = column;
+ if (p->pb) {
+ p->pb = append((node*)list, p->pb);
+ }
+ else {
+ p->pb = list;
+ }
+ return 0;
+ }
+ pushback(p, c);
+ break;
+ }
+ return result;
+}
+#endif
static int
heredoc_identifier(parser_state *p)
@@ -5094,6 +5194,7 @@ parser_yylex(parser_state *p)
case '5': case '6': case '7': case '8': case '9':
{
int is_float, seen_point, seen_e, nondigit;
+ int suffix;
is_float = seen_point = seen_e = nondigit = 0;
p->lstate = EXPR_ENDARG;
@@ -5127,7 +5228,10 @@ parser_yylex(parser_state *p)
no_digits();
}
else if (nondigit) goto trailing_uc;
- pylval.nd = new_int(p, tok(p), 16);
+ #ifdef MRB_SUFFIX_SUPPORT
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ #endif
+ pylval.nd = new_int(p, tok(p), 16, suffix);
return tINTEGER;
}
if (c == 'b' || c == 'B') {
@@ -5151,7 +5255,10 @@ parser_yylex(parser_state *p)
no_digits();
}
else if (nondigit) goto trailing_uc;
- pylval.nd = new_int(p, tok(p), 2);
+ #ifdef MRB_SUFFIX_SUPPORT
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ #endif
+ pylval.nd = new_int(p, tok(p), 2, suffix);
return tINTEGER;
}
if (c == 'd' || c == 'D') {
@@ -5175,7 +5282,10 @@ parser_yylex(parser_state *p)
no_digits();
}
else if (nondigit) goto trailing_uc;
- pylval.nd = new_int(p, tok(p), 10);
+ #ifdef MRB_SUFFIX_SUPPORT
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ #endif
+ pylval.nd = new_int(p, tok(p), 10, suffix);
return tINTEGER;
}
if (c == '_') {
@@ -5208,7 +5318,10 @@ parser_yylex(parser_state *p)
pushback(p, c);
tokfix(p);
if (nondigit) goto trailing_uc;
- pylval.nd = new_int(p, tok(p), 8);
+ #ifdef MRB_SUFFIX_SUPPORT
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ #endif
+ pylval.nd = new_int(p, tok(p), 8, suffix);
return tINTEGER;
}
if (nondigit) {
@@ -5225,7 +5338,10 @@ parser_yylex(parser_state *p)
}
else {
pushback(p, c);
- pylval.nd = new_int(p, "0", 10);
+ #ifdef MRB_SUFFIX_SUPPORT
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ #endif
+ pylval.nd = new_int(p, "0", 10, suffix);
return tINTEGER;
}
}
@@ -5299,7 +5415,7 @@ parser_yylex(parser_state *p)
if (is_float) {
#ifdef MRB_WITHOUT_FLOAT
yywarning(p, "floating point numbers are not supported");
- pylval.nd = new_int(p, "0", 10);
+ pylval.nd = new_int(p, "0", 10, 0);
return tINTEGER;
#else
double d;
@@ -5314,11 +5430,17 @@ parser_yylex(parser_state *p)
yywarning_s(p, "float out of range", tok(p));
errno = 0;
}
- pylval.nd = new_float(p, tok(p));
+ #ifdef MRB_SUFFIX_SUPPORT
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ #endif
+ pylval.nd = new_float(p, tok(p), suffix);
return tFLOAT;
#endif
}
- pylval.nd = new_int(p, tok(p), 10);
+ #ifdef MRB_SUFFIX_SUPPORT
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ #endif
+ pylval.nd = new_int(p, tok(p), 10, suffix);
return tINTEGER;
}

ADD
mrbgems/mruby-complex/mrbgem.rake
+MRuby::Gem::Specification.new('mruby-complex') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Complex class'
+end

ADD
mrbgems/mruby-complex/mrblib/complex.rb
+class Complex < Numeric
+ def initialize(real = 0, imaginary = 0)
+ @real = real
+ @imaginary = imaginary
+ end
+
+ def inspect
+ "(#{to_s})"
+ end
+
+ def to_s
+ "#{real}#{'+'}#{imaginary}i"
+ end
+
+ def +@
+ Complex.new(real, imaginary)
+ end
+
+ def -@
+ Complex.new(-real, -imaginary)
+ end
+
+ def +(rhs)
+ if rhs.is_a? Complex
+ Complex.new(real + rhs.real, imaginary + rhs.imaginary)
+ elsif rhs.is_a? Numeric
+ Complex.new(real + rhs, imaginary)
+ end
+ end
+
+ def -(rhs)
+ if rhs.is_a? Complex
+ Complex.new(real - rhs.real, imaginary - rhs.imaginary)
+ elsif rhs.is_a? Numeric
+ Complex.new(real - rhs, imaginary)
+ end
+ end
+
+ def *(rhs)
+ if rhs.is_a? Complex
+ Complex.new(real * rhs.real - imaginary * rhs.imaginary, real * rhs.imaginary + rhs.real * imaginary)
+ elsif rhs.is_a? Numeric
+ Complex.new(real * rhs, imaginary * rhs)
+ end
+ end
+
+ def /(rhs)
+ if rhs.is_a? Complex
+ div = rhs.real * rhs.real + rhs.imaginary * rhs.imaginary
+ Complex.new((real * rhs.real + imaginary * rhs.imaginary) / div, (rhs.real * imaginary - real * rhs.imaginary) / div)
+ elsif rhs.is_a? Numeric
+ Complex.new(real / rhs, imaginary / rhs)
+ end
+ end
+
+ attr_reader :real, :imaginary
+end
+
+def Complex(real = 0, imaginary = 0)
+ Complex.new(real, imaginary)
+end
+
+module ForwardOperatorToComplex
+ def __forward_operator_to_complex(op, &b)
+ original_operator_name = "__original_operator_#{op}_complex"
+ alias_method original_operator_name, op
+ define_method op do |rhs|
+ if rhs.is_a? Complex
+ Complex.new(self).send(op, rhs)
+ else
+ send(original_operator_name, rhs)
+ end
+ end
+ end
+
+ def __forward_operators_to_complex
+ __forward_operator_to_complex :+
+ __forward_operator_to_complex :-
+ __forward_operator_to_complex :*
+ __forward_operator_to_complex :/
+
+ singleton_class.undef_method :__forward_operator_to_complex
+ singleton_class.undef_method :__forward_operators_to_complex
+ end
+end
+
+class Fixnum
+ extend ForwardOperatorToComplex
+ __forward_operators_to_complex
+end
+
+class Float
+ extend ForwardOperatorToComplex
+ __forward_operators_to_complex
+end
\ No newline at end of file

ADD
mrbgems/mruby-complex/test/complex.rb
+assert 'Complex' do
+ assert_equal Complex, 0i.class
+end
\ No newline at end of file

ADD
mrbgems/mruby-rational/mrbgem.rake
+MRuby::Gem::Specification.new('mruby-rational') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Rational class'
+end

ADD
mrbgems/mruby-rational/mrblib/rational.rb
+class Rational < Numeric
+ def initialize(numerator = 0, denominator = 1)
+ @numerator = numerator
+ @denominator = denominator
+ end
+
+ attr_reader :numerator, :denominator
+end
+
+def Rational(numerator = 0, denominator = 1)
+ Rational.new(numerator, denominator)
+end
+
+module ForwardOperatorToRational
+ def __forward_operator_to_rational(op, &b)
+ original_operator_name = "__original_operator_#{op}_rational"
+ alias_method original_operator_name, op
+ define_method op do |rhs|
+ if rhs.is_a? Rational
+ Rational.new(self).send(op, rhs)
+ else
+ send(original_operator_name, rhs)
+ end
+ end
+ end
+
+ def __forward_operators_to_rational
+ __forward_operator_to_rational :+
+ __forward_operator_to_rational :-
+ __forward_operator_to_rational :*
+ __forward_operator_to_rational :/
+
+ singleton_class.undef_method :__forward_operator_to_rational
+ singleton_class.undef_method :__forward_operators_to_rational
+ end
+end
+
+class Fixnum
+ extend ForwardOperatorToRational
+ __forward_operators_to_rational
+end
+
+class Float
+ extend ForwardOperatorToRational
+ __forward_operators_to_rational
+end
\ No newline at end of file

ADD
mrbgems/mruby-rational/test/rational.rb
+assert 'Rational' do
+ assert_equal Rational, 0r.class
+end
\ No newline at end of file

Initial suffix support