X-Git-Url: http://nitlanguage.org diff --git a/src/doc/doc_commands.nit b/src/doc/doc_commands.nit index e69bb38..208738b 100644 --- a/src/doc/doc_commands.nit +++ b/src/doc/doc_commands.nit @@ -19,129 +19,243 @@ # * `nitdoc` wikilinks like `[[doc: MEntity::name]]` module doc_commands -import doc_base - -# 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 - - # Command name. - fun name: 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 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 + # + # var command = parser.parse("doc: core::Array") + # assert command isa CommentCommand + # assert command.arg == "core::Array" # - # 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) + # 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 + + # 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 new UnknownCommand(command_string) + + 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 + + # Message explanatory string + var message: String - init do - # parse command + # 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 + str.append message + var col = self.col + if col != null then + str.append " (col: {col})" end - name = str.write_to_string - # parse args - args.add string.substring_from(i).trim + 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: `. 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`. @@ -151,5 +265,17 @@ end # * `./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