Commit 0c4fe37f authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Support record type in TypeParser.

In supporting Google Closure Compiler syntax.

For the record types to even reach type parser the DocParser needed
to be modified to parse the {...} type blocks by taking account the
need to balance the braces.
parent 66e50725
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -439,14 +439,24 @@ module JsDuck
    # matches {...=} and returns text inside brackets
    def typedef
      match(/\{/)
      name = @input.scan(/[^}]+/)
      name = @input.scan(/[^{}]*/)

      # Type definition can contain nested braces: {{foo:Number}}
      # In such case we parse the definition so that the braces are balanced.
      while @input.check(/[{]/)
        name += "{" + typedef[:type] +"}"
        name += @input.scan(/[^{}]*/)
      end

      if name =~ /=$/
        name = name.chop
        optional = true
      else
        optional = false
      end

      match(/\}/)

      return {:type => name, :optional => optional}
    end

+45 −5
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ module JsDuck
  #     Array.<string>
  #     Object.<string, number>
  #
  #     {myNum: number, myObject}
  #
  #     (number|boolean)
  #     ?number
  #     !Object
@@ -35,10 +37,6 @@ module JsDuck
  #     function(?string=, number=)
  #     function(string, ...[number])
  #
  # Currently not supported:
  #
  #     {myNum: number, myObject}
  #
  class TypeParser
    # Allows to check the type of error that was encountered.
    # It will be either of the two:
@@ -143,7 +141,7 @@ module JsDuck
    #
    #     <array-type> ::= <atomic-type> [ "[]" ]*
    #
    #     <atomic-type> ::= <type-name> | <union-type> | <function-type>
    #     <atomic-type> ::= <union-type> | <record-type> | <function-type> | <type-name>
    #
    def null_type
      if nullability = @input.scan(/[?!]/)
@@ -152,6 +150,8 @@ module JsDuck

      if @input.check(/\(/)
        return false unless union_type
      elsif @input.check(/\{/)
        return false unless record_type
      elsif @input.check(/function\(/)
        return false unless function_type
      else
@@ -179,6 +179,46 @@ module JsDuck
      true
    end

    #
    #     <record-type> ::= "{" <rtype-item> [ "," <rtype-item> ]* "}"
    #
    def record_type
      @out << @input.scan(/\{/)

      return false unless rtype_item

      while @input.scan(/,/)
        @out << ","
        return false unless rtype_item
      end

      return false unless @input.scan(/\}/)
      @out << "}"

      true
    end

    #
    #     <rtype-item> ::= <ident> ":" <null-type>
    #                    | <ident>
    #
    def rtype_item
      skip_whitespace

      key = @input.scan(/[a-zA-Z0-9_]+/)
      return false unless key

      skip_whitespace
      if @input.scan(/:/)
        @out << ":"
        skip_whitespace
        return false unless null_type
        skip_whitespace
      end

      true
    end

    #
    #     <function-type> ::= "function(" <function-type-arguments> ")" [ ":" <null-type> ]
    #
+13 −0
Original line number Diff line number Diff line
@@ -117,5 +117,18 @@ describe JsDuck::DocParser do
    end
  end

  describe "type definition with nested {braces}" do
    before do
      @tag = parse_single(<<-EOS.strip)[0]
        /**
         * @param {{foo:{bar:Number}}} x
         */
      EOS
    end
    it "is parsed ensuring balanced braces" do
      @tag[:type].should == "{foo:{bar:Number}}"
    end
  end

end
+18 −0
Original line number Diff line number Diff line
@@ -268,6 +268,24 @@ describe JsDuck::TypeParser do
      end
    end

    describe "record type" do
      it "matches list of properties" do
        parse("{foo, bar, baz}").should == true
      end

      it "matches properties with types" do
        parse("{foo: String, bar: Number}").should == true
      end

      it "matches property with complex type" do
        parse("{foo: (String|Array.<String>)}").should == true
      end

      it "matches nested record type" do
        parse("{foo: {bar}}").should == true
      end
    end

    it "always matches primitive types" do
      parse("boolean").should == true
      parse("number").should == true