
What is Unimal?
( Contents)
(Initial intro is
here. For a brief executive summary click
here). Complete
online manual
(large) is available in a new window.
Unimal is an
advanced preprocessor, or macro processor, which is designed to be
independent of the target programming language. Therefore, Unimal supplements
existing programming languages with common macro facility.
How a
preprocessor such as Unimal can improve project maintainability and data
optimization is described in the white paper,
also available in PDF format.
Independence of the target language allows parameter
sharing among the different languages used in a project, e.g., C++,
Assembler and the make utility language.
To
maximize the benefits of parameter sharing, Unimal is capable of
switching among different preprocessor output files on the
fly.
The Unimal
language is powerful enough to enable very
sophisticated compile-time (static) initialization of constant objects.
So, what otherwise had to be done in runtime (e.g., during initialization), can
now be done in compile time. For embedded systems, it also means freeing up
precious RAM by keeping pre-calculated data in ROM.
In addition, Unimal has a
full-blown 32-bit integer math capability with standard math library floating-point
extensions.
And, of course, Unimal
has operators normally expected from a macro processor: conditional branches,
loops, composite names, macros and include files.
What is in Unimal for me?
( Contents)
Like other macro
preprocessors, Unimal is a means of improving
maintainability, reusability and quality of code. What sets Unimal apart
is what exactly can be accomplished by appropriate "code" written in Unimal:
the language power makes the difference. Here are some examples of compile-time
applications:
- automatically
generating a tabulated function, with parameter-controlled
resolution
- fully automatic
generation of constant lookup tables
- compaction of sparse
tables
- exporting
configuration parameters (e.g., array size) to different languages
Unimal does not provide
most of these capabilities as part of the language. Instead, it is the language
that makes implementation of complex compile-time
algorithms possible.
If you choose to
download Unimal, take a look in the AppNotes
directory for a few application examples.
You can also read the
Application notes online in PDF format (to get the optional sample files, click
here):
Application Note 1. Managing unstructured data layouts from legacy
code |
Relations between strings and names in
Unimal |
Application Note 2.
Stretching
Unimal |
Extend Unimal capabilities using
temporary files |
Application Note 3.
Computing a remainder modulo a constant
|
Develop an algorithm, then implement it
at compile time |
Application Note 4.
Automating sparse and lookup tables
|
Examples of developing rather complex
algorithms and implementing them at compile time: - storage and maintenance
optimization for sparse tables; - zero-maintenance lookup tables for
accessing constant data |
| New
Application Notes (not yet in unimal2.zip) |
Application Note
5.
Common string
conversions |
Conversions between numbers and strings Replacing characters
in strings (e.g., converting to uppercase) Generating an error message
algorithmically |
Application Note
6.
Compile-time
sorting of symbolic constants |
Seamless integration of Unimal with the C
language.
Complex compile-time algorithm implemented
jointly by Unimal and C |
Application Note 7.
Guarding the
include files |
When to guard and when not to guard Unimal include
files. Using uAutoLine as an include file guardian |
Application Note 8.
Implementing
complex string algorithms at compile time |
Representation of pre-defined encoding of strings in
predefined charset Generating
tree-like structures and recursive macro expansion Generating a containing superstring and
multi-suffix composite names Pretty-formatting the output |
Also, a few examples and
motivations were presented at DesignCon 2001 in Santa Clara, CA,
TecForum HP-TF2. For your convenience, you can download the presentation
here.
It's probably worth
noting that some macroassemblers provide number crunching macro facilities
comparable to that of Unimal. A generic treatment of this issue was (supposed
to be) the topic of Class #347 at Embedded
Systems Conference West 2000 in San Jose, CA. The paper and the handouts
are available for download here. Note
however that the same algorithm implemented in Unimal will run faster, and
probably much faster.
Unimal 2.1 as a
command line utility is available for Win32 and Linux platforms.
Other platforms can be supported if requested.
You can download Unimal 2.1 for Win32 and use it
freely for unlimited evaluation. Any use of Unimal output, whether verbatim or
however modified, in a product requires a license. You can purchase a license
now by clicking the "Purchase" button on the left. For volume discounts,
please, contact MacroExpressions.
How Unimal works (
Contents)
The Unimal preprocessor
utility takes an input file and produces an output file, which presumably is a
source code file in a target programming language. The input file is a source
file in the target language, marked up (sometimes, very generously) with the
Unimal language statements. (In fact, Unimal can produce several output files,
maybe, in different target languages.)
The Unimal language
consists of two related parts: Unimal operators
and Unimal target language interface. In somewhat
simplistic terms, operators manipulate signed 32-bit numbers, and the target
language interface submits the numbers to the target language.
Unimal operators are line-based. They start with a
signature #MP at the beginning of the line, with optional preceding
white spaces. (To demystify the signature, "MP" stands for "Macro Parameter.")
Example:
#MP
Set mything =
17
assigns the value of 17
to the macro parameter mything. Unimal operators manipulate macro parameters
for their own sake; they do not produce any output to the (target language)
source code file. Any line starting with an #MP is considered a Unimal
operator. If Unimal fails to figure out what it means, it will generate an
error.
Any line in the input
file not starting with an #MP is considered a line in the target
language possibly sprinkled with Unimal target language interface statements.
Unimal replaces any target language interface
statements with appropriately formatted numbers and copies the
transformed line to the output file. The way the language interface works is
very simple: Unimal finds any occurrence of the sequence
#mp<format><name>
in the source code and
replaces it with the value currently held by <name>. <format>
specifies how exactly the value is "printed" into the target language source
file. Example:
int
x =#mp%dmything;
will generate the
code
int
x =17;
in the source file,
provided that mything is still 17. Note the C style format "%d" meaning "render
as decimal number."
As an illustrative
example, consider tabulating a sine wave in
the interval [0, π/2] with the scale factor 10000 and integer precision. The
number N+1 of tabulation points may change from time to time. Here is a snippet
of Unimal-enhanced C definition:
#MP
Set N=30 ; for example, 31 points
int const
sine_wave[] = {
#MP For I=0,
N ;from
0 to N inclusive
#MP X = Usin(10000,
1, I, N)
#MP ;Now, render computed value
as decimal
#mp%dX,
#MP Endfor
};
This simple example
demonstrates that tabulating a function is as easy as calculating its value in
Unimal. And, most importantly, all maintenance
reduces to setting a single parameter (N, in our case).
Is it hard to write Unimal code? (
Contents)
The Unimal language is
very simple. So, simple (yet useful) tasks like in the example above are simple
to code.
But then, one
may want to venture in more complicated algorithms. Unimal sets no limit on the
algorithm's complexity. In some cases, Unimal reduces complexity because of its
built-in capabilities, but in general, the Unimal code is going to be
accordingly complex. The reward, though, is that Unimal code written once
improves the maintainability for the lifetime of the project family.
The good news is that
Unimal code implementing a complex algorithm does not have to be
very efficient. Indeed, since Unimal is a preprocessor, the only
thing sacrificed by inefficient Unimal code is the compilation time. Probably,
reasonable efforts should be taken not to make inefficient code (like unused
parameters), but other than that – who cares?
Unimal comes with a
number of Application Notes; they can be downloaded separately or viewed
online.
Any debugging aids? (
Contents)
To debug Unimal code, you
need to insert artificial "target language" statements providing debugging
information. It is similar to old-fashioned debug prints in common programming
languages, except that you inspect the debug information in the Unimal output
file rather than during the program execution.
A powerful technique of
debugging Unimal code is to craft appropriately and employ the special macro
uAutoLine which expands automatically. Please see the manual for more
information on this automatic macro.
Currently, there is no
interactive debugger for Unimal. If you would like to see interactive debugging
capabilities in Unimal, please, let us
know.
How about a convincing example? ( Contents)
"Unimal allows reducing code maintenance complexity, reducing
the project's memory requirements and (in embedded systems) putting in ROM what
otherwise had to be calculated in runtime." To support this claim,
consider the following example.
A project uses a table of
objects of some sort. Each object is equipped with its unique numeric key. In
C, it might look like this:
const ob_type myobjects[] = {
{9, myob},
{11, yourob},
{24, ourob},
{27, theirob},
};
It is required to create
a very fast access method to the objects by the key , i.e., a function that
takes the key as an argument and returns a 1-based index of the object's entry
(or 0 if no such object exists). Below is Unimal implementation of a two-tier
key lookup table search ("Expand" is the macro invocation operator; for
explanation of the Unimal macros used here, please, refer to Unimal Application
Note 4). #MP Set SplitDivisor = 4 ;algorithm parameter
#MP Expand StartTable(myobjects)
#MP Expand ObjDef (9, myob)
#MP Expand ObjDef (11, yourob)
#MP Expand ObjDef(24, ourob)
#MP Expand ObjDef(27, theirob)
#MP Expand EndTable()
Here is the C code Unimal
generates:
const
ob_type myobjects[] = {
{9, myob},
{11, yourob},
{24, ourob},
{27, theirob},
}; //End of the original object table const int
mylookup[]={//merged lookup table(s)
0,
1,
3,
2,
2,
4,
}; //done with common table
myaccess(int key)
//access method
{
int Ob; //index of the object in ObjTable
Ob = mylookup[mylookup[key/4-2]+key%4];
if(Ob<1 || Ob >4) return 0;
//out of range
if(myobjects[Ob-1].key!=key) return 0;
//check failed
return Ob;
}
One can see that the
access method is very efficient and the necessary lookup table is very small.
So, we've got a fast and compact access function with performance
independent of the size of the table.
The next observation: If
the table of objects changes, the new access method will be generated
automatically at the next compilation, so we've got zero-maintenance
implementation.
And indeed, our lookup
table is calculated in compile time and can be ROM'ed, so we've got start-up
time and RAM consumption reduced.
Including Unimal in a toolchain ( Contents) This is fairly straightforward. Since Unimal is a
console (command line) utility, it is included in a project pretty much like
any compiler is.
If
you use Make utility to build your project, you can define dependencies on
Unimal files, or you can define inference rules for Unimal file extension(s).
Unimal can generate include file dependencies, so it is possible to use it in
professionally robust makefiles.
To include
Unimal in an integrated development environment, such as Codewright, the
simplest way is to define Unimal as a compiler and associate Unimal file
extension(s) with this new compiler.
It is also
possible to include Unimal in proprietary IDEs which allow third-party tools
integration (such as, for instance, IAR Embedded Workbench).
To make
the integration with IDE error parsers and code browsers easier, Unimal
supports customization of error message format and can emit input and output
file and line information.
If you
have any difficulties integrating Unimal, please, contact us and we will be happy to help.
|