1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # A parser that create DocCommand from a string
17 # Used by both Nitx and the Markdown doc commands.
18 module commands_parser
20 import commands
::commands_model
21 import commands
::commands_graph
22 import commands
::commands_usage
23 import commands
::commands_catalog
25 # Parse string commands to create DocQueries
28 # ModelView used to retrieve mentities
31 # ModelBuilder used to retrieve AST nodes
32 var modelbuilder
: ModelBuilder
34 # Catalog used for catalog commands
35 var catalog
: nullable Catalog
37 # List of allowed command names for this parser
38 var allowed_commands
: Array[String] = [
39 "link", "doc", "code", "lin", "uml", "graph", "search",
40 "parents", "ancestors", "children", "descendants",
41 "param", "return", "new", "call", "defs", "list", "random",
42 "catalog", "stats", "tags", "tag", "person", "contrib", "maintain"] is writable
44 # List of commands usage and documentation
45 var commands_usage
: Map[String, String] do
46 var usage
= new ArrayMap[String, String]
47 usage
["search: <string>"] = "list entities matching `string`"
48 usage
["link: <name>"] = "display the link to `name`"
49 usage
["doc: <name>"] = "display the documentation for `name`"
50 usage
["defs: <name>"] = "list all definitions for `name`"
51 usage
["code: <name>"] = "display the code for `name`"
52 usage
["lin: <name>"] = "display the linearization for `name`"
53 usage
["uml: <name>"] = "display the UML diagram for `name`"
54 usage
["graph: <name>"] = "display the inheritance graph for `name`"
55 usage
["parents: <name>"] = "list the direct parents of `name`"
56 usage
["ancestors: <name>"] = "list all ancestors of `name`"
57 usage
["children: <name>"] = "list direct children of `name`"
58 usage
["descendants: <name>"] = "list all descendants of `name`"
59 usage
["param: <type>"] = "list all methods accepting `type` as parameter"
60 usage
["return: <type>"] = "list all methods returning `type`"
61 usage
["new: <class>"] = "list all methods initializing `class`"
62 usage
["call: <property>"] = "list all methods calling `property`"
63 usage
["list: <kind>"] = "list all entities of `kind` from the model"
64 usage
["random: <kind>"] = "list random entities of `kind` from the model"
65 usage
["catalog:"] = "list packages from catalog"
66 usage
["stats:"] = "display catalog statistics"
67 usage
["tags:"] = "list all tabs from catalog"
68 usage
["tag: <tag>"] = "list all packages with `tag`"
69 usage
["maintain: <person>"] = "list all packages maintained by `person`"
70 usage
["contrib: <person>"] = "list all packages contributed by `person`"
74 # Parse `string` as a DocCommand
76 # Returns `null` if the string cannot be parsed.
77 # See `error` for the error messages produced by both the parser and the commands.
78 fun parse
(string
: String): nullable DocCommand do
80 var tmp
= new FlatBuffer
84 pos
= string
.read_until
(tmp
, pos
, ':', '|')
85 var name
= tmp
.write_to_string
.trim
87 # Check allowed commands
89 error
= new CmdParserError("Empty command name", 0)
92 # If the command name contains two consecutive colons or there is no colon in the name,
93 # we certainly have a wiki link to a mentity
94 var is_short_link
= false
95 if (pos
< string
.length
- 2 and string
[pos
] == ':' and string
[pos
+ 1] == ':') or
96 pos
== string
.length
then
98 else if pos
< string
.length
- 1 and string
[pos
] == '|' then
101 else if not allowed_commands
.has
(name
) then
102 error
= new CmdParserError("Unknown command name `{name}`", 0)
108 pos
= string
.read_until
(tmp
, pos
+ 1, '|')
109 var arg
= tmp
.write_to_string
.trim
110 if is_short_link
and not arg
.is_empty
then
112 else if is_short_link
then
116 # Parse command options
117 var opts
= new HashMap[String, String]
118 while pos
< string
.length
do
121 pos
= string
.read_until
(tmp
, pos
+ 1, ':', ',')
122 var oname
= tmp
.write_to_string
.trim
124 if oname
.is_empty
then break
126 if pos
< string
.length
and string
[pos
] == ':' then
128 pos
= string
.read_until
(tmp
, pos
+ 1, ',')
129 oval
= tmp
.write_to_string
.trim
136 if is_short_link
then
137 command
= new CmdEntityLink(view
)
139 command
= new_command
(name
)
141 if command
== null then
142 error
= new CmdParserError("Unknown command name `{name}`", 0)
146 # Initialize command from string options
147 var status
= command
.parser_init
(arg
, opts
)
148 if not status
isa CmdSuccess then error
= status
153 # Init a new DocCommand from its `name`
155 # You must redefine this method to add new custom commands.
156 fun new_command
(name
: String): nullable DocCommand do
158 if name
== "link" then return new CmdEntityLink(view
)
159 if name
== "doc" then return new CmdComment(view
)
160 if name
== "code" then return new CmdEntityCode(view
, modelbuilder
)
161 if name
== "lin" then return new CmdLinearization(view
)
162 if name
== "defs" then return new CmdFeatures(view
)
163 if name
== "parents" then return new CmdParents(view
)
164 if name
== "ancestors" then return new CmdAncestors(view
)
165 if name
== "children" then return new CmdChildren(view
)
166 if name
== "descendants" then return new CmdDescendants(view
)
167 if name
== "param" then return new CmdParam(view
)
168 if name
== "return" then return new CmdReturn(view
)
169 if name
== "new" then return new CmdNew(view
, modelbuilder
)
170 if name
== "call" then return new CmdCall(view
, modelbuilder
)
172 if name
== "uml" then return new CmdUML(view
)
173 if name
== "graph" then return new CmdInheritanceGraph(view
)
175 if name
== "list" then return new CmdModelEntities(view
)
176 if name
== "random" then return new CmdRandomEntities(view
)
178 var catalog
= self.catalog
179 if catalog
!= null then
180 if name
== "catalog" then return new CmdCatalogPackages(view
, catalog
)
181 if name
== "stats" then return new CmdCatalogStats(view
, catalog
)
182 if name
== "tags" then return new CmdCatalogTags(view
, catalog
)
183 if name
== "tag" then return new CmdCatalogTag(view
, catalog
)
184 if name
== "person" then return new CmdCatalogPerson(view
, catalog
)
185 if name
== "contrib" then return new CmdCatalogContributing(view
, catalog
)
186 if name
== "maintain" then return new CmdCatalogMaintaining(view
, catalog
)
187 if name
== "search" then return new CmdCatalogSearch(view
, catalog
)
189 if name
== "search" then return new CmdSearch(view
)
194 # Error or warning from last call to `parse`
195 var error
: nullable CmdMessage = null
198 # An error produced by the CmdParser
205 # Column related to the error
206 var column
: nullable Int
208 redef fun to_s
do return message
211 redef class DocCommand
213 # Initialize the command from the CommandParser data
214 fun parser_init
(arg
: String, options
: Map[String, String]): CmdMessage do
219 redef class CmdEntity
220 redef fun parser_init
(mentity_name
, options
) do
221 self.mentity_name
= mentity_name
227 redef fun parser_init
(mentity_name
, options
) do
228 if options
.has_key
("limit") and options
["limit"].is_int
then limit
= options
["limit"].to_i
235 redef class CmdComment
236 redef fun parser_init
(mentity_name
, options
) do
237 full_doc
= not options
.has_key
("only-synopsis")
238 fallback
= not options
.has_key
("no-fallback")
239 if options
.has_key
("format") then format
= options
["format"]
244 redef class CmdEntityLink
245 redef fun parser_init
(mentity_name
, options
) do
246 if options
.has_key
("text") then text
= options
["text"]
247 if options
.has_key
("title") then title
= options
["title"]
253 redef fun parser_init
(mentity_name
, options
) do
254 if options
.has_key
("format") then format
= options
["format"]
259 redef class CmdSearch
260 redef fun parser_init
(mentity_name
, options
) do
262 if options
.has_key
("page") and options
["page"].is_int
then page
= options
["page"].to_i
267 redef class CmdAncestors
268 redef fun parser_init
(mentity_name
, options
) do
269 if options
.has_key
("parents") and options
["parents"] == "false" then parents
= false
274 redef class CmdDescendants
275 redef fun parser_init
(mentity_name
, options
) do
276 if options
.has_key
("children") and options
["children"] == "false" then children
= false
281 redef class CmdModelEntities
282 redef fun parser_init
(kind
, options
) do
289 redef fun parser_init
(mentity_name
, options
) do
290 if options
.has_key
("format") then format
= options
["format"]
295 redef class CmdInheritanceGraph
296 redef fun parser_init
(mentity_name
, options
) do
297 if options
.has_key
("pdepth") and options
["pdepth"].is_int
then
298 pdepth
= options
["pdepth"].to_i
300 if options
.has_key
("cdepth") and options
["cdepth"].is_int
then
301 cdepth
= options
["cdepth"].to_i
309 redef class CmdCatalogTag
310 redef fun parser_init
(mentity_name
, options
) do
316 redef class CmdCatalogPerson
317 redef fun parser_init
(mentity_name
, options
) do
318 person_name
= mentity_name
326 # Read `self` as raw text until `nend` and append it to the `out` buffer.
327 private fun read_until
(out
: FlatBuffer, start
: Int, nend
: Char...): Int do
329 while pos
< length
do
331 var end_reached
= false
338 if end_reached
then break