AWKDoc
Generates markdown documentation from comment annotations in [ba]sh source files.
Table of Contents
Usage
Write bash, add comments with annotation flags.
Run awkdoc to turn annotations → markdown documentation.
Brief example
input.sh
#!/usr/bin/env bash #--------------------------------------------------------- # @section Arithmetic functions # @description Some useless functions that complicate # addition and subtraction in bash. #--------------------------------------------------------- # @type declare -i RESULT= # @description # Adds a number to the global RESULT. # # @set RESULT # @arg $1 :int Number to add to the $RESULT function add { (( RESULT = RESULT + $1 )) } # @description # Subtracts a number from the global RESULT. # # @set RESULT # @arg $1 :int Number to subtract from the $RESULT function sub { (( RESULT = RESULT - $1 )) }
out.md
# Contents. - [Arithmetic functions](#arithmetic-functions) - [`add`](#add) - [`sub`](#sub) # Types. |type|line|file| |:---|:---|:---| |`RESULT`|`9`|*input.sh*| # Functions. ## Arithmetic functions Some useless functions that complicate addition and subtraction in bash. ### add() (*ln. 17, in input.sh*) Adds a number to the global RESULT. #### args |name|type|desc| |:---|:---|:---| |`$1`|`:int`|Number to add to the $RESULT| ### sub() (*ln. 27, in input.sh*) Subtracts a number from the global RESULT. #### args |name|type|desc| |:---|:---|:---| |`$1`|`:int`|Number to subtract from the $RESULT| <br/><br/> :: *generated by @carlinigraphy/awkdoc* ::
Generate documentation
Run awkdoc.
(Optionally) use pandoc to convert to .html
pandoc -s out.md -o out.html --metadata title="documentation"Note: awkdoc does not include markdown metadata to be more generally applicable.
The --metadata flag can fill some gaps.
Features
Things that make awkdoc nifty.
NOTE: Instead of random examples, all the snippets below come from @carlinigraphy/conflang. Helps me to understand when looking at real life use cases.
Error synchronization
Won’t fail on the first error encountered. Collects all possible errors & info messages before printing output.
Error messaging
Includes the source file name, line number, and original line itself in error output. Easier to see where problems occurred.
Example
==> INFO: Unknown @-flag: asd
in test/testfile.sh
ln. 6: # @asd
==> ERROR: Missing section title
in test/testfile.sh
ln. 26: # @sectionMultiline descriptions
Description text is dedented to the level of the first line, allowing for more flexible comment styles.
Variables set/used
Generates a list of variables specified by @env or @set, with sub-items for the function that set them. Useful when debugging, or reasoning about the whole program.
Example
# Variables referenced. - `LOCATION` - [`token:new`](#tokennew) - `CURSOR` - [`location:cursor`](#locationcursor) - [`lexer:advance`](#lexeradvance) - `TOKEN` - [`parser:declaration`](#parserdeclaration) - [`parser:typedef`](#parsertypedef) - [`parser:type`](#parsertype) # Variables set. - `LOCATION` - [`location:cursor`](#locationcursor) - `CHAR` - [`lexer:advance`](#lexeradvance) - `TOKENS[]` - [`token:new`](#tokennew) - `NODE` - [`parser:declaration`](#parserdeclaration) - [`parser:program`](#parserprogram) - [`parser:typedef`](#parsertypedef)
Flags
Annotation flags must occur…
-
attached to a function declaration (
@arg,@set,@env,@internal) -
attached to a variable declaration (
@type) -
attached to a function/section annotation (
@description) -
anywhere (
@section)
arg
Specifies an argument, with optional type and one-line description.
Types are indicated by a : prefix.
An anchor to the Types heading is created when the parameter type matches a typedef.
Example
# @description # Copies the properties from $1's location node to $2's. If no properties are # specified, copies all of them. May only operate on TOKENs and NODEs. # # @arg $1 :NODE Source location-containing node # @arg $2 :NODE Destination location-containing node function location:copy {
Output
### location:copy() (*ln. 8, in input.sh*) Copies the properties from $1's location node to $2's. If no properties are specified, copies all of them. May only operate on TOKENs and NODEs. #### args |name|type|desc| |:---|:---|:---| |`$1`|`NODE`|Source location-containing node| |`$2`|`NODE`|Destination location-containing node|
env
Indicates the function references an environment/global variable.
Example
# @description # Convenience function to create a location at the current cursor's position. # Cleans up otherwise messy and repetitive code in the lexer. # # @set LOCATION # @env FILE_IDX # @env CURSOR # # @noargs function location:cursor {
Output
# Variables referenced. - `CURSOR` - [`location:cursor`](#locationcursor) - `FILE_IDX` - [`location:cursor`](#locationcursor) # Functions. ### location:cursor() (*ln. 11, in input.sh*) Convenience function to create a location at the current cursor's position. Cleans up otherwise messy and repetitive code in the lexer. #### uses - `FILE_IDX` - `CURSOR`
set
Indicates the function sets a global variable.
Example
# @description # Throws error on circular imports, resolves relative paths to fully qualified # path. # # @set FILES[] # @set FILE_IDX # @arg $1 :str Relative or absolute path to config file # @arg $2 :LOCATION [Optional] For error reporting import statements function utils:add_file {
Output
# Variables set. - `FILE_IDX` - [`utils:add_file`](#utilsadd_file) - `FILES[]` - [`utils:add_file`](#utilsadd_file) # Functions. ### utils:add_file() (*ln. 10, in input.sh*) Throws error on circular imports, resolves relative paths to fully qualified path. #### args |name|type|desc| |:---|:---|:---| |`$1`|`str`|Relative or absolute path to config file| |`$2`|`LOCATION`|[Optional] For error reporting import statements| #### set - `FILES[]` - `FILE_IDX`
see
Creates an anchor to another declared function.
Example
# @description # Identifies and calls `utils:parse` on all import statements. # # @see utils:parse # @arg $1 :NODE Root AST node for a file function imports:parse {
Output
### imports:parse() (*ln. 7, in input.sh*) Identifies and calls `utils:parse` on all import statements. #### args |name|type|desc| |:---|:---|:---| |`$1`|`NODE`|Root AST node for a file| #### see - [`utils:parse`](#utilsparse)
internal
Ignores this function definition in generated output. Useful for library functions you still wish to document.
Example
# @internal # @description # Holdover until I wire up synchronization function. Called by parser:advance() # to advance current global Token and nameref pointers. # # @see parser:advance # # @env TOKENS # @env IDX # @set TOKEN # @set TOKEN_r # @noargs function parser:_advance {
description
May be attached to either a function definition or a sections’s annotations to provide more information.
Descriptions may be multiline, and text is dedented to the position of the first
text-containing line after the @description flag.
See plenty of examples above.
section
Creates a higher level heading in the TOC, and the markdown body. Useful for indicating the following functions are all related.
Example
#=============================================================================== # @section Utils # @description # All of the utilities that tie together functionality from the lexer, parser, # and compiler. Allows re-entering the parser for each included file, and # concatenating (not literally, but in spirit) imported files. #-------------------------------------------------------------------------------
Output
# Contents. - [Utils](#utils) # Functions. ## Utils All of the utilities that tie together functionality from the lexer, parser, and compiler. Allows re-entering the parser for each included file, and concatenating (not literally, but in spirit) imported files.
The same dedentation rules apply as in the description.
type
Indicates the following variable declaration is a "type". Adds to a list in generated output, with reference to its line number. Useful if later annotating a function’s arguments.
Example
# @type declare -g LOCATION= # @description # Throws error on circular imports, resolves relative paths to fully qualified # path. # # @set FILES[] # @set FILE_IDX # @arg $1 :str Relative or absolute path to config file # @arg $2 :LOCATION [Optional] For error reporting import statements function utils:add_file {
Output
# Types. |type|line|file| |:---|:---|:---| |`LOCATION`|`2`|*input.sh*| # Functions. ### utils:add_file() (*ln. 13, in input.sh*) Throws error on circular imports, resolves relative paths to fully qualified path. #### args |name|type|desc| |:---|:---|:---| |`$1`|`str`|Relative or absolute path to config file| |`$2`|[`LOCATION`](#types)|[Optional] For error reporting import statements| #### set - `FILES[]` - `FILE_IDX`
Known limitations
Annotation placement
Comments with annotations must occur directly before function definitions. They may not be placed inside the function’s body, or after it.
This works.
# @arg $1 Adds one to this number function add_one { echo $(( $1 + 1 )) ;}
These do not.
# @arg $1 Adds two to this number function add_two { echo $(( $1 + 2 )) ;} function add_three { # @arg $1 Adds three to this number echo $(( $1 + 3 )) }
Markdown anchors
It is currently possible to have an ambiguous anchor reference. I don’t know how to make markdown anchors more specific.
Links to typedefs
Source files are parsed linearly. If there is a defined typedef for a function’s argument type, an anchor is created.
If the typedef is declared after the function, there is no link. There is no backtracking.
Example
No link will be created for the function’s 1st parameter type, as it’s declared after the function itself.
# @arg $1 :IP_ADDRESS function ping_ip_addr { :; } # @type declare -g IP_ADDRESS
One will need to change the structure of their .sh files, or the order the files are sourced, so type declarations always precede their use.
Or don’t, and some links may not exist. It’s not a big deal.
Recognition
Obvious inspiration, and some outright function theft, from shdoc.
I wanted to improve on a few edge cases, largely surrounding handling leading whitespace.
Use shdoc, it is better and more robust than awkdoc.