diff --git a/.gitmodules b/.gitmodules index 1a1a6cc..1434b3e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "ext/strings"] path = ext/strings url = https://github.com/dylan-lang/strings +[submodule "documentation/sphinx-extensions"] + path = documentation/sphinx-extensions + url = https://github.com/dylan-lang/sphinx-extensions diff --git a/documentation/Makefile b/documentation/Makefile new file mode 100644 index 0000000..d0c3cbf --- /dev/null +++ b/documentation/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/documentation/make.bat b/documentation/make.bat new file mode 100644 index 0000000..747ffb7 --- /dev/null +++ b/documentation/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/documentation/source/conf.py b/documentation/source/conf.py new file mode 100644 index 0000000..b5459b3 --- /dev/null +++ b/documentation/source/conf.py @@ -0,0 +1,78 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. + +import os +import sys + +sys.path.insert(0, os.path.abspath('../sphinx-extensions/sphinxcontrib')) + +import dylan.domain +import dylan.themes as dylan_themes + + + +# -- Project information ----------------------------------------------------- + +project = 'command-line-parser' +copyright = '2022, Dylan Hackers' +author = 'Dylan Hackers' + +# The full version, including alpha/beta/rc tags +release = 'v3.2.0' + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'dylan.domain', + 'sphinx.ext.intersphinx' +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build'] + +# This makes it so that each document doesn't have to use +# .. default-domain:: dylan +# but they probably should anyway, so that they can be built separately +# without depending on this top-level config file. +primary_domain = 'dylan' + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = dylan_themes.get_html_theme_default() + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +html_theme_options = dylan_themes.get_html_theme_options_default() + +# Add any paths that contain custom themes here, relative to this directory. +html_theme_path = [dylan_themes.get_html_theme_path()] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +html_title = "Dylan Library Reference" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] diff --git a/documentation/source/index.rst b/documentation/source/index.rst new file mode 100644 index 0000000..8f3544e --- /dev/null +++ b/documentation/source/index.rst @@ -0,0 +1,723 @@ +******************************* +The command-line-parser Library +******************************* + +.. current-library:: command-line-parser +.. current-module:: command-line-parser + +.. contents:: Contents + :local: + + +The *command-line-parser* library provides a facility to parse the +command line. It exports two modules: + +* *command-line-parser* - Main API module +* *option-parser-protocol* - For extending the API + + +Overview +======== + +Here's a quick list of features to get a sense of what's available: + +* Various option types + + * :class:`` for simple ``--boolean`` flags. + * :class:`` for ``--foo=bar`` options. + * :class:`` options may be repeated: ``-r a -r b`` + * :class:`` in which the value is optional: + ``--foo`` or ``--foo bar`` + * :class:`` fills a table with key/value pairs, for example: + ``-Dk1=v1 -Dk2=v2`` + * :class:`` the value may be chosen from a predefined set. + * :class:`` to receive positional arguments. + +* Automatic and extensible conversion of arguments to other types. For example, + an option with ``type: `` is automatically converted to integer by + the parser. +* Subcommands +* Automatic usage and ``--help`` generation. +* A convenient and readable syntax for creating the command line. +* Extensibility. Add your own argument converters with :gf:`parse-option-value` + or create entirely new option types. + + +Quick Start +=========== + +The best way to build a command line parser is with +:macro:`command-line-definer`, since it is far more concise and readable than +building one "by hand" with ``make(, options: ...)``. It +also provides convenient methods to retrieve command-line option values rather +than accessing them by name, with strings. + +.. note:: :macro:`command-line-definer` does not currently support subcommands. + If you need subcommands you will have to build the parser by hand for + now. + +Let's say you want to parse a command line that looks like this:: + + frob --name=zoo --debug -r a -r b -r c --choice=foo one two three + +The "frob" command accepts a ``--name`` option that takes a value, a boolean +``--debug`` (or ``--nodebug``) a repeatable ``-r`` option, a ``--choice`` +option that accepts one of several values, and then at least one positional +argument (here "one", "two", and "three"). Here's what that parser looks like: + +.. code-block:: dylan + + define command-line () + option frob-name :: , + names: #("name"), + help: "Name of the frob", + kind: ; + option frob-debug? :: , + names: #("debug"), + negative-names: #("nodebug"), + help: "Enable or disable debugging", + kind: ; // This is the default. + option frob-radicals :: , + names: #("r"), + kind: , + variable: "RAD", // Makes --help show "-r RAD" + help: "Free radicals"; + option frob-choice :: , + names: #("choice"), + choices: #("foo", "bar", "baz"), + default: "foo", + help: "Your choice"; + option frob-filenames :: , + names: #("filenames"), + kind: , + repeated?: #t, + help: "One or more filenames"; + end command-line; + +Now parse the command line: + +.. code-block:: dylan + + block () + let cmd = make(, help: "frob things"); + parse-command-line(cmd, application-arguments()); + // Now execute your main program code with cmd containing + // the parsed argument values. + frob(cmd); + exception (err :: ) + // This condition is signaled by parse-command-line and also if + // your own code calls abort-command(). + format-err("%s", err); + exit-application(err.exit-status); + end; + +To access the option values simply read the ```` slot +values. Assuming ``cmd`` is the command parsed above: + +.. code-block:: dylan + + for (file in cmd.frob-filenames) + if (cmd.frob-debug?) + format-out(...); + end; + ...more... + end; + +Of course, it is also possible to make a command line parser without the macro +above, but doing so is much more verbose and requires accessing the option +values by calling ``get-option-value(cmd, "option-name")``. Briefly, just call +:drm:`make`, like this: + +.. code-block:: dylan + + let cmd + = make(, + help: "a most excellent program", + options: list(make(, + names: #("name"), + help: "provide a name"), + ..., + make(, + names: #("filenames"), + repeated?: #t, + help: "one or more filenames")), + subcommands: list(make(, ...))); + parse-command-line(cmd, application-arguments()); + let filenames = get-option-value(cmd, "filenames"); + ...etc... + + +Reference +========= + + +The command-line-parser Module +------------------------------ + +.. class:: + :abstract: + :sealed: + + Abstract superclass of :class:`` and + :class:``. + + :keyword options: + + A sequence of :class:`