* NEW: Scripts to generate Gambas documentation files from C/C++ sources



git-svn-id: svn://localhost/gambas/trunk@6712 867c0c6c-44f3-4631-809d-bfa615b0a4ec
This commit is contained in:
Tobias Boege 2014-12-06 13:18:04 +00:00
parent a213747294
commit ea6578d19b
7 changed files with 309 additions and 0 deletions

113
c2help/README Normal file
View file

@ -0,0 +1,113 @@
About c2help
------------
c2help is used to extract Gambas documentation from C/C++ source files and
create .help files for a component from them.
Project compass
---------------
./extract.awk -- Extract help comments
./xlate.sh -- Translate C/C++ function names to Gambas symbol names
./prefix.awk -- Prefix symbol names with the classes they belong to
./mkhelp.gbs3 -- Create a .help file from the (properly formatted) input
./c2help.sh -- Executes the above programs in the correct order to create
the .help in a number of C/C++ sources.
... and why?
------------
Components written in Gambas can already be documented in-code. This docu-
mentation is then stored inside their .info files. With a similar result
provided by c2help for C/C++ components, we can document all[*] Gambas in
its source files (at least my components will be) which makes it, IMHO,
easier to keep the documentation up-to-date. Also the help could be
displayed locally by the IDE or remotely once these .help files are imported
to the gambaswiki.org site.
Also, documentation can be bound to a specific source code version, so that
you get docs for the version of Gambas you are running, not only for the
development branch.
[*] I'm not sure about intrinsic functions, though. Should be feasible,
looking at gbx_class_info.c...
Great! How do I prepare my component?
-------------------------------------
The No 0 rule is to BE CAREFUL! There are quite some rules to remember:
1. Documentation syntax.
You may write a Gambas documentation comment like
/**G
* Here goes the documentation.
**/
BEGIN_METHOD_VOID(ClassName_MethodName)
/* ... */
END_METHOD
or alternatively
/**G Class Symbol
* Documenting Class.Symbol
**/
Instead of the **/ at the end, you may also write */. Cool, huh?
The difference between both notations is that the first must immediately
precede a BEGIN_{PROPERTY,METHOD,METHOD_VOID) line that defines the symbol
to which the documentation refers. The second syntax documents the indicated
symbol -- no matter where the comment is.
This is intended to document GB_CONSTANTs or GB_PROPERTY_SELFs which don't
have a BEGIN_* line. It can neverthelss be used for any other symbol but
NEVER intermix both syntaxes on a single symbol.
The ClassName_MethodName part I call "(implementation) function name". The
particular format shown is not mandatory. The scripts take the function name
and look up the corresponding symbol in your GB_DESC[].
2. Strict regular expressions.
The regular expressions used to filter these comments are somewhat strict.
You have to pass the spaces exactly as indicated in the line where you
comment text starts, i.e. <space>*<space>text. Also note that Class and
Symbol in the second syntax are case sensitive!
3. Strict structure.
Really. The next empty-or-not line after a syntax-1 help block is taken as
the symbol definition line. If it's not, you get a wrong symbol name.
DON'T try to confuse the parsers by not giving information it needs where
it expects it.
4. Synonyms.
The first match for a function name lookup in a GB_DESC[] is taken as the
"primary" implementation of that symbol. That means the help comment will
be attched to that symbol. All other symbols which call the same function
are considered synonyms and are *automatically* documented with the default
sentence: "This is a synonym for <primary-symbol>". DO NOT document them
separately using a syntax-2 help block!
5. Don't reuse method and property implementations (except for synonyms).
As the implementation function name is searched in the GB_DESC[]s in your
source file, you may not put the same function into different classes -- or
the parser may get confused.
Umm... I need an example
------------------------
gb.openssl and gb.data are partially documented that way. Digest.Hash() and
Digest._call() in gb.openssl show how to deal with synonyms. There the entry
for Hash precedes the entry for _call, thus Hash is the primary symbol and
_call a synonym.
To see a sample .help file (on stdout), you can do at the source tree root:
$ c2help/c2help.sh gb.openssl

1
c2help/TODO Normal file
View file

@ -0,0 +1 @@
- Class documentation. Shouldn't be too difficult.

27
c2help/c2help.sh Executable file
View file

