Generates markdown documentation from comment annotations in [ba]sh source files.
Write bash, add comments with annotation flags.
Run awkdoc
to turn annotations → markdown documentation.
#!/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* ::
Run awkdoc
.
./awkdoc *.sh > out.md
(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.
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.
Won’t fail on the first error encountered. Collects all possible errors & info messages before printing output.
Includes the source file name, line number, and original line itself in error output. Easier to see where problems occurred.
==> INFO: Unknown @-flag: asd
in test/testfile.sh
ln. 6: # @asd
==> ERROR: Missing section title
in test/testfile.sh
ln. 26: # @section
Description text is dedented to the level of the first line, allowing for more flexible comment styles.
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.
# 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)
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
)
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.
# @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 {
### 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|
Indicates the function references an environment/global variable.
# @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 {
# 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`
Indicates the function sets a global variable.
# @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 {
# 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`
Creates an anchor to another declared function.
# @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 {
### 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)
Ignores this function definition in generated output. Useful for library functions you still wish to document.
# @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 {
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.
Creates a higher level heading in the TOC, and the markdown body. Useful for indicating the following functions are all related.
#===============================================================================
# @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.
#-------------------------------------------------------------------------------
# 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.
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.
# @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 {
# 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`
Comments with annotations must occur directly before function definitions. They may not be placed inside the function’s body, or after it.
# @arg $1 Adds one to this number
function add_one { echo $(( $1 + 1 )) ;}
# @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 ))
}
It is currently possible to have an ambiguous anchor reference. I don’t know how to make markdown anchors more specific.
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.
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.
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
.