diff --git a/jsduck.rb b/jsduck.rb index 0fec5ebe6226a9cd573d9fc8502363676674a8dc..3dc933d635bae6b7be3f5536ae54a1b4aec83358 100755 --- a/jsduck.rb +++ b/jsduck.rb @@ -94,16 +94,22 @@ module JsDuck class DocComment - attr_accessor :function, :doc - def initialize(input) - @function = "" - @doc = "" - @params = [] - @return = "void" + @current_tag = {:doc => ""} + @tags = {:default => @current_tag} parse(purify(input)) end + # sets the name and properties of the default at-tag + def set_default(tagname, attrs={}) + @tags[tagname] = attrs + @tags[tagname][:doc] = @tags[:default][:doc] + end + + def [](tagname) + @tags[tagname] + end + # Extracts content inside /** ... */ def purify(input) result = [] @@ -121,25 +127,78 @@ module JsDuck end def parse(input) - doc = [] - input.each_line do |line| - line.chomp! - if line =~ /\A@param\b/ then - @params << line - elsif line =~ /\A@return\b/ then - @return = line - else - doc << line + @input = StringScanner.new(input) + while !@input.eos? do + if look(/@return\b/) then + at_return + elsif look(/@param\b/) then + at_param + elsif look(/@/) then + @current_tag[:doc] += @input.scan(/@/) + elsif look(/[^@]/) then + @current_tag[:doc] += @input.scan(/[^@]+/) end end - @doc = doc.join("\n") + end + + # matches @return {type} ... + def at_return + match(/@return/) + @current_tag = @tags[:return] = {:doc => ""} + skip_white + if look(/\{/) then + @current_tag[:type] = typedef + end + skip_white + end + + # matches @param {type} variable ... + def at_param + match(/@param/) + @current_tag = {:doc => ""} + if @tags[:param] then + @tags[:param] << @current_tag + else + @tags[:param] = [@current_tag] + end + skip_white + if look(/\{/) then + @current_tag[:type] = typedef + end + skip_white + if look(/\w/) then + @current_tag[:name] = ident + end + skip_white + end + + # matches {...} and returns text inside brackets + def typedef + match(/\{/) + name = @input.scan(/[^}]+/) + match(/\}/) + return name + end + + # matches identifier and returns its name + def ident + @input.scan(/\w+/) + end + + def look(re) + @input.check(re) + end + + def match(re) + @input.scan(re) + end + + def skip_white + @input.scan(/\s+/) end def print - puts "function: " + @function - puts "doc: " + @doc - puts "params: " + @params.join("\n") - puts "return: " + @return + pp @tags end end @@ -153,16 +212,16 @@ module JsDuck if lex.look("function", :keyword) then lex.next # function name(){ - doc.function = lex.next + doc.set_default(:function, {:name => lex.next}) elsif lex.look("var", :keyword, "=", "function") then lex.next # var name = function(){ - doc.function = lex.next + doc.set_default(:function, {:name => lex.next}) elsif lex.look(:keyword, "=", "function") || lex.look(:keyword, ":", "function") || lex.look(:string, ":", "function") then # name: function(){ - doc.function = lex.next + doc.set_default(:function, {:name => lex.next}) end docs << doc else diff --git a/tc_jsduck.rb b/tc_jsduck.rb index 25290287b7ef64c3f161e1562dff83d1e76c6e90..a7d247ec64e4cd1dd0e26a5e9fc3e051c81ed506 100644 --- a/tc_jsduck.rb +++ b/tc_jsduck.rb @@ -15,6 +15,38 @@ class TestJsDuck < Test::Unit::TestCase assert_equal([], JsDuck.parse("/* ") ) end + def test_return + docs = JsDuck.parse(" +/** + * Some function + * @return {String} some value + * on several + * lines + */ +") + assert_equal("String", docs[0][:return][:type]) + assert_equal("some value\non several\nlines", docs[0][:return][:doc]) + end + + def test_param + docs = JsDuck.parse(" +/** + * Some function + * @param {Number} x value 1 + * @param {Float} y value 2 + */ +") + param1 = docs[0][:param][0] + assert_equal("Number", param1[:type]) + assert_equal("x", param1[:name]) + assert_equal("value 1\n", param1[:doc]) + + param2 = docs[0][:param][1] + assert_equal("Float", param2[:type]) + assert_equal("y", param2[:name]) + assert_equal("value 2", param2[:doc]) + end + def test_function docs = JsDuck.parse(" /** @@ -23,8 +55,8 @@ class TestJsDuck < Test::Unit::TestCase function foo() { } ") - assert_equal("Some function", docs[0].doc) - assert_equal("foo", docs[0].function) + assert_equal("Some function", docs[0][:function][:doc]) + assert_equal("foo", docs[0][:function][:name]) end def test_function_with_var @@ -34,7 +66,7 @@ function foo() { var foo = function() { } ") - assert_equal("foo", docs[0].function) + assert_equal("foo", docs[0][:function][:name]) end def test_function_without_var @@ -44,7 +76,7 @@ var foo = function() { foo = function() { } ") - assert_equal("foo", docs[0].function) + assert_equal("foo", docs[0][:function][:name]) end def test_function_in_object_literal @@ -54,7 +86,7 @@ foo = function() { foo: function() { } ") - assert_equal("foo", docs[0].function) + assert_equal("foo", docs[0][:function][:name]) end def test_function_in_object_literal_string @@ -64,7 +96,7 @@ foo: function() { 'foo': function() { } ") - assert_equal("foo", docs[0].function) + assert_equal("foo", docs[0][:function][:name]) end def test_function_private @@ -73,7 +105,7 @@ foo: function() { function foo() { } ") - assert_equal(0, docs.length) + assert_equal([], docs) end end