Logo

MacroExpressions Products: Unimal

Home Products Services Download Contact us
General introduction to MacroExpressions ^
<You are here>
Consulting, products customization, custom development C-Slang, Unimal and Snob are available for evaluation Email, telephone and fax numbers

Click on a button below to go to a related page
 



customer care
 
Table of contents
What is Unimal?
Benefits of Unimal
How it works
Availability
Is it hard?
Any debugging aids?
Convincing example
Unimal in toolchain


 
Credits and acknowledgments

 
   

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.

Unimalis 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

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
New Application Notes (not yet in unimal2.zip)
Stay tuned!  

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.


Availability ( Contents)

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.