yard 0.9.29 → 0.9.30
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of yard might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/docs/CodeObjects.md +115 -0
- data/docs/GettingStarted.md +679 -0
- data/docs/Handlers.md +152 -0
- data/docs/Overview.md +61 -0
- data/docs/Parser.md +191 -0
- data/docs/Tags.md +283 -0
- data/docs/TagsArch.md +123 -0
- data/docs/Templates.md +496 -0
- data/docs/WhatsNew.md +1245 -0
- data/docs/images/code-objects-class-diagram.png +0 -0
- data/docs/images/handlers-class-diagram.png +0 -0
- data/docs/images/overview-class-diagram.png +0 -0
- data/docs/images/parser-class-diagram.png +0 -0
- data/docs/images/tags-class-diagram.png +0 -0
- data/docs/templates/default/fulldoc/html/full_list_tag.erb +9 -0
- data/docs/templates/default/fulldoc/html/setup.rb +6 -0
- data/docs/templates/default/layout/html/setup.rb +9 -0
- data/docs/templates/default/layout/html/tag_list.erb +11 -0
- data/docs/templates/default/yard_tags/html/list.erb +18 -0
- data/docs/templates/default/yard_tags/html/setup.rb +26 -0
- data/docs/templates/plugin.rb +70 -0
- data/lib/yard/version.rb +1 -1
- data/po/ja.po +31108 -0
- data/templates/default/class/dot/setup.rb +7 -0
- data/templates/default/class/dot/superklass.erb +3 -0
- data/templates/default/class/html/constructor_details.erb +8 -0
- data/templates/default/class/html/setup.rb +2 -0
- data/templates/default/class/html/subclasses.erb +4 -0
- data/templates/default/class/setup.rb +36 -0
- data/templates/default/class/text/setup.rb +12 -0
- data/templates/default/class/text/subclasses.erb +5 -0
- data/templates/default/constant/text/header.erb +11 -0
- data/templates/default/constant/text/setup.rb +4 -0
- data/templates/default/docstring/html/abstract.erb +4 -0
- data/templates/default/docstring/html/deprecated.erb +1 -0
- data/templates/default/docstring/html/index.erb +5 -0
- data/templates/default/docstring/html/note.erb +6 -0
- data/templates/default/docstring/html/private.erb +4 -0
- data/templates/default/docstring/html/returns_void.erb +1 -0
- data/templates/default/docstring/html/text.erb +1 -0
- data/templates/default/docstring/html/todo.erb +6 -0
- data/templates/default/docstring/setup.rb +52 -0
- data/templates/default/docstring/text/abstract.erb +2 -0
- data/templates/default/docstring/text/deprecated.erb +2 -0
- data/templates/default/docstring/text/index.erb +2 -0
- data/templates/default/docstring/text/note.erb +4 -0
- data/templates/default/docstring/text/private.erb +2 -0
- data/templates/default/docstring/text/returns_void.erb +1 -0
- data/templates/default/docstring/text/text.erb +1 -0
- data/templates/default/docstring/text/todo.erb +4 -0
- data/templates/default/fulldoc/html/css/common.css +1 -0
- data/templates/default/fulldoc/html/css/full_list.css +58 -0
- data/templates/default/fulldoc/html/css/style.css +497 -0
- data/templates/default/fulldoc/html/frames.erb +17 -0
- data/templates/default/fulldoc/html/full_list.erb +37 -0
- data/templates/default/fulldoc/html/full_list_class.erb +2 -0
- data/templates/default/fulldoc/html/full_list_file.erb +7 -0
- data/templates/default/fulldoc/html/full_list_method.erb +10 -0
- data/templates/default/fulldoc/html/js/app.js +314 -0
- data/templates/default/fulldoc/html/js/full_list.js +216 -0
- data/templates/default/fulldoc/html/js/jquery.js +4 -0
- data/templates/default/fulldoc/html/setup.rb +241 -0
- data/templates/default/layout/dot/header.erb +6 -0
- data/templates/default/layout/dot/setup.rb +15 -0
- data/templates/default/layout/html/breadcrumb.erb +11 -0
- data/templates/default/layout/html/files.erb +11 -0
- data/templates/default/layout/html/footer.erb +5 -0
- data/templates/default/layout/html/headers.erb +15 -0
- data/templates/default/layout/html/index.erb +2 -0
- data/templates/default/layout/html/layout.erb +24 -0
- data/templates/default/layout/html/listing.erb +4 -0
- data/templates/default/layout/html/objects.erb +32 -0
- data/templates/default/layout/html/script_setup.erb +4 -0
- data/templates/default/layout/html/search.erb +13 -0
- data/templates/default/layout/html/setup.rb +89 -0
- data/templates/default/method/html/header.erb +17 -0
- data/templates/default/method/setup.rb +4 -0
- data/templates/default/method/text/header.erb +1 -0
- data/templates/default/method_details/html/header.erb +3 -0
- data/templates/default/method_details/html/method_signature.erb +25 -0
- data/templates/default/method_details/html/source.erb +10 -0
- data/templates/default/method_details/setup.rb +11 -0
- data/templates/default/method_details/text/header.erb +10 -0
- data/templates/default/method_details/text/method_signature.erb +12 -0
- data/templates/default/method_details/text/setup.rb +11 -0
- data/templates/default/module/dot/child.erb +1 -0
- data/templates/default/module/dot/dependencies.erb +3 -0
- data/templates/default/module/dot/header.erb +6 -0
- data/templates/default/module/dot/info.erb +14 -0
- data/templates/default/module/dot/setup.rb +15 -0
- data/templates/default/module/html/attribute_details.erb +10 -0
- data/templates/default/module/html/attribute_summary.erb +8 -0
- data/templates/default/module/html/box_info.erb +43 -0
- data/templates/default/module/html/children.erb +8 -0
- data/templates/default/module/html/constant_summary.erb +17 -0
- data/templates/default/module/html/defines.erb +3 -0
- data/templates/default/module/html/header.erb +5 -0
- data/templates/default/module/html/inherited_attributes.erb +14 -0
- data/templates/default/module/html/inherited_constants.erb +8 -0
- data/templates/default/module/html/inherited_methods.erb +19 -0
- data/templates/default/module/html/item_summary.erb +40 -0
- data/templates/default/module/html/method_details_list.erb +9 -0
- data/templates/default/module/html/method_summary.erb +14 -0
- data/templates/default/module/html/methodmissing.erb +12 -0
- data/templates/default/module/html/pre_docstring.erb +1 -0
- data/templates/default/module/setup.rb +167 -0
- data/templates/default/module/text/children.erb +10 -0
- data/templates/default/module/text/class_meths_list.erb +8 -0
- data/templates/default/module/text/extends.erb +8 -0
- data/templates/default/module/text/header.erb +7 -0
- data/templates/default/module/text/includes.erb +8 -0
- data/templates/default/module/text/instance_meths_list.erb +8 -0
- data/templates/default/module/text/setup.rb +13 -0
- data/templates/default/onefile/html/files.erb +5 -0
- data/templates/default/onefile/html/headers.erb +6 -0
- data/templates/default/onefile/html/layout.erb +17 -0
- data/templates/default/onefile/html/readme.erb +3 -0
- data/templates/default/onefile/html/setup.rb +62 -0
- data/templates/default/root/dot/child.erb +3 -0
- data/templates/default/root/dot/setup.rb +6 -0
- data/templates/default/root/html/setup.rb +2 -0
- data/templates/default/tags/html/example.erb +11 -0
- data/templates/default/tags/html/index.erb +3 -0
- data/templates/default/tags/html/option.erb +24 -0
- data/templates/default/tags/html/overload.erb +14 -0
- data/templates/default/tags/html/see.erb +8 -0
- data/templates/default/tags/html/tag.erb +20 -0
- data/templates/default/tags/setup.rb +57 -0
- data/templates/default/tags/text/example.erb +12 -0
- data/templates/default/tags/text/index.erb +1 -0
- data/templates/default/tags/text/option.erb +20 -0
- data/templates/default/tags/text/overload.erb +19 -0
- data/templates/default/tags/text/see.erb +11 -0
- data/templates/default/tags/text/tag.erb +13 -0
- data/templates/guide/class/html/setup.rb +2 -0
- data/templates/guide/docstring/html/setup.rb +2 -0
- data/templates/guide/fulldoc/html/css/style.css +108 -0
- data/templates/guide/fulldoc/html/js/app.js +33 -0
- data/templates/guide/fulldoc/html/setup.rb +74 -0
- data/templates/guide/layout/html/layout.erb +81 -0
- data/templates/guide/layout/html/setup.rb +25 -0
- data/templates/guide/method/html/header.erb +18 -0
- data/templates/guide/method/html/setup.rb +22 -0
- data/templates/guide/module/html/header.erb +7 -0
- data/templates/guide/module/html/method_list.erb +5 -0
- data/templates/guide/module/html/setup.rb +27 -0
- data/templates/guide/onefile/html/files.erb +4 -0
- data/templates/guide/onefile/html/setup.rb +6 -0
- data/templates/guide/onefile/html/toc.erb +3 -0
- data/templates/guide/tags/html/setup.rb +9 -0
- metadata +151 -2
data/docs/Handlers.md
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
# @title Handlers Architecture
|
2
|
+
|
3
|
+
# Handlers Architecture
|
4
|
+
|
5
|
+
Handlers allow the processing of parsed source code. Handling is done after
|
6
|
+
parsing to abstract away the implementation details of lexical and semantic
|
7
|
+
analysis on source and to only deal with the logic regarding recognizing
|
8
|
+
source statements as {file:docs/CodeObjects.md code objects}.
|
9
|
+
|
10
|
+
![Handlers Architecture Class Diagram](images/handlers-class-diagram.png)
|
11
|
+
|
12
|
+
## The Pipeline
|
13
|
+
|
14
|
+
After the {file:docs/Parser.md parser component} finishes analyzing the
|
15
|
+
source, it is handed off for post-processing to the {YARD::Handlers::Processor}
|
16
|
+
class, which is responsible for traversing the set of statements given by
|
17
|
+
the parser and delegating them to matching handlers. Handlers match when the
|
18
|
+
{YARD::Handlers::Base.handles?} method returns true for a given statement.
|
19
|
+
The handler can then perform any action after being invoked by the `process`
|
20
|
+
method.
|
21
|
+
|
22
|
+
## The Processor Class
|
23
|
+
|
24
|
+
The main purpose of the processor, as mentioned above, is to traverse through
|
25
|
+
the list of statements given to it by the parser. The processor also keeps
|
26
|
+
state about what is being processed. For instance, the processor is what keeps
|
27
|
+
track of the current namespace (the module or class an object is being defined
|
28
|
+
in), scope (class or instance), file and owner. The owner refers to the object
|
29
|
+
that is most directly responsible for the source statement being processed. This
|
30
|
+
is most often the same as the namespace, except when parsing the body of a method,
|
31
|
+
where the namespace would be the class/module the method is defined in and the
|
32
|
+
owner would be the method object itself.
|
33
|
+
|
34
|
+
## Implementing a Handler
|
35
|
+
|
36
|
+
This section covers the basics of implementing a *new-style* Ruby handler. For
|
37
|
+
details on implementing a legacy handler, see the "API Differences" section below.
|
38
|
+
|
39
|
+
a Ruby handler can be implemented simply by subclassing the {YARD::Handlers::Ruby::Base}
|
40
|
+
class and declaring what node types or source to process with the {YARD::Handlers::Base.handles handles}
|
41
|
+
class method. A very simple handler that handles a module definition would be:
|
42
|
+
|
43
|
+
class MyModuleHandler < YARD::Handlers::Ruby::Base
|
44
|
+
handles :module
|
45
|
+
|
46
|
+
def process
|
47
|
+
puts "Handling a module named #{statement[0].source}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
For details on what nodes are, and what node types are, see the
|
52
|
+
{file:docs/Parser.md parser architecture document}.
|
53
|
+
|
54
|
+
In this case the node type being handled is the `:module` type. More than one
|
55
|
+
node type or `handles` declarations may describe a single handler, for instance,
|
56
|
+
a handler that handles class definitions should handle the `:class` and `:sclass`
|
57
|
+
node types respectively (the latter refers to classes defined as `class << Something`).
|
58
|
+
The {YARD::Handlers::Base#statement statement} attribute refers to the current
|
59
|
+
node (or statement) that is being handled by the handler.
|
60
|
+
|
61
|
+
### Handling a Method Call
|
62
|
+
|
63
|
+
In some cases, a developer might need to handle a method call. The parser can
|
64
|
+
express a method call in many AST forms, so to simplify this process, a method
|
65
|
+
call can be handled by declaring the following in a `handles` statement:
|
66
|
+
|
67
|
+
class MyHandler < YARD::Handlers::Ruby::Base
|
68
|
+
handles method_call(:describe)
|
69
|
+
|
70
|
+
def process
|
71
|
+
# Process the method call
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
In this case we handle any of the method calls to method name `describe` with
|
76
|
+
the following syntaxes:
|
77
|
+
|
78
|
+
describe(something)
|
79
|
+
describe arg1, arg2, arg3
|
80
|
+
describe(something) { perform_a_block }
|
81
|
+
describe "Something" do
|
82
|
+
a_block
|
83
|
+
end
|
84
|
+
|
85
|
+
### Creating a new Code Object
|
86
|
+
|
87
|
+
Usually (but not always) handling is performed to create new code objects to add
|
88
|
+
to the registry (for information about code objects, see {file:docs/CodeObjects.md this document}).
|
89
|
+
Code objects should simply be created and added to the existing `namespace`. This
|
90
|
+
will be enough to add them to the registry. There is also a convenience
|
91
|
+
{YARD::Handlers::Base#register register} method which quickly sets standard attributed
|
92
|
+
on the newly created object, such as the file, line, source and docstring of the
|
93
|
+
object. This method will be seen in the next example.
|
94
|
+
|
95
|
+
### Handling an Inner Block
|
96
|
+
|
97
|
+
By default, the parser gives the processor class a list of all the top level
|
98
|
+
statements and the processor parses only those top level statements. If an inner
|
99
|
+
block of a module, class, method declaration or even a block passed to a method call
|
100
|
+
needs to be handled, the {YARD::Handlers::Base#parse_block parse_block} method must be called on the list of statements
|
101
|
+
to parse. This will send the list to the processor to continue processing on that
|
102
|
+
statement list. The source tree can be selectively parsed in this manner by parsing
|
103
|
+
only the inner blocks that are relevant to documentation.
|
104
|
+
|
105
|
+
For example, the module handler parses the inner body of a module by performing
|
106
|
+
the following commands:
|
107
|
+
|
108
|
+
class YARD::Handlers::Ruby::ModuleHandler < YARD::Handlers::Ruby::Base
|
109
|
+
handles :module
|
110
|
+
|
111
|
+
def process
|
112
|
+
modname = statement[0].source
|
113
|
+
mod = register ModuleObject.new(namespace, modname)
|
114
|
+
parse_block(statement[1], :namespace => mod)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
In this case `statement[1]` refers to a list of extra statements, the block we
|
119
|
+
wish to parse. Note here that when parsing objects like modules and classes,
|
120
|
+
we set the namespace for the duration of the block parsing by setting options
|
121
|
+
on the `parse_block` method.
|
122
|
+
|
123
|
+
### API Differences for Legacy Handler
|
124
|
+
|
125
|
+
Because the legacy handler uses the legacy parser and therefore a different kind
|
126
|
+
of AST, there are subtle differences in the handler API. Most importantly, the
|
127
|
+
`handles` method usually deals with either lexical tokens or source code as a string
|
128
|
+
or RegExp object. The statement object, similarly, is made up of lexical tokens instead
|
129
|
+
of semantically parsed nodes (this is described in the {file:docs/Parser.md parser document}).
|
130
|
+
|
131
|
+
The module example above can be rewritten as a legacy handler as follows:
|
132
|
+
|
133
|
+
class YARD::Handlers::Ruby::Legacy::ModuleHandler < YARD::Handlers::Ruby::Legacy::Base
|
134
|
+
handles TkMODULE
|
135
|
+
|
136
|
+
def process
|
137
|
+
modname = statement.tokens.to_s[/^module\s+(#{NAMESPACEMATCH})/, 1]
|
138
|
+
mod = register ModuleObject.new(namespace, modname)
|
139
|
+
parse_block(:namespace => mod)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
A few notes on the differences:
|
144
|
+
|
145
|
+
* We inherit from `Legacy::Base` instead of the standard Ruby Base handler class.
|
146
|
+
* We exchange node type `:module` for `TkMODULE`, which represents the
|
147
|
+
first token in the statement.
|
148
|
+
* We perform direct string manipulation to get the module name.
|
149
|
+
* `parse_block` does not take a list of statements. In the old parser API,
|
150
|
+
each statement has a `block` attribute which defines the list of
|
151
|
+
statements within that statement, if any. Therefore, `parse_block` will
|
152
|
+
always parse the `statement.block` if it exists.
|
data/docs/Overview.md
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# @title Architecture Overview
|
2
|
+
|
3
|
+
# Architecture Overview
|
4
|
+
|
5
|
+
YARD is separated in three major components, each of which allows YARD to be
|
6
|
+
extended for a separate purpose. The split also emphasizes YARD's design choice
|
7
|
+
to explicitly separate data gathering from HTML document generation, something
|
8
|
+
that tools like RDoc do not do. These components are:
|
9
|
+
|
10
|
+
* [Code Parsing & Processing Component](#parsing)
|
11
|
+
* [Data Storage Component](#storage)
|
12
|
+
* [Post Processing & Templating System](#templates)
|
13
|
+
|
14
|
+
This separation is a major goal of the project, and means that YARD is not *just*
|
15
|
+
a tool to generate HTML output. The expectation is that any subset of YARD's
|
16
|
+
major components may be used, extended or modified independently. YARD may be
|
17
|
+
used just as a data gathering tool (to parse and audit code), just as a data
|
18
|
+
source (a webserver containing raw unformatted data about code), or just as a
|
19
|
+
conventional HTML documentation generation tool (like RDoc).
|
20
|
+
|
21
|
+
The important classes and dependencies of these components are shown in the
|
22
|
+
following class diagram:
|
23
|
+
|
24
|
+
![Overview Class Diagram](images/overview-class-diagram.png)
|
25
|
+
|
26
|
+
<a name="parsing"></a>
|
27
|
+
|
28
|
+
## Code Parsing & Processing Component
|
29
|
+
|
30
|
+
This component is made up of four sub-components, each of which have separate
|
31
|
+
tasks during the data gathering process (*note: the tag architecture is not*
|
32
|
+
*shown in the class diagram*). These sub-components are:
|
33
|
+
|
34
|
+
* {file:docs/Parser.md}
|
35
|
+
* {file:docs/Handlers.md}
|
36
|
+
* {file:docs/CodeObjects.md}
|
37
|
+
* {file:docs/Tags.md}
|
38
|
+
|
39
|
+
The parser component reads source files and converts it into a set of statements
|
40
|
+
which the handlers then process, creating code objects which in turn create tags
|
41
|
+
(meta-data) attached to the objects. These objects are all added to the {YARD::Registry},
|
42
|
+
the data store component.
|
43
|
+
|
44
|
+
<a name="storage"></a>
|
45
|
+
|
46
|
+
## Data Storage Component
|
47
|
+
|
48
|
+
This component is currently implemented as a simple Ruby marshalled flat namespace
|
49
|
+
of object. The implementation is found in the single class {YARD::Registry}, which
|
50
|
+
is the centralized repository for all data being parsed, stored and accessed. There
|
51
|
+
are future plans to improve this storage mechanism to be backend agnostic and allow
|
52
|
+
for more robust storage.
|
53
|
+
|
54
|
+
<a name="templates"></a>
|
55
|
+
|
56
|
+
## Post Processing & Templating System
|
57
|
+
|
58
|
+
This component handles processing of objects from the registry through a templating
|
59
|
+
engine that allows output to a variety of formats. Practically speaking, this is
|
60
|
+
where templates can be implemented to change the design, output or structure of
|
61
|
+
the data. See {file:docs/Templates.md Templates Architecture} for a complete overview.
|
data/docs/Parser.md
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
# @title Parser Architecture
|
2
|
+
|
3
|
+
# Parser Architecture
|
4
|
+
|
5
|
+
The parser component of YARD is the first component in the data processing pipeline
|
6
|
+
that runs before any handling is done on the source. The parser is meant to translate
|
7
|
+
the source into a set of statements that can be understood by the {file:docs/Handlers.md Handlers}
|
8
|
+
that run immediately afterwards.
|
9
|
+
|
10
|
+
The important classes are described in the class diagram of the entire parser
|
11
|
+
system below:
|
12
|
+
|
13
|
+
![Parser Class Diagram](images/parser-class-diagram.png)
|
14
|
+
|
15
|
+
(Note: the RubyToken classes are omitted from the diagram)
|
16
|
+
|
17
|
+
## SourceParser
|
18
|
+
|
19
|
+
The main class {YARD::Parser::SourceParser} acts as a factory class, instantiating
|
20
|
+
the correct parser class, an implementation of {YARD::Parser::Base}. The selected parser
|
21
|
+
is chosen based on either the file extension or by selecting it explicitly (as an argument
|
22
|
+
to parsing methods). YARD supports Ruby and C source files, but custom parsers can
|
23
|
+
be implemented and registered for various other languages by subclassing `Parser::Base`
|
24
|
+
and registering the parser with {YARD::Parser::SourceParser.register_parser_type}.
|
25
|
+
|
26
|
+
This factory class should always be used when parsing source files rather than
|
27
|
+
the individual parser classes since it initiates the pipeline that runs the
|
28
|
+
handlers on the parsed source. The parser used must also match the handlers,
|
29
|
+
and this is coordinated by the `SourceParser` class as well.
|
30
|
+
|
31
|
+
## Using the SourceParser Class
|
32
|
+
|
33
|
+
The `SourceParser` class API is optimized for parsing globs of files. As such,
|
34
|
+
the main method to use the class is the `parse` class method, which takes an
|
35
|
+
array of file globs or a single file glob.
|
36
|
+
|
37
|
+
YARD::Parser::SourceParser.parse('spec_*.rb')
|
38
|
+
YARD::Parser::SourceParser.parse(['spec_*.rb', '*_helper.rb'])
|
39
|
+
|
40
|
+
This is equivalent to the convenience method {YARD.parse}:
|
41
|
+
|
42
|
+
YARD.parse('lib/**/*.rb')
|
43
|
+
|
44
|
+
In some cases (ie. for testing), it may be more helpful to parse a string of input
|
45
|
+
directly. In such a case, the method {YARD::Parser::SourceParser.parse_string} should be
|
46
|
+
used:
|
47
|
+
|
48
|
+
YARD::Parser::SourceParser.parse_string("def method(a, b) end")
|
49
|
+
|
50
|
+
You can also provide the parser type explicitly as the second argument:
|
51
|
+
|
52
|
+
# Parses a string of C
|
53
|
+
YARD::Parser::SourceParser.parse_string("int main() { }", :c)
|
54
|
+
|
55
|
+
Note that these two methods are aliased as {YARD.parse} and {YARD.parse_string} for
|
56
|
+
convenience.
|
57
|
+
|
58
|
+
## Implementing and Registering a Custom Parser
|
59
|
+
|
60
|
+
To implement a custom parser, subclass {YARD::Parser::Base}. Documentation on which
|
61
|
+
abstract methods should be implemented are documented in that class. After the class
|
62
|
+
is implemented, it is registered with the {YARD::Parser::SourceParser} factory class
|
63
|
+
to be called when a file of the right extension needs to be parsed, or when a user
|
64
|
+
selects that parser type explicitly. To register your new parser class, call the
|
65
|
+
method {YARD::Parser::SourceParser.register_parser_type}:
|
66
|
+
|
67
|
+
SourceParser.register_parser_type(:my_parser, MyParser, 'my_parser_ext')
|
68
|
+
|
69
|
+
The last argument can be a single extension, a list of extensions (Array), a single Regexp, or a
|
70
|
+
list of Regexps. Do not include the '.' in the extension.
|
71
|
+
|
72
|
+
|
73
|
+
## The Two Ruby Parser Types
|
74
|
+
|
75
|
+
When parsing Ruby, the SourceParser can either instantiate the new {YARD::Parser::Ruby::RubyParser}
|
76
|
+
class or the {YARD::Parser::Ruby::Legacy::StatementList} class. The first of the
|
77
|
+
two, although faster, more robust and more efficient, is only available for
|
78
|
+
Ruby 1.9. The legacy parser parser is available in both 1.8.x and 1.9, if
|
79
|
+
compatibility is required. The choice of parser will affect which handlers
|
80
|
+
ultimately get used, since new handlers can only use the new parser and the
|
81
|
+
same requirement applies to the legacy parser & handlers.
|
82
|
+
|
83
|
+
## Switching to Legacy Parser
|
84
|
+
|
85
|
+
By default, running YARD under Ruby 1.9 will automatically select the new parser
|
86
|
+
and new handlers by extension. Although YARD supports both handler styles, plugins
|
87
|
+
may choose to only implement one of the two (though this is not recommended). If
|
88
|
+
only the legacy handlers are implemented, the `SourceParser` class should force
|
89
|
+
the use of the legacy parser by setting the `parser_type` attribute as such:
|
90
|
+
|
91
|
+
YARD::Parser::SourceParser.parser_type = :ruby18
|
92
|
+
|
93
|
+
The default value is `:ruby`. Note that this cannot be forced the other way around,
|
94
|
+
a parser type of `:ruby` cannot be set under Ruby 1.8.x as the new parser is not
|
95
|
+
supported under 1.8.
|
96
|
+
|
97
|
+
## RubyParser (the New Parser)
|
98
|
+
|
99
|
+
The new Ruby parser uses the Ripper library that is packaged as part of stdlib
|
100
|
+
in Ruby 1.9. Because of this, it can generate an AST from a string of Ruby input
|
101
|
+
that is similar to the style of other sexp libraries (such as ParseTree). Each
|
102
|
+
node generated in the tree is of the base type {YARD::Parser::Ruby::AstNode},
|
103
|
+
which has some subclasses for common node types.
|
104
|
+
|
105
|
+
### AstNode Basics
|
106
|
+
|
107
|
+
The `AstNode` class behaves like a standard Array class in which all of its data
|
108
|
+
make up the list of elements in the array. Unlike other sexp style libraries, however,
|
109
|
+
the node type is not the first element of the list. Instead, the node type is defined
|
110
|
+
by the `#type` method. The following examples show some of the basic uses of `AstNode`:
|
111
|
+
|
112
|
+
# The sexp defines the statement `hello if 1`
|
113
|
+
node = s(:if_mod, s(:int, "1"), s(:var_ref, s(:ident, "hello")))
|
114
|
+
node.type #=> :if_mod
|
115
|
+
node[0] #=> s(:int, "1")
|
116
|
+
node[0][0] #=> "1"
|
117
|
+
|
118
|
+
(Note the `s()` syntax is shorthand for `AstNode.new(...)`. `s()` with no type
|
119
|
+
is shorthand for a node of type `:list`)
|
120
|
+
|
121
|
+
As shown, not all of the elements are AstNodes in themselves, some are String
|
122
|
+
objects containing values. A list of only the AstNodes within a node can be
|
123
|
+
accessed via the {YARD::Parser::Ruby::AstNode#children #children} method. Using
|
124
|
+
the sexp declared above, we can do:
|
125
|
+
|
126
|
+
node.children #=> [s(:int, "1"), s(:var_ref, s(:ident, "hello"))]
|
127
|
+
|
128
|
+
### AstNode#source and #line
|
129
|
+
|
130
|
+
Every node defines the `#source` method which returns the source code that the
|
131
|
+
node represents. One of the most common things to do with a node is to grab its
|
132
|
+
source. The following example shows how this can be done:
|
133
|
+
|
134
|
+
source = "if 1 == 1 then\n raise Exception\n end"
|
135
|
+
ast = YARD::Parser::Ruby::RubyParser.parse(source).root
|
136
|
+
ast[0].condition.source #=> "1 == 1"
|
137
|
+
ast[0].then_block.source #=> "raise Exception"
|
138
|
+
|
139
|
+
Note that this only works on source parsed from the RubyParser, not sexps
|
140
|
+
declared using the `s()` syntax. This is because no source code is generated
|
141
|
+
or stored by nodes. Instead, only the character ranges are stored, which are
|
142
|
+
then looked up in the original full source string object. For example:
|
143
|
+
|
144
|
+
# Following the code snippet above
|
145
|
+
ast[0].then_block.source_range #=> 17..31
|
146
|
+
|
147
|
+
We can also get the line and line ranges in a similar fashion:
|
148
|
+
|
149
|
+
ast[0].type #=> :if
|
150
|
+
ast[0].line #=> 1
|
151
|
+
ast[0].line_range #=> 1..3 (note the newlines in the source)
|
152
|
+
|
153
|
+
### AstNode#jump
|
154
|
+
|
155
|
+
Often the AST will be such that the node we care about might be buried arbitrarily
|
156
|
+
deep in a node's hierarchy. The {YARD::Parser::Ruby::AstNode#jump} method exists
|
157
|
+
to quickly get at a node of a specific type in such a situation:
|
158
|
+
|
159
|
+
# Get the first identifier in the statement
|
160
|
+
ast = s(s(:int, "1"), s(s(:var_ref, s(:ident, "hello"))))
|
161
|
+
ast.jump(:ident)[0] #=> "hello"
|
162
|
+
|
163
|
+
Multiple types can be searched for at once. If none are found, the original root
|
164
|
+
node is returned so that it may be chained.
|
165
|
+
|
166
|
+
## The Legacy Parser
|
167
|
+
|
168
|
+
The goal of the legacy parser is much the same as the new parser, but it is far
|
169
|
+
more simplistic. Instead of a full-blown AST, the legacy parser simply groups
|
170
|
+
together lists of "statements" called a {YARD::Parser::Ruby::Legacy::StatementList}.
|
171
|
+
These statement lists are made up of {YARD::Parser::Ruby::Legacy::Statement} objects.
|
172
|
+
A statement is any method call condition, loop, or declaration. Each statement
|
173
|
+
may or may not have a block. In the case of a condition or loop, the block is
|
174
|
+
the inner list of statements; in the case of a method call, the block is a do
|
175
|
+
block (if provided). The statements themselves are made up of tokens, so instead
|
176
|
+
of being semantic in nature like the new parser, statements are tied directly
|
177
|
+
to the lexical tokens that make them up. To convert a statement into source, you
|
178
|
+
simply join all the tokens together (this is done through the use of `#to_s`).
|
179
|
+
|
180
|
+
Note that because there is little semantic parsing, the legacy parser is less
|
181
|
+
able to deal with certain Ruby syntaxes. Specifically, the `:if_mod` syntax
|
182
|
+
seen above ("hello if 1") would be considered two statements with the new parser,
|
183
|
+
but using the legacy parser it is only one statement:
|
184
|
+
|
185
|
+
stmts = ARD::Parser::Ruby::Legacy::StatementList.new("hello if 1")
|
186
|
+
stmts[0].block #=> nil
|
187
|
+
stmts[0].tokens.to_s #=> "hello if 1"
|
188
|
+
|
189
|
+
In addition, this means that most handling still needs to be done via string
|
190
|
+
manipulation and regular expression matching, making it considerably more
|
191
|
+
difficult to use in edge case scenarios.
|