part of shnell – a source to source compiler enhancement tool
© Jens Gustedt, 2019
The TRADE dialect – dealings in import-export
This dialect can be used to organize a whole project with a consistent naming convention that combines export
and implicit
import. If used without any adjustment it has the following features
Each TU receives a “long name” that is composed by the same identifiers that make up its filename. For the filename these identifiers are joined with a
-
, in the long name they are joined withSHNELL_SEPARATOR
, usually::
. Seeexport
for the naming conventions that apply. The long name of a TU is the analogous of the long name of a human, only that we note these names the Hungarian way, that is the family name comes first and then the given name.Each TU also has a “short name”, usually the last component of the long name for internal use. In our analogy this would be the given name, but it can also be a different nick name if that is more convenient.
The whole code is scanned for other composed names and these are used to determine implicit
#include
that are added. Header files (.h
) are produced and used automatically, there is no need to maintain these manually or to keep track which TU includes features of which other TU. So, again in our analogy, if you know a person’s full name you may refer to that person by that name.
All of this assumes that all long names are unique and that there are no homonyms, and this is clearly the point where our analogy to the names of people ends.
Visibility of identifiers
By that all globally visible names of objects, functions, types, enumeration constants and macros are unique across the whole project and no TU impedes on any other. Mutually visible between TU are only those that are “public”. Per default these are only names of global objects and functions that are not declared static
, that is that (in C speech) have external linkage, plus the short name of the TU. Thus if you have a TU named test-string.c
that defines a struct
string
type, the type and all global functions that are defined are visible from the outside and constitute the interface of the TU. Other TU see and can use the type as test::string
and can use the functions as test::string::func
for a function func
defined within test-string.c
.
Other identifiers, such as types or macros, that you want to share can be made public by prefixing them with the short name in their definition. See export
for a summary of all the conventions that apply.
Fine-tuning of the naming conventions
Such a fine tuning can be achieved by using some other directives. These should all be used with the marker PRETRADE
such as in #pragma PRETRADE alias
.
private
can be used to make identifiers private that would be public otherwise. This could e.g be functions that for some reason need external linkage, but which should not be part of the API of the TU.
alias
can be used to introduce nick names for other TU, or to provide a different short name than the default. These can then be used as convenient prefixes if otherwise you could have long impractical names.
using
establishes a whole set of identifier short-cuts that can be used directly without prefix, e.g
make it possible to use fprintf(stderr, ...)
just as you would in a traditional C project.
legacy
Internally we maintain a list of pseudo-TU that are to be used in “legacy” mode. Per default this contains stdc
for the whole standard C library. E.g by using stdc::printf
you will have access to the printf
function of the C library, just as you are used to.
To integrate a legacy project trex
, say, you’d have to provide a traditional header file trex.h
that provides all the symbols that are needed and you’d have to add trex
to the list, something like
Then all your code can refer to the birth
function in the trex
project by using the name trex::birth
. By that there would be no collision with a birth
identifier that you’d use directly for your own project.
main
is special
The main
symbol is the only one from the C standard that is defined by user code, and so we must be able to do so without rewriting it to a long name. This is achieved by defining it as stdc::main
, that is treating main
as a legacy identifier from the C standard (which it is).
The export
module then takes even care that several TU may have such a main
, such that each TU may for example implement a standalone test.