gm2
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Dialect & Target Specific M2 Code


From: Benjamin Kowarsch
Subject: Dialect & Target Specific M2 Code
Date: Fri, 5 Jan 2024 00:15:54 +0900

Further to my comments on (1) foreign function interfacing within M2 and (2) using M2 from C and thus targeting C, I would also like to comment on a related subject matter: writing dialect specific and target specific M2 code, in particular within the context of conditional compilation.

I used to work on cross-platform code in C and the prevailing approach there is to use conditional compilation via the C pre-processor to support multiple platforms.

At first I found the conditional compilation approach using #if #else #end pragmas directly within the C source code quite convenient and I felt that this was a feature that should also be supported by Modula-2 compilers, ideally using a common specification so as to make the code also work across different compilers.

Over time I found out that such sources will become more and more unwieldy, difficult to read and eventually unmaintainable. So, I moved to an alternative approach: Each platform specific source file contains only the code for one specific platform and for each platform to be supported there is a platform specific version of every file that contains platform specific code.

Later, I used the very same approach for source files that contain dialect specific code: Each dialect specific source file contains only the code for one dialect and for each dialect to be supported there is a dialect specific version of every file that contains dialect specific code.

This means no conditional compilation directives are needed. When writing code, the author can focus on the platform and dialect of the source file being edited, reducing mental load. When reading and maintaining code, the reader or maintainer can focus on the platform and dialect of the source file being read or maintained, also reducing mental load. When testing code, the tests can focus on the platform and dialect of the source file being tested, reducing mental load for the tester and decreasing the chance that important test cases are omitted as a result of the "can't see the forest for the trees" effect. Also, unit tests do not need to be retested when an unrelated test case for a different platform or dialect has been added or modified. Only tests specific to the tested platform and dialect need to be tested for each source file.  

I don't know if GM2 supports conditional compilation nor if it is planned to be added in case it isn't supported, but I would advise against supporting/using it.

Instead, I would propose support for selecting a specific variant of a platform or dialect specific source file. This could be done directly in the compiler, or in a build configuration utility.

The simplest way to tell the compiler or build configuration script which variant a given source file belongs to is a file naming convention where the platform and/or dialect is encoded into the filename.

Examples

hashlib.64bit.mod => 64 bit version of hashlib.mod
hashlib.32bit.mod => 32 bit version of hashlib.mod

stdio0.gm2.mod => GM2 version of stdio.mod
stdio0.mocka.mod => MOCKA version of stdio.mod

foolib.x86.mod => x86 version of foolib.mod
foolib.x86-64.mod => x86-64 version of foolib.mod

barlib.pim.mod => PIM version of barlib.mod
barlib.iso.mod => ISO version of barlib.mod

All there is to do then to compile the correct files is to make a copy thereof and save them under the simple file name without any platform/compiler/dialect specific name component into the build directory, then compile that build tree.

This can be automated. I usually write a bash script for this purpose which is then specific to a given project. But this could also be done in the compiler, or in a build configuration utility.

$ m2cfg foolib.mod --select-variant=x86-64
$ m2cfg barlib.mod --select-variant=pim

etc

This could also be done for multiple such variant tags in the filename.

Most people will agree that such platform or dialect specific source files are easier to read, test and debug but argue nevertheless in favour of conditional compilation because they find it cumbersome to edit multiple files, one for each variant per original source file. This is a genuine concern, but it is not justification for conditional compilation and the spaghetti code that results from it.

The solution to the problem of multiple versions is to generate the variants from a common template. This can be done with a very simple template expansion utility that searches for placeholders in the source template and replaces them with the variant specific content to write out the variant specific file.

This way, there is a single template file, which is then expanded to generate the variant specific versions.

I have used this approach for many years in C projects using a very simple template engine utility. I have also used this for doing generics in C with the added benefit that I could always read (and edit) the generated code without having to do the expansions/replacements in my head which adds significant mental load and is typical for generics built into languages that have them.

In M2 R10 we reserved certain symbols for use by a template engine:

https://github.com/m2sf/m2bsk/wiki/Language-Specification-(1)-:-Lexical-Entities#symbols-reserved-for-other-uses

where /* and */ are delimiters for template comments to be removed altogether; and <# and #> are delimiters for placeholders to be replaced with their respective variant specific translations in the generated file.

Thus, my recommendation for the further development of GM2 is to abstain from adding language syntax or pragmas for conditional compilation -- or if that has been done already, to deprecate it -- and instead use a multiple file variant approach implementing utilities that support this by configuring the source tree and expanding template files into variant specific versions.

Again, this follows a philosophy where compiler/dialect/target specific details are kept out of the source file as much as possible and limit such details to one variant per file in cases where compiler/dialect/target specific details cannot be avoided in the source code.

regards
benjamin


reply via email to

[Prev in Thread] Current Thread [Next in Thread]