ListGen
Author:
Karsten Thamm
Website: http://www.kthamm.de
Disclaimer
List Gen is a Closed Source Freeware Tool package. Use ListGen at your own risk. Copying and distribution is unlimited. Modification of the package or one or more of it's parts is prohibited.
The author takes no warranty for data loss or damage caused by the usage or installation of this tool, for example through virus infection, spyware installation or any other kind of third party trespassing.
Furthermore the author takes no warranty for the proper operation of this tool. This tool was explicity created to manipulate Sourcecode. It is equipped with an automatical and non confiruable backup function for files being manipulated with ListGen. Neither the desired operation nor the backup functionallity have passed a long term quality test. They worked fine so far, but could fail under certain conditions. Therefore use it on your own risk. The author cannot be held accountable or liable for dataloss or damage caused by improper functionallity or improper usage of this tool.
The operation and distribution underlie German and European legilation. If the usage, operation or distribution of this package violates the law of any other country, then I prohibit usage, operation and distribution in or through this country, it's citicens or it's legal persons. The author takes no warranty for modified packages or law-violation caused by distribution, operation or usage of this package out of Germany or the European Union.
What is it ?
What is the purpose of ListGen ?
Well, ListGen makes list maintnance easier. What does this mean. Imagine you have an enumeration, like the enum type in C/C++. Now you want to write a hard coded parser, which reads keywords from a file (let's say XML or any other format) and builds a syntax tree. You want to directly translate the keyword into a token-code specified in the enumeration. Forthermore, you want to write a text-file that uses these tokens. You need at last 2 or 3 'lists', having the same content. When now maintaining the list (for example you want to add a new keyword), then you will have to add it to all corresponding lists. That's a lot of maintnace work and you will get problems when forgetting one entry.
This is the case where ListGen makes programming work easier. Instead of maintaining 3 different lists, you can focus on a single list and ListGen does the rest for you.
Here is an example (the one of the 'sample' folder of the package):
My intention was it to write a package specific exception class, which can handle exceptions in a platform independent manner. Now, exceptions are not platform independent ... they are a part of the underlying architecture and are treated in different manner with different operating systems, different CPUs or different compilers. There are some exceptions alpha platform, which don't exist on Intel platforms and vince versa. The exception type is mostly stored as a 32 or 64 bit value, but on different platforms these values may have different semantics. My solution was an own, package internal exception code, with a function that can return an exception independent string and other function to translate the platform dependent exception code to the package own code and vince versa. So far, so good. This makes the following tables:
An enumeration of the package internal exception- A string table with the mapping from exception code to the descriptive string
- A table to convert Platform dependent exceptions to package dependent exceptions (for each supported platform)
- A table to convert package dependent exceptions to Platform dependent exceptions (for each supported platform)
That makes a total of 2 + 4*S lists or tables to maintain, where S is the number of supported platforms.
Now, this isn't not much work when operating with the hardware and OS specific exceptions only, but you can also throw own exceptions, and you whish to be able to find out the reason for this exception later, you probably want to specify you own set of exceptions. When adding an exception you will have to maintain 2 + 4 * S tables.
Why not reducing maintnance to a single table ?
And this is where ListGen does a great job to you. With ListGen you can define your own 'list' which represents the content itself and some patterns, which tell ListGen how to insert the information into your tables within your code.
Command line options
Calling ListGen from command line:
ListGen <Sourcefile> <Sourcepath> <Configfile>
Unluckily you need full paths for ListGen. Later versions will have a configurable search path, but this version does only take full paths.
Note for future versions
Future versions of ListGen will have a completely different syntax. This is because ListGen will become a part of a whole toolset for sourcecode manipulation and has to fit to that suite.
IDE configuration for M$ Visual Studio
Notes:
If you are using an IDE other than Micro$oft VisualStudio, then you can skip this part.
If you are using older Micro$oft IDEs than 7.1, then you may probably use a slightly different procedure than the one I am describing.
The Visual Studio IDE (which is BTW an excellent piece of software .... I say that because I hate Microsoft, but I must admit VS7 is excellent, like some other products aswell !!) supports 'external tools', which means that you can call external command line programs and pass some IDE specific command line options to the tools you are calling.
![]() |
The Microsoft
Visual studio IDE supports external tools (this is the German version).
You can configure the IDE to support the usage of external command line
tools. |
![]() |
If you click on "external tools" in the menu, then the 'external tools' dialog will open. you can now enter the name (choose a descriptive name for listgen), the complete path to the executable and the command line arguments. If you choose 'standard output to ide ourput' then you will see the output of ListGen in the IDE output window. You can then directly click on a line in the case of an error and be directed to the file and line where the error occured (like when compiling) |
The command line arguments should be: $(ItemPath) $(ItemDir) " <Pfad zum Configfile> For example: |
I don't know
exactly why this additional quote is needed, but however, I need it
to make the IDE work properly ... I think it's a bug in the IDEs variable
substitution routine. |
Now hit the OK button
and ListGen should appear in the menu. |
|
![]() |
Now count the entries of 'external tools' until you reach your new 'listgen ' item. It's Nr. 8 in this example, but could differ in your own settings. When having localized the rank of the new item hit 'adapt' or whatever it's called in English (damn, I want the English version ... even some Compiler Errors are translated into German .... It took a while until I understood them, 'cause I am used to the English errors) |
![]() |
When the dialog opens hit 'Keyboard' ..... |
![]() |
Now search for 'external commands' in the list-control .... I don't know how it's named in the English version. I am using external command 8 in this case, because ListGen was the 8th external command in my IDE. Select the item in the list box and then klick into the key-combination field. Enter the desired keyboad combination, which you want to start 'ListGen' ... I used 'Ctrl-Alt-L' (even the Key 'Ctrl' is translated into German) and don't forget to hit 'assign' (here 'Zuweisen', which is the german word vor 'assign'. |
Try it .... If you did
not configure something yet then ListGen should stop wth an error. The
error message should be printed in your IDE output window. |
The config file
There are two purposes for the config file:
Note that the searchpath specification DOES NOT include subpaths. This will however be configurable in future versions, but this version does not support it. This kind of sucks, because you will have to add all bubpaths manually.
The first version of ListGen had even more configurations to be set, but this was to rigid, so I moved the rest of the configuration to the inline section of the files.
This is how the config file whould look like:
@logfile: C:\projects\glibs\logs\listcompile.log
# All paths to search into
@begin_paths
C:\projects\glibs\ker_basic_test
C:\projects\glibs\ker_basic\platform
C:\projects\glibs\ker_basic\platform_more
@end_paths
ListGen only touches files within that path, and (I say this again) does not touch subpaths (yet).
The configfile itself is the third command line option when calling ListGen. It has to be specified with it's full path.
Inline Blocks
The basic principle of ListGen is reading and writing information 'inline' from or into files. Therefore blocks can be defined that contain these inline directives for ListGen. These inline blocks can be either located within sourcefiles directly or within other files. To hide these blocks for the normal compiler, you have to specify ListGen blocks within comment sections. The better solution is to define everything you need in an external file and only put the substitution regions into the sourcecode files. This will avoid huge and ugly comment blocks within your sourcecode.
The tag that defines an inline looks as follows:
$$%%$$%%Blocktype>>>> Parameterlist
where Blocktype and Parameterlist are replaced with special tags which will be dicussed later.
An inline block is terminated with:
$$%%$$%%Blocktype<<<<
Listgen only reads the lines within these tags and will ignore everything else.
ListGen Block types
There are 3 different block types for ListGen:
Config blocks contain all list and pattern definitions and a list of files to be touched. ListGen only reads the config block of the file that was specified as the first paramter when calling the tool. If you have configured ListGen as an external tool for the Visual Studio IDE, then ListGen will only read the config block of the currently opened file.
ListGen Working passes
ListGen works in three passes. The first pass reads the specified config file and the Source file, which was also specified via command line option. From the config file it receives the path ans logfile information, and it receives the list-, file and pattern definitions from the source file.
After that ListGen builds lists all files to be red .... combining the path information with the file information and checking whether the file exists or not. This is the reason why the Sourcefile itself must be listed in the file-definition (see config block) and it's path must be specified in the logfile.
Then all the files are scanned for list tags (see below) to receive the list content.
ListGen then has all information it needs to substitute the lists.
The last pass writes the list content according to the pattern specification between the corresponding substitution tags.
NOTE: It's very important that the path of the sourcefile is listed in the config file and the source file itself is listen in it's file-block in the config section. If not, all the other content (list definition and substitutions) of the source file will be ignored. This 'bug' will be eliminated in further version (where ListGen works totally different than it dows now)
From the sample file glibPlatformException.h see how this is meant:
$$%%$$%%conf>>>> lists.conf
@begin_list
exceptions
@end_list
@begin_files
glibPlatformException.h
glibPlatformException.cpp
@end_files
The config file must have the full path where glibPlatformException.h resides it !! This is
@begin_paths
C:\projects\glibs\ker_basic_test
C:\projects\glibs\ker_basic\platform
C:\projects\glibs\glibs\ker_basic\platform_more
@end_paths
The config block
The config block is only red for the file specified as the sourcefile in the command line options. There can be more that one config section, then they will be treated as a coherent block. Config blocks contain:
Look into codelist.cfg for and example.List declaration is an enumeration of all lists to be processed by ListGen. Lists that are not declared in the list block will not be touched by ListGen.
Same to say for files. Only the files specified in the file block are touched by ListGen. Of course these must reside within the path specified within the config file.
It's getting interesting now !
The Pattern declaration
Patterns are declared within the pattern block. Each list must have it's own pattern block. Only those pattern blocks for lists that are declared in the list declaration are used by ListGen. Pattern blocks look like this:
@begin_patterns: <listname>
.....some pattern desctiption ....
@end_patterns
This means: you are declating the substitution patterns for the specified list. ListGen also takes lines with a leading '//' or any other character, as long as it it delimited by at last one whitespace.
The Pattern description
Pattern descriptions are blocks of 4 (or 5, if using commentstyle) lines. One pattern declaration block must have at last one pattern description, but it can have an unlimited number of pattern descriptions. Each descrition block must have a unique name.
@pattern: <Type> <Name> <Indent> $<pattern>
Type: Each pattern has 4 types of patterns:
All of these types MUST be specified. This is the reason why you have to have exactly 4 (+1 optional) line of pattern description, because you must specify all 4 cases.
So what is it ? When having an enumeration, you are delimiting the symbols with a comma:
enum MyEnum {
First,
Second,
Third,
Fourth}
The last symbol does not have a comma (most compilers will take it, but it's not very nice, so it would be fine not having a comma (other constructs like constant arrays must have a comma for example). And that's the difference between norm and last .... the last item within a list will always be substituted with the 'last' pattern, while all other items will use the 'norm' pattern. This gives you the possibility to make a different substitution for the last item.
The n_ prefix stands for numeric. Now .... ListGen internally manages a counter (numeric value) for each list item, which can be influenced by the list descrition.ListGen also keeps a mode for each single item, which specifies an item as numeric or not numeric. And that's all about it. If the internal mode is set to numeric then ListGen will simply substitute the n_-patterns, if the mode is not numeric it substitutes the non-prefixed patterns.
What is it for ? If you selectively which to explicitely specify the number to an item:
enum MyEnum {
Invalid = -1,
First = 0,
Second ,
Third,
Fourth,//----------------------------------
MyEnum_END,
//----------------------------------RESERVE = 1000
}
Name: Each pattern desription block must have a unique name. You can (and that'a actually the purpose of ListGen at all) specify more than one pattern to ListGen, so each single pattern must have a unique name.
Indent: Specifies the indentation of the pattern. Additionally, the value of the 'norm' line specifies the indentation of automatically generated comments.
Pattern: Note that a pattern descrition is initiated with a leading '$' (Dollar character) ... well, this is the string that describes the pattern itself.
The pattern String
The pattern string starts at the $ character and runs to the end of the line. ListGen first substitutes the substitution tags and then prints the substituted string to the file. These are the characters that will be substituted by ListGen:
Character(s) | Substituted to .... |
§ (paragraph) | Will be replaced with the Name of the list item |
# | Will be replaced with the numeric value of the list item |
@0 - @9 | Will be replaced with the n-th optional substitution parameter, if it exists, with an empty string otherwise |
\# | # |
\§ | # |
\@ | @ |
\$ | $ |
\n | newline or newline/cr (the latter on M$ Operating systems) |
\t | TAB |
\i | number of indent tabspaces (see indent parameter) |
\\ | \ |
What's missing ? Well, I would like to add a formatting description for the numeric value (so you can also format it as hexadecimal for example) and a shift operator (for example for flags) or a formula to compute a value from the numeric value. But this is however not yet implemented.
This is a typical pattern description for an enumeration (with an indentation of 1 tab):
@pattern: norm enum 1 $§,
@pattern: last enum 1 $§
@pattern: n_norm enum 1 $§ = #,
@pattern: n_last enum 1 $§ = #
By the way: the name of the pattern must be unique within a list .... but not globally ! You can use the pattern name 'enum' for other lists aswell.
Specifying a pattern declaration for more that one list is not possible (yet !) ... If you declare another list, then you will have to do this redundantly.
This pattern block can be followed by an optional commentstyle-line (see next section).
The comment style description
ListGen automatically generates comment lines, a feature that cannot be turned off yet. Originally, ListGen was made as small utility for private use and restricted to C/C++ sourcecode, but later I had to use it on TCL and C++ in combination, which forced me to include a configurable commenting style.
Well, this is simple. Note that it must come AFTER the actual pattern block:
The Syntax:
@commentstyle: <pattern-Name> <Starting sequence> <optional end sequence>
Examples:
Programming language | Commentstyle line |
Basic | @commentstyle: <pattern-Name> rem |
Linux shell script | @commentstyle: <pattern-Name> # |
Classic C | @commentstyle: <pattern-Name> /* */ |
DOS-Batch-Script | @commentstyle: <pattern-Name> ; |
TCL | @commentstyle: <pattern-Name> # |
XML/HTML | @commentstyle: <pattern-Name> <!-- --> |
The List definition block (content)
See this file for an example.
Unfortuately you need a hierarchy with the depth of exactly 3. This has historical reasons and is another point to be changed in future versions. These 3 hierarchy stages are:
This is how you have to structure you content. At last, the minimum content you will have to have is one section block, one comment block and one item. Each of the blocks must have at last one child ..... !! Empty blocks are not yet supported.
The syntax:
A list definition block looks as follows:
$$%%$$%%list>>>> exceptions
$ <Section block description>
§ <Comment block description>
... items and control lines
... more structure
$$%%$$%%list<<<<
Control lines:
Control lines are little directives passed to ListGen to influence the following items. Yet, these take effect to only two item state-parameters: numeric mode and internal counter. Initially an item has numeric mode turned off and the counter set to 0 for the first item and being yutomatically increased with each item. However, you can manipulate these values:
% <counter value> | Turns on numeric mode (toggle) and sets the internal counter to the specified value |
# <counter value> | Turns on numeric mode (oneshot) for the following item, and turns it off for all items following ...counter is set to the specified value |
+ | Turns on numeric mode until the appearence of a new command sequence |
- | Turns off numeric mode until the appearence of a new command sequence |
List items:
List items are all non empty lines, that do not start with one of the following characters:
§,$, #, %, -, +
A list item must consist of at last the name of the list item. It can consist of some optional parameters, which are delimited by at last one TAB (!!!). The delimiter must really be a tab, not a space. This is because you must be able to use space within the optional parameters. This is because you sepcify print strings containing spaces (for example exception descriptions) as optional parameters.
Look into the sample file how this is meant to be !!
Pattern blocks (substitution blocks)
Now, when having specified the formatting patterns and having content, then you are ready to run ListGen and insert the content into your code.
This is very simple, just tell ListGen where you want to have your code inserted, by adding markers where you want to have the code being substituted:
Begin of substitution block
//$$%%$$%%pattern>>>> <list name> <pattern name>
End of subtitution block:
//$$%%$$%%pattern<<<<
This means that the content of the specified list ins substituted within this block by using the specified pattern.
Here are some examples:
//$$%%$$%%pattern>>>> exceptions enum
//$$%%$$%%pattern<<<<
//$$%%$$%%pattern>>>> exceptions win2glib
//$$%%$$%%pattern<<<<
//$$%%$$%%pattern>>>> exceptions glib2win
//$$%%$$%%pattern<<<<
//$$%%$$%%pattern>>>> exceptions tostring
//$$%%$$%%pattern<<<<
WARNING ! Do not enter important code within these tags ! It will be overwirtten when ListGen is started.
ListGen automatically backups the old file BEFORE it applys any changes. This is a feature which cannot be configured yet ! The old backup will however be overwritten.