The lexer maintains a stack of last seen comments.
On blank lines the lexer flush the comments.
On each opening brace the lexer enters a new stack level.
On each block AST nodes, the stack is popped.

Each AST nodes has a doc property that is filled with the
last seen comments on node creation (in fact only on important node
creation representing statements).

Signed-off-by: Brice Figureau <[EMAIL PROTECTED]>
---
 lib/puppet/parser/ast.rb                   |   16 ++
 lib/puppet/parser/ast/casestatement.rb     |    2 +
 lib/puppet/parser/ast/collection.rb        |    2 +
 lib/puppet/parser/ast/definition.rb        |    2 +
 lib/puppet/parser/ast/else.rb              |    3 +
 lib/puppet/parser/ast/function.rb          |    3 +
 lib/puppet/parser/ast/hostclass.rb         |    3 +
 lib/puppet/parser/ast/ifstatement.rb       |    3 +
 lib/puppet/parser/ast/node.rb              |    3 +
 lib/puppet/parser/ast/resource.rb          |    3 +
 lib/puppet/parser/ast/resource_defaults.rb |    2 +
 lib/puppet/parser/ast/resource_override.rb |    3 +
 lib/puppet/parser/ast/vardef.rb            |    3 +
 lib/puppet/parser/grammar.ra               |   22 +++-
 lib/puppet/parser/lexer.rb                 |   60 +++++++-
 lib/puppet/parser/parser.rb                |  212 +++++++++++++++-------------
 lib/puppet/parser/parser_support.rb        |   22 +++-
 17 files changed, 253 insertions(+), 111 deletions(-)

diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb
index ddf8852..198f5f4 100644
--- a/lib/puppet/parser/ast.rb
+++ b/lib/puppet/parser/ast.rb
@@ -12,8 +12,24 @@ class Puppet::Parser::AST
 
     include Puppet::Util::Errors
     include Puppet::Util::MethodHelper
+    include Puppet::Util::Docs
+
     attr_accessor :line, :file, :parent, :scope
 
+    # don't fetch lexer comment by default
+    def use_docs
+        false
+    end
+
+    # allow our subclass to specify they want documentation
+    class << self
+        def associates_doc
+            define_method("use_docs") do
+                true
+            end
+        end
+    end
+
     # Does this ast object set something?  If so, it gets evaluated first.
     def self.settor?
         if defined? @settor
diff --git a/lib/puppet/parser/ast/casestatement.rb 
b/lib/puppet/parser/ast/casestatement.rb
index aa03090..73fbdcf 100644
--- a/lib/puppet/parser/ast/casestatement.rb
+++ b/lib/puppet/parser/ast/casestatement.rb
@@ -6,6 +6,8 @@ class Puppet::Parser::AST
     class CaseStatement < AST::Branch
         attr_accessor :test, :options, :default
 
+        associates_doc
+
         # Short-curcuit evaluation.  Return the value of the statements for
         # the first option that matches.
         def evaluate(scope)
diff --git a/lib/puppet/parser/ast/collection.rb 
b/lib/puppet/parser/ast/collection.rb
index 9e795a3..a51b9b4 100644
--- a/lib/puppet/parser/ast/collection.rb
+++ b/lib/puppet/parser/ast/collection.rb
@@ -8,6 +8,8 @@ class Puppet::Parser::AST
 class Collection < AST::Branch
     attr_accessor :type, :query, :form
 
+    associates_doc
+
     # We return an object that does a late-binding evaluation.
     def evaluate(scope)
         if self.query
diff --git a/lib/puppet/parser/ast/definition.rb 
b/lib/puppet/parser/ast/definition.rb
index 0c65c70..3cd8e79 100644
--- a/lib/puppet/parser/ast/definition.rb
+++ b/lib/puppet/parser/ast/definition.rb
@@ -10,6 +10,8 @@ class Puppet::Parser::AST::Definition < 
Puppet::Parser::AST::Branch
         attr_accessor :name
     end
 
+    associates_doc
+
     # The class name
     @name = :definition
 
diff --git a/lib/puppet/parser/ast/else.rb b/lib/puppet/parser/ast/else.rb
index affac62..70e80b4 100644
--- a/lib/puppet/parser/ast/else.rb
+++ b/lib/puppet/parser/ast/else.rb
@@ -4,6 +4,9 @@ class Puppet::Parser::AST
     # A separate ElseIf statement; can function as an 'else' if there's no
     # test.
     class Else < AST::Branch