@ -0,0 +1,27 @@
#!/bin/bash
if [ $# -lt 1 ]
then cat >&2 <<EOF
Usage: $0 SOURCES...
Extract Gambas help comments from all SOURCES and output a .help file.
If SOURCES contains directories, they are searched recursively for .c
and .cpp files.
EOF
exit 1
fi
function doit {
for arg in ${@:1}
do echo Processing $arg... >&2
if [ -d "$arg" ]
then doit $(find -H $arg -name \*.c -or -name \*.cpp)
else $(dirname $0)/extract.awk <$arg | sed "s/^[ ]*\*/\'/" |
$(dirname $0)/xlate.sh $arg | $(dirname $0)/mkhelp.gbs3
fi
done
}
doit ${@:1}

72
c2help/extract.awk Executable file
View file

@ -0,0 +1,72 @@
#!/usr/bin/gawk -f
# Extract comments from C/C++ source files (stdin)
BEGIN {
do_record = 0;
not_this = 0;
symname = "";
FS="";
}
/^[\t ]*\*?\*\/$/ {
if (length(symname)) {
print "G "symname "\n";
do_record = 0;
}
not_this = 1;
}
/^BEGIN\_.*\(.*/ {
if (do_record && length(symname) == 0) {
s = gensub(/^BEGIN\_.*\(([^,)]+).*/, "\\1", "");
if (length(s) == 0)
print "ERROR\n";
else
print s "\n";
do_record = 0;
}
}
{
if (do_record && !not_this)
print gensub(/^[\t ]*/, " ", "");
not_this = 0;
}
# The 'G' flag is important to distinguish intended Gambas documentation
# for the wiki (*.help files) and already existing C function commentary.
#
# The start marker may either be:
#
# ^/**G$ # Note that this describes an *entire* line
#
# when it preceeds a symbol definition (BEGIN_*) or alternatively
#
# /**G Class Symbol$ # Here, only the end of line matters
#
# if it documents the Gambas symbol "Symbol" in "Class". This is intended
# for documentation of symbols that are not to be implemented in something
# that begins with BEGIN_*, like GB_CONSTANTs or GB_PROPERTY_SELFs. It can
# also be used for any other symbol type but both documentation types should
# not be used together on a single symbol.
/^\/\*\*G$/ {
if (do_record)
print "ERROR\n";
symname = "";
do_record = 1;
}
/[\t ]*\/\*\*G .+$/ {
if (do_record)
print "ERROR\n";
symname = gensub(/\/\*\*G (.*)$/, "\\1", "");
gsub(/^[\t ]*/, "", symname);
do_record = 1;
}
END {
if (do_record)
print "ERROR\n";
}

42
c2help/mkhelp.gbs3 Executable file
View file

@ -0,0 +1,42 @@
#!/usr/bin/gbs3
Private $cHelp As New Collection
Private $aCurrent As New String[]
Public Sub Main()
Dim sLine As String
Dim cClass As Collection, aHelp, aSyn As String[]
Dim iInd As Integer
For Each sLine in File.In.Lines
If Not sLine Then Continue
If sLine Begins "'" Then
$aCurrent.Add(sLine)
Else
With Scan(sLine, "* *") ' <Class> <Symbol-and-Synonym-List>
.[0] = Trim$(.[0])
.[1] = Trim$(.[1])
If .Count <> 2 Then
Error "Skipping malformed line:";; sLine
Continue
Endif
If Not $cHelp[.[0]] Then $cHelp[.[0]] = New Collection
$cHelp[.[0]][.[1]] = $aCurrent
$aCurrent = New String[]
End With
Endif
Next
For Each cClass In $cHelp
Print "#"; $cHelp.Key
For Each aHelp In cClass
aSyn = Split(cClass.Key, " ")
Print aSyn[0]
If aHelp.Count Then Print aHelp.Join("\n")
For iInd = 1 To aSyn.Max
Print aSyn[iInd]
Print "' A synonym for";; aSyn[0]; "."
Next
Next
Next
End

16
c2help/prefix.awk Executable file
View file

@ -0,0 +1,16 @@
#!/usr/bin/gawk -f
BEGIN {
class = "ERROR";
}
/GB_DECLARE/ {
class = gensub(/GB_DECLARE\("([^"]+).*/, "\\1", "");
gsub(/[\t ]+/, "", class);
}
match($0, "GB_.*" funcname) {
printf class " ";
# Only print once as there may be synonyms.
exit;
}

38
c2help/xlate.sh Executable file
View file

@ -0,0 +1,38 @@
#!/bin/bash
# Translate C/C++ function name to Gambas symbol name
if [ $# -ne 1 ]
then cat >&2 <<EOF
Usage: $0 CLASSDEF
Translate C/C++ function names (from stdin) to Gambas symbol
names using the class definition in the CLASSDEF file.
EOF
exit 1
fi
while read line
do if [ -z "$line" -o "$line" == "NULL" ]
then echo $line
continue
fi
if [[ $line == \'* ]]
# Quoting is important here to not lose whitespace
then echo "$line"
continue
fi
# It's the symbol name already (a syntax-2 help block)?
if [[ $line =~ ^G[\ ] ]]
then echo "$line" | sed 's/^G //'
continue
fi
# Now, we have a C/C++ function name in $line. First search the class.
$(dirname $0)/prefix.awk -v funcname=$line <$1
# Put all the synonyms behind.
egrep "GB_.*$line[,)]" $1 | sed 's/^[\t]*GB_[^(]\+("\([^"]\+\)".*$/\1/' |
tr '\n' ' '
done