Skip to content

Shell documentation generation from comment annotations.

License

Notifications You must be signed in to change notification settings

carlinigraphy/awkdoc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

45 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AWKDoc

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.

./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.

Environment variables

Some behavior can be modified by environment variables.

Variable Acceptable values Description

AWKDOC_NO_COLOR

["", "yes"]

Any non-empty value will disable color in error output

AWKDOC_LOG_LEVEL

[-1, 0, 1, 2]

Higher number, more verbose. -1 disables error reporting entirely

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: # @section

Multiline 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…​

  1. attached to a function declaration (@arg, @set, @env, @internal)

  2. attached to a variable declaration (@type)

  3. attached to a function/section annotation (@description)

  4. 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.

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.