diff options
-rw-r--r-- | ChangeLog | 18 | ||||
-rw-r--r-- | ext/bigdecimal/bigdecimal.c | 68 | ||||
-rw-r--r-- | lib/rdoc/parsers/parse_c.rb | 226 | ||||
-rw-r--r-- | test/rdoc/parsers/test_parse_c.rb | 148 |
4 files changed, 342 insertions, 118 deletions
@@ -1,3 +1,21 @@ +Mon Jan 1 06:13:11 2007 Eric Hodel <drbrain@segment7.net> + + * lib/rdoc/parsers/c_parser.rb: Make Rdoc accessible. Update constant + value information. + +Mon Jan 1 06:13:11 2007 Eric Hodel <drbrain@segment7.net> + + * ext/bigdecimal/bigdecimal.c: Update constant comments to provide + values for RDoc. + +Mon Jan 1 06:05:55 2007 Eric Hodel <drbrain@segment7.net> + + * lib/rdoc/parsers/parse_c.rb (RDoc::C_Parser#handle_constansts): + Allow RDoc comment to give friendly value for rb_define_const. Patch + by Daniel Berger <djberg96 at gmail.com>, [ruby-patches-7499]. + * lib/rdoc/parsers/parse_c.rb (RDoc::C_Parser#handle_constansts): Fix + whitespace handling in constant comments. + Sun Dec 31 00:31:16 2006 Tadayoshi Funaba <tadf@dotrb.org> * lib/date.rb, lib/date/format.rb: updated based on date2 4.0. diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index f10af59f5b..370f5ecd50 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -1811,76 +1811,94 @@ Init_bigdecimal(void) /* Exceptions */ /* - * Determines whether overflow, underflow or zero divide result in + * 0xff: Determines whether overflow, underflow or zero divide result in * an exception being thrown. See BigDecimal.mode. */ rb_define_const(rb_cBigDecimal, "EXCEPTION_ALL",INT2FIX(VP_EXCEPTION_ALL)); /* - * Determines what happens when the result of a computation is not a + * 0x02: Determines what happens when the result of a computation is not a * number (NaN). See BigDecimal.mode. */ rb_define_const(rb_cBigDecimal, "EXCEPTION_NaN",INT2FIX(VP_EXCEPTION_NaN)); /* - * Determines what happens when the result of a computation is infinity. - * See BigDecimal.mode. + * 0x01: Determines what happens when the result of a computation is + * infinity. See BigDecimal.mode. */ rb_define_const(rb_cBigDecimal, "EXCEPTION_INFINITY",INT2FIX(VP_EXCEPTION_INFINITY)); /* - * Determines what happens when the result of a computation is an underflow - * (a result too small to be represented). See BigDecimal.mode. + * 0x04: Determines what happens when the result of a computation is an + * underflow (a result too small to be represented). See BigDecimal.mode. */ rb_define_const(rb_cBigDecimal, "EXCEPTION_UNDERFLOW",INT2FIX(VP_EXCEPTION_UNDERFLOW)); /* - * Determines what happens when the result of a computation is an underflow - * (a result too large to be represented). See BigDecimal.mode. + * 0x01: Determines what happens when the result of a computation is an + * underflow (a result too large to be represented). See BigDecimal.mode. */ rb_define_const(rb_cBigDecimal, "EXCEPTION_OVERFLOW",INT2FIX(VP_EXCEPTION_OVERFLOW)); /* - * Determines what happens when a division by zero is performed. + * 0x01: Determines what happens when a division by zero is performed. * See BigDecimal.mode. */ rb_define_const(rb_cBigDecimal, "EXCEPTION_ZERODIVIDE",INT2FIX(VP_EXCEPTION_ZERODIVIDE)); /* - * Determines what happens when a result must be rounded in order to - * fit in the appropriate number of significant digits. See + * 0x100: Determines what happens when a result must be rounded in order to + * fit in the appropriate number of significant digits. See * BigDecimal.mode. */ rb_define_const(rb_cBigDecimal, "ROUND_MODE",INT2FIX(VP_ROUND_MODE)); - /* Indicates that values should be rounded away from zero. See BigDecimal.mode. */ + /* 1: Indicates that values should be rounded away from zero. See + * BigDecimal.mode. + */ rb_define_const(rb_cBigDecimal, "ROUND_UP",INT2FIX(VP_ROUND_UP)); - /* Indicates that values should be rounded towards zero. See BigDecimal.mode. */ + + /* 2: Indicates that values should be rounded towards zero. See + * BigDecimal.mode. + */ rb_define_const(rb_cBigDecimal, "ROUND_DOWN",INT2FIX(VP_ROUND_DOWN)); - /* Indicates that digits >= 5 should be rounded up, others rounded down. See BigDecimal.mode. */ + + /* 3: Indicates that digits >= 5 should be rounded up, others rounded down. + * See BigDecimal.mode. */ rb_define_const(rb_cBigDecimal, "ROUND_HALF_UP",INT2FIX(VP_ROUND_HALF_UP)); - /* Indicates that digits >= 6 should be rounded up, others rounded down. See BigDecimal.mode. */ + + /* 4: Indicates that digits >= 6 should be rounded up, others rounded down. + * See BigDecimal.mode. + */ rb_define_const(rb_cBigDecimal, "ROUND_HALF_DOWN",INT2FIX(VP_ROUND_HALF_DOWN)); - /* Round towards +infinity. See BigDecimal.mode. */ + /* 5: Round towards +infinity. See BigDecimal.mode. */ rb_define_const(rb_cBigDecimal, "ROUND_CEILING",INT2FIX(VP_ROUND_CEIL)); - /* Round towards -infinity. See BigDecimal.mode. */ + + /* 6: Round towards -infinity. See BigDecimal.mode. */ rb_define_const(rb_cBigDecimal, "ROUND_FLOOR",INT2FIX(VP_ROUND_FLOOR)); - /* Round towards the even neighbor. See BigDecimal.mode. */ + + /* 7: Round towards the even neighbor. See BigDecimal.mode. */ rb_define_const(rb_cBigDecimal, "ROUND_HALF_EVEN",INT2FIX(VP_ROUND_HALF_EVEN)); - /* Indicates that a value is not a number. See BigDecimal.sign. */ + /* 0: Indicates that a value is not a number. See BigDecimal.sign. */ rb_define_const(rb_cBigDecimal, "SIGN_NaN",INT2FIX(VP_SIGN_NaN)); - /* Indicates that a value is +0. See BigDecimal.sign. */ + + /* 1: Indicates that a value is +0. See BigDecimal.sign. */ rb_define_const(rb_cBigDecimal, "SIGN_POSITIVE_ZERO",INT2FIX(VP_SIGN_POSITIVE_ZERO)); - /* Indicates that a value is -0. See BigDecimal.sign. */ + + /* -1: Indicates that a value is -0. See BigDecimal.sign. */ rb_define_const(rb_cBigDecimal, "SIGN_NEGATIVE_ZERO",INT2FIX(VP_SIGN_NEGATIVE_ZERO)); - /* Indicates that a value is positive and finite. See BigDecimal.sign. */ + + /* 2: Indicates that a value is positive and finite. See BigDecimal.sign. */ rb_define_const(rb_cBigDecimal, "SIGN_POSITIVE_FINITE",INT2FIX(VP_SIGN_POSITIVE_FINITE)); - /* Indicates that a value is negative and finite. See BigDecimal.sign. */ + + /* -2: Indicates that a value is negative and finite. See BigDecimal.sign. */ rb_define_const(rb_cBigDecimal, "SIGN_NEGATIVE_FINITE",INT2FIX(VP_SIGN_NEGATIVE_FINITE)); - /* Indicates that a value is positive and infinite. See BigDecimal.sign. */ + + /* 3: Indicates that a value is positive and infinite. See BigDecimal.sign. */ rb_define_const(rb_cBigDecimal, "SIGN_POSITIVE_INFINITE",INT2FIX(VP_SIGN_POSITIVE_INFINITE)); - /* Indicates that a value is negative and infinite. See BigDecimal.sign. */ + + /* -3: Indicates that a value is negative and infinite. See BigDecimal.sign. */ rb_define_const(rb_cBigDecimal, "SIGN_NEGATIVE_INFINITE",INT2FIX(VP_SIGN_NEGATIVE_INFINITE)); /* instance methods */ diff --git a/lib/rdoc/parsers/parse_c.rb b/lib/rdoc/parsers/parse_c.rb index 19b5d443f6..4ebd25c709 100644 --- a/lib/rdoc/parsers/parse_c.rb +++ b/lib/rdoc/parsers/parse_c.rb @@ -1,3 +1,78 @@ +# Classes and modules built in to the interpreter. We need +# these to define superclasses of user objects + +require "rdoc/code_objects" +require "rdoc/parsers/parserfactory" +require "rdoc/options" +require "rdoc/rdoc" + +module RDoc + + ## + # Ruby's built-in classes. + + KNOWN_CLASSES = { + "rb_cObject" => "Object", + "rb_cArray" => "Array", + "rb_cBignum" => "Bignum", + "rb_cClass" => "Class", + "rb_cDir" => "Dir", + "rb_cData" => "Data", + "rb_cFalseClass" => "FalseClass", + "rb_cFile" => "File", + "rb_cFixnum" => "Fixnum", + "rb_cFloat" => "Float", + "rb_cHash" => "Hash", + "rb_cInteger" => "Integer", + "rb_cIO" => "IO", + "rb_cModule" => "Module", + "rb_cNilClass" => "NilClass", + "rb_cNumeric" => "Numeric", + "rb_cProc" => "Proc", + "rb_cRange" => "Range", + "rb_cRegexp" => "Regexp", + "rb_cString" => "String", + "rb_cSymbol" => "Symbol", + "rb_cThread" => "Thread", + "rb_cTime" => "Time", + "rb_cTrueClass" => "TrueClass", + "rb_cStruct" => "Struct", + "rb_eException" => "Exception", + "rb_eStandardError" => "StandardError", + "rb_eSystemExit" => "SystemExit", + "rb_eInterrupt" => "Interrupt", + "rb_eSignal" => "Signal", + "rb_eFatal" => "Fatal", + "rb_eArgError" => "ArgError", + "rb_eEOFError" => "EOFError", + "rb_eIndexError" => "IndexError", + "rb_eRangeError" => "RangeError", + "rb_eIOError" => "IOError", + "rb_eRuntimeError" => "RuntimeError", + "rb_eSecurityError" => "SecurityError", + "rb_eSystemCallError" => "SystemCallError", + "rb_eTypeError" => "TypeError", + "rb_eZeroDivError" => "ZeroDivError", + "rb_eNotImpError" => "NotImpError", + "rb_eNoMemError" => "NoMemError", + "rb_eFloatDomainError" => "FloatDomainError", + "rb_eScriptError" => "ScriptError", + "rb_eNameError" => "NameError", + "rb_eSyntaxError" => "SyntaxError", + "rb_eLoadError" => "LoadError", + + "rb_mKernel" => "Kernel", + "rb_mComparable" => "Comparable", + "rb_mEnumerable" => "Enumerable", + "rb_mPrecision" => "Precision", + "rb_mErrno" => "Errno", + "rb_mFileTest" => "FileTest", + "rb_mGC" => "GC", + "rb_mMath" => "Math", + "rb_mProcess" => "Process" + } + + ## # We attempt to parse C extension files. Basically we look for # the standard patterns that you find in extensions: <tt>rb_define_class, # rb_define_method</tt> and so on. We also try to find the corresponding @@ -52,8 +127,8 @@ # when the <tt>Init_xxx</tt> method is not named after the class. # # [Document-method: <i>name</i>] - # This comment documents the named method. Use when RDoc cannot outomatically - # find the method from it's declaration + # This comment documents the named method. Use when RDoc cannot + # automatically find the method from it's declaration # # [call-seq: <i>text up to an empty line</i>] # Because C source doesn't give descripive names to Ruby-level parameters, @@ -89,82 +164,9 @@ # */ # - - # Classes and modules built in to the interpreter. We need - # these to define superclasses of user objects - -require "rdoc/code_objects" -require "rdoc/parsers/parserfactory" - - -module RDoc - - KNOWN_CLASSES = { - "rb_cObject" => "Object", - "rb_cArray" => "Array", - "rb_cBignum" => "Bignum", - "rb_cClass" => "Class", - "rb_cDir" => "Dir", - "rb_cData" => "Data", - "rb_cFalseClass" => "FalseClass", - "rb_cFile" => "File", - "rb_cFixnum" => "Fixnum", - "rb_cFloat" => "Float", - "rb_cHash" => "Hash", - "rb_cInteger" => "Integer", - "rb_cIO" => "IO", - "rb_cModule" => "Module", - "rb_cNilClass" => "NilClass", - "rb_cNumeric" => "Numeric", - "rb_cProc" => "Proc", - "rb_cRange" => "Range", - "rb_cRegexp" => "Regexp", - "rb_cString" => "String", - "rb_cSymbol" => "Symbol", - "rb_cThread" => "Thread", - "rb_cTime" => "Time", - "rb_cTrueClass" => "TrueClass", - "rb_cStruct" => "Struct", - "rb_eException" => "Exception", - "rb_eStandardError" => "StandardError", - "rb_eSystemExit" => "SystemExit", - "rb_eInterrupt" => "Interrupt", - "rb_eSignal" => "Signal", - "rb_eFatal" => "Fatal", - "rb_eArgError" => "ArgError", - "rb_eEOFError" => "EOFError", - "rb_eIndexError" => "IndexError", - "rb_eRangeError" => "RangeError", - "rb_eIOError" => "IOError", - "rb_eRuntimeError" => "RuntimeError", - "rb_eSecurityError" => "SecurityError", - "rb_eSystemCallError" => "SystemCallError", - "rb_eTypeError" => "TypeError", - "rb_eZeroDivError" => "ZeroDivError", - "rb_eNotImpError" => "NotImpError", - "rb_eNoMemError" => "NoMemError", - "rb_eFloatDomainError" => "FloatDomainError", - "rb_eScriptError" => "ScriptError", - "rb_eNameError" => "NameError", - "rb_eSyntaxError" => "SyntaxError", - "rb_eLoadError" => "LoadError", - - "rb_mKernel" => "Kernel", - "rb_mComparable" => "Comparable", - "rb_mEnumerable" => "Enumerable", - "rb_mPrecision" => "Precision", - "rb_mErrno" => "Errno", - "rb_mFileTest" => "FileTest", - "rb_mGC" => "GC", - "rb_mMath" => "Math", - "rb_mProcess" => "Process" - - } - - # See rdoc/c_parse.rb - class C_Parser + attr_accessor :progress extend ParserFactory parse_files_matching(/\.(c|cc|cpp|CC)$/) @@ -217,8 +219,9 @@ module RDoc comment.sub!(/\/?\*--.*/m, '') end - # remove lines that are commented out that might otherwise get - # picked up when scanning for classes and methods + ## + # removes lines that are commented out that might otherwise get picked up + # when scanning for classes and methods def remove_commented_out_lines @body.gsub!(%r{//.*rb_define_}, '//') @@ -260,7 +263,6 @@ module RDoc @classes[var_name] = cm @known_classes[var_name] = cm.full_name end - ############################################################ @@ -425,7 +427,16 @@ module RDoc end end - ############################################################ + ## + # Adds constant comments. By providing some_value: at the start ofthe + # comment you can override the C value of the comment to give a friendly + # definition. + # + # /* 300: The perfect score in bowling */ + # rb_define_const(cFoo, "PERFECT", INT2FIX(300); + # + # Will override +INT2FIX(300)+ with the value +300+ in the output RDoc. + # Values may include quotes and escaped colons (\:). def handle_constants(type, var_name, const_name, definition) #@stats.num_constants += 1 @@ -442,14 +453,39 @@ module RDoc comment = find_const_comment(type, const_name) - con = Constant.new(const_name, definition, mangle_comment(comment)) + # In the case of rb_define_const, the definition and comment are in + # "/* definition: comment */" form. The literal ':' and '\' characters + # can be escaped with a backslash. + if type.downcase == 'const' then + elements = mangle_comment(comment).split(':') + if elements.nil? or elements.empty? then + con = Constant.new(const_name, definition, mangle_comment(comment)) + else + new_definition = elements[0..-2].join(':') + if new_definition.empty? then # Default to literal C definition + new_definition = definition + else + new_definition.gsub!("\:", ":") + new_definition.gsub!("\\", '\\') + end + new_definition.sub!(/\A(\s+)/, '') + new_comment = $1.nil? ? elements.last : "#{$1}#{elements.last.lstrip}" + con = Constant.new(const_name, new_definition, + mangle_comment(new_comment)) + end + else + con = Constant.new(const_name, definition, mangle_comment(comment)) + end + class_obj.add_constant(con) end - ########################################################### + ## + # Finds a comment matching +type+ and +const_name+ either above the + # comment or in the matching Document- section. def find_const_comment(type, const_name) - if @body =~ %r{((?>/\*.*?\*/\s+)) + if @body =~ %r{((?>^\s*/\*.*?\*/\s+)) rb_define_#{type}\((?:\s*(\w+),)?\s*"#{const_name}"\s*,.*?\)\s*;}xmi $1 elsif @body =~ %r{Document-(?:const|global|variable):\s#{const_name}\s*?\n((?>.*?\*/))}m @@ -610,13 +646,15 @@ module RDoc end - ################################################## - # - # If the comment block contains a section that looks like + ## + # If the comment block contains a section that looks like: + # # call-seq: # Array.new # Array.new(10) - # use it for the parameters + # + # use it for the parameters. + def find_modifiers(comment, meth_obj) if comment.sub!(/:nodoc:\s*^\s*\*?\s*$/m, '') or comment.sub!(/\A\/\*\s*:nodoc:\s*\*\/\Z/, '') @@ -639,10 +677,11 @@ module RDoc end end - ############################################################ - - # Look for includes of the form + ## + # Look for includes of the form: + # # rb_include_module(rb_cArray, rb_mEnumerable); + def do_includes @body.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |c,m| if cls = @classes[c] @@ -652,8 +691,7 @@ module RDoc end end - ############################################################ - + ## # Remove the /*'s and leading asterisks from C comments def mangle_comment(comment) @@ -686,7 +724,8 @@ module RDoc end end - # Remove #ifdefs that would otherwise confuse us + ## + # Removes #ifdefs that would otherwise confuse us def handle_ifdefs_in(body) body.gsub(/^#ifdef HAVE_PROTOTYPES.*?#else.*?\n(.*?)#endif.*?\n/m) { $1 } @@ -695,3 +734,4 @@ module RDoc end end + diff --git a/test/rdoc/parsers/test_parse_c.rb b/test/rdoc/parsers/test_parse_c.rb new file mode 100644 index 0000000000..c7815a95c0 --- /dev/null +++ b/test/rdoc/parsers/test_parse_c.rb @@ -0,0 +1,148 @@ +require 'pp' +require 'stringio' +require 'tempfile' +require 'test/unit' +require 'rdoc/parsers/parse_c' + +class RDoc::C_Parser + attr_accessor :classes + + public :do_classes, :do_constants +end + +class TestRdocC_Parser < Test::Unit::TestCase + + def setup + @tempfile = Tempfile.new self.class.name + filename = @tempfile.path + + @top_level = RDoc::TopLevel.new filename + @fn = filename + @options = Options.instance + @stats = RDoc::Stats.new + + @progress = StringIO.new + end + + def teardown + @tempfile.unlink + end + + def test_do_constants + content = <<-EOF +#include <ruby.h> + +void Init_foo(){ + VALUE cFoo = rb_define_class("Foo", rb_cObject); + + /* 300: The highest possible score in bowling */ + rb_define_const(cFoo, "PERFECT", INT2FIX(300)); + + /* Huzzah!: What you cheer when you roll a perfect game */ + rb_define_const(cFoo, "CHEER", rb_str_new2("Huzzah!")); + + /* TEST\:TEST: Checking to see if escaped semicolon works */ + rb_define_const(cFoo, "TEST", rb_str_new2("TEST:TEST")); + + /* \\: The file separator on MS Windows */ + rb_define_const(cFoo, "MSEPARATOR", rb_str_new2("\\")); + + /* /: The file separator on Unix */ + rb_define_const(cFoo, "SEPARATOR", rb_str_new2("/")); + + /* C:\\Program Files\\Stuff: A directory on MS Windows */ + rb_define_const(cFoo, "STUFF", rb_str_new2("C:\\Program Files\\Stuff")); + + /* Default definition */ + rb_define_const(cFoo, "NOSEMI", INT2FIX(99)); + + rb_define_const(cFoo, "NOCOMMENT", rb_str_new2("No comment")); + + /* + * Multiline comment goes here because this comment spans multiple lines. + * Multiline comment goes here because this comment spans multiple lines. + */ + rb_define_const(cFoo, "MULTILINE", INT2FIX(1)); + + /* + * 1: Multiline comment goes here because this comment spans multiple lines. + * Multiline comment goes here because this comment spans multiple lines. + */ + rb_define_const(cFoo, "MULTILINE_VALUE", INT2FIX(1)); + + /* Multiline comment goes here because this comment spans multiple lines. + * Multiline comment goes here because this comment spans multiple lines. + */ + rb_define_const(cFoo, "MULTILINE_NOT_EMPTY", INT2FIX(1)); + +} + EOF + + parser = util_parser content + + parser.do_classes + parser.do_constants + + klass = parser.classes['cFoo'] + assert klass + + constants = klass.constants + assert !klass.constants.empty? + + constants = constants.map { |c| [c.name, c.value, c.comment] } + + assert_equal ['PERFECT', '300', + "\n The highest possible score in bowling \n "], + constants.shift + assert_equal ['CHEER', 'Huzzah!', + "\n What you cheer when you roll a perfect game \n "], + constants.shift + assert_equal ['TEST', 'TEST:TEST', + "\n Checking to see if escaped semicolon works \n "], + constants.shift + assert_equal ['MSEPARATOR', '\\', + "\n The file separator on MS Windows \n "], + constants.shift + assert_equal ['SEPARATOR', '/', + "\n The file separator on Unix \n "], + constants.shift + assert_equal ['STUFF', 'C:\\Program Files\\Stuff', + "\n A directory on MS Windows \n "], + constants.shift + assert_equal ['NOSEMI', 'INT2FIX(99)', + "\n Default definition \n "], + constants.shift + assert_equal ['NOCOMMENT', 'rb_str_new2("No comment")', nil], + constants.shift + + comment = <<-EOF.chomp + + + Multiline comment goes here because this comment spans multiple lines. + Multiline comment goes here because this comment spans multiple lines. + + + EOF + assert_equal ['MULTILINE', 'INT2FIX(1)', comment], constants.shift + assert_equal ['MULTILINE_VALUE', '1', comment], constants.shift + + comment = <<-EOF.chomp + + Multiline comment goes here because this comment spans multiple lines. + Multiline comment goes here because this comment spans multiple lines. + + + EOF + assert_equal ['MULTILINE_NOT_EMPTY', 'INT2FIX(1)', comment], constants.shift + + assert constants.empty?, constants.inspect + end + + def util_parser(content) + parser = RDoc::C_Parser.new @top_level, @fn, content, @options, @stats + parser.progress = @progress + parser + end + +end + |