Nagelfar allows you to set up a plugin that can hook up and affect the
checks in different stages.

A plugin can be used for things like:
* Enforce local rules
* Handle checks for custom commands that cannot be handled within Nagelfar's syntax tokens
* Annotate or ignore constructs in e.g. legacy code that cannot be changed
* Collect statistics, e.g. a call graph

See further examples below.

<ul>Generic rules</ul>

A plugin is a Tcl script file that must start with the verbatim sequence
"##Nagelfar Plugin :". A plugin is sourced and used in its own safe
interpreter and thus have free access to its own global space. Hookup
points are defined by declaring specifically named procedures as specified
below, and apart from those, a plugin can define and do whatever within
the limits of a safe interpreter.

In addition to the standard safe interpreter environment, a plugin has
access to stdout as well.

Note that backslash-newline is always removed at an early stage in Nagelfar,
so when hooks receive "unparsed" data, those have been removed.

<ul>Result of plugin procedures</ul>

Each hookup procedure returns a list with an even number of elements.
These are interpreted as keyword-value pairs, with the following keywords
allowed.

replace : The value is used to replace the incoming value for further processing.
comment : The value is fed through inline comment parsing to affect surroundings.
error   : The value produces an error message.
warning : The value produces a warning message.
note    : The value produces a note message.

To do nothing, return an empty list.

<ul>Finalizing checking</ul>

proc finalizePlugin {} { }

If this procedure is declared, it is called at the end
of checking.

The return value from finalizePlugin may only contain messages.

<ul>Information dictionary</ul>

Each hook procedure receives an information dictionary as one argument.
It currently has at least these elements:

namespace : Current namespace
caller    : Current procedure
file      : Current file

<ul>Raw Statement Hook</ul>

proc statementRaw {stmt info} { }

If declared, this receives each statement unparsed.

<ul>Statement Words Hook</ul>

proc statementWords {words info} { }

If declared, this receives each statement split into a list of words but
otherwise unprocessed/unsubstituted. Things like quotes and braces are left
in the words.

Many checks can be done in a simple way here since you have direct access to
the command word and the number of arguments.

<ul>Raw Expression Hook</ul>

proc earlyExpr {exp info} { }

If declared, this receives any expression unparsed.

<ul>Late Expression Hook</ul>

proc lateExpr {exp info} { }

If declared, this receives any expression after all variable or
command substitutions have been replaced by "${_____}". It is still
basically the same expression and this allows a handler that
knows fewer syntax rules.

<ul>Examples</ul>

Here are some examples. Some are somewhat silly from a practical point
of view but hopefully informative.

##Nagelfar Plugin : Ignore mugg command
proc statementRaw {stmt info} {
    set res {}
    if {[string match "mugg *" $stmt]} {
        lappend res replace {}
    }
    return $res
}

##Nagelfar Plugin : Handle known side effect
proc statementWords {words info} {
    set res {}
    # The command "mugg" sets a variable in the caller
    if {[lindex $words 0] eq "mugg"} {
        lappend res comment
        lappend res "##nagelfar variable gurka"
    }
    return $res
}

##Nagelfar Plugin : Forbid operator
proc lateExpr {exp info} {
    if {[string match "* eq *" $exp]} {
        return [list error "Operator \"eq\" is forbidden here"]
    }
    return {}
}

##Nagelfar Plugin : Allow custom operator
proc lateExpr {exp info} {
    # Just replace it with something further processing recognizes
    set exp [string map {{ my_cool_bin_op } { eq }} $exp]
    return [list replace $exp]
}

##Nagelfar Plugin : Create a call graph
proc statementWords {words info} {
    set caller [dict get $info caller]
    set callee [lindex $words 0]
    if {$caller ne "" && $callee ne ""} {
        array set ::callGraph [list "$caller -> $callee" 1]
    }
    return
}
proc finalizePlugin {} {
    foreach item [lsort -dictionary [array names ::callGraph]] {
        puts "Call: $item"
    }
    return
}

##Nagelfar Plugin : Handle special syntax
proc statementWords {words info} {
    set res {}
    # We are only interested in calls to "mugg"
    if {[lindex $words 0] ne "mugg"} {
        return $res
    }
    # If a command has varying syntax depending on contents it can be handled,
    # compare e.g. with a complex command like "if".
    # In this example, only 1 or 5 arguments are allowed, which could
    # also be expressed directly with the syntax string "1: x : 5"
    lappend res comment
    if {[llength $words] == 6} {
        lappend res "##nagelfar syntax mugg x x x x x"
    } else {
        lappend res "##nagelfar syntax mugg x"
    }
    return $res
}
