diff --git a/lib/yard/parser/c_parser.rb b/lib/yard/parser/c_parser.rb index f8d3cde4b..51762cf80 100644 --- a/lib/yard/parser/c_parser.rb +++ b/lib/yard/parser/c_parser.rb @@ -158,8 +158,8 @@ def find_method_body(object, func_name, content = @content) # distinct (for example Kernel.hash and Kernel.object_id share the same # implementation - # override_comment = find_override_comment(object) - # comment = override_comment if override_comment + override_comment = find_override_comment(object) + comment = override_comment if override_comment object.docstring = parse_comments(object, comment) if comment object.source = body_text @@ -168,11 +168,22 @@ def find_method_body(object, func_name, content = @content) find_method_body(object, $2, content) else # No body, but might still have an override comment - # comment = find_override_comment(object) - comment = nil + comment = find_override_comment(object) object.docstring = parse_comments(object, comment) if comment end end + + def find_override_comment(object, content = @content) + name = Regexp.escape(object.name.to_s) + class_name = object.parent.path + if content =~ %r{Document-method:\s+#{class_name}(?:\.|::|#)#{name}\s*?\n((?>.*?\*/))}m then + $1 + elsif content =~ %r{Document-method:\s#{name}\s*?\n((?>.*?\*/))}m then + $1 + else + nil + end + end def parse_comments(object, comments) spaces = nil diff --git a/spec/parser/c_parser_spec.rb b/spec/parser/c_parser_spec.rb index 6c550f90a..019e3f065 100644 --- a/spec/parser/c_parser_spec.rb +++ b/spec/parser/c_parser_spec.rb @@ -5,6 +5,9 @@ before(:all) do file = File.join(File.dirname(__FILE__), 'examples', 'array.c.txt') @parser = Parser::CParser.new(IO.read(file)).parse + + override_file = File.join(File.dirname(__FILE__), 'examples', 'override.c.txt') + @override_parser = Parser::CParser.new(IO.read(override_file)).parse end describe '#parse' do @@ -20,4 +23,39 @@ obj.tags(:overload).size.should > 1 end end + + describe '#find_override_comment' do + it "should parse GMP::Z class" do + z = YARD::Registry.at('GMP::Z') + z.should_not be_nil + z.docstring.should_not be_blank + end + + it "should parse GMP::Z methods w/ bodies" do + add = YARD::Registry.at('GMP::Z#+') + add.docstring.should_not be_blank + add.source.should_not be_nil + add.source.should_not be_empty + + add_self = YARD::Registry.at('GMP::Z#+') + add_self.docstring.should_not be_blank + add_self.source.should_not be_nil + add_self.source.should_not be_empty + + sqrtrem = YARD::Registry.at('GMP::Z#+') + sqrtrem.docstring.should_not be_blank + sqrtrem.source.should_not be_nil + sqrtrem.source.should_not be_empty + end + + it "should parse GMP::Z methods w/o bodies" do + neg = YARD::Registry.at('GMP::Z#neg') + neg.docstring.should_not be_blank + neg.source.should be_nil + + neg_self = YARD::Registry.at('GMP::Z#neg') + neg_self.docstring.should_not be_blank + neg_self.source.should be_nil + end + end end \ No newline at end of file diff --git a/spec/parser/examples/override.c.txt b/spec/parser/examples/override.c.txt new file mode 100644 index 000000000..5ffeea6ef --- /dev/null +++ b/spec/parser/examples/override.c.txt @@ -0,0 +1,424 @@ +#include <gmpz.h> +#include <gmpq.h> +#include <gmpf.h> + +/* + * Document-class: GMP::Z + * + * GMP Multiple Precision Integer. + * + * Instances of this class can store variables of the type mpz_t. This class + * also contains many methods that act as the functions for mpz_t variables, + * as well as a few methods that attempt to make this library more Ruby-ish. + */ + +/********************************************************************** + * Macros * + **********************************************************************/ + +/* + * DEFUN_INT2INT defines two functions. The first takes a GMP::Z as + * self, calls mpz_fname on the contained mpz_t, whose arguments are + * exactly (0) the return argument and (1) self. The second is the same + * destructive method. + */ +#define DEFUN_INT2INT(fname,mpz_fname) \ +static VALUE r_gmpz_##fname(VALUE self) \ +{ \ + MP_INT *self_val, *res_val; \ + VALUE res; \ + mpz_get_struct(self, self_val); \ + mpz_make_struct_init(res, res_val); \ + mpz_fname(res_val, self_val); \ + return res; \ +} \ + \ +static VALUE r_gmpz_##fname##_self(VALUE self) \ +{ \ + MP_INT *self_val; \ + mpz_get_struct(self, self_val); \ + mpz_fname(self_val, self_val); \ + return self; \ +} + + +/********************************************************************** + * Integer Arithmetic * + **********************************************************************/ + +/* + * call-seq: + * a + b + * + * Adds _a_ to _b_. _b_ must be an instance of one of: + * * GMP::Z + * * Fixnum + * * GMP::Q + * * GMP::F + * * Bignum + */ +VALUE r_gmpz_add(VALUE self, VALUE arg) +{ + MP_INT *self_val, *arg_val, *res_val; + VALUE res; + + mpz_get_struct(self,self_val); + + if (GMPZ_P(arg)) { + mpz_get_struct(arg,arg_val); + mpz_make_struct_init(res, res_val); + mpz_add(res_val, self_val, arg_val); + } else if (FIXNUM_P(arg)) { + mpz_make_struct_init(res, res_val); + if (FIX2NUM(arg) > 0) + mpz_add_ui(res_val, self_val, FIX2NUM(arg)); + else + mpz_sub_ui(res_val, self_val, -FIX2NUM(arg)); + } else if (GMPQ_P(arg)) { + return r_gmpq_add(arg, self); + } else if (GMPF_P(arg)) { +#ifndef MPFR + return r_gmpf_add(arg, self); +#else + return rb_funcall(arg, rb_intern("+"), 1, self); +#endif + } else if (BIGNUM_P(arg)) { + mpz_make_struct_init(res, res_val); + mpz_init(res_val); + mpz_set_bignum(res_val, arg); + mpz_add(res_val, res_val, self_val); + } else { + typeerror(ZQFXB); + } + return res; +} + +/* + * call-seq: + * a.add!(_b_) + * + * Adds _a_ to _b_ in-place, setting _a_ to the sum. _b_ must be an instance of one of: + * * GMP::Z + * * Fixnum + * * GMP::Q + * * GMP::F + * * Bignum + */ +VALUE r_gmpz_add_self(VALUE self, VALUE arg) +{ + MP_INT *self_val, *arg_val; + + mpz_get_struct(self,self_val); + + if (GMPZ_P(arg)) { + mpz_get_struct(arg,arg_val); + mpz_add(self_val, self_val, arg_val); + } else if (FIXNUM_P(arg)) { + if (FIX2NUM(arg) > 0) + mpz_add_ui(self_val, self_val, FIX2NUM(arg)); + else + mpz_sub_ui(self_val, self_val, -FIX2NUM(arg)); + } else if (BIGNUM_P(arg)) { + mpz_temp_from_bignum(arg_val, arg); + mpz_add(self_val, self_val, arg_val); + mpz_temp_free(arg_val); + } else { + typeerror(ZXB); + } + return Qnil; +} + +/* + * call-seq: + * a - b + * + * Subtracts _b_ from _a_. _b_ must be an instance of one of: + * * GMP::Z + * * Fixnum + * * GMP::Q + * * GMP::F + * * Bignum + */ +VALUE r_gmpz_sub(VALUE self, VALUE arg) +{ + MP_RAT *res_val_q, *arg_val_q; + MP_INT *self_val, *arg_val, *res_val; + MP_FLOAT *arg_val_f, *res_val_f; + VALUE res; + unsigned long prec; + + mpz_get_struct(self,self_val); + + if (GMPZ_P(arg)) { + mpz_make_struct_init(res, res_val); + mpz_get_struct(arg,arg_val); + mpz_sub (res_val, self_val, arg_val); + } else if (FIXNUM_P(arg)) { + mpz_make_struct_init(res, res_val); + if (FIX2NUM(arg) > 0) + mpz_sub_ui (res_val, self_val, FIX2NUM(arg)); + else + mpz_add_ui (res_val, self_val, -FIX2NUM(arg)); + } else if (GMPQ_P(arg)) { + mpq_make_struct_init(res, res_val_q); + mpq_get_struct(arg,arg_val_q); + mpz_set (mpq_denref(res_val_q), mpq_denref(arg_val_q)); + mpz_mul (mpq_numref(res_val_q), mpq_denref(arg_val_q), self_val); + mpz_sub (mpq_numref(res_val_q), mpq_numref(res_val_q), mpq_numref(arg_val_q)); + } else if (GMPF_P(arg)) { + mpf_get_struct_prec (arg, arg_val_f, prec); + mpf_make_struct_init(res, res_val_f, prec); + mpf_set_z (res_val_f, self_val); + mpf_sub (res_val_f, res_val_f, arg_val_f); + } else if (BIGNUM_P(arg)) { + mpz_make_struct_init(res, res_val); + mpz_set_bignum (res_val, arg); + mpz_sub (res_val, self_val, res_val); + } else { + typeerror (ZQFXB); + } + return res; +} + +/* + * call-seq: + * a.sub!(b) + * + * Subtracts _b_ from _a_ in-place, setting _a_ to the difference. _b_ must be an + * instance of one of: + * * GMP::Z + * * Fixnum + * * GMP::Q + * * GMP::F + * * Bignum + */ +VALUE r_gmpz_sub_self(VALUE self, VALUE arg) +{ + MP_INT *self_val, *arg_val; + + mpz_get_struct(self,self_val); + + if (GMPZ_P(arg)) { + mpz_get_struct(arg, arg_val); + mpz_sub (self_val, self_val, arg_val); + } else if (FIXNUM_P(arg)) { + if (FIX2NUM(arg) > 0) + mpz_sub_ui (self_val, self_val, FIX2NUM(arg)); + else + mpz_add_ui (self_val, self_val, -FIX2NUM(arg)); + } else if (BIGNUM_P(arg)) { + mpz_temp_from_bignum(arg_val, arg); + mpz_sub (self_val, self_val, arg_val); + mpz_temp_free (arg_val); + } else { + typeerror (ZXB); + } + return Qnil; +} + +/* + * call-seq: + * a * b + * + * Multiplies _a_ with _b_. _a_ must be an instance of one of + * * GMP::Z + * * Fixnum + * * GMP::Q + * * GMP::F + * * Bignum + */ +VALUE r_gmpz_mul(VALUE self, VALUE arg) +{ + MP_INT *self_val, *arg_val, *res_val; + VALUE res; + + mpz_get_struct(self,self_val); + + if (GMPZ_P(arg)) { + mpz_make_struct_init(res, res_val); + mpz_get_struct(arg,arg_val); + mpz_mul(res_val, self_val, arg_val); + } else if (FIXNUM_P(arg)) { + mpz_make_struct_init(res, res_val); + mpz_mul_si(res_val, self_val, FIX2NUM(arg)); + } else if (GMPQ_P(arg)) { + return r_gmpq_mul(arg, self); + } else if (GMPF_P(arg)) { +#ifndef MPFR + return r_gmpf_mul(arg, self); +#else + return rb_funcall(arg, rb_intern("*"), 1, self); +#endif + } else if (BIGNUM_P(arg)) { + mpz_make_struct_init(res, res_val); + mpz_set_bignum(res_val, arg); + mpz_mul(res_val, res_val, self_val); + } else { + typeerror(ZQFXB); + } + return res; +} + +/* + * call-seq: + * a.addmul!(b, c) + * + * @since 0.4.17 + * + * Sets _a_ to _a_ plus _b_ times _c_. _b_ and _c_ must each be an instance of one of + * * GMP::Z + * * Fixnum + * * Bignum + */ +VALUE r_gmpz_addmul_self(VALUE self, VALUE b, VALUE c) +{ + MP_INT *self_val, *b_val, *c_val; + int free_b_val = 0; + + if (GMPZ_P(b)) { + mpz_get_struct(b, b_val); + } else if (FIXNUM_P(b)) { + mpz_temp_alloc(b_val); + mpz_init_set_si(b_val, FIX2NUM(b)); + free_b_val = 1; + } else if (BIGNUM_P(b)) { + mpz_temp_from_bignum(b_val, b); + free_b_val = 1; + } else { + typeerror_as(ZXB, "addend"); + } + mpz_get_struct(self, self_val); + + if (GMPZ_P(c)) { + mpz_get_struct(c, c_val); + mpz_addmul(self_val, b_val, c_val); + } else if (FIXNUM_P(c)) { + if (FIX2NUM(c) < 0) + { + if (free_b_val) { mpz_temp_free(b_val); } + rb_raise(rb_eRangeError, "multiplicand (Fixnum) must be nonnegative"); + } + mpz_addmul_ui(self_val, b_val, FIX2NUM(c)); + } else if (BIGNUM_P(c)) { + mpz_temp_from_bignum(c_val, c); + mpz_addmul(self_val, b_val, c_val); + mpz_temp_free(c_val); + } else { + if (free_b_val) + mpz_temp_free(b_val); + typeerror_as(ZXB, "multiplicand"); + } + if (free_b_val) + mpz_temp_free(b_val); + return self; +} + +/* + * Document-method: neg + * + * call-seq: + * a.neg + * -a + * + * Returns -_a_. + */ +/* + * Document-method: neg! + * + * call-seq: + * a.neg! + * + * Sets _a_ to -_a_. + */ +DEFUN_INT2INT(neg, mpz_neg) +/* + * Document-method: abs + * + * call-seq: + * a.abs + * + * Returns the absolute value of _a_. + */ +/* + * Document-method: abs! + * + * call-seq: + * a.abs! + * + * Sets _a_ to its absolute value. + */ +DEFUN_INT2INT(abs, mpz_abs) + + +/********************************************************************** + * Integer Roots * + **********************************************************************/ + +/* + * Document-method: sqrt + * + * call-seq: + * a.sqrt + * + * Returns the truncated integer part of the square root of _a_. + */ +/* + * Document-method: sqrt! + * + * call-seq: + * a.sqrt! + * + * Sets _a_ to the truncated integer part of its square root. + */ +DEFUN_INT2INT(sqrt, mpz_sqrt) + +/* + * call-seq: + * a.sqrtrem #=> s, r + * + * Returns the truncated integer part of the square root of _a_ as _s_ and the remainder + * <i>a - s * s</i> as _r_, which will be zero if _a_ is a perfect square. + */ +static VALUE r_gmpz_sqrtrem(VALUE self) +{ + MP_INT *self_val, *sqrt_val, *rem_val; + VALUE sqrt, rem; + + mpz_get_struct(self, self_val); + mpz_make_struct_init(sqrt, sqrt_val); + mpz_make_struct_init(rem, rem_val); + mpz_sqrtrem(sqrt_val, rem_val, self_val); + return rb_assoc_new(sqrt, rem); +} + + +/********************************************************************** + * Init function * + **********************************************************************/ + +void init_gmpz() +{ + mGMP = rb_define_module("GMP"); + rb_define_module_function(mGMP, "Z", r_gmpmod_z, -1); + + cGMP_Z = rb_define_class_under(mGMP, "Z", rb_cInteger); + + // Integer Arithmetic + rb_define_method(cGMP_Z, "+", r_gmpz_add, 1); + rb_define_method(cGMP_Z, "add!", r_gmpz_add_self, 1); + rb_define_method(cGMP_Z, "-", r_gmpz_sub, 1); + rb_define_method(cGMP_Z, "sub!", r_gmpz_sub_self, 1); + rb_define_method(cGMP_Z, "*", r_gmpz_mul, 1); + rb_define_method(cGMP_Z, "addmul!", r_gmpz_addmul_self, 2); + rb_define_method(cGMP_Z, "neg", r_gmpz_neg, 0); + rb_define_method(cGMP_Z, "neg!", r_gmpz_neg_self, 0); + rb_define_method(cGMP_Z, "-@", r_gmpz_neg, 0); + rb_define_method(cGMP_Z, "abs", r_gmpz_abs, 0); + rb_define_method(cGMP_Z, "abs!", r_gmpz_abs_self, 0); + + // Integer Roots + rb_define_method(cGMP_Z, "root", r_gmpz_root, 1); + rb_define_method(cGMP_Z, "sqrt", r_gmpz_sqrt, 0); + rb_define_method(cGMP_Z, "sqrt!", r_gmpz_sqrt_self, 0); + rb_define_method(cGMP_Z, "sqrtrem", r_gmpz_sqrtrem, 0); +}