This is an attamept to write a basic parser for Perl’s POD (Plain old Documentation) format. Ruby code must co-exist with Perl, and some tasks are easier in Perl than Ruby because of existing libraries.
One difficult is that Perl POD has no means of identifying the classes (packages) and methods (subs) with which it is associated, it is more like literate programming in so far as it just happens to be in the same place as the code, but need not be.
We would like to support all the markup the POD provides so that it will convert happily to HTML. At the moment I don’t think I can do that: time constraints.
Prepare to parse a perl file
# File lib/rdoc/parser/perl.rb, line 28
28: def initialize(top_level, file_name, content, options, stats)
29: super
30:
31: preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include
32:
33: preprocess.handle @content do |directive, param|
34: warn "Unrecognized directive '#{directive}' in #{@file_name}"
35: end
36: end
Filter the perl markup that does the same as the rdoc filtering. Only basic for now. Will probably need a proper parser to cope with C<<…>> etc
# File lib/rdoc/parser/perl.rb, line 151
151: def filter(comment)
152: return '' if comment =~ /^=pod\s*$/
153: comment.gsub!(/^=pod/, '==')
154: comment.gsub!(/^=head(\d+)/) do
155: "=" * $1.to_i
156: end
157: comment.gsub!(/=item/, '');
158: comment.gsub!(/C<(.*?)>/, '<tt>\1</tt>');
159: comment.gsub!(/I<(.*?)>/, '<i>\1</i>');
160: comment.gsub!(/B<(.*?)>/, '<b>\1</b>');
161: comment
162: end
Extract the Pod(-like) comments from the code. At its most basic there will ne no need to distinguish between the different types of header, etc.
This uses a simple finite state machine, in a very procedural pattern. I could “replace case with polymorphism” but I think it would obscure the intent, scatter the code all over tha place. This machine is necessary because POD requires that directives be preceded by blank lines, so reading line by line is necessary, and preserving state about what is seen is necesary.
# File lib/rdoc/parser/perl.rb, line 51
51: def scan
52:
53: @top_level.comment ||= ""
54: state=:code_blank
55: line_number = 0
56: line = nil
57:
58: # This started out as a really long nested case statement,
59: # which also led to repetitive code. I'd like to avoid that
60: # so I'm using a "table" instead.
61:
62: # Firstly we need some procs to do the transition and processing
63: # work. Because these are procs they are closures, and they can
64: # use variables in the local scope.
65: #
66: # First, the "nothing to see here" stuff.
67: code_noop = lambda do
68: if line =~ /^\s+$/
69: state = :code_blank
70: end
71: end
72:
73: pod_noop = lambda do
74: if line =~ /^\s+$/
75: state = :pod_blank
76: end
77: @top_level.comment += filter(line)
78: end
79:
80: begin_noop = lambda do
81: if line =~ /^\s+$/
82: state = :begin_blank
83: end
84: @top_level.comment += filter(line)
85: end
86:
87: # Now for the blocks that process code and comments...
88:
89: transit_to_pod = lambda do
90: case line
91: when /^=(?:pod|head\d+)/
92: state = :pod_no_blank
93: @top_level.comment += filter(line)
94: when /^=over/
95: state = :over_no_blank
96: @top_level.comment += filter(line)
97: when /^=(?:begin|for)/
98: state = :begin_no_blank
99: end
100: end
101:
102: process_pod = lambda do
103: case line
104: when /^\s*$/
105: state = :pod_blank
106: @top_level.comment += filter(line)
107: when /^=cut/
108: state = :code_no_blank
109: when /^=end/
110: $stderr.puts "'=end' unexpected at #{line_number} in #{@file_name}"
111: else
112: @top_level.comment += filter(line)
113: end
114: end
115:
116:
117: process_begin = lambda do
118: case line
119: when /^\s*$/
120: state = :begin_blank
121: @top_level.comment += filter(line)
122: when /^=end/
123: state = :code_no_blank
124: when /^=cut/
125: $stderr.puts "'=cut' unexpected at #{line_number} in #{@file_name}"
126: else
127: @top_level.comment += filter(line)
128: end
129:
130: end
131:
132:
133: transitions = { :code_no_blank => code_noop,
134: :code_blank => transit_to_pod,
135: :pod_no_blank => pod_noop,
136: :pod_blank => process_pod,
137: :begin_no_blank => begin_noop,
138: :begin_blank => process_begin}
139: @content.each_line do |l|
140: line = l
141: line_number += 1
142: transitions[state].call
143: end # each line
144:
145: @top_level
146: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.