# * `nitdoc` wikilinks like `[[doc: MEntity::name]]`
module doc_commands
-# A command aimed at a documentation tool like `nitdoc` or `nitx`.
#
-# `DocCommand` are generally of the form `command: args`.
-interface DocCommand
+class DocCommandParser
- # Original command string.
- fun string: String is abstract
+ # List of allowed command names for this parser
+ var allowed_commands: Array[String] = [ "doc", "list", "param", "return",
+ "new", "call", "code", "graph"] is writable
- # Command name.
- fun name: String is abstract
-
- # Command arguments.
+ # Parse `string` as a DocCommand
#
- # FIXME: define a syntax
- fun args: Array[String] is abstract
-
- # Command factory.
+ # Returns `null` if the string cannot be parsed.
+ #
+ # ~~~
+ # var parser = new DocCommandParser
#
- # Returns a concrete instance of `DocCommand` depending on the string.
- new(command_string: String) do
- if command_string.has_prefix("doc:") then
- return new ArticleCommand(command_string)
- else if command_string.has_prefix("comment:") then
- return new CommentCommand(command_string)
- else if command_string.has_prefix("list:") then
- return new ListCommand(command_string)
- else if command_string.has_prefix("param:") then
- return new ParamCommand(command_string)
- else if command_string.has_prefix("return:") then
- return new ReturnCommand(command_string)
- else if command_string.has_prefix("new:") then
- return new NewCommand(command_string)
- else if command_string.has_prefix("call:") then
- return new CallCommand(command_string)
- else if command_string.has_prefix("code:") then
- return new CodeCommand(command_string)
+ # var command = parser.parse("doc: core::Array")
+ # assert command isa CommentCommand
+ # assert command.arg == "core::Array"
+ #
+ # command = parser.parse(":") # syntax error
+ # assert command == null
+ # assert parser.errors.not_empty
+ # ~~~
+ fun parse(string: String): nullable DocCommand do
+ var pos = 0
+ var tmp = new FlatBuffer
+ errors.clear
+
+ # Parse command name
+ pos = string.read_until(tmp, pos, ':')
+ var name = tmp.write_to_string.trim
+
+ # Check allowed commands
+ if name.is_empty then
+ error("empty command name", 0)
+ return null
+ end
+ if not allowed_commands.has(name) then
+ error("unknown command name", 0)
+ return null
end
- return new UnknownCommand(command_string)
+
+ # Build the command
+ var command = new_command(name, string)
+ if command == null then
+ error("unknown command name", 0)
+ return null
+ end
+
+ # Parse the argument
+ tmp.clear
+ pos = string.read_until(tmp, pos + 1, '|')
+ var arg = tmp.write_to_string.trim
+ if arg.is_empty then
+ error("empty command arg", pos)
+ return null
+ end
+ command.arg = arg
+
+ # Parse command options
+ while pos < string.length do
+ # Parse option name
+ tmp.clear
+ pos = string.read_until(tmp, pos + 1, ':', ',')
+ var oname = tmp.write_to_string.trim
+ var oval = ""
+ if oname.is_empty then break
+ # Parse option value
+ if pos < string.length and string[pos] == ':' then
+ tmp.clear
+ pos = string.read_until(tmp, pos + 1, ',')
+ oval = tmp.write_to_string.trim
+ end
+ command.opts[oname] = oval
+ # TODO Check options
+ end
+
+ return command
end
- redef fun to_s do return string
+ # Init a new DocCommand from its `name`
+ #
+ # You must redefine this method to add new custom commands.
+ fun new_command(name, string: String): nullable DocCommand do
+ if name == "doc" then return new CommentCommand(string)
+ if name == "list" then return new ListCommand(string)
+ if name == "param" then return new ParamCommand(string)
+ if name == "return" then return new ReturnCommand(string)
+ if name == "new" then return new NewCommand(string)
+ if name == "call" then return new CallCommand(string)
+ if name == "code" then return new CodeCommand(string)
+ if name == "graph" then return new GraphCommand(string)
+ return null
+ end
+
+ # Errors and warnings from last call to `parse`
+ var errors = new Array[DocMessage]
+
+ # Generate an error
+ fun error(message: String, col: nullable Int) do
+ errors.add new DocMessage(1, message, col)
+ end
+
+ # Generate a warning
+ fun warning(message: String, col: nullable Int) do
+ errors.add new DocMessage(2, message, col)
+ end
end
-# Used to factorize initialization of DocCommands.
-abstract class AbstractDocCommand
- super DocCommand
+# A message generated by the DocCommandParser
+class DocMessage
- redef var string
- redef var name is noinit
- redef var args = new Array[String]
+ # Message severity
+ #
+ # 1- Error
+ # 2- Warning
+ var level: Int
- init do
- # parse command
+ # Message explanatory string
+ var message: String
+
+ # Related column in original string if any
+ var col: nullable Int
+
+ redef fun to_s do
var str = new FlatBuffer
- var i = 0
- while i < string.length do
- var c = string[i]
- i += 1
- if c == ':' then break
- str.add c
+ if level == 1 then
+ str.append "Error: "
+ else
+ str.append "Warning: "
end
- name = str.write_to_string
- # parse args
- args.add string.substring_from(i).trim
+ str.append message
+ var col = self.col
+ if col != null then
+ str.append " (col: {col})"
+ end
+ return str.write_to_string
end
end
-# A `DocCommand` not recognized by documentation tools.
-#
-# Used to provide warnings or any other behavior for unexisting commands.
-class UnknownCommand
- super AbstractDocCommand
+redef class Text
+ # Read `self` as raw text until `nend` and append it to the `out` buffer.
+ private fun read_until(out: FlatBuffer, start: Int, nend: Char...): Int do
+ var pos = start
+ while pos < length do
+ var c = self[pos]
+ var end_reached = false
+ for n in nend do
+ if c == n then
+ end_reached = true
+ break
+ end
+ end
+ if end_reached then break
+ out.add c
+ pos += 1
+ end
+ return pos
+ end
end
-# A `DocCommand` that includes the documentation article of a `MEntity`.
+# A command aimed at a documentation tool like `nitdoc` or `nitx`.
#
-# Syntax: `doc: MEntity::name`.
-class ArticleCommand
- super AbstractDocCommand
+# `DocCommand` are generally of the form `command: arg | opt1: val1, opt2: val2`.
+abstract class DocCommand
+
+ # Original command string.
+ var string: String
+
+ # Command name.
+ var name: String is noinit
+
+ # Command arguments.
+ var arg: String is noinit, writable
+
+ # Command options.
+ var opts = new HashMap[String, String] is writable
+
+ redef fun to_s do
+ if opts.is_empty then
+ return "{name}: {arg}"
+ end
+ return "{name}: {arg} | {opts.join(", ", ": ")}"
+ end
end
-# A `DocCommand` that includes the MDoc of a `MEntity`.
+# A `DocCommand` that includes the documentation article of a `MEntity`.
#
-# Syntax: `comment: MEntity::name`.
+# Syntax: `doc: MEntity::name`.
class CommentCommand
- super AbstractDocCommand
+ super DocCommand
+
+ redef var name = "doc"
end
# A `DocCommand` that includes a list of something.
#
# Syntax: `list:kind: <arg>`.
class ListCommand
- super AbstractDocCommand
+ super DocCommand
+
+ redef var name = "list"
end
# A `DocCommand` that includes the list of methods tanking a `MType` as parameter.
#
# Syntax: `param: MType`.
class ParamCommand
- super AbstractDocCommand
+ super DocCommand
+
+ redef var name = "param"
end
# A `DocCommand` that includes the list of methods returning a `MType` as parameter.
#
-# Syntax: `param: MType`.
+# Syntax: `return: MType`.
class ReturnCommand
- super AbstractDocCommand
+ super DocCommand
+
+ redef var name = "return"
end
# A `DocCommand` that includes the list of methods creating new instances of a specific `MType`
#
# Syntax: `new: MType`.
class NewCommand
- super AbstractDocCommand
+ super DocCommand
+
+ redef var name = "new"
end
# A `DocCommand` that includes the list of methods calling a specific `MProperty`.
#
# Syntax: `call: MEntity::name`.
class CallCommand
- super AbstractDocCommand
+ super DocCommand
+
+ redef var name = "call"
end
# A `DocCommand` that includes the source code of a `MEntity`.
# * `./src/file.nit` to include source code from a file.
# * `./src/file.nit:1,2--3,4` to select code between positions.
class CodeCommand
- super AbstractDocCommand
+ super DocCommand
+
+ redef var name = "code"
+end
+
+# A `DocCommand` that display an graph for a `MEntity`.
+#
+# Syntax:
+# * `graph: MEntity::name`
+class GraphCommand
+ super DocCommand
+
+ redef var name = "graph"
end