+
+        associates_doc
+
         attr_accessor :statements
 
         def each
diff --git a/lib/puppet/parser/ast/function.rb 
b/lib/puppet/parser/ast/function.rb
index eb36fa9..192940a 100644
--- a/lib/puppet/parser/ast/function.rb
+++ b/lib/puppet/parser/ast/function.rb
@@ -3,6 +3,9 @@ require 'puppet/parser/ast/branch'
 class Puppet::Parser::AST
     # An AST object to call a function.
     class Function < AST::Branch
+
+        associates_doc
+
         attr_accessor :name, :arguments
 
         @settor = true
diff --git a/lib/puppet/parser/ast/hostclass.rb 
b/lib/puppet/parser/ast/hostclass.rb
index 4f5c479..23d9a00 100644
--- a/lib/puppet/parser/ast/hostclass.rb
+++ b/lib/puppet/parser/ast/hostclass.rb
@@ -4,6 +4,9 @@ require 'puppet/parser/ast/definition'
 # in that each class is a singleton -- only one will exist for a given
 # node.
 class Puppet::Parser::AST::HostClass < Puppet::Parser::AST::Definition
+
+    associates_doc
+
     @name = :class
 
     # Are we a child of the passed class?  Do a recursive search up our
diff --git a/lib/puppet/parser/ast/ifstatement.rb 
b/lib/puppet/parser/ast/ifstatement.rb
index afa2cd5..d216b7c 100644
--- a/lib/puppet/parser/ast/ifstatement.rb
+++ b/lib/puppet/parser/ast/ifstatement.rb
@@ -3,6 +3,9 @@ require 'puppet/parser/ast/branch'
 class Puppet::Parser::AST
     # A basic 'if/elsif/else' statement.
     class IfStatement < AST::Branch
+
+        associates_doc
+
         attr_accessor :test, :else, :statements
 
         def each
diff --git a/lib/puppet/parser/ast/node.rb b/lib/puppet/parser/ast/node.rb
index 2bf6c18..442518f 100644
--- a/lib/puppet/parser/ast/node.rb
+++ b/lib/puppet/parser/ast/node.rb
@@ -3,6 +3,9 @@ require 'puppet/parser/ast/hostclass'
 # The specific code associated with a host.  Nodes are annoyingly unlike
 # other objects.  That's just the way it is, at least for now.
 class Puppet::Parser::AST::Node < Puppet::Parser::AST::HostClass
+
+    associates_doc
+
     @name = :node
 
     def initialize(options)
diff --git a/lib/puppet/parser/ast/resource.rb 
b/lib/puppet/parser/ast/resource.rb
index 8a60522..1a07fc5 100644
--- a/lib/puppet/parser/ast/resource.rb
+++ b/lib/puppet/parser/ast/resource.rb
@@ -4,6 +4,9 @@ require 'puppet/parser/ast/resource_reference'
 # builtin type.
 class Puppet::Parser::AST
 class Resource < AST::ResourceReference
+
+    associates_doc
+
     attr_accessor :title, :type, :exported, :virtual
     attr_reader :params
 
diff --git a/lib/puppet/parser/ast/resource_defaults.rb 
b/lib/puppet/parser/ast/resource_defaults.rb
index 4856f05..4919817 100644
--- a/lib/puppet/parser/ast/resource_defaults.rb
+++ b/lib/puppet/parser/ast/resource_defaults.rb
@@ -6,6 +6,8 @@ class Puppet::Parser::AST
     class ResourceDefaults < AST::Branch
         attr_accessor :type, :params
 
+        associates_doc
+
         # As opposed to ResourceDef, this stores each default for the given
         # object type.
         def evaluate(scope)
diff --git a/lib/puppet/parser/ast/resource_override.rb 
b/lib/puppet/parser/ast/resource_override.rb
index 8380dcd..5c4a241 100644
--- a/lib/puppet/parser/ast/resource_override.rb
+++ b/lib/puppet/parser/ast/resource_override.rb
@@ -4,6 +4,9 @@ class Puppet::Parser::AST
     # Set a parameter on a resource specification created somewhere else in the
     # configuration.  The object is responsible for verifying that this is 
allowed.
     class ResourceOverride < Resource
+
+        associates_doc
+
         attr_accessor :object
         attr_reader :params
 
diff --git a/lib/puppet/parser/ast/vardef.rb b/lib/puppet/parser/ast/vardef.rb
index a3094ac..2d5f623 100644
--- a/lib/puppet/parser/ast/vardef.rb
+++ b/lib/puppet/parser/ast/vardef.rb
@@ -3,6 +3,9 @@ require 'puppet/parser/ast/branch'
 class Puppet::Parser::AST
     # Define a variable.  Stores the value in the current scope.
     class VarDef < AST::Branch
+
+        associates_doc
+
         attr_accessor :name, :value, :append
 
         @settor = true
diff --git a/lib/puppet/parser/grammar.ra b/lib/puppet/parser/grammar.ra
index 23c2934..67303ab 100644
--- a/lib/puppet/parser/grammar.ra
+++ b/lib/puppet/parser/grammar.ra
@@ -130,6 +130,7 @@ namestring:       name
                 }
 
 resource:       classname LBRACE resourceinstances endsemi RBRACE {
+    @lexer.commentpop
     array = val[2]
     if array.instance_of?(AST::ResourceInstance)
         array = [array]
@@ -158,6 +159,7 @@ resource:       classname LBRACE resourceinstances endsemi 
RBRACE {
 
 # Override a value set elsewhere in the configuration.
 resourceoverride:     resourceref LBRACE anyparams endcomma RBRACE {
+    @lexer.commentpop
     result = ast AST::ResourceOverride, :object => val[0], :params => val[2]
 }
 
@@ -198,7 +200,7 @@ collection:     classref collectrhand {
         Puppet.warning addcontext("Collection names must now be capitalized")
     end
     type = val[0].downcase
-    args = {:type => type}
+    args = {:type => type }
 
     if val[1].is_a?(AST::CollExpr)
         args[:query] = val[1]
@@ -410,6 +412,7 @@ resourceref: NAME LBRACK rvalues RBRACK {
 }
 
 ifstatement:      IF expression LBRACE statements RBRACE else {
+    @lexer.commentpop
     args = {
         :test => val[1],
         :statements => val[3]
@@ -422,6 +425,7 @@ ifstatement:      IF expression LBRACE statements RBRACE 
else {
     result = ast AST::IfStatement, args
 }
                 | IF expression LBRACE RBRACE else {
+    @lexer.commentpop
     args = {
         :test => val[1],
         :statements => ast(AST::Nop)
@@ -436,9 +440,11 @@ ifstatement:      IF expression LBRACE statements RBRACE 
else {
 
 else:             # nothing
                 | ELSE LBRACE statements RBRACE {
+    @lexer.commentpop
     result = ast AST::Else, :statements => val[2]
 }
                 | ELSE LBRACE RBRACE {
+    @lexer.commentpop
     result = ast AST::Else, :statements => ast(AST::Nop)
 }
 
@@ -508,6 +514,7 @@ expression:   rvalue
 }
 
 casestatement:  CASE rvalue LBRACE caseopts RBRACE {
+    @lexer.commentpop
     options = val[3]
     unless options.instance_of?(AST::ASTArray)
         options = ast AST::ASTArray, :children => [val[3]]
@@ -526,8 +533,10 @@ caseopts:     caseopt
 }
 
 caseopt:        casevalues COLON LBRACE statements RBRACE {
+    @lexer.commentpop
     result = ast AST::CaseOpt, :value => val[0], :statements => val[3]
 }               | casevalues COLON LBRACE RBRACE {
+    @lexer.commentpop
     result = ast(AST::CaseOpt,
         :value => val[0],
         :statements => ast(AST::ASTArray)
@@ -549,7 +558,10 @@ selector:     selectlhand QMARK svalues {
 }
 
 svalues:      selectval
-            | LBRACE sintvalues endcomma RBRACE { result = val[1] }
+            | LBRACE sintvalues endcomma RBRACE { 
+    @lexer.commentpop
+    result = val[1] 
+}
 
 sintvalues:   selectval
             | sintvalues comma selectval {
@@ -593,12 +605,14 @@ import: IMPORT qtexts {
 # Disable definition inheritance for now. 8/27/06, luke
 #definition: DEFINE NAME argumentlist parent LBRACE statements RBRACE {
 definition: DEFINE classname argumentlist LBRACE statements RBRACE {
+    @lexer.commentpop
     newdefine classname(val[1]), :arguments => val[2], :code => val[4]
     @lexer.indefine = false
     result = nil
 
 #}           | DEFINE NAME argumentlist parent LBRACE RBRACE {
 }           | DEFINE classname argumentlist LBRACE RBRACE {
+    @lexer.commentpop
     newdefine classname(val[1]), :arguments => val[2]
     @lexer.indefine = false
     result = nil
@@ -606,11 +620,13 @@ definition: DEFINE classname argumentlist LBRACE 
statements RBRACE {
 
 #hostclass: CLASS NAME argumentlist parent LBRACE statements RBRACE {
 hostclass: CLASS classname classparent LBRACE statements RBRACE {
+    @lexer.commentpop
     # Our class gets defined in the parent namespace, not our own.
     @lexer.namepop
     newclass classname(val[1]), :code => val[4], :parent => val[2]
     result = nil
 }           | CLASS classname classparent LBRACE RBRACE {
+    @lexer.commentpop
     # Our class gets defined in the parent namespace, not our own.
     @lexer.namepop
     newclass classname(val[1]), :parent => val[2]
@@ -618,9 +634,11 @@ hostclass: CLASS classname classparent LBRACE statements 
RBRACE {
 }
 
 nodedef: NODE hostnames nodeparent LBRACE statements RBRACE {
+    @lexer.commentpop
     newnode val[1], :parent => val[2], :code => val[4]
     result = nil
 }       |  NODE hostnames nodeparent LBRACE RBRACE {
+    @lexer.commentpop
     newnode val[1], :parent => val[2]
     result = nil
 }
diff --git a/lib/puppet/parser/lexer.rb b/lib/puppet/parser/lexer.rb
index dd6c29d..69a46d0 100644
--- a/lib/puppet/parser/lexer.rb
+++ b/lib/puppet/parser/lexer.rb
@@ -17,7 +17,7 @@ class Puppet::Parser::Lexer
 
     # Our base token class.
     class Token
-        attr_accessor :regex, :name, :string, :skip, :incr_line, :skip_text
+        attr_accessor :regex, :name, :string, :skip, :incr_line, :skip_text, 
:accumulate
 
         def initialize(regex, name)
             if regex.is_a?(String)
@@ -28,8 +28,10 @@ class Puppet::Parser::Lexer
             end
         end
 
-        def skip?
-            self.skip
+        %w{skip accumulate}.each do |method|
+            define_method(method+"?") do
+                self.send(method)
+            end
         end
 
         def to_s
@@ -155,11 +157,16 @@ class Puppet::Parser::Lexer
         [string_token, value]
     end
 
-    TOKENS.add_token :COMMENT, %r{#.*}, :skip => true
+    TOKENS.add_token :COMMENT, %r{#.*}, :accumulate => true, :skip => true do 
|lexer,value|
+        value.sub!(/# ?/,'')
+        [self, value]
+    end
 
-    TOKENS.add_token :MLCOMMENT, %r{/\*(.*?)\*/}m do |lexer, value|
+    TOKENS.add_token :MLCOMMENT, %r{/\*(.*?)\*/}m, :accumulate => true, :skip 
=> true do |lexer, value|
         lexer.line += value.count("\n")
-        [nil,nil]
+        value.sub!(/^\/\* ?/,'')
+        value.sub!(/ ?\*\/$/,'')
+        [self,value]
     end
 
     TOKENS.add_token :RETURN, "\n", :skip => true, :incr_line => true, 
:skip_text => true
@@ -325,6 +332,7 @@ class Puppet::Parser::Lexer
         @namestack = []
         @indefine = false
         @expected = []
+        @commentstack = ['']
     end
 
     # Make any necessary changes to the token and/or value.
@@ -333,12 +341,18 @@ class Puppet::Parser::Lexer
 
         skip() if token.skip_text
 
-        return if token.skip
+        return if token.skip and not token.accumulate?
 
         token, value = token.convert(self, value) if 
token.respond_to?(:convert)
 
         return unless token
 
+        if token.accumulate?
+            @commentstack.last << value + "\n"
+        end
+
+        return if token.skip
+
         return token, value
     end
 
@@ -389,6 +403,18 @@ class Puppet::Parser::Lexer
                 raise "Could not match '%s'" % nword
             end
 
+            if matched_token.name == :RETURN
+                # this matches a blank line
+                if @last_return
+                    # eat the previously accumulated comments
+                    getcomment
+                end
+                # since :RETURN skips, we won't survive to munge_token
+                @last_return = true
+            else
+                @last_return = false
+            end
+
             final_token, value = munge_token(matched_token, value)
 
             next unless final_token
@@ -399,6 +425,10 @@ class Puppet::Parser::Lexer
                 @expected.pop
             end
 
+            if final_token.name == :LBRACE
+                commentpush
+            end
+
             yield [final_token.name, value]
 
             if @previous_token
@@ -414,7 +444,6 @@ class Puppet::Parser::Lexer
                     @indefine = value
                 end
             end
-
             @previous_token = final_token
             skip()
         end
@@ -453,4 +482,19 @@ class Puppet::Parser::Lexer
     def string=(string)
         @scanner = StringScanner.new(string)
     end
+
+    # returns the content of the currently accumulated content cache
+    def commentpop
+        return @commentstack.pop
+    end
+
+    def getcomment
+        comment = @commentstack.pop
+        @commentstack.push('')
+        return comment
+    end
+
+    def commentpush
+        @commentstack.push('')
+    end
 end
diff --git a/lib/puppet/parser/parser_support.rb 
b/lib/puppet/parser/parser_support.rb
index 1583973..d590937 100644
--- a/lib/puppet/parser/parser_support.rb
+++ b/lib/puppet/parser/parser_support.rb
@@ -18,6 +18,7 @@ class Puppet::Parser::Parser
     attr_reader :version, :environment
     attr_accessor :files
 
+    attr_accessor :lexer
 
     # Add context to a message; useful for error messages and such.
     def addcontext(message, obj = nil)
@@ -56,7 +57,9 @@ class Puppet::Parser::Parser
             end
         end
 
-        return klass.new(hash)
+        k = klass.new(hash)
+        k.doc = lexer.getcomment if !k.nil? and k.use_docs and k.doc.empty?
+        return k
     end
 
     # The fully qualifed name, with the full namespace.
@@ -272,6 +275,7 @@ class Puppet::Parser::Parser
         end
         code = options[:code]
         parent = options[:parent]
+        doc = options[:doc]
 
         # If the class is already defined, then add code to it.
         if other = @astset.classes[name]
@@ -304,6 +308,12 @@ class Puppet::Parser::Parser
                     other.code ||= code
                 end
             end
+
+            if other.doc and doc
+                other.doc += doc
+            else
+                other.doc ||= doc
+            end
         else
             # Define it anew.
             # Note we're doing something somewhat weird here -- we're setting
@@ -312,6 +322,8 @@ class Puppet::Parser::Parser
             args = {:namespace => name, :classname => name, :parser => self}
             args[:code] = code if code
             args[:parentclass] = parent if parent
+            args[:doc] = doc
+
             @astset.classes[name] = ast AST::HostClass, args
         end
 
@@ -336,7 +348,8 @@ class Puppet::Parser::Parser
             :arguments => options[:arguments],
             :code => options[:code],
             :parser => self,
-            :classname => name
+            :classname => name,
+            :doc => options[:doc]
         }
 
         [:code, :arguments].each do |param|
@@ -350,6 +363,7 @@ class Puppet::Parser::Parser
     # table, not according to namespaces.
     def newnode(names, options = {})
         names = [names] unless names.instance_of?(Array)
+        doc = lexer.getcomment
         names.collect do |name|
             name = name.to_s.downcase
             if other = @astset.nodes[name]
@@ -358,7 +372,8 @@ class Puppet::Parser::Parser
             name = name.to_s if name.is_a?(Symbol)
             args = {
                 :name => name,
-                :parser => self
+                :parser => self,
+                :doc => doc
             }
             if options[:code]
                 args[:code] = options[:code]
@@ -399,6 +414,7 @@ class Puppet::Parser::Parser
             self.string = string
         end
         begin
+            @yydebug = false
             main = yyparse(@lexer,:scan)
         rescue Racc::ParseError => except
             error = Puppet::ParseError.new(except)
-- 
1.6.0.2


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Puppet Developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